[dolfin] 01/01: New upstream version 2017.2.0.post0
Drew Parsons
dparsons at moszumanska.debian.org
Sun Jan 21 15:00:11 UTC 2018
This is an automated email from the git hooks/post-receive script.
dparsons pushed a commit to annotated tag upstream/2017.2.0.post0
in repository dolfin.
commit 3907d051b624b67ce53ce54b4d7cb946f9f0173c
Author: Drew Parsons <dparsons at debian.org>
Date: Sun Jan 21 19:30:41 2018 +0800
New upstream version 2017.2.0.post0
---
.circleci/config.yml | 32 +-
CMakeLists.txt | 54 +-
ChangeLog.rst | 13 +
bench/fem/jit/python/bench_fem_jit_python | 2 +-
cmake/modules/FindGMP.cmake | 19 +
cmake/modules/FindMPFR.cmake | 83 +
cmake/templates/dolfin.conf.in | 3 -
demo/CMakeLists.txt | 2 +-
demo/documented/bcs/python/demo_bcs.py | 2 +-
demo/documented/biharmonic/cpp/Biharmonic.ufl.rst | 2 +-
.../biharmonic/python/demo_biharmonic.py.rst | 2 +-
.../documented/cahn-hilliard/cpp/documentation.rst | 3 +-
demo/documented/cahn-hilliard/cpp/main.cpp | 4 +-
.../cahn-hilliard/python/demo_cahn-hilliard.py.rst | 2 +-
demo/documented/neumann-poisson/cpp/main.cpp | 3 +-
.../python/demo_neumann-poisson.py.rst | 6 +-
demo/documented/poisson/cpp/main.cpp.rst | 3 +-
demo/documented/stokes-iterative/cpp/main.cpp | 3 +-
.../python/demo_stokes-iterative.py.rst | 6 +-
.../python/demo_subdomains-poisson.py | 4 +-
.../subdomains-poisson/python/documentation.rst | 21 +-
.../python/demo_tensor-weighted-poisson.py | 15 +-
.../python/demo_advection-diffusion.py | 2 +-
.../auto-adaptive-navier-stokes/cpp/main.cpp | 4 +-
.../buckling-tao/python/demo_buckling-tao.py | 2 +-
.../python/demo_collision-detection.py | 2 +-
.../curl-curl/python/demo_curl-curl.py | 2 +-
.../cpp/AdvectionDiffusion.ufl | 2 +-
.../python/demo_dg-advection-diffusion.py | 2 +-
demo/undocumented/dg-poisson/cpp/Poisson.ufl | 2 +-
demo/undocumented/dg-poisson/cpp/main.cpp | 2 +-
.../dg-poisson/python/demo_dg-poisson.py | 4 +-
demo/undocumented/elasticity/cpp/main.cpp | 4 +-
.../elasticity/python/demo_elasticity.py | 2 +-
.../ghost-mesh/python/demo_ghost-mesh.py | 2 +-
demo/undocumented/lift-drag/cpp/main.cpp | 2 +-
.../lift-drag/python/demo_lift-drag.py | 2 +-
demo/undocumented/multimesh-3d/cpp/CMakeLists.txt | 38 +
.../multimesh-3d/cpp/MultiMeshH10Norm.ufl | 20 +
.../multimesh-3d/cpp/MultiMeshL2Norm.ufl | 20 +
.../cpp/MultiMeshPoisson.ufl | 9 +-
demo/undocumented/multimesh-3d/cpp/main.cpp | 224 +
.../multimesh-poisson/cpp/MultiMeshPoisson.ufl | 10 +-
.../undocumented/multimesh-poisson/cpp/P1.ufl | 24 +-
demo/undocumented/multimesh-poisson/cpp/main.cpp | 51 +-
.../python/demo_multimesh-poisson.py | 53 +-
demo/undocumented/multimesh-quadrature/cpp/README | 2 +
.../propeller_2d_coarse.xml.gz | Bin
.../propeller_2d_fine.xml.gz | Bin
.../python/demo_multimesh-quadrature.py | 147 +
.../multimesh-stokes/cpp/MultiMeshStokes.ufl | 10 +-
demo/undocumented/multimesh-stokes/cpp/Stokes.ufl | 52 -
demo/undocumented/multimesh-stokes/cpp/main.cpp | 42 +-
demo/undocumented/multimesh-stokes/cpp/reference.h | 49 -
.../python/demo_multimesh-stokes.py | 52 +-
.../multimesh/python/demo_multimesh.py | 138 -
.../python/demo_multistage-solver.py | 2 +-
.../python/demo_overlapping-regions.py | 2 +-
demo/undocumented/parallel-refinement/cpp/main.cpp | 16 +-
.../python/demo_parallel-refinement.py | 8 +-
.../point-integral/python/demo_point-integral.py | 2 +-
.../refinement/python/demo_refinement.py | 2 +-
doc/source/conf.py | 4 +-
dolfin/CMakeLists.txt | 10 +
dolfin/common/Variable.h | 2 +-
dolfin/fem/AssemblerBase.cpp | 62 +-
dolfin/fem/DirichletBC.cpp | 2 +-
dolfin/fem/DiscreteOperators.cpp | 4 +-
dolfin/fem/DofMap.h | 2 +-
dolfin/fem/DofMapBuilder.cpp | 8 +-
dolfin/fem/DofMapBuilder.h | 1 +
dolfin/fem/MultiMeshAssembler.cpp | 219 +-
dolfin/fem/MultiMeshAssembler.h | 3 +
dolfin/fem/MultiMeshDirichletBC.cpp | 47 +
dolfin/fem/MultiMeshDirichletBC.h | 18 +-
dolfin/fem/MultiMeshDofMap.cpp | 47 +
dolfin/fem/MultiMeshDofMap.h | 6 +
dolfin/fem/MultiMeshForm.cpp | 28 +
dolfin/fem/MultiMeshForm.h | 18 +
dolfin/fem/PETScDMCollection.cpp | 1 +
dolfin/fem/PETScDMCollection.h | 2 +-
dolfin/fem/UFC.h | 7 +-
dolfin/fem/fem_utils.cpp | 1 +
dolfin/function/Function.cpp | 9 +-
dolfin/function/FunctionAssigner.cpp | 1 +
dolfin/function/FunctionSpace.h | 3 +-
dolfin/function/MultiMeshCoefficientAssigner.cpp | 7 +-
dolfin/function/MultiMeshCoefficientAssigner.h | 2 +-
dolfin/function/MultiMeshFunction.cpp | 165 +-
dolfin/function/MultiMeshFunction.h | 62 +
dolfin/function/MultiMeshFunctionSpace.cpp | 33 +-
dolfin/function/MultiMeshFunctionSpace.h | 7 +-
dolfin/generation/BoxMesh.cpp | 90 +-
dolfin/generation/BoxMesh.h | 29 +-
dolfin/generation/CMakeLists.txt | 4 -
dolfin/generation/IntervalMesh.cpp | 2 +-
dolfin/generation/RectangleMesh.cpp | 77 +-
dolfin/generation/RectangleMesh.h | 42 +-
dolfin/generation/SphericalShellMesh.cpp | 2 +-
dolfin/generation/UnitCubeMesh.h | 32 +-
dolfin/generation/UnitDiscMesh.cpp | 2 +-
dolfin/generation/UnitHexMesh.cpp | 111 -
dolfin/generation/UnitHexMesh.h | 56 -
dolfin/generation/UnitQuadMesh.cpp | 96 -
dolfin/generation/UnitQuadMesh.h | 59 -
dolfin/generation/UnitSquareMesh.h | 39 +-
dolfin/generation/UnitTetrahedronMesh.cpp | 2 +-
dolfin/generation/UnitTriangleMesh.cpp | 2 +-
dolfin/generation/dolfin_generation.h | 2 -
dolfin/geometry/BoundingBoxTree2D.h | 6 +-
dolfin/geometry/CGALExactArithmetic.h | 1152 ++
dolfin/geometry/CMakeLists.txt | 17 +-
dolfin/geometry/CollisionDetection.cpp | 1158 --
dolfin/geometry/CollisionDetection.h | 296 -
dolfin/geometry/CollisionPredicates.cpp | 1053 ++
dolfin/geometry/CollisionPredicates.h | 316 +
dolfin/geometry/ConvexTriangulation.cpp | 603 +
dolfin/geometry/ConvexTriangulation.h | 84 +
dolfin/geometry/GenericBoundingBoxTree.cpp | 14 +-
dolfin/geometry/GeometryDebugging.cpp | 151 +
dolfin/geometry/GeometryDebugging.h | 83 +
dolfin/geometry/GeometryPredicates.cpp | 214 +
dolfin/geometry/GeometryPredicates.h | 70 +
dolfin/geometry/GeometryTools.h | 120 +
dolfin/geometry/IntersectionConstruction.cpp | 862 ++
dolfin/geometry/IntersectionConstruction.h | 292 +
dolfin/geometry/IntersectionTriangulation.cpp | 1365 ---
dolfin/geometry/IntersectionTriangulation.h | 214 -
dolfin/geometry/Point.h | 10 +-
dolfin/geometry/SimplexQuadrature.cpp | 3944 ++++++-
dolfin/geometry/SimplexQuadrature.h | 237 +-
dolfin/geometry/dolfin_geometry.h | 1 +
dolfin/geometry/intersect.cpp | 12 +-
dolfin/geometry/predicates.cpp | 2372 ++++
dolfin/geometry/predicates.h | 59 +
dolfin/io/HDF5File.cpp | 18 +-
dolfin/io/HDF5Utility.cpp | 4 +-
dolfin/io/VTKWriter.cpp | 12 +-
dolfin/io/X3DFile.cpp | 4 +-
dolfin/io/XDMFFile.cpp | 43 +-
dolfin/io/XMLMesh.cpp | 6 +-
dolfin/la/EigenVector.cpp | 12 +-
dolfin/la/GenericMatrix.cpp | 4 +-
dolfin/la/GenericMatrix.h | 5 +-
dolfin/la/PETScObject.h | 1 +
dolfin/la/SLEPcEigenSolver.cpp | 41 +-
dolfin/la/SLEPcEigenSolver.h | 3 -
dolfin/log/Logger.cpp | 17 +-
dolfin/log/Logger.h | 5 +-
dolfin/log/log.cpp | 5 +
dolfin/log/log.h | 3 +
dolfin/mesh/BoundaryComputation.cpp | 6 +-
dolfin/mesh/CMakeLists.txt | 1 +
dolfin/{geometry/intersect.cpp => mesh/Cell.cpp} | 32 +-
dolfin/mesh/Cell.h | 33 +-
dolfin/mesh/CellType.cpp | 46 +-
dolfin/mesh/CellType.h | 17 +-
dolfin/mesh/DistributedMeshTools.cpp | 4 +-
dolfin/mesh/DynamicMeshEditor.cpp | 8 +-
dolfin/mesh/Edge.h | 12 +-
dolfin/mesh/Face.h | 12 +-
dolfin/mesh/Facet.h | 12 +-
dolfin/mesh/HexahedronCell.cpp | 14 -
dolfin/mesh/HexahedronCell.h | 17 +-
dolfin/mesh/IntervalCell.cpp | 19 +-
dolfin/mesh/IntervalCell.h | 20 +-
dolfin/mesh/LocalMeshData.cpp | 2 +-
dolfin/mesh/Mesh.cpp | 9 +-
dolfin/mesh/Mesh.h | 27 +-
dolfin/mesh/MeshColoring.cpp | 6 +-
dolfin/mesh/MeshColoring.h | 4 +-
dolfin/mesh/MeshEditor.cpp | 42 +-
dolfin/mesh/MeshEditor.h | 18 -
dolfin/mesh/MeshFunction.h | 8 +-
dolfin/mesh/MeshHierarchy.cpp | 10 +-
dolfin/mesh/MeshQuality.cpp | 10 +-
dolfin/mesh/MeshQuality.h | 4 +-
dolfin/mesh/MeshRenumbering.cpp | 8 +-
dolfin/mesh/MeshTopology.cpp | 5 +-
dolfin/mesh/MeshTopology.h | 4 +-
dolfin/mesh/MeshTransformation.cpp | 18 +-
dolfin/mesh/MeshTransformation.h | 13 +-
dolfin/mesh/MultiMesh.cpp | 1495 ++-
dolfin/mesh/MultiMesh.h | 202 +-
dolfin/mesh/PointCell.cpp | 19 +-
dolfin/mesh/PointCell.h | 19 +-
dolfin/mesh/QuadrilateralCell.cpp | 14 -
dolfin/mesh/QuadrilateralCell.h | 17 +-
dolfin/mesh/TetrahedronCell.cpp | 19 +-
dolfin/mesh/TetrahedronCell.h | 20 +-
dolfin/mesh/TopologyComputation.cpp | 4 +-
dolfin/mesh/TriangleCell.cpp | 19 +-
dolfin/mesh/TriangleCell.h | 18 +-
dolfin/mesh/Vertex.h | 12 +-
dolfin/refinement/BisectionRefinement1D.cpp | 2 +-
dolfin/refinement/LocalMeshCoarsening.cpp | 12 +-
dolfin/refinement/ParallelRefinement.cpp | 14 +-
dolfin/refinement/ParallelRefinement.h | 8 +-
dolfin/refinement/RegularCutRefinement.cpp | 3 +-
dolfin/refinement/refine.cpp | 22 +-
dolfin/refinement/refine.h | 2 +-
dolfin/swig/la/post.i | 13 +-
dolfin/swig/mesh/post.i | 42 +
dolfin/swig/mesh/pre.i | 91 +
dolfin/swig/shared_ptr_classes.i | 1 +
dolfin/swig/typemaps/std_map.i | 27 +
dolfin/swig/typemaps/std_vector.i | 22 +-
python/CMakeLists.txt | 36 +-
python/cmake/FindPETSc4py.cmake | 112 +
python/doc/Makefile | 20 +
python/doc/source/api.rst | 21 +-
python/dolfin/__init__.py | 58 +-
python/dolfin/common/plotting.py | 26 +-
python/dolfin/common/timer.py | 6 +-
python/dolfin/fem/adaptivesolving.py | 12 +-
python/dolfin/fem/assembling.py | 6 +-
python/dolfin/fem/form.py | 5 +-
python/dolfin/fem/formmanipulations.py | 24 +-
python/dolfin/fem/norms.py | 55 +-
python/dolfin/fem/problem.py | 95 +
python/dolfin/fem/projection.py | 13 +-
python/dolfin/fem/solvers.py | 50 +
python/dolfin/fem/solving.py | 147 +-
python/dolfin/function/argument.py | 13 +-
python/dolfin/function/constant.py | 2 +
python/dolfin/function/expression.py | 90 +-
python/dolfin/function/function.py | 61 +-
python/dolfin/function/functionspace.py | 37 +-
python/dolfin/function/jit.py | 9 +-
python/dolfin/function/specialfunctions.py | 91 +-
python/dolfin/jit/__init__.py | 2 +-
python/dolfin/jit/jit.py | 99 +-
python/dolfin/jit/pybind11jit.py | 46 +-
python/dolfin/la/__init__.py | 50 +-
python/dolfin/la/solver.py | 72 +
python/dolfin/mesh/__init__.py | 3 +
python/dolfin/mesh/ale.py | 38 +-
python/dolfin/mesh/meshfunction.py | 59 +-
python/dolfin/mesh/subdomain.py | 29 +-
python/dolfin/mesh/svgtools.py | 32 +-
python/dolfin/multistage/__init__.py | 8 +-
python/dolfin/multistage/factorize.py | 18 +-
python/dolfin/multistage/multistagescheme.py | 203 +-
python/dolfin/multistage/multistagesolvers.py | 1 +
python/dolfin/multistage/rushlarsenschemes.py | 74 +-
python/dolfin/parameter/__init__.py | 13 +-
python/dolfin_utils/meshconvert/meshconvert.py | 2 +-
python/dolfin_utils/test/fixtures.py | 80 +-
python/setup.cfg | 2 +
python/setup.py | 10 +-
.../intersect.cpp => python/src/MPICommWrapper.cpp | 32 +-
.../PETScObject.h => python/src/MPICommWrapper.h | 45 +-
python/src/adaptivity.cpp | 8 +-
python/src/ale.cpp | 2 -
python/src/casters.h | 3 +-
python/src/common.cpp | 165 +-
python/src/dolfin.cpp | 4 +-
python/src/fem.cpp | 46 +-
python/src/function.cpp | 29 +-
python/src/generation.cpp | 101 +-
python/src/geometry.cpp | 31 +-
python/src/io.cpp | 23 +-
python/src/la.cpp | 339 +-
python/src/log.cpp | 23 +-
python/src/mesh.cpp | 112 +-
python/src/mpi_casters.h | 170 +-
python/src/multistage.cpp | 4 +-
python/src/nls.cpp | 22 +-
python/src/parameter.cpp | 21 +-
python/src/petsc_casters.h | 147 +-
scripts/dolfin-order/mesh0.xml.gz | Bin 312 -> 0 bytes
scripts/dolfin-order/mesh1.xml.gz | Bin 247 -> 0 bytes
scripts/dolfin-plot/mesh.xml.gz | Bin 20894 -> 0 bytes
shippable.yml | 22 -
site-packages/dolfin/common/plotting.py | 11 +-
.../dolfin/compilemodules/compilemodule.py | 2 +-
site-packages/dolfin/fem/assembling.py | 16 +-
site-packages/dolfin/fem/form.py | 2 +
site-packages/dolfin/fem/interpolation.py | 22 +-
site-packages/dolfin/fem/norms.py | 109 +-
.../dolfin/functions/multimeshfunction.py | 67 +-
site-packages/dolfin/functions/specialfunctions.py | 89 +-
site-packages/dolfin_utils/meshconvert/abaqus.py | 2 +-
.../dolfin_utils/meshconvert/meshconvert.py | 7 +-
site-packages/dolfin_utils/test/fixtures.py | 26 +-
test/README | 11 -
test/regression/test.py | 2 +
test/unit/CMakeLists.txt | 32 -
test/unit/README | 3 -
test/unit/cpp/CMakeLists.txt | 116 +-
test/unit/cpp/catch/catch.hpp | 11545 +++++++++++++++++++
test/unit/cpp/common/SubSystemsManager.cpp | 29 +-
test/unit/cpp/function/Expression.cpp | 99 +-
test/unit/cpp/geometry/ConvexTriangulation.cpp | 287 +
test/unit/cpp/io/XMLMeshData.cpp | 80 +-
test/unit/cpp/io/XMLMeshValueCollection.cpp | 23 +-
test/unit/cpp/la/LinearOperator.cpp | 111 +-
test/unit/cpp/la/Vector.cpp | 257 +-
test/unit/cpp/main.cpp | 2 +
test/unit/cpp/mesh/Mesh.cpp | 427 +-
test/unit/cpp/mesh/MeshColoring.cpp | 70 +-
test/unit/cpp/mesh/MeshFunction.cpp | 93 +-
test/unit/cpp/mesh/MeshValueCollection.cpp | 245 +-
test/unit/cpp/mesh/MultiMesh.cpp | 91 +
test/unit/cpp/multimesh/MultiMesh.cpp | 307 -
test/unit/cpp/parameter/Parameters.cpp | 56 +-
test/unit/pytest.ini | 3 +
test/unit/python/adaptivity/test_error_control.py | 2 -
test/unit/python/adaptivity/test_time_series.py | 2 -
test/unit/python/ale/test_harmonic_smoothing.py | 6 +-
test/unit/python/common/test_mpi.py | 52 +
test/unit/python/fem/test_assembler.py | 19 +-
test/unit/python/fem/test_dirichlet_bc.py | 16 +-
test/unit/python/fem/test_discrete_operators.py | 6 +-
test/unit/python/fem/test_dofmap.py | 138 +-
test/unit/python/fem/test_dp_assemble.py | 16 +-
test/unit/python/fem/test_finite_element.py | 10 +-
test/unit/python/fem/test_form.py | 14 +-
.../fem/test_interior_facet_integral_sides.py | 4 +-
test/unit/python/fem/test_local_assembler.py | 2 -
test/unit/python/fem/test_local_solver.py | 4 +-
test/unit/python/fem/test_manifolds.py | 2 -
test/unit/python/fem/test_petsc_transfer_matrix.py | 2 -
test/unit/python/fem/test_point_source.py | 8 +-
test/unit/python/fem/test_solving.py | 2 -
.../python/fem/test_symbolic_geometry_assembly.py | 343 +-
test/unit/python/fem/test_system_assembler.py | 18 +-
test/unit/python/fem/test_variational_problem.py | 8 +-
test/unit/python/function/test_constant.py | 16 +-
.../function/test_constrained_function_space.py | 2 -
test/unit/python/function/test_expression.py | 12 +-
test/unit/python/function/test_function.py | 27 +-
.../unit/python/function/test_function_assigner.py | 40 +-
test/unit/python/function/test_function_space.py | 28 +-
.../python/function/test_lagrange_interpolator.py | 2 -
.../function/test_nonmatching_interpolation.py | 2 -
.../unit/python/function/test_special_functions.py | 2 -
.../unit/python/geometry/test_bounding_box_tree.py | 11 +-
.../python/geometry/test_collision_detection.py | 244 +-
.../geometry/test_collision_segment_segment.py | 137 +
test/unit/python/geometry/test_coordinates.py | 2 -
test/unit/python/geometry/test_geometry_issues.py | 163 +
test/unit/python/geometry/test_intersection.py | 8 +-
.../geometry/test_intersection_construction.py | 323 +
.../geometry/test_intersection_triangulation.py | 174 -
test/unit/python/geometry/test_point.py | 11 +-
test/unit/python/graph/test_graph_build.py | 2 -
test/unit/python/io/test_HDF5.py | 12 +-
test/unit/python/io/test_HDF5_attribute.py | 2 -
test/unit/python/io/test_HDF5_series.py | 4 +-
test/unit/python/io/test_SVG.py | 2 -
test/unit/python/io/test_X3D.py | 10 +-
test/unit/python/io/test_XDMF.py | 84 +-
test/unit/python/io/test_XDMF_cell_output.py | 2 +-
test/unit/python/io/test_XMLFunction.py | 3 +-
test/unit/python/io/test_XML_mesh.py | 2 -
test/unit/python/io/test_XML_mesh_function.py | 2 -
.../python/io/test_XML_mesh_value_collection.py | 2 -
test/unit/python/io/test_XML_table.py | 2 -
test/unit/python/io/test_XML_vector.py | 2 -
test/unit/python/io/test_vtk.py | 27 +-
test/unit/python/jit/test_jit.py | 162 +-
test/unit/python/la/test_krylov_solver.py | 2 -
test/unit/python/la/test_la_basic.py | 14 +-
test/unit/python/la/test_linear_operator.py | 8 +-
test/unit/python/la/test_lu_solver.py | 7 +-
test/unit/python/la/test_matrix.py | 64 +-
test/unit/python/la/test_mg_solver.py | 5 +-
test/unit/python/la/test_nullspace.py | 2 -
test/unit/python/la/test_petsc4py.py | 97 +
test/unit/python/la/test_scalar.py | 2 -
test/unit/python/la/test_slepc_solver.py | 15 +-
test/unit/python/la/test_solve.py | 2 -
test/unit/python/la/test_tensor_layout.py | 2 -
test/unit/python/la/test_vector.py | 35 +-
.../{mesh/test_mesh_data.py => log/test_log.py} | 24 +-
test/unit/python/math/test_math.py | 2 -
test/unit/python/mesh/test_boundary_mesh.py | 8 +-
test/unit/python/mesh/test_cell.py | 6 +-
test/unit/python/mesh/test_edge.py | 2 -
test/unit/python/mesh/test_face.py | 2 -
.../mesh/{test_mesh_data.py => test_facet.py} | 26 +-
test/unit/python/mesh/test_ghost_mesh.py | 2 -
.../unit/python/mesh/test_manifold_point_search.py | 7 +-
test/unit/python/mesh/test_mesh.py | 119 +-
test/unit/python/mesh/test_mesh_coloring.py | 0
test/unit/python/mesh/test_mesh_data.py | 2 -
test/unit/python/mesh/test_mesh_editor.py | 2 -
test/unit/python/mesh/test_mesh_function.py | 18 +-
test/unit/python/mesh/test_mesh_iterator.py | 2 -
test/unit/python/mesh/test_mesh_markers.py | 2 -
test/unit/python/mesh/test_mesh_quality.py | 2 -
test/unit/python/mesh/test_mesh_transformation.py | 2 -
.../unit/python/mesh/test_mesh_value_collection.py | 10 +-
.../python/mesh/test_multi_mesh_integration.py | 113 -
.../mesh/test_periodic_boundary_computation.py | 2 -
test/unit/python/mesh/test_sub_domain.py | 32 +-
test/unit/python/mesh/test_sub_mesh.py | 6 +-
.../unit/python/meshconvert/test_mesh_converter.py | 8 +-
test/unit/python/multimesh/test_compression.py | 103 +
test/unit/python/multimesh/test_interface_area.py | 139 +
test/unit/python/multimesh/test_multimesh.py | 58 +-
.../python/multimesh/test_multimesh_cell_types.py | 94 +
.../multimesh/test_multimesh_coefficients.py | 113 +
.../test_multimesh_initialization.py} | 41 +-
.../unit/python/multimesh/test_multimesh_issues.py | 69 +
test/unit/python/multimesh/test_multimesh_solve.py | 125 +
test/unit/python/multimesh/test_volume.py | 206 +
test/unit/python/multistage/test_RK_solver.py | 8 +-
.../multistage/test_point_integral_solver.py | 2 -
test/unit/python/nls/test_PETScSNES_solver.py | 2 -
test/unit/python/nls/test_PETScTAOSolver.py | 2 -
.../python/nls/test_TAO_linear_bound_solver.py | 2 -
.../test_solve_result_against_reference.py | 5 +-
test/unit/python/parameter/test_parameters.py | 4 +-
.../test_assembly_derivatives.py | 2 -
.../ufl-jit-assemble-chain/test_form_operations.py | 2 -
417 files changed, 32479 insertions(+), 8938 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 22e6af1..a9a3b68 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,26 +11,22 @@ jobs:
- checkout
- run:
name: Install/update dependencies # Install with sudo as tests not run as superuser in circleci/python
- command: |
- sudo pip3 install pytest six --upgrade
- - run:
- name: Install FIAT
- command: pip3 install git+https://bitbucket.org/fenics-project/fiat.git@master
- - run:
- name: Install UFL
- command: pip3 install git+https://bitbucket.org/fenics-project/ufl.git@master
+ command: sudo pip3 install pytest pytest-xdist six flake8 --upgrade
- run:
- name: Install Dijitso
- command: pip3 install git+https://bitbucket.org/fenics-project/dijitso.git@master
- - run:
- name: Install Instant
- command: pip3 install git+https://bitbucket.org/fenics-project/instant.git@master
+ name: Install FEniCS dependencies
+ command: |
+ if [ "${CIRCLE_BRANCH}" == "next" ] ; then export DEP_BRANCH_NAME="next" ; else export DEP_BRANCH_NAME="master" ; fi
+ pip3 install git+https://bitbucket.org/fenics-project/fiat.git@"${DEP_BRANCH_NAME}"
+ pip3 install git+https://bitbucket.org/fenics-project/ufl.git@"${DEP_BRANCH_NAME}"
+ pip3 install git+https://bitbucket.org/fenics-project/dijitso.git@"${DEP_BRANCH_NAME}"
+ pip3 install git+https://bitbucket.org/fenics-project/instant.git@"${DEP_BRANCH_NAME}"
+ pip3 install git+https://bitbucket.org/fenics-project/ffc.git@"${DEP_BRANCH_NAME}"
- run:
- name: Install FFC
- command: pip3 install git+https://bitbucket.org/fenics-project/ffc.git@master
+ name: Flake8 checks on pybind11 Python code
+ command: python3 -m flake8 python/dolfin
- run:
name: Configure DOLFIN
- command: mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DDOLFIN_ENABLE_TESTING=ON
+ command: mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Developer -DDOLFIN_ENABLE_TESTING=ON
- run:
name: Build DOLFIN and install
command: cd build && make install -j2
@@ -39,7 +35,7 @@ jobs:
command: cd build && make run_unittests_cpp
- run:
name: Run Python unit tests (SWIG, serial)
- command: source /usr/local/share/dolfin/dolfin.conf && python3 -m pytest test/unit/python/
+ command: source /usr/local/share/dolfin/dolfin.conf && python3 -m pytest -n 2 test/unit/python/
- run:
name: Run Python unit tests (SWIG, MPI)
command: source /usr/local/share/dolfin/dolfin.conf && mpirun -n 3 python3 -m pytest test/unit/python/
@@ -65,7 +61,7 @@ jobs:
pip3 -v install . --user
- run:
name: Run unit tests (pybind11, serial)
- command: python3 -m pytest test/unit/python/
+ command: python3 -m pytest -n 2 test/unit/python/
- run:
name: Run unit tests (pybind11, MPI parallel)
command: mpirun -n 3 python3 -m pytest test/unit/python/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4e18344..a744382 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.5)
# Set project name and version number
project(DOLFIN)
-set(DOLFIN_VERSION_RELEASE 0)
+set(DOLFIN_VERSION_RELEASE 1)
set(DOLFIN_VERSION_MAJOR "2017")
set(DOLFIN_VERSION_MINOR "2")
set(DOLFIN_VERSION_MICRO "0")
@@ -85,11 +85,11 @@ option(DOLFIN_DEPRECATION_ERROR "Turn deprecation warnings into errors." OFF)
option(DOLFIN_ENABLE_BENCHMARKS "Enable benchmark programs." OFF)
option(DOLFIN_ENABLE_CODE_COVERAGE "Enable code coverage." OFF)
option(DOLFIN_ENABLE_DOCS "Enable generation of documentation." ON)
-option(DOLFIN_ENABLE_GTEST "Enable C++ unit tests with Google Test if DOLFIN_ENABLE_TESTING is true (requires Internet connection to download Google Test when first configured)." ON)
option(DOLFIN_ENABLE_TESTING "Enable testing." OFF)
option(DOLFIN_IGNORE_PETSC4PY_VERSION "Ignore version of PETSc4py." OFF)
option(DOLFIN_SKIP_BUILD_TESTS "Skip build tests for testing usability of dependency packages." OFF)
option(DOLFIN_WITH_LIBRARY_VERSION "Build with library version information." ON)
+option(DOLFIN_ENABLE_GEOMETRY_DEBUGGING "Enable geometry debugging (developers only; requires libcgal-dev and libcgal-qt5-dev)." OFF)
add_feature_info(BUILD_SHARED_LIBS BUILD_SHARED_LIBS "Build DOLFIN with shared libraries.")
add_feature_info(CMAKE_USE_RELATIVE_PATHS CMAKE_USE_RELATIVE_PATHS "Use relative paths in makefiles and projects.")
@@ -97,12 +97,12 @@ add_feature_info(DOLFIN_AUTO_DETECT_MPI DOLFIN_AUTO_DETECT_MPI "Detect MPI autom
add_feature_info(DOLFIN_ENABLE_CODE_COVERAGE DOLFIN_ENABLE_CODE_COVERAGE "Enable code coverage.")
add_feature_info(DOLFIN_WITH_LIBRARY_VERSION DOLFIN_WITH_LIBRARY_VERSION "Build with library version information.")
add_feature_info(DOLFIN_ENABLE_TESTING DOLFIN_ENABLE_TESTING "Enable testing.")
-add_feature_info(DOLFIN_ENABLE_GTEST DOLFIN_ENABLE_GTEST "Enable C++ unit tests with Google Test if DOLFIN_ENABLE_TESTING is true (requires Internet connection to download Google Test when first configured).")
add_feature_info(DOLFIN_ENABLE_BENCHMARKS DOLFIN_ENABLE_BENCHMARKS "Enable benchmark programs.")
add_feature_info(DOLFIN_ENABLE_DOCS DOLFIN_ENABLE_DOCS "Enable generation of documentation.")
add_feature_info(DOLFIN_SKIP_BUILD_TESTS DOLFIN_SKIP_BUILD_TESTS "Skip build tests for testing usability of dependency packages.")
add_feature_info(DOLFIN_DEPRECATION_ERROR DOLFIN_DEPRECATION_ERROR "Turn deprecation warnings into errors.")
add_feature_info(DOLFIN_IGNORE_PETSC4PY_VERSION DOLFIN_IGNORE_PETSC4PY_VERSION "Ignore version of PETSc4py.")
+add_feature_info(DOLFIN_ENABLE_GEOMETRY_DEBUGGING DOLFIN_ENABLE_GEOMETRY_DEBUGGING "Enable geometry debugging.")
# Add shared library paths so shared libs in non-system paths are found
option(CMAKE_INSTALL_RPATH_USE_LINK_PATH "Add paths to linker search and installed rpath." ON)
@@ -283,7 +283,7 @@ set_package_properties(Boost PROPERTIES TYPE REQUIRED
URL "http://www.boost.org")
# Check for required package Eigen3
-find_package(Eigen3 3.2.8 REQUIRED)
+find_package(Eigen3 3.2.90 REQUIRED)
set_package_properties(Eigen3 PROPERTIES TYPE REQUIRED
DESCRIPTION "Lightweight C++ template library for linear algebra"
URL "http://eigen.tuxfamily.org")
@@ -294,6 +294,7 @@ set_package_properties(Eigen3 PROPERTIES TYPE REQUIRED
# Note: Check for Python interpreter even when Python is disabled
# because it is used to get the installation path for
# dolfin_utils
+
if (DOLFIN_USE_PYTHON3)
find_package(PythonInterp 3)
else()
@@ -479,7 +480,10 @@ if (DOLFIN_ENABLE_HDF5)
if (NOT DEFINED ENV{HDF5_ROOT})
set(ENV{HDF5_ROOT} "$ENV{HDF5_DIR}")
endif()
- set(HDF5_PREFER_PARALLEL TRUE)
+ set(HDF5_PREFER_PARALLEL FALSE)
+ if (DOLFIN_ENABLE_MPI)
+ set(HDF5_PREFER_PARALLEL TRUE)
+ endif()
find_package(HDF5 COMPONENTS C)
set_package_properties(HDF5 PROPERTIES TYPE OPTIONAL
DESCRIPTION "Hierarchical Data Format 5 (HDF5)"
@@ -579,6 +583,23 @@ if (DOLFIN_ENABLE_DOCS AND PYTHON_FOUND)
PURPOSE "Needed to build documentation")
endif()
+# Check for geometry debugging
+if (DOLFIN_ENABLE_GEOMETRY_DEBUGGING)
+ message(STATUS "Enabling geometry debugging")
+ find_package(CGAL REQUIRED)
+ find_package(GMP REQUIRED)
+ find_package(MPFR REQUIRED)
+ if (NOT CGAL_FOUND)
+ message(FATAL_ERROR "Unable to find package CGAL required for DOLFIN_ENABLE_GEOMETRY_DEBUGGING")
+ endif()
+ if (NOT GMP_FOUND)
+ message(FATAL_ERROR "Unable to find package GMP required for DOLFIN_ENABLE_GEOMETRY_DEBUGGING")
+ endif()
+ if (NOT MPFR_FOUND)
+ message(FATAL_ERROR "Unable to find package MPFR required for DOLFIN_ENABLE_GEOMETRY_DEBUGGING")
+ endif()
+endif()
+
#------------------------------------------------------------------------------
# Print summary of found and not found optional packages
@@ -962,24 +983,13 @@ endif()
if (DOLFIN_ENABLE_TESTING)
- # Google Test needs to be downloaded and compiled by CMake, so we
- # handle it as a special case.
- if (DOLFIN_ENABLE_GTEST)
- # Add sub directory to build GoogleTest
- add_subdirectory(test/unit)
-
- # Add target "unittests_cpp", but do not add to default target
- add_subdirectory(test/unit/cpp EXCLUDE_FROM_ALL)
+ # Add target "unittests_cpp", but do not add to default target
+ add_subdirectory(test/unit/cpp EXCLUDE_FROM_ALL)
- # Add target "run_unittests_cpp" for running only C++ unit tests
- add_custom_target(run_unittests_cpp
- COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test/unit/cpp/unittests_cpp
- DEPENDS copy_data_test_demo unittests_cpp)
- else()
- # Add dummy targets for C++ unit tests
- add_custom_target(unittests_cpp)
- add_custom_target(run_unittests_cpp)
- endif()
+ # Add target "run_unittests_cpp" for running only C++ unit tests
+ add_custom_target(run_unittests_cpp
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test/unit/cpp/unittests_cpp
+ DEPENDS copy_data_test_demo unittests_cpp)
# FIXME: remove this buildbot updated to call unittests_cpp
# Add alias for unittests_cpp
diff --git a/ChangeLog.rst b/ChangeLog.rst
index 0cf576d..9c88be7 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -4,6 +4,12 @@ Change log
2017.2.0 (2017-09-25)
---------------------
+- Remove ``UnitQuadMesh`` and ``UnitHexMesh``. Now use ``UnitSquareMesh`` and
+ ``UnitCubeMesh`` with cell type qualifiers.
+- Remove ``MeshEditor::open`` without cell type. Now you must explicitly
+ specify CellType when opening a ``Mesh`` with ``MeshEditor``.
+- Rename ``Mesh::size_global`` to ``Mesh::num_entities_global``.
+- Remove ``Mesh::size``. Use ``Mesh::num_entities`` instead.
- Improved mesh topology computation performance.
- Remove excessive calls to MPI init. It may now be necessary in some
cases to explicitly intialise MPI.
@@ -38,6 +44,13 @@ Change log
elements.
- Updates for some demos and tests to show usage of quadrilateral and
hexahedral meshes.
+- Deprecate ``CellSize`` (equivalent to ``2*Circumradius``)
+ in favour of new ``CellDiameter``; add ``MinCellEdgeLength``
+ and ``MaxCellEdgeLength``
+- Deprecate subclassing of ``Expression`` in Python; new Python class
+ ``UserExpression`` introduced for user overloads
+- Deprecate ``VertexFunction``, ``EdgeFunction``, ``FaceFunction``,
+ ``FacetFunction``, ``CellFunction``; use ``MeshFunction`` instead
2017.1.0 (2017-05-09)
diff --git a/bench/fem/jit/python/bench_fem_jit_python b/bench/fem/jit/python/bench_fem_jit_python
index 7e94bf6..a411c81 100755
--- a/bench/fem/jit/python/bench_fem_jit_python
+++ b/bench/fem/jit/python/bench_fem_jit_python
@@ -50,7 +50,7 @@ p1 = Function(Q)
W = Function(DGv)
nu = Constant(0.1)
k = Constant(0.1)
-h = CellSize(mesh)
+h = 2*Circumradius(mesh)
d1 = h
d2 = 2.0*h
diff --git a/cmake/modules/FindGMP.cmake b/cmake/modules/FindGMP.cmake
new file mode 100644
index 0000000..820f402
--- /dev/null
+++ b/cmake/modules/FindGMP.cmake
@@ -0,0 +1,19 @@
+# Try to find the GMP librairies
+# GMP_FOUND - system has GMP lib
+# GMP_INCLUDE_DIR - the GMP include directory
+# GMP_LIBRARIES - Libraries needed to use GMP
+
+if (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
+ # Already in cache, be silent
+ set(GMP_FIND_QUIETLY TRUE)
+endif (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
+
+find_path(GMP_INCLUDE_DIR NAMES gmp.h )
+find_library(GMP_LIBRARIES NAMES gmp libgmp )
+find_library(GMPXX_LIBRARIES NAMES gmpxx libgmpxx )
+MESSAGE(STATUS "GMP libs: " ${GMP_LIBRARIES} " " ${GMPXX_LIBRARIES} )
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES)
+
+mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES)
diff --git a/cmake/modules/FindMPFR.cmake b/cmake/modules/FindMPFR.cmake
new file mode 100644
index 0000000..094aa2d
--- /dev/null
+++ b/cmake/modules/FindMPFR.cmake
@@ -0,0 +1,83 @@
+# Try to find the MPFR library
+# See http://www.mpfr.org/
+#
+# This module supports requiring a minimum version, e.g. you can do
+# find_package(MPFR 2.3.0)
+# to require version 2.3.0 to newer of MPFR.
+#
+# Once done this will define
+#
+# MPFR_FOUND - system has MPFR lib with correct version
+# MPFR_INCLUDES - the MPFR include directory
+# MPFR_LIBRARIES - the MPFR library
+# MPFR_VERSION - MPFR version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2010 Jitse Niesen, <jitse at maths.leeds.ac.uk>
+# Redistribution and use is allowed according to the terms of the BSD license.
+
+# Set MPFR_INCLUDES
+
+find_path(MPFR_INCLUDES
+ NAMES
+ mpfr.h
+ PATHS
+ $ENV{GMPDIR}
+ ${INCLUDE_INSTALL_DIR}
+)
+
+# Set MPFR_FIND_VERSION to 1.0.0 if no minimum version is specified
+
+if(NOT MPFR_FIND_VERSION)
+ if(NOT MPFR_FIND_VERSION_MAJOR)
+ set(MPFR_FIND_VERSION_MAJOR 1)
+ endif(NOT MPFR_FIND_VERSION_MAJOR)
+ if(NOT MPFR_FIND_VERSION_MINOR)
+ set(MPFR_FIND_VERSION_MINOR 0)
+ endif(NOT MPFR_FIND_VERSION_MINOR)
+ if(NOT MPFR_FIND_VERSION_PATCH)
+ set(MPFR_FIND_VERSION_PATCH 0)
+ endif(NOT MPFR_FIND_VERSION_PATCH)
+
+ set(MPFR_FIND_VERSION "${MPFR_FIND_VERSION_MAJOR}.${MPFR_FIND_VERSION_MINOR}.${MPFR_FIND_VERSION_PATCH}")
+endif(NOT MPFR_FIND_VERSION)
+
+
+if(MPFR_INCLUDES)
+
+ # Set MPFR_VERSION
+
+ file(READ "${MPFR_INCLUDES}/mpfr.h" _mpfr_version_header)
+
+ string(REGEX MATCH "define[ \t]+MPFR_VERSION_MAJOR[ \t]+([0-9]+)" _mpfr_major_version_match "${_mpfr_version_header}")
+ set(MPFR_MAJOR_VERSION "${CMAKE_MATCH_1}")
+ string(REGEX MATCH "define[ \t]+MPFR_VERSION_MINOR[ \t]+([0-9]+)" _mpfr_minor_version_match "${_mpfr_version_header}")
+ set(MPFR_MINOR_VERSION "${CMAKE_MATCH_1}")
+ string(REGEX MATCH "define[ \t]+MPFR_VERSION_PATCHLEVEL[ \t]+([0-9]+)" _mpfr_patchlevel_version_match "${_mpfr_version_header}")
+ set(MPFR_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}")
+
+ set(MPFR_VERSION ${MPFR_MAJOR_VERSION}.${MPFR_MINOR_VERSION}.${MPFR_PATCHLEVEL_VERSION})
+
+ # Check whether found version exceeds minimum version
+
+ if(${MPFR_VERSION} VERSION_LESS ${MPFR_FIND_VERSION})
+ set(MPFR_VERSION_OK FALSE)
+ message(STATUS "MPFR version ${MPFR_VERSION} found in ${MPFR_INCLUDES}, "
+ "but at least version ${MPFR_FIND_VERSION} is required")
+ else(${MPFR_VERSION} VERSION_LESS ${MPFR_FIND_VERSION})
+ set(MPFR_VERSION_OK TRUE)
+ endif(${MPFR_VERSION} VERSION_LESS ${MPFR_FIND_VERSION})
+
+endif(MPFR_INCLUDES)
+
+# Set MPFR_LIBRARIES
+
+find_library(MPFR_LIBRARIES mpfr PATHS $ENV{GMPDIR} ${LIB_INSTALL_DIR})
+
+# Epilogue
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(MPFR DEFAULT_MSG
+ MPFR_INCLUDES MPFR_LIBRARIES MPFR_VERSION_OK)
+mark_as_advanced(MPFR_INCLUDES MPFR_LIBRARIES)
diff --git a/cmake/templates/dolfin.conf.in b/cmake/templates/dolfin.conf.in
index da5d33f..2e5be4f 100644
--- a/cmake/templates/dolfin.conf.in
+++ b/cmake/templates/dolfin.conf.in
@@ -7,8 +7,5 @@ export PKG_CONFIG_PATH=@CMAKE_INSTALL_PREFIX@/@DOLFIN_PKGCONFIG_DIR@:$PKG_CONFIG
export PYTHONPATH=@DOLFIN_INSTALL_PYTHON_MODULE_DIR@:@DOLFIN_INSTALL_PYTHON_PURE_MODULE_DIR@:$PYTHONPATH
export MANPATH=@CMAKE_INSTALL_PREFIX@/@DOLFIN_MAN_DIR@:$MANPATH
-# Helper to find VTK if not installed in a system path
-export @OS_LIBRARY_PATH_NAME@=@VTK_INSTALL_PREFIX@/lib:$@OS_LIBRARY_PATH_NAME@
-
# Special Mac variables
export DYLD_FRAMEWORK_PATH=/opt/local/Library/Frameworks:$DYLD_FRAMEWORK_PATH
diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt
index 44fa7cd..c2b41af 100644
--- a/demo/CMakeLists.txt
+++ b/demo/CMakeLists.txt
@@ -67,8 +67,8 @@ if (DOLFIN_FOUND)
add_subdirectory(undocumented/mesh-quality/cpp)
#add_subdirectory(undocumented/mixed-poisson-sphere/cpp)
#add_subdirectory(undocumented/mplot/cpp)
- #add_subdirectory(undocumented/multimesh/cpp)
add_subdirectory(undocumented/multimesh-poisson/cpp)
+ #add_subdirectory(undocumented/multimesh-quadrature/cpp)
add_subdirectory(undocumented/multimesh-stokes/cpp)
add_subdirectory(undocumented/meshfunction-refinement/cpp)
#add_subdirectory(undocumented/multistage-solver/cpp)
diff --git a/demo/documented/bcs/python/demo_bcs.py b/demo/documented/bcs/python/demo_bcs.py
index f36be4a..6485199 100644
--- a/demo/documented/bcs/python/demo_bcs.py
+++ b/demo/documented/bcs/python/demo_bcs.py
@@ -46,7 +46,7 @@ u2 = Constant(2.0)
u3 = Constant(3.0)
if has_pybind11():
- markers = FacetFunction("size_t", mesh, 9999)
+ markers = MeshFunction("size_t", mesh, mesh.topology().dim()-1, 9999)
for (f, v) in mesh.domains().markers(mesh.topology().dim()-1).items():
markers[f] = v
diff --git a/demo/documented/biharmonic/cpp/Biharmonic.ufl.rst b/demo/documented/biharmonic/cpp/Biharmonic.ufl.rst
index 13b69d8..8033eb3 100644
--- a/demo/documented/biharmonic/cpp/Biharmonic.ufl.rst
+++ b/demo/documented/biharmonic/cpp/Biharmonic.ufl.rst
@@ -24,7 +24,7 @@ regenerating the code. ::
# Normal component, mesh size and right-hand side
n = FacetNormal(triangle)
- h = 2.0*Circumradius(triangle)
+ h = CellDiameter(triangle)
h_avg = (h('+') + h('-'))/2
# Parameters
diff --git a/demo/documented/biharmonic/python/demo_biharmonic.py.rst b/demo/documented/biharmonic/python/demo_biharmonic.py.rst
index fe1e836..96d589b 100644
--- a/demo/documented/biharmonic/python/demo_biharmonic.py.rst
+++ b/demo/documented/biharmonic/python/demo_biharmonic.py.rst
@@ -157,7 +157,7 @@ the penalty parameter ``alpha``. The penalty parameters is made a
can be changed without needing to regenerate code. ::
# Define normal component, mesh size and right-hand side
- h = CellSize(mesh)
+ h = CellDiameter(mesh)
h_avg = (h('+') + h('-'))/2.0
n = FacetNormal(mesh)
f = Source(degree=2)
diff --git a/demo/documented/cahn-hilliard/cpp/documentation.rst b/demo/documented/cahn-hilliard/cpp/documentation.rst
index fb1db3f..6f23b69 100644
--- a/demo/documented/cahn-hilliard/cpp/documentation.rst
+++ b/demo/documented/cahn-hilliard/cpp/documentation.rst
@@ -313,7 +313,8 @@ A mesh is then created with 97 (96 + 1) vertices in each direction:
.. code-block:: c++
// Mesh
- auto mesh = std::make_shared<Mesh>(UnitQuadMesh::create(96, 96));
+ auto mesh = std::make_shared<Mesh>(
+ UnitSquareMesh::create({{96, 96}}, CellType::Type::quadrilateral));
A set of constants (required for the assembling of the forms) and two
scalars (to be used in the time stepping) are then declared:
diff --git a/demo/documented/cahn-hilliard/cpp/main.cpp b/demo/documented/cahn-hilliard/cpp/main.cpp
index c21605a..afa1156 100644
--- a/demo/documented/cahn-hilliard/cpp/main.cpp
+++ b/demo/documented/cahn-hilliard/cpp/main.cpp
@@ -78,7 +78,9 @@ int main(int argc, char* argv[])
dolfin::init(argc, argv);
// Mesh
- auto mesh = std::make_shared<Mesh>(UnitQuadMesh::create(96, 96));
+ auto mesh = std::make_shared<Mesh>(
+ UnitSquareMesh::create({{96, 96}}, CellType::Type::quadrilateral));
+
// Create function space and forms, depending on spatial dimension
// of the mesh
diff --git a/demo/documented/cahn-hilliard/python/demo_cahn-hilliard.py.rst b/demo/documented/cahn-hilliard/python/demo_cahn-hilliard.py.rst
index 8a1fba7..9a816fe 100644
--- a/demo/documented/cahn-hilliard/python/demo_cahn-hilliard.py.rst
+++ b/demo/documented/cahn-hilliard/python/demo_cahn-hilliard.py.rst
@@ -195,7 +195,7 @@ created, and on this mesh a :py:class:`FunctionSpace
a pair of linear Lagrangian elements. ::
# Create mesh and build function space
- mesh = UnitQuadMesh.create(96, 96)
+ mesh = UnitSquareMesh.create(96, 96, CellType.Type_quadrilateral)
P1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1)
ME = FunctionSpace(mesh, P1*P1)
diff --git a/demo/documented/neumann-poisson/cpp/main.cpp b/demo/documented/neumann-poisson/cpp/main.cpp
index fd3646d..c7360ef 100644
--- a/demo/documented/neumann-poisson/cpp/main.cpp
+++ b/demo/documented/neumann-poisson/cpp/main.cpp
@@ -70,7 +70,8 @@ class Flux : public Expression
int main()
{
// Create mesh and function space
- auto mesh = std::make_shared<Mesh>(UnitQuadMesh::create(64, 64));
+ auto mesh = std::make_shared<Mesh>(
+ UnitSquareMesh::create({{64, 64}}, CellType::Type::quadrilateral));
auto V = std::make_shared<Poisson::FunctionSpace>(mesh);
// Define variational problem
diff --git a/demo/documented/neumann-poisson/python/demo_neumann-poisson.py.rst b/demo/documented/neumann-poisson/python/demo_neumann-poisson.py.rst
index 1d81973..560577c 100644
--- a/demo/documented/neumann-poisson/python/demo_neumann-poisson.py.rst
+++ b/demo/documented/neumann-poisson/python/demo_neumann-poisson.py.rst
@@ -22,12 +22,12 @@ First, the :py:mod:`dolfin` module is imported: ::
from dolfin import *
We proceed by defining a mesh of the domain. We use a built-in mesh
-provided by the class :py:class:`UnitQuadMesh
-<dolfin.cpp.UnitQuadMesh>`. In order to create a mesh consisting of
+provided by the class :py:class:`UnitSquareMesh
+<dolfin.cpp.UnitSquareMesh>`. In order to create a mesh consisting of
:math:`64 \times 64` squares, we do as follows: ::
# Create mesh
- mesh = UnitQuadMesh.create(64, 64)
+ mesh = UnitSquareMesh.create(64, 64, CellType.Type_quadrilateral)
Next, we need to define the function space. ::
diff --git a/demo/documented/poisson/cpp/main.cpp.rst b/demo/documented/poisson/cpp/main.cpp.rst
index 90e3de9..daf1828 100644
--- a/demo/documented/poisson/cpp/main.cpp.rst
+++ b/demo/documented/poisson/cpp/main.cpp.rst
@@ -144,7 +144,8 @@ the form file) defined relative to this mesh, we do as follows
int main()
{
// Create mesh and function space
- auto mesh = std::make_shared<Mesh>(UnitSquareMesh::create({{32, 32}}));
+ auto mesh = std::make_shared<Mesh>(
+ UnitSquareMesh::create({{32, 32}}, CellType::Type::triangle));
auto V = std::make_shared<Poisson::FunctionSpace>(mesh);
Now, the Dirichlet boundary condition (:math:`u = 0`) can be created
diff --git a/demo/documented/stokes-iterative/cpp/main.cpp b/demo/documented/stokes-iterative/cpp/main.cpp
index de336b0..69d1fec 100644
--- a/demo/documented/stokes-iterative/cpp/main.cpp
+++ b/demo/documented/stokes-iterative/cpp/main.cpp
@@ -100,7 +100,8 @@ int main()
}
// Create mesh
- auto mesh = std::make_shared<Mesh>(UnitHexMesh::create(16, 16, 16));
+ auto mesh = std::make_shared<Mesh>(
+ UnitCubeMesh::create({{16, 16, 16}}, CellType::Type::hexahedron));
// Create function space and subspaces
auto W = std::make_shared<Stokes::FunctionSpace>(mesh);
diff --git a/demo/documented/stokes-iterative/python/demo_stokes-iterative.py.rst b/demo/documented/stokes-iterative/python/demo_stokes-iterative.py.rst
index f908f45..8e208e4 100644
--- a/demo/documented/stokes-iterative/python/demo_stokes-iterative.py.rst
+++ b/demo/documented/stokes-iterative/python/demo_stokes-iterative.py.rst
@@ -57,8 +57,8 @@ If not available, costly QMR method is choosen. ::
"Krylov subspace method. Terminating.")
exit()
-Next, we define the mesh (a :py:class:`UnitHexMesh
-<dolfin.cpp.UnitHexMesh>`) and a mixed finite element ``TH``.
+Next, we define the mesh (a :py:class:`UnitCubeMesh
+<dolfin.cpp.UnitCubeMesh>`) and a mixed finite element ``TH``.
Then we build a :py:class:`FunctionSpace
<dolfin.functions.functionspace.FunctionSpace>` on this element.
(This mixed finite element space is known as the
@@ -66,7 +66,7 @@ Taylor--Hood elements and is a stable, standard element pair for the
Stokes equations.) ::
# Load mesh
- mesh = UnitHexMesh.create(16, 16, 16)
+ mesh = UnitCubeMesh.create(16, 16, 16, CellType.Type_hexahedron)
# Build function space
P2 = VectorElement("Lagrange", mesh.ufl_cell(), 2)
diff --git a/demo/documented/subdomains-poisson/python/demo_subdomains-poisson.py b/demo/documented/subdomains-poisson/python/demo_subdomains-poisson.py
index a4c6600..965d622 100644
--- a/demo/documented/subdomains-poisson/python/demo_subdomains-poisson.py
+++ b/demo/documented/subdomains-poisson/python/demo_subdomains-poisson.py
@@ -56,12 +56,12 @@ obstacle = Obstacle()
mesh = UnitSquareMesh(64, 64)
# Initialize mesh function for interior domains
-domains = CellFunction("size_t", mesh)
+domains = MeshFunction("size_t", mesh, mesh.topology().dim())
domains.set_all(0)
obstacle.mark(domains, 1)
# Initialize mesh function for boundary domains
-boundaries = FacetFunction("size_t", mesh)
+boundaries = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
boundaries.set_all(0)
left.mark(boundaries, 1)
top.mark(boundaries, 2)
diff --git a/demo/documented/subdomains-poisson/python/documentation.rst b/demo/documented/subdomains-poisson/python/documentation.rst
index 85d8341..dca72a7 100644
--- a/demo/documented/subdomains-poisson/python/documentation.rst
+++ b/demo/documented/subdomains-poisson/python/documentation.rst
@@ -84,16 +84,8 @@ We next define a mesh of the domain:
The above subdomains are defined with the sole purpose of populating
mesh functions. (For more complicated geometries, the mesh functions
-would typically be provided by other means.) The classes
-:py:class:`CellFunction <dolfin.cpp.CellFunction>` and
-:py:class:`FacetFunction <dolfin.cpp.FacetFunction>` are specialized
-versions of the more general :py:class:`MeshFunction
-<dolfin.cpp.MeshFunction>`. :py:class:`CellFunction
-<dolfin.cpp.CellFunction>` represents a function with a value for each
-cell of a mesh, while :py:class:`FacetFunction
-<dolfin.cpp.FacetFunction>` represents a function with a value for
-each facet. We define a :py:class:`CellFunction
-<dolfin.cpp.CellFunction>` to indicate which cells that correspond to
+would typically be provided by other means.) We define a :py:class:`MeshFunction
+<dolfin.cpp.MeshFunction>` over the mesh cells to indicate which cells that correspond to
the different interior subregions :math:`\Omega_0` and
:math:`\Omega_1`. Those in the interior rectangle will be tagged by
`1`, while the remainder is tagged by `0`. We can set all the values
@@ -108,20 +100,21 @@ value):
.. code-block:: python
# Initialize mesh function for interior domains
- domains = CellFunction("size_t", mesh)
+ domains = MeshFunction("size_t", mesh, mesh.topology().dim())
domains.set_all(0)
obstacle.mark(domains, 1)
-We can do the same for the boundaries using a :py:class:`FacetFunction
-<dolfin.cpp.FacetFunction>`. We first tag all the edges by ``0``, then
+We can do the same for the boundaries using a :py:class:`MeshFunction
+<dolfin.cpp.MeshFunction>` defined ove the topological dimension `
+mesh.topology().dim()-1`. We first tag all the edges by ``0``, then
the edges on the left by ``1``, on the top by ``2``, on the right by
``3`` and on the bottom by ``4``:
.. code-block:: python
# Initialize mesh function for boundary domains
- boundaries = FacetFunction("size_t", mesh)
+ boundaries = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
boundaries.set_all(0)
left.mark(boundaries, 1)
top.mark(boundaries, 2)
diff --git a/demo/documented/tensor-weighted-poisson/python/demo_tensor-weighted-poisson.py b/demo/documented/tensor-weighted-poisson/python/demo_tensor-weighted-poisson.py
index e343edd..cb50dfa 100644
--- a/demo/documented/tensor-weighted-poisson/python/demo_tensor-weighted-poisson.py
+++ b/demo/documented/tensor-weighted-poisson/python/demo_tensor-weighted-poisson.py
@@ -104,7 +104,7 @@ public:
PYBIND11_MODULE(SIGNATURE, m)
{
py::class_<Conductivity, std::shared_ptr<Conductivity>, dolfin::Expression>
- (m, "Conductivity", py::dynamic_attr())
+ (m, "Conductivity")
.def(py::init<>())
.def_readwrite("c00", &Conductivity::c00)
.def_readwrite("c01", &Conductivity::c01)
@@ -112,16 +112,9 @@ PYBIND11_MODULE(SIGNATURE, m)
}
"""
- class UserConductivity(UserExpression):
- def value_shape(self):
- return (3,)
-
- c = UserConductivity(degree=0)
- cc = compile_cpp_code(conductivity_code).Conductivity()
- cc.c00 = c00
- cc.c01 = c01
- cc.c11 = c11
- c._cpp_object = cc
+
+ c = CompiledExpression(compile_cpp_code(conductivity_code).Conductivity(),
+ c00=c00, c01=c01, c11=c11, degree=0)
else:
conductivity_code = """
diff --git a/demo/undocumented/advection-diffusion/python/demo_advection-diffusion.py b/demo/undocumented/advection-diffusion/python/demo_advection-diffusion.py
index 1779548..d80ef50 100644
--- a/demo/undocumented/advection-diffusion/python/demo_advection-diffusion.py
+++ b/demo/undocumented/advection-diffusion/python/demo_advection-diffusion.py
@@ -37,7 +37,7 @@ def boundary_value(n):
# Load mesh and subdomains
mesh = Mesh("../dolfin_fine.xml.gz")
sub_domains = MeshFunction("size_t", mesh, "../dolfin_fine_subdomains.xml.gz");
-h = CellSize(mesh)
+h = CellDiameter(mesh)
# Create FunctionSpaces
Q = FunctionSpace(mesh, "CG", 1)
diff --git a/demo/undocumented/auto-adaptive-navier-stokes/cpp/main.cpp b/demo/undocumented/auto-adaptive-navier-stokes/cpp/main.cpp
index 77559ef..ca44a1c 100644
--- a/demo/undocumented/auto-adaptive-navier-stokes/cpp/main.cpp
+++ b/demo/undocumented/auto-adaptive-navier-stokes/cpp/main.cpp
@@ -68,7 +68,7 @@ int main()
// Define boundary condition
auto u0 = std::make_shared<Constant>(0.0, 0.0);
Noslip noslip;
- auto noslip_markers = std::make_shared<FacetFunction<std::size_t>>(mesh, 1);
+ auto noslip_markers = std::make_shared<MeshFunction<std::size_t>>(mesh, mesh->topology().dim()-1, 1);
noslip.mark(*noslip_markers, 0);
auto W0 = W->sub(0);
auto bc = std::make_shared<DirichletBC>(W0, u0, noslip_markers, 0);
@@ -90,7 +90,7 @@ int main()
auto M = std::make_shared<AdaptiveNavierStokes::GoalFunctional>(mesh);
M->w = w;
Outflow outflow;
- auto outflow_markers = std::make_shared<FacetFunction<std::size_t>>(mesh, 1);
+ auto outflow_markers = std::make_shared<MeshFunction<std::size_t>>(mesh, mesh->topology().dim()-1, 1);
outflow.mark(*outflow_markers, 0);
M->ds = outflow_markers;
diff --git a/demo/undocumented/buckling-tao/python/demo_buckling-tao.py b/demo/undocumented/buckling-tao/python/demo_buckling-tao.py
index deb124c..13fd16b 100644
--- a/demo/undocumented/buckling-tao/python/demo_buckling-tao.py
+++ b/demo/undocumented/buckling-tao/python/demo_buckling-tao.py
@@ -74,7 +74,7 @@ class Left(SubDomain):
class Right(SubDomain):
def inside(self, x, on_boundary):
return on_boundary and near(x[0], 10)
-boundaries = FacetFunction("size_t", mesh)
+boundaries = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
boundaries.set_all(0)
left = Left()
left.mark(boundaries, 1)
diff --git a/demo/undocumented/collision-detection/python/demo_collision-detection.py b/demo/undocumented/collision-detection/python/demo_collision-detection.py
index 535f28a..6d6a2c0 100644
--- a/demo/undocumented/collision-detection/python/demo_collision-detection.py
+++ b/demo/undocumented/collision-detection/python/demo_collision-detection.py
@@ -40,7 +40,7 @@ mesh_B.translate(Point(x_B, y_B))
mesh_C.translate(Point(x_C, y_C))
# Create mesh function for plotting
-f = CellFunction("size_t", mesh_A)
+f = MeshFunction("size_t", mesh_A, mesh_A.topology().dim())
# Build bounding box trees for background mesh
tree_A = BoundingBoxTree()
diff --git a/demo/undocumented/curl-curl/python/demo_curl-curl.py b/demo/undocumented/curl-curl/python/demo_curl-curl.py
index 34f68a1..c8ace04 100644
--- a/demo/undocumented/curl-curl/python/demo_curl-curl.py
+++ b/demo/undocumented/curl-curl/python/demo_curl-curl.py
@@ -60,7 +60,7 @@ if not has_petsc4py():
exit()
# Import petsc4py and check that HYPRE bindings are available
-from petsc4py import *
+from petsc4py import PETSc
try:
getattr(PETSc.PC, 'getHYPREType')
except AttributeError:
diff --git a/demo/undocumented/dg-advection-diffusion/cpp/AdvectionDiffusion.ufl b/demo/undocumented/dg-advection-diffusion/cpp/AdvectionDiffusion.ufl
index 9e5c996..ba67f5d 100644
--- a/demo/undocumented/dg-advection-diffusion/cpp/AdvectionDiffusion.ufl
+++ b/demo/undocumented/dg-advection-diffusion/cpp/AdvectionDiffusion.ufl
@@ -32,7 +32,7 @@ v = TestFunction(scalar)
u = Coefficient(vector)
f = Coefficient(scalar)
-h = 2.0*Circumradius(cell)
+h = CellDiameter(cell)
h_avg = (h('+') + h('-'))/2
n = FacetNormal(cell)
diff --git a/demo/undocumented/dg-advection-diffusion/python/demo_dg-advection-diffusion.py b/demo/undocumented/dg-advection-diffusion/python/demo_dg-advection-diffusion.py
index 082154f..747dda3 100644
--- a/demo/undocumented/dg-advection-diffusion/python/demo_dg-advection-diffusion.py
+++ b/demo/undocumented/dg-advection-diffusion/python/demo_dg-advection-diffusion.py
@@ -61,7 +61,7 @@ alpha = Constant(5.0)
# Mesh-related functions
n = FacetNormal(mesh)
-h = CellSize(mesh)
+h = CellDiameter(mesh)
h_avg = (h('+') + h('-'))/2
# ( dot(v, n) + |dot(v, n)| )/2.0
diff --git a/demo/undocumented/dg-poisson/cpp/Poisson.ufl b/demo/undocumented/dg-poisson/cpp/Poisson.ufl
index 9a9fb4d..fc48abc 100644
--- a/demo/undocumented/dg-poisson/cpp/Poisson.ufl
+++ b/demo/undocumented/dg-poisson/cpp/Poisson.ufl
@@ -34,7 +34,7 @@ u0 = Coefficient(element)
g = Coefficient(element)
# Normal component, cell size and right-hand side
-h = 2.0*Circumradius(triangle)
+h = CellDiameter(triangle)
h_avg = (h('+') + h('-'))/2
n = FacetNormal(triangle)
diff --git a/demo/undocumented/dg-poisson/cpp/main.cpp b/demo/undocumented/dg-poisson/cpp/main.cpp
index c3ad1f9..ef7ecf3 100644
--- a/demo/undocumented/dg-poisson/cpp/main.cpp
+++ b/demo/undocumented/dg-poisson/cpp/main.cpp
@@ -105,7 +105,7 @@ int main()
NeumannBoundary neumann_boundary;
DirichletBoundary dirichlet_boundary;
- auto boundaries = std::make_shared<FacetFunction<std::size_t>>(mesh, 0);
+ auto boundaries = std::make_shared<MeshFunction<std::size_t>>(mesh, mesh->topology().dim()-1, 0);
neumann_boundary.mark(*boundaries, 2);
dirichlet_boundary.mark(*boundaries, 1);
diff --git a/demo/undocumented/dg-poisson/python/demo_dg-poisson.py b/demo/undocumented/dg-poisson/python/demo_dg-poisson.py
index 8b4a65c..9061577 100644
--- a/demo/undocumented/dg-poisson/python/demo_dg-poisson.py
+++ b/demo/undocumented/dg-poisson/python/demo_dg-poisson.py
@@ -62,7 +62,7 @@ v = TestFunction(V)
# Define normal vector and mesh size
n = FacetNormal(mesh)
-h = CellSize(mesh)
+h = CellDiameter(mesh)
h_avg = (h('+') + h('-'))/2
# Define the source term f, Dirichlet term u0 and Neumann term g
@@ -71,7 +71,7 @@ u0 = Expression('x[0] + 0.25*sin(2*pi*x[1])', degree=2)
g = Expression('(x[1] - 0.5)*(x[1] - 0.5)', degree=2)
# Mark facets of the mesh
-boundaries = FacetFunction('size_t', mesh, 0)
+boundaries = MeshFunction('size_t', mesh, mesh.topology().dim()-1, 0)
NeumanBoundary().mark(boundaries, 2)
DirichletBoundary().mark(boundaries, 1)
diff --git a/demo/undocumented/elasticity/cpp/main.cpp b/demo/undocumented/elasticity/cpp/main.cpp
index e0dec0d..6af2c3d 100644
--- a/demo/undocumented/elasticity/cpp/main.cpp
+++ b/demo/undocumented/elasticity/cpp/main.cpp
@@ -195,8 +195,8 @@ int main()
// Save colored mesh paritions in VTK format if running in parallel
if (dolfin::MPI::size(mesh->mpi_comm()) > 1)
{
- CellFunction<std::size_t>
- partitions(mesh, dolfin::MPI::rank(mesh->mpi_comm()));
+ MeshFunction<std::size_t>
+ partitions(mesh, mesh->topology().dim(), dolfin::MPI::rank(mesh->mpi_comm()));
File file("partitions.pvd");
file << partitions;
}
diff --git a/demo/undocumented/elasticity/python/demo_elasticity.py b/demo/undocumented/elasticity/python/demo_elasticity.py
index 387a1a2..586e4a8 100644
--- a/demo/undocumented/elasticity/python/demo_elasticity.py
+++ b/demo/undocumented/elasticity/python/demo_elasticity.py
@@ -143,7 +143,7 @@ File("elasticity.pvd", "compressed") << u
# Save colored mesh partitions in VTK format if running in parallel
if MPI.size(mesh.mpi_comm()) > 1:
- File("partitions.pvd") << CellFunction("size_t", mesh, \
+ File("partitions.pvd") << MeshFunction("size_t", mesh, mesh.topology().dim(), \
MPI.rank(mesh.mpi_comm()))
# Project and write stress field to post-processing file
diff --git a/demo/undocumented/ghost-mesh/python/demo_ghost-mesh.py b/demo/undocumented/ghost-mesh/python/demo_ghost-mesh.py
index 2037896..3d42614 100755
--- a/demo/undocumented/ghost-mesh/python/demo_ghost-mesh.py
+++ b/demo/undocumented/ghost-mesh/python/demo_ghost-mesh.py
@@ -142,7 +142,7 @@ for note in verts_note:
for note in facet_note:
plt.text(note[0], note[1], note[2], size=8, verticalalignment='center', backgroundcolor=note[3])
-# Q = FacetFunction("double", mesh)
+# Q = MeshFunction("double", mesh, mesh.topology().dim()-1)
# # Save solution in XDMF format if available
# xdmf = XDMFFile(mesh.mpi_comm(), "Q.xdmf")
diff --git a/demo/undocumented/lift-drag/cpp/main.cpp b/demo/undocumented/lift-drag/cpp/main.cpp
index d271037..bc97dc6 100644
--- a/demo/undocumented/lift-drag/cpp/main.cpp
+++ b/demo/undocumented/lift-drag/cpp/main.cpp
@@ -57,7 +57,7 @@ int main()
auto p = std::make_shared<Function>(Vp, "../dolfin_fine_pressure.xml.gz");
// Mark 'fish'
- auto markers = std::make_shared<FacetFunction<std::size_t>>(mesh, 1);
+ auto markers = std::make_shared<MeshFunction<std::size_t>>(mesh, mesh->topology().dim()-1, 1);
Fish fish;
fish.mark(*markers, 1);
diff --git a/demo/undocumented/lift-drag/python/demo_lift-drag.py b/demo/undocumented/lift-drag/python/demo_lift-drag.py
index 1f60c55..aa9781d 100644
--- a/demo/undocumented/lift-drag/python/demo_lift-drag.py
+++ b/demo/undocumented/lift-drag/python/demo_lift-drag.py
@@ -48,7 +48,7 @@ class Fish(SubDomain):
return x[0] > DOLFIN_EPS and x[0] < (1.0 - DOLFIN_EPS) and \
x[1] > DOLFIN_EPS and x[1] < (1.0 - DOLFIN_EPS)
-markers = FacetFunction("size_t", mesh, 1)
+markers = MeshFunction("size_t", mesh, mesh.topology().dim()-1, 1)
Fish().mark(markers, 1);
# Define functionals for drag and lift
diff --git a/demo/undocumented/multimesh-3d/cpp/CMakeLists.txt b/demo/undocumented/multimesh-3d/cpp/CMakeLists.txt
new file mode 100644
index 0000000..7b0c84a
--- /dev/null
+++ b/demo/undocumented/multimesh-3d/cpp/CMakeLists.txt
@@ -0,0 +1,38 @@
+# This file is automatically generated by running
+#
+# cmake/scripts/generate-cmakefiles
+#
+# Require CMake 3.5
+cmake_minimum_required(VERSION 3.5)
+
+set(PROJECT_NAME demo_multimesh-3d)
+project(${PROJECT_NAME})
+
+# Set CMake behavior
+cmake_policy(SET CMP0004 NEW)
+
+# Get DOLFIN configuration data (DOLFINConfig.cmake must be in
+# DOLFIN_CMAKE_CONFIG_PATH)
+find_package(DOLFIN REQUIRED)
+
+include(${DOLFIN_USE_FILE})
+
+# Default build type (can be overridden by user)
+if (NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
+ "Choose the type of build, options are: Debug MinSizeRel Release RelWithDebInfo." FORCE)
+endif()
+
+# Do not throw error for 'multi-line comments' (these are typical in
+# rst which includes LaTeX)
+include(CheckCXXCompilerFlag)
+CHECK_CXX_COMPILER_FLAG("-Wno-comment" HAVE_NO_MULTLINE)
+if (HAVE_NO_MULTLINE)
+ set(CMAKE_CXX_FLAGS "-Wno-comment ${CMAKE_CXX_FLAGS}")
+endif()
+
+# Executable
+add_executable(${PROJECT_NAME} main.cpp)
+
+# Target libraries
+target_link_libraries(${PROJECT_NAME} dolfin)
diff --git a/demo/undocumented/multimesh-3d/cpp/MultiMeshH10Norm.ufl b/demo/undocumented/multimesh-3d/cpp/MultiMeshH10Norm.ufl
new file mode 100644
index 0000000..64ae712
--- /dev/null
+++ b/demo/undocumented/multimesh-3d/cpp/MultiMeshH10Norm.ufl
@@ -0,0 +1,20 @@
+#from L2Norm import *
+
+#M = l2norm(triangle)
+
+from ufl import *
+
+ORDER = 1
+
+domain = triangle
+
+# Define element and FE solution
+P1 = FiniteElement("Lagrange", domain, ORDER)
+uh = Coefficient(P1)
+
+# Exact solution
+P3 = FiniteElement("Lagrange", domain, ORDER + 2)
+u = Coefficient(P3)
+
+# Squared L2 norm of error
+M = inner(grad(u - uh), grad(u - uh))*dX
diff --git a/demo/undocumented/multimesh-3d/cpp/MultiMeshL2Norm.ufl b/demo/undocumented/multimesh-3d/cpp/MultiMeshL2Norm.ufl
new file mode 100644
index 0000000..a726277
--- /dev/null
+++ b/demo/undocumented/multimesh-3d/cpp/MultiMeshL2Norm.ufl
@@ -0,0 +1,20 @@
+#from L2Norm import *
+
+#M = l2norm(triangle)
+
+from ufl import *
+
+ORDER = 1
+
+domain = triangle
+
+# Define element and FE solution
+P1 = FiniteElement("Lagrange", domain, ORDER)
+uh = Coefficient(P1)
+
+# Exact solution
+Px = FiniteElement("Lagrange", domain, ORDER + 2)
+u = Coefficient(Px)
+
+# Squared L2 norm of error
+M = inner(u - uh, u - uh)*dX
diff --git a/demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl b/demo/undocumented/multimesh-3d/cpp/MultiMeshPoisson.ufl
similarity index 90%
copy from demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl
copy to demo/undocumented/multimesh-3d/cpp/MultiMeshPoisson.ufl
index b7a06fe..e97d6f2 100644
--- a/demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl
+++ b/demo/undocumented/multimesh-3d/cpp/MultiMeshPoisson.ufl
@@ -16,7 +16,7 @@
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
# First added: 2013-06-26
-# Last changed: 2015-10-16
+# Last changed: 2017-08-23
#
# The bilinear form a(u, v) and linear form L(v) for a multimesh
# formulation of Poisson's equation.
@@ -24,7 +24,8 @@
# Compile this form with FFC: ffc -l dolfin MultiMeshPoisson.ufl.
# Define element
-element = FiniteElement("Lagrange", triangle, 1)
+simplex = tetrahedron
+element = FiniteElement("Lagrange", simplex, 1)
# Define trial and test functions and right-hand side
u = TrialFunction(element)
@@ -32,8 +33,8 @@ v = TestFunction(element)
f = Coefficient(element)
# Define facet normal and mesh size
-n = FacetNormal(triangle)
-h = 2.0*Circumradius(triangle)
+n = FacetNormal(simplex)
+h = 2.0*Circumradius(simplex)
h = (h('+') + h('-')) / 2
# Parameters
diff --git a/demo/undocumented/multimesh-3d/cpp/main.cpp b/demo/undocumented/multimesh-3d/cpp/main.cpp
new file mode 100644
index 0000000..d59e20f
--- /dev/null
+++ b/demo/undocumented/multimesh-3d/cpp/main.cpp
@@ -0,0 +1,224 @@
+// Copyright (C) 2017 August Johansson
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2013-08-24
+// Last changed: 2017-08-30
+//
+// This demo program solves Poisson's equation on a domain defined by
+// three overlapping and non-matching meshes. The solution is computed
+// on a sequence of rotating meshes to test the multimesh
+// functionality.
+
+#include <cmath>
+#include <fstream>
+#include <dolfin.h>
+#include "MultiMeshPoisson.h"
+#include "MultiMeshL2Norm.h"
+#include "MultiMeshH10Norm.h"
+
+using namespace dolfin;
+
+class Arguments
+{
+public:
+ std::size_t N = 1;
+ std::size_t Nx = 10;
+
+ std::string print() const
+ {
+ std::stringstream ss;
+ ss << "N" << N<<"_"
+ << "Nx" << Nx;
+ return ss.str();
+ }
+
+ void parse(int argc, char** argv)
+ {
+ std::size_t c = 1;
+
+ if (argc > c)
+ {
+ this->N = atoi(argv[c]);
+ c++;
+ if (argc > c)
+ this->Nx = atoi(argv[c]);
+ }
+ }
+};
+
+
+// Sub domain for Dirichlet boundary condition
+class DirichletBoundary : public SubDomain
+{
+ bool inside(const Array<double>& x, bool on_boundary) const
+ {
+ return on_boundary;
+ }
+};
+
+class ExactSolution : public Expression
+{
+ void eval(Array<double>& values, const Array<double>& x) const
+ {
+ values[0] = sin(DOLFIN_PI*x[0])*sin(DOLFIN_PI*x[1])*sin(DOLFIN_PI*x[2]);
+ }
+};
+
+class Source : public Expression
+{
+ void eval(Array<double>& values, const Array<double>& x) const
+ {
+ values[0] = 3.0*DOLFIN_PI*DOLFIN_PI*sin(DOLFIN_PI*x[0])*sin(DOLFIN_PI*x[1])*sin(DOLFIN_PI*x[2]);
+ }
+};
+
+void build_multimesh(std::size_t N,
+ std::size_t Nx,
+ std::shared_ptr<MultiMesh> multimesh,
+ double& exact_volume)
+{
+ // Background mesh
+ auto mesh_0 = std::make_shared<UnitCubeMesh>(Nx, Nx, Nx);
+ multimesh->add(mesh_0);
+ exact_volume = 1.;
+
+ // Initialize random generator (dolfin built-in)
+ dolfin::seed(1);
+
+ for (std::size_t i = 1; i < N; ++i)
+ {
+ // Create domain range
+ double x_a = dolfin::rand();
+ double x_b = dolfin::rand();
+
+ if (x_a > x_b)
+ std::swap(x_a, x_b);
+ double y_a = dolfin::rand();
+ double y_b = dolfin::rand();
+ if (y_a > y_b)
+ std::swap(y_a, y_b);
+
+ double z_a = dolfin::rand();
+ double z_b = dolfin::rand();
+ if (z_a > z_b)
+ std::swap(z_a, z_b);
+
+ // std::cout << i << ": " << x_a<<' '<<y_a<<' ' << z_a << ", " << x_b<<' '<<y_b << ' ' << z_b <<std::endl;
+
+ // Find number of elements
+ const std::size_t Nx_part = (std::size_t)std::max(std::abs(x_a - x_b)*Nx, 1.);
+ const std::size_t Ny_part = (std::size_t)std::max(std::abs(y_a - y_b)*Nx, 1.);
+ const std::size_t Nz_part = (std::size_t)std::max(std::abs(z_a - z_b)*Nx, 1.);
+
+ // Create mesh
+ auto mesh_i = std::make_shared<BoxMesh>(Point(x_a, y_a, z_a),
+ Point(x_b, y_b, z_b),
+ Nx_part, Ny_part, Nz_part);
+ multimesh->add(mesh_i);
+ }
+
+ // Build
+ multimesh->build();
+}
+
+template<class TFunctional>
+double compute_error(const std::shared_ptr<MultiMeshFunctionSpace> V,
+ const std::shared_ptr<MultiMeshFunction> u_h,
+ const std::shared_ptr<Expression> u)
+{
+ std::cout << "Compute error" << std::endl;
+
+ auto M = std::make_shared<MultiMeshForm>(V);
+
+ for (std::size_t i = 0; i < V->multimesh()->num_parts(); ++i)
+ {
+ auto M_i = std::make_shared<TFunctional>(V->multimesh()->part(i));
+ const bool deepcopy = true;
+ M_i->uh = std::make_shared<const Function>(*u_h->part(i, deepcopy));
+ M_i->u = u;
+ M->add(M_i);
+ }
+
+ M->build();
+
+ auto assembler = std::make_shared<MultiMeshAssembler>();
+ auto m = std::make_shared<Scalar>();
+ assembler->assemble(*m, *M);
+
+ dolfin_assert(m->get_scalar_value() > 0.);
+ return std::sqrt(m->get_scalar_value());
+}
+
+void solve(const std::shared_ptr<MultiMesh> multimesh)
+{
+ // Create function space
+ auto V = std::make_shared<MultiMeshPoisson::MultiMeshFunctionSpace>(multimesh);
+
+ // Create forms
+ auto a = std::make_shared<MultiMeshPoisson::MultiMeshBilinearForm>(V, V);
+ auto L = std::make_shared<MultiMeshPoisson::MultiMeshLinearForm>(V);
+
+ // Attach source
+ auto f = std::make_shared<Source>();
+ L->f = f;
+
+ // Assemble linear system
+ auto A = std::make_shared<Matrix>();
+ auto b = std::make_shared<Vector>();
+ assemble_multimesh(*A, *a);
+ assemble_multimesh(*b, *L);
+
+ // Apply boundary condition
+ auto zero = std::make_shared<Constant>(0.0);
+ auto boundary = std::make_shared<DirichletBoundary>();
+ auto bc = std::make_shared<MultiMeshDirichletBC>(V, zero, boundary);
+ bc->apply(*A, *b);
+
+ // Compute solution
+ auto uh = std::make_shared<MultiMeshFunction>(V);
+ std::cout << "Solve" << std::endl;
+ solve(*A, *uh->vector(), *b, "cg");
+
+ // Compute errors
+ auto exact_solution = std::make_shared<ExactSolution>();
+ const double L2error = compute_error<MultiMeshL2Norm::Functional>(V, uh, exact_solution);
+ const double H10error = compute_error<MultiMeshH10Norm::Functional>(V, uh, exact_solution);
+ std::cout << L2error << ' ' << H10error << std::endl;
+}
+
+
+int main(int argc, char* argv[])
+{
+ Arguments args;
+ args.parse(argc, argv);
+
+ auto multimesh = std::make_shared<MultiMesh>();
+ double exact_volume;
+ build_multimesh(args.N, args.Nx, multimesh, exact_volume);
+
+ const double volume = multimesh->compute_volume();
+ const double volume_error = std::abs(volume - exact_volume);
+ dolfin::cout << "volume error " << volume_error << dolfin::endl;
+
+ if (volume_error > DOLFIN_EPS_LARGE)
+ {
+ dolfin::cout << " large error" << dolfin::endl;
+ }
+
+ solve(multimesh);
+
+}
diff --git a/demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl b/demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl
index b7a06fe..032638d 100644
--- a/demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl
+++ b/demo/undocumented/multimesh-poisson/cpp/MultiMeshPoisson.ufl
@@ -16,7 +16,7 @@
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
# First added: 2013-06-26
-# Last changed: 2015-10-16
+# Last changed: 2015-11-28
#
# The bilinear form a(u, v) and linear form L(v) for a multimesh
# formulation of Poisson's equation.
@@ -37,15 +37,15 @@ h = 2.0*Circumradius(triangle)
h = (h('+') + h('-')) / 2
# Parameters
-alpha = 4.0
-beta = 4.0
+alpha = 10.0
+beta = 1.0
# Define bilinear form
a = dot(grad(u), grad(v))*dX \
- dot(avg(grad(u)), jump(v, n))*dI \
- dot(avg(grad(v)), jump(u, n))*dI \
- + alpha/h*jump(u)*jump(v)*dI \
- + beta*dot(jump(grad(u)), jump(grad(v)))*dO
+ + alpha/h * jump(u)*jump(v)*dI \
+ + beta/h**2 * dot(jump(u), jump(v))*dO
# Define linear form
L = f*v*dX
diff --git a/test/unit/python/mesh/test_mesh_data.py b/demo/undocumented/multimesh-poisson/cpp/P1.ufl
old mode 100755
new mode 100644
similarity index 68%
copy from test/unit/python/mesh/test_mesh_data.py
copy to demo/undocumented/multimesh-poisson/cpp/P1.ufl
index 7145ef4..9d92788
--- a/test/unit/python/mesh/test_mesh_data.py
+++ b/demo/undocumented/multimesh-poisson/cpp/P1.ufl
@@ -1,8 +1,4 @@
-#!/usr/bin/env py.test
-
-"Unit tests for the MeshData class"
-
-# Copyright (C) 2011 Anders Logg
+# Copyright (C) 2009 Garth N. Wells
#
# This file is part of DOLFIN.
#
@@ -19,15 +15,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
-# First added: 2011-08-22
-# Last changed: 2011-08-22
-
-import pytest
-from dolfin import *
-
+# First added: 2009-06-18
+# Last changed:
+#
+# The bilinear form a(v, u) and linear form L(v) for
+# projection onto piecewise quadratics.
+#
+# Compile this form with FFC: ffc -l dolfin P1.ufl
-def test_meshfunction():
- "Test input/output"
+element = FiniteElement("Lagrange", triangle, 1)
- mesh = UnitCubeMesh(3, 3, 3)
- f = mesh.data().create_array("foo", 3)
diff --git a/demo/undocumented/multimesh-poisson/cpp/main.cpp b/demo/undocumented/multimesh-poisson/cpp/main.cpp
index 1f77e0d..3c85406 100644
--- a/demo/undocumented/multimesh-poisson/cpp/main.cpp
+++ b/demo/undocumented/multimesh-poisson/cpp/main.cpp
@@ -30,15 +30,6 @@
using namespace dolfin;
using std::make_shared;
-// Source term (right-hand side)
-class Source : public Expression
-{
- void eval(Array<double>& values, const Array<double>& x) const
- {
- values[0] = 1.0;
- }
-};
-
// Sub domain for Dirichlet boundary condition
class DirichletBoundary : public SubDomain
{
@@ -49,10 +40,9 @@ class DirichletBoundary : public SubDomain
};
// Compute solution for given mesh configuration
-void solve_poisson(double t,
- double x1, double y1,
- double x2, double y2,
- File& u0_file, File& u1_file, File& u2_file)
+std::shared_ptr<MultiMeshFunction> solve_poisson(double t,
+ double x1, double y1,
+ double x2, double y2)
{
// Create meshes
double r = 0.5;
@@ -77,7 +67,7 @@ void solve_poisson(double t,
auto L = make_shared<MultiMeshPoisson::MultiMeshLinearForm>(V);
// Attach coefficients
- auto f = make_shared<Source>();
+ auto f = make_shared<Constant>(1);
L->f = f;
// Assemble linear system
@@ -92,14 +82,14 @@ void solve_poisson(double t,
auto bc = make_shared<MultiMeshDirichletBC>(V, zero, boundary);
bc->apply(*A, *b);
+ // Lock inactive dofs
+ V->lock_inactive_dofs(*A, *b);
+
// Compute solution
auto u = make_shared<MultiMeshFunction>(V);
solve(*A, *u->vector(), *b);
- // Save to file
- u0_file << *u->part(0);
- u1_file << *u->part(1);
- u2_file << *u->part(2);
+ return u;
}
int main(int argc, char* argv[])
@@ -111,14 +101,14 @@ int main(int argc, char* argv[])
}
// Parameters
- const double T = 40.0;
- const std::size_t N = 400;
+ const double T = 10.0;
+ const std::size_t N = 100;
const double dt = T / N;
- // Files for storing solution
- File u0_file("u0.pvd");
- File u1_file("u1.pvd");
- File u2_file("u2.pvd");
+ // Create files for output
+ XDMFFile f0("output/u0.xdmf");
+ XDMFFile f1("output/u1.xdmf");
+ XDMFFile f2("output/u2.xdmf");
// Iterate over configurations
for (std::size_t n = 0; n < N; n++)
@@ -133,9 +123,18 @@ int main(int argc, char* argv[])
const double y2 = sin(t)*cos(2*t);
// Compute solution
- solve_poisson(t, x1, y1, x2, y2,
- u0_file, u1_file, u2_file);
+ std::shared_ptr<MultiMeshFunction> u = solve_poisson(t, x1, y1, x2, y2);
+
+ // Save to file
+ f0.write(*u->part(0), t);
+ f1.write(*u->part(1), t);
+ f2.write(*u->part(2), t);
}
+ // Close files
+ f0.close();
+ f1.close();
+ f2.close();
+
return 0;
}
diff --git a/demo/undocumented/multimesh-poisson/python/demo_multimesh-poisson.py b/demo/undocumented/multimesh-poisson/python/demo_multimesh-poisson.py
index 238f958..ee79125 100644
--- a/demo/undocumented/multimesh-poisson/python/demo_multimesh-poisson.py
+++ b/demo/undocumented/multimesh-poisson/python/demo_multimesh-poisson.py
@@ -24,7 +24,6 @@
# functionality.
from dolfin import *
-import matplotlib.pyplot as plt
if has_pybind11():
print("Not supported in pybind11")
@@ -34,8 +33,7 @@ class DirichletBoundary(SubDomain):
def inside(self, x, on_boundary):
return on_boundary
-def solve_poisson(t, x1, y1, x2, y2, plot_solution,
- u0_file, u1_file, u2_file):
+def solve_poisson(t, x1, y1, x2, y2):
"Compute solution for given mesh configuration"
# Create meshes
@@ -67,15 +65,15 @@ def solve_poisson(t, x1, y1, x2, y2, plot_solution,
h = (h('+') + h('-')) / 2
# Set parameters
- alpha = 4.0
- beta = 4.0
+ alpha = 10.0
+ beta = 1.0
# Define bilinear form
a = dot(grad(u), grad(v))*dX \
- dot(avg(grad(u)), jump(v, n))*dI \
- dot(avg(grad(v)), jump(u, n))*dI \
- + alpha/h*jump(u)*jump(v)*dI \
- + beta*dot(jump(grad(u)), jump(grad(v)))*dO
+ + alpha/h * jump(u)*jump(v)*dI \
+ + beta/h**2 * dot(jump(u), jump(v))*dO
# Define linear form
L = f*v*dX
@@ -90,36 +88,28 @@ def solve_poisson(t, x1, y1, x2, y2, plot_solution,
bc = MultiMeshDirichletBC(V, zero, boundary)
bc.apply(A, b)
+ # Lock inactive dofs
+ V.lock_inactive_dofs(A, b)
+
# Compute solution
u = MultiMeshFunction(V)
solve(A, u.vector(), b)
- # Save to file
- u0_file << u.part(0)
- u1_file << u.part(1)
- u2_file << u.part(2)
-
- # Plot solution (last time)
- #if plot_solution:
- # plt.figure(); plot(V.multimesh())
- # plt.figure(); plot(u.part(0), title="u_0")
- # plt.figure(); plot(u.part(1), title="u_1")
- # plt.figure(); plot(u.part(2), title="u_2")
- # plt.show()
+ return u
if MPI.size(mpi_comm_world()) > 1:
info("Sorry, this demo does not (yet) run in parallel.")
exit(0)
# Parameters
-T = 40.0
-N = 400
+T = 10.0
+N = 100
dt = T / N
-# Files for storing solution
-u0_file = File("u0.pvd")
-u1_file = File("u1.pvd")
-u2_file = File("u2.pvd")
+# Create files for output
+f0 = XDMFFile("output/u0.xdmf")
+f1 = XDMFFile("output/u1.xdmf")
+f2 = XDMFFile("output/u2.xdmf")
# Iterate over configurations
for n in range(N):
@@ -133,5 +123,14 @@ for n in range(N):
y2 = sin(t)*cos(2*t)
# Compute solution
- solve_poisson(t, x1, y1, x2, y2, n == N - 1,
- u0_file, u1_file, u2_file)
+ u = solve_poisson(t, x1, y1, x2, y2)
+
+ # Save to file
+ f0.write(u.part(0), t)
+ f1.write(u.part(1), t)
+ f2.write(u.part(2), t)
+
+# Close files
+f0.close()
+f1.close()
+f2.close()
diff --git a/demo/undocumented/multimesh-quadrature/cpp/README b/demo/undocumented/multimesh-quadrature/cpp/README
new file mode 100644
index 0000000..2cf893f
--- /dev/null
+++ b/demo/undocumented/multimesh-quadrature/cpp/README
@@ -0,0 +1,2 @@
+This demo does not have a C++ version since the demo relies heavily
+on plotting via Matplotlib.
diff --git a/demo/undocumented/multimesh/propeller_2d_coarse.xml.gz b/demo/undocumented/multimesh-quadrature/propeller_2d_coarse.xml.gz
similarity index 100%
rename from demo/undocumented/multimesh/propeller_2d_coarse.xml.gz
rename to demo/undocumented/multimesh-quadrature/propeller_2d_coarse.xml.gz
diff --git a/demo/undocumented/multimesh/propeller_2d_fine.xml.gz b/demo/undocumented/multimesh-quadrature/propeller_2d_fine.xml.gz
similarity index 100%
rename from demo/undocumented/multimesh/propeller_2d_fine.xml.gz
rename to demo/undocumented/multimesh-quadrature/propeller_2d_fine.xml.gz
diff --git a/demo/undocumented/multimesh-quadrature/python/demo_multimesh-quadrature.py b/demo/undocumented/multimesh-quadrature/python/demo_multimesh-quadrature.py
new file mode 100644
index 0000000..c1805be
--- /dev/null
+++ b/demo/undocumented/multimesh-quadrature/python/demo_multimesh-quadrature.py
@@ -0,0 +1,147 @@
+# Copyright (C) 2014-2017 Anders Logg
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# First added: 2014-04-07
+# Last changed: 2017-05-17
+#
+# This demo program illustrates multimesh quadrature on a pair
+# of overlapping meshes using red dots to mark positive quadature
+# weights and black dots to mark negative quadature weights.
+#
+# To create movies from the generated PNG files, enter the output
+# directory and run the following commmands:
+#
+# ffmpeg -i multimesh_quadrature_%04d.png multimesh_quadrature.mp4
+# ffmpeg -i multimesh_quadrature_%04d.png multimesh_quadrature_compressed.mp4
+
+from dolfin import *
+import os
+
+# Don't plot when DOLFIN_NOPLOT is set
+plot = os.environ.get("DOLFIN_NOPLOT") is None
+if plot:
+ import pylab as pl
+
+# Colors for plotting
+red = "#ff3c00"
+green = "#59ce55"
+yellow = "#fff0aa"
+blue = "#b4d8e7"
+white = "#ffffff"
+black = "#000000"
+
+def plot_triangle(c, mesh, color=None, alpha_fill=None, alpha_line=None):
+ if not plot: return
+ cell = Cell(mesh, c)
+ xy = cell.get_vertex_coordinates()
+ x = [xy[0], xy[2], xy[4]]
+ y = [xy[1], xy[3], xy[5]]
+ if not color is None: pl.fill(x, y, color=color, alpha=alpha_fill)
+ pl.plot(x + [x[0]], y + [y[0]], color='k', alpha=alpha_line)
+
+def plot_point_red(x, y):
+ if not plot: return
+ pl.plot(x, y, '.', markersize=5, color=red)
+
+def plot_point_black(x, y):
+ if not plot: return
+ pl.plot(x, y, '.', markersize=2, color=black)
+
+def clear_plot():
+ if not plot: return
+ pl.clf()
+
+def save_plot(frame, compress):
+ if not plot: return
+ if not os.path.isdir("output"): os.makedirs("output")
+ pl.axis("equal")
+ pl.axis("off")
+ c = "_compressed" if compress else ""
+ pl.savefig("output/multimesh_quadrature%s_%.4d.png" % (c, frame), dpi=300)
+
+def show_plot():
+ if not plot: return
+ pl.show()
+
+# Parameters
+N = 8
+dv = 5
+R = 0.6
+num_frames = 3
+
+# Uncomment to generate a longer movie
+#num_frames = 360
+
+# Iterate over quadrature compression on/off
+for compress in [True, False]:
+ print("Compression:", compress)
+
+ # Create and load meshes
+ mesh = RectangleMesh(Point(-R, -R), Point(R, R), N, N)
+ propeller = Mesh("../propeller_2d_coarse.xml.gz")
+
+ # Iterate over frames (rotations of the propeller)
+ for frame in range(num_frames):
+ print("Frame %d out of %d..." % (frame + 1, num_frames))
+
+ # Rotate propeller
+ propeller.rotate(dv)
+
+ # Build multimesh
+ multimesh = MultiMesh()
+ multimesh.parameters["compress_volume_quadrature"] = compress
+ multimesh.add(mesh)
+ multimesh.add(propeller)
+ multimesh.build()
+
+ # Extract data
+ cut_cells = multimesh.cut_cells(0)
+ uncut_cells = multimesh.uncut_cells(0)
+ covered_cells = multimesh.covered_cells(0)
+
+ # Clear plot
+ clear_plot()
+
+ # Plot cells in background mesh
+ for c in cut_cells:
+ plot_triangle(c, mesh, yellow)
+ for c in uncut_cells:
+ plot_triangle(c, mesh, blue)
+ for c in covered_cells:
+ plot_triangle(c, mesh, white, alpha_line=0.1)
+
+ # Plot propeller mesh
+ for c in range(propeller.num_cells()):
+ plot_triangle(c, propeller, color=white, alpha_fill=0.25)
+
+ # Plot quadrature points
+ for c in cut_cells:
+ points, weights = multimesh.quadrature_rules_cut_cells(0, c)
+ for i in range(len(weights)):
+ w = weights[i]
+ x = points[2*i]
+ y = points[2*i + 1]
+ if w > 0:
+ plot_point_red(x, y)
+ else:
+ plot_point_black(x, y)
+
+ # Save plot
+ save_plot(frame, compress)
+
+# Show last frame
+show_plot()
diff --git a/demo/undocumented/multimesh-stokes/cpp/MultiMeshStokes.ufl b/demo/undocumented/multimesh-stokes/cpp/MultiMeshStokes.ufl
index 30cf049..88b297a 100644
--- a/demo/undocumented/multimesh-stokes/cpp/MultiMeshStokes.ufl
+++ b/demo/undocumented/multimesh-stokes/cpp/MultiMeshStokes.ufl
@@ -38,7 +38,8 @@ n = FacetNormal(triangle)
h = 2.0*Circumradius(triangle)
# Parameters
-alpha = 4.0
+alpha = 20.0
+beta = 5
def tensor_jump(v, n):
return outer(v('+'), n('+')) + outer(v('-'), n('-'))
@@ -55,8 +56,9 @@ def b_h(v, q):
def l_h(v, q, f):
return inner(f, v)*dX
-def s_O(v, w):
- return inner(jump(grad(v)), jump(grad(w)))*dO
+def s_O(v, q, w, r):
+ return (beta/avg(h)**2) * inner(jump(v), jump(w))*dO \
+ + beta * inner(jump(q), jump(r))*dO
def s_C(v, q, w, r):
return h*h*inner(-div(grad(v)) + grad(q), -div(grad(w)) - grad(r))*dC
@@ -65,7 +67,7 @@ def l_C(v, q, f):
return h*h*inner(f, -div(grad(v)) - grad(q))*dC
# Define bilinear form
-a = a_h(u, v) + b_h(v, p) + b_h(u, q) + s_O(u, v) + s_C(u, p, v, q)
+a = a_h(u, v) + b_h(v, p) + b_h(u, q) + s_O(u, p, v, q) + s_C(u, p, v, q)
# Define linear form
L = l_h(v, q, f) + l_C(v, q, f)
diff --git a/demo/undocumented/multimesh-stokes/cpp/Stokes.ufl b/demo/undocumented/multimesh-stokes/cpp/Stokes.ufl
deleted file mode 100644
index d903702..0000000
--- a/demo/undocumented/multimesh-stokes/cpp/Stokes.ufl
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright (C) 2014 Anders Logg
-#
-# This file is part of DOLFIN.
-#
-# DOLFIN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# DOLFIN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2014-06-10
-# Last changed: 2014-06-11
-#
-# The bilinear form a(u, v) and linear form L(v) for a standard
-# formulation of the Stokes equations.
-#
-# Compile this form with FFC: ffc -l dolfin Stokes.ufl.
-
-# Define element
-P2 = VectorElement("Lagrange", triangle, 2)
-P1 = FiniteElement("Lagrange", triangle, 1)
-TH = P2 * P1
-
-# Define trial and test functions and right-hand side
-(u, p) = TrialFunctions(TH)
-(v, q) = TestFunctions(TH)
-f = Coefficient(P2)
-
-# Define facet normal
-n = FacetNormal(triangle)
-
-def tensor_jump(v, n):
- return outer(v('+'), n('+')) + outer(v('-'), n('-'))
-
-def a_h(v, w):
- return inner(grad(v), grad(w))*dx
-
-def b_h(v, q):
- return -div(v)*q*dx
-
-# Bilinear form
-a = a_h(u, v) + b_h(v, p) + b_h(u, q)
-
-# Linear form
-L = dot(f, v)*dx
diff --git a/demo/undocumented/multimesh-stokes/cpp/main.cpp b/demo/undocumented/multimesh-stokes/cpp/main.cpp
index b266631..e8d40e4 100644
--- a/demo/undocumented/multimesh-stokes/cpp/main.cpp
+++ b/demo/undocumented/multimesh-stokes/cpp/main.cpp
@@ -16,7 +16,7 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2014-06-10
-// Last changed: 2016-03-02
+// Last changed: 2017-09-28
//
// This demo program solves the Stokes equations on a domain defined
// by three overlapping and non-matching meshes.
@@ -130,31 +130,23 @@ int main(int argc, char* argv[])
bc1->apply(*A, *b);
bc2->apply(*A, *b);
+ // Remove inactive dofs
+ V->lock_inactive_dofs(*A, *b);
+
// Compute solution
- MultiMeshFunction w(W);
- solve(*A, *w.vector(), *b);
-
- // Extract solution components
- Function& u0 = (*w.part(0))[0];
- Function& u1 = (*w.part(1))[0];
- Function& u2 = (*w.part(2))[0];
- Function& p0 = (*w.part(0))[1];
- Function& p1 = (*w.part(1))[1];
- Function& p2 = (*w.part(2))[1];
-
- // Save to file
- File u0_file("u0.pvd");
- File u1_file("u1.pvd");
- File u2_file("u2.pvd");
- File p0_file("p0.pvd");
- File p1_file("p1.pvd");
- File p2_file("p2.pvd");
- u0_file << u0;
- u1_file << u1;
- u2_file << u2;
- p0_file << p0;
- p1_file << p1;
- p2_file << p2;
+ auto w = make_shared<MultiMeshFunction>(W);
+ solve(*A, *w->vector(), *b);
+
+ // Save solution parts and components to file
+ for (int part = 0; part < 3; part++)
+ {
+ XDMFFile ufile("output/u" + std::to_string(part) + ".xdmf");
+ XDMFFile pfile("output/p" + std::to_string(part) + ".xdmf");
+ ufile.write((*w->part(part))[0]);
+ pfile.write((*w->part(part))[1]);
+ ufile.close();
+ pfile.close();
+ }
return 0;
}
diff --git a/demo/undocumented/multimesh-stokes/cpp/reference.h b/demo/undocumented/multimesh-stokes/cpp/reference.h
deleted file mode 100644
index b82b5e7..0000000
--- a/demo/undocumented/multimesh-stokes/cpp/reference.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#include "Stokes.h"
-
-void run_reference()
-{
- info("Running reference case");
-
- // Read mesh and sub domain markers
- UnitSquareMesh mesh(16, 16);
-
- // Create function space and subspaces
- Stokes::FunctionSpace W(mesh);
- SubSpace W0(W, 0);
- SubSpace W1(W, 1);
-
- // Create boundary values
- Constant noslip_value(0, 0);
- InflowValue inflow_value;
- Constant outflow_value(0);
-
- // Create subdomains for boundary conditions
- NoslipBoundary noslip_boundary;
- InflowBoundary inflow_boundary;
- OutflowBoundary outflow_boundary;
-
- // No-slip boundary condition for velocity
- DirichletBC bc0(W0, noslip_value, noslip_boundary);
-
- // Inflow boundary condition for velocity
- DirichletBC bc1(W0, inflow_value, inflow_boundary);
-
- // Boundary condition for pressure at outflow
- DirichletBC bc2(W1, outflow_value, outflow_boundary);
-
- // Collect boundary conditions
- std::vector<const DirichletBC*> bcs;
- bcs.push_back(&bc0); bcs.push_back(&bc1); bcs.push_back(&bc2);
-
- // Define variational problem
- Constant f(0.0, 0.0);
- Stokes::BilinearForm a(W, W);
- Stokes::LinearForm L(W);
- L.f = f;
-
- // Compute solution
- Function w(W);
- solve(a == L, w, bcs);
- Function u = w[0];
- Function p = w[1];
-}
diff --git a/demo/undocumented/multimesh-stokes/python/demo_multimesh-stokes.py b/demo/undocumented/multimesh-stokes/python/demo_multimesh-stokes.py
index 129b785..83e2f67 100644
--- a/demo/undocumented/multimesh-stokes/python/demo_multimesh-stokes.py
+++ b/demo/undocumented/multimesh-stokes/python/demo_multimesh-stokes.py
@@ -16,13 +16,12 @@
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
# First added: 2015-11-11
-# Last changed: 2015-11-24
+# Last changed: 2017-05-25
#
# This demo program solves the Stokes equations on a domain defined
# by three overlapping and non-matching meshes.
from dolfin import *
-import matplotlib.pyplot as plt
if has_pybind11():
print("Not supported in pybind11")
@@ -56,8 +55,6 @@ multimesh.add(mesh_1)
multimesh.add(mesh_2)
multimesh.build()
-# FIXME: Tensor algebra not supported for multimesh function spaces
-
# Create function space
P2 = VectorElement("Lagrange", triangle, 2)
P1 = FiniteElement("Lagrange", triangle, 1)
@@ -78,7 +75,8 @@ n = FacetNormal(multimesh)
h = 2.0*Circumradius(multimesh)
# Parameters
-alpha = 4.0
+alpha = 20.0
+beta = 5.0
def tensor_jump(v, n):
return outer(v('+'), n('+')) + outer(v('-'), n('-'))
@@ -95,8 +93,9 @@ def b_h(v, q):
def l_h(v, q, f):
return inner(f, v)*dX
-def s_O(v, w):
- return inner(jump(grad(v)), jump(grad(w)))*dO
+def s_O(v, q, w, r):
+ return (beta/avg(h)**2) * inner(jump(v), jump(w))*dO \
+ + beta * inner(jump(q), jump(r))*dO
def s_C(v, q, w, r):
return h*h*inner(-div(grad(v)) + grad(q), -div(grad(w)) - grad(r))*dC
@@ -105,7 +104,7 @@ def l_C(v, q, f):
return h*h*inner(f, -div(grad(v)) - grad(q))*dC
# Define bilinear form
-a = a_h(u, v) + b_h(v, p) + b_h(u, q) + s_O(u, v) + s_C(u, p, v, q)
+a = a_h(u, v) + b_h(v, p) + b_h(u, q) + s_O(u, p, v, q) + s_C(u, p, v, q)
# Define linear form
L = l_h(v, q, f) + l_C(v, q, f)
@@ -137,36 +136,17 @@ bc2 = MultiMeshDirichletBC(Q, outflow_value, outflow_boundary)
bc0.apply(A, b)
bc1.apply(A, b)
bc2.apply(A, b)
+W.lock_inactive_dofs(A, b)
# Compute solution
w = MultiMeshFunction(W)
solve(A, w.vector(), b)
-# FIXME: w.part(i).split() not working for extracted parts
-# FIXME: since they are only dolfin::Functions
-
-# Extract solution components
-u0 = w.part(0).sub(0)
-u1 = w.part(1).sub(0)
-u2 = w.part(2).sub(0)
-p0 = w.part(0).sub(1)
-p1 = w.part(1).sub(1)
-p2 = w.part(2).sub(1)
-
-# Save to file
-File("u0.pvd") << u0
-File("u1.pvd") << u1
-File("u2.pvd") << u2
-File("p0.pvd") << p0
-File("p1.pvd") << p1
-File("p2.pvd") << p2
-
-# Plot solution
-#plt.figure(); plot(W.multimesh())
-#plt.figure(); plot(u0, title="u_0")
-#plt.figure(); plot(u1, title="u_1")
-#plt.figure(); plot(u2, title="u_2")
-#plt.figure(); plot(p0, title="p_0")
-#plt.figure(); plot(p1, title="p_1")
-#plt.figure(); plot(p2, title="p_2")
-#plt.show()
+# Save solution parts and components to file
+for part in range(3):
+ ufile = XDMFFile("output/u%d.xdmf" % part)
+ pfile = XDMFFile("output/p%d.xdmf" % part)
+ ufile.write(w.part(part).sub(0))
+ pfile.write(w.part(part).sub(1))
+ ufile.close()
+ pfile.close()
diff --git a/demo/undocumented/multimesh/python/demo_multimesh.py b/demo/undocumented/multimesh/python/demo_multimesh.py
deleted file mode 100644
index 528cdb5..0000000
--- a/demo/undocumented/multimesh/python/demo_multimesh.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright (C) 2014-2015 Anders Logg
-#
-# This file is part of DOLFIN.
-#
-# DOLFIN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# DOLFIN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2014-04-07
-# Last changed: 2015-06-15
-
-from __future__ import print_function
-from dolfin import *
-import os
-
-print("Multi-mesh plotting requires updating")
-exit(0)
-
-# Don't plot when DOLFIN_NOPLOT is set
-plot = os.environ.get("DOLFIN_NOPLOT") is None
-if plot:
- import pylab as pl
-
-# Colors for plotting
-red = "#ff3c00"
-green = "#59ce55"
-yellow = "#fff0aa"
-blue = "#b4d8e7"
-white = "#ffffff"
-black = "#000000"
-
-def plot_triangle(c, mesh, color=None, alpha_fill=None, alpha_line=None):
- if not plot: return
- cell = Cell(mesh, c)
- xy = cell.get_vertex_coordinates()
- x = [xy[0], xy[2], xy[4]]
- y = [xy[1], xy[3], xy[5]]
- if not color is None: pl.fill(x, y, color=color, alpha=alpha_fill)
- pl.plot(x + [x[0]], y + [y[0]], color='k', alpha=alpha_line)
-
-def plot_point_red(x, y):
- if not plot: return
- pl.plot(x, y, '.', markersize=5, color=red)
-
-def plot_point_black(x, y):
- if not plot: return
- pl.plot(x, y, '.', markersize=2, color=black)
-
-def clear_plot():
- if not plot: return
- pl.clf()
-
-def save_plot(frame):
- if not plot: return
- pl.axis("equal")
- pl.axis("off")
- pl.savefig("multimesh_propeller_%.4d.png" % frame, dpi=300)
-
-def show_plot():
- if not plot: return
- pl.show()
-
-# Parameters
-N = 20
-dv = 5
-R = 0.6
-num_frames = 3
-
-# Uncomment to generate a longer movie
-#num_frames = 360
-
-# Create and load meshes
-mesh = RectangleMesh(Point(-R, -R), Point(R, R), N, N)
-propeller = Mesh("../propeller_2d_coarse.xml.gz")
-
-# Iterate over rotations
-for frame in range(num_frames):
-
- print("Frame %d out of %d..." % (frame + 1, num_frames))
-
- # Rotate propeller
- propeller.rotate(dv)
-
- # Build multimesh
- multimesh = MultiMesh()
- multimesh.add(mesh)
- multimesh.add(propeller)
- multimesh.build()
-
- # Extract data
- cut_cells = multimesh.cut_cells(0)
- uncut_cells = multimesh.uncut_cells(0)
- covered_cells = multimesh.covered_cells(0)
-
- # Clear plot
- clear_plot()
-
- # Plot cells in background mesh
- for c in cut_cells:
- plot_triangle(c, mesh, yellow)
- for c in uncut_cells:
- plot_triangle(c, mesh, blue)
- for c in covered_cells:
- plot_triangle(c, mesh, white, alpha_line=0.1)
-
- # Plot propeller mesh
- for c in range(propeller.num_cells()):
- plot_triangle(c, propeller, color=white, alpha_fill=0.25)
-
- # Plot quadrature points
- for c in cut_cells:
- points, weights = multimesh.quadrature_rule_cut_cell(0, c)
- for i in range(len(weights)):
- w = weights[i]
- x = points[2*i]
- y = points[2*i + 1]
- if w > 0:
- plot_point_red(x, y)
- else:
- plot_point_black(x, y)
-
- # Save plot
- save_plot(frame)
-
-# Show last frame
-show_plot()
-
-# Generate movie using
-#ffmpeg -r 25 -b 1800 -i multimesh_propeller_%04d.png multimesh_propeller.mp4
diff --git a/demo/undocumented/multistage-solver/python/demo_multistage-solver.py b/demo/undocumented/multistage-solver/python/demo_multistage-solver.py
index 9c93a43..c9738cc 100644
--- a/demo/undocumented/multistage-solver/python/demo_multistage-solver.py
+++ b/demo/undocumented/multistage-solver/python/demo_multistage-solver.py
@@ -43,7 +43,7 @@ exit(0)
# Load mesh and subdomains
mesh = Mesh("../dolfin_fine.xml.gz")
sub_domains = MeshFunction("size_t", mesh, "../dolfin_fine_subdomains.xml.gz");
-h = CellSize(mesh)
+h = CellDiameter(mesh)
# Create FunctionSpaces
Q = FunctionSpace(mesh, "CG", 1)
diff --git a/demo/undocumented/overlapping-regions/python/demo_overlapping-regions.py b/demo/undocumented/overlapping-regions/python/demo_overlapping-regions.py
index a86e9f6..669055e 100644
--- a/demo/undocumented/overlapping-regions/python/demo_overlapping-regions.py
+++ b/demo/undocumented/overlapping-regions/python/demo_overlapping-regions.py
@@ -41,7 +41,7 @@ class Right(SubDomain):
# Define mesh and subdomains
mesh = UnitSquareMesh(64, 64)
-domains = CellFunction("size_t", mesh)
+domains = MeshFunction("size_t", mesh, mesh.topology().dim())
domains.set_all(0)
Left().mark(domains, 1)
Mid().mark(domains, 2)
diff --git a/demo/undocumented/parallel-refinement/cpp/main.cpp b/demo/undocumented/parallel-refinement/cpp/main.cpp
index 9672943..c6fab1a 100644
--- a/demo/undocumented/parallel-refinement/cpp/main.cpp
+++ b/demo/undocumented/parallel-refinement/cpp/main.cpp
@@ -28,31 +28,31 @@ int main()
auto mesh = std::make_shared<UnitSquareMesh>(20, 20);
// Create MeshFunction to hold cell process rank
- CellFunction<std::size_t>
- processes0(mesh, dolfin::MPI::rank(mesh->mpi_comm()));
+ MeshFunction<std::size_t>
+ processes0(mesh, mesh->topology().dim(), dolfin::MPI::rank(mesh->mpi_comm()));
// Output cell distribution to VTK file
File file("processes.pvd");
file << processes0;
// Mark all cells on process 0 for refinement
- const CellFunction<bool>
- marker(mesh, (dolfin::MPI::rank(mesh->mpi_comm()) == 0));
+ const MeshFunction<bool>
+ marker(mesh, mesh->topology().dim(), (dolfin::MPI::rank(mesh->mpi_comm()) == 0));
// Refine mesh, but keep all new cells on parent process
auto mesh0 = std::make_shared<Mesh>(refine(*mesh, marker, false));
// Create MeshFunction to hold cell process rank
- const CellFunction<std::size_t>
- processes1(mesh0, dolfin::MPI::rank(mesh->mpi_comm()));
+ const MeshFunction<std::size_t>
+ processes1(mesh0, mesh->topology().dim(), dolfin::MPI::rank(mesh->mpi_comm()));
file << processes1;
// Refine mesh, but this time repartition the mesh after refinement
auto mesh1 = std::make_shared<Mesh>(refine(*mesh, marker, false));
// Create MeshFunction to hold cell process rank
- CellFunction<std::size_t>
- processes2(mesh1, dolfin::MPI::rank(mesh->mpi_comm()));
+ MeshFunction<std::size_t>
+ processes2(mesh1, mesh->topology().dim(), dolfin::MPI::rank(mesh->mpi_comm()));
file << processes2;
return 0;
diff --git a/demo/undocumented/parallel-refinement/python/demo_parallel-refinement.py b/demo/undocumented/parallel-refinement/python/demo_parallel-refinement.py
index 06602ba..e2bad2d 100644
--- a/demo/undocumented/parallel-refinement/python/demo_parallel-refinement.py
+++ b/demo/undocumented/parallel-refinement/python/demo_parallel-refinement.py
@@ -24,25 +24,25 @@ from dolfin import *
mesh = UnitSquareMesh(20, 20)
# Create MeshFunction to hold cell process rank
-processes = CellFunction('size_t', mesh, MPI.rank(mesh.mpi_comm()))
+processes = MeshFunction('size_t', mesh, mesh.topology().dim(), MPI.rank(mesh.mpi_comm()))
# Output cell distribution to VTK file
file = File("processes.pvd")
file << processes
# Mark all cells on process 0 for refinement
-marker = CellFunction('bool', mesh, (MPI.rank(mesh.mpi_comm()) == 0))
+marker = MeshFunction('bool', mesh, mesh.topology().dim(), (MPI.rank(mesh.mpi_comm()) == 0))
# Refine mesh, but keep all news cells on parent process
mesh0 = refine(mesh, marker, False)
# Create MeshFunction to hold cell process rank for refined mesh
-processes1 = CellFunction('size_t', mesh0, MPI.rank(mesh.mpi_comm()))
+processes1 = MeshFunction('size_t', mesh0, mesh0.topology().dim(), MPI.rank(mesh.mpi_comm()))
file << processes1
# Refine mesh, but this time repartition the mesh after refinement
mesh1 = refine(mesh, marker, True)
# Create MeshFunction to hold cell process rank for refined mesh
-processes2 = CellFunction('size_t', mesh1, MPI.rank(mesh.mpi_comm()))
+processes2 = MeshFunction('size_t', mesh1, mesh1.topology().dim(), MPI.rank(mesh.mpi_comm()))
file << processes2
diff --git a/demo/undocumented/point-integral/python/demo_point-integral.py b/demo/undocumented/point-integral/python/demo_point-integral.py
index 0c59f53..aa0f4ea 100644
--- a/demo/undocumented/point-integral/python/demo_point-integral.py
+++ b/demo/undocumented/point-integral/python/demo_point-integral.py
@@ -50,7 +50,7 @@ def center_func(x):
0.45 <= x[1] and x[1] <= 0.55 and near(x[0], 0.5)
# Define domain for point integral
-center_domain = VertexFunction("size_t", mesh, 0)
+center_domain = MeshFunction("size_t", mesh, 0, 0)
center = AutoSubDomain(center_func)
center.mark(center_domain, 1)
dPP = dP(subdomain_data=center_domain)
diff --git a/demo/undocumented/refinement/python/demo_refinement.py b/demo/undocumented/refinement/python/demo_refinement.py
index 446faef..22b861d 100644
--- a/demo/undocumented/refinement/python/demo_refinement.py
+++ b/demo/undocumented/refinement/python/demo_refinement.py
@@ -56,7 +56,7 @@ for i in range(5):
print("marking for refinement")
# Mark cells for refinement
- cell_markers = CellFunction("bool", mesh)
+ cell_markers = MeshFunction("bool", mesh, mesh.topology().dim())
for c in cells(mesh):
if c.midpoint().distance(p) < 0.1:
cell_markers[c] = True
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 621853d..0eea0a4 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -119,9 +119,9 @@ author = u'FEniCS Project'
# built documents.
#
# The short X.Y version.
-version = u'2017.2.0.dev0'
+version = u'2017.2.0'
# The full version, including alpha/beta/rc tags.
-release = u'2017.2.0.dev0'
+release = u'2017.2.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/dolfin/CMakeLists.txt b/dolfin/CMakeLists.txt
index db92c91..c0f875b 100644
--- a/dolfin/CMakeLists.txt
+++ b/dolfin/CMakeLists.txt
@@ -217,6 +217,16 @@ if (DOLFIN_ENABLE_MPI AND MPI_CXX_FOUND)
target_include_directories(dolfin SYSTEM PUBLIC ${MPI_CXX_INCLUDE_PATH})
endif()
+if (DOLFIN_ENABLE_GEOMETRY_DEBUGGING AND GMP_FOUND AND MPFR_FOUND AND CGAL_FOUND)
+ message(STATUS "Appending link flags for geometry debugging")
+ target_compile_definitions(dolfin PUBLIC "-DDOLFIN_ENABLE_GEOMETRY_DEBUGGING")
+ target_include_directories(dolfin PRIVATE ${CGAL_INCLUDE_DIRS})
+ target_include_directories(dolfin PRIVATE ${GMP_INCLUDE_DIRS})
+ target_include_directories(dolfin PRIVATE ${MPFR_INCLUDE_DIRS})
+ target_link_libraries(dolfin PRIVATE ${GMP_LIBRARIES})
+ target_link_libraries(dolfin PRIVATE ${MPFR_LIBRARIES})
+endif()
+
#------------------------------------------------------------------------------
# Set compiler flags, include directories and library dependencies
diff --git a/dolfin/common/Variable.h b/dolfin/common/Variable.h
index 25c0a8c..a4129e3 100644
--- a/dolfin/common/Variable.h
+++ b/dolfin/common/Variable.h
@@ -55,7 +55,7 @@ namespace dolfin
void rename(const std::string name, const std::string label);
/// Return name
- std::string name() const;
+ std::string name() const;
/// Return label (description)
std::string label() const;
diff --git a/dolfin/fem/AssemblerBase.cpp b/dolfin/fem/AssemblerBase.cpp
index 75d9d7a..92accd3 100644
--- a/dolfin/fem/AssemblerBase.cpp
+++ b/dolfin/fem/AssemblerBase.cpp
@@ -294,60 +294,20 @@ You might have forgotten to specify the value dimension correctly in an Expressi
"Mesh geometry degree does not match degree of coordinate element in form");
}
- switch (mesh.type().cell_type())
- {
- case CellType::interval:
- if (coordinate_element->cell_shape() != ufc::shape::interval)
- {
- dolfin_error("AssemblerBase.cpp",
- "assemble form",
- "Mesh cell type (intervals) does not match cell type of form");
- }
- break;
- case CellType::triangle:
- if (coordinate_element->cell_shape() != ufc::shape::triangle)
- {
- dolfin_error("AssemblerBase.cpp",
- "assemble form",
- "Mesh cell type (triangles) does not match cell type of form");
- }
- break;
- case CellType::tetrahedron:
- if (coordinate_element->cell_shape() != ufc::shape::tetrahedron)
- {
- dolfin_error("AssemblerBase.cpp",
- "assemble form",
- "Mesh cell type (tetrahedra) does not match cell type of form");
- }
- break;
- case CellType::quadrilateral:
- if (coordinate_element->cell_shape() != ufc::shape::quadrilateral)
- {
- dolfin_error("AssemblerBase.cpp",
- "assemble form",
- "Mesh cell type (quadrilateral) does not match cell type of form");
- }
- break;
- case CellType::hexahedron:
- if (coordinate_element->cell_shape() != ufc::shape::hexahedron)
- {
- dolfin_error("AssemblerBase.cpp",
- "assemble form",
- "Mesh cell type (hexahedron) does not match cell type of form");
- }
- break;
- default:
- dolfin_error("AssemblerBase.cpp",
- "assemble form",
- "Mesh cell type is unknown!");
- }
-
- // Check that the mesh is ordered
- if (!mesh.ordered())
+ std::map<CellType::Type, ufc::shape> dolfin_to_ufc_shapes
+ = { {CellType::Type::interval, ufc::shape::interval},
+ {CellType::Type::triangle, ufc::shape::triangle},
+ {CellType::Type::tetrahedron, ufc::shape::tetrahedron},
+ {CellType::Type::quadrilateral, ufc::shape::quadrilateral},
+ {CellType::Type::hexahedron, ufc::shape::hexahedron} };
+
+ auto cell_type_pair = dolfin_to_ufc_shapes.find(mesh.type().cell_type());
+ dolfin_assert(cell_type_pair != dolfin_to_ufc_shapes.end());
+ if (coordinate_element->cell_shape() != cell_type_pair->second)
{
dolfin_error("AssemblerBase.cpp",
"assemble form",
- "Mesh is not correctly ordered. Consider calling mesh.order()");
+ "Mesh cell type does not match cell type of UFC form");
}
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/fem/DirichletBC.cpp b/dolfin/fem/DirichletBC.cpp
index 31c9f31..3f254aa 100644
--- a/dolfin/fem/DirichletBC.cpp
+++ b/dolfin/fem/DirichletBC.cpp
@@ -641,7 +641,7 @@ void DirichletBC::init_from_sub_domain(std::shared_ptr<const SubDomain>
// all facet as subdomain 1
const std::size_t dim = mesh->topology().dim();
_function_space->mesh()->init(dim - 1);
- FacetFunction<std::size_t> sub_domains(mesh, 1);
+ MeshFunction<std::size_t> sub_domains(mesh, dim - 1, 1);
// Set geometric dimension (needed for SWIG interface)
sub_domain->_geometric_dimension = mesh->geometry().dim();
diff --git a/dolfin/fem/DiscreteOperators.cpp b/dolfin/fem/DiscreteOperators.cpp
index de6eb1c..56dc503 100644
--- a/dolfin/fem/DiscreteOperators.cpp
+++ b/dolfin/fem/DiscreteOperators.cpp
@@ -54,7 +54,7 @@ DiscreteOperators::build_gradient(const FunctionSpace& V0,
// Check that V0 is a (lowest-order) edge basis
mesh.init(1);
- if (V0.dim() != mesh.size_global(1))
+ if (V0.dim() != mesh.num_entities_global(1))
{
dolfin_error("DiscreteGradient.cpp",
"compute discrete gradient operator",
@@ -62,7 +62,7 @@ DiscreteOperators::build_gradient(const FunctionSpace& V0,
}
// Check that V1 is a linear nodal basis
- if (V1.dim() != mesh.size_global(0))
+ if (V1.dim() != mesh.num_entities_global(0))
{
dolfin_error("DiscreteGradient.cpp",
"compute discrete gradient operator",
diff --git a/dolfin/fem/DofMap.h b/dolfin/fem/DofMap.h
index 3f399c8..da87c67 100644
--- a/dolfin/fem/DofMap.h
+++ b/dolfin/fem/DofMap.h
@@ -104,7 +104,7 @@ namespace dolfin
{ return _is_view; }
/// Return the dimension of the global finite element function
- /// space
+ /// space. Use index_map()->size() to get the local dimension.
///
/// *Returns*
/// std::size_t
diff --git a/dolfin/fem/DofMapBuilder.cpp b/dolfin/fem/DofMapBuilder.cpp
index a534fd5..0c20ab1 100644
--- a/dolfin/fem/DofMapBuilder.cpp
+++ b/dolfin/fem/DofMapBuilder.cpp
@@ -1077,7 +1077,7 @@ std::shared_ptr<const ufc::dofmap> DofMapBuilder::build_ufc_node_graph(
mesh.init(d);
DistributedMeshTools::number_entities(mesh, d);
num_mesh_entities_local[d] = mesh.num_entities(d);
- num_mesh_entities_global_unconstrained[d] = mesh.size_global(d);
+ num_mesh_entities_global_unconstrained[d] = mesh.num_entities_global(d);
}
}
@@ -1181,8 +1181,8 @@ DofMapBuilder::build_ufc_node_graph_constrained(
for (std::size_t d = 0; d <= D; ++d)
needs_entities[d] = ufc_dofmap->needs_mesh_entities(d);
- // Generate and number required mesh entities (local & global, and
- // constrained global)
+ // Generate and number required mesh entities (local & global, and constrained
+ // global)
std::vector<std::size_t> num_mesh_entities_local(D + 1, 0);
std::vector<std::size_t> num_mesh_entities_global_unconstrained(D + 1, 0);
std::vector<bool> required_mesh_entities(D + 1, false);
@@ -1194,7 +1194,7 @@ DofMapBuilder::build_ufc_node_graph_constrained(
mesh.init(d);
DistributedMeshTools::number_entities(mesh, d);
num_mesh_entities_local[d] = mesh.num_entities(d);
- num_mesh_entities_global_unconstrained[d] = mesh.size_global(d);
+ num_mesh_entities_global_unconstrained[d] = mesh.num_entities_global(d);
}
}
diff --git a/dolfin/fem/DofMapBuilder.h b/dolfin/fem/DofMapBuilder.h
index 7a2e8fd..4fc0811 100644
--- a/dolfin/fem/DofMapBuilder.h
+++ b/dolfin/fem/DofMapBuilder.h
@@ -43,6 +43,7 @@ namespace dolfin
class IndexMap;
class SubDomain;
class UFC;
+ class Cell;
/// Builds a DofMap on a Mesh
diff --git a/dolfin/fem/MultiMeshAssembler.cpp b/dolfin/fem/MultiMeshAssembler.cpp
index 5272e4e..ff17215 100644
--- a/dolfin/fem/MultiMeshAssembler.cpp
+++ b/dolfin/fem/MultiMeshAssembler.cpp
@@ -27,7 +27,10 @@
#include <dolfin/log/log.h>
#include <dolfin/mesh/Cell.h>
#include <dolfin/mesh/Mesh.h>
+#include <dolfin/mesh/Facet.h>
#include <dolfin/mesh/MultiMesh.h>
+#include <dolfin/function/MultiMeshFunction.h>
+#include <dolfin/function/FunctionSpace.h>
#include "SparsityPatternBuilder.h"
#include "UFC.h"
@@ -60,6 +63,9 @@ void MultiMeshAssembler::assemble(GenericTensor& A, const MultiMeshForm& a)
// Assemble over uncut cells
_assemble_uncut_cells(A, a);
+ // Assemble over exterior facets
+ _assemble_uncut_exterior_facets(A, a);
+
// Assemble over cut cells
_assemble_cut_cells(A, a);
@@ -73,13 +79,107 @@ void MultiMeshAssembler::assemble(GenericTensor& A, const MultiMeshForm& a)
if (finalize_tensor)
A.apply("add");
- // Lock any remaining inactive dofs
- if (A.rank() == 2)
- static_cast<GenericMatrix&>(A).ident_zeros();
-
end();
}
//-----------------------------------------------------------------------------
+void MultiMeshAssembler::_assemble_uncut_exterior_facets(GenericTensor& A,
+ const MultiMeshForm& a)
+{
+ // FIXME: This implementation assumes that there is one background mesh
+ // that contains the entire exterior facet.
+
+ // Get form rank
+ const std::size_t form_rank = a.rank();
+
+ // Extract multimesh
+ std::shared_ptr<const MultiMesh> multimesh = a.multimesh();
+
+ // Collect pointers to dof maps
+ std::vector<const MultiMeshDofMap*> dofmaps;
+ for (std::size_t i = 0; i < form_rank; i++)
+ dofmaps.push_back(a.function_space(i)->dofmap().get());
+
+ // Vector to hold dof map for a cell
+ std::vector<ArrayView<const dolfin::la_index>> dofs(form_rank);
+
+ // Initialize variables that will be reused throughout assembly
+ ufc::cell ufc_cell;
+ std::vector<double> coordinate_dofs;
+
+ // Assembly exterior uncut facets on mesh 0, the background mesh
+ int part = 0;
+
+ log(PROGRESS, "Assembling multimesh form over uncut facets on part %d.", part);
+
+ // Get form for current part
+ const Form& a_part = *a.part(part);
+
+ // Create data structure for local assembly data
+ UFC ufc_part(a_part);
+
+ // Extract mesh
+ dolfin_assert(a_part.mesh());
+ const Mesh& mesh_part = *(a_part.mesh());
+
+ // FIXME: Handle subdomains
+
+ // Exterior facet integral
+ const ufc::exterior_facet_integral* integral = ufc_part.default_exterior_facet_integral.get();
+
+ // Skip if we don't have a facet integral
+ if (!integral) return;
+
+ // Iterate over uncut cells
+ for (FacetIterator facet(mesh_part); !facet.end(); ++facet)
+ {
+
+ // Only consider exterior facets
+ if (!facet->exterior())
+ {
+ continue;
+ }
+
+ const std::size_t D = mesh_part.topology().dim();
+
+ // Get mesh cell to which mesh facet belongs (pick first, there is
+ // only one)
+ dolfin_assert(facet->num_entities(D) == 1);
+ Cell mesh_cell(mesh_part, facet->entities(D)[0]);
+
+ // Check that cell is not a ghost
+ dolfin_assert(!mesh_cell.is_ghost());
+
+ // Get local index of facet with respect to the cell
+ const std::size_t local_facet = mesh_cell.index(*facet);
+
+ // Update UFC cell
+ mesh_cell.get_cell_data(ufc_cell, local_facet);
+ mesh_cell.get_coordinate_dofs(coordinate_dofs);
+
+ // Update UFC object
+ ufc_part.update(mesh_cell, coordinate_dofs, ufc_cell,
+ integral->enabled_coefficients());
+
+ // Get local-to-global dof maps for cell
+ for (std::size_t i = 0; i < form_rank; ++i)
+ {
+ const auto dofmap = a.function_space(i)->dofmap()->part(part);
+ const auto dmap = dofmap->cell_dofs(mesh_cell.index());
+ dofs[i] = ArrayView<const dolfin::la_index>(dmap.size(), dmap.data());
+ }
+
+ // Tabulate cell tensor
+ integral->tabulate_tensor(ufc_part.A.data(),
+ ufc_part.w(),
+ coordinate_dofs.data(),
+ local_facet,
+ ufc_cell.orientation);
+
+ // Add entries to global tensor
+ A.add(ufc_part.A.data(), dofs);
+ }
+}
+//-----------------------------------------------------------------------------
void MultiMeshAssembler::_assemble_uncut_cells(GenericTensor& A,
const MultiMeshForm& a)
{
@@ -204,7 +304,7 @@ void MultiMeshAssembler::_assemble_cut_cells(GenericTensor& A,
// Get cut cells and quadrature rules
const std::vector<unsigned int>& cut_cells = multimesh->cut_cells(part);
- const auto& quadrature_rules = multimesh->quadrature_rule_cut_cells(part);
+ const auto& quadrature_rules = multimesh->quadrature_rules_cut_cells(part);
// Iterate over cut cells
for (auto it = cut_cells.begin(); it != cut_cells.end(); ++it)
@@ -283,6 +383,21 @@ void MultiMeshAssembler::_assemble_interface(GenericTensor& A,
// Get form rank
const std::size_t form_rank = a.rank();
+ // Get multimesh coefficients
+ // These are updated in within this assembly loop
+ std::map<std::size_t, std::shared_ptr<const MultiMeshFunction> >
+ multimesh_coefficients = a.multimesh_coefficients();
+
+ // Identify the coefficents that are not MultiMeshFunction
+ // These will be updated by UFC
+ // It is assumed that the coefficents are the same for all parts
+ std::vector<bool> ufc_enabled_coefficients;
+ for (std::size_t i = 0; i < a.part(0)->coefficients().size(); i++)
+ {
+ bool ufc_update = multimesh_coefficients.find(i) == multimesh_coefficients.end();
+ ufc_enabled_coefficients.push_back(ufc_update);
+ }
+
// Collect pointers to dof maps
std::vector<const MultiMeshDofMap*> dofmaps;
for (std::size_t i = 0; i < form_rank; i++)
@@ -321,7 +436,7 @@ void MultiMeshAssembler::_assemble_interface(GenericTensor& A,
if (!integral) continue;
// Get quadrature rules
- const auto& quadrature_rules = multimesh->quadrature_rule_interface(part);
+ const auto& quadrature_rules = multimesh->quadrature_rules_interface(part);
// Get collision map
const auto& cmap = multimesh->collision_map_cut_cells(part);
@@ -363,38 +478,56 @@ void MultiMeshAssembler::_assemble_interface(GenericTensor& A,
continue;
// Create aliases for cells to simplify notation
- const Cell& cell_0 = cut_cell;
+ const std::size_t& part_1 = cutting_part;
+ const std::size_t& part_0 = part;
const Cell& cell_1 = cutting_cell;
+ const Cell& cell_0 = cut_cell;
// Update to current pair of cells
+ // Let UFC update the coefficients that are not MultiMeshFunction
cell_0.get_cell_data(ufc_cell[0], 0);
cell_1.get_cell_data(ufc_cell[1], 0);
cell_0.get_coordinate_dofs(coordinate_dofs[0]);
cell_1.get_coordinate_dofs(coordinate_dofs[1]);
ufc_part.update(cell_0, coordinate_dofs[0], ufc_cell[0],
- cell_1, coordinate_dofs[1], ufc_cell[1]);
+ cell_1, coordinate_dofs[1], ufc_cell[1],
+ ufc_enabled_coefficients);
+
+ // Manually update multimesh coefficients
+ for (auto it : multimesh_coefficients)
+ {
+ std::size_t coefficient_number = it.first;
+ std::shared_ptr<const MultiMeshFunction> coefficient = it.second;
+ double** macro_w = ufc_part.macro_w();
+ const FiniteElement& element = *coefficient->function_space()->part(part_0)->element();
+ std::size_t offset = element.space_dimension();
+
+ double * w_0 = macro_w[coefficient_number];
+ double * w_1 = macro_w[coefficient_number] + offset;
+
+ coefficient->restrict(w_0, element,
+ part_0, cell_0, coordinate_dofs[0].data(), ufc_cell[0]);
+ coefficient->restrict(w_1, element,
+ part_1, cell_1, coordinate_dofs[1].data(), ufc_cell[1]);
+ }
// Collect vertex coordinates
macro_coordinate_dofs.resize(coordinate_dofs[0].size() +
coordinate_dofs[0].size());
- std::copy(coordinate_dofs[0].begin(),
- coordinate_dofs[0].end(),
+ std::copy(coordinate_dofs[0].begin(), coordinate_dofs[0].end(),
macro_coordinate_dofs.begin());
- std::copy(coordinate_dofs[1].begin(),
- coordinate_dofs[1].end(),
- macro_coordinate_dofs.begin()
- + coordinate_dofs[0].size());
+ std::copy(coordinate_dofs[1].begin(), coordinate_dofs[1].end(),
+ macro_coordinate_dofs.begin() + coordinate_dofs[0].size());
// Tabulate dofs for each dimension on macro element
for (std::size_t i = 0; i < form_rank; i++)
{
// Get dofs for cut mesh
- const auto dofmap_0 = a.function_space(i)->dofmap()->part(part);
+ const auto dofmap_0 = a.function_space(i)->dofmap()->part(part_0);
const auto dofs_0 = dofmap_0->cell_dofs(cell_0.index());
// Get dofs for cutting mesh
- const auto dofmap_1
- = a.function_space(i)->dofmap()->part(cutting_part);
+ const auto dofmap_1 = a.function_space(i)->dofmap()->part(part_1);
const auto dofs_1 = dofmap_1->cell_dofs(cell_1.index());
// Create space in macro dof vector
@@ -454,6 +587,21 @@ void MultiMeshAssembler::_assemble_overlap(GenericTensor& A,
// Get form rank
const std::size_t form_rank = a.rank();
+ // Get multimesh coefficients
+ // These are updated in this assembly loop
+ std::map<std::size_t, std::shared_ptr<const MultiMeshFunction> >
+ multimesh_coefficients = a.multimesh_coefficients();
+
+ // Identify the coefficients that are not MultiMeshFunction
+ // These will be updated by UFC
+ // It is assumed that the coefficents are the same for all parts
+ std::vector<bool> ufc_enabled_coefficients;
+ for (std::size_t i = 0; i < a.part(0)->coefficients().size(); i++)
+ {
+ bool ufc_update = multimesh_coefficients.find(i) == multimesh_coefficients.end();
+ ufc_enabled_coefficients.push_back(ufc_update);
+ }
+
// Collect pointers to dof maps
std::vector<const MultiMeshDofMap*> dofmaps;
for (std::size_t i = 0; i < form_rank; i++)
@@ -491,7 +639,7 @@ void MultiMeshAssembler::_assemble_overlap(GenericTensor& A,
if (!integral) continue;
// Get quadrature rules
- const auto& quadrature_rules = multimesh->quadrature_rule_overlap(part);
+ const auto& quadrature_rules = multimesh->quadrature_rules_overlap(part);
// Get collision map
const auto& cmap = multimesh->collision_map_cut_cells(part);
@@ -529,37 +677,56 @@ void MultiMeshAssembler::_assemble_overlap(GenericTensor& A,
continue;
// Create aliases for cells to simplify notation
- const Cell& cell_0 = cut_cell;
+ const std::size_t& part_1 = cutting_part;
+ const std::size_t& part_0 = part;
const Cell& cell_1 = cutting_cell;
+ const Cell& cell_0 = cut_cell;
// Update to current pair of cells
+ // Let UFC update the coefficients that are not MultiMeshFunction
cell_0.get_cell_data(ufc_cell[0], 0);
cell_1.get_cell_data(ufc_cell[1], 0);
cell_0.get_coordinate_dofs(coordinate_dofs[0]);
cell_1.get_coordinate_dofs(coordinate_dofs[1]);
ufc_part.update(cell_0, coordinate_dofs[0], ufc_cell[0],
- cell_1, coordinate_dofs[1], ufc_cell[1]);
+ cell_1, coordinate_dofs[1], ufc_cell[1],
+ ufc_enabled_coefficients);
+ // Manually update multimesh coefficients
+ for (auto it : multimesh_coefficients)
+ {
+ std::size_t coefficient_number = it.first;
+ std::shared_ptr<const MultiMeshFunction> coefficient = it.second;
+ double** macro_w = ufc_part.macro_w();
+ const FiniteElement& element = *coefficient->function_space()->part(part_0)->element();
+ std::size_t offset = element.space_dimension();
+
+ double * w_0 = macro_w[coefficient_number];
+ double * w_1 = macro_w[coefficient_number] + offset;
+
+ coefficient->restrict(w_0, element,
+ part_0, cell_0, coordinate_dofs[0].data(), ufc_cell[0]);
+ coefficient->restrict(w_1, element,
+ part_1, cell_1, coordinate_dofs[1].data(), ufc_cell[1]);
+ }
// Collect vertex coordinates
macro_coordinate_dofs.resize(coordinate_dofs[0].size() +
coordinate_dofs[0].size());
- std::copy(coordinate_dofs[0].begin(),
- coordinate_dofs[0].end(),
+ std::copy(coordinate_dofs[0].begin(), coordinate_dofs[0].end(),
macro_coordinate_dofs.begin());
- std::copy(coordinate_dofs[1].begin(),
- coordinate_dofs[1].end(),
+ std::copy(coordinate_dofs[1].begin(), coordinate_dofs[1].end(),
macro_coordinate_dofs.begin() + coordinate_dofs[0].size());
// Tabulate dofs for each dimension on macro element
for (std::size_t i = 0; i < form_rank; i++)
{
// Get dofs for cut mesh
- const auto dofmap_0 = a.function_space(i)->dofmap()->part(part);
+ const auto dofmap_0 = a.function_space(i)->dofmap()->part(part_0);
const auto dofs_0 = dofmap_0->cell_dofs(cell_0.index());
// Get dofs for cutting mesh
- const auto dofmap_1 = a.function_space(i)->dofmap()->part(cutting_part);
+ const auto dofmap_1 = a.function_space(i)->dofmap()->part(part_1);
const auto dofs_1 = dofmap_1->cell_dofs(cell_1.index());
// Create space in macro dof vector
diff --git a/dolfin/fem/MultiMeshAssembler.h b/dolfin/fem/MultiMeshAssembler.h
index a2e296c..ea2ac9d 100644
--- a/dolfin/fem/MultiMeshAssembler.h
+++ b/dolfin/fem/MultiMeshAssembler.h
@@ -65,6 +65,9 @@ namespace dolfin
// Assemble over cut cells
void _assemble_cut_cells(GenericTensor& A, const MultiMeshForm& a);
+ // Assemble over uncut exterior facets
+ void _assemble_uncut_exterior_facets(GenericTensor& A, const MultiMeshForm& a);
+
// Assemble over interface
void _assemble_interface(GenericTensor& A, const MultiMeshForm& a);
diff --git a/dolfin/fem/MultiMeshDirichletBC.cpp b/dolfin/fem/MultiMeshDirichletBC.cpp
index abc5460..b24db82 100644
--- a/dolfin/fem/MultiMeshDirichletBC.cpp
+++ b/dolfin/fem/MultiMeshDirichletBC.cpp
@@ -96,6 +96,15 @@ MultiMeshDirichletBC::MultiMeshDirichletBC(std::shared_ptr<const MultiMeshFuncti
_bcs.push_back(bc);
}
//-----------------------------------------------------------------------------
+MultiMeshDirichletBC::MultiMeshDirichletBC(const MultiMeshDirichletBC& bc)
+{
+ *this = bc;
+
+ // Iterate over boundary conditions and call the copy constructor
+ for (std::size_t part = 0; part < _bcs.size(); part++)
+ _bcs[part] = std::make_shared<DirichletBC>(*_bcs[part]);
+}
+//-----------------------------------------------------------------------------
MultiMeshDirichletBC::~MultiMeshDirichletBC()
{
// Do nothing
@@ -245,6 +254,44 @@ void MultiMeshDirichletBC::apply(GenericMatrix& A,
}
}
//-----------------------------------------------------------------------------
+void MultiMeshDirichletBC::zero(GenericMatrix& A) const
+{
+ // Check whether we have a list of boundary conditions, one for each
+ // part, or if we have a single boundary condition for a single
+ // part.
+
+ if (_sub_domain)
+ {
+ // Iterate over boundary conditions
+ for (std::size_t part = 0; part < _bcs.size(); part++)
+ {
+ // Set current part for subdomain wrapper
+ dolfin_assert(_sub_domain);
+ _sub_domain->set_current_part(part);
+
+ // Apply boundary condition for current part
+ _bcs[part]->zero(A);
+ }
+ }
+ else
+ {
+ dolfin_assert(_bcs.size() == 1);
+
+ // Apply the single boundary condition
+ _bcs[0]->zero(A);
+ }
+}
+//-----------------------------------------------------------------------------
+void MultiMeshDirichletBC::homogenize()
+{
+ // Iterate over boundary conditions
+ for (std::size_t part = 0; part < _bcs.size(); part++)
+ {
+ // Homogenize boundary condition
+ _bcs[part]->homogenize();
+ }
+}
+//-----------------------------------------------------------------------------
MultiMeshDirichletBC::MultiMeshSubDomain::MultiMeshSubDomain
(std::shared_ptr<const SubDomain> sub_domain,
std::shared_ptr<const MultiMesh> multimesh,
diff --git a/dolfin/fem/MultiMeshDirichletBC.h b/dolfin/fem/MultiMeshDirichletBC.h
index 0cb6d36..8a3b636 100644
--- a/dolfin/fem/MultiMeshDirichletBC.h
+++ b/dolfin/fem/MultiMeshDirichletBC.h
@@ -89,6 +89,13 @@ namespace dolfin
std::size_t part,
std::string method="topological");
+ /// Copy constructor. Either cached DOF data are copied.
+ ///
+ /// *Arguments*
+ /// bc (_MultiMeshDirichletBC_)
+ /// The object to be copied.
+ MultiMeshDirichletBC(const MultiMeshDirichletBC& bc);
+
/// Destructor
~MultiMeshDirichletBC();
@@ -134,6 +141,15 @@ namespace dolfin
GenericVector& b,
const GenericVector& x) const;
+ /// Zero the rows in a matrix A corresponding to boundary dofs
+ ///
+ /// @param A (_GenericMatrix_)
+ /// The matrix to zero rows in.
+ void zero(GenericMatrix& A) const;
+
+ /// Set value to 0.0
+ void homogenize();
+
private:
// Subclass of SubDomain wrapping user-defined subdomain
@@ -172,7 +188,7 @@ namespace dolfin
};
// List of boundary conditions for parts
- std::vector<std::shared_ptr<const DirichletBC>> _bcs;
+ std::vector<std::shared_ptr<DirichletBC>> _bcs;
// Wrapper of user-defined subdomain
mutable std::shared_ptr<MultiMeshSubDomain> _sub_domain;
diff --git a/dolfin/fem/MultiMeshDofMap.cpp b/dolfin/fem/MultiMeshDofMap.cpp
index 154ebd8..a78e5fd 100644
--- a/dolfin/fem/MultiMeshDofMap.cpp
+++ b/dolfin/fem/MultiMeshDofMap.cpp
@@ -20,6 +20,7 @@
#include <dolfin/common/types.h>
#include <dolfin/common/NoDeleter.h>
+#include <dolfin/common/ArrayView.h>
#include <dolfin/mesh/Cell.h>
#include <dolfin/function/FunctionSpace.h>
#include <dolfin/function/MultiMeshFunctionSpace.h>
@@ -158,3 +159,49 @@ std::string MultiMeshDofMap::str(bool verbose) const
return s.str();
}
//-----------------------------------------------------------------------------
+std::vector<dolfin::la_index>
+MultiMeshDofMap::inactive_dofs(const MultiMesh &multimesh, std::size_t part) const
+{
+ std::shared_ptr<const GenericDofMap> dofmap_part = this->part(part);
+
+ // Get all dofs on covered cells
+ std::vector<unsigned int> covered_cells = multimesh.covered_cells(part);
+
+ std::vector<dolfin::la_index> covered_dofs;
+ covered_dofs.reserve(dofmap_part->max_element_dofs() * covered_cells.size());
+ for (unsigned int cell : covered_cells)
+ {
+ const auto dmap = dofmap_part->cell_dofs(cell);
+ const auto local_dofs = ArrayView<const dolfin::la_index>(dmap.size(), dmap.data());
+ std::copy(local_dofs.begin(), local_dofs.end(), std::back_inserter(covered_dofs));
+ }
+ // Sort and remove duplicates
+ std::sort(covered_dofs.begin(), covered_dofs.end());
+ covered_dofs.erase(std::unique(covered_dofs.begin(), covered_dofs.end()),
+ covered_dofs.end());
+
+ // Get all dofs on cut cells
+ std::vector<unsigned int> cut_cells = multimesh.cut_cells(part);
+
+ std::vector<dolfin::la_index> cut_cell_dofs;
+ cut_cell_dofs.reserve(dofmap_part->max_element_dofs() * cut_cells.size());
+ for (unsigned int cell : cut_cells)
+ {
+ const auto dmap = dofmap_part->cell_dofs(cell);
+ const auto local_dofs = ArrayView<const dolfin::la_index>(dmap.size(), dmap.data());
+ std::copy(local_dofs.begin(), local_dofs.end(), std::back_inserter(cut_cell_dofs));
+ }
+
+ // Sort and remove duplicates
+ std::sort(cut_cell_dofs.begin(), cut_cell_dofs.end());
+ cut_cell_dofs.erase(std::unique(cut_cell_dofs.begin(), cut_cell_dofs.end()),
+ cut_cell_dofs.end());
+
+ // Remove cut cell dofs from covered dofs
+ std::vector<dolfin::la_index> _inactive_dofs;
+ std::set_difference(covered_dofs.begin(), covered_dofs.end(),
+ cut_cell_dofs.begin(), cut_cell_dofs.end(),
+ std::inserter(_inactive_dofs, _inactive_dofs.begin()));
+ return _inactive_dofs;
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/fem/MultiMeshDofMap.h b/dolfin/fem/MultiMeshDofMap.h
index df70302..7519d7f 100644
--- a/dolfin/fem/MultiMeshDofMap.h
+++ b/dolfin/fem/MultiMeshDofMap.h
@@ -22,12 +22,14 @@
#define __MULTI_MESH_DOF_MAP_H
#include "GenericDofMap.h"
+#include <dolfin/mesh/MultiMesh.h>
namespace dolfin
{
// Forward declarations
class MultiMeshFunctionSpace;
+ class MultiMesh;
/// This class handles the mapping of degrees of freedom for MultiMesh
/// function spaces.
@@ -91,6 +93,10 @@ namespace dolfin
/// Return informal string representation (pretty-print)
std::string str(bool verbose) const;
+ /// Return inactive dofs
+ std::vector<dolfin::la_index> inactive_dofs(const MultiMesh &multimesh,
+ std::size_t part_id) const;
+
private:
// Index Map containing total global dimension (sum of parts)
diff --git a/dolfin/fem/MultiMeshForm.cpp b/dolfin/fem/MultiMeshForm.cpp
index aec8891..fe2964e 100644
--- a/dolfin/fem/MultiMeshForm.cpp
+++ b/dolfin/fem/MultiMeshForm.cpp
@@ -149,3 +149,31 @@ void MultiMeshForm::clear()
_forms.clear();
}
//-----------------------------------------------------------------------------
+void MultiMeshForm::set_multimesh_coefficient(std::size_t i,
+ std::shared_ptr<const MultiMeshFunction> coefficient)
+{
+ dolfin_assert(coefficient);
+ _multimesh_coefficients[i] = coefficient;
+}
+//-----------------------------------------------------------------------------
+std::map<std::size_t, std::shared_ptr<const MultiMeshFunction>>
+ MultiMeshForm::multimesh_coefficients() const
+{
+ return _multimesh_coefficients;
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<const MultiMeshFunction>
+ MultiMeshForm::multimesh_coefficient(std::size_t i) const
+{
+ dolfin_assert(i < _multimesh_coefficients.size());
+ return _multimesh_coefficients.at(i);
+}
+//-----------------------------------------------------------------------------
+std::vector<std::size_t> MultiMeshForm::multimesh_coefficient_keys() const
+{
+ std::vector<size_t> keys;
+ for (auto it = _multimesh_coefficients.begin(); it != _multimesh_coefficients.end(); it++)
+ keys.push_back(it->first);
+ return keys;
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/fem/MultiMeshForm.h b/dolfin/fem/MultiMeshForm.h
index 97aceca..2508bba 100644
--- a/dolfin/fem/MultiMeshForm.h
+++ b/dolfin/fem/MultiMeshForm.h
@@ -29,6 +29,7 @@ namespace dolfin
// Forward declarations
class MultiMeshFunctionSpace;
+ class MultiMeshFunction;
class MultiMesh;
class Form;
@@ -103,6 +104,19 @@ namespace dolfin
/// Clear MultiMesh form
void clear();
+ /// Set MultiMeshCoeeficient
+ void set_multimesh_coefficient(std::size_t i,
+ std::shared_ptr<const MultiMeshFunction> coefficient);
+
+ /// Get all MultiMesh Coefficients
+ std::map<std::size_t, std::shared_ptr<const MultiMeshFunction>> multimesh_coefficients() const;
+
+ /// Get one multimesh coefficient
+ std::shared_ptr<const MultiMeshFunction> multimesh_coefficient(std::size_t i) const;
+
+ /// Get multimesh coefficient keys
+ std::vector<std::size_t> multimesh_coefficient_keys() const;
+
private:
// The rank of the form
@@ -117,6 +131,10 @@ namespace dolfin
// List of forms (one for each part)
std::vector<std::shared_ptr<const Form>> _forms;
+ // Map of MultiMesh coefficents
+ std::map<std::size_t, std::shared_ptr<const MultiMeshFunction>> _multimesh_coefficients;
+
+
};
}
diff --git a/dolfin/fem/PETScDMCollection.cpp b/dolfin/fem/PETScDMCollection.cpp
index 67009df..191a0fb 100644
--- a/dolfin/fem/PETScDMCollection.cpp
+++ b/dolfin/fem/PETScDMCollection.cpp
@@ -24,6 +24,7 @@
#include <dolfin/la/PETScMatrix.h>
#include <dolfin/la/PETScVector.h>
#include <dolfin/geometry/BoundingBoxTree.h>
+#include <dolfin/function/Function.h>
#include <dolfin/fem/FiniteElement.h>
#include <dolfin/fem/GenericDofMap.h>
#include <dolfin/common/RangedIndexSet.h>
diff --git a/dolfin/fem/PETScDMCollection.h b/dolfin/fem/PETScDMCollection.h
index a98d20a..325700a 100644
--- a/dolfin/fem/PETScDMCollection.h
+++ b/dolfin/fem/PETScDMCollection.h
@@ -40,7 +40,7 @@ namespace dolfin
///
/// Warning: This classs is highly experimental and will change
- class PETScDMCollection
+ class PETScDMCollection : public PETScObject
{
public:
diff --git a/dolfin/fem/UFC.h b/dolfin/fem/UFC.h
index ce8dc42..79351dc 100644
--- a/dolfin/fem/UFC.h
+++ b/dolfin/fem/UFC.h
@@ -97,7 +97,7 @@ namespace dolfin
/// Pointer to coefficient data. Used to support UFC
/// interface. None const version
- double* * w()
+ double** w()
{ return w_pointer.data(); }
/// Pointer to macro element coefficient data. Used to support UFC
@@ -105,6 +105,11 @@ namespace dolfin
const double* const * macro_w() const
{ return macro_w_pointer.data(); }
+ /// Pointer to macro element coefficient data. Used to support UFC
+ /// interface. Non-const version.
+ double** macro_w()
+ { return macro_w_pointer.data(); }
+
private:
// Finite elements for coefficients
diff --git a/dolfin/fem/fem_utils.cpp b/dolfin/fem/fem_utils.cpp
index ad9e2bf..6ba9962 100644
--- a/dolfin/fem/fem_utils.cpp
+++ b/dolfin/fem/fem_utils.cpp
@@ -18,6 +18,7 @@
#include <dolfin/common/ArrayView.h>
#include <dolfin/fem/GenericDofMap.h>
#include <dolfin/function/FunctionSpace.h>
+#include <dolfin/function/Function.h>
#include <dolfin/mesh/Mesh.h>
#include <dolfin/mesh/Vertex.h>
#include <dolfin/mesh/MeshEntityIterator.h>
diff --git a/dolfin/function/Function.cpp b/dolfin/function/Function.cpp
index 1702dad..950b5da 100644
--- a/dolfin/function/Function.cpp
+++ b/dolfin/function/Function.cpp
@@ -295,8 +295,13 @@ void Function::eval(Array<double>& values, const Array<double>& x) const
// If not found, use the closest cell
if (id == std::numeric_limits<unsigned int>::max())
{
- if (_allow_extrapolation)
- id = mesh.bounding_box_tree()->compute_closest_entity(point).first;
+ // Check if the closest cell is within DOLFIN_EPS. This we can
+ // allow without _allow_extrapolation
+ std::pair<unsigned int, double> close
+ = mesh.bounding_box_tree()->compute_closest_entity(point);
+
+ if (_allow_extrapolation or close.second < DOLFIN_EPS)
+ id = close.first;
else
{
dolfin_error("Function.cpp",
diff --git a/dolfin/function/FunctionAssigner.cpp b/dolfin/function/FunctionAssigner.cpp
index be51656..d8b569d 100644
--- a/dolfin/function/FunctionAssigner.cpp
+++ b/dolfin/function/FunctionAssigner.cpp
@@ -23,6 +23,7 @@
#include <dolfin/common/types.h>
#include <dolfin/fem/GenericDofMap.h>
#include <dolfin/function/FunctionSpace.h>
+#include <dolfin/function/Function.h>
#include <dolfin/la/GenericVector.h>
#include <dolfin/log/log.h>
#include <dolfin/mesh/Cell.h>
diff --git a/dolfin/function/FunctionSpace.h b/dolfin/function/FunctionSpace.h
index ee040a5..313d5ff 100644
--- a/dolfin/function/FunctionSpace.h
+++ b/dolfin/function/FunctionSpace.h
@@ -149,7 +149,8 @@ namespace dolfin
/// The dofmap.
std::shared_ptr<const GenericDofMap> dofmap() const;
- /// Return dimension of function space
+ /// Return global dimension of the function space.
+ /// Equivalent to dofmap()->global_dimension()
///
/// *Returns*
/// std::size_t
diff --git a/dolfin/function/MultiMeshCoefficientAssigner.cpp b/dolfin/function/MultiMeshCoefficientAssigner.cpp
index 5711558..996ea07 100644
--- a/dolfin/function/MultiMeshCoefficientAssigner.cpp
+++ b/dolfin/function/MultiMeshCoefficientAssigner.cpp
@@ -54,13 +54,16 @@ void MultiMeshCoefficientAssigner::operator=
}
//-----------------------------------------------------------------------------
void MultiMeshCoefficientAssigner::operator=
-(const MultiMeshFunction& coefficient)
+(std::shared_ptr<const MultiMeshFunction> coefficient)
{
+ dolfin_assert(coefficient);
// Assign to all parts of form
for (std::size_t part = 0; part < _form.num_parts(); part++)
{
Form& a = const_cast<Form&>(*_form.part(part));
- a.set_coefficient(_number, coefficient.part(part));
+ a.set_coefficient(_number, coefficient->part(part));
}
+ // Assign to multimesh form
+ _form.set_multimesh_coefficient(_number, coefficient);
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/function/MultiMeshCoefficientAssigner.h b/dolfin/function/MultiMeshCoefficientAssigner.h
index 5187820..8455e29 100644
--- a/dolfin/function/MultiMeshCoefficientAssigner.h
+++ b/dolfin/function/MultiMeshCoefficientAssigner.h
@@ -59,7 +59,7 @@ namespace dolfin
void operator= (std::shared_ptr<const GenericFunction> coefficient);
/// Assign coefficient from MultiMeshFunction
- void operator= (const MultiMeshFunction& coefficient);
+ void operator= (std::shared_ptr<const MultiMeshFunction> coefficient);
private:
diff --git a/dolfin/function/MultiMeshFunction.cpp b/dolfin/function/MultiMeshFunction.cpp
index 83fd420..cdbafde 100644
--- a/dolfin/function/MultiMeshFunction.cpp
+++ b/dolfin/function/MultiMeshFunction.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2016 Anders Logg
+// Copyright (C) 2013-2017 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -16,12 +16,13 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2013-09-25
-// Last changed: 2016-03-02
+// Last changed: 2017-10-09
#include <dolfin/common/NoDeleter.h>
#include <dolfin/la/GenericVector.h>
#include <dolfin/la/DefaultFactory.h>
#include <dolfin/fem/MultiMeshDofMap.h>
+#include <dolfin/geometry/BoundingBoxTree.h>
#include "Function.h"
#include "FunctionSpace.h"
#include "MultiMeshFunctionSpace.h"
@@ -43,7 +44,7 @@ MultiMeshFunction::MultiMeshFunction(std::shared_ptr<const MultiMeshFunctionSpac
}
//-----------------------------------------------------------------------------
MultiMeshFunction::MultiMeshFunction(std::shared_ptr<const MultiMeshFunctionSpace> V,
- std::shared_ptr<GenericVector> x)
+ std::shared_ptr<GenericVector> x)
: _function_space(V), _vector(x)
{
// We do not check for a subspace since this constructor is used for
@@ -74,13 +75,78 @@ std::shared_ptr<const Function> MultiMeshFunction::part(std::size_t i) const
// Get view of function space for part
std::shared_ptr<const FunctionSpace> V = _function_space->view(i);
- // Insert into cache and return reference
- std::shared_ptr<const Function> ui(new Function(V, _vector));
+ // Create and rename function for part
+ std::shared_ptr<Function> ui(new Function(V, _vector));
+ ui->rename(name(), label());
+
+ // Insert into cache
_function_parts[i] = ui;
return _function_parts.find(i)->second;
}
//-----------------------------------------------------------------------------
+void MultiMeshFunction::assign_part(std::size_t part, const Function& v)
+{
+ // Replace old values with new ones
+ std::size_t start_idx = 0;
+ for (std::size_t j = 0; j < part; ++j)
+ start_idx += _function_space->part(j)->dim();
+
+ const std::size_t N = v.vector()->size();
+
+ std::vector<double> buffer(N);
+ std::vector<la_index> indices(N);
+
+ // Get from [0,N)
+ std::iota(indices.begin(), indices.end(), 0);
+ v.vector()->get_local(buffer.data(), N, indices.data());
+
+ // set [start_idx, N+start_idx)
+ std::iota(indices.begin(), indices.end(), start_idx);
+ _vector->set_local(buffer.data(), N, indices.data());
+
+ // for (dolfin::la_index i = 0; i < (v.vector()->size()); ++i)
+ // _vector->setitem(start_idx+i, v.vector()->getitem(i));
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<const Function> MultiMeshFunction::part(std::size_t i,
+ bool deepcopy) const
+{
+ if (not deepcopy)
+ return part(i);
+
+ assert(i < _function_space->num_parts());
+
+ // Create output function
+ std::shared_ptr<const FunctionSpace> V = _function_space->part(i);
+ std::shared_ptr<Function> ui(new Function(V));
+
+ // Finding the relevant part of the global vector
+ std::size_t start_idx = 0;
+ for (std::size_t j = 0; j < i; ++j)
+ {
+ start_idx += _function_space->part(j)->dim();
+ }
+
+ const std::size_t N = ui->vector()->size();
+ std::vector<double> buffer(N);
+ std::vector<dolfin::la_index> indices(N);
+
+ // Get [start_idx, N+start_idx)
+ std::iota(indices.begin(), indices.end(), start_idx);
+ _vector->get_local(buffer.data(), N, indices.data());
+
+ // set [0, N)
+ std::iota(indices.begin(), indices.end(), 0);
+ ui->vector()->set_local(buffer.data(), N, indices.data());
+
+ // // Copy values into output function
+ // for (dolfin::la_index i = 0; i < (ui->vector()->size()); ++i)
+ // ui->vector()->setitem(i, _vector->getitem(start_idx+i));
+
+ return ui;
+}
+//-----------------------------------------------------------------------------
std::shared_ptr<GenericVector> MultiMeshFunction::vector()
{
dolfin_assert(_vector);
@@ -146,8 +212,95 @@ void MultiMeshFunction::init_vector()
_vector->zero();
}
//-----------------------------------------------------------------------------
+void MultiMeshFunction::restrict(double* w, const FiniteElement& element,
+ std::size_t part,
+ const Cell& dolfin_cell,
+ const double* coordinate_dofs,
+ const ufc::cell& ufc_cell) const
+{
+ dolfin_assert(w);
+ dolfin_assert(_function_space);
+ dolfin_assert(_function_space->dofmap());
+ dolfin_assert(part < _function_space->num_parts());
+
+ // Check if we are restricting to an element of this function space
+ if (_function_space->part(part)->has_element(element)
+ && _function_space->part(part)->has_cell(dolfin_cell))
+ {
+ // Get dofmap for cell
+ const GenericDofMap& dofmap = *_function_space->dofmap()->part(part);
+ const auto dofs = dofmap.cell_dofs(dolfin_cell.index());
+
+ // Note: We should have dofmap.max_element_dofs() == dofs.size() here.
+ // Pick values from vector(s)
+ _vector->get_local(w, dofs.size(), dofs.data());
+ }
+ else
+ {
+ // Restrict as UFC function (by calling eval)
+ restrict_as_ufc_function(w, element, part, dolfin_cell,
+ coordinate_dofs, ufc_cell);
+ }
+}
+//-----------------------------------------------------------------------------
+void MultiMeshFunction::eval(Array<double>& values,
+ const Array<double>& x,
+ std::size_t part,
+ const ufc::cell& ufc_cell) const
+{
+ dolfin_assert(_function_space);
+ dolfin_assert(_function_space->multimesh());
+ const Mesh& mesh = *_function_space->multimesh()->part(part);
+
+ // Check if UFC cell comes from mesh, otherwise
+ // find the cell which contains the point
+ dolfin_assert(ufc_cell.mesh_identifier >= 0);
+ if (ufc_cell.mesh_identifier == (int) mesh.id())
+ {
+ const Cell cell(mesh, ufc_cell.index);
+ this->part(part)->eval(values, x, cell, ufc_cell);
+ }
+ else
+ this->part(part)->eval(values, x);
+}
+//-----------------------------------------------------------------------------
+void MultiMeshFunction::eval(Array<double>& values,
+ const Array<double>& x) const
+{
+ dolfin_assert(_function_space);
+ dolfin_assert(_function_space->multimesh());
+ const MultiMesh& multimesh = *_function_space->multimesh();
+
+ // Iterate over meshes from top to bottom
+ for (std::size_t j = 0; j < multimesh.num_parts(); j++)
+ {
+ std::size_t part = multimesh.num_parts() - 1 - j;
+
+ // Stop if we reached the bottom part (layer) or found layer containing point
+ if (part == 0 or multimesh.part(part)->bounding_box_tree()->collides_entity(Point(x)))
+ {
+ this->part(part)->eval(values, x);
+ break;
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+void MultiMeshFunction::restrict_as_ufc_function(double* w,
+ const FiniteElement& element,
+ std::size_t part,
+ const Cell& dolfin_cell,
+ const double* coordinate_dofs,
+ const ufc::cell& ufc_cell) const
+{
+ dolfin_assert(w);
+
+ // Evaluate dofs to get the expansion coefficients
+ element.evaluate_dofs(w, *this->part(part), coordinate_dofs, ufc_cell.orientation,
+ ufc_cell);
+}
+//-----------------------------------------------------------------------------
void MultiMeshFunction::compute_ghost_indices(std::pair<std::size_t, std::size_t> range,
- std::vector<la_index>& ghost_indices) const
+ std::vector<la_index>& ghost_indices) const
{
// NOTE: Well, don't implement me! Rather rewrite init_vector().
// See Function::init_vector().
diff --git a/dolfin/function/MultiMeshFunction.h b/dolfin/function/MultiMeshFunction.h
index 9c9de40..eea6ab6 100644
--- a/dolfin/function/MultiMeshFunction.h
+++ b/dolfin/function/MultiMeshFunction.h
@@ -33,6 +33,7 @@ namespace dolfin
class GenericVector;
class Function;
class MultiMeshFunction;
+ class FiniteElement;
/// This class represents a function on a cut and composite finite
/// element function space (MultiMesh) defined on one or more possibly
@@ -68,6 +69,15 @@ namespace dolfin
/// Destructor
virtual ~MultiMeshFunction();
+ /// Assign Function to part of a mesh
+ ///
+ /// *Arguments*
+ /// a (int)
+ /// Part mesh assigned to
+ /// V (_Function_)
+ /// The vector
+ void assign_part(std::size_t a, const Function& v);
+
/// Return function (part) number i
///
/// *Returns*
@@ -75,6 +85,14 @@ namespace dolfin
/// Function (part) number i
std::shared_ptr<const Function> part(std::size_t i) const;
+ /// Return a (deepcopy) function (part) number i
+ ///
+ /// *Returns*
+ /// _Function_
+ /// Function (part) number i
+ /// bool deepcopy flag
+ std::shared_ptr<const Function> part(std::size_t i, bool deepcopy) const;
+
/// Return vector of expansion coefficients (non-const version)
///
/// *Returns*
@@ -99,6 +117,50 @@ namespace dolfin
return _function_space;
}
+ /// Restrict function to local cell in given part (compute expansion coefficients w)
+ ///
+ /// *Arguments*
+ /// w (list of doubles)
+ /// Expansion coefficients.
+ /// element (_FiniteElement_)
+ /// The element.
+ /// part (std::size_t)
+ /// The mesh part
+ /// dolfin_cell (_Cell_)
+ /// The cell.
+ /// ufc_cell (ufc::cell).
+ /// The ufc::cell.
+ void restrict(double* w,
+ const FiniteElement& element,
+ std::size_t part,
+ const Cell& dolfin_cell,
+ const double* coordinate_dofs,
+ const ufc::cell& ufc_cell) const;
+
+ /// Evaluate at given point in given cell in given part
+ ///
+ /// *Arguments*
+ /// values (_Array_ <double>)
+ /// The values at the point.
+ /// x (_Array_ <double>)
+ /// The coordinates of the point.
+ /// cell (ufc::cell)
+ /// The cell which contains the given point.
+ void eval(Array<double>& values, const Array<double>& x,
+ std::size_t part,
+ const ufc::cell& cell) const;
+
+ /// Evaluate at a given point
+ void eval(Array<double>& values, const Array<double>& x) const;
+
+ /// Restrict as UFC function (by calling eval)
+ void restrict_as_ufc_function(double* w,
+ const FiniteElement& element,
+ std::size_t part,
+ const Cell& dolfin_cell,
+ const double* coordinate_dofs,
+ const ufc::cell& ufc_cell) const;
+
private:
// Initialize vector
diff --git a/dolfin/function/MultiMeshFunctionSpace.cpp b/dolfin/function/MultiMeshFunctionSpace.cpp
index de2e2e6..2ca3082 100644
--- a/dolfin/function/MultiMeshFunctionSpace.cpp
+++ b/dolfin/function/MultiMeshFunctionSpace.cpp
@@ -26,6 +26,9 @@
#include <dolfin/geometry/BoundingBoxTree.h>
#include <dolfin/geometry/SimplexQuadrature.h>
#include <dolfin/fem/MultiMeshDofMap.h>
+#include <dolfin/la/GenericMatrix.h>
+#include <dolfin/la/GenericVector.h>
+
#include "FunctionSpace.h"
#include "MultiMeshFunctionSpace.h"
@@ -36,7 +39,13 @@ MultiMeshFunctionSpace::MultiMeshFunctionSpace(std::shared_ptr<const MultiMesh>
: _multimesh(multimesh),
_dofmap(new MultiMeshDofMap())
{
- // Do nothing
+ // Check that multimesh has been built
+ if (!multimesh->is_built())
+ {
+ dolfin_error("MultiMeshFunctionSpace.cpp",
+ "create multimesh function space",
+ "Multimesh has not been built; did you forget to call multimesh.build()?");
+ }
}
//-----------------------------------------------------------------------------
MultiMeshFunctionSpace::~MultiMeshFunctionSpace()
@@ -157,3 +166,25 @@ void MultiMeshFunctionSpace::_build_views()
}
}
//-----------------------------------------------------------------------------
+void MultiMeshFunctionSpace::lock_inactive_dofs(GenericMatrix &A, GenericVector &b) const
+{
+ // Iterate over parts
+ for (std::size_t part = 0; part < num_parts(); part++)
+ {
+ // Get inactive dofs
+ std::vector<dolfin::la_index> inactive_dofs_on_part =
+ dofmap()->inactive_dofs(*multimesh(), part);
+
+ // Zero rows of A and put 1 on the diagonal
+ A.ident(inactive_dofs_on_part.size(), &inactive_dofs_on_part[0]);
+
+ // Zero entries in b
+ double zero = 0;
+ for (auto dof : inactive_dofs_on_part)
+ {
+ b.set(&zero, 1, &dof);
+ }
+ //std::vector<const double> zeroes(inactive_dofs_on_part.size());
+ //b.set(&zeroes[0], inactive_dofs_on_part.size(), &inactive_dofs_on_part[0]);
+ }
+}
diff --git a/dolfin/function/MultiMeshFunctionSpace.h b/dolfin/function/MultiMeshFunctionSpace.h
index cb6e820..c7a6a44 100644
--- a/dolfin/function/MultiMeshFunctionSpace.h
+++ b/dolfin/function/MultiMeshFunctionSpace.h
@@ -16,7 +16,7 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2013-08-05
-// Last changed: 2016-03-02
+// Last changed: 2017-09-22
#ifndef __MULTI_MESH_FUNCTION_SPACE_H
#define __MULTI_MESH_FUNCTION_SPACE_H
@@ -33,6 +33,8 @@ namespace dolfin
{
// Forward declarations
+ class GenericMatrix;
+ class GenericVector;
class FunctionSpace;
class MultiMeshDofMap;
@@ -120,6 +122,9 @@ namespace dolfin
/// computed from the full function spaces on each part.
void build(const std::vector<dolfin::la_index>& offsets);
+ /// Lock inactive dofs of a system
+ void lock_inactive_dofs(GenericMatrix &A, GenericVector &b) const;
+
private:
// Friends
diff --git a/dolfin/generation/BoxMesh.cpp b/dolfin/generation/BoxMesh.cpp
index 5fe1fac..eb6dc25 100644
--- a/dolfin/generation/BoxMesh.cpp
+++ b/dolfin/generation/BoxMesh.cpp
@@ -44,11 +44,11 @@ BoxMesh::BoxMesh(const Point& p0, const Point& p1,
BoxMesh::BoxMesh(MPI_Comm comm, const Point& p0, const Point& p1,
std::size_t nx, std::size_t ny, std::size_t nz) : Mesh(comm)
{
- build(*this, {{p0, p1}}, {{nx, ny, nz}});
+ build_tet(*this, {{p0, p1}}, {{nx, ny, nz}});
}
//-----------------------------------------------------------------------------
-void BoxMesh::build(Mesh& mesh, const std::array<Point,2 >& p,
- std::array<std::size_t, 3> n)
+void BoxMesh::build_tet(Mesh& mesh, const std::array<Point,2 >& p,
+ std::array<std::size_t, 3> n)
{
Timer timer("Build BoxMesh");
@@ -100,7 +100,7 @@ void BoxMesh::build(Mesh& mesh, const std::array<Point,2 >& p,
// Open mesh for editing
MeshEditor editor;
- editor.open(mesh, CellType::tetrahedron, 3, 3);
+ editor.open(mesh, CellType::Type::tetrahedron, 3, 3);
// Storage for vertex coordinates
std::vector<double> x(3);
@@ -169,3 +169,85 @@ void BoxMesh::build(Mesh& mesh, const std::array<Point,2 >& p,
}
}
//-----------------------------------------------------------------------------
+void BoxMesh::build_hex(Mesh& mesh, std::array<std::size_t, 3> n)
+{
+ // Receive mesh according to parallel policy
+ if (MPI::is_receiver(mesh.mpi_comm()))
+ {
+ MeshPartitioning::build_distributed_mesh(mesh);
+ return;
+ }
+
+ const std::size_t nx = n[0];
+ const std::size_t ny = n[1];
+ const std::size_t nz = n[2];
+
+ MeshEditor editor;
+ editor.open(mesh, CellType::Type::hexahedron, 3, 3);
+
+ // Create vertices and cells:
+ editor.init_vertices_global((nx + 1)*(ny + 1)*(nz + 1),
+ (nx + 1)*(ny + 1)*(nz + 1));
+ editor.init_cells_global(nx*ny*nz, nx*ny*nz);
+
+ // Storage for vertices
+ std::vector<double> x(3);
+
+ const double a = 0.0;
+ const double b = 1.0;
+ const double c = 0.0;
+ const double d = 1.0;
+ const double e = 0.0;
+ const double f = 1.0;
+
+ // Create main vertices:
+ std::size_t vertex = 0;
+ for (std::size_t iz = 0; iz <= nz; iz++)
+ {
+ x[2] = e + ((static_cast<double>(iz))*(f - e)/static_cast<double>(nz));
+ for (std::size_t iy = 0; iy <= ny; iy++)
+ {
+ x[1] = c + ((static_cast<double>(iy))*(d - c)/static_cast<double>(ny));
+ for (std::size_t ix = 0; ix <= nx; ix++)
+ {
+ x[0] = a + ((static_cast<double>(ix))*(b - a)/static_cast<double>(nx));
+ editor.add_vertex(vertex, x);
+ vertex++;
+ }
+ }
+ }
+
+ // Create cuboids
+ std::size_t cell = 0;
+ std::vector<std::size_t> v(8);
+ for (std::size_t iz = 0; iz < nz; iz++)
+ {
+ for (std::size_t iy = 0; iy < ny; iy++)
+ {
+ for (std::size_t ix = 0; ix < nx; ix++)
+ {
+ v[0] = (iz*(ny + 1) + iy)*(nx + 1) + ix;
+ v[1] = v[0] + 1;
+ v[2] = v[0] + (nx + 1);
+ v[3] = v[1] + (nx + 1);
+ v[4] = v[0] + (nx + 1)*(ny + 1);
+ v[5] = v[1] + (nx + 1)*(ny + 1);
+ v[6] = v[2] + (nx + 1)*(ny + 1);
+ v[7] = v[3] + (nx + 1)*(ny + 1);
+ editor.add_cell(cell, v);
+ ++cell;
+ }
+ }
+ }
+
+ // Close mesh editor
+ editor.close();
+
+ // Broadcast mesh according to parallel policy
+ if (MPI::is_broadcaster(mesh.mpi_comm()))
+ {
+ MeshPartitioning::build_distributed_mesh(mesh);
+ return;
+ }
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/generation/BoxMesh.h b/dolfin/generation/BoxMesh.h
index eb6af19..cd4b624 100644
--- a/dolfin/generation/BoxMesh.h
+++ b/dolfin/generation/BoxMesh.h
@@ -20,6 +20,7 @@
#include <array>
#include <cstddef>
+#include <dolfin/log/log.h>
#include <dolfin/common/MPI.h>
#include <dolfin/mesh/Mesh.h>
@@ -44,6 +45,8 @@ namespace dolfin
/// Points of box.
/// @param n (std::array<double, 3> )
/// Number of cells in each direction.
+ /// @param cell_type
+ /// Tetrahedron or Hexahedron
///
/// @code{.cpp}
/// // Mesh with 8 cells in each direction on the
@@ -52,8 +55,8 @@ namespace dolfin
/// Point p1(2, 2, 2);
/// auto mesh = BoxMesh::create({p0, p1}, {8, 8, 8});
/// @endcode
- static Mesh create(const std::array<Point,2 >& p, std::array<std::size_t, 3> n)
- { return create(MPI_COMM_WORLD, p, n); }
+ static Mesh create(const std::array<Point,2 >& p, std::array<std::size_t, 3> n, CellType::Type cell_type)
+ { return create(MPI_COMM_WORLD, p, n, cell_type); }
/// Create a uniform finite element _Mesh_ over the rectangular
/// prism spanned by the two _Point_s p0 and p1. The order of the
@@ -66,6 +69,8 @@ namespace dolfin
/// Points of box.
/// @param n (std::array<double, 3> )
/// Number of cells in each direction.
+ /// @param cell_type
+ /// Tetrahedron or hexahedron
///
/// @code{.cpp}
/// // Mesh with 8 cells in each direction on the
@@ -75,10 +80,20 @@ namespace dolfin
/// auto mesh = BoxMesh::create({p0, p1}, {8, 8, 8});
/// @endcode
static Mesh create(MPI_Comm comm, const std::array<Point, 2>& p,
- std::array<std::size_t, 3> n)
+ std::array<std::size_t, 3> n, CellType::Type cell_type)
{
Mesh mesh(comm);
- build(mesh, p, n);
+ if (cell_type == CellType::Type::tetrahedron)
+ build_tet(mesh, p, n);
+ else if (cell_type == CellType::Type::hexahedron)
+ build_hex(mesh, n);
+ else
+ {
+ dolfin_error("BoxMesh.h",
+ "generate box mesh",
+ "Wrong cell type '%d'", cell_type);
+ }
+
return mesh;
}
@@ -141,8 +156,10 @@ namespace dolfin
private:
// Build mesh
- static void build(Mesh& mesh, const std::array<Point, 2>& p,
- std::array<std::size_t, 3> n);
+ static void build_tet(Mesh& mesh, const std::array<Point, 2>& p,
+ std::array<std::size_t, 3> n);
+
+ static void build_hex(Mesh& mesh, std::array<std::size_t, 3> n);
};
diff --git a/dolfin/generation/CMakeLists.txt b/dolfin/generation/CMakeLists.txt
index de80d24..b705cea 100644
--- a/dolfin/generation/CMakeLists.txt
+++ b/dolfin/generation/CMakeLists.txt
@@ -6,9 +6,7 @@ set(HEADERS
SphericalShellMesh.h
UnitCubeMesh.h
UnitDiscMesh.h
- UnitHexMesh.h
UnitIntervalMesh.h
- UnitQuadMesh.h
UnitSquareMesh.h
UnitTetrahedronMesh.h
UnitTriangleMesh.h
@@ -20,8 +18,6 @@ set(SOURCES
RectangleMesh.cpp
SphericalShellMesh.cpp
UnitDiscMesh.cpp
- UnitHexMesh.cpp
- UnitQuadMesh.cpp
UnitTetrahedronMesh.cpp
UnitTriangleMesh.cpp
PARENT_SCOPE)
diff --git a/dolfin/generation/IntervalMesh.cpp b/dolfin/generation/IntervalMesh.cpp
index c2d180b..b49036a 100644
--- a/dolfin/generation/IntervalMesh.cpp
+++ b/dolfin/generation/IntervalMesh.cpp
@@ -75,7 +75,7 @@ void IntervalMesh::build(Mesh& mesh, std::size_t nx, std::array<double, 2> x)
// Open mesh for editing
MeshEditor editor;
- editor.open(mesh, CellType::interval, 1, 1);
+ editor.open(mesh, CellType::Type::interval, 1, 1);
// Create vertices and cells:
editor.init_vertices_global((nx+1), (nx+1));
diff --git a/dolfin/generation/RectangleMesh.cpp b/dolfin/generation/RectangleMesh.cpp
index 63502ab..4697a05 100644
--- a/dolfin/generation/RectangleMesh.cpp
+++ b/dolfin/generation/RectangleMesh.cpp
@@ -40,12 +40,12 @@ RectangleMesh::RectangleMesh(MPI_Comm comm,
std::size_t nx, std::size_t ny,
std::string diagonal) : Mesh(comm)
{
- build(*this, {{p0, p1}}, {{nx, ny}}, diagonal);
+ build_tri(*this, {{p0, p1}}, {{nx, ny}}, diagonal);
}
//-----------------------------------------------------------------------------
-void RectangleMesh::build(Mesh& mesh, const std::array<Point, 2>& p,
- std::array<std::size_t, 2> n,
- std::string diagonal)
+void RectangleMesh::build_tri(Mesh& mesh, const std::array<Point, 2>& p,
+ std::array<std::size_t, 2> n,
+ std::string diagonal)
{
// Receive mesh according to parallel policy
if (MPI::is_receiver(mesh.mpi_comm()))
@@ -98,7 +98,7 @@ void RectangleMesh::build(Mesh& mesh, const std::array<Point, 2>& p,
// Open mesh for editing
MeshEditor editor;
- editor.open(mesh, CellType::triangle, 2, 2);
+ editor.open(mesh, CellType::Type::triangle, 2, 2);
// Create vertices and cells:
if (diagonal == "crossed")
@@ -232,3 +232,70 @@ void RectangleMesh::build(Mesh& mesh, const std::array<Point, 2>& p,
}
}
//-----------------------------------------------------------------------------
+void RectangleMesh::build_quad(Mesh& mesh, const std::array<Point, 2>& p,
+ std::array<std::size_t, 2> n)
+{
+ // Receive mesh according to parallel policy
+ if (MPI::is_receiver(mesh.mpi_comm()))
+ {
+ MeshPartitioning::build_distributed_mesh(mesh);
+ return;
+ }
+
+ const std::size_t nx = n[0];
+ const std::size_t ny = n[1];
+
+ MeshEditor editor;
+ editor.open(mesh, CellType::Type::quadrilateral, 2, 2);
+
+ // Create vertices and cells:
+ editor.init_vertices_global((nx + 1)*(ny + 1), (nx + 1)*(ny + 1));
+ editor.init_cells_global(nx*ny, nx*ny);
+
+ // Storage for vertices
+ std::vector<double> x(2);
+
+ const double a = 0.0;
+ const double b = 1.0;
+ const double c = 0.0;
+ const double d = 1.0;
+
+ // Create main vertices:
+ std::size_t vertex = 0;
+ for (std::size_t iy = 0; iy <= ny; iy++)
+ {
+ x[1] = c + ((static_cast<double>(iy))*(d - c)/static_cast<double>(ny));
+ for (std::size_t ix = 0; ix <= nx; ix++)
+ {
+ x[0] = a + ((static_cast<double>(ix))*(b - a)/static_cast<double>(nx));
+ editor.add_vertex(vertex, x);
+ vertex++;
+ }
+ }
+
+ // Create rectangles
+ std::size_t cell = 0;
+ std::vector<std::size_t> v(4);
+ for (std::size_t iy = 0; iy < ny; iy++)
+ for (std::size_t ix = 0; ix < nx; ix++)
+ {
+ v[0] = iy*(nx + 1) + ix;
+ v[1] = v[0] + 1;
+ v[2] = v[0] + (nx + 1);
+ v[3] = v[1] + (nx + 1);
+ editor.add_cell(cell, v);
+ ++cell;
+ }
+
+ // Close mesh editor
+ editor.close();
+
+ // Broadcast mesh according to parallel policy
+ if (MPI::is_broadcaster(mesh.mpi_comm()))
+ {
+ MeshPartitioning::build_distributed_mesh(mesh);
+ return;
+ }
+
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/generation/RectangleMesh.h b/dolfin/generation/RectangleMesh.h
index 796dc03..1df237f 100644
--- a/dolfin/generation/RectangleMesh.h
+++ b/dolfin/generation/RectangleMesh.h
@@ -21,6 +21,8 @@
#include <array>
#include <string>
#include <dolfin/common/MPI.h>
+#include <dolfin/log/log.h>
+#include <dolfin/mesh/CellType.h>
#include <dolfin/mesh/Mesh.h>
namespace dolfin
@@ -39,21 +41,24 @@ namespace dolfin
/// Vertex points.
/// @param n (std::array<std::size_t, 2>)
/// Number of cells in each direction
+ /// @param cell_type (dolfin::CellType::Type)
+ /// Cell type
/// @param diagonal (string)
/// Direction of diagonals: "left", "right", "left/right", "crossed"
///
/// @code{.cpp}
///
- /// // Mesh with 8 cells in each direction on the
+ /// // Mesh with 8 cell edges in each direction on the
/// // set [-1,2] x [-1,2]
/// Point p0(-1, -1);
/// Point p1(2, 2);
/// auto Mesh = RectangleMesh::create({p0, p1}, {8, 8});
/// @endcode
- static Mesh create(const std::array<Point, 2>& p, std::array<std::size_t, 2> n,
+ static Mesh create(const std::array<Point, 2>& p,
+ std::array<std::size_t, 2> n,
+ CellType::Type cell_type,
std::string diagonal="right")
- { return create(MPI_COMM_WORLD, p, n); }
-
+ { return create(MPI_COMM_WORLD, p, n, cell_type, diagonal); }
/// @param comm (MPI_Comm)
/// MPI communicator
@@ -61,6 +66,8 @@ namespace dolfin
/// Vertex points.
/// @param n (std::array<std::size_t, 2>)
/// Number of cells in each direction
+ /// @param cell_type (dolfin::CellType::Type)
+ /// Cell type
/// @param diagonal (string)
/// Direction of diagonals: "left", "right", "left/right", "crossed"
///
@@ -74,12 +81,25 @@ namespace dolfin
/// @endcode
static Mesh create(MPI_Comm comm, const std::array<Point, 2>& p,
std::array<std::size_t, 2> n,
+ CellType::Type cell_type,
std::string diagonal="right")
- { Mesh mesh(comm);
- build(mesh, p, n);
+ {
+ Mesh mesh(comm);
+ if (cell_type == CellType::Type::triangle)
+ build_tri(mesh, p, n, diagonal);
+ else if (cell_type == CellType::Type::quadrilateral)
+ build_quad(mesh, p, n);
+ else
+ {
+ dolfin_error("RectangleMesh.h",
+ "generate rectangle mesh",
+ "Wrong cell type '%d'", cell_type);
+ }
+
return mesh;
}
+ // Deprecated
/// @param p0 (_Point_)
/// First point.
/// @param p1 (_Point_)
@@ -103,6 +123,7 @@ namespace dolfin
std::size_t nx, std::size_t ny,
std::string diagonal="right");
+ // Deprecated
/// @param comm (MPI_Comm)
/// MPI communicator
/// @param p0 (_Point_)
@@ -132,10 +153,13 @@ namespace dolfin
private:
// Build mesh
- static void build(Mesh& mesh, const std::array<Point, 2>& p,
- std::array<std::size_t, 2> n,
- std::string diagonal="right");
+ static void build_tri(Mesh& mesh, const std::array<Point, 2>& p,
+ std::array<std::size_t, 2> n,
+ std::string diagonal="right");
+
+ static void build_quad(Mesh& mesh, const std::array<Point, 2>& p,
+ std::array<std::size_t, 2> n);
};
}
diff --git a/dolfin/generation/SphericalShellMesh.cpp b/dolfin/generation/SphericalShellMesh.cpp
index 963fc9e..eefc731 100644
--- a/dolfin/generation/SphericalShellMesh.cpp
+++ b/dolfin/generation/SphericalShellMesh.cpp
@@ -32,7 +32,7 @@ void SphericalShellMesh::build(Mesh& mesh, std::size_t degree)
const std::size_t gdim = 3;
dolfin_assert(degree > 0 and degree < 3);
- editor.open(mesh, tdim, gdim, degree);
+ editor.open(mesh, CellType::Type::triangle, tdim, gdim, degree);
editor.init_vertices_global(12, 12);
diff --git a/dolfin/generation/UnitCubeMesh.h b/dolfin/generation/UnitCubeMesh.h
index 4267bd6..0be213a 100644
--- a/dolfin/generation/UnitCubeMesh.h
+++ b/dolfin/generation/UnitCubeMesh.h
@@ -21,12 +21,13 @@
#include <array>
#include <cstddef>
#include <dolfin/common/MPI.h>
+#include <dolfin/mesh/CellType.h>
#include "BoxMesh.h"
namespace dolfin
{
- /// Tetrahedral mesh of the 3D unit cube [0,1] x [0,1] x [0,1].
+ /// Tetrahedral/hexahedral mesh of the 3D unit cube [0,1] x [0,1] x [0,1].
/// Given the number of cells (nx, ny, nz) in each direction, the
/// total number of tetrahedra will be 6*nx*ny*nz and the total
/// number of vertices will be (nx + 1)*(ny + 1)*(nz + 1).
@@ -40,13 +41,14 @@ namespace dolfin
///
/// @param n (std::array<std::size_t, 3>)
/// Number of cells in each direction.
- ///
+ /// @param cell_type
+ /// Tetrahedron or hexahedron
/// @code{.cpp}
///
- /// auto mesh = UnitCubeMesh::create(32, 32, 32);
+ /// auto mesh = UnitCubeMesh::create(32, 32, 32, CellType::Type::tetrahedron);
/// @endcode
- static Mesh create(std::array<std::size_t, 3> n)
- { return create(MPI_COMM_WORLD, n); }
+ static Mesh create(std::array<std::size_t, 3> n, CellType::Type cell_type)
+ { return create(MPI_COMM_WORLD, n, cell_type); }
/// Create a uniform finite element _Mesh_ over the unit cube
/// [0,1] x [0,1] x [0,1].
@@ -55,12 +57,26 @@ namespace dolfin
/// MPI communicator
/// @param n (std::aray<std::size_t, 3>)
/// Number of cells in each direction.
+ /// @param cell_type
+ /// Tetrahedron or hexahedron
///
/// @code{.cpp}
- /// auto mesh = UnitCubeMesh::create(MPI_COMM_WORLD, 32, 32, 32);
+ /// auto mesh = UnitCubeMesh::create(MPI_COMM_WORLD, {32, 32, 32}, CellType::Type::hexahedron);
/// @endcode
- static Mesh create(MPI_Comm comm, std::array<std::size_t, 3> n)
- { return BoxMesh::create(comm, {{Point(0.0, 0.0, 0.0), Point(1.0, 1.0, 1.0)}}, n); }
+ static Mesh create(MPI_Comm comm, std::array<std::size_t, 3> n, CellType::Type cell_type)
+ { return BoxMesh::create(comm, {{Point(0.0, 0.0, 0.0), Point(1.0, 1.0, 1.0)}}, n, cell_type); }
+
+ // Temporary - part of pybind11 transition and will be
+ // removed. Avoid using.
+ static Mesh create(std::size_t nx, std::size_t ny, std::size_t nz,
+ CellType::Type cell_type)
+ { return create({nx, ny, nz}, cell_type); }
+
+ // Temporary - part of pybind11 transition and will be
+ // removed. Avoid using.
+ static Mesh create(MPI_Comm comm, std::size_t nx, std::size_t ny, std::size_t nz,
+ CellType::Type cell_type)
+ { return create({nx, ny, nz}, cell_type); }
/// Create a uniform finite element _Mesh_ over the unit cube
/// [0,1] x [0,1] x [0,1].
diff --git a/dolfin/generation/UnitDiscMesh.cpp b/dolfin/generation/UnitDiscMesh.cpp
index daaf5f8..3eaa0bc 100644
--- a/dolfin/generation/UnitDiscMesh.cpp
+++ b/dolfin/generation/UnitDiscMesh.cpp
@@ -45,7 +45,7 @@ void UnitDiscMesh::build(Mesh& mesh, std::size_t n, std::size_t degree,
}
MeshEditor editor;
- editor.open(mesh, 2, gdim, degree);
+ editor.open(mesh, CellType::Type::triangle, 2, gdim, degree);
editor.init_vertices_global(1 + 3*n*(n + 1), 1 + 3*n*(n + 1));
std::size_t c = 0;
diff --git a/dolfin/generation/UnitHexMesh.cpp b/dolfin/generation/UnitHexMesh.cpp
deleted file mode 100644
index 2da4f66..0000000
--- a/dolfin/generation/UnitHexMesh.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2015 Chris Richardson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-
-// NB: this code is experimental, just for testing, and will generally not
-// work with anything else
-
-#include <dolfin/common/constants.h>
-#include <dolfin/mesh/MeshEditor.h>
-#include <dolfin/mesh/MeshPartitioning.h>
-#include "UnitHexMesh.h"
-
-using namespace dolfin;
-
-//-----------------------------------------------------------------------------
-void UnitHexMesh::build(Mesh& mesh, std::array<std::size_t, 3> n)
-{
- // Receive mesh according to parallel policy
- if (MPI::is_receiver(mesh.mpi_comm()))
- {
- MeshPartitioning::build_distributed_mesh(mesh);
- return;
- }
-
- const std::size_t nx = n[0];
- const std::size_t ny = n[1];
- const std::size_t nz = n[2];
-
- MeshEditor editor;
- editor.open(mesh, CellType::hexahedron, 3, 3);
-
- // Create vertices and cells:
- editor.init_vertices_global((nx + 1)*(ny + 1)*(nz + 1),
- (nx + 1)*(ny + 1)*(nz + 1));
- editor.init_cells_global(nx*ny*nz, nx*ny*nz);
-
- // Storage for vertices
- std::vector<double> x(3);
-
- const double a = 0.0;
- const double b = 1.0;
- const double c = 0.0;
- const double d = 1.0;
- const double e = 0.0;
- const double f = 1.0;
-
- // Create main vertices:
- std::size_t vertex = 0;
- for (std::size_t iz = 0; iz <= nz; iz++)
- {
- x[2] = e + ((static_cast<double>(iz))*(f - e)/static_cast<double>(nz));
- for (std::size_t iy = 0; iy <= ny; iy++)
- {
- x[1] = c + ((static_cast<double>(iy))*(d - c)/static_cast<double>(ny));
- for (std::size_t ix = 0; ix <= nx; ix++)
- {
- x[0] = a + ((static_cast<double>(ix))*(b - a)/static_cast<double>(nx));
- editor.add_vertex(vertex, x);
- vertex++;
- }
- }
- }
-
- // Create cuboids
- std::size_t cell = 0;
- std::vector<std::size_t> v(8);
- for (std::size_t iz = 0; iz < nz; iz++)
- {
- for (std::size_t iy = 0; iy < ny; iy++)
- {
- for (std::size_t ix = 0; ix < nx; ix++)
- {
- v[0] = (iz*(ny + 1) + iy)*(nx + 1) + ix;
- v[1] = v[0] + 1;
- v[2] = v[0] + (nx + 1);
- v[3] = v[1] + (nx + 1);
- v[4] = v[0] + (nx + 1)*(ny + 1);
- v[5] = v[1] + (nx + 1)*(ny + 1);
- v[6] = v[2] + (nx + 1)*(ny + 1);
- v[7] = v[3] + (nx + 1)*(ny + 1);
- editor.add_cell(cell, v);
- ++cell;
- }
- }
- }
-
- // Close mesh editor
- editor.close();
-
- // Broadcast mesh according to parallel policy
- if (MPI::is_broadcaster(mesh.mpi_comm()))
- {
- MeshPartitioning::build_distributed_mesh(mesh);
- return;
- }
-}
-//-----------------------------------------------------------------------------
diff --git a/dolfin/generation/UnitHexMesh.h b/dolfin/generation/UnitHexMesh.h
deleted file mode 100644
index 67b6657..0000000
--- a/dolfin/generation/UnitHexMesh.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2015 Chris Richardson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#ifndef __UNITHEXMESH_MESH_H
-#define __UNITHEXMESH_MESH_H
-
-#include <string>
-#include <dolfin/common/MPI.h>
-#include <dolfin/mesh/Mesh.h>
-
-namespace dolfin
-{
-
- /// NB: this code is experimental, just for testing, and will
- /// generally not work with anything else
- class UnitHexMesh
- {
- public:
-
- /// NB: this code is experimental, just for testing, and will generally not
- /// work with anything else
- static Mesh create(std::size_t nx, std::size_t ny, std::size_t nz)
- { return create(MPI_COMM_WORLD, nx, ny, nz); }
-
- /// Create mesh
- static Mesh create(MPI_Comm comm, std::size_t nx, std::size_t ny, std::size_t nz)
- {
- Mesh mesh(comm);
- build(mesh, {{nx, ny, nz}});
- return mesh;
- }
-
- private:
-
- static void build(Mesh& mesh, std::array<std::size_t, 3>);
-
- };
-
-}
-
-#endif
diff --git a/dolfin/generation/UnitQuadMesh.cpp b/dolfin/generation/UnitQuadMesh.cpp
deleted file mode 100644
index f7d0746..0000000
--- a/dolfin/generation/UnitQuadMesh.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2015 Chris Richardson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-
-
-// NB: this code is experimental, just for testing, and will generally not
-// work with anything else
-
-#include <dolfin/common/constants.h>
-#include <dolfin/mesh/MeshEditor.h>
-#include <dolfin/mesh/MeshPartitioning.h>
-#include "UnitQuadMesh.h"
-
-using namespace dolfin;
-
-//-----------------------------------------------------------------------------
-void UnitQuadMesh::build(Mesh& mesh, std::array<std::size_t, 2> n)
-{
- // Receive mesh according to parallel policy
- if (MPI::is_receiver(mesh.mpi_comm()))
- {
- MeshPartitioning::build_distributed_mesh(mesh);
- return;
- }
-
- const std::size_t nx = n[0];
- const std::size_t ny = n[1];
-
- MeshEditor editor;
- editor.open(mesh, CellType::quadrilateral, 2, 2);
-
- // Create vertices and cells:
- editor.init_vertices_global((nx + 1)*(ny + 1), (nx + 1)*(ny + 1));
- editor.init_cells_global(nx*ny, nx*ny);
-
- // Storage for vertices
- std::vector<double> x(2);
-
- const double a = 0.0;
- const double b = 1.0;
- const double c = 0.0;
- const double d = 1.0;
-
- // Create main vertices:
- std::size_t vertex = 0;
- for (std::size_t iy = 0; iy <= ny; iy++)
- {
- x[1] = c + ((static_cast<double>(iy))*(d - c)/static_cast<double>(ny));
- for (std::size_t ix = 0; ix <= nx; ix++)
- {
- x[0] = a + ((static_cast<double>(ix))*(b - a)/static_cast<double>(nx));
- editor.add_vertex(vertex, x);
- vertex++;
- }
- }
-
- // Create rectangles
- std::size_t cell = 0;
- std::vector<std::size_t> v(4);
- for (std::size_t iy = 0; iy < ny; iy++)
- for (std::size_t ix = 0; ix < nx; ix++)
- {
- v[0] = iy*(nx + 1) + ix;
- v[1] = v[0] + 1;
- v[2] = v[0] + (nx + 1);
- v[3] = v[1] + (nx + 1);
- editor.add_cell(cell, v);
- ++cell;
- }
-
- // Close mesh editor
- editor.close();
-
- // Broadcast mesh according to parallel policy
- if (MPI::is_broadcaster(mesh.mpi_comm()))
- {
- MeshPartitioning::build_distributed_mesh(mesh);
- return;
- }
-
-}
-//-----------------------------------------------------------------------------
diff --git a/dolfin/generation/UnitQuadMesh.h b/dolfin/generation/UnitQuadMesh.h
deleted file mode 100644
index 5f389bb..0000000
--- a/dolfin/generation/UnitQuadMesh.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2015 Chris Richardson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#ifndef __UNITQUADMESH_MESH_H
-#define __UNITQUADMESH_MESH_H
-
-#include <string>
-#include <dolfin/common/MPI.h>
-#include <dolfin/mesh/Mesh.h>
-
-namespace dolfin
-{
-
- /// NB: this code is experimental, just for testing, and will
- /// generally not work with anything else
-
- class UnitQuadMesh : public Mesh
- {
- public:
-
- /// NB: this code is experimental, just for testing, and will generally not
- /// work with anything else
-
- /// Create a mesh
- static Mesh create(std::size_t nx, std::size_t ny)
- { return create(MPI_COMM_WORLD, nx, ny); }
-
- /// Create a mesh
- static Mesh create(MPI_Comm comm, std::size_t nx, std::size_t ny)
- {
- Mesh mesh(comm);
- build(mesh, {{nx, ny}});;
- return mesh;
- }
-
- private:
-
- static void build(Mesh& mesh, std::array<std::size_t, 2> n);
-
- };
-
-}
-
-#endif
diff --git a/dolfin/generation/UnitSquareMesh.h b/dolfin/generation/UnitSquareMesh.h
index 8d68533..6bb0dd5 100644
--- a/dolfin/generation/UnitSquareMesh.h
+++ b/dolfin/generation/UnitSquareMesh.h
@@ -25,12 +25,13 @@
#include <array>
#include <string>
+#include <dolfin/mesh/CellType.h>
#include "RectangleMesh.h"
namespace dolfin
{
- /// Triangular mesh of the 2D unit square [0,1] x [0,1]. Given the
+ /// Triangular/quadrilateral mesh of the 2D unit square [0,1] x [0,1]. Given the
/// number of cells (nx, ny) in each direction, the total number of
/// triangles will be 2*nx*ny and the total number of vertices will
/// be (nx + 1)*(ny + 1).
@@ -47,6 +48,8 @@ namespace dolfin
///
/// @param n (std:::array<std::size_t, 2>)
/// Number of cells in each direction.
+ /// @param cell_type
+ /// Triangle or quadrilateral
/// @param diagonal (std::string)
/// Optional argument: A std::string indicating
/// the direction of the diagonals.
@@ -56,8 +59,32 @@ namespace dolfin
/// auto mesh1 = UnitSquareMesh::create(32, 32);
/// auto mesh2 = UnitSquareMesh::create(32, 32, "crossed");
/// @endcode
- static Mesh create(std::array<std::size_t, 2> n, std::string diagonal="right")
- { return RectangleMesh::create({{Point(0.0, 0.0), Point(1.0, 1.0)}}, n); }
+ static Mesh create(std::array<std::size_t, 2> n,
+ CellType::Type cell_type,
+ std::string diagonal="right")
+ {
+ return RectangleMesh::create({{Point(0.0, 0.0), Point(1.0, 1.0)}}, n,
+ cell_type, diagonal);
+ }
+
+ // Temporary - part of pybind11 transition and will be
+ // removed. Avoid using.
+ static Mesh create(std::size_t nx, std::size_t ny, CellType::Type cell_type,
+ std::string diagonal="right")
+ {
+ return RectangleMesh::create({{Point(0.0, 0.0), Point(1.0, 1.0)}}, {nx, ny},
+ cell_type, diagonal);
+ }
+
+ // Temporary - part of pybind11 transition and will be
+ // removed. Avoid using.
+ static Mesh create(MPI_Comm comm, std::size_t nx, std::size_t ny,
+ CellType::Type cell_type,
+ std::string diagonal="right")
+ {
+ return RectangleMesh::create(comm, {{Point(0.0, 0.0), Point(1.0, 1.0)}}, {nx, ny},
+ cell_type, diagonal);
+ }
/// Create a uniform finite element _Mesh_ over the unit square
/// [0,1] x [0,1].
@@ -66,6 +93,8 @@ namespace dolfin
/// MPI communicator
/// @param n (std:::array<std::size_t, 2>)
/// Number of cells in each direction.
+ /// @param cell_type
+ /// Triangle or quadrilateral.
/// @param diagonal (std::string)
/// Optional argument: A std::string indicating
/// the direction of the diagonals.
@@ -76,8 +105,10 @@ namespace dolfin
/// auto mesh2 = UnitSquareMesh::create(MPI_COMM_WORLD, 32, 32, "crossed");
/// @endcode
static Mesh create(MPI_Comm comm, std::array<std::size_t, 2> n,
+ CellType::Type cell_type,
std::string diagonal="right")
- { return RectangleMesh::create(comm, {{Point(0.0, 0.0), Point(1.0, 1.0)}}, n); }
+ { return RectangleMesh::create(comm, {{Point(0.0, 0.0), Point(1.0, 1.0)}}, n,
+ cell_type, diagonal); }
/// Create a uniform finite element _Mesh_ over the unit square
/// [0,1] x [0,1].
diff --git a/dolfin/generation/UnitTetrahedronMesh.cpp b/dolfin/generation/UnitTetrahedronMesh.cpp
index 1248535..ce1ed9e 100644
--- a/dolfin/generation/UnitTetrahedronMesh.cpp
+++ b/dolfin/generation/UnitTetrahedronMesh.cpp
@@ -39,7 +39,7 @@ dolfin::Mesh UnitTetrahedronMesh::create()
// Open mesh for editing
MeshEditor editor;
- editor.open(mesh, CellType::tetrahedron, 3, 3);
+ editor.open(mesh, CellType::Type::tetrahedron, 3, 3);
// Create vertices
editor.init_vertices_global(4, 4);
diff --git a/dolfin/generation/UnitTriangleMesh.cpp b/dolfin/generation/UnitTriangleMesh.cpp
index 75d9332..1a81bfb 100644
--- a/dolfin/generation/UnitTriangleMesh.cpp
+++ b/dolfin/generation/UnitTriangleMesh.cpp
@@ -40,7 +40,7 @@ dolfin::Mesh UnitTriangleMesh::create()
// Open mesh for editing
MeshEditor editor;
- editor.open(mesh, CellType::triangle, 2, 2);
+ editor.open(mesh, CellType::Type::triangle, 2, 2);
// Create vertices
editor.init_vertices_global(3, 3);
diff --git a/dolfin/generation/dolfin_generation.h b/dolfin/generation/dolfin_generation.h
index 8e99809..53f40cf 100644
--- a/dolfin/generation/dolfin_generation.h
+++ b/dolfin/generation/dolfin_generation.h
@@ -11,8 +11,6 @@
#include <dolfin/generation/UnitIntervalMesh.h>
#include <dolfin/generation/UnitTriangleMesh.h>
#include <dolfin/generation/UnitSquareMesh.h>
-#include <dolfin/generation/UnitQuadMesh.h>
-#include <dolfin/generation/UnitHexMesh.h>
#include <dolfin/generation/UnitDiscMesh.h>
#include <dolfin/generation/SphericalShellMesh.h>
diff --git a/dolfin/geometry/BoundingBoxTree2D.h b/dolfin/geometry/BoundingBoxTree2D.h
index fc87e3e..7fd6f94 100644
--- a/dolfin/geometry/BoundingBoxTree2D.h
+++ b/dolfin/geometry/BoundingBoxTree2D.h
@@ -15,8 +15,10 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
+// Modified by August Johansson 2016
+//
// First added: 2013-05-02
-// Last changed: 2013-11-30
+// Last changed: 2016-11-15
#ifndef __BOUNDING_BOX_TREE_2D_H
#define __BOUNDING_BOX_TREE_2D_H
@@ -99,7 +101,7 @@ namespace dolfin
const double eps0 = DOLFIN_EPS_LARGE*(b[2] - b[0]);
const double eps1 = DOLFIN_EPS_LARGE*(b[3] - b[1]);
return (b[0] - eps0 <= a[2] && a[0] <= b[2] + eps0 &&
- b[1] - eps1 <= a[3] && a[1] <= b[3] + eps1);
+ b[1] - eps1 <= a[3] && a[1] <= b[3] + eps1);
}
/// Compute squared distance between point and bounding box
diff --git a/dolfin/geometry/CGALExactArithmetic.h b/dolfin/geometry/CGALExactArithmetic.h
new file mode 100644
index 0000000..42227aa
--- /dev/null
+++ b/dolfin/geometry/CGALExactArithmetic.h
@@ -0,0 +1,1152 @@
+// Copyright (C) 2016-2017 Benjamin Kehlet, August Johansson, and Anders Logg
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-05-03
+// Last changed: 2017-10-07
+//
+// Developer note:
+//
+// This file contains reference implementations of collision detection
+// algorithms using exact arithmetic with CGAL. It is not included in
+// a normal build but is used as a reference for verification and
+// debugging of the inexact DOLFIN collision detection algorithms.
+// To enable, set the option DOLFIN_ENABLE_GEOMETRY_DEBUGGING when
+// configuring DOLFIN
+
+#ifndef __CGAL_EXACT_ARITHMETIC_H
+#define __CGAL_EXACT_ARITHMETIC_H
+
+#ifndef DOLFIN_ENABLE_GEOMETRY_DEBUGGING
+
+// Comparison macro just bypasses CGAL and test when not enabled
+#define CHECK_CGAL(RESULT_DOLFIN, RESULT_CGAL) RESULT_DOLFIN
+
+#define CGAL_INTERSECTION_CHECK(RESULT_DOLFIN, RESULT_CGAL) RESULT_DOLFIN
+
+#else
+
+#define CGAL_CHECK_TOLERANCE 1e-10
+
+#include "Point.h"
+#include "predicates.h"
+#include <dolfin/log/log.h>
+#include <dolfin/math/basic.h>
+#include <vector>
+#include <algorithm>
+#include <sstream>
+#include <iomanip>
+
+// Check that results from DOLFIN and CGAL match
+namespace dolfin
+{
+ //---------------------------------------------------------------------------
+ // Functions to compare results between DOLFIN and CGAL
+ //---------------------------------------------------------------------------
+ inline bool
+ check_cgal(bool result_dolfin,
+ bool result_cgal,
+ const std::string& function)
+ {
+ if (result_dolfin != result_cgal)
+ {
+ // Convert results to strings
+ std::stringstream s_dolfin;
+ std::stringstream s_cgal;
+ s_dolfin << result_dolfin;
+ s_cgal << result_cgal;
+
+ // Issue error
+ dolfin_error("CGALExactArithmetic.h",
+ "verify geometric predicate with exact types",
+ "Error in predicate %s\n DOLFIN: %s\n CGAL: %s",
+ function.c_str(), s_dolfin.str().c_str(), s_cgal.str().c_str());
+ }
+
+ return result_dolfin;
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point>
+ cgal_intersection_check(const std::vector<Point>& dolfin_result,
+ const std::vector<Point>& cgal_result,
+ const std::string& function)
+ {
+ if (dolfin_result.size() != cgal_result.size())
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "verify intersection",
+ "size of point set differs (%d vs %d)",
+ dolfin_result.size(), cgal_result.size());
+ }
+
+ for (const Point& p1 : dolfin_result)
+ {
+ bool found = false;
+ for (const Point& p2 : cgal_result)
+ {
+ if ( (p1-p2).norm() < 1e-15 )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ dolfin_error("CGALExactArithmetic.h",
+ "verify intersection construction result",
+ "Point (%f, %f, %f) in dolfin result not found in cgal result",
+ p1[0], p1[1], p1[2]);
+ }
+ return dolfin_result;
+ }
+} // end namespace dolfin
+//-----------------------------------------------------------------------------
+// Comparison macro that calls comparison function
+#define CHECK_CGAL(RESULT_DOLFIN, RESULT_CGAL) \
+ check_cgal(RESULT_DOLFIN, RESULT_CGAL, __FUNCTION__)
+
+#define CGAL_INTERSECTION_CHECK(RESULT_DOLFIN, RESULT_CGAL) \
+ cgal_intersection_check(RESULT_DOLFIN, RESULT_CGAL, __FUNCTION__)
+
+// CGAL includes
+#define CGAL_HEADER_ONLY
+#include <CGAL/Cartesian.h>
+#include <CGAL/Quotient.h>
+#include <CGAL/MP_Float.h>
+#include <CGAL/Point_2.h>
+#include <CGAL/Triangle_2.h>
+#include <CGAL/Segment_2.h>
+#include <CGAL/Point_3.h>
+#include <CGAL/Triangle_3.h>
+#include <CGAL/Segment_3.h>
+#include <CGAL/Tetrahedron_3.h>
+#include <CGAL/Polyhedron_3.h>
+#include <CGAL/convex_hull_3.h>
+#include <CGAL/intersections.h>
+#include <CGAL/intersection_of_Polyhedra_3.h>
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Triangulation_2.h>
+#include <CGAL/Nef_polyhedron_3.h>
+
+namespace
+{
+ // CGAL typedefs
+ /* typedef CGAL::Quotient<CGAL::MP_Float> ExactNumber; */
+ /* typedef CGAL::Cartesian<ExactNumber> ExactKernel; */
+ typedef CGAL::Exact_predicates_exact_constructions_kernel ExactKernel;
+ typedef ExactKernel::FT ExactNumber;
+
+ typedef ExactKernel::Point_2 Point_2;
+ typedef ExactKernel::Triangle_2 Triangle_2;
+ typedef ExactKernel::Segment_2 Segment_2;
+ typedef ExactKernel::Intersect_2 Intersect_2;
+ typedef ExactKernel::Point_3 Point_3;
+ typedef ExactKernel::Vector_3 Vector_3;
+ typedef ExactKernel::Triangle_3 Triangle_3;
+ typedef ExactKernel::Segment_3 Segment_3;
+ typedef ExactKernel::Tetrahedron_3 Tetrahedron_3;
+ typedef ExactKernel::Intersect_3 Intersect_3;
+ typedef CGAL::Nef_polyhedron_3<ExactKernel> Nef_polyhedron_3;
+ typedef CGAL::Polyhedron_3<ExactKernel> Polyhedron_3;
+ typedef CGAL::Triangulation_2<ExactKernel> Triangulation_2;
+
+ //---------------------------------------------------------------------------
+ // CGAL utility functions
+ //---------------------------------------------------------------------------
+ inline Point_2 convert_to_cgal_2d(double a, double b)
+ {
+ return Point_2(a, b);
+ }
+ //-----------------------------------------------------------------------------
+ inline Point_3 convert_to_cgal_3d(double a, double b, double c)
+ {
+ return Point_3(a, b, c);
+ }
+ //-----------------------------------------------------------------------------
+ inline Point_2 convert_to_cgal_2d(const dolfin::Point& p)
+ {
+ return Point_2(p[0], p[1]);
+ }
+ //-----------------------------------------------------------------------------
+ inline Point_3 convert_to_cgal_3d(const dolfin::Point& p)
+ {
+ return Point_3(p[0], p[1], p[2]);
+ }
+ //-----------------------------------------------------------------------------
+ inline Segment_2 convert_to_cgal_2d(const dolfin::Point& a,
+ const dolfin::Point& b)
+ {
+ return Segment_2(convert_to_cgal_2d(a), convert_to_cgal_2d(b));
+ }
+ //-----------------------------------------------------------------------------
+ inline Segment_3 convert_to_cgal_3d(const dolfin::Point& a,
+ const dolfin::Point& b)
+ {
+ return Segment_3(convert_to_cgal_3d(a), convert_to_cgal_3d(b));
+ }
+ //-----------------------------------------------------------------------------
+ inline Triangle_2 convert_to_cgal_2d(const dolfin::Point& a,
+ const dolfin::Point& b,
+ const dolfin::Point& c)
+ {
+ return Triangle_2(convert_to_cgal_2d(a),
+ convert_to_cgal_2d(b),
+ convert_to_cgal_2d(c));
+ }
+ //-----------------------------------------------------------------------------
+ inline Triangle_3 convert_to_cgal_3d(const dolfin::Point& a,
+ const dolfin::Point& b,
+ const dolfin::Point& c)
+ {
+ return Triangle_3(convert_to_cgal_3d(a),
+ convert_to_cgal_3d(b),
+ convert_to_cgal_3d(c));
+ }
+ //-----------------------------------------------------------------------------
+ inline Tetrahedron_3 convert_to_cgal_3d(const dolfin::Point& a,
+ const dolfin::Point& b,
+ const dolfin::Point& c,
+ const dolfin::Point& d)
+ {
+ return Tetrahedron_3(convert_to_cgal_3d(a),
+ convert_to_cgal_3d(b),
+ convert_to_cgal_3d(c),
+ convert_to_cgal_3d(d));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool is_degenerate_2d(const dolfin::Point& a,
+ const dolfin::Point& b)
+ {
+ const Segment_2 s(convert_to_cgal_2d(a), convert_to_cgal_2d(b));
+ return s.is_degenerate();
+ }
+ //-----------------------------------------------------------------------------
+ inline bool is_degenerate_3d(const dolfin::Point& a,
+ const dolfin::Point& b)
+ {
+ const Segment_3 s(convert_to_cgal_3d(a), convert_to_cgal_3d(b));
+ return s.is_degenerate();
+ }
+ //-----------------------------------------------------------------------------
+ inline bool is_degenerate_2d(const dolfin::Point& a,
+ const dolfin::Point& b,
+ const dolfin::Point& c)
+ {
+ const Triangle_2 t(convert_to_cgal_2d(a),
+ convert_to_cgal_2d(b),
+ convert_to_cgal_2d(c));
+ return t.is_degenerate();
+ }
+ //-----------------------------------------------------------------------------
+ inline bool is_degenerate_3d(const dolfin::Point& a,
+ const dolfin::Point& b,
+ const dolfin::Point& c)
+ {
+ const Triangle_3 t(convert_to_cgal_3d(a),
+ convert_to_cgal_3d(b),
+ convert_to_cgal_3d(c));
+ return t.is_degenerate();
+ }
+ //-----------------------------------------------------------------------------
+ inline bool is_degenerate_3d(const dolfin::Point& a,
+ const dolfin::Point& b,
+ const dolfin::Point& c,
+ const dolfin::Point& d)
+ {
+ const Tetrahedron_3 t(convert_to_cgal_3d(a),
+ convert_to_cgal_3d(b),
+ convert_to_cgal_3d(c),
+ convert_to_cgal_3d(d));
+ return t.is_degenerate();
+ }
+ //-----------------------------------------------------------------------------
+ inline dolfin::Point convert_from_cgal(const Point_2& p)
+ {
+ return dolfin::Point(CGAL::to_double(p.x()),CGAL::to_double(p.y()));
+ }
+ //-----------------------------------------------------------------------------
+ inline dolfin::Point convert_from_cgal(const Point_3& p)
+ {
+ return dolfin::Point(CGAL::to_double(p.x()),
+ CGAL::to_double(p.y()),
+ CGAL::to_double(p.z()));
+ }
+ //-----------------------------------------------------------------------------
+ inline std::vector<dolfin::Point> convert_from_cgal(const Segment_2& s)
+ {
+ const std::vector<dolfin::Point> triangulation =
+ {{ dolfin::Point(CGAL::to_double(s.vertex(0)[0]),
+ CGAL::to_double(s.vertex(0)[1])),
+ dolfin::Point(CGAL::to_double(s.vertex(1)[0]),
+ CGAL::to_double(s.vertex(1)[1]))
+ }};
+ return triangulation;
+ }
+ //-----------------------------------------------------------------------------
+ inline std::vector<dolfin::Point> convert_from_cgal(const Segment_3& s)
+ {
+ const std::vector<dolfin::Point> triangulation =
+ {{ dolfin::Point(CGAL::to_double(s.vertex(0)[0]),
+ CGAL::to_double(s.vertex(0)[1]),
+ CGAL::to_double(s.vertex(0)[2])),
+ dolfin::Point(CGAL::to_double(s.vertex(1)[0]),
+ CGAL::to_double(s.vertex(1)[1]),
+ CGAL::to_double(s.vertex(1)[2]))
+ }};
+ return triangulation;
+ }
+ //-----------------------------------------------------------------------------
+ inline std::vector<dolfin::Point> convert_from_cgal(const Triangle_2& t)
+ {
+ const std::vector<dolfin::Point> triangulation =
+ {{ dolfin::Point(CGAL::to_double(t.vertex(0)[0]),
+ CGAL::to_double(t.vertex(0)[1])),
+ dolfin::Point(CGAL::to_double(t.vertex(2)[0]),
+ CGAL::to_double(t.vertex(2)[1])),
+ dolfin::Point(CGAL::to_double(t.vertex(1)[0]),
+ CGAL::to_double(t.vertex(1)[1]))
+ }};
+ return triangulation;
+ }
+ //-----------------------------------------------------------------------------
+ inline std::vector<dolfin::Point> convert_from_cgal(const Triangle_3& t)
+ {
+ const std::vector<dolfin::Point> triangulation =
+ {{ dolfin::Point(CGAL::to_double(t.vertex(0)[0]),
+ CGAL::to_double(t.vertex(0)[1]),
+ CGAL::to_double(t.vertex(0)[2])),
+ dolfin::Point(CGAL::to_double(t.vertex(2)[0]),
+ CGAL::to_double(t.vertex(2)[1]),
+ CGAL::to_double(t.vertex(2)[2])),
+ dolfin::Point(CGAL::to_double(t.vertex(1)[0]),
+ CGAL::to_double(t.vertex(1)[1]),
+ CGAL::to_double(t.vertex(1)[2]))
+ }};
+ return triangulation;
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<std::vector<dolfin::Point>>
+ triangulate_polygon_2d(const std::vector<dolfin::Point>& points)
+ {
+ // Convert points
+ std::vector<Point_2> pcgal(points.size());
+ for (std::size_t i = 0; i < points.size(); ++i)
+ pcgal[i] = convert_to_cgal_2d(points[i]);
+
+ // Triangulate
+ Triangulation_2 tcgal;
+ tcgal.insert(pcgal.begin(), pcgal.end());
+
+ // Convert back
+ std::vector<std::vector<dolfin::Point>> t;
+ for (Triangulation_2::Finite_faces_iterator fit = tcgal.finite_faces_begin();
+ fit != tcgal.finite_faces_end(); ++fit)
+ {
+ t.push_back({{ convert_from_cgal(tcgal.triangle(fit)[0]),
+ convert_from_cgal(tcgal.triangle(fit)[1]),
+ convert_from_cgal(tcgal.triangle(fit)[2]) }});
+ }
+
+ return t;
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<std::vector<dolfin::Point>>
+ triangulate_polygon_3d(const std::vector<dolfin::Point>& points)
+ {
+ // FIXME
+ dolfin::dolfin_error("CGALExactArithmetic.h",
+ "triangulate_polygon_3d",
+ "Not implemented");
+ return std::vector<std::vector<dolfin::Point>>();
+ }
+ //-----------------------------------------------------------------------------
+}
+
+namespace dolfin
+{
+ //---------------------------------------------------------------------------
+ // Reference implementations of DOLFIN collision detection predicates
+ // using CGAL exact arithmetic
+ // ---------------------------------------------------------------------------
+ inline bool cgal_collides_segment_point_2d(const Point& q0,
+ const Point& q1,
+ const Point& p,
+ bool only_interior=false)
+ {
+ const Point_2 q0_ = convert_to_cgal_2d(q0);
+ const Point_2 q1_ = convert_to_cgal_2d(q1);
+ const Point_2 p_ = convert_to_cgal_2d(p);
+
+ const bool intersects = CGAL::do_intersect(Segment_2(q0_, q1_), p_);
+ return only_interior ? intersects && p_ != q0_ && p_ != q1_ : intersects;
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_segment_point_3d(const Point& q0,
+ const Point& q1,
+ const Point& p,
+ bool only_interior=false)
+ {
+ const Point_3 q0_ = convert_to_cgal_3d(q0);
+ const Point_3 q1_ = convert_to_cgal_3d(q1);
+ const Point_3 p_ = convert_to_cgal_3d(p);
+
+ const Segment_3 segment(q0_, q1_);
+ const bool intersects = segment.has_on(p_);
+ return only_interior ? intersects && p_ != q0_ && p_ != q1_ : intersects;
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+ {
+ return CGAL::do_intersect(convert_to_cgal_2d(p0, p1),
+ convert_to_cgal_2d(q0, q1));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+ {
+ return CGAL::do_intersect(convert_to_cgal_3d(p0, p1),
+ convert_to_cgal_3d(q0, q1));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point &point)
+ {
+ return CGAL::do_intersect(convert_to_cgal_2d(p0, p1, p2),
+ convert_to_cgal_2d(point));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point &point)
+ {
+ const Triangle_3 tri = convert_to_cgal_3d(p0, p1, p2);
+ return tri.has_on(convert_to_cgal_3d(point));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+ {
+ return CGAL::do_intersect(convert_to_cgal_2d(p0, p1, p2),
+ convert_to_cgal_2d(q0, q1));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+ {
+ return CGAL::do_intersect(convert_to_cgal_3d(p0, p1, p2),
+ convert_to_cgal_3d(q0, q1));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ return CGAL::do_intersect(convert_to_cgal_2d(p0, p1, p2),
+ convert_to_cgal_2d(q0, q1, q2));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ return CGAL::do_intersect(convert_to_cgal_3d(p0, p1, p2),
+ convert_to_cgal_3d(q0, q1, q2));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0)
+ {
+ const Tetrahedron_3 tet = convert_to_cgal_3d(p0, p1, p2, p3);
+ return !tet.has_on_unbounded_side(convert_to_cgal_3d(q0));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1)
+ {
+ if (cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, q0) or
+ cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, q1))
+ return true;
+
+ if (cgal_collides_triangle_segment_3d(p0, p1, p2, q0, q1) or
+ cgal_collides_triangle_segment_3d(p0, p2, p3, q0, q1) or
+ cgal_collides_triangle_segment_3d(p0, p3, p1, q0, q1) or
+ cgal_collides_triangle_segment_3d(p1, p3, p2, q0, q1))
+ return true;
+
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ return CGAL::do_intersect(convert_to_cgal_3d(p0, p1, p2, p3),
+ convert_to_cgal_3d(q0, q1, q2));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_collides_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3)
+ {
+ // Check volume collisions
+ if (cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, q0)) return true;
+ if (cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, q1)) return true;
+ if (cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, q2)) return true;
+ if (cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, q3)) return true;
+ if (cgal_collides_tetrahedron_point_3d(q0, q1, q2, q3, p0)) return true;
+ if (cgal_collides_tetrahedron_point_3d(q0, q1, q2, q3, p1)) return true;
+ if (cgal_collides_tetrahedron_point_3d(q0, q1, q2, q3, p2)) return true;
+ if (cgal_collides_tetrahedron_point_3d(q0, q1, q2, q3, p3)) return true;
+
+ Polyhedron_3 tet_a;
+ tet_a.make_tetrahedron(convert_to_cgal_3d(p0),
+ convert_to_cgal_3d(p1),
+ convert_to_cgal_3d(p2),
+ convert_to_cgal_3d(p3));
+
+ Polyhedron_3 tet_b;
+ tet_b.make_tetrahedron(convert_to_cgal_3d(q0),
+ convert_to_cgal_3d(q1),
+ convert_to_cgal_3d(q2),
+ convert_to_cgal_3d(q3));
+
+ // Check for polyhedron intersection (recall that a polyhedron is
+ // only its vertices, edges and faces)
+ std::size_t cnt = 0;
+ CGAL::Counting_output_iterator out(&cnt);
+ CGAL::intersection_Polyhedron_3_Polyhedron_3<Polyhedron_3>(tet_a,
+ tet_b,
+ out);
+ // The tetrahedra does not intersect if cnt == 0
+ return cnt != 0;
+ }
+ //----------------------------------------------------------------------------
+ // Reference implementations of DOLFIN intersection triangulation
+ // functions using CGAL with exact arithmetic
+ // ---------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_intersection_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+ {
+ dolfin_assert(!is_degenerate_2d(p0, p1));
+ dolfin_assert(!is_degenerate_2d(q0, q1));
+
+ const auto I0 = convert_to_cgal_2d(p0, p1);
+ const auto I1 = convert_to_cgal_2d(q0, q1);
+
+ if (const auto ii = CGAL::intersection(I0, I1))
+ {
+ if (const Point_2* p = boost::get<Point_2>(&*ii))
+ {
+ return std::vector<Point>{convert_from_cgal(*p)};
+ }
+ else if (const Segment_2* s = boost::get<Segment_2>(&*ii))
+ {
+ return convert_from_cgal(*s);
+ }
+ else
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_segment_segment_2d",
+ "Unexpected behavior");
+ }
+ }
+
+ return std::vector<Point>();
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_intersection_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1));
+ dolfin_assert(!is_degenerate_3d(q0, q1));
+
+ const auto I0 = convert_to_cgal_3d(p0, p1);
+ const auto I1 = convert_to_cgal_3d(q0, q1);
+
+ if (const auto ii = CGAL::intersection(I0, I1))
+ {
+ if (const Point_3* p = boost::get<Point_3>(&*ii))
+ {
+ return std::vector<Point>{convert_from_cgal(*p)};
+ }
+ else if (const Segment_3* s = boost::get<Segment_3>(&*ii))
+ {
+ return convert_from_cgal(*s);
+ }
+ else
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_segment_segment_3d",
+ "Unexpected behavior");
+ }
+ }
+
+ return std::vector<Point>();
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_triangulate_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+ {
+ return cgal_intersection_segment_segment_2d(p0, p1, q0, q1);
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_triangulate_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+ {
+ return cgal_intersection_segment_segment_3d(p0, p1, q0, q1);
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_intersection_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+ {
+ dolfin_assert(!is_degenerate_2d(p0, p1, p2));
+ dolfin_assert(!is_degenerate_2d(q0, q1));
+
+ const auto T = convert_to_cgal_2d(p0, p1, p2);
+ const auto I = convert_to_cgal_2d(q0, q1);
+
+ if (const auto ii = CGAL::intersection(T, I))
+ {
+ if (const Point_2* p = boost::get<Point_2>(&*ii))
+ {
+ return std::vector<Point>{convert_from_cgal(*p)};
+ }
+ else if (const Segment_2* s = boost::get<Segment_2>(&*ii))
+ {
+ return convert_from_cgal(*s);
+ }
+ else
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_triangle_segment_2d",
+ "Unexpected behavior");
+ }
+ }
+
+ return std::vector<Point>();
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_intersection_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1, p2));
+ dolfin_assert(!is_degenerate_3d(q0, q1));
+
+ const auto T = convert_to_cgal_3d(p0, p1, p2);
+ const auto I = convert_to_cgal_3d(q0, q1);
+
+ if (const auto ii = CGAL::intersection(T, I))
+ {
+ if (const Point_3* p = boost::get<Point_3>(&*ii))
+ return std::vector<Point>{convert_from_cgal(*p)};
+ else if (const Segment_3* s = boost::get<Segment_3>(&*ii))
+ return convert_from_cgal(*s);
+ else
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_triangle_segment_3d",
+ "Unexpected behavior");
+ }
+ }
+
+ return std::vector<Point>();
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_triangulate_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+ {
+ return cgal_intersection_triangle_segment_2d(p0, p1, p2, q0, q1);
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_triangulate_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+ {
+ return cgal_intersection_triangle_segment_3d(p0, p1, p2, q0, q1);
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_intersection_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ dolfin_assert(!is_degenerate_2d(p0, p1, p2));
+ dolfin_assert(!is_degenerate_2d(q0, q1, q2));
+
+ const Triangle_2 T0 = convert_to_cgal_2d(p0, p1, p2);
+ const Triangle_2 T1 = convert_to_cgal_2d(q0, q1, q2);
+ std::vector<Point> intersection;
+
+ if (const auto ii = CGAL::intersection(T0, T1))
+ {
+ if (const Point_2* p = boost::get<Point_2>(&*ii))
+ {
+ intersection.push_back(convert_from_cgal(*p));
+ }
+ else if (const Segment_2* s = boost::get<Segment_2>(&*ii))
+ {
+ intersection = convert_from_cgal(*s);
+ }
+ else if (const Triangle_2* t = boost::get<Triangle_2>(&*ii))
+ {
+ intersection = convert_from_cgal(*t);;
+ }
+ else if (const std::vector<Point_2>* cgal_points = boost::get<std::vector<Point_2>>(&*ii))
+ {
+ for (Point_2 p : *cgal_points)
+ {
+ intersection.push_back(convert_from_cgal(p));
+ }
+ }
+ else
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_triangle_triangle_2d",
+ "Unexpected behavior");
+ }
+
+ // NB: the parsing can return triangulation of size 0, for example
+ // if it detected a triangle but it was found to be flat.
+ /* if (triangulation.size() == 0) */
+ /* dolfin_error("CGALExactArithmetic.h", */
+ /* "find intersection of two triangles in cgal_intersection_triangle_triangle function", */
+ /* "no intersection found"); */
+ }
+
+ return intersection;
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point> cgal_intersection_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1, p2));
+ dolfin_assert(!is_degenerate_3d(q0, q1, q2));
+
+ const Triangle_3 T0 = convert_to_cgal_3d(p0, p1, p2);
+ const Triangle_3 T1 = convert_to_cgal_3d(q0, q1, q2);
+ std::vector<Point> intersection;
+
+ if (const auto ii = CGAL::intersection(T0, T1))
+ {
+ if (const Point_3* p = boost::get<Point_3>(&*ii))
+ {
+ intersection.push_back(convert_from_cgal(*p));
+ }
+ else if (const Segment_3* s = boost::get<Segment_3>(&*ii))
+ {
+ intersection = convert_from_cgal(*s);
+ }
+ else if (const Triangle_3* t = boost::get<Triangle_3>(&*ii))
+ {
+ intersection = convert_from_cgal(*t);;
+ }
+ else if (const std::vector<Point_3>* cgal_points = boost::get<std::vector<Point_3>>(&*ii))
+ {
+ for (Point_3 p : *cgal_points)
+ {
+ intersection.push_back(convert_from_cgal(p));
+ }
+ }
+ else
+ {
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_triangle_triangle_3d",
+ "Unexpected behavior");
+ }
+ }
+
+ return intersection;
+ }
+ //----------------------------------------------------------------------------
+ inline
+ std::vector<std::vector<Point>>
+ cgal_triangulate_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ dolfin_assert(!is_degenerate_2d(p0, p1, p2));
+ dolfin_assert(!is_degenerate_2d(q0, q1, q2));
+
+ const std::vector<Point> intersection
+ = cgal_intersection_triangle_triangle_2d(p0, p1, p2, q0, q1, q2);
+
+ if (intersection.size() < 4)
+ {
+ return std::vector<std::vector<Point>>{intersection};
+ }
+ else
+ {
+ dolfin_assert(intersection.size() == 4 ||
+ intersection.size() == 5 ||
+ intersection.size() == 6);
+ return triangulate_polygon_2d(intersection);
+ }
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<std::vector<Point>>
+ cgal_triangulate_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1, p2));
+ dolfin_assert(!is_degenerate_3d(q0, q1, q2));
+
+ const std::vector<Point> intersection
+ = cgal_intersection_triangle_triangle_3d(p0, p1, p2, q0, q1, q2);
+
+ if (intersection.size() < 4)
+ {
+ return std::vector<std::vector<Point>>{intersection};
+ }
+ else
+ {
+ dolfin_assert(intersection.size() == 4 ||
+ intersection.size() == 5 ||
+ intersection.size() == 6);
+ return triangulate_polygon_3d(intersection);
+ }
+ }
+ //-----------------------------------------------------------------------------
+ inline
+ std::vector<Point>
+ cgal_intersection_tetrahedron_triangle(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1, p2, p3));
+ dolfin_assert(!is_degenerate_3d(q0, q1, q2));
+
+ // const Tetrahedron_3 tet = convert_from_cgal(p0, p1, p2, p3);
+ // const Triangle_3 tri = convert_from_cgal(q0, q1, q2);
+
+ Polyhedron_3 tet;
+ tet.make_tetrahedron(convert_to_cgal_3d(p0),
+ convert_to_cgal_3d(p1),
+ convert_to_cgal_3d(p2),
+ convert_to_cgal_3d(p3));
+ Polyhedron_3 tri;
+ tri.make_triangle(convert_to_cgal_3d(q0),
+ convert_to_cgal_3d(q1),
+ convert_to_cgal_3d(q2));
+
+ std::list<std::vector<Point_3> > triangulation;
+ CGAL::intersection_Polyhedron_3_Polyhedron_3(tet,
+ tri,
+ std::back_inserter(triangulation));
+
+ // FIXME: do we need to add interior point checks? Maybe
+ // Polyhedron_3 is only top dim 2?
+
+ // Shouldn't get here
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_intersection_tetrahedron_triangle",
+ "Not implemented");
+
+ return std::vector<Point>();
+ }
+ //-----------------------------------------------------------------------------
+ inline std::vector<std::vector<Point>>
+ cgal_triangulate_tetrahedron_triangle(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1, p2, p3));
+ dolfin_assert(!is_degenerate_3d(q0, q1, q2));
+
+ std::vector<Point> intersection =
+ cgal_intersection_tetrahedron_triangle(p0, p1, p2, p3, q0, q1, q2);
+
+ // Shouldn't get here
+ dolfin_error("CGALExactArithmetic.h",
+ "cgal_triangulation_tetrahedron_triangle",
+ "Not implemented");
+
+ return std::vector<std::vector<Point>>();
+ }
+ //-----------------------------------------------------------------------------
+ inline std::vector<Point>
+ cgal_intersection_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3)
+ {
+ dolfin_assert(!is_degenerate_3d(p0, p1, p2, p3));
+ dolfin_assert(!is_degenerate_3d(q0, q1, q2, q3));
+
+ Polyhedron_3 tet_a;
+ tet_a.make_tetrahedron(convert_to_cgal_3d(p0),
+ convert_to_cgal_3d(p1),
+ convert_to_cgal_3d(p2),
+ convert_to_cgal_3d(p3));
+ Polyhedron_3 tet_b;
+ tet_b.make_tetrahedron(convert_to_cgal_3d(q0),
+ convert_to_cgal_3d(q1),
+ convert_to_cgal_3d(q2),
+ convert_to_cgal_3d(q3));
+
+ const Nef_polyhedron_3 tet_a_nef(tet_a);
+ const Nef_polyhedron_3 tet_b_nef(tet_b);
+
+ const Nef_polyhedron_3 intersection_nef = tet_a_nef*tet_a_nef;
+
+ Polyhedron_3 intersection;
+ intersection_nef.convert_to_polyhedron(intersection);
+
+ std::vector<Point> res;
+ for (Polyhedron_3::Vertex_const_iterator vit = intersection.vertices_begin();
+ vit != intersection.vertices_end(); vit++)
+ {
+ res.push_back(Point(CGAL::to_double(vit->point().x()),
+ CGAL::to_double(vit->point().y()),
+ CGAL::to_double(vit->point().z())));
+ }
+
+ return res;
+ }
+ //----------------------------------------------------------------------------
+ // Reference implementations of DOLFIN is_degenerate
+ //-----------------------------------------------------------------------------
+ inline bool cgal_is_degenerate_2d(const std::vector<Point>& s)
+ {
+ if (s.size() < 2 or s.size() > 3)
+ {
+ info("Degenerate 2D simplex with %d vertices.", s.size());
+ return true;
+ }
+
+ switch (s.size())
+ {
+ case 2: return is_degenerate_2d(s[0], s[1]);
+ case 3: return is_degenerate_2d(s[0], s[1], s[2]);
+ }
+
+ // Shouldn't get here
+ dolfin_error("CGALExactArithmetic.h",
+ "call cgal_is_degenerate_2d",
+ "Only implemented for simplices of tdim 0, 1 and 2, not tdim = %d", s.size() - 1);
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_is_degenerate_3d(const std::vector<Point>& s)
+ {
+ if (s.size() < 2 or s.size() > 4)
+ {
+ info("Degenerate 3D simplex with %d vertices.", s.size());
+ return true;
+ }
+
+ switch (s.size())
+ {
+ case 2: return is_degenerate_3d(s[0], s[1]);
+ case 3: return is_degenerate_3d(s[0], s[1], s[2]);
+ case 4: return is_degenerate_3d(s[0], s[1], s[2], s[3]);
+ }
+
+ // Shouldn't get here
+ dolfin_error("CGALExactArithmetic.h",
+ "call cgal_is_degenerate_3d",
+ "Only implemented for simplices of tdim 0, 1, 2 and 3, not tdim = %d", s.size() - 1);
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+ // Computes the volume of the convex hull of the given points
+ inline double cgal_polyhedron_volume(const std::vector<Point>& ch)
+ {
+ std::vector<Point_3> exact_points;
+ exact_points.reserve(ch.size());
+ for (const Point p : ch)
+ {
+ exact_points.push_back(Point_3(p.x(), p.y(), p.z()));
+ }
+
+ // Compute the convex hull as a cgal polyhedron_3
+ Polyhedron_3 p;
+ CGAL::convex_hull_3(exact_points.begin(), exact_points.end(), p);
+
+ ExactNumber volume = .0;
+ for (Polyhedron_3::Facet_const_iterator it = p.facets_begin();
+ it != p.facets_end(); it++)
+ {
+ const Polyhedron_3::Halfedge_const_handle h = it->halfedge();
+ const Vector_3 V0 = h->vertex()->point()-CGAL::ORIGIN;
+ const Vector_3 V1 = h->next()->vertex()->point()-CGAL::ORIGIN;
+ const Vector_3 V2 = h->next()->next()->vertex()->point()-CGAL::ORIGIN;
+
+ volume += V0*CGAL::cross_product(V1, V2);
+ }
+
+ return std::abs(CGAL::to_double(volume/6.0));
+ }
+ //-----------------------------------------------------------------------------
+ inline double cgal_tet_volume(const std::vector<Point>& ch)
+ {
+ dolfin_assert(ch.size() == 3);
+ return CGAL::to_double(CGAL::volume(Point_3(ch[0].x(), ch[0].y(), ch[0].z()),
+ Point_3(ch[1].x(), ch[1].y(), ch[1].z()),
+ Point_3(ch[2].x(), ch[2].y(), ch[2].z()),
+ Point_3(ch[3].x(), ch[3].y(), ch[3].z())));
+ }
+ //-----------------------------------------------------------------------------
+ inline bool cgal_tet_is_degenerate(const std::vector<Point>& t)
+ {
+ Tetrahedron_3 tet(Point_3(t[0].x(), t[0].y(), t[0].z()),
+ Point_3(t[1].x(), t[1].y(), t[1].z()),
+ Point_3(t[2].x(), t[2].y(), t[2].z()),
+ Point_3(t[3].x(), t[3].y(), t[3].z()));
+
+ return tet.is_degenerate();
+ }
+
+ //-----------------------------------------------------------------------------
+ inline bool cgal_triangulation_has_degenerate(std::vector<std::vector<Point>> triangulation)
+ {
+ for (const std::vector<Point>& t : triangulation)
+ {
+ if (cgal_tet_is_degenerate(t))
+ return true;
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ inline bool cgal_triangulation_overlap(std::vector<std::vector<Point>> triangulation)
+ {
+ std::vector<Tetrahedron_3> tets;
+
+ for (const std::vector<Point>& t : triangulation)
+ {
+ Tetrahedron_3 tet(Point_3(t[0].x(), t[0].y(), t[0].z()),
+ Point_3(t[1].x(), t[1].y(), t[1].z()),
+ Point_3(t[2].x(), t[2].y(), t[2].z()),
+ Point_3(t[3].x(), t[3].y(), t[3].z()));
+
+ for (const Tetrahedron_3& t0 : tets)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if (t0.has_on_bounded_side(tet[i]) || tet.has_on_bounded_side(t0[i]))
+ return true;
+ }
+ }
+
+ tets.push_back(tet);
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+
+}
+#endif
+
+#endif
diff --git a/dolfin/geometry/CMakeLists.txt b/dolfin/geometry/CMakeLists.txt
index 2e0c397..49812d9 100644
--- a/dolfin/geometry/CMakeLists.txt
+++ b/dolfin/geometry/CMakeLists.txt
@@ -3,23 +3,32 @@ set(HEADERS
BoundingBoxTree2D.h
BoundingBoxTree3D.h
BoundingBoxTree.h
- CollisionDetection.h
+ CGALExactArithmetic.h
+ CollisionPredicates.h
+ ConvexTriangulation.h
dolfin_geometry.h
GenericBoundingBoxTree.h
+ GeometryDebugging.h
+ GeometryPredicates.h
intersect.h
- IntersectionTriangulation.h
+ IntersectionConstruction.h
MeshPointIntersection.h
Point.h
+ predicates.h
SimplexQuadrature.h
PARENT_SCOPE)
set(SOURCES
BoundingBoxTree.cpp
- CollisionDetection.cpp
+ CollisionPredicates.cpp
+ ConvexTriangulation.cpp
GenericBoundingBoxTree.cpp
+ GeometryDebugging.cpp
+ GeometryPredicates.cpp
intersect.cpp
- IntersectionTriangulation.cpp
+ IntersectionConstruction.cpp
MeshPointIntersection.cpp
Point.cpp
+ predicates.cpp
SimplexQuadrature.cpp
PARENT_SCOPE)
diff --git a/dolfin/geometry/CollisionDetection.cpp b/dolfin/geometry/CollisionDetection.cpp
deleted file mode 100644
index 199d178..0000000
--- a/dolfin/geometry/CollisionDetection.cpp
+++ /dev/null
@@ -1,1158 +0,0 @@
-// Copyright (C) 2014 Anders Logg and August Johansson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// Modified by Chris Richardson, 2014.
-//
-// First added: 2014-02-03
-// Last changed: 2014-04-03
-//
-//-----------------------------------------------------------------------------
-// Special note regarding the function collides_tetrahedron_tetrahedron
-//-----------------------------------------------------------------------------
-//
-// The source code for the tetrahedron-tetrahedron collision test is
-// from Fabio Ganovelli, Federico Ponchio and Claudio Rocchini: Fast
-// Tetrahedron-Tetrahedron Overlap Algorithm, Journal of Graphics
-// Tools, 7(2), 2002, and is under the following copyright:
-//
-// Visual Computing Group
-// IEI Institute, CNUCE Institute, CNR Pisa
-//
-// Copyright(C) 2002 by Fabio Ganovelli, Federico Ponchio and Claudio
-// Rocchini
-//
-// All rights reserved.
-//
-// Permission to use, copy, modify, distribute and sell this software
-// and its documentation for any purpose is hereby granted without
-// fee, provided that the above copyright notice appear in all copies
-// and that both that copyright notice and this permission notice
-// appear in supporting documentation. the author makes no
-// representations about the suitability of this software for any
-// purpose. It is provided "as is" without express or implied
-// warranty.
-//
-//-----------------------------------------------------------------------------
-// Special note regarding the function collides_triangle_triangle
-//-----------------------------------------------------------------------------
-//
-// The source code for the triangle-triangle collision test is from
-// Tomas Moller: A Fast Triangle-Triangle Intersection Test, Journal
-// of Graphics Tools, 2(2), 1997, and is in the public domain.
-//
-//-----------------------------------------------------------------------------
-
-#include <dolfin/mesh/MeshEntity.h>
-#include "Point.h"
-#include "CollisionDetection.h"
-
-using namespace dolfin;
-
-//-----------------------------------------------------------------------------
-bool CollisionDetection::collides(const MeshEntity& entity,
- const Point& point)
-{
- switch (entity.dim())
- {
- case 0:
- dolfin_not_implemented();
- break;
- case 1:
- return collides_interval_point(entity, point);
- case 2:
- return collides_triangle_point(entity, point);
- case 3:
- return collides_tetrahedron_point(entity, point);
- default:
- dolfin_error("CollisionDetection.cpp",
- "collides entity with point",
- "Unknown dimension of entity");
- }
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides(const MeshEntity& entity_0,
- const MeshEntity& entity_1)
-{
- switch (entity_0.dim())
- {
- case 0:
- // Collision with PointCell
- dolfin_not_implemented();
- break;
- case 1:
- // Collision with interval
- switch (entity_1.dim())
- {
- case 0:
- dolfin_not_implemented();
- break;
- case 1:
- return collides_interval_interval(entity_1, entity_0);
- break;
- case 2:
- dolfin_not_implemented();
- break;
- case 3:
- dolfin_not_implemented();
- break;
- default:
- dolfin_error("CollisionDetection.cpp",
- "collides entity_0 with entity_1",
- "Unknown dimension of entity_1 in IntervalCell collision");
- }
- break;
- case 2:
- // Collision with triangle
- switch (entity_1.dim())
- {
- case 0:
- dolfin_not_implemented();
- break;
- case 1:
- dolfin_not_implemented();
- break;
- case 2:
- return collides_triangle_triangle(entity_0, entity_1);
- case 3:
- return collides_tetrahedron_triangle(entity_1, entity_0);
- default:
- dolfin_error("CollisionDetection.cpp",
- "collides entity_0 with entity_1",
- "Unknown dimension of entity_1 in TriangleCell collision");
- }
- break;
- case 3:
- // Collision with tetrahedron
- switch (entity_1.dim())
- {
- case 0:
- dolfin_not_implemented();
- break;
- case 1:
- dolfin_not_implemented();
- break;
- case 2:
- return collides_tetrahedron_triangle(entity_0, entity_1);
- break;
- case 3:
- return collides_tetrahedron_tetrahedron(entity_0, entity_1);
- break;
- default:
- dolfin_error("CollisionDetection.cpp",
- "collides entity_0 with entity_1",
- "Unknown dimension of entity_1 in TetrahedronCell collision");
- }
- break;
- default:
- dolfin_error("CollisionDetection.cpp",
- "collides entity_0 with entity_1",
- "Unknown dimension of entity_0");
- }
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::collides_interval_point(const MeshEntity& entity,
- const Point& point)
-{
- // Get coordinates
- const MeshGeometry& geometry = entity.mesh().geometry();
- const unsigned int* vertices = entity.entities(0);
- return collides_interval_point(geometry.point(vertices[0]),
- geometry.point(vertices[1]),
- point);
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_interval_interval(const MeshEntity& interval_0,
- const MeshEntity& interval_1)
-{
- // Get coordinates
- const MeshGeometry& geometry_0 = interval_0.mesh().geometry();
- const MeshGeometry& geometry_1 = interval_1.mesh().geometry();
- const unsigned int* vertices_0 = interval_0.entities(0);
- const unsigned int* vertices_1 = interval_1.entities(0);
- const double x00 = geometry_0.point(vertices_0[0])[0];
- const double x01 = geometry_0.point(vertices_0[1])[0];
- const double x10 = geometry_1.point(vertices_1[0])[0];
- const double x11 = geometry_1.point(vertices_1[1])[0];
-
- const double a0 = std::min(x00, x01);
- const double b0 = std::max(x00, x01);
- const double a1 = std::min(x10, x11);
- const double b1 = std::max(x10, x11);
-
- // Check for collisions
- const double dx = std::min(b0 - a0, b1 - a1);
- const double eps = std::max(DOLFIN_EPS_LARGE, DOLFIN_EPS_LARGE*dx);
- return b1 > a0 - eps && a1 < b0 + eps;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::collides_triangle_point(const MeshEntity& triangle,
- const Point& point)
-{
- dolfin_assert(triangle.mesh().topology().dim() == 2);
-
- const MeshGeometry& geometry = triangle.mesh().geometry();
- const unsigned int* vertices = triangle.entities(0);
-
- if (triangle.mesh().geometry().dim() == 2)
- return collides_triangle_point_2d(geometry.point(vertices[0]),
- geometry.point(vertices[1]),
- geometry.point(vertices[2]),
- point);
- else
- return collides_triangle_point(geometry.point(vertices[0]),
- geometry.point(vertices[1]),
- geometry.point(vertices[2]),
- point);
-
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_triangle_triangle(const MeshEntity& triangle_0,
- const MeshEntity& triangle_1)
-{
- dolfin_assert(triangle_0.mesh().topology().dim() == 2);
- dolfin_assert(triangle_1.mesh().topology().dim() == 2);
-
- // Get vertices as points
- const MeshGeometry& geometry_0 = triangle_0.mesh().geometry();
- const unsigned int* vertices_0 = triangle_0.entities(0);
- const MeshGeometry& geometry_1 = triangle_1.mesh().geometry();
- const unsigned int* vertices_1 = triangle_1.entities(0);
-
- return collides_triangle_triangle(geometry_0.point(vertices_0[0]),
- geometry_0.point(vertices_0[1]),
- geometry_0.point(vertices_0[2]),
- geometry_1.point(vertices_1[0]),
- geometry_1.point(vertices_1[1]),
- geometry_1.point(vertices_1[2]));
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_tetrahedron_point(const MeshEntity& tetrahedron,
- const Point& point)
-{
- dolfin_assert(tetrahedron.mesh().topology().dim() == 3);
-
- // Get the vertices as points
- const MeshGeometry& geometry = tetrahedron.mesh().geometry();
- const unsigned int* vertices = tetrahedron.entities(0);
-
- return collides_tetrahedron_point(geometry.point(vertices[0]),
- geometry.point(vertices[1]),
- geometry.point(vertices[2]),
- geometry.point(vertices[3]),
- point);
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_tetrahedron_triangle(const MeshEntity& tetrahedron,
- const MeshEntity& triangle)
-{
- dolfin_assert(tetrahedron.mesh().topology().dim() == 3);
- dolfin_assert(triangle.mesh().topology().dim() == 2);
-
- // Get the vertices of the tetrahedron as points
- const MeshGeometry& geometry_tet = tetrahedron.mesh().geometry();
- const unsigned int* vertices_tet = tetrahedron.entities(0);
-
- // Get the vertices of the triangle as points
- const MeshGeometry& geometry_tri = triangle.mesh().geometry();
- const unsigned int* vertices_tri = triangle.entities(0);
-
- return collides_tetrahedron_triangle(geometry_tet.point(vertices_tet[0]),
- geometry_tet.point(vertices_tet[1]),
- geometry_tet.point(vertices_tet[2]),
- geometry_tet.point(vertices_tet[3]),
- geometry_tri.point(vertices_tri[0]),
- geometry_tri.point(vertices_tri[1]),
- geometry_tri.point(vertices_tri[2]));
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_tetrahedron_tetrahedron
-(const MeshEntity& tetrahedron_0,
- const MeshEntity& tetrahedron_1)
-{
- // This algorithm checks whether two tetrahedra intersect.
-
- // Algorithm and source code from Fabio Ganovelli, Federico Ponchio
- // and Claudio Rocchini: Fast Tetrahedron-Tetrahedron Overlap
- // Algorithm, Journal of Graphics Tools, 7(2), 2002. DOI:
- // 10.1080/10867651.2002.10487557. Source code available at
- // http://web.archive.org/web/20031130075955/http://www.acm.org/jgt/papers/GanovelliPonchioRocchini02/tet_a_tet.html
-
- dolfin_assert(tetrahedron_0.mesh().topology().dim() == 3);
- dolfin_assert(tetrahedron_1.mesh().topology().dim() == 3);
-
- // Get the vertices as points
- const MeshGeometry& geometry = tetrahedron_0.mesh().geometry();
- const unsigned int* vertices = tetrahedron_0.entities(0);
- const MeshGeometry& geometry_q = tetrahedron_1.mesh().geometry();
- const unsigned int* vertices_q = tetrahedron_1.entities(0);
- std::vector<Point> V1(4), V2(4);
- for (std::size_t i = 0; i < 4; ++i)
- {
- V1[i] = geometry.point(vertices[i]);
- V2[i] = geometry_q.point(vertices_q[i]);
- }
-
- // Get the vectors between V2 and V1[0]
- std::vector<Point> P_V1(4);
- for (std::size_t i = 0; i < 4; ++i)
- P_V1[i] = V2[i]-V1[0];
-
- // Data structure for edges of V1 and V2
- std::vector<Point> e_v1(5), e_v2(5);
- e_v1[0] = V1[1] - V1[0];
- e_v1[1] = V1[2] - V1[0];
- e_v1[2] = V1[3] - V1[0];
- Point n = e_v1[1].cross(e_v1[0]);
-
- // Maybe flip normal. Normal should be outward.
- if (n.dot(e_v1[2]) > 0)
- n *= -1;
- std::vector<int> masks(4);
- std::vector<std::vector<double>> Coord_1(4, std::vector<double>(4));
- if (separating_plane_face_A_1(P_V1, n, Coord_1[0], masks[0]))
- return false;
- n = e_v1[0].cross(e_v1[2]);
-
- // Maybe flip normal
- if (n.dot(e_v1[1]) > 0)
- n *= -1;
- if (separating_plane_face_A_1(P_V1, n, Coord_1[1], masks[1]))
- return false;
- if (separating_plane_edge_A(Coord_1, masks, 0, 1))
- return false;
- n = e_v1[2].cross(e_v1[1]);
-
- // Maybe flip normal
- if (n.dot(e_v1[0]) > 0)
- n *= -1;
- if (separating_plane_face_A_1(P_V1, n, Coord_1[2], masks[2]))
- return false;
- if (separating_plane_edge_A(Coord_1, masks, 0, 2))
- return false;
- if (separating_plane_edge_A(Coord_1, masks, 1,2))
- return false;
- e_v1[4] = V1[3] - V1[1];
- e_v1[3] = V1[2] - V1[1];
- n = e_v1[3].cross(e_v1[4]);
-
- // Maybe flip normal. Note the < since e_v1[0]=v1-v0.
- if (n.dot(e_v1[0]) < 0)
- n *= -1;
- if (separating_plane_face_A_2(V1, V2, n, Coord_1[3], masks[3]))
- return false;
- if (separating_plane_edge_A(Coord_1, masks, 0, 3))
- return false;
- if (separating_plane_edge_A(Coord_1, masks, 1, 3))
- return false;
- if (separating_plane_edge_A(Coord_1, masks, 2, 3))
- return false;
- if ((masks[0] | masks[1] | masks[2] | masks[3] )!= 15)
- return true;
-
- // From now on, if there is a separating plane, it is parallel to a
- // face of b.
- std::vector<Point> P_V2(4);
- for (std::size_t i = 0; i < 4; ++i)
- P_V2[i] = V1[i] - V2[0];
- e_v2[0] = V2[1] - V2[0];
- e_v2[1] = V2[2] - V2[0];
- e_v2[2] = V2[3] - V2[0];
- n = e_v2[1].cross(e_v2[0]);
-
- // Maybe flip normal
- if (n.dot(e_v2[2])>0)
- n *= -1;
- if (separating_plane_face_B_1(P_V2, n))
- return false;
- n=e_v2[0].cross(e_v2[2]);
-
- // Maybe flip normal
- if (n.dot(e_v2[1]) > 0)
- n *= -1;
- if (separating_plane_face_B_1(P_V2, n))
- return false;
- n = e_v2[2].cross(e_v2[1]);
-
- // Maybe flip normal
- if (n.dot(e_v2[0]) > 0)
- n *= -1;
- if (separating_plane_face_B_1(P_V2, n))
- return false;
- e_v2[4] = V2[3] - V2[1];
- e_v2[3] = V2[2] - V2[1];
- n = e_v2[3].cross(e_v2[4]);
-
- // Maybe flip normal. Note the < since e_v2[0] = V2[1] - V2[0].
- if (n.dot(e_v2[0]) < 0)
- n *= -1;
- if (separating_plane_face_B_2(V1, V2, n))
- return false;
-
- return true;
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_edge_edge(const Point& a,
- const Point& b,
- const Point& c,
- const Point& d)
-{
- const double tol = DOLFIN_EPS_LARGE;
-
- // Check if two edges are the same
- if ((a - c).norm() < tol and (b - d).norm() < tol)
- return false;
- if ((a - d).norm() < tol and (b - c).norm() < tol)
- return false;
-
- // Get edges as vectors and compute the normal
- const Point L1 = b - a;
- const Point L2 = d - c;
- const Point n = L1.cross(L2);
-
- // Check if L1 and L2 are coplanar
- const Point ca = c - a;
- if (std::abs(ca.dot(n)) > tol)
- return false;
-
- // Find orthogonal plane with normal n1
- const Point n1 = n.cross(L1);
- const double n1dotL2 = n1.dot(L2);
- if (std::abs(n1dotL2) < tol)
- return false;
- const double t = n1.dot(a - c) / n1dotL2;
- if (t <= 0 or t >= 1)
- return false;
-
- // Find orthogonal plane with normal n2
- const Point n2 = n.cross(L2);
- const double n2dotL1 = n2.dot(L1);
- if (std::abs(n2dotL1) < tol)
- return false;
- const double s = n2.dot(c - a) / n2dotL1;
- if (s <= 0 or s >= 1)
- return false;
-
- return true;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::collides_interval_point(const Point& p0,
- const Point& p1,
- const Point& point)
-{
- // Compute angle between v = p1 - p0 and w = point - p0
- Point v = p1 - p0;
- const double vnorm = v.norm();
-
- // p0 and p1 are the same points
- if (vnorm < DOLFIN_EPS_LARGE)
- return false;
-
- const Point w = point - p0;
- const double wnorm = w.norm();
-
- // point and p0 are the same points
- if (wnorm < DOLFIN_EPS)
- return true;
-
- // Compute cosine
- v /= vnorm;
- const double a = v.dot(w) / wnorm;
-
- // Cosine should be 1, and point should lie between p0 and p1
- if (std::abs(1-a) < DOLFIN_EPS_LARGE and wnorm <= vnorm)
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-bool CollisionDetection::collides_triangle_point_2d(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point &point)
-{
- // Simplified algorithm for coplanar triangles and points (z=0)
- // This algorithm is robust because it will perform the same numerical
- // test on each edge of neighbouring triangles. Points cannot slip
- // between the edges, and evade detection.
-
- // Vectors defining each edge in consistent orientation
- const Point r0 = p0 - p2;
- const Point r1 = p1 - p0;
- const Point r2 = p2 - p1;
-
- // Normal to triangle
- double normal = r1.x()*r0.y() - r1.y()*r0.x();
-
- // Compute normal to triangle based on point and first edge
- // Will have opposite sign if outside triangle
-
- Point r = point - p0;
- double pnormal = r.x()*r0.y() - r.y()*r0.x();
- if (pnormal != 0.0 and std::signbit(normal) != std::signbit(pnormal))
- return false;
-
- // Repeat for each edge
- r = point - p1;
- pnormal = r.x()*r1.y() - r.y()*r1.x();
- if (pnormal != 0.0 and std::signbit(normal) != std::signbit(pnormal))
- return false;
-
- r = point - p2;
- pnormal = r.x()*r2.y() - r.y()*r2.x();
- if (pnormal != 0.0 and std::signbit(normal) != std::signbit(pnormal))
- return false;
-
- return true;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::collides_triangle_point(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point &point)
-{
- // Algorithm from http://www.blackpawn.com/texts/pointinpoly/
-
- // Vectors defining each edge in consistent orientation
- const Point r0 = p0 - p2;
- const Point r1 = p1 - p0;
- const Point r2 = p2 - p1;
-
- // Normal to triangle: should be the same as
- // r2.cross(r1) and r0.cross(r2).
- Point normal = r1.cross(r0);
-
- Point r = point - p0;
- // Check point is in plane of triangle (for manifold)
- double volume = r.dot(normal);
- if (std::abs(volume) > DOLFIN_EPS)
- return false;
-
- // Compute normal to triangle based on point and first edge
- // Dot product of two normals should be positive, if inside.
- Point pnormal = r.cross(r0);
- double t1 = normal.dot(pnormal);
- if (t1 < 0) return false;
-
- // Repeat for each edge
- r = point - p1;
- pnormal = r.cross(r1);
- double t2 = normal.dot(pnormal);
- if (t2 < 0) return false;
-
- r = point - p2;
- pnormal = r.cross(r2);
- double t3 = normal.dot(pnormal);
- if (t3 < 0) return false;
-
- return true;
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_triangle_triangle(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& q0,
- const Point& q1,
- const Point& q2)
-{
- // Algorithm and code from Tomas Moller: A Fast Triangle-Triangle
- // Intersection Test, Journal of Graphics Tools, 2(2), 1997. Source
- // code is available at
- // http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/opttritri.txt
-
- // First check if the triangles are the same. We need to do this
- // separately if we do _not_ allow for adjacent edges to be
- // classified as colliding (see the edge_edge_test).
-
- const Point Vmid = (p0 + p1 + p2) / 3.;
- const Point Umid = (q0 + q1 + q2) / 3.;
- if ((Vmid-Umid).norm() < DOLFIN_EPS_LARGE)
- return true;
-
- Point E1, E2;
- Point N1, N2;
- double d1, d2;
- double du0, du1, du2, dv0, dv1, dv2;
- Point D;
- double isect1[2], isect2[2];
- double du0du1, du0du2, dv0dv1, dv0dv2;
- int index;
- double vp0, vp1, vp2;
- double up0, up1, up2;
- double bb, cc, max;
-
- // Compute plane equation of triangle(p0,p1,p2)
- E1 = p1-p0;
- E2 = p2-p0;
- N1 = E1.cross(E2);
- d1 = -N1.dot(p0);
-
- // Plane equation 1: N1.X+d1=0. Put q0,q1,q2 into plane equation 1
- // to compute signed distances to the plane
- du0 = N1.dot(q0)+d1;
- du1 = N1.dot(q1)+d1;
- du2 = N1.dot(q2)+d1;
-
- // Coplanarity robustness check
- if (std::abs(du0) < DOLFIN_EPS_LARGE)
- du0 = 0.0;
- if (std::abs(du1) < DOLFIN_EPS_LARGE)
- du1 = 0.0;
- if (std::abs(du2) < DOLFIN_EPS_LARGE)
- du2 = 0.0;
- du0du1 = du0*du1;
- du0du2 = du0*du2;
-
- // Same sign on all of them + not equal 0?
- if (du0du1>0. && du0du2>0.)
- return false;
-
- // Compute plane of triangle (q0,q1,q2)
- E1 = q1-q0;
- E2 = q2-q0;
- N2 = E1.cross(E2);
- d2 = -N2.dot(q0);
- // Plane equation 2: N2.X+d2=0. Put p0,p1,p2 into plane equation 2
- dv0 = N2.dot(p0)+d2;
- dv1 = N2.dot(p1)+d2;
- dv2 = N2.dot(p2)+d2;
-
- // Coplanarity check
- if (std::abs(dv0) < DOLFIN_EPS_LARGE)
- dv0 = 0.0;
- if (std::abs(dv1) < DOLFIN_EPS_LARGE)
- dv1 = 0.0;
- if (std::abs(dv2) < DOLFIN_EPS_LARGE)
- dv2 = 0.0;
- dv0dv1 = dv0*dv1;
- dv0dv2 = dv0*dv2;
-
- // Same sign on all of them + not equal 0 ?
- if (dv0dv1>0. && dv0dv2>0.)
- return false;
-
- // Compute direction of intersection line
- D = N1.cross(N2);
-
- // Compute and index to the largest component of D
- max = (double)std::abs(D[0]);
- index = 0;
- bb = (double)std::abs(D[1]);
- cc = (double)std::abs(D[2]);
- if (bb > max)
- max = bb, index = 1;
- if (cc > max)
- max = cc, index = 2;
-
- // This is the simplified projection onto L
- vp0 = p0[index];
- vp1 = p1[index];
- vp2 = p2[index];
-
- up0 = q0[index];
- up1 = q1[index];
- up2 = q2[index];
-
- // Compute interval for triangle 1
- double a, b, c, x0, x1;
- if (compute_intervals(vp0, vp1, vp2, dv0, dv1, dv2, dv0dv1, dv0dv2,
- a, b, c, x0, x1))
- return coplanar_tri_tri(N1, p0, p1, p2, q0, q1, q2);
-
- // Compute interval for triangle 2
- double d, e, f, y0, y1;
- if (compute_intervals(up0, up1, up2, du0, du1, du2, du0du1, du0du2,
- d, e, f, y0, y1))
- return coplanar_tri_tri(N1, p0, p1, p2, q0, q1, q2);
-
- double xx, yy, xxyy, tmp;
- xx = x0*x1;
- yy = y0*y1;
- xxyy = xx*yy;
-
- tmp = a*xxyy;
- isect1[0] = tmp+b*x1*yy;
- isect1[1] = tmp+c*x0*yy;
-
- tmp = d*xxyy;
- isect2[0] = tmp+e*xx*y1;
- isect2[1] = tmp+f*xx*y0;
-
- if (isect1[0] > isect1[1])
- std::swap(isect1[0], isect1[1]);
- if (isect2[0] > isect2[1])
- std::swap(isect2[0], isect2[1]);
-
- if (isect1[1] < isect2[0] ||
- isect2[1] < isect1[0])
- return false;
-
- return true;
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_tetrahedron_point(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& p3,
- const Point& point)
-{
- // Algorithm from http://www.blackpawn.com/texts/pointinpoly/
- // See also "Real-Time Collision Detection" by Christer Ericson.
-
- const Point *p[4] = {&p0, &p1, &p2, &p3};
-
- // Consider each face in turn
- for (unsigned int i = 0; i != 4; ++i)
- {
- // Compute vectors relative to p[i]
- const Point v1 = *p[(i + 1)%4] - *p[i];
- const Point v2 = *p[(i + 2)%4] - *p[i];
- const Point v3 = *p[(i + 3)%4] - *p[i];
- const Point v = point - *p[i];
- // Normal to plane containing v1 and v2
- const Point n1 = v1.cross(v2);
- // Find which side of face plane points v and v3 lie
- const double t1 = n1.dot(v);
- const double t2 = n1.dot(v3);
- // Catch case where point is exactly on plane
- // otherwise require points to be on same side
- if (t1 != 0.0 and std::signbit(t1) != std::signbit(t2))
- return false;
- }
- return true;
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::collides_tetrahedron_triangle(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& p3,
- const Point& q0,
- const Point& q1,
- const Point& q2)
-{
- // Collision is determined by first if any triangle vertex is inside
- // the tetrahedron. If not, we continue checking the intersection of
- // the triangle with the four faces of the tetrahedron.
-
- // Triangle vertex in tetrahedron collision
- if (collides_tetrahedron_point(p0, p1, p2, p3, q0))
- return true;
- if (collides_tetrahedron_point(p0, p1, p2, p3, q1))
- return true;
- if (collides_tetrahedron_point(p0, p1, p2, p3, q2))
- return true;
-
- // Triangle-triangle collision tests
- if (collides_triangle_triangle(q0, q1, q2, p1, p2, p3))
- return true;
- if (collides_triangle_triangle(q0, q1, q2, p0, p2, p3))
- return true;
- if (collides_triangle_triangle(q0, q1, q2, p0, p1, p3))
- return true;
- if (collides_triangle_triangle(q0, q1, q2, p0, p1, p2))
- return true;
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::edge_edge_test(int i0,
- int i1,
- double Ax,
- double Ay,
- const Point& V0,
- const Point& U0,
- const Point& U1)
-{
- // Helper function for triangle triangle collision. Test edge vs
- // edge.
-
- // Here we have the option of classifying adjacent edges of two
- // triangles as colliding by changing > to >= and < to <= below.
-
- const double Bx = U0[i0] - U1[i0];
- const double By = U0[i1] - U1[i1];
- const double Cx = V0[i0] - U0[i0];
- const double Cy = V0[i1] - U0[i1];
- const double f = Ay*Bx - Ax*By;
- const double d = By*Cx - Bx*Cy;
-
- if ((f > 0 && d >= 0 && d <= f) ||
- (f < 0 && d <= 0 && d >= f))
- {
- const double e = Ax*Cy - Ay*Cx;
- if (f > 0)
- {
- // Allow or not allow adjacent edges as colliding:
- //if (e >= 0 && e <= f) return true;
- if (e > 0 && e < f)
- return true;
- }
- else
- {
- // Allow or not allow adjacent edges as colliding:
- //if (e <= 0 && e >= f) return true;
- if (e < 0 && e > f)
- return true;
- }
- }
- return false;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::edge_against_tri_edges(int i0,
- int i1,
- const Point& V0,
- const Point& V1,
- const Point& U0,
- const Point& U1,
- const Point& U2)
-{
- // Helper function for triangle triangle collision
- const double Ax = V1[i0] - V0[i0];
- const double Ay = V1[i1] - V0[i1];
-
- // Test edge U0,U1 against V0,V1
- if (edge_edge_test(i0, i1, Ax, Ay, V0, U0, U1))
- return true;
-
- // Test edge U1,U2 against V0,V1
- if (edge_edge_test(i0, i1, Ax, Ay, V0, U1, U2))
- return true;
-
- // Test edge U2,U1 against V0,V1
- if (edge_edge_test(i0, i1, Ax, Ay, V0, U2, U0))
- return true;
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::point_in_tri(int i0,
- int i1,
- const Point& V0,
- const Point& U0,
- const Point& U1,
- const Point& U2)
-{
- // Helper function for triangle triangle collision
- // Is T1 completely inside T2?
- // Check if V0 is inside tri(U0,U1,U2)
- double a = U1[i1] - U0[i1];
- double b = -(U1[i0] - U0[i0]);
- double c = -a*U0[i0] - b*U0[i1];
- const double d0 = a*V0[i0] + b*V0[i1] + c;
-
- a = U2[i1] - U1[i1];
- b = -(U2[i0] - U1[i0]);
- c = -a*U1[i0] - b*U1[i1];
- const double d1 = a*V0[i0] + b*V0[i1] + c;
-
- a = U0[i1] - U2[i1];
- b = -(U0[i0] - U2[i0]);
- c = -a*U2[i0] - b*U2[i1];
- const double d2 = a*V0[i0] + b*V0[i1] + c;
-
- if (d0*d1 > 0.)
- {
- if (d0*d2 > 0.)
- return true;
- }
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::coplanar_tri_tri(const Point& N,
- const Point& V0,
- const Point& V1,
- const Point& V2,
- const Point& U0,
- const Point& U1,
- const Point& U2)
-{
- // Helper function for triangle triangle collision
-
- double A[3];
- int i0,i1;
-
- // First project onto an axis-aligned plane, that maximizes the area
- // of the triangles, compute indices: i0,i1.
- A[0] = std::abs(N[0]);
- A[1] = std::abs(N[1]);
- A[2] = std::abs(N[2]);
-
- if (A[0] > A[1])
- {
- if (A[0] > A[2])
- {
- i0 = 1; // A[0] is greatest
- i1 = 2;
- }
- else
- {
- i0 = 0; // A[2] is greatest
- i1 = 1;
- }
- }
- else // A[0]<=A[1]
- {
- if (A[2] > A[1])
- {
- i0 = 0; // A[2] is greatest
- i1 = 1;
- }
- else
- {
- i0 = 0; // A[1] is greatest
- i1 = 2;
- }
- }
-
- // Test all edges of triangle 1 against the edges of triangle 2
- if (edge_against_tri_edges(i0, i1, V0, V1, U0, U1, U2))
- return true;
- if (edge_against_tri_edges(i0, i1, V1, V2, U0, U1, U2))
- return true;
- if (edge_against_tri_edges(i0, i1, V2, V0, U0, U1, U2))
- return true;
-
- // Finally, test if tri1 is totally contained in tri2 or vice versa
- if (point_in_tri(i0, i1, V0, U0, U1, U2))
- return true;
- if (point_in_tri(i0, i1, U0, V0, V1, V2))
- return true;
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::compute_intervals(double VV0,
- double VV1,
- double VV2,
- double D0,
- double D1,
- double D2,
- double D0D1,
- double D0D2,
- double& A,
- double& B,
- double& C,
- double& X0,
- double& X1)
-{
- // Helper function for triangle triangle collision
-
- if (D0D1 > 0.)
- {
- // Here we know that D0D2<=0.0, that is D0, D1 are on the same
- // side, D2 on the other or on the plane
- A = VV2;
- B = (VV0 - VV2)*D2;
- C = (VV1 - VV2)*D2;
- X0 = D2 - D0;
- X1 = D2 - D1;
- }
- else if (D0D2 > 0.)
- {
- // Here we know that d0d1<=0.0
- A = VV1;
- B = (VV0 - VV1)*D1;
- C = (VV2 - VV1)*D1;
- X0 = D1 - D0;
- X1 = D1 - D2;
- }
- else if (D1*D2 > 0. || D0 != 0.)
- {
- // Here we know that d0d1<=0.0 or that D0!=0.0
- A = VV0;
- B = (VV1 - VV0)*D0;
- C = (VV2 - VV0)*D0;
- X0 = D0 - D1;
- X1 = D0 - D2;
- }
- else if (D1 != 0.)
- {
- A = VV1;
- B = (VV0 - VV1)*D1;
- C = (VV2 - VV1)*D1;
- X0 = D1 - D0;
- X1 = D1 - D2;
- }
- else if (D2 != 0.)
- {
- A = VV2;
- B = (VV0 - VV2)*D2;
- C = (VV1 - VV2)*D2;
- X0 = D2 - D0;
- X1 = D2 - D1;
- }
- else {
- // Go to coplanar test
- return true;
- }
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::separating_plane_face_A_1(const std::vector<Point>& pv1,
- const Point& n,
- std::vector<double>& coord,
- int& mask_edges)
-{
- // Helper function for tetrahedron-tetrahedron collision test:
- // checks if plane pv1 is a separating plane. Stores local
- // coordinates and the mask bit mask_edges.
-
- mask_edges = 0;
- const int shifts[4] = {1, 2, 4, 8};
-
- for (std::size_t i = 0; i < 4; ++i)
- {
- coord[i] = pv1[i].dot(n);
- if (coord[i] > 0)
- mask_edges |= shifts[i];
- }
-
- return (mask_edges == 15);
-}
-//-----------------------------------------------------------------------------
-bool
-CollisionDetection::separating_plane_face_A_2(const std::vector<Point>& V1,
- const std::vector<Point>& V2,
- const Point& n,
- std::vector<double>& coord,
- int& mask_edges)
-{
- // Helper function for tetrahedron-tetrahedron collision test:
- // checks if plane v1,v2 is a separating plane. Stores local
- // coordinates and the mask bit mask_edges.
-
- mask_edges = 0;
- const int shifts[4] = {1, 2, 4, 8};
-
- for (std::size_t i = 0; i < 4; ++i)
- {
- coord[i] = (V2[i] - V1[1]).dot(n);
- if (coord[i] > 0)
- mask_edges |= shifts[i];
- }
-
- return (mask_edges == 15);
-}
-//-----------------------------------------------------------------------------
-bool CollisionDetection::separating_plane_edge_A(
- const std::vector<std::vector<double>>& coord_1,
- const std::vector<int>& masks, int f0, int f1)
-{
- // Helper function for tetrahedron-tetrahedron collision: checks if
- // edge is in the plane separating faces f0 and f1.
-
- const std::vector<double>& coord_f0 = coord_1[f0];
- const std::vector<double>& coord_f1 = coord_1[f1];
-
- int maskf0 = masks[f0];
- int maskf1 = masks[f1];
-
- if ((maskf0 | maskf1) != 15) // if there is a vertex of b
- return false; // included in (-,-) return false
-
- maskf0 &= (maskf0 ^ maskf1); // exclude the vertices in (+,+)
- maskf1 &= (maskf0 ^ maskf1);
-
- // edge 0: 0--1
- if ((maskf0 & 1) && // the vertex 0 of b is in (-,+)
- (maskf1 & 2)) // the vertex 1 of b is in (+,-)
- if ((coord_f0[1]*coord_f1[0] - coord_f0[0]*coord_f1[1]) > 0)
- // the edge of b (0,1) intersect (-,-) (see the paper)
- return false;
-
- if ((maskf0 & 2) &&
- (maskf1 & 1))
- if ((coord_f0[1]*coord_f1[0] - coord_f0[0]*coord_f1[1]) < 0)
- return false;
-
- // edge 1: 0--2
- if ((maskf0 & 1) &&
- (maskf1 & 4))
- if ((coord_f0[2]*coord_f1[0] - coord_f0[0]*coord_f1[2]) > 0)
- return false;
-
- if ((maskf0 & 4) &&
- (maskf1 & 1))
- if ((coord_f0[2]*coord_f1[0] - coord_f0[0]*coord_f1[2]) < 0)
- return false;
-
- // edge 2: 0--3
- if ((maskf0 & 1) &&
- (maskf1 & 8))
- if ((coord_f0[3]*coord_f1[0] - coord_f0[0]*coord_f1[3]) > 0)
- return false;
-
- if ((maskf0 & 8) &&
- (maskf1 & 1))
- if ((coord_f0[3]*coord_f1[0] - coord_f0[0]*coord_f1[3]) < 0)
- return false;
-
- // edge 3: 1--2
- if ((maskf0 & 2) &&
- (maskf1 & 4))
- if ((coord_f0[2]*coord_f1[1] - coord_f0[1]*coord_f1[2]) > 0)
- return false;
-
- if ((maskf0 & 4) &&
- (maskf1 & 2))
- if ((coord_f0[2]*coord_f1[1] - coord_f0[1]*coord_f1[2]) < 0)
- return false;
-
- // edge 4: 1--3
- if ((maskf0 & 2) &&
- (maskf1 & 8))
- if ((coord_f0[3]*coord_f1[1] - coord_f0[1]*coord_f1[3]) > 0)
- return false;
-
- if ((maskf0 & 8) &&
- (maskf1 & 2))
- if ((coord_f0[3]*coord_f1[1] - coord_f0[1]*coord_f1[3]) < 0)
- return false;
-
- // edge 5: 2--3
- if ((maskf0 & 4) &&
- (maskf1 & 8))
- if ((coord_f0[3]*coord_f1[2] - coord_f0[2]*coord_f1[3]) > 0)
- return false;
-
- if ((maskf0 & 8) &&
- (maskf1 & 4))
- if ((coord_f0[3]*coord_f1[2] - coord_f0[2]*coord_f1[3]) < 0)
- return false;
-
- // Now there exists a separating plane supported by the edge shared
- // by f0 and f1.
- return true;
-}
-//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/CollisionDetection.h b/dolfin/geometry/CollisionDetection.h
deleted file mode 100644
index 08c5f63..0000000
--- a/dolfin/geometry/CollisionDetection.h
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (C) 2014 Anders Logg and August Johansson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2014-02-03
-// Last changed: 2014-04-03
-
-#include <vector>
-#include <dolfin/log/log.h>
-
-#ifndef __COLLISION_DETECTION_H
-#define __COLLISION_DETECTION_H
-
-namespace dolfin
-{
-
- // Forward declarations
- class MeshEntity;
-
- /// This class implements algorithms for detecting pairwise
- /// collisions between mesh entities of varying dimensions.
-
- class CollisionDetection
- {
- public:
-
- /// Check whether entity collides with point.
- ///
- /// @param entity (_MeshEntity_)
- /// The entity.
- /// @param point (_Point_)
- /// The point.
- ///
- /// @return bool
- /// True iff entity collides with cell.
- static bool collides(const MeshEntity& entity,
- const Point& point);
-
- /// Check whether two entities collide.
- ///
- /// @param entity_0 (_MeshEntity_)
- /// The first entity.
- /// @param entity_1 (_MeshEntity_)
- /// The second entity.
- ///
- /// @return bool
- /// True iff entity collides with cell.
- static bool collides(const MeshEntity& entity_0,
- const MeshEntity& entity_1);
-
- /// Check whether interval collides with point.
- ///
- /// @param interval (MeshEntity)
- /// The interval.
- /// @param point (Point)
- /// The point.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_interval_point(const MeshEntity& interval,
- const Point& point);
-
- /// Check whether interval collides with interval.
- ///
- /// @param interval_0 (MeshEntity)
- /// The first interval.
- /// @param interval_1 (MeshEntity)
- /// The second interval.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_interval_interval(const MeshEntity& interval_0,
- const MeshEntity& interval_1);
-
- /// Check whether triangle collides with point.
- ///
- /// @param triangle (MeshEntity)
- /// The triangle.
- /// @param point (Point)
- /// The point.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_triangle_point(const MeshEntity& triangle,
- const Point& point);
-
- /// Check whether triangle collides with triangle.
- ///
- /// @param triangle_0 (_MeshEntity_)
- /// The first triangle.
- /// @param triangle_1 (_MeshEntity_)
- /// The second triangle.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_triangle_triangle(const MeshEntity& triangle_0,
- const MeshEntity& triangle_1);
-
- /// Check whether tetrahedron collides with point.
- ///
- /// @param tetrahedron (MeshEntity)
- /// The tetrahedron.
- /// @param point (Point)
- /// The point.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_tetrahedron_point(const MeshEntity& tetrahedron,
- const Point& point);
-
- /// Check whether tetrahedron collides with triangle.
- ///
- /// @param tetrahedron (_MeshEntity_)
- /// The tetrahedron.
- /// @param triangle (_MeshEntity_)
- /// The triangle.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_tetrahedron_triangle(const MeshEntity& tetrahedron,
- const MeshEntity& triangle);
-
- /// Check whether tetrahedron collides with tetrahedron.
- ///
- /// @param tetrahedron_0 (_MeshEntity_)
- /// The first tetrahedron.
- /// @param tetrahedron_1 (_MeshEntity_)
- /// The second tetrahedron.
- ///
- /// @return bool
- /// True iff objects collide.
- static bool collides_tetrahedron_tetrahedron(const MeshEntity& tetrahedron_0,
- const MeshEntity& tetrahedron_1);
-
- /// Check whether edge a-b collides with edge c-d.
- static bool collides_edge_edge(const Point& a, const Point& b,
- const Point& c, const Point& d);
-
-
- /// The implementation of collides_interval_point
- static bool collides_interval_point(const Point& p0, const Point& p1,
- const Point& point);
-
- /// The implementation of collides_triangle_point
- static bool collides_triangle_point(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& point);
-
- /// Specialised implementation of collides_triangle_point in 2D
- static bool collides_triangle_point_2d(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& point);
-
- /// The implementation of collides_tetrahedron_point
- static bool collides_tetrahedron_point(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& p3,
- const Point& point);
- private:
-
- // The implementation of collides_triangle_triangle
- static bool collides_triangle_triangle(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& q0,
- const Point& q1,
- const Point& q2);
-
-
- // The implementation of collides_tetrahedron_triangle
- static bool collides_tetrahedron_triangle(const Point& p0,
- const Point& p1,
- const Point& p2,
- const Point& p3,
- const Point& q0,
- const Point& q1,
- const Point& q2);
-
- // Helper function for triangle-triangle collision
- static bool edge_edge_test(int i0,
- int i1,
- double Ax,
- double Ay,
- const Point& V0,
- const Point& U0,
- const Point& U1);
-
- // Helper function for triangle-triangle collision
- static bool edge_against_tri_edges(int i0,
- int i1,
- const Point& V0,
- const Point& V1,
- const Point& U0,
- const Point& U1,
- const Point& U2);
-
- // Helper function for triangle-triangle collision
- static bool point_in_tri(int i0,
- int i1,
- const Point& V0,
- const Point& U0,
- const Point& U1,
- const Point& U2);
-
- // Helper function for triangle-triangle collision
- static bool coplanar_tri_tri(const Point& N,
- const Point& V0,
- const Point& V1,
- const Point& V2,
- const Point& U0,
- const Point& U1,
- const Point& U2);
-
- // Helper function for triangle-triangle collision
- static bool compute_intervals(double VV0,
- double VV1,
- double VV2,
- double D0,
- double D1,
- double D2,
- double D0D1,
- double D0D2,
- double& A,
- double& B,
- double& C,
- double& X0,
- double& X1);
-
- // Helper function for collides_tetrahedron_tetrahedron: checks if
- // plane pv1 is a separating plane. Stores local coordinates bc
- // and the mask bit mask_edges.
- static bool separating_plane_face_A_1(const std::vector<Point>& pv1,
- const Point& n,
- std::vector<double>& bc,
- int& mask_edges);
-
- // Helper function for collides_tetrahedron_tetrahedron: checks if
- // plane v1, v2 is a separating plane. Stores local coordinates bc
- // and the mask bit mask_edges.
- static bool separating_plane_face_A_2(const std::vector<Point>& v1,
- const std::vector<Point>& v2,
- const Point& n,
- std::vector<double>& bc,
- int& mask_edges);
-
- // Helper function for collides_tetrahedron_tetrahedron: checks if
- // plane pv2 is a separating plane.
- static bool separating_plane_face_B_1(const std::vector<Point>& P_V2,
- const Point& n)
- {
- return ((P_V2[0].dot(n) > 0) &&
- (P_V2[1].dot(n) > 0) &&
- (P_V2[2].dot(n) > 0) &&
- (P_V2[3].dot(n) > 0));
- }
-
- // Helper function for collides_tetrahedron_tetrahedron: checks if
- // plane v1, v2 is a separating plane.
- static bool separating_plane_face_B_2(const std::vector<Point>& V1,
- const std::vector<Point>& V2,
- const Point& n)
- {
- return (((V1[0] - V2[1]).dot(n) > 0) &&
- ((V1[1] - V2[1]).dot(n) > 0) &&
- ((V1[2] - V2[1]).dot(n) > 0) &&
- ((V1[3] - V2[1]).dot(n) > 0));
- }
-
- // Helper function for collides_tetrahedron_tetrahedron: checks if
- // edge is in the plane separating faces f0 and f1.
- static bool separating_plane_edge_A(const std::vector<std::vector<double> >& coord_1,
- const std::vector<int>& masks,
- int f0,
- int f1);
-
- };
-
-}
-
-#endif
diff --git a/dolfin/geometry/CollisionPredicates.cpp b/dolfin/geometry/CollisionPredicates.cpp
new file mode 100644
index 0000000..2a5bc5c
--- /dev/null
+++ b/dolfin/geometry/CollisionPredicates.cpp
@@ -0,0 +1,1053 @@
+// Copyright (C) 2014-2017 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2014-02-03
+// Last changed: 2017-10-09
+
+#include <dolfin/mesh/MeshEntity.h>
+#include <dolfin/mesh/CellType.h>
+#include "predicates.h"
+#include "Point.h"
+#include "CollisionPredicates.h"
+#include "GeometryTools.h"
+
+#include "CGALExactArithmetic.h"
+
+using namespace dolfin;
+
+//-----------------------------------------------------------------------------
+// High-level collision detection predicates
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::collides(const MeshEntity& entity,
+ const Point& point)
+{
+ // Intersection is only implemented for simplex meshes
+ if (!entity.mesh().type().is_simplex())
+ {
+ dolfin_error("Cell.cpp",
+ "intersect cell and point",
+ "Intersection is only implemented for simplex meshes");
+ }
+
+ // Get data
+ const MeshGeometry& g = entity.mesh().geometry();
+ const unsigned int* v = entity.entities(0);
+ const std::size_t tdim = entity.mesh().topology().dim();
+ const std::size_t gdim = entity.mesh().geometry().dim();
+
+ // Pick correct specialized implementation
+ if (tdim == 1 && gdim == 1)
+ return collides_segment_point_1d(g.point(v[0])[0], g.point(v[1])[0], point[0]);
+
+ if (tdim == 1 && gdim == 2)
+ return collides_segment_point_2d(g.point(v[0]), g.point(v[1]), point);
+
+ if (tdim == 1 && gdim == 3)
+ return collides_segment_point_3d(g.point(v[0]), g.point(v[1]), point);
+
+ if (tdim == 2 && gdim == 2)
+ return collides_triangle_point_2d(g.point(v[0]),
+ g.point(v[1]),
+ g.point(v[2]),
+ point);
+
+ if (tdim == 2 && gdim == 3)
+ return collides_triangle_point_3d(g.point(v[0]),
+ g.point(v[1]),
+ g.point(v[2]),
+ point);
+
+ if (tdim == 3)
+ return collides_tetrahedron_point_3d(g.point(v[0]),
+ g.point(v[1]),
+ g.point(v[2]),
+ g.point(v[3]),
+ point);
+
+ dolfin_error("CollisionPredicates.cpp",
+ "compute entity-point collision",
+ "Not implemented for dimensions %d / %d", tdim, gdim);
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::collides(const MeshEntity& entity_0,
+ const MeshEntity& entity_1)
+{
+ // Intersection is only implemented for simplex meshes
+ if (!entity_0.mesh().type().is_simplex() ||
+ !entity_1.mesh().type().is_simplex())
+ {
+ dolfin_error("Cell.cpp",
+ "intersect cell and point",
+ "intersection is only implemented for simplex meshes");
+ }
+
+ // Get data
+ const MeshGeometry& g0 = entity_0.mesh().geometry();
+ const MeshGeometry& g1 = entity_1.mesh().geometry();
+ const unsigned int* v0 = entity_0.entities(0);
+ const unsigned int* v1 = entity_1.entities(0);
+ const std::size_t d0 = entity_0.dim();
+ const std::size_t d1 = entity_1.dim();
+ const std::size_t gdim = g0.dim();
+ dolfin_assert(gdim == g1.dim());
+
+ // Pick correct specialized implementation
+ if (d0 == 1 && d1 == 1)
+ {
+ return collides_segment_segment(g0.point(v0[0]),
+ g0.point(v0[1]),
+ g1.point(v1[0]),
+ g1.point(v1[1]),
+ gdim);
+ }
+
+ if (d0 == 1 && d1 == 2)
+ {
+ return collides_triangle_segment(g1.point(v1[0]),
+ g1.point(v1[1]),
+ g1.point(v1[2]),
+ g0.point(v0[0]),
+ g0.point(v0[1]),
+ gdim);
+ }
+
+ if (d0 == 2 && d1 == 1)
+ {
+ return collides_triangle_segment(g0.point(v0[0]),
+ g0.point(v0[1]),
+ g0.point(v0[2]),
+ g1.point(v1[0]),
+ g1.point(v1[1]),
+ gdim);
+ }
+
+ if (d0 == 2 && d1 == 2)
+ {
+ return collides_triangle_triangle(g0.point(v0[0]),
+ g0.point(v0[1]),
+ g0.point(v0[2]),
+ g1.point(v1[0]),
+ g1.point(v1[1]),
+ g1.point(v1[2]),
+ gdim);
+ }
+
+ if (d0 == 2 && d1 == 3)
+ {
+ return collides_tetrahedron_triangle_3d(g1.point(v1[0]),
+ g1.point(v1[1]),
+ g1.point(v1[2]),
+ g1.point(v1[3]),
+ g0.point(v0[0]),
+ g0.point(v0[1]),
+ g0.point(v0[2]));
+ }
+
+ if (d0 == 3 && d1 == 2)
+ {
+ return collides_tetrahedron_triangle_3d(g0.point(v0[0]),
+ g0.point(v0[1]),
+ g0.point(v0[2]),
+ g0.point(v0[3]),
+ g1.point(v1[0]),
+ g1.point(v1[1]),
+ g1.point(v1[2]));
+ }
+
+ if (d0 == 3 && d1 == 3)
+ {
+ return collides_tetrahedron_tetrahedron_3d(g0.point(v0[0]),
+ g0.point(v0[1]),
+ g0.point(v0[2]),
+ g0.point(v0[3]),
+ g1.point(v1[0]),
+ g1.point(v1[1]),
+ g1.point(v1[2]),
+ g1.point(v1[3]));
+ }
+
+ dolfin_error("CollisionPredicates.cpp",
+ "compute entity-entity collision",
+ "Not implemented for topological dimensions %d / %d and geometrical dimension %d", d0, d1, gdim);
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+// Low-level collision detection predicates
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_point(const Point& p0,
+ const Point& p1,
+ const Point& point,
+ std::size_t gdim)
+{
+ switch (gdim)
+ {
+ case 1:
+ return collides_segment_point_1d(p0[0], p1[0], point[0]);
+ case 2:
+ return collides_segment_point_2d(p0, p1, point);
+ case 3:
+ return collides_segment_point_3d(p0, p1, point);
+ default:
+ dolfin_error("CollisionPredicates.cpp",
+ "call collides_segment_point",
+ "Unknown dimension (only implemented for dimension 2 and 3");
+ }
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_segment(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1,
+ std::size_t gdim)
+{
+ switch (gdim)
+ {
+ case 1:
+ return collides_segment_segment_1d(p0[0], p1[0], q0[0], q1[0]);
+ case 2:
+ return collides_segment_segment_2d(p0, p1, q0, q1);
+ case 3:
+ return collides_segment_segment_3d(p0, p1, q0, q1);
+ default:
+ dolfin_error("CollisionPredicates.cpp",
+ "compute segment-segment collision ",
+ "Unknown dimension (Implemented for dimension 1, 2 and 3)");
+ }
+ return false;
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_point(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point,
+ std::size_t gdim)
+{
+ switch (gdim)
+ {
+ case 2:
+ return collides_triangle_point_2d(p0, p1, p2, point);
+ case 3:
+ return collides_triangle_point_3d(p0, p1, p2, point);
+ default:
+ dolfin_error("CollisionPredicates.cpp",
+ "compute triangle-point collision ",
+ "Implemented only for dimension 2 and 3.");
+ }
+ return false;
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_segment(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ std::size_t gdim)
+{
+ switch (gdim)
+ {
+ case 2:
+ return collides_triangle_segment_2d(p0, p1, p2, q0, q1);
+ case 3:
+ return collides_triangle_segment_3d(p0, p1, p2, q0, q1);
+ default:
+ dolfin_error("CollisionPredicates.cpp",
+ "compute triangle-segment collision ",
+ "Implmented only for dimension 2 and 3.");
+ }
+ return false;
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_triangle(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ std::size_t gdim)
+{
+ switch (gdim)
+ {
+ case 2:
+ return collides_triangle_triangle_2d(p0, p1, p2, q0, q1, q2);
+ case 3:
+ return collides_triangle_triangle_3d(p0, p1, p2, q0, q1, q2);
+ default:
+ dolfin_error("CollisionPredicates.cpp",
+ "compute triangle-triangle collision ",
+ "Implmented only for dimension 2 and 3.");
+ }
+ return false;
+}
+
+
+
+ //--- Low-level collision detection predicates ---
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_point_1d(double p0,
+ double p1,
+ double point)
+{
+ // FIXME: Skip CGAL for now
+ return _collides_segment_point_1d(p0, p1, point);
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& point)
+{
+ return CHECK_CGAL(_collides_segment_point_2d(p0, p1, point),
+ cgal_collides_segment_point_2d(p0, p1, point));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& point)
+{
+ return CHECK_CGAL(_collides_segment_point_3d(p0, p1, point),
+ cgal_collides_segment_point_3d(p0, p1, point));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_segment_1d(double p0,
+ double p1,
+ double q0,
+ double q1)
+{
+ return _collides_segment_segment_1d(p0, p1, q0, q1);
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+{
+ return CHECK_CGAL(_collides_segment_segment_2d(p0, p1, q0, q1),
+ cgal_collides_segment_segment_2d(p0, p1, q0, q1));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+{
+ return CHECK_CGAL(_collides_segment_segment_3d(p0, p1, q0, q1),
+ cgal_collides_segment_segment_3d(p0, p1, q0, q1));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point)
+{
+ return CHECK_CGAL(_collides_triangle_point_2d(p0, p1, p2, point),
+ cgal_collides_triangle_point_2d(p0, p1, p2, point));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point)
+{
+ return CHECK_CGAL(_collides_triangle_point_3d(p0, p1, p2, point),
+ cgal_collides_triangle_point_3d(p0, p1, p2, point));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+{
+ return CHECK_CGAL(_collides_triangle_segment_2d(p0, p1, p2, q0, q1),
+ cgal_collides_triangle_segment_2d(p0, p1, p2, q0, q1));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+{
+ return CHECK_CGAL(_collides_triangle_segment_3d(p0, p1, p2, q0, q1),
+ cgal_collides_triangle_segment_3d(p0, p1, p2, q0, q1));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ return CHECK_CGAL(_collides_triangle_triangle_2d(p0, p1, p2, q0, q1, q2),
+ cgal_collides_triangle_triangle_2d(p0, p1, p2, q0, q1, q2));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ return CHECK_CGAL(_collides_triangle_triangle_3d(p0, p1, p2, q0, q1, q2),
+ cgal_collides_triangle_triangle_3d(p0, p1, p2, q0, q1, q2));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& point)
+{
+ return CHECK_CGAL(_collides_tetrahedron_point_3d(p0, p1, p2, p3, point),
+ cgal_collides_tetrahedron_point_3d(p0, p1, p2, p3, point));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1)
+{
+ return CHECK_CGAL(_collides_tetrahedron_segment_3d(p0, p1, p2, p3, q0, q1),
+ cgal_collides_tetrahedron_segment_3d(p0, p1, p2, p3, q0, q1));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ return CHECK_CGAL(_collides_tetrahedron_triangle_3d(p0, p1, p2, p3, q0, q1, q2),
+ cgal_collides_tetrahedron_triangle_3d(p0, p1, p2, p3, q0, q1, q2));
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::collides_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3)
+{
+ return CHECK_CGAL(_collides_tetrahedron_tetrahedron_3d(p0, p1, p2, p3, q0, q1, q2, q3),
+ cgal_collides_tetrahedron_tetrahedron_3d(p0, p1, p2, p3, q0, q1, q2, q3));
+}
+//-----------------------------------------------------------------------------
+// Implementation of private members
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_segment_point_1d(double p0,
+ double p1,
+ double point)
+{
+ if (p0 > p1)
+ std::swap(p0, p1);
+ return p0 <= point and point <= p1;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_segment_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& point)
+{
+ const double orientation = orient2d(p0, p1, point);
+
+ const Point dp = p1 - p0;
+ const double segment_length = dp.squared_norm();
+
+ return orientation == 0.0 &&
+ (point-p0).squared_norm() <= segment_length &&
+ (point-p1).squared_norm() <= segment_length &&
+ dp.dot(p1-point) >= 0.0 && dp.dot(point-p0) >= 0.0;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_segment_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& point)
+{
+
+ if (point == p0 or point == p1)
+ return true;
+
+ // Poject to reduce to three 2d problems
+ const double det_xy = orient2d(p0, p1, point);
+
+ if (det_xy == 0.0)
+ {
+ const std::array<std::array<double, 2>, 3> xz = {{ {{p0.x(), p0.z()}},
+ {{p1.x(), p1.z()}},
+ {{point.x(), point.z()}} }};
+ const double det_xz = _orient2d(xz[0].data(),
+ xz[1].data(),
+ xz[2].data());
+
+ if (det_xz == 0.0)
+ {
+ const std::array<std::array<double, 2>, 3> yz = {{ {{p0.y(), p0.z()}},
+ {{p1.y(), p1.z()}},
+ {{point.y(), point.z()}} }};
+ const double det_yz = _orient2d(yz[0].data(),
+ yz[1].data(),
+ yz[2].data());
+
+ if (det_yz == 0.0)
+ {
+ // Point is aligned with segment
+ const double length = (p0 - p1).squared_norm();
+ return (point-p0).squared_norm() <= length and
+ (point-p1).squared_norm() <= length;
+ }
+ }
+ }
+
+ return false;
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::_collides_segment_segment_1d(double p0,
+ double p1,
+ double q0,
+ double q1)
+{
+ // Get range
+ const double a0 = std::min(p0, p1);
+ const double b0 = std::max(p0, p1);
+ const double a1 = std::min(q0, q1);
+ const double b1 = std::max(q0, q1);
+
+ // Check for collision
+ const double dx = std::min(b0 - a0, b1 - a1);
+ return b1 >= a0 - dx && a1 <= b0 + dx;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient2d
+
+ if (collides_segment_point_2d(p0, p1, q0))
+ return true;
+ if (collides_segment_point_2d(p0, p1, q1))
+ return true;
+ if (collides_segment_point_2d(q0, q1, p0))
+ return true;
+ if (collides_segment_point_2d(q0, q1, p1))
+ return true;
+
+ // Points must be on different sides
+ if (((orient2d(q0, q1, p0) > 0.0) xor (orient2d(q0, q1, p1) > 0.0)) and
+ ((orient2d(p0, p1, q0) > 0.0) xor (orient2d(p0, p1, q1) > 0.0)))
+ return true;
+ else
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+{
+ // Vertex collisions
+ if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1)
+ return true;
+
+ if (collides_segment_point_3d(p0, p1, q0) or
+ collides_segment_point_3d(p0, p1, q1) or
+ collides_segment_point_3d(q0, q1, p0) or
+ collides_segment_point_3d(q0, q1, p1))
+ {
+ return true;
+ }
+
+ // Determinant must be zero
+ const double det = orient3d(p0, p1, q0, q1);
+
+ if (det < 0.0 or det > 0.0)
+ return false;
+
+ // Now we know that the segments are in the same plane. This means
+ // that they can be parallel, or even collinear.
+
+ // Check for collinearity
+ const Point u = GeometryTools::cross_product(p0, p1, q0);
+ if (u[0] == 0.0 and u[1] == 0.0 and u[2] == 0.0)
+ {
+ const Point v = GeometryTools::cross_product(p0, p1, q1);
+ if (v[0] == 0.0 and v[1] == 0.0 and v[2] == 0.0)
+ {
+ // Now we know that the segments are collinear
+ if ((p0-q0).squared_norm() <= (q1-q0).squared_norm() and
+ (p0-q1).squared_norm() <= (q0-q1).squared_norm())
+ return true;
+
+ if ((p1-q0).squared_norm() <= (q1-q0).squared_norm() and
+ (p1-q1).squared_norm() <= (q0-q1).squared_norm())
+ return true;
+
+ if ((q0-p0).squared_norm() <= (p1-p0).squared_norm() and
+ (q0-p1).squared_norm() <= (p0-p1).squared_norm())
+ return true;
+
+ if ((q1-p0).squared_norm() <= (p1-p0).squared_norm() and
+ (q1-p1).squared_norm() <= (p0-p1).squared_norm())
+ return true;
+ }
+ }
+
+ // Segments are not collinear, but in the same plane
+ // Try to reduce to 2d by elimination
+
+ for (std::size_t d = 0; d < 3; ++d)
+ {
+ if (p0[d] == p1[d] and p0[d] == q0[d] and p0[d] == q1[d])
+ {
+ const std::array<std::array<std::size_t, 2>, 3> dims = {{ {{1, 2}},
+ {{0, 2}},
+ {{0, 1}} }};
+ const Point p0_2d(p0[dims[d][0]], p0[dims[d][1]]);
+ const Point p1_2d(p1[dims[d][0]], p1[dims[d][1]]);
+ const Point q0_2d(q0[dims[d][0]], q0[dims[d][1]]);
+ const Point q1_2d(q1[dims[d][0]], q1[dims[d][1]]);
+
+ return collides_segment_segment_2d(p0_2d, p1_2d, q0_2d, q1_2d);
+ }
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point)
+{
+ const double ref = orient2d(p0, p1, p2);
+
+ if (ref > 0.0)
+ {
+ return (orient2d(p1, p2, point) >= 0.0 and
+ orient2d(p2, p0, point) >= 0.0 and
+ orient2d(p0, p1, point) >= 0.0);
+ }
+ else if (ref < 0.0)
+ {
+ return (orient2d(p1, p2, point) <= 0.0 and
+ orient2d(p2, p0, point) <= 0.0 and
+ orient2d(p0, p1, point) <= 0.0);
+ }
+ else
+ {
+ return ((orient2d(p0, p1, point) == 0.0 and
+ collides_segment_point_1d(p0[0], p1[0], point[0]) and
+ collides_segment_point_1d(p0[1], p1[1], point[1])) or
+ (orient2d(p1, p2, point) == 0.0 and
+ collides_segment_point_1d(p1[0], p2[0], point[0]) and
+ collides_segment_point_1d(p1[1], p2[1], point[1])) or
+ (orient2d(p2, p0, point) == 0.0 and
+ collides_segment_point_1d(p2[0], p0[0], point[0]) and
+ collides_segment_point_1d(p2[1], p0[1], point[1])));
+ }
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point)
+{
+ if (p0 == point or p1 == point or p2 == point)
+ return true;
+
+ const double tet_det = orient3d(p0, p1, p2, point);
+
+ if (tet_det < 0.0 or tet_det > 0.0)
+ return false;
+
+ // Use normal
+ const Point n = GeometryTools::cross_product(p0, p1, p2);
+
+ return !(n.dot(GeometryTools::cross_product(point, p0, p1)) < 0.0 or
+ n.dot(GeometryTools::cross_product(point, p2, p0)) < 0.0 or
+ n.dot(GeometryTools::cross_product(point, p1, p2)) < 0.0);
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient2d
+
+ // Check if end points are in triangle
+ if (collides_triangle_point_2d(p0, p1, p2, q0))
+ return true;
+ if (collides_triangle_point_2d(p0, p1, p2, q1))
+ return true;
+
+ // Check if any of the triangle edges are cut by the segment
+ if (collides_segment_segment_2d(p0, p1, q0, q1))
+ return true;
+ if (collides_segment_segment_2d(p0, p2, q0, q1))
+ return true;
+ if (collides_segment_segment_2d(p1, p2, q0, q1))
+ return true;
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_triangle_segment_3d(const Point& r,
+ const Point& s,
+ const Point& t,
+ const Point& a,
+ const Point& b)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient3d
+
+ // Compute correspondic tetrahedra determinants
+ const double rsta = orient3d(r, s, t, a);
+ const double rstb = orient3d(r, s, t, b);
+
+ // Check if a and b are on same side of triangle rst
+ if ((rsta < 0.0 and rstb < 0.0) or
+ (rsta > 0.0 and rstb > 0.0))
+ return false;
+
+ // We check triangle point first. We use this below.
+ if (collides_triangle_point_3d(r, s, t, a))
+ return true;
+
+ if (collides_triangle_point_3d(r, s, t, b))
+ return true;
+
+ // Now we know a and b are either on different sides or in the same
+ // plane (in which case rsta = rstb = 0). Check if intersection is
+ // in triangle by creating some other tets.
+
+ if (rsta == 0.0 and rstb == 0.0)
+ {
+ // Since we have checked that the points does not collide, the
+ // segment is either completely outside the triangle, or we have a
+ // collision over edges.
+
+ // FIXME: To avoid collision over edges, maybe we can test if both
+ // a and b are on the same side of one of the edges rs, rt or st.
+
+ if (collides_segment_segment_3d(r, s, a, b))
+ return true;
+ if (collides_segment_segment_3d(r, t, a, b))
+ return true;
+ if (collides_segment_segment_3d(s, t, a, b))
+ return true;
+
+ return false;
+ }
+ else
+ {
+ // Temporarily flip a and b to make sure a is above
+ Point _a = a;
+ Point _b = b;
+ if (rsta < 0.0)
+ std::swap(_a, _b);
+
+ const double rasb = orient3d(r, _a, s, _b);
+ if (rasb < 0)
+ return false;
+
+ const double satb = orient3d(s, _a, t, _b);
+ if (satb < 0)
+ return false;
+
+ const double tarb = orient3d(t, _a, r, _b);
+ if (tarb < 0)
+ return false;
+ }
+
+ return true;
+}
+//------------------------------------------------------------------------------
+bool CollisionPredicates::_collides_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient2d
+
+ // Pack points as vectors
+ const std::array<Point, 3> tri_0 = {{p0, p1, p2}};
+ const std::array<Point, 3> tri_1 = {{q0, q1, q2}};
+
+ const bool s0 = std::signbit(orient2d(p0, p1, p2));
+ const bool s1 = std::signbit(orient2d(q0, q1, q2));
+
+ for (std::size_t i = 0; i < 3; ++i)
+ {
+ if ((s0 and
+ orient2d(tri_0[0], tri_0[1], tri_1[i]) <= 0.0 and
+ orient2d(tri_0[1], tri_0[2], tri_1[i]) <= 0.0 and
+ orient2d(tri_0[2], tri_0[0], tri_1[i]) <= 0.0)
+ or
+ (!s0 and
+ orient2d(tri_0[0], tri_0[1], tri_1[i]) >= 0.0 and
+ orient2d(tri_0[1], tri_0[2], tri_1[i]) >= 0.0 and
+ orient2d(tri_0[2], tri_0[0], tri_1[i]) >= 0.0))
+ {
+ return true;
+ }
+
+ if ((s1 and
+ orient2d(tri_1[0], tri_1[1], tri_0[i]) <= 0.0 and
+ orient2d(tri_1[1], tri_1[2], tri_0[i]) <= 0.0 and
+ orient2d(tri_1[2], tri_1[0], tri_0[i]) <= 0.0)
+ or
+ (!s1 and
+ orient2d(tri_1[0], tri_1[1], tri_0[i]) >= 0.0 and
+ orient2d(tri_1[1], tri_1[2], tri_0[i]) >= 0.0 and
+ orient2d(tri_1[2], tri_1[0], tri_0[i]) >= 0.0))
+ {
+ return true;
+ }
+ }
+
+ // Find all edge-edge collisions
+ for (std::size_t i0 = 0; i0 < 3; i0++)
+ {
+ const std::size_t j0 = (i0 + 1) % 3;
+ const Point& p0 = tri_0[i0];
+ const Point& q0 = tri_0[j0];
+ for (std::size_t i1 = 0; i1 < 3; i1++)
+ {
+ const std::size_t j1 = (i1 + 1) % 3;
+ const Point& p1 = tri_1[i1];
+ const Point& q1 = tri_1[j1];
+ if (collides_segment_segment_2d(p0, q0, p1, q1))
+ return true;
+ }
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient3d
+
+ // Pack points as vectors
+ const std::array<Point, 3> tri_0 = {{p0, p1, p2}};
+ const std::array<Point, 3> tri_1 = {{q0, q1, q2}};
+
+ // First test edge-face collisions
+ for (std::size_t i = 0; i < 3; ++i)
+ {
+ const std::size_t j = (i + 1) % 3;
+
+ if (collides_triangle_segment_3d(p0, p1, p2, tri_1[i], tri_1[j]))
+ return true;
+
+ if (collides_triangle_segment_3d(q0, q1, q2, tri_0[i], tri_0[j]))
+ return true;
+ }
+
+ // Test edge-edge collisions
+ for (std::size_t i0 = 0; i0 < 3; i0++)
+ {
+ const std::size_t j0 = (i0 + 1) % 3;
+ for (std::size_t i1 = 0; i1 < 3; i1++)
+ {
+ const std::size_t j1 = (i1 + 1) % 3;
+ if (collides_segment_segment_3d(tri_0[i0], tri_0[j0],
+ tri_1[i1], tri_1[j1]))
+ {
+ return true;
+ }
+ }
+ }
+
+ // FIXME
+ // Test point-face collisions (could also be detected by
+ // triangle_segment collision above)
+ for (std::size_t i = 0; i < 3; ++i)
+ {
+ if (collides_triangle_point_3d(p0, p1, p2, tri_1[i]))
+ return true;
+
+ if (collides_triangle_point_3d(q0, q1, q2, tri_0[i]))
+ return true;
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& point)
+{
+ const double ref = orient3d(p0, p1, p2, p3);
+
+ if (ref > 0.0)
+ {
+ return (orient3d(p0, p1, p2, point) >= 0.0 and
+ orient3d(p0, p3, p1, point) >= 0.0 and
+ orient3d(p0, p2, p3, point) >= 0.0 and
+ orient3d(p1, p3, p2, point) >= 0.0);
+ }
+ else if (ref < 0.0)
+ {
+ return (orient3d(p0, p1, p2, point) <= 0.0 and
+ orient3d(p0, p3, p1, point) <= 0.0 and
+ orient3d(p0, p2, p3, point) <= 0.0 and
+ orient3d(p1, p3, p2, point) <= 0.0);
+ }
+ else
+ {
+ dolfin_error("CollisionPredicates.cpp",
+ "compute tetrahedron point collision",
+ "Not implemented for degenerate tetrahedron");
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient3d
+
+ // Segment vertex in tetrahedron collision
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q0))
+ return true;
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q1))
+ return true;
+
+ // Triangle-segment collision tests
+ if (collides_triangle_segment_3d(p1, p2, p3, q0, q1))
+ return true;
+ if (collides_triangle_segment_3d(p0, p2, p3, q0, q1))
+ return true;
+ if (collides_triangle_segment_3d(p0, p1, p3, q0, q1))
+ return true;
+ if (collides_triangle_segment_3d(p0, p1, p2, q0, q1))
+ return true;
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient3d
+
+ // Triangle vertex in tetrahedron collision
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q0))
+ return true;
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q1))
+ return true;
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q2))
+ return true;
+
+ // Triangle-triangle collision tests
+ if (collides_triangle_triangle_3d(q0, q1, q2, p1, p2, p3))
+ return true;
+ if (collides_triangle_triangle_3d(q0, q1, q2, p0, p2, p3))
+ return true;
+ if (collides_triangle_triangle_3d(q0, q1, q2, p0, p1, p3))
+ return true;
+ if (collides_triangle_triangle_3d(q0, q1, q2, p0, p1, p2))
+ return true;
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool CollisionPredicates::_collides_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3)
+{
+ // FIXME: Optimize by avoiding redundant calls to orient3d
+
+ const std::array<Point, 4> tetp = {{p0, p1, p2, p3}};
+ const std::array<Point, 4> tetq = {{q0, q1, q2, q3}};
+
+ // Triangle face collisions
+ const std::array<std::array<std::size_t, 3>, 4> faces = {{ {{1, 2, 3}},
+ {{0, 2, 3}},
+ {{0, 1, 3}},
+ {{0, 1, 2}} }};
+ for (std::size_t i = 0; i < 4; ++i)
+ {
+ for (std::size_t j = 0; j < 4; ++j)
+ {
+ if (collides_triangle_triangle_3d(tetp[faces[i][0]], tetp[faces[i][1]], tetp[faces[i][2]],
+ tetq[faces[j][0]], tetq[faces[j][1]], tetq[faces[j][2]]))
+ {
+ return true;
+ }
+ }
+ }
+
+ // Vertex in tetrahedron collision
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q0))
+ return true;
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q1))
+ return true;
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q2))
+ return true;
+ if (collides_tetrahedron_point_3d(p0, p1, p2, p3, q3))
+ return true;
+
+ if (collides_tetrahedron_point_3d(q0, q1, q2, q3, p0))
+ return true;
+ if (collides_tetrahedron_point_3d(q0, q1, q2, q3, p1))
+ return true;
+ if (collides_tetrahedron_point_3d(q0, q1, q2, q3, p2))
+ return true;
+ if (collides_tetrahedron_point_3d(q0, q1, q2, q3, p3))
+ return true;
+
+ return false;
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/CollisionPredicates.h b/dolfin/geometry/CollisionPredicates.h
new file mode 100644
index 0000000..f0966ab
--- /dev/null
+++ b/dolfin/geometry/CollisionPredicates.h
@@ -0,0 +1,316 @@
+// Copyright (C) 2014-2016 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2014-02-03
+// Last changed: 2017-09-29
+
+#ifndef __COLLISION_PREDICATES_H
+#define __COLLISION_PREDICATES_H
+
+namespace dolfin
+{
+
+ // Forward declarations
+ class Point;
+ class MeshEntity;
+
+ /// This class implements algorithms for detecting pairwise
+ /// collisions between mesh entities of varying dimensions.
+
+ class CollisionPredicates
+ {
+ public:
+
+ //--- High-level collision detection predicates ---
+
+ /// Check whether entity collides with point.
+ ///
+ /// *Arguments*
+ /// entity (_MeshEntity_)
+ /// The entity.
+ /// point (_Point_)
+ /// The point.
+ ///
+ /// *Returns*
+ /// bool
+ /// True iff entity collides with cell.
+ static bool collides(const MeshEntity& entity,
+ const Point& point);
+
+ /// Check whether two entities collide.
+ ///
+ /// *Arguments*
+ /// entity_0 (_MeshEntity_)
+ /// The first entity.
+ /// entity_1 (_MeshEntity_)
+ /// The second entity.
+ ///
+ /// *Returns*
+ /// bool
+ /// True iff entity collides with cell.
+ static bool collides(const MeshEntity& entity_0,
+ const MeshEntity& entity_1);
+
+ //--- Low-level collision detection predicates ---
+
+ /// Check whether segment p0-p1 collides with point
+ static bool collides_segment_point(const Point& p0,
+ const Point& p1,
+ const Point& point,
+ std::size_t gdim);
+
+ /// Check whether segment p0-p1 collides with point (1D version)
+ static bool collides_segment_point_1d(double p0,
+ double p1,
+ double point);
+
+ /// Check whether segment p0-p1 collides with point (2D version)
+ static bool collides_segment_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& point);
+
+ /// Check whether segment p0-p1 collides with point (3D version)
+ static bool collides_segment_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& point);
+
+ /// Check whether segment p0-p1 collides with segment q0-q1
+ static bool collides_segment_segment(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1,
+ std::size_t gdim);
+
+ /// Check whether segment p0-p1 collides with segment q0-q1 (1D version)
+ static bool collides_segment_segment_1d(double p0,
+ double p1,
+ double q0,
+ double q1);
+
+ /// Check whether segment p0-p1 collides with segment q0-q1 (2D version)
+ static bool collides_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1);
+
+ /// Check whether segment p0-p1 collides with segment q0-q1 (3D version)
+ static bool collides_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1);
+
+ /// Check whether triangle p0-p1-p2 collides with point
+ static bool collides_triangle_point(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point,
+ std::size_t gdim);
+
+ /// Check whether triangle p0-p1-p2 collides with point (2D version)
+ static bool collides_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point);
+
+ /// Check whether triangle p0-p1-p2 collides with point (3D version)
+ static bool collides_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point);
+
+ /// Check whether triangle p0-p1-p2 collides with segment q0-q1
+ static bool collides_triangle_segment(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ std::size_t gdim);
+
+ /// Check whether triangle p0-p1-p2 collides with segment q0-q1 (2D version)
+ static bool collides_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ /// Check whether triangle p0-p1-p2 collides with segment q0-q1 (3D version)
+ static bool collides_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ /// Check whether triangle p0-p1-p2 collides with triangle q0-q1-q2
+ static bool collides_triangle_triangle(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ std::size_t gdim);
+
+ /// Check whether triangle p0-p1-p2 collides with triangle q0-q1-q2 (2D version)
+ static bool collides_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ /// Check whether triangle p0-p1-p2 collides with triangle q0-q1-q2 (3D version)
+ static bool collides_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ /// Check whether tetrahedron p0-p1-p2-p3 collides with point
+ static bool collides_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& point);
+
+ /// Check whether tetrahedron p0-p1-p2-p3 collides with segment q0-q1
+ static bool collides_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1);
+
+ /// Check whether tetrahedron p0-p1-p2-p3 collides with triangle q0-q1-q2
+ static bool collides_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ /// Check whether tetrahedron p0-p1-p2-p3 collides with tetrahedron q0-q1-q2
+ static bool collides_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3);
+
+ private:
+
+ // Implementation of collision detection predicates
+
+ static bool _collides_segment_point_1d(double p0,
+ double p1,
+ double point);
+
+ static bool _collides_segment_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& point);
+
+ static bool _collides_segment_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& point);
+
+ static bool _collides_segment_segment_1d(double p0,
+ double p1,
+ double q0,
+ double q1);
+
+ static bool _collides_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1);
+
+ static bool _collides_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1);
+
+ static bool _collides_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point);
+
+ static bool _collides_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& point);
+
+ static bool _collides_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ static bool _collides_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ static bool _collides_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ static bool _collides_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ static bool _collides_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& point);
+
+ static bool _collides_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1);
+
+ static bool _collides_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ static bool _collides_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3);
+ };
+
+}
+
+#endif
diff --git a/dolfin/geometry/ConvexTriangulation.cpp b/dolfin/geometry/ConvexTriangulation.cpp
new file mode 100644
index 0000000..6445a61
--- /dev/null
+++ b/dolfin/geometry/ConvexTriangulation.cpp
@@ -0,0 +1,603 @@
+// Copyright (C) 2016-2017 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-06-01
+// Last changed: 2017-10-09
+
+#include <algorithm>
+#include <tuple>
+#include <set>
+#include "predicates.h"
+#include "GeometryPredicates.h"
+#include "GeometryTools.h"
+#include "CollisionPredicates.h"
+#include "IntersectionConstruction.h"
+#include "ConvexTriangulation.h"
+
+#include "CGALExactArithmetic.h"
+
+using namespace dolfin;
+
+//-----------------------------------------------------------------------------
+namespace
+{
+ //-----------------------------------------------------------------------------
+ // Create a unique list of points in the sense that |p-q| > tol in each dimension
+ std::vector<Point>
+ unique_points(const std::vector<Point>& input_points,
+ std::size_t gdim,
+ double tol)
+ {
+ std::vector<Point> points;
+
+ for (std::size_t i = 0; i < input_points.size(); ++i)
+ {
+ bool unique = true;
+ for (std::size_t j = i+1; j < input_points.size(); ++j)
+ {
+ std::size_t cnt = 0;
+ for (std::size_t d = 0; d < gdim; ++d)
+ {
+ if (std::abs(input_points[i][d] - input_points[j][d]) > tol)
+ {
+ cnt++;
+ }
+ }
+ if (cnt == 0)
+ {
+ unique = false;
+ break;
+ }
+ }
+
+ if (unique)
+ points.push_back(input_points[i]);
+ }
+
+ return points;
+ }
+ //------------------------------------------------------------------------------
+ // Check if q lies between p0 and p1. p0, p1 and q are assumed to be colinear
+ bool is_between(Point p0, Point p1, Point q)
+ {
+ const double sqnorm = (p1-p0).squared_norm();
+ return (p0-q).squared_norm() < sqnorm && (p1-q).squared_norm() < sqnorm;
+ }
+ //------------------------------------------------------------------------------
+ // Return the indices to the points that forms the polygon that is
+ // the convex hull of the points. The points are assumed to be coplanar
+ std::vector<std::pair<std::size_t, std::size_t>>
+ compute_convex_hull_planar(const std::vector<Point>& points)
+ {
+ // FIXME: Ensure that 0, 1, 2 are not colinear
+ Point normal = GeometryTools::cross_product(points[0], points[1], points[2]);
+ normal /= normal.norm();
+
+ std::vector<std::pair<std::size_t, std::size_t>> edges;
+
+ // Filter out points which are in the interior of the
+ // convex hull of the planar points.
+ for (std::size_t i = 0; i < points.size(); i++)
+ {
+ for (std::size_t j = i+1; j < points.size(); j++)
+ {
+ // Form at plane of i, j and i + the normal of plane
+ const Point r = points[i]+normal;
+
+ // search for the first point which is not in the
+ // i, j, p plane to determine sign of orietation
+ double edge_orientation = 0;
+ {
+ std::size_t a = 0;
+ while (edge_orientation == 0)
+ {
+ if (a != i && a != j)
+ {
+ edge_orientation = orient3d(points[i],
+ points[j],
+ r,
+ points[a]);
+ }
+ a++;
+ }
+ }
+
+ bool on_convex_hull = true;
+ std::vector<std::size_t> colinear;
+ for (std::size_t p = 0; p < points.size(); p++)
+ {
+ if (p != i && p != j)
+ {
+ const double orientation = orient3d(points[i],
+ points[j],
+ r,
+ points[p]);
+
+ if (orientation == 0)
+ {
+ colinear.push_back(p);
+ }
+
+ // Sign change: triangle is not on convex hull
+ if (edge_orientation * orientation < 0)
+ {
+ on_convex_hull = false;
+ }
+ }
+ }
+
+ if (on_convex_hull)
+ {
+ if (!colinear.empty())
+ {
+ // Several points are colinear. Only add if i and j are
+ // the 1d convex hull of the colinear points
+ bool is_linear_convex_hull = true;
+ for (std::size_t q : colinear)
+ {
+ if (!is_between(points[i], points[j], points[q]))
+ {
+ is_linear_convex_hull = false;
+ break;
+ }
+ }
+
+ if (is_linear_convex_hull)
+ {
+ edges.push_back(std::make_pair(i, j));
+ }
+ }
+ else
+ {
+ edges.push_back(std::make_pair(i, j));
+ }
+ }
+ }
+ }
+
+ return edges;
+ }
+}
+
+//------------------------------------------------------------------------------
+std::vector<std::vector<Point>>
+ConvexTriangulation::triangulate(const std::vector<Point>& p,
+ std::size_t gdim,
+ std::size_t tdim)
+{
+ if (p.empty())
+ return std::vector<std::vector<Point>>();
+
+ if (tdim == 1)
+ {
+ return triangulate_1d(p, gdim);
+ }
+ else if (tdim == 2 && gdim == 2)
+ {
+ return triangulate_graham_scan_2d(p);
+ }
+ else if (tdim == 3 && gdim == 3)
+ {
+ return triangulate_graham_scan_3d(p);
+ }
+
+ dolfin_error("ConvexTriangulation.cpp",
+ "triangulate convex polyhedron",
+ "Triangulation of polyhedron of topological dimension %u and geometric dimension %u not implemented", tdim, gdim);
+
+ return std::vector<std::vector<Point>>();
+}
+//-----------------------------------------------------------------------------
+std::vector<std::vector<Point>>
+ConvexTriangulation::_triangulate_1d(const std::vector<Point>& p,
+ std::size_t gdim)
+{
+ // A convex polyhedron of topological dimension 1 can not have more
+ // than two points. If more, they must be collinear (more or
+ // less). This can happen due to tolerances in
+ // IntersectionConstruction::intersection_segment_segment_2d.
+
+ if (gdim != 2)
+ {
+ dolfin_error("ConvexTriangulation.cpp",
+ "triangulate topological 1d",
+ "Function is only implemented for gdim = 2");
+ }
+
+ const std::vector<Point> unique_p = unique_points(p, gdim, DOLFIN_EPS);
+
+ if (unique_p.size() > 2)
+ {
+ // Make sure the points are approximately collinear
+ bool collinear = true;
+ for (std::size_t i = 2; i < unique_p.size(); ++i)
+ {
+ const double o = orient2d(unique_p[0], unique_p[1], unique_p[i]);
+ if (std::abs(o) > DOLFIN_EPS_LARGE)
+ {
+ collinear = false;
+ break;
+ }
+ }
+
+ dolfin_assert(collinear);
+
+ // Average
+ Point average(0.0, 0.0, 0.0);
+ for (const Point& q: unique_p)
+ average += q;
+ average /= unique_p.size();
+ std::vector<std::vector<Point>> t = {{ average }};
+ return t;
+
+ dolfin_error("ConvexTriangulation.cpp",
+ "triangulate convex polyhedron",
+ "A convex polyhedron of topological dimension 1 can not have more than 2 points");
+ }
+
+ std::vector<std::vector<Point>> t = { unique_p };
+ return t;
+}
+
+//------------------------------------------------------------------------------
+std::vector<std::vector<Point>>
+ConvexTriangulation::_triangulate_graham_scan_2d(const std::vector<Point>& input_points)
+{
+ dolfin_assert(GeometryPredicates::is_finite(input_points));
+
+ // Make sure the input points are unique
+ const std::size_t gdim = 2;
+ std::vector<Point> points = unique_points(input_points, gdim, DOLFIN_EPS);
+
+ if (points.size() < 3)
+ return std::vector<std::vector<Point>>();
+
+ if (points.size() == 3)
+ {
+ std::vector<std::vector<Point>> triangulation(1, points);
+ return triangulation;
+ }
+
+ // Sometimes we can get an extra point on an edge: a-----c--b. This
+ // point c may cause problems for the graham scan. To avoid this,
+ // use an extra center point. Use this center point and point no 0
+ // as reference for the angle calculation
+ Point pointscenter = points[0];
+ for (std::size_t m = 1; m < points.size(); ++m)
+ pointscenter += points[m];
+ pointscenter /= points.size();
+
+ // Reference
+ const Point ref = points[0] - pointscenter;
+
+ // Calculate and store angles
+ std::vector<std::pair<double, std::size_t>> order;
+ for (std::size_t m = 1; m < points.size(); ++m)
+ {
+ const double A = orient2d(pointscenter, points[0], points[m]);
+ const Point s = points[m] - pointscenter;
+ double alpha = std::atan2(A, s.dot(ref));
+ if (alpha < 0)
+ alpha += 2.0*DOLFIN_PI;
+ order.emplace_back(alpha, m);
+ }
+
+ // Sort angles
+ std::sort(order.begin(), order.end());
+
+ // Tessellate
+ std::vector<std::vector<Point>> triangulation(order.size() - 1);
+
+ for (std::size_t m = 0; m < order.size()-1; ++m)
+ {
+ // FIXME: We could consider only triangles with area > tolerance here.
+ triangulation[m] = {{ points[0],
+ points[order[m].second],
+ points[order[m + 1].second] }};
+ }
+
+ return triangulation;
+}
+//-----------------------------------------------------------------------------
+std::vector<std::vector<Point>>
+ConvexTriangulation::_triangulate_graham_scan_3d(const std::vector<Point>& input_points)
+{
+ dolfin_assert(GeometryPredicates::is_finite(input_points));
+
+ //std::cout << "Input to 3D Graham scan:" << std::endl;
+ //for (auto p : input_points)
+ // std::cout << p << std::endl;
+
+ // Make sure the input points are unique. We assume this has
+ // negligble effect on volume
+ const std::size_t gdim = 3;
+ std::vector<Point> points = unique_points(input_points, gdim, DOLFIN_EPS);
+
+ std::vector<std::vector<Point>> triangulation;
+
+ if (points.size() < 4)
+ {
+ // Empty
+ return triangulation;
+ }
+ else if (points.size() == 4)
+ {
+ // Single tetrahedron
+ triangulation.push_back(points);
+ return triangulation;
+ }
+ else
+ {
+ // Construct tetrahedra using facet points and a center point
+ Point polyhedroncenter(0,0,0);
+ for (const Point& p: points)
+ polyhedroncenter += p;
+ polyhedroncenter /= points.size();
+
+ // FIXME: Better data structure than set?
+ std::set<std::tuple<std::size_t, std::size_t, std::size_t> > checked;
+
+ // Loop over all triplets
+ for (std::size_t i = 0; i < points.size(); ++i)
+ {
+ for (std::size_t j = i+1; j < points.size(); ++j)
+ {
+ for (std::size_t k = j+1; k < points.size(); ++k)
+ {
+ if (checked.emplace(std::make_tuple(i, j, k)).second)
+ {
+ // Test for the special case where i, j, k are collinear
+ {
+ const Point ij = points[j] - points[i];
+ const Point ik = points[k] - points[i];
+ if ( -(std::abs( (ij/ij.norm() ).dot(ik/ik.norm()))-1) < DOLFIN_EPS)
+ continue;
+ }
+
+ // Check whether all other points are on one side of this
+ // (i,j,k) facet, i.e. we're on the convex
+ // hull. Initialize as true for the case of only three
+ // coplanar points.
+ bool on_convex_hull = true;
+
+ // Use orient3d to determine if the plane (i,j,k) is on the
+ // convex hull.
+ std::vector<std::size_t> coplanar = { i, j, k };
+ double previous_orientation;
+ bool first = true;
+
+ for (std::size_t m = 0; m < points.size(); ++m)
+ {
+ if (m != i and m != j and m != k)
+ {
+ const double orientation = orient3d(points[i],
+ points[j],
+ points[k],
+ points[m]);
+ // Save point index if we find coplanar points
+ if (orientation == 0)
+ coplanar.push_back(m);
+ else
+ {
+ if (first)
+ {
+ previous_orientation = orientation;
+ first = false;
+ }
+ else
+ {
+ // Sign change: triangle is not on convex hull
+ if (previous_orientation * orientation < 0)
+ {
+ on_convex_hull = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (on_convex_hull)
+ {
+ if (coplanar.size() == 3)
+ {
+ // Form one tetrahedron
+ std::vector<Point> cand = { points[i],
+ points[j],
+ points[k],
+ polyhedroncenter };
+
+#ifdef DOLFIN_ENABLE_GEOMETRY_DEBUGGING
+ if (cgal_tet_is_degenerate(cand))
+ dolfin_error("ConvexTriangulation.cpp",
+ "triangulation 3d points",
+ "tet is degenerate");
+
+#endif
+
+ // FIXME: Here we could include if determinant is sufficiently large
+ //for (auto p : cand)
+ // std::cout << " " << p;
+ //std::cout << std::endl;
+ triangulation.push_back(cand);
+ }
+ else // At least four coplanar points
+ {
+ // Tessellate as in the triangle-triangle intersection
+ // case: First sort points using a Graham scan, then
+ // connect to form triangles. Finally form tetrahedra
+ // using the center of the polyhedron.
+
+ // Use the center of the coplanar points and point no 0
+ // as reference for the angle calculation
+
+ std::vector<Point> coplanar_points;
+ for (std::size_t i : coplanar)
+ coplanar_points.push_back(points[i]);
+
+ std::vector<std::pair<std::size_t, std::size_t>> coplanar_convex_hull =
+ compute_convex_hull_planar(coplanar_points);
+
+ Point coplanar_center(0,0,0);
+ for (Point p : coplanar_points)
+ coplanar_center += p;
+ coplanar_center /= coplanar_points.size();
+
+ // Tessellate
+ for (const std::pair<std::size_t, std::size_t>& edge : coplanar_convex_hull)
+ {
+ triangulation.push_back({polyhedroncenter,
+ coplanar_center,
+ coplanar_points[edge.first],
+ coplanar_points[edge.second]});
+
+#ifdef DOLFIN_ENABLE_GEOMETRY_DEBUGGING
+ if (cgal_tet_is_degenerate(triangulation.back()))
+ {
+ dolfin_error("ConvexTriangulation.cpp:544",
+ "triangulation 3d points",
+ "tet is degenerate");
+ }
+
+ if (cgal_triangulation_overlap(triangulation))
+ {
+ dolfin_error("ConvexTriangulation.cpp:544",
+ "triangulation 3d points",
+ "now triangulation overlaps");
+ }
+#endif
+ }
+
+ // Mark all combinations of the coplanar vertices as
+ // checked to avoid duplicating triangles
+ std::sort(coplanar.begin(), coplanar.end());
+
+ for (int i = 0; i < (int)coplanar.size()-2; i++)
+ {
+ for (int j = i+1; j < (int)coplanar.size()-1; j++)
+ {
+ for (std::size_t k = j+1; k < coplanar.size(); k++)
+ {
+ checked.emplace( std::make_tuple(coplanar[i], coplanar[j], coplanar[k]) );
+ }
+ }
+ }
+ } // end coplanar.size() > 3
+ } // end on_convexhull
+ }
+ }
+ }
+ }
+
+ return triangulation;
+ }
+}
+//-----------------------------------------------------------------------------
+std::vector<std::vector<Point>>
+ConvexTriangulation::triangulate_graham_scan_3d(const std::vector<Point>& pm)
+{
+ std::vector<std::vector<Point>> triangulation =
+ _triangulate_graham_scan_3d(pm);
+
+#ifdef DOLFIN_ENABLE_GEOMETRY_DEBUGGING
+
+ if (cgal_triangulation_has_degenerate(triangulation))
+ dolfin_error("ConvexTriangulation.cpp",
+ "verify convex triangulation",
+ "triangulation contains degenerate tetrahedron");
+
+ if (cgal_triangulation_overlap(triangulation))
+ {
+ dolfin_error("ConvexTriangulation.cpp",
+ "verify convex triangulation",
+ "tetrahedrons overlap");
+ }
+
+
+ double volume = .0;
+ for (const std::vector<Point>& tet : triangulation)
+ {
+ dolfin_assert(tet.size() == 4);
+ const double tet_volume = std::abs(orient3d(tet[0], tet[1], tet[2], tet[3]))/6.0;
+ volume += tet_volume;
+ }
+
+ const double reference_volume = cgal_polyhedron_volume(pm);
+
+ if (std::abs(volume - reference_volume) > DOLFIN_EPS)
+ dolfin_error("ConvexTriangulation.cpp",
+ "verifying convex triangulation",
+ "computed volume %f, but reference volume is %f",
+ volume, reference_volume);
+
+
+#endif
+ return triangulation;
+}
+//-----------------------------------------------------------------------------
+bool ConvexTriangulation::selfintersects(const std::vector<std::vector<Point>>& p)
+{
+ for (std::size_t i = 0; i < p.size(); i++)
+ {
+ for (std::size_t j = i+1; j < p.size(); j++)
+ {
+ dolfin_assert(p[i].size() == p[j].size());
+ if (p[i].size() == 4)
+ {
+ if (CollisionPredicates::collides_tetrahedron_tetrahedron_3d(p[i][0],
+ p[i][1],
+ p[i][2],
+ p[i][3],
+ p[j][0],
+ p[j][1],
+ p[j][2],
+ p[j][3]))
+ {
+ auto intersection =
+ IntersectionConstruction::intersection_tetrahedron_tetrahedron_3d(p[i][0],
+ p[i][1],
+ p[i][2],
+ p[i][3],
+ p[j][0],
+ p[j][1],
+ p[j][2],
+ p[j][3]);
+ if (intersection.size() > 3)
+ {
+ for (std::size_t k = 3; k < intersection.size(); k++)
+ {
+ // FIXME: Note that this fails if the first three points are colinear!
+ if (orient3d(intersection[0],
+ intersection[1],
+ intersection[2],
+ intersection[k]) != 0)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ else if (p[i].size() == 3)
+ {
+ dolfin_not_implemented();
+ }
+ }
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/ConvexTriangulation.h b/dolfin/geometry/ConvexTriangulation.h
new file mode 100644
index 0000000..44f7a3e
--- /dev/null
+++ b/dolfin/geometry/ConvexTriangulation.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-06-01
+// Last changed: 2017-09-30
+
+#ifndef __CONVEX_TRIANGULATION
+#define __CONVEX_TRIANGULATION
+
+#include <vector>
+#include "Point.h"
+
+namespace dolfin
+{
+
+ /// This class implements algorithms for triangulating convex
+ /// domains represented as a set of points.
+
+ class ConvexTriangulation
+ {
+ public:
+
+ /// Tdim independent wrapper
+ static std::vector<std::vector<Point>>
+ triangulate(const std::vector<Point>& p,
+ std::size_t gdim,
+ std::size_t tdim);
+
+ /// Triangulate 1D
+ static std::vector<std::vector<Point>>
+ triangulate_1d(const std::vector<Point>& pm,
+ std::size_t gdim)
+ {
+ return _triangulate_1d(pm, gdim);
+ }
+
+ /// Triangulate using the Graham scan 2D
+ static std::vector<std::vector<Point>>
+ triangulate_graham_scan_2d(const std::vector<Point>& pm)
+ {
+ return _triangulate_graham_scan_2d(pm);
+ }
+
+ /// Triangulate using the Graham scan 3D
+ static std::vector<std::vector<Point>>
+ triangulate_graham_scan_3d(const std::vector<Point>& pm);
+
+ /// Determine if there are self-intersecting tetrahedra
+ static bool selfintersects(const std::vector<std::vector<Point>>& p);
+
+ private:
+
+ // Implementation declarations
+
+ /// Implementation of 1D triangulation
+ static std::vector<std::vector<Point>>
+ _triangulate_1d(const std::vector<Point>& pm,
+ std::size_t gdim);
+
+ /// Implementation of Graham scan 2D
+ static std::vector<std::vector<Point>>
+ _triangulate_graham_scan_2d(const std::vector<Point>& pm);
+
+ /// Implementation of Graham scan 3D
+ static std::vector<std::vector<Point>>
+ _triangulate_graham_scan_3d(const std::vector<Point>& pm);
+ };
+
+} // end namespace dolfin
+#endif
diff --git a/dolfin/geometry/GenericBoundingBoxTree.cpp b/dolfin/geometry/GenericBoundingBoxTree.cpp
index 3103b1d..cd5c4d5 100644
--- a/dolfin/geometry/GenericBoundingBoxTree.cpp
+++ b/dolfin/geometry/GenericBoundingBoxTree.cpp
@@ -16,7 +16,7 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2013-05-02
-// Last changed: 2014-02-06
+// Last changed: 2016-11-15
// Define a maximum dimension used for a local array in the recursive
// build function. Speeds things up compared to allocating it in each
@@ -125,10 +125,6 @@ void GenericBoundingBoxTree::build(const Mesh& mesh, std::size_t tdim)
info("Computed global bounding box tree with %d boxes.",
_global_tree->num_bboxes());
- // Print on rank 0
- // if(MPI::rank(mesh.mpi_comm()) == 0)
- // std::cout << _global_tree->str() << "\n";
-
}
}
//-----------------------------------------------------------------------------
@@ -274,7 +270,13 @@ GenericBoundingBoxTree::compute_closest_entity(const Point& point,
// Search point cloud to get a good starting guess
dolfin_assert(_point_search_tree);
- double r = _point_search_tree->compute_closest_point(point).second;
+ std::pair<unsigned int, double> guess
+ = _point_search_tree->compute_closest_point(point);
+ double r = guess.second;
+
+ // Return if we have found the point
+ if (r == 0.)
+ return guess;
// Initialize index and distance to closest entity
unsigned int closest_entity = std::numeric_limits<unsigned int>::max();
diff --git a/dolfin/geometry/GeometryDebugging.cpp b/dolfin/geometry/GeometryDebugging.cpp
new file mode 100644
index 0000000..d1c465f
--- /dev/null
+++ b/dolfin/geometry/GeometryDebugging.cpp
@@ -0,0 +1,151 @@
+// Copyright (C) 2016 Anders Logg
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-05-05
+// Last changed: 2017-03-01
+
+#include <sstream>
+#include <dolfin/log/log.h>
+#include <dolfin/log/LogStream.h>
+#include "GeometryDebugging.h"
+
+using namespace dolfin;
+
+// Plotting not initialized
+bool GeometryDebugging::_initialized = false;
+
+//-----------------------------------------------------------------------------
+void GeometryDebugging::print(const Point& point)
+{
+ set_indentation_level(0);
+ cout << "Point: " << point << endl;
+}
+//-----------------------------------------------------------------------------
+void GeometryDebugging::print(const std::vector<Point>& simplex)
+{
+ set_indentation_level(0);
+ cout << "Simplex:";
+ for (const Point p : simplex)
+ cout << " " << p;
+ cout << endl;
+}
+//-----------------------------------------------------------------------------
+void GeometryDebugging::print(const std::vector<Point>& simplex_0,
+ const std::vector<Point>& simplex_1)
+{
+ set_indentation_level(0);
+ cout << "Simplex 0:";
+ for (const Point p : simplex_0)
+ cout << "-" << point2string(p);
+ cout << endl;
+
+ cout << "Simplex 1:";
+ for (const Point p : simplex_1)
+ cout << "-" << point2string(p);
+ cout << endl;
+}
+//-----------------------------------------------------------------------------
+void GeometryDebugging::plot(const Point& point)
+{
+ set_indentation_level(0);
+ init_plot();
+
+ cout << "# Plot point" << endl;
+ cout << "ax.plot(" << simplex2string({point}) << ", 'x')" << endl;
+ cout << endl;
+}
+//-----------------------------------------------------------------------------
+void GeometryDebugging::plot(const std::vector<Point>& simplex)
+{
+ set_indentation_level(0);
+ init_plot();
+
+ cout << "# Plot simplex" << endl;
+ if (simplex.size() >= 3)
+ cout << "ax.plot_trisurf(" << simplex2string(simplex) << ")" << endl;
+ else
+ cout << "ax.plot(" << simplex2string(simplex) << ", marker='x')" << endl;
+ cout << endl;
+}
+//-----------------------------------------------------------------------------
+void GeometryDebugging::plot(const std::vector<Point>& simplex_0,
+ const std::vector<Point>& simplex_1)
+{
+ set_indentation_level(0);
+ init_plot();
+
+ cout << "# Plot simplex intersection" << endl;
+ if (simplex_0.size() >= 3)
+ cout << "ax.plot_trisurf(" << simplex2string(simplex_0) << ", color='r')" << endl;
+ else
+ cout << "ax.plot(" << simplex2string(simplex_0) << ", marker='o', color='r')" << endl;
+ if (simplex_1.size() >= 3)
+ cout << "ax.plot_trisurf(" << simplex2string(simplex_1) << ", color='b')" << endl;
+ else
+ cout << "ax.plot(" << simplex2string(simplex_1) << ", marker='o', color='b')" << endl;
+ cout << endl;
+}
+//-----------------------------------------------------------------------------
+void GeometryDebugging::init_plot()
+{
+ if (_initialized)
+ return;
+
+ set_indentation_level(0);
+ cout << "# Initialize matplotlib 3D plotting" << endl;
+ cout << "from mpl_toolkits.mplot3d import Axes3D" << endl;
+ cout << "import matplotlib.pyplot as pl" << endl;
+ cout << "ax = pl.figure().gca(projection='3d')" << endl;
+ cout << "pl.ion(); pl.show()" << endl;
+ cout << endl;
+ cout << "# Note 1: Rotate/interact with figure to update plot." << endl;
+ cout << "# Note 2: Use pl.cla() to clear figure between plots." << endl;
+ cout << endl;
+
+ _initialized = true;
+}
+//-----------------------------------------------------------------------------
+std::string GeometryDebugging::point2string(const Point& p)
+{
+ std::stringstream s;
+ s << "(" << p.x() << "," << p.y() << "," << p.z() << ")";
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::string GeometryDebugging::simplex2string(const std::vector<Point>& simplex)
+{
+ std::size_t n = simplex.size();
+ if (n == 0) return "";
+ std::stringstream s;
+ s << "[";
+ for (std::size_t i = 0; i < n - 1; i++)
+ s << simplex[i].x() << ",";
+ s << simplex[n - 1].x() << "]";
+ s << ",";
+ s << "[";
+ for (std::size_t i = 0; i < n - 1; i++)
+ s << simplex[i].y() << ",";
+ s << simplex[n - 1].y() << "]";
+ s << ",";
+ s << "[";
+ for (std::size_t i = 0; i < n - 1; i++)
+ s << simplex[i].z() << ",";
+ s << simplex[n - 1].z() << "]";
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/GeometryDebugging.h b/dolfin/geometry/GeometryDebugging.h
new file mode 100644
index 0000000..bc8d287
--- /dev/null
+++ b/dolfin/geometry/GeometryDebugging.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2016-2017 Anders Logg
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-05-05
+// Last changed: 2017-05-17
+
+#ifndef __GEOMETRY_DEBUGGING_H
+#define __GEOMETRY_DEBUGGING_H
+
+#include <vector>
+#include <string>
+#include "Point.h"
+
+namespace dolfin
+{
+
+ /// This class provides useful functionality for debugging algorithms
+ /// dealing with geometry such as collision detection and intersection
+ /// triangulation.
+
+ class GeometryDebugging
+ {
+ public:
+
+ /// Print coordinates of a point.
+ /// Example usage: print(p0)
+ static void print(const Point& point);
+
+ /// Print coordinates of a simplex.
+ /// Example usage: print({p0, p1, p2})
+ static void print(const std::vector<Point>& simplex);
+
+ /// Print coordinates of a pair of simplices.
+ /// Example usage: print({p0, p1, p2}, {q0, q1})
+ static void print(const std::vector<Point>& simplex_0,
+ const std::vector<Point>& simplex_1);
+
+ /// Plot a point (print matplotlib code).
+ /// Example usage: plot(p0)
+ static void plot(const Point& point);
+
+ /// Plot a simplex (print matplotlib code).
+ /// Example usage: plot({p0, p1, p2})
+ static void plot(const std::vector<Point>& simplex);
+
+ /// Plot a pair of simplices (print matplotlib code).
+ /// Example usage: plot({p0, p1, p2}, {q0, q1})
+ static void plot(const std::vector<Point>& simplex_0,
+ const std::vector<Point>& simplex_1);
+
+ /// Initialize plotting (print matplotlib code).
+ static void init_plot();
+
+ /// Compact point to string conversion
+ static std::string point2string(const Point& p);
+
+ /// Compact simplex to string conversion
+ static std::string simplex2string(const std::vector<Point>& simplex);
+
+ private:
+
+ // Check whether plotting has been initialized
+ static bool _initialized;
+
+ };
+
+}
+
+#endif
diff --git a/dolfin/geometry/GeometryPredicates.cpp b/dolfin/geometry/GeometryPredicates.cpp
new file mode 100644
index 0000000..ea8a64e
--- /dev/null
+++ b/dolfin/geometry/GeometryPredicates.cpp
@@ -0,0 +1,214 @@
+// Copyright (C) 2016-2017 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-11-21
+// Last changed: 2017-10-09
+
+#include <cmath>
+#include "CGALExactArithmetic.h"
+#include "predicates.h"
+#include "GeometryPredicates.h"
+
+using namespace dolfin;
+
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::is_degenerate(const std::vector<Point>& simplex,
+ std::size_t gdim)
+{
+ switch (gdim)
+ {
+ case 2:
+ return is_degenerate_2d(simplex);
+ case 3:
+ return is_degenerate_3d(simplex);
+ default:
+ dolfin_error("GeometryPredicates.cpp",
+ "is_degenerate",
+ "Unkonwn dimension (only implemented for dimension 2 and 3");
+ }
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::_is_degenerate_2d(const std::vector<Point>& simplex)
+{
+ if (simplex.size() < 2 or simplex.size() > 3)
+ {
+ info("Degenerate 2D simplex with %d vertices.", simplex.size());
+ return true;
+ }
+
+ switch (simplex.size())
+ {
+ case 2:
+ return simplex[0] == simplex[1];
+ case 3:
+ return orient2d(simplex[0], simplex[1], simplex[2]) == 0.0;
+ }
+
+ // Shouldn't get here
+ dolfin_error("GeometryPredicates.h",
+ "call _is_degenerate_2d",
+ "Only implemented for simplices of tdim 0, 1 and 2, not tdim = %d",
+ simplex.size() - 1);
+
+ return true;
+}
+//------------------------------------------------------------------------------
+bool GeometryPredicates::_is_degenerate_3d(const std::vector<Point>& simplex)
+{
+ if (simplex.size() < 2 or simplex.size() > 4)
+ {
+ info("Degenerate 3D simplex with %d vertices.", simplex.size());
+ return true;
+ }
+
+ switch (simplex.size())
+ {
+ case 2:
+ return simplex[0] == simplex[1];
+ case 3:
+ {
+ const double ayz[2] = {simplex[0].y(), simplex[0].z()};
+ const double byz[2] = {simplex[1].y(), simplex[1].z()};
+ const double cyz[2] = {simplex[2].y(), simplex[2].z()};
+ if (_orient2d(ayz, byz, cyz) != 0.0)
+ return false;
+
+ const double azx[2] = {simplex[0].z(), simplex[0].x()};
+ const double bzx[2] = {simplex[1].z(), simplex[1].x()};
+ const double czx[2] = {simplex[2].z(), simplex[2].x()};
+ if (_orient2d(azx, bzx, czx) != 0.0)
+ return false;
+
+ const double axy[2] = {simplex[0].x(), simplex[0].y()};
+ const double bxy[2] = {simplex[1].x(), simplex[1].y()};
+ const double cxy[2] = {simplex[2].x(), simplex[2].y()};
+ if (_orient2d(axy, bxy, cxy) != 0.0)
+ return false;
+
+ return true;
+ }
+ case 4:
+ return orient3d(simplex[0], simplex[1], simplex[2], simplex[3]) == 0.0;
+ }
+
+ // Shouldn't get here
+ dolfin_error("GeometryPredicates.h",
+ "call _is_degenerate_3d",
+ "Only implemented for simplices of tdim 0, 1, 2 and 3, not tdim = %d",
+ simplex.size() - 1);
+
+ return true;
+}
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::is_finite(const std::vector<Point>& simplex)
+{
+ for (auto p : simplex)
+ {
+ if (!std::isfinite(p.x()))
+ return false;
+ if (!std::isfinite(p.y()))
+ return false;
+ if (!std::isfinite(p.z()))
+ return false;
+ }
+ return true;
+}
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::is_finite(const std::vector<double>& simplex)
+{
+ for (double p : simplex)
+ {
+ if (!std::isfinite(p))
+ return false;
+ }
+ return true;
+}
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::convex_hull_is_degenerate(const std::vector<Point>& points,
+ std::size_t gdim)
+{
+ // Points are assumed to be unique
+
+ if (points.size() < gdim+1)
+ return true;
+
+ if (gdim == 2)
+ {
+ // FIXME!
+ return false;
+ }
+ else if (gdim == 3)
+ {
+ std::size_t i = 0, j = 1, k = 2;
+ bool found = false;
+
+ // Find three point which are not collinear
+ for (; i < points.size(); i++)
+ {
+ for (j = i+1; j < points.size(); j++)
+ {
+ for (k = j+1; k < points.size(); k++)
+ {
+ const Point ij = points[j] - points[i];
+ const Point ik = points[k] - points[i];
+ if ( -(std::abs( (ij/ij.norm() ).dot(ik/ik.norm()))-1) > DOLFIN_EPS)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (found) break;
+ }
+ if (found)
+ break;
+ }
+
+ // All points are collinear
+ if (!found)
+ return false;
+
+ for (std::size_t l = 0; l < points.size(); l++)
+ {
+ if (l == i || l == j || l == k)
+ continue;
+
+ if (orient3d(points[i], points[j], points[k], points[l]) == 0.0)
+ return true;
+ }
+
+ return false;
+ }
+
+ dolfin_error("GeometryPredicates.h",
+ "call convex_hull_is_degenerate",
+ "Only fully implemented for gdim == 3, not gdim = %d", gdim);
+ return false;
+}
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::is_degenerate_2d(const std::vector<Point>& simplex)
+{
+ return CHECK_CGAL(_is_degenerate_2d(simplex),
+ cgal_is_degenerate_2d(simplex));
+}
+//-----------------------------------------------------------------------------
+bool GeometryPredicates::is_degenerate_3d(const std::vector<Point>& simplex)
+{
+ return CHECK_CGAL(_is_degenerate_3d(simplex),
+ cgal_is_degenerate_3d(simplex));
+}
+//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/GeometryPredicates.h b/dolfin/geometry/GeometryPredicates.h
new file mode 100644
index 0000000..5e56a0a
--- /dev/null
+++ b/dolfin/geometry/GeometryPredicates.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2016-2017 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2016-11-21
+// Last changed: 2017-09-22
+
+#ifndef __GEOMETRY_PREDICATES_H
+#define __GEOMETRY_PREDICATES_H
+
+#include <vector>
+#include <dolfin/log/LogStream.h>
+#include "Point.h"
+
+namespace dolfin
+{
+
+ /// This class implements geometric predicates, i.e. function that
+ /// return either true or false.
+
+ class GeometryPredicates
+ {
+ public:
+
+ /// Check whether simplex is degenerate
+ static bool is_degenerate(const std::vector<Point>& simplex,
+ std::size_t gdim);
+
+
+ /// Check whether simplex is degenerate (2D version)
+ static bool is_degenerate_2d(const std::vector<Point>& simplex);
+
+ /// Check whether simplex is degenerate (3D version)
+ static bool is_degenerate_3d(const std::vector<Point>& simplex);
+
+
+ /// Check whether simplex is finite (not Inf or NaN)
+ static bool is_finite(const std::vector<Point>& simplex);
+
+ /// Check whether simplex is finite (not Inf or NaN)
+ static bool is_finite(const std::vector<double>& simplex);
+
+ /// Check whether the convex hull is degenerate
+ static bool convex_hull_is_degenerate(const std::vector<Point>& p,
+ std::size_t gdim);
+
+ private:
+
+ // Implementations of is_degenerate
+ static bool _is_degenerate_2d(const std::vector<Point>& simplex);
+ static bool _is_degenerate_3d(const std::vector<Point>& simplex);
+
+ };
+
+}
+
+#endif
diff --git a/dolfin/geometry/GeometryTools.h b/dolfin/geometry/GeometryTools.h
new file mode 100644
index 0000000..94ce5c9
--- /dev/null
+++ b/dolfin/geometry/GeometryTools.h
@@ -0,0 +1,120 @@
+// Copyright (C) 2017 Anders Logg
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2017-02-11
+// Last changed: 2017-02-17
+
+#ifndef __GEOMETRY_TOOLS_H
+#define __GEOMETRY_TOOLS_H
+
+#include "predicates.h"
+#include "Point.h"
+
+namespace dolfin
+{
+
+ /// This class provides useful tools (functions) for computational geometry.
+
+ class GeometryTools
+ {
+ public:
+
+ /// Compute numerically stable cross product (a - c) x (b - c)
+ static inline Point cross_product(const Point& a, const Point& b, const Point& c)
+ {
+ // See Shewchuk Lecture Notes on Geometric Robustness
+ double ayz[2] = {a.y(), a.z()};
+ double byz[2] = {b.y(), b.z()};
+ double cyz[2] = {c.y(), c.z()};
+ double azx[2] = {a.z(), a.x()};
+ double bzx[2] = {b.z(), b.x()};
+ double czx[2] = {c.z(), c.x()};
+ double axy[2] = {a.x(), a.y()};
+ double bxy[2] = {b.x(), b.y()};
+ double cxy[2] = {c.x(), c.y()};
+ return Point (_orient2d(ayz, byz, cyz),
+ _orient2d(azx, bzx, czx),
+ _orient2d(axy, bxy, cxy));
+ }
+
+ /// Compute determinant of 3 x 3 matrix defined by vectors, ab, dc, ec
+ inline double determinant(const Point& ab, const Point& dc, const Point& ec)
+ {
+ const double a = ab.x(), b = ab.y(), c = ab.z();
+ const double d = dc.x(), e = dc.y(), f = dc.z();
+ const double g = ec.x(), h = ec.y(), i = ec.z();
+ return a * (e * i - f * h)
+ + b * (f * g - d * i)
+ + c * (d * h - e * g);
+ }
+
+ /// Compute major (largest) axis of vector (2D)
+ static inline std::size_t major_axis_2d(const Point& v)
+ {
+ return (std::abs(v.x()) >= std::abs(v.y()) ? 0 : 1);
+ }
+
+ /// Compute major (largest) axis of vector (3D)
+ static inline std::size_t major_axis_3d(const Point& v)
+ {
+ const double vx = std::abs(v.x());
+ const double vy = std::abs(v.y());
+ const double vz = std::abs(v.z());
+ if (vx >= vy && vx >= vz)
+ return 0;
+ if (vy >= vz)
+ return 1;
+ return 2;
+ }
+
+ /// Project point to axis (2D)
+ static inline double project_to_axis_2d(const Point& p, std::size_t axis)
+ {
+ dolfin_assert(axis <= 1);
+ return p[axis];
+ }
+
+ /// Project point to plane (3D)
+ static inline Point project_to_plane_3d(const Point& p, std::size_t axis)
+ {
+ dolfin_assert(axis <= 2);
+ switch (axis)
+ {
+ case 0: return Point(p.y(), p.z());
+ case 1: return Point(p.x(), p.z());
+ case 2: return Point(p.x(), p.y());
+ }
+ return p;
+ }
+
+ /// Check whether x in [a, b]
+ static inline bool contains(double a, double b, double x)
+ {
+ return a <= x and x <= b;
+ }
+
+ /// Check whether x in (a, b)
+ static inline bool contains_strict(double a, double b, double x)
+ {
+ return a < x and x < b;
+ }
+
+ };
+
+}
+
+#endif
diff --git a/dolfin/geometry/IntersectionConstruction.cpp b/dolfin/geometry/IntersectionConstruction.cpp
new file mode 100644
index 0000000..284bd22
--- /dev/null
+++ b/dolfin/geometry/IntersectionConstruction.cpp
@@ -0,0 +1,862 @@
+// Copyright (C) 2014-2017 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2014-02-03
+// Last changed: 2017-10-07
+
+#include <iomanip>
+#include <dolfin/mesh/MeshEntity.h>
+#include "predicates.h"
+#include "GeometryPredicates.h"
+#include "GeometryTools.h"
+#include "GeometryDebugging.h"
+#include "CollisionPredicates.h"
+#include "IntersectionConstruction.h"
+#include "CGALExactArithmetic.h"
+
+using namespace dolfin;
+
+namespace
+{
+ // Add points to vector
+ template <typename T>
+ inline void add(std::vector<T>& points,
+ const std::vector<T>& _points)
+ {
+ points.insert(points.end(), _points.begin(), _points.end());
+ }
+
+ // Filter unique points
+ template <typename T>
+ inline std::vector<T> unique(const std::vector<T>& points)
+ {
+ std::vector<T> _unique;
+ _unique.reserve(points.size());
+
+ for (std::size_t i = 0; i < points.size(); ++i)
+ {
+ bool found = false;
+ for (std::size_t j = i+1; j < points.size(); ++j)
+ {
+ if (points[i] == points[j])
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ _unique.push_back(points[i]);
+ }
+
+ return _unique;
+ }
+
+ // Convert vector of doubles to vector of points
+ std::vector<Point> to_points(const std::vector<double>& points)
+ {
+ std::vector<Point> _points;
+ for (auto x : points)
+ _points.push_back(Point(x));
+ return _points;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// High-level intersection construction functions
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection(const MeshEntity& entity_0,
+ const MeshEntity& entity_1)
+{
+ // Get data
+ const MeshGeometry& g0 = entity_0.mesh().geometry();
+ const MeshGeometry& g1 = entity_1.mesh().geometry();
+ const unsigned int* v0 = entity_0.entities(0);
+ const unsigned int* v1 = entity_1.entities(0);
+
+ // Pack data as vectors of points
+ std::vector<Point> points_0(entity_0.dim() + 1);
+ std::vector<Point> points_1(entity_1.dim() + 1);
+ for (std::size_t i = 0; i <= entity_0.dim(); i++)
+ points_0[i] = g0.point(v0[i]);
+ for (std::size_t i = 0; i <= entity_1.dim(); i++)
+ points_1[i] = g1.point(v1[i]);
+
+ // Only look at first entity to get geometric dimension
+ std::size_t gdim = g0.dim();
+
+ // Call common implementation
+ return intersection(points_0, points_1, gdim);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection(const std::vector<Point>& p,
+ const std::vector<Point>& q,
+ std::size_t gdim)
+{
+ // Get topological dimensions
+ const std::size_t d0 = p.size() - 1;
+ const std::size_t d1 = q.size() - 1;
+
+ // Swap if d0 < d1 (reduce from 16 to 10 cases)
+ if (d0 < d1)
+ return intersection(q, p, gdim);
+
+ // Pick correct specialized implementation
+ if (d0 == 0 and d1 == 0)
+ {
+ switch (gdim)
+ {
+ case 1: return to_points(intersection_point_point_1d(p[0][0], q[0][0]));
+ case 2: return intersection_point_point_2d(p[0], q[0]);
+ case 3: return intersection_point_point_3d(p[0], q[0]);
+ }
+ }
+ else if (d0 == 1 and d1 == 0)
+ {
+ switch (gdim)
+ {
+ case 1: return to_points(intersection_segment_point_1d(p[0][0], p[1][0], q[0][0]));
+ case 2: return intersection_segment_point_2d(p[0], p[1], q[0]);
+ case 3: return intersection_segment_point_3d(p[0], p[1], q[0]);
+ }
+ }
+ else if (d0 == 1 and d1 == 1)
+ {
+ switch (gdim)
+ {
+ case 1: return to_points(intersection_segment_segment_1d(p[0][0], p[1][0], q[0][0], q[1][0]));
+ case 2: return intersection_segment_segment_2d(p[0], p[1], q[0], q[1]);
+ case 3: return intersection_segment_segment_3d(p[0], p[1], q[0], q[1]);
+ }
+ }
+ else if (d0 == 2 and d1 == 0)
+ {
+ switch (gdim)
+ {
+ case 2: return intersection_triangle_point_2d(p[0], p[1], p[2], q[0]);
+ case 3: return intersection_triangle_point_3d(p[0], p[1], p[2], q[0]);
+ }
+ }
+ else if (d0 == 2 and d1 == 1)
+ {
+ switch (gdim)
+ {
+ case 2: return intersection_triangle_segment_2d(p[0], p[1], p[2], q[0], q[1]);
+ case 3: return intersection_triangle_segment_3d(p[0], p[1], p[2], q[0], q[1]);
+ }
+ }
+ else if (d0 == 2 and d1 == 2)
+ {
+ switch (gdim)
+ {
+ case 2: return intersection_triangle_triangle_2d(p[0], p[1], p[2], q[0], q[1], q[2]);
+ case 3: return intersection_triangle_triangle_3d(p[0], p[1], p[2], q[0], q[1], q[2]);
+ }
+ }
+ else if (d0 == 3 and d1 == 0)
+ {
+ switch (gdim)
+ {
+ case 3: return intersection_tetrahedron_point_3d(p[0], p[1], p[2], p[3], q[0]);
+ }
+ }
+ else if (d0 == 3 and d1 == 1)
+ {
+ switch (gdim)
+ {
+ case 3: return intersection_tetrahedron_segment_3d(p[0], p[1], p[2], p[3], q[0], q[1]);
+ }
+ }
+ else if (d0 == 3 and d1 == 2)
+ {
+ switch (gdim)
+ {
+ case 3: return intersection_tetrahedron_triangle_3d(p[0], p[1], p[2], p[3], q[0], q[1], q[2]);
+ }
+ }
+ else if (d0 == 3 and d1 == 3)
+ {
+ switch (gdim)
+ {
+ case 3: return intersection_tetrahedron_tetrahedron_3d(p[0], p[1], p[2], p[3], q[0], q[1], q[2], q[3]);
+ }
+ }
+
+ // We should not reach this point
+ dolfin_error("IntersectionConstruction.cpp",
+ "compute intersection",
+ "Unexpected intersection: %d-%d in %d dimensions", d0, d1, gdim);
+
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+// Low-level intersection construction functions
+//-----------------------------------------------------------------------------
+std::vector<double>
+IntersectionConstruction::intersection_point_point_1d(double p0,
+ double q0)
+{
+ if (p0 == q0)
+ return std::vector<double>(1, p0);
+ return std::vector<double>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_point_point_2d(const Point& p0,
+ const Point& q0)
+{
+ if (p0.x() == q0.x() && p0.y() == q0.y())
+ return std::vector<Point>(1, p0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_point_point_3d(const Point& p0,
+ const Point& q0)
+{
+ if (p0.x() == q0.x() && p0.y() == q0.y() && p0.z() == q0.z())
+ return std::vector<Point>(1, p0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<double>
+IntersectionConstruction::intersection_segment_point_1d(double p0,
+ double p1,
+ double q0)
+{
+ if (CollisionPredicates::collides_segment_point_1d(p0, p1, q0))
+ return std::vector<double>(1, q0);
+ else
+ return std::vector<double>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_segment_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0)
+{
+ if (CollisionPredicates::collides_segment_point_2d(p0, p1, q0))
+ return std::vector<Point>(1, q0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_segment_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0)
+{
+ if (CollisionPredicates::collides_segment_point_3d(p0, p1, q0))
+ return std::vector<Point>(1, q0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0)
+{
+ if (CollisionPredicates::collides_triangle_point_2d(p0, p1, p2, q0))
+ return std::vector<Point>(1, q0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0)
+{
+ if (CollisionPredicates::collides_triangle_point_3d(p0, p1, p2, q0))
+ return std::vector<Point>(1, q0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0)
+{
+ if (CollisionPredicates::collides_tetrahedron_point_3d(p0, p1, p2, p3, q0))
+ return std::vector<Point>(1, q0);
+ else
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<double>
+IntersectionConstruction::intersection_segment_segment_1d(double p0,
+ double p1,
+ double q0,
+ double q1)
+{
+ // The list of points (convex hull)
+ std::vector<double> points;
+
+ // Add point intersections (2)
+ add(points, intersection_segment_point_1d(p0, p1, q0));
+ add(points, intersection_segment_point_1d(p0, p1, q1));
+ add(points, intersection_segment_point_1d(q0, q1, p0));
+ add(points, intersection_segment_point_1d(q0, q1, p1));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+{
+ // We consider the following 4 cases for the segment q0-q1
+ // relative to the line defined by the segment p0-p1:
+ //
+ // Case 0: qo = q0o*q1o > 0.
+ //
+ // --> points on the same side.
+ // --> no intersection
+ //
+ // Case 1: (q0o == 0. and q1o != 0.) or (q0o != 0. and q1o == 0.)
+ //
+ // --> exactly one point on line
+ // --> possible point intersection
+ //
+ // Case 2: q0o = 0. and q10 = 0. [or unstable case]
+ //
+ // --> both points on line
+ // --> project to 1D
+ //
+ // Case 3: qo = q0o*q1o < 0.
+ //
+ // --> points on different sides
+ // --> compute intersection point with line
+ // --> project to 1D and check if point is inside segment
+ //
+ // Note that the computation in Case 3 may be sensitive to rounding
+ // errors if both points are almost on the line. If this happens
+ // we instead consider the points to be on the line [Case 2] to
+ // obtain one or more sensible points (if any).
+
+ // Compute orientation of segment end points wrt line
+ const double q0o = orient2d(p0, p1, q0);
+ const double q1o = orient2d(p0, p1, q1);
+
+ // Case 0: points on the same side --> no intersection
+ if ((q0o > 0.0 and q1o > 0.0) or(q0o < 0.0 and q1o < 0.0))
+ return std::vector<Point>();
+
+ // Repeat the same procedure for p
+ const double p0o = orient2d(q0, q1, p0);
+ const double p1o = orient2d(q0, q1, p1);
+ if ((p0o > 0.0 and p1o > 0.0) or (p0o < 0.0 and p1o < 0.0))
+ return std::vector<Point>();
+
+ // Case 1: exactly one point on line --> possible point intersection
+ if (q0o == 0.0 and q1o != 0.0)
+ return intersection_segment_point_2d(p0, p1, q0);
+ else if (q0o != 0.0 and q1o == 0.0)
+ return intersection_segment_point_2d(p0, p1, q1);
+ else if (p0o == 0.0 and p1o != 0.0)
+ return intersection_segment_point_2d(q0, q1, p0);
+ else if (p0o != 0.0 and p1o == 0.0)
+ return intersection_segment_point_2d(q0, q1, p1);
+
+ // Compute line vector and major axis
+ const Point v = p1 - p0;
+ const std::size_t major_axis = GeometryTools::major_axis_2d(v);
+
+ // Project points to major axis
+ const double P0 = GeometryTools::project_to_axis_2d(p0, major_axis);
+ const double P1 = GeometryTools::project_to_axis_2d(p1, major_axis);
+ const double Q0 = GeometryTools::project_to_axis_2d(q0, major_axis);
+ const double Q1 = GeometryTools::project_to_axis_2d(q1, major_axis);
+
+ // Case 2: both points on line (or almost)
+ if (std::abs(q0o) < DOLFIN_EPS and std::abs(q1o) < DOLFIN_EPS)
+ {
+ // Compute 1D intersection points
+ const std::vector<double>
+ points_1d = intersection_segment_segment_1d(P0, P1, Q0, Q1);
+
+ // Unproject points: add back second coordinate
+ std::vector<Point> points;
+ switch (major_axis)
+ {
+ case 0:
+ for (auto p : points_1d)
+ {
+ const double y = p0.y() + (p - p0.x()) * v.y() / v.x();
+ points.push_back(Point(p, y));
+ }
+ break;
+ default:
+ for (auto p : points_1d)
+ {
+ const double x = p0.x() + (p - p0.y()) * v.x() / v.y();
+ points.push_back(Point(x, p));
+ }
+ }
+
+ return unique(points);
+ }
+
+ // Case 3: points on different sides (main case)
+
+ // Compute determinant needed for intersection computation
+ const Point w = q1 - q0;
+ const double den = w.x()*v.y() - w.y()*v.x();
+
+ // Figure out which one of the four points we want to use
+ // as starting point for numerical robustness
+ const double p_dist = v.norm();
+ const double q_dist = w.norm();
+ enum orientation { P0O, P1O, Q0O, Q1O };
+ std::array<std::pair<double, orientation>, 4> oo
+ = {{ { std::abs(p0o)*p_dist, P0O },
+ { std::abs(p1o)*p_dist, P1O },
+ { std::abs(q0o)*q_dist, Q0O },
+ { std::abs(q1o)*q_dist, Q1O } }};
+ const auto it = std::min_element(oo.begin(), oo.end());
+
+ // Compute the intersection point
+ Point x;
+ switch (it->second)
+ {
+ case P0O:
+ // Flip sign because den = det(q1 - q0, v), but we want det(q1 - q0, -v)
+ x = p0 - p0o / den * v;
+ break;
+ case P1O:
+ // Flip sign because v = p1 - p0, but we want p0 - p1
+ x = p1 - p1o / den * v;
+ break;
+ case Q0O:
+ // Default case
+ x = q0 + q0o / den * w;
+ break;
+ case Q1O:
+ // Use q1o
+ x = q1 + q1o / den * w;
+ break;
+ }
+
+ // Project point to major axis and check if inside segment
+ const double X = GeometryTools::project_to_axis_2d(x, major_axis);
+ if (CollisionPredicates::collides_segment_point_1d(P0, P1, X))
+ return std::vector<Point>(1, x);
+
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1)
+{
+ // This function is not used so no need to spend time on the implementation.
+ dolfin_not_implemented();
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+{
+ // The list of points (convex hull)
+ std::vector<Point> points;
+
+ // Add point intersections (2)
+ add(points, intersection_triangle_point_2d(p0, p1, p2, q0));
+ add(points, intersection_triangle_point_2d(p0, p1, p2, q1));
+
+ // Add segment-segment intersections (3)
+ add(points, intersection_segment_segment_2d(p0, p1, q0, q1));
+ add(points, intersection_segment_segment_2d(p0, p2, q0, q1));
+ add(points, intersection_segment_segment_2d(p1, p2, q0, q1));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::_intersection_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+{
+ // We consider the following 4 cases for the segment q0-q1
+ // relative to the plane defined by the triangle p0-p1-p2:
+ //
+ // Case 0: qo = q0o*q1o > 0.
+ //
+ // --> points on the same side
+ // --> no intersection
+ //
+ // Case 1: (q0o == 0. and q1o != 0.) or (q0o != 0. and q1o == 0.)
+ //
+ // --> exactly one point in plane
+ // --> possible point intersection
+ //
+ // Case 2: q0o = 0. and q10 = 0. [or unstable case]
+ //
+ // --> points in plane
+ // --> project to 2D
+ //
+ // Case 3: qo = q0o*q1o < 0.
+ //
+ // --> points on different sides
+ // --> compute intersection point with plane
+ // --> project to 2D and check if point is inside triangle
+ //
+ // Note that the computation in Case 3 may be sensitive to rounding
+ // errors if both points are almost in the plane. If this happens
+ // we instead consider the points to be in the plane [Case 2] to
+ // obtain one or more sensible points (if any).
+
+ // Compute orientation of segment end points wrt plane
+ const double q0o = orient3d(p0, p1, p2, q0);
+ const double q1o = orient3d(p0, p1, p2, q1);
+
+ // Compute total orientation of segment wrt plane
+ const double qo = q0o*q1o;
+
+ // Case 0: points on the same side --> no intersection
+ if (qo > 0.0)
+ return std::vector<Point>();
+
+ // Case 1: exactly one point in plane --> possible point intersection
+ if (q0o == 0.0 and q1o != 0.0)
+ return intersection_triangle_point_3d(p0, p1, p2, q0);
+ else if (q0o != 0.0 and q1o == 0.0)
+ return intersection_triangle_point_3d(p0, p1, p2, q1);
+
+ // Compute plane normal and major axis
+ const Point n = GeometryTools::cross_product(p0, p1, p2);
+ const std::size_t major_axis = GeometryTools::major_axis_3d(n);
+
+ // Project points to major axis plane
+ const Point P0 = GeometryTools::project_to_plane_3d(p0, major_axis);
+ const Point P1 = GeometryTools::project_to_plane_3d(p1, major_axis);
+ const Point P2 = GeometryTools::project_to_plane_3d(p2, major_axis);
+ const Point Q0 = GeometryTools::project_to_plane_3d(q0, major_axis);
+ const Point Q1 = GeometryTools::project_to_plane_3d(q1, major_axis);
+
+ // Case 2: both points in plane (or almost)
+ if (std::abs(q0o) < DOLFIN_EPS_LARGE and std::abs(q1o) < DOLFIN_EPS_LARGE)
+ {
+ // Compute 2D intersection points
+ const std::vector<Point>
+ points_2d = intersection_triangle_segment_2d(P0, P1, P2, Q0, Q1);
+
+ // Unproject points: add back third coordinate
+ std::vector<Point> points;
+ switch (major_axis)
+ {
+ case 0:
+ for (auto P : points_2d)
+ {
+ const double x = p0.x() + ((p0.y() - P.x())*n.y() + (p0.z() - P.y())*n.z()) / n.x();
+ points.push_back(Point(x, P.x(), P.y()));
+ }
+ break;
+ case 1:
+ for (auto P : points_2d)
+ {
+ const double y = p0.y() + ((p0.x() - P.x())*n.x() + (p0.z() - P.y())*n.z()) / n.y();
+ points.push_back(Point(P.x(), y, P.y()));
+ }
+ break;
+ default:
+ for (auto P : points_2d)
+ {
+ const double z = p0.z() + ((p0.x() - P.x())*n.x() + (p0.y() - P.y())*n.y()) / n.z();
+ points.push_back(Point(P.x(), P.y(), z));
+ }
+ }
+
+ return unique(points);
+ }
+
+ // Case 3: points on different sides (main case)
+
+ // Compute intersection point
+ const double num = n.dot(p0 - q0);
+ const double den = n.dot(q1 - q0);
+ const Point x = p0 + num / den * (p1 - p0);
+
+ // Project point to major axis plane and check if inside triangle
+ const Point X = GeometryTools::project_to_plane_3d(x, major_axis);
+ if (CollisionPredicates::collides_triangle_point_2d(P0, P1, P2, X))
+ return std::vector<Point>(1, x);
+
+ return std::vector<Point>();
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1)
+{
+ // The list of points (convex hull)
+ std::vector<Point> points;
+
+ // Add point intersections (4 + 4 = 8)
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q0));
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q1));
+
+ // Add triangle-segment intersections (4)
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q0, q1));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+// Intersections with triangles and tetrahedra: computed by delegation
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ // The list of points (convex hull)
+ std::vector<Point> points;
+
+ // Add point intersections (3 + 3 = 6)
+ add(points, intersection_triangle_point_2d(p0, p1, p2, q0));
+ add(points, intersection_triangle_point_2d(p0, p1, p2, q1));
+ add(points, intersection_triangle_point_2d(p0, p1, p2, q2));
+ add(points, intersection_triangle_point_2d(q0, q1, q2, p0));
+ add(points, intersection_triangle_point_2d(q0, q1, q2, p1));
+ add(points, intersection_triangle_point_2d(q0, q1, q2, p2));
+
+ // Add segment-segment intersections (3 x 3 = 9)
+ add(points, intersection_segment_segment_2d(p0, p1, q0, q1));
+ add(points, intersection_segment_segment_2d(p0, p1, q0, q2));
+ add(points, intersection_segment_segment_2d(p0, p1, q1, q2));
+ add(points, intersection_segment_segment_2d(p0, p2, q0, q1));
+ add(points, intersection_segment_segment_2d(p0, p2, q0, q2));
+ add(points, intersection_segment_segment_2d(p0, p2, q1, q2));
+ add(points, intersection_segment_segment_2d(p1, p2, q0, q1));
+ add(points, intersection_segment_segment_2d(p1, p2, q0, q2));
+ add(points, intersection_segment_segment_2d(p1, p2, q1, q2));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ // The list of points (convex hull)
+ std::vector<Point> points;
+
+ // Add point intersections (3 + 3 = 6)
+ add(points, intersection_triangle_point_3d(p0, p1, p2, q0));
+ add(points, intersection_triangle_point_3d(p0, p1, p2, q1));
+ add(points, intersection_triangle_point_3d(p0, p1, p2, q2));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p0));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p1));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p2));
+
+ // Add triangle-segment intersections (3 + 3 = 6)
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q1, q2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p1));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p1, p2));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2)
+{
+ // The list of points (convex hull)
+ std::vector<Point> points;
+
+ // Add point intersections (3 + 4 = 7)
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q0));
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q1));
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q2));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p0));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p1));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p2));
+ add(points, intersection_triangle_point_3d(q0, q1, q2, p3));
+
+ // Add triangle-segment intersections (4 x 3 + 1 x 6 = 18)
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q1, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q1, q2));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q1, q2));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q0, q2));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q1, q2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p1));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p1, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p1, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p2, p3));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::_intersection_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3)
+{
+ // The list of points (convex hull)
+ std::vector<Point> points;
+
+ // Add point intersections (4 + 4 = 8)
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q0));
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q1));
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q2));
+ add(points, intersection_tetrahedron_point_3d(p0, p1, p2, p3, q3));
+ add(points, intersection_tetrahedron_point_3d(q0, q1, q2, q3, p0));
+ add(points, intersection_tetrahedron_point_3d(q0, q1, q2, q3, p1));
+ add(points, intersection_tetrahedron_point_3d(q0, q1, q2, q3, p2));
+ add(points, intersection_tetrahedron_point_3d(q0, q1, q2, q3, p3));
+
+ // Let's hope we got this right... :-)
+
+ // Add triangle-segment intersections (4 x 6 + 4 x 6 = 48)
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q0, q3));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q1, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q1, q3));
+ add(points, intersection_triangle_segment_3d(p0, p1, p2, q2, q3));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q0, q3));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q1, q2));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q1, q3));
+ add(points, intersection_triangle_segment_3d(p0, p1, p3, q2, q3));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q0, q2));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q0, q3));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q1, q2));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q1, q3));
+ add(points, intersection_triangle_segment_3d(p0, p2, p3, q2, q3));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q0, q1));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q0, q2));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q0, q3));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q1, q2));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q1, q3));
+ add(points, intersection_triangle_segment_3d(p1, p2, p3, q2, q3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p1));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p0, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p1, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p1, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q2, p2, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q3, p0, p1));
+ add(points, intersection_triangle_segment_3d(q0, q1, q3, p0, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q3, p0, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q3, p1, p2));
+ add(points, intersection_triangle_segment_3d(q0, q1, q3, p1, p3));
+ add(points, intersection_triangle_segment_3d(q0, q1, q3, p2, p3));
+ add(points, intersection_triangle_segment_3d(q0, q2, q3, p0, p1));
+ add(points, intersection_triangle_segment_3d(q0, q2, q3, p0, p2));
+ add(points, intersection_triangle_segment_3d(q0, q2, q3, p0, p3));
+ add(points, intersection_triangle_segment_3d(q0, q2, q3, p1, p2));
+ add(points, intersection_triangle_segment_3d(q0, q2, q3, p1, p3));
+ add(points, intersection_triangle_segment_3d(q0, q2, q3, p2, p3));
+ add(points, intersection_triangle_segment_3d(q1, q2, q3, p0, p1));
+ add(points, intersection_triangle_segment_3d(q1, q2, q3, p0, p2));
+ add(points, intersection_triangle_segment_3d(q1, q2, q3, p0, p3));
+ add(points, intersection_triangle_segment_3d(q1, q2, q3, p1, p2));
+ add(points, intersection_triangle_segment_3d(q1, q2, q3, p1, p3));
+ add(points, intersection_triangle_segment_3d(q1, q2, q3, p2, p3));
+
+ dolfin_assert(GeometryPredicates::is_finite(points));
+ return unique(points);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1)
+{
+ return CGAL_INTERSECTION_CHECK(_intersection_triangle_segment_3d(p0, p1, p2, q0, q1),
+ cgal_intersection_triangle_segment_3d(p0, p1, p2, q0, q1));
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+IntersectionConstruction::intersection_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3)
+{
+ return CGAL_INTERSECTION_CHECK(_intersection_tetrahedron_tetrahedron_3d(p0, p1, p2, p3, q0, q1, q2, q3),
+ cgal_intersection_tetrahedron_tetrahedron_3d(p0, p1, p2, p3, q0, q1, q2, q3));
+}
diff --git a/dolfin/geometry/IntersectionConstruction.h b/dolfin/geometry/IntersectionConstruction.h
new file mode 100644
index 0000000..8511a68
--- /dev/null
+++ b/dolfin/geometry/IntersectionConstruction.h
@@ -0,0 +1,292 @@
+// Copyright (C) 2014-2016 Anders Logg, August Johansson and Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2014-02-03
+// Last changed: 2017-09-29
+
+#ifndef __INTERSECTION_CONSTRUCTION_H
+#define __INTERSECTION_CONSTRUCTION_H
+
+#include <vector>
+#include <dolfin/log/log.h>
+#include "Point.h"
+
+namespace dolfin
+{
+ // Forward declarations
+ class MeshEntity;
+
+ /// This class implements algorithms for computing pairwise
+ /// intersections of simplices. The computed intersection is always
+ /// convex and represented as a set of points s.t. the intersection
+ /// is the convex hull of these points.
+
+ class IntersectionConstruction
+ {
+ public:
+
+ /// Compute intersection of two entities.
+ ///
+ /// *Arguments*
+ /// entity_0 (_MeshEntity_)
+ /// The first entity.
+ /// entity_1 (_MeshEntity_)
+ /// The second entity.
+ ///
+ /// *Returns*
+ /// std::vector<Pointdouble>
+ /// A vector of points s.t. the intersection is the convex hull of
+ /// these points.
+ static std::vector<Point>
+ intersection(const MeshEntity& entity_0,
+ const MeshEntity& entity_1);
+
+ /// Compute intersection of two entities.
+ /// This version takes two vectors of points representing the entities.
+ ///
+ /// *Arguments*
+ /// points_0 (std::vector<Point>)
+ /// The vertex coordinates of the first entity.
+ /// points_1 (std::vector<Point>)
+ /// The vertex coordinates of the second entity.
+ /// gdim (std::size_t)
+ /// The geometric dimension.
+ ///
+ /// *Returns*
+ /// std::vector<Point>
+ /// A vector of points s.t. the intersection is the convex hull of
+ /// these points.
+ static std::vector<Point>
+ intersection(const std::vector<Point>& points_0,
+ const std::vector<Point>& points_1,
+ std::size_t gdim);
+
+ //--- Low-level intersection construction functions ---
+
+ // There are 19 different intersections to consider. Initially, we have
+ // 4 different entities: point, segment, triangle, tetrahedron, and thus
+ // 16 combinations. Because of symmetry, these are reduced to 10. However,
+ // some of the combination are relevant in both 1D, 2D and 3D, and thus
+ // the total number of intersections lands at 19. The table indicates the
+ // number of versions (1D, 2D, 3D) for each relevant combination.
+ //
+ // | 0 1 2 3
+ // --------------
+ // 0 | 3 x x x point-foo (1D, 2D, 3D)
+ // 1 | 3 3 x x segment-foo (1D, 2D, 3D)
+ // 2 | 2 2 2 x triangle-foo (--, 2D, 3D)
+ // 3 | 1 1 1 1 tetrahedron-foo (--, --, 3D)
+ //
+ // The intersection construction functions can be grouped into
+ // three classes:
+ //
+ // [P] Use point collision predicates (9)
+ // [C] Compute collision by solving for intersection points (3)
+ // [D] Delegate computation to [P] or [C] for subsimplices (7)
+ //
+ // [P] intersection_point_point_1d
+ // [P] intersection_point_point_2d
+ // [P] intersection_point_point_3d
+ // [P] intersection_segment_point_1d
+ // [P] intersection_segment_point_2d
+ // [P] intersection_segment_point_3d
+ // [P] intersection_triangle_point_2d
+ // [P] intersection_triangle_point_3d
+ // [P] intersection_tetrahedron_point_3d
+ // [D] intersection_segment_segment_1d
+ // [C] intersection_segment_segment_2d
+ // [C] intersection_segment_segment_3d <-- not used/implemented
+ // [D] intersection_triangle_segment_2d
+ // [C] intersection_triangle_segment_3d <-- needs review
+ // [D] intersection_tetrahedron_segment_3d
+ // [D] intersection_triangle_triangle_2d
+ // [D] intersection_triangle_triangle_3d
+ // [D] intersection_tetrahedron_triangle_3d
+ // [D] intersection_tetrahedron_tetrahedron_3d
+ //
+ // Note that intersection_segment_segment_3d is not used/implemented.
+ // In summary, this means that there are only two functions that require
+ // computation, other than simple checks for point collisions or delegation
+ // to lower-level intersection functions. These two functions are:
+ //
+ // [C] intersection_segment_segment_2d
+ // [C] intersection_triangle_segment_3d
+
+ /// Compute intersection of points p0 and q0 (1D)
+ static std::vector<double>
+ intersection_point_point_1d(double p0,
+ double q0);
+
+ /// Compute intersection of points p0 and q0 (2D)
+ static std::vector<Point>
+ intersection_point_point_2d(const Point& p0,
+ const Point& q0);
+
+ /// Compute intersection of points p0 and q0 (3D)
+ static std::vector<Point>
+ intersection_point_point_3d(const Point& p0,
+ const Point& q0);
+
+ /// Compute intersection of segment p0-p1 with point q0 (1D)
+ static std::vector<double>
+ intersection_segment_point_1d(double p0,
+ double p1,
+ double q0);
+
+ /// Compute intersection of segment p0-p1 with point q0 (2D)
+ static std::vector<Point>
+ intersection_segment_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0);
+
+ /// Compute intersection of segment p0-p1 with point q0 (3D)
+ static std::vector<Point>
+ intersection_segment_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0);
+
+ /// Compute intersection of triangle p0-p1-p2 with point q0 (2D)
+ static std::vector<Point>
+ intersection_triangle_point_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0);
+
+ /// Compute intersection of triangle p0-p1-p2 with point q0 (3D)
+ static std::vector<Point>
+ intersection_triangle_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0);
+
+ /// Compute intersection of tetrahedron p0-p1-p2-p3 with point q0 (3D)
+ static std::vector<Point>
+ intersection_tetrahedron_point_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0);
+
+ /// Compute intersection of segment p0-p1 with segment q0-q1 (1D)
+ static std::vector<double>
+ intersection_segment_segment_1d(double p0,
+ double p1,
+ double q0,
+ double q1);
+
+ /// Compute intersection of segment p0-p1 with segment q0-q1 (2D)
+ static std::vector<Point>
+ intersection_segment_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1);
+
+ /// Compute intersection of segment p0-p1 with segment q0-q1 (3D)
+ static std::vector<Point>
+ intersection_segment_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& q0,
+ const Point& q1);
+
+ /// Compute intersection of triangle p0-p1-p2 with segment q0-q1 (2D)
+ static std::vector<Point>
+ intersection_triangle_segment_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ /// Compute intersection of triangle p0-p1-p2 with segment q0-q1 (3D)
+ static std::vector<Point>
+ intersection_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ /// Compute intersection of tetrahedron p0-p1-p2-p3 with segment q0-q1 (3D)
+ static std::vector<Point>
+ intersection_tetrahedron_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1);
+
+ /// Compute intersection of triangle p0-p1-p2 with triangle q0-q1-q2 (2D)
+ static std::vector<Point>
+ intersection_triangle_triangle_2d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ /// Compute intersection of triangle p0-p1-p2 with triangle q0-q1-q2 (3D)
+ static std::vector<Point>
+ intersection_triangle_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ /// Compute intersection of tetrahedron p0-p1-p2-p3 with triangle q0-q1-q2 (3D)
+ static std::vector<Point>
+ intersection_tetrahedron_triangle_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2);
+
+ /// Compute intersection of tetrahedron p0-p1-p2-p3 with tetrahedron q0-q1-q2-q3 (3D)
+ static std::vector<Point>
+ intersection_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3);
+
+ private :
+ /// Compute intersection of triangle p0-p1-p2 with segment q0-q1 (3D)
+ static std::vector<Point>
+ _intersection_triangle_segment_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& q0,
+ const Point& q1);
+
+ static std::vector<Point>
+ _intersection_tetrahedron_tetrahedron_3d(const Point& p0,
+ const Point& p1,
+ const Point& p2,
+ const Point& p3,
+ const Point& q0,
+ const Point& q1,
+ const Point& q2,
+ const Point& q3);
+
+ };
+
+}
+
+#endif
diff --git a/dolfin/geometry/IntersectionTriangulation.cpp b/dolfin/geometry/IntersectionTriangulation.cpp
deleted file mode 100644
index 9009865..0000000
--- a/dolfin/geometry/IntersectionTriangulation.cpp
+++ /dev/null
@@ -1,1365 +0,0 @@
-// Copyright (C) 2014 Anders Logg and August Johansson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2014-02-03
-// Last changed: 2014-05-28
-
-#include <dolfin/mesh/MeshEntity.h>
-#include "IntersectionTriangulation.h"
-#include "CollisionDetection.h"
-
-using namespace dolfin;
-
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection(const MeshEntity& entity_0,
- const MeshEntity& entity_1)
-{
- switch (entity_0.dim())
- {
- case 0:
- // PointCell
- dolfin_not_implemented();
- break;
- case 1:
- // IntervalCell
- dolfin_not_implemented();
- break;
- case 2:
- // TriangleCell
- switch (entity_1.dim())
- {
- case 0:
- dolfin_not_implemented();
- break;
- case 1:
- return triangulate_intersection_triangle_interval(entity_0, entity_1);
- case 2:
- return triangulate_intersection_triangle_triangle(entity_0, entity_1);
- case 3:
- return triangulate_intersection_tetrahedron_triangle(entity_1, entity_0);
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of entity_0 and entity_1",
- "unknown dimension of entity_1 in TriangleCell");
- }
- case 3:
- // TetrahedronCell
- switch (entity_1.dim())
- {
- case 0:
- dolfin_not_implemented();
- break;
- case 1:
- dolfin_not_implemented();
- break;
- case 2:
- return triangulate_intersection_tetrahedron_triangle(entity_0, entity_1);
- case 3:
- return triangulate_intersection_tetrahedron_tetrahedron(entity_0, entity_1);
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of entity_0 and entity_1",
- "unknown dimension of entity_1 in TetrahedronCell");
- }
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of entity_0 and entity_1",
- "unknown dimension of entity_0");
- }
- return std::vector<double>();
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_interval_interval
-(const MeshEntity& interval_0, const MeshEntity& interval_1)
-{
- dolfin_assert(interval_0.mesh().topology().dim() == 1);
- dolfin_assert(interval_1.mesh().topology().dim() == 1);
-
- const std::size_t gdim = interval_0.mesh().geometry().dim();
- dolfin_assert(interval_1.mesh().topology().dim() == gdim);
-
- // Get geometry and vertex data
- std::vector<Point> inter_0(2), inter_1(2);
- for (std::size_t i = 0; i < 2; ++i)
- {
- inter_0[i] = interval_0.mesh().geometry().point(interval_0.entities(0)[i]);
- inter_1[i] = interval_1.mesh().geometry().point(interval_1.entities(0)[i]);
- }
-
- return triangulate_intersection_interval_interval(inter_0, inter_1, gdim);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_triangle_interval
-(const MeshEntity& triangle,
- const MeshEntity& interval)
-{
- dolfin_assert(triangle.mesh().topology().dim() == 2);
- dolfin_assert(interval.mesh().topology().dim() == 1);
-
- const std::size_t gdim = triangle.mesh().geometry().dim();
- dolfin_assert(interval.mesh().geometry().dim() == gdim);
-
- // Get geometry and vertex data
- std::vector<Point> tri(3);
- for (std::size_t i = 0; i < 3; ++i)
- tri[i] = triangle.mesh().geometry().point(triangle.entities(0)[i]);
-
- std::vector<Point> inter(2);
- for (std::size_t i = 0; i < 2; ++i)
- inter[i] = interval.mesh().geometry().point(interval.entities(0)[i]);
-
- return triangulate_intersection_triangle_interval(tri, inter, gdim);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_triangle_triangle
-(const MeshEntity& c0, const MeshEntity& c1)
-{
- // Triangulate the intersection of the two triangles c0 and c1
-
- dolfin_assert(c0.mesh().topology().dim() == 2);
- dolfin_assert(c1.mesh().topology().dim() == 2);
-
- // FIXME: Only 2D for now
- dolfin_assert(c0.mesh().geometry().dim() == 2);
-
- // Get geometry and vertex data
- const MeshGeometry& geometry_0 = c0.mesh().geometry();
- const MeshGeometry& geometry_1 = c1.mesh().geometry();
- const unsigned int* vertices_0 = c0.entities(0);
- const unsigned int* vertices_1 = c1.entities(0);
-
- std::vector<Point> tri_0(3), tri_1(3);
-
- for (std::size_t i = 0; i < 3; ++i)
- {
- tri_0[i] = geometry_0.point(vertices_0[i]);
- tri_1[i] = geometry_1.point(vertices_1[i]);
- }
-
- return triangulate_intersection_triangle_triangle(tri_0, tri_1);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_tetrahedron_triangle
-(const MeshEntity& tetrahedron, const MeshEntity& triangle)
-{
- // Triangulate the intersection of a tetrahedron and a triangle
-
- dolfin_assert(tetrahedron.mesh().topology().dim() == 3);
- dolfin_assert(triangle.mesh().topology().dim() == 2);
-
- // Get geometry and vertex data
- const MeshGeometry& tet_geom = tetrahedron.mesh().geometry();
- const unsigned int* tet_vert = tetrahedron.entities(0);
- std::vector<Point> tet(4);
- for (std::size_t i = 0; i < 4; ++i)
- tet[i] = tet_geom.point(tet_vert[i]);
-
- const MeshGeometry& tri_geom = triangle.mesh().geometry();
- const unsigned int* tri_vert = triangle.entities(0);
- std::vector<Point> tri(3);
- for (std::size_t i = 0; i < 3; ++i)
- tri[i] = tri_geom.point(tri_vert[i]);
-
- return triangulate_intersection_tetrahedron_triangle(tet, tri);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_tetrahedron_tetrahedron
-(const MeshEntity& tetrahedron_0,
- const MeshEntity& tetrahedron_1)
-{
- // Triangulate the intersection of the two tetrahedra
-
- dolfin_assert(tetrahedron_0.mesh().topology().dim() == 3);
- dolfin_assert(tetrahedron_1.mesh().topology().dim() == 3);
-
- // Get the vertices as points
- const MeshGeometry& geometry_0 = tetrahedron_0.mesh().geometry();
- const unsigned int* vertices_0 = tetrahedron_0.entities(0);
- const MeshGeometry& geometry_1 = tetrahedron_1.mesh().geometry();
- const unsigned int* vertices_1 = tetrahedron_1.entities(0);
-
- std::vector<Point> tet_0(4), tet_1(4);
-
- for (std::size_t i = 0; i < 4; ++i)
- {
- tet_0[i] = geometry_0.point(vertices_0[i]);
- tet_1[i] = geometry_1.point(vertices_1[i]);
- }
-
- return triangulate_intersection_tetrahedron_tetrahedron(tet_0, tet_1);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection(const std::vector<Point>& s0,
- std::size_t tdim0,
- const std::vector<Point>& s1,
- std::size_t tdim1,
- std::size_t gdim)
-{
- // General intersection computation of two simplices with different
- // topological dimension but the same geometrical dimension
-
- switch (tdim0) {
- // s0 is interval
- case 1:
- switch (tdim1) {
- case 1: // s1 is interval
- return triangulate_intersection_interval_interval(s0, s1, gdim);
- case 2: // s1 is triangle
- return triangulate_intersection_triangle_interval(s1, s0, gdim);
- case 3: // s1 is tetrahedron
- dolfin_not_implemented();
- break;
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of two simplices s0 and s1",
- "unknown topology %d", tdim1);
- }
- break;
- // s0 is a triangle
- case 2:
- switch (tdim1) {
- case 1: // s1 is interval
- return triangulate_intersection_triangle_interval(s0, s1, gdim);
- case 2: // s1 is triangle
- return triangulate_intersection_triangle_triangle(s0, s1);
- case 3: // s1 is tetrahedron
- return triangulate_intersection_tetrahedron_triangle(s1, s0);
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of two simplices s0 and s1",
- "unknown topology %d", tdim1);
- }
- break;
- // s0 is a tetrahedron
- case 3:
- switch (tdim1) {
- case 1: // s1 is interval
- dolfin_not_implemented();
- break;
- case 2: // s1 is triangle
- return triangulate_intersection_tetrahedron_triangle(s0, s1);
- case 3: // s1 is tetrahedron
- return triangulate_intersection_tetrahedron_tetrahedron(s0, s1);
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of two simplices s0 and s1",
- "unknown topology %d", tdim1);
- }
- break;
- default:
- dolfin_error("IntersectionTriangulation.cpp",
- "triangulate intersection of two simplices s0 and s1",
- "unknown topology %d", tdim0);
- }
-
- // We never end up here
- return std::vector<double>();
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_interval_interval
-(const std::vector<Point>& interval_0,
- const std::vector<Point>& interval_1,
- std::size_t gdim)
-{
- // Flat array for triangulation
- std::vector<double> triangulation;
-
- if (CollisionDetection::collides_edge_edge(interval_0[0], interval_0[1],
- interval_1[0], interval_1[1]))
- {
- // List of colliding points
- std::vector<Point> points;
-
- for (std::size_t i = 0; i < 2; ++i)
- {
- if (CollisionDetection::collides_interval_point(interval_0[0], interval_0[1],
- interval_1[i]))
- points.push_back(interval_1[i]);
- if (CollisionDetection::collides_interval_point(interval_1[0], interval_1[1],
- interval_0[i]))
- points.push_back(interval_0[i]);
- }
-
- // Must not have more than two points
- if (points.size() == 2)
- {
- triangulation.resize(2*gdim);
- for (std::size_t d = 0; d < gdim; ++d)
- {
- triangulation[d] = points[0][d];
- triangulation[gdim+d] = points[1][d];
- }
- }
- }
-
- return triangulation;
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_triangle_interval
-(const std::vector<Point>& triangle,
- const std::vector<Point>& interval,
- std::size_t gdim)
-{
- std::vector<double> triangulation;
- std::vector<Point> points;
-
- // Detect edge intersection points
- Point pt;
- if (intersection_edge_edge(triangle[0], triangle[1],
- interval[0], interval[1],
- pt))
- points.push_back(pt);
- if (intersection_edge_edge(triangle[0], triangle[2],
- interval[0], interval[1],
- pt))
- points.push_back(pt);
- if (intersection_edge_edge(triangle[1], triangle[2],
- interval[0], interval[1],
- pt))
- points.push_back(pt);
-
- // If we get zero intersection points, then both interval ends must
- // be inside
- // FIXME: can we really use two different types of intersection tests: intersection_edge_edge above and Collides here?
- if (points.size() == 0)
- {
- if (CollisionDetection::collides_triangle_point(triangle[0],
- triangle[1],
- triangle[2],
- interval[0]) and
- CollisionDetection::collides_triangle_point(triangle[0],
- triangle[1],
- triangle[2],
- interval[1]))
- {
- triangulation.resize(2*gdim);
- for (std::size_t d = 0; d < gdim; ++d)
- {
- triangulation[d] = interval[0][d];
- triangulation[gdim+d] = interval[1][d];
- }
- return triangulation;
- }
- }
-
- // If we get one intersection point, find the interval end point
- // which is inside the triangle. Note that this points should
- // absolutely not be the same point as we found above. This can
- // happen since we use different types of tests here and above.
- if (points.size() == 1)
- {
- for (std::size_t k = 0; k < 2; ++k)
- {
- // Make sure the point interval[k] is not points[0]
- if ((interval[k]-points[0]).norm() > DOLFIN_EPS_LARGE and
- CollisionDetection::collides_triangle_point(triangle[0],
- triangle[1],
- triangle[2],
- interval[k]))
- {
- triangulation.resize(2*gdim);
- for (std::size_t d = 0; d < gdim; ++d)
- {
- triangulation[d] = points[0][d];
- triangulation[gdim+d] = interval[k][d];
- }
- return triangulation;
- }
- }
- }
-
- // If we get two intersection points, triangulate this line.
- if (points.size() == 2)
- {
- triangulation.resize(2*gdim);
- for (std::size_t d = 0; d < gdim; ++d)
- {
- triangulation[d] = points[0][d];
- triangulation[gdim+d] = points[1][d];
- }
- return triangulation;
- }
-
- return triangulation;
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_triangle_triangle
-(const std::vector<Point>& tri_0,
- const std::vector<Point>& tri_1)
-{
- // This algorithm computes the (convex) polygon resulting from the
- // intersection of two triangles. It then triangulates the polygon
- // by trivially drawing an edge from one vertex to all other
- // vertices. The polygon is computed by first identifying all
- // vertex-cell collisions and then all edge-edge collisions. The
- // points are then sorted using a simplified Graham scan (simplified
- // since we know the polygon is convex).
-
- // Tolerance for duplicate points (p and q are the same if
- // (p-q).norm() < same_point_tol)
- const double same_point_tol = DOLFIN_EPS_LARGE;
-
-
- // Create empty list of collision points
- std::vector<Point> points;
-
- // Find all vertex-cell collisions
- for (std::size_t i = 0; i < 3; i++)
- {
- // Note: this routine is changed to being public:
- if (CollisionDetection::collides_triangle_point(tri_1[0],
- tri_1[1],
- tri_1[2],
- tri_0[i]))
- points.push_back(tri_0[i]);
-
- if (CollisionDetection::collides_triangle_point(tri_0[0],
- tri_0[1],
- tri_0[2],
- tri_1[i]))
- points.push_back(tri_1[i]);
- }
-
- // Find all edge-edge collisions (not needed?)
- for (std::size_t i0 = 0; i0 < 3; i0++)
- {
- const std::size_t j0 = (i0 + 1) % 3;
- const Point p0 = tri_0[i0];
- const Point q0 = tri_0[j0];
- for (std::size_t i1 = 0; i1 < 3; i1++)
- {
- const std::size_t j1 = (i1 + 1) % 3;
- const Point p1 = tri_1[i1];
- const Point q1 = tri_1[j1];
- Point point;
- if (intersection_edge_edge(p0, q0, p1, q1, point))
- points.push_back(point);
- }
- }
-
- // Remove duplicate points
- std::vector<Point> tmp;
- tmp.reserve(points.size());
-
- for (std::size_t i = 0; i < points.size(); ++i)
- {
- bool different = true;
- for (std::size_t j = i+1; j < points.size(); ++j)
- if ((points[i] - points[j]).norm() < same_point_tol)
- {
- different = false;
- break;
- }
- if (different)
- tmp.push_back(points[i]);
- }
- points = tmp;
-
- // Special case: no points found
- std::vector<double> triangulation;
- if (points.size() < 3)
- return triangulation;
-
- // Find left-most point (smallest x-coordinate)
- std::size_t i_min = 0;
- double x_min = points[0].x();
- for (std::size_t i = 1; i < points.size(); i++)
- {
- const double x = points[i].x();
- if (x < x_min)
- {
- x_min = x;
- i_min = i;
- }
- }
-
- // Compute signed squared cos of angle with (0, 1) from i_min to all points
- std::vector<std::pair<double, std::size_t>> order;
- for (std::size_t i = 0; i < points.size(); i++)
- {
- // Skip left-most point used as origin
- if (i == i_min)
- continue;
-
- // Compute vector to point
- const Point v = points[i] - points[i_min];
-
- // Compute square cos of angle
- const double cos2 = (v.y() < 0.0 ? -1.0 : 1.0)*v.y()*v.y() / v.squared_norm();
-
- // Store for sorting
- order.push_back(std::make_pair(cos2, i));
- }
-
- // Sort points based on angle
- std::sort(order.begin(), order.end());
-
- // Triangulate polygon by connecting i_min with the ordered points
- triangulation.reserve((points.size() - 2)*3*2);
- const Point& p0 = points[i_min];
- for (std::size_t i = 0; i < points.size() - 2; i++)
- {
- const Point& p1 = points[order[i].second];
- const Point& p2 = points[order[i + 1].second];
- triangulation.push_back(p0.x());
- triangulation.push_back(p0.y());
- triangulation.push_back(p1.x());
- triangulation.push_back(p1.y());
- triangulation.push_back(p2.x());
- triangulation.push_back(p2.y());
- }
-
- return triangulation;
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_tetrahedron_tetrahedron
-(const std::vector<Point>& tet_0,
- const std::vector<Point>& tet_1)
-{
- // This algorithm computes the intersection of cell_0 and cell_1 by
- // returning a vector<double> with points describing a tetrahedral
- // mesh of the intersection. We will use the fact that the
- // intersection is a convex polyhedron. The algorithm works by first
- // identifying intersection points: vertex points inside a cell,
- // edge-face collision points and edge-edge collision points (the
- // edge-edge is a rare occurance). Having the intersection points,
- // we identify points that are coplanar and thus form a facet of the
- // polyhedron. These points are then used to form a tessellation of
- // triangles, which are used to form tetrahedra by the use of the
- // center point of the polyhedron. This center point is thus an
- // additional point not found on the polyhedron facets.
-
- // Tolerance for coplanar points
- const double coplanar_tol = 1000*DOLFIN_EPS_LARGE;
-
- // Tolerance for the tetrahedron determinant (otherwise problems
- // with warped tets)
- const double tet_det_tol = DOLFIN_EPS_LARGE;
-
- // Tolerance for duplicate points (p and q are the same if
- // (p-q).norm() < same_point_tol)
- const double same_point_tol = DOLFIN_EPS_LARGE;
-
- // Tolerance for small triangle (could be improved by identifying
- // sliver and small triangles)
- const double tri_det_tol = DOLFIN_EPS_LARGE;
-
- // Points in the triangulation (unique)
- std::vector<Point> points;
-
- // Node intersection
- for (int i = 0; i<4; ++i)
- {
- if (CollisionDetection::collides_tetrahedron_point(tet_0[0],
- tet_0[1],
- tet_0[2],
- tet_0[3],
- tet_1[i]))
- points.push_back(tet_1[i]);
-
- if (CollisionDetection::collides_tetrahedron_point(tet_1[0],
- tet_1[1],
- tet_1[2],
- tet_1[3],
- tet_0[i]))
- points.push_back(tet_0[i]);
- }
-
- // Edge face intersections
- std::vector<std::vector<std::size_t>> edges_0(6, std::vector<std::size_t>(2));
- edges_0[0][0] = 2;
- edges_0[0][1] = 3;
- edges_0[1][0] = 1;
- edges_0[1][1] = 3;
- edges_0[2][0] = 1;
- edges_0[2][1] = 2;
- edges_0[3][0] = 0;
- edges_0[3][1] = 3;
- edges_0[4][0] = 0;
- edges_0[4][1] = 2;
- edges_0[5][0] = 0;
- edges_0[5][1] = 1;
-
- std::vector<std::vector<std::size_t>> edges_1(6, std::vector<std::size_t>(2));
- edges_1[0][0] = 2;
- edges_1[0][1] = 3;
- edges_1[1][0] = 1;
- edges_1[1][1] = 3;
- edges_1[2][0] = 1;
- edges_1[2][1] = 2;
- edges_1[3][0] = 0;
- edges_1[3][1] = 3;
- edges_1[4][0] = 0;
- edges_1[4][1] = 2;
- edges_1[5][0] = 0;
- edges_1[5][1] = 1;
-
- std::vector<std::vector<std::size_t>> faces_0(4, std::vector<std::size_t>(3));
- faces_0[0][0] = 1;
- faces_0[0][1] = 2;
- faces_0[0][2] = 3;
- faces_0[1][0] = 0;
- faces_0[1][1] = 2;
- faces_0[1][2] = 3;
- faces_0[2][0] = 0;
- faces_0[2][1] = 1;
- faces_0[2][2] = 3;
- faces_0[3][0] = 0;
- faces_0[3][1] = 1;
- faces_0[3][2] = 2;
-
- std::vector<std::vector<std::size_t>> faces_1(4, std::vector<std::size_t>(3));
- faces_1[0][0] = 1;
- faces_1[0][1] = 2;
- faces_1[0][2] = 3;
- faces_1[1][0] = 0;
- faces_1[1][1] = 2;
- faces_1[1][2] = 3;
- faces_1[2][0] = 0;
- faces_1[2][1] = 1;
- faces_1[2][2] = 3;
- faces_1[3][0] = 0;
- faces_1[3][1] = 1;
- faces_1[3][2] = 2;
-
- // Loop over edges e and faces f
- for (std::size_t e = 0; e < 6; ++e)
- for (std::size_t f = 0; f < 4; ++f)
- {
- Point pta;
- if (intersection_face_edge(tet_0[faces_0[f][0]],
- tet_0[faces_0[f][1]],
- tet_0[faces_0[f][2]],
- tet_1[edges_1[e][0]],
- tet_1[edges_1[e][1]],
- pta))
- points.push_back(pta);
-
- Point ptb;
- if (intersection_face_edge(tet_1[faces_1[f][0]],
- tet_1[faces_1[f][1]],
- tet_1[faces_1[f][2]],
- tet_0[edges_0[e][0]],
- tet_0[edges_0[e][1]],
- ptb))
- points.push_back(ptb);
- }
-
- // Edge edge intersection
- Point pt;
- for (int i = 0; i < 6; ++i)
- for (int j = 0; j < 6; ++j)
- {
- if (intersection_edge_edge(tet_0[edges_0[i][0]],
- tet_0[edges_0[i][1]],
- tet_1[edges_1[j][0]],
- tet_1[edges_1[j][1]],
- pt))
- points.push_back(pt);
- }
-
- // Remove duplicate nodes
- std::vector<Point> tmp;
- tmp.reserve(points.size());
- for (std::size_t i = 0; i < points.size(); ++i)
- {
- bool different=true;
- for (std::size_t j = i+1; j < points.size(); ++j)
- {
- if ((points[i] - points[j]).norm() < same_point_tol) {
- different = false;
- break;
- }
- }
-
- if (different)
- tmp.push_back(points[i]);
- }
- points = tmp;
-
- // We didn't find sufficiently many points: can't form any
- // tetrahedra.
- if (points.size() < 4)
- return std::vector<double>();
-
- // Points forming the tetrahedral partitioning of the polyhedron. We
- // have 4 points per tetrahedron in three dimensions => 12 doubles
- // per tetrahedron.
- std::vector<double> triangulation;
-
- // Start forming a tessellation
- if (points.size() == 4)
- {
- // Include if determinant is sufficiently large. The determinant
- // can possibly be computed in a more stable way if needed.
- const double det = (points[3] - points[0]).dot
- ((points[1] - points[0]).cross(points[2] - points[0]));
-
- if (std::abs(det) > tet_det_tol)
- {
- if (det < -tet_det_tol)
- std::swap(points[0], points[1]);
-
- // One tet with four vertices in 3D gives 12 doubles
- triangulation.resize(12);
- for (std::size_t m = 0, idx = 0; m < 4; ++m)
- for (std::size_t d = 0; d < 3; ++d, ++idx)
- triangulation[idx] = points[m][d];
- }
- // Note: this can be empty if the tetrahedron was not sufficiently
- // large
- return triangulation;
- }
-
- // Tetrahedra are created using the facet points and a center point.
- Point polyhedroncenter = points[0];
- for (std::size_t i = 1; i < points.size(); ++i)
- polyhedroncenter += points[i];
- polyhedroncenter /= points.size();
-
- // Data structure for storing checked triangle indices (do this
- // better with some fancy stl structure?)
- const std::size_t N = points.size(), N2 = points.size()*points.size();
- std::vector<bool> checked(N*N2 + N2 + N, false);
-
- // Find coplanar points
- for (std::size_t i = 0; i < N; ++i)
- for (std::size_t j = i+1; j < N; ++j)
- for (std::size_t k = 0; k < N; ++k)
- if (!checked[i*N2 + j*N + k] and k != i and k != j)
- {
- // Check that triangle area is sufficiently large
- Point n = (points[j] - points[i]).cross(points[k] - points[i]);
- const double tridet = n.norm();
- if (tridet < tri_det_tol)
- break;
-
- // Normalize normal
- n /= tridet;
-
- // Compute triangle center
- const Point tricenter = (points[i] + points[j] + points[k]) / 3.;
-
- // Check whether all other points are on one side of thus
- // facet. Initialize as true for the case of only three
- // coplanar points.
- bool on_convex_hull = true;
-
- // Compute dot products to check which side of the plane
- // (i,j,k) we're on. Note: it seems to be better to compute
- // n.dot(points[m]-n.dot(tricenter) rather than
- // n.dot(points[m]-tricenter).
- std::vector<double> ip(N, -(n.dot(tricenter)));
- for (std::size_t m = 0; m < N; ++m)
- ip[m] += n.dot(points[m]);
-
- // Check inner products range by finding max & min (this
- // seemed possibly more numerically stable than checking all
- // vs all and then break).
- double minip = 9e99, maxip = -9e99;
- for (size_t m = 0; m < N; ++m)
- if (m != i and m != j and m != k)
- {
- minip = (minip > ip[m]) ? ip[m] : minip;
- maxip = (maxip < ip[m]) ? ip[m] : maxip;
- }
-
- // Different sign => triangle is not on the convex hull
- if (minip*maxip < -DOLFIN_EPS)
- on_convex_hull = false;
-
- if (on_convex_hull)
- {
- // Find all coplanar points on this facet given the
- // tolerance coplanar_tol
- std::vector<std::size_t> coplanar;
- for (std::size_t m = 0; m < N; ++m)
- if (std::abs(ip[m]) < coplanar_tol)
- coplanar.push_back(m);
-
- // Mark this plane (how to do this better?)
- for (std::size_t m = 0; m < coplanar.size(); ++m)
- for (std::size_t n = m+1; n < coplanar.size(); ++n)
- for (std::size_t o = n+1; o < coplanar.size(); ++o)
- checked[coplanar[m]*N2 + coplanar[n]*N + coplanar[o]]
- = checked[coplanar[m]*N2 + coplanar[o]*N + coplanar[n]]
- = checked[coplanar[n]*N2 + coplanar[m]*N + coplanar[o]]
- = checked[coplanar[n]*N2 + coplanar[o]*N + coplanar[m]]
- = checked[coplanar[o]*N2 + coplanar[n]*N + coplanar[m]]
- = checked[coplanar[o]*N2 + coplanar[m]*N + coplanar[n]]
- = true;
-
- // Do the actual tessellation using the coplanar points and
- // a center point
- if (coplanar.size() == 3)
- {
- // Form one tetrahedron
- std::vector<Point> cand(4);
- cand[0] = points[coplanar[0]];
- cand[1] = points[coplanar[1]];
- cand[2] = points[coplanar[2]];
- cand[3] = polyhedroncenter;
-
- // Include if determinant is sufficiently large
- const double det = (cand[3]-cand[0]).dot
- ((cand[1] - cand[0]).cross(cand[2] - cand[0]));
- if (std::abs(det) > tet_det_tol)
- {
- if (det < -tet_det_tol)
- std::swap(cand[0], cand[1]);
-
- for (std::size_t m = 0; m < 4; ++m)
- for (std::size_t d = 0; d < 3; ++d)
- triangulation.push_back(cand[m][d]);
- }
-
- }
- else if (coplanar.size() > 3)
- {
- // Tessellate as in the triangle-triangle intersection
- // case: First sort points using a Graham scan, then
- // connect to form triangles. Finally form tetrahedra
- // using the center of the polyhedron.
-
- // Use the center of the coplanar points and point no 0
- // as reference for the angle calculation
- Point pointscenter = points[coplanar[0]];
- for (std::size_t m = 1; m < coplanar.size(); ++m)
- pointscenter += points[coplanar[m]];
- pointscenter /= coplanar.size();
-
- std::vector<std::pair<double, std::size_t>> order;
- Point ref = points[coplanar[0]] - pointscenter;
- ref /= ref.norm();
-
- // Calculate and store angles
- for (std::size_t m = 1; m < coplanar.size(); ++m)
- {
- const Point v = points[coplanar[m]] - pointscenter;
- const double frac = ref.dot(v) / v.norm();
- double alpha;
- if (frac <= -1)
- alpha=DOLFIN_PI;
- else if (frac>=1)
- alpha=0;
- else
- {
- alpha = acos(frac);
- if (v.dot(n.cross(ref)) < 0)
- alpha = 2*DOLFIN_PI-alpha;
- }
- order.push_back(std::make_pair(alpha, m));
- }
-
- // Sort angles
- std::sort(order.begin(), order.end());
-
- // Tessellate
- for (std::size_t m = 0; m < coplanar.size()-2; ++m)
- {
- // Candidate tetrahedron:
- std::vector<Point> cand(4);
- cand[0] = points[coplanar[0]];
- cand[1] = points[coplanar[order[m].second]];
- cand[2] = points[coplanar[order[m + 1].second]];
- cand[3] = polyhedroncenter;
-
- // Include tetrahedron if determinant is "large"
- const double det = (cand[3] - cand[0]).dot
- ((cand[1] - cand[0]).cross(cand[2] - cand[0]));
- if (std::abs(det) > tet_det_tol)
- {
- if (det < -tet_det_tol)
- std::swap(cand[0], cand[1]);
- for (std::size_t n = 0; n < 4; ++n)
- for (std::size_t d = 0; d < 3; ++d)
- triangulation.push_back(cand[n][d]);
- }
- }
- }
- }
- }
-
-
- return triangulation;
-
-
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection_tetrahedron_triangle
-(const std::vector<Point>& tet,
- const std::vector<Point>& tri)
-{
- // This code mimics the
- // triangulate_intersection_tetrahedron_tetrahedron and the
- // triangulate_intersection_tetrahedron_tetrahedron_triangle_codes:
- // we first identify triangle nodes in the tetrahedra. Them we
- // continue with edge-face detection for the four faces of the
- // tetrahedron and the triangle. The points found are used to form a
- // triangulation by first sorting them using a Graham scan.
-
- // Tolerance for duplicate points (p and q are the same if
- // (p-q).norm() < same_point_tol)
- const double same_point_tol = DOLFIN_EPS_LARGE;
-
- // Tolerance for small triangle (could be improved by identifying
- // sliver and small triangles)
- const double tri_det_tol = DOLFIN_EPS_LARGE;
-
- std::vector<Point> points;
-
- // Triangle node in tetrahedron intersection
- for (std::size_t i = 0; i < 3; ++i)
- if (CollisionDetection::collides_tetrahedron_point(tet[0],
- tet[1],
- tet[2],
- tet[3],
- tri[i]))
- points.push_back(tri[i]);
-
- // Check if a tetrahedron edge intersects the triangle
- std::vector<std::vector<int>> tet_edges(6, std::vector<int>(2));
- tet_edges[0][0] = 2;
- tet_edges[0][1] = 3;
- tet_edges[1][0] = 1;
- tet_edges[1][1] = 3;
- tet_edges[2][0] = 1;
- tet_edges[2][1] = 2;
- tet_edges[3][0] = 0;
- tet_edges[3][1] = 3;
- tet_edges[4][0] = 0;
- tet_edges[4][1] = 2;
- tet_edges[5][0] = 0;
- tet_edges[5][1] = 1;
-
- Point pt;
- for (std::size_t e = 0; e < 6; ++e)
- if (intersection_face_edge(tri[0], tri[1], tri[2],
- tet[tet_edges[e][0]],
- tet[tet_edges[e][1]],
- pt))
- points.push_back(pt);
-
- // Check if a triangle edge intersects a tetrahedron face
- std::vector<std::vector<std::size_t>>
- tet_faces(4, std::vector<std::size_t>(3));
-
- tet_faces[0][0] = 1;
- tet_faces[0][1] = 2;
- tet_faces[0][2] = 3;
- tet_faces[1][0] = 0;
- tet_faces[1][1] = 2;
- tet_faces[1][2] = 3;
- tet_faces[2][0] = 0;
- tet_faces[2][1] = 1;
- tet_faces[2][2] = 3;
- tet_faces[3][0] = 0;
- tet_faces[3][1] = 1;
- tet_faces[3][2] = 2;
-
- for (std::size_t f = 0; f < 4; ++f)
- {
- if (intersection_face_edge(tet[tet_faces[f][0]],
- tet[tet_faces[f][1]],
- tet[tet_faces[f][2]],
- tri[0], tri[1],
- pt))
- points.push_back(pt);
- if (intersection_face_edge(tet[tet_faces[f][0]],
- tet[tet_faces[f][1]],
- tet[tet_faces[f][2]],
- tri[0], tri[2],
- pt))
- points.push_back(pt);
- if (intersection_face_edge(tet[tet_faces[f][0]],
- tet[tet_faces[f][1]],
- tet[tet_faces[f][2]],
- tri[1], tri[2],
- pt))
- points.push_back(pt);
- }
-
-
- // edge edge intersection
- for (std::size_t f = 0; f < 6; ++f)
- {
- if (intersection_edge_edge(tet[tet_edges[f][0]],
- tet[tet_edges[f][1]],
- tri[0], tri[1],
- pt))
- points.push_back(pt);
- if (intersection_edge_edge(tet[tet_edges[f][0]],
- tet[tet_edges[f][1]],
- tri[0], tri[2],
- pt))
- points.push_back(pt);
- if (intersection_edge_edge(tet[tet_edges[f][0]],
- tet[tet_edges[f][1]],
- tri[1], tri[2],
- pt))
- points.push_back(pt);
- }
-
-
-
- // Remove duplicate nodes
- std::vector<Point> tmp;
- tmp.reserve(points.size());
-
- for (std::size_t i = 0; i < points.size(); ++i)
- {
- bool different = true;
- for (std::size_t j = i+1; j < points.size(); ++j)
- if ((points[i] - points[j]).norm() < same_point_tol)
- {
- different = false;
- break;
- }
- if (different)
- tmp.push_back(points[i]);
- }
- points = tmp;
-
- // We didn't find sufficiently many points
- if (points.size() < 3)
- return std::vector<double>();
-
- std::vector<double> triangulation;
-
- Point n = (points[2] - points[0]).cross(points[1] - points[0]);
- const double det = n.norm();
- n /= det;
-
- if (points.size() == 3) {
- // Include if determinant is sufficiently large
- if (det > tri_det_tol)
- {
- // One triangle with three vertices in 3D gives 9 doubles
- triangulation.resize(9);
- for (std::size_t m = 0, idx = 0; m < 3; ++m)
- for (std::size_t d = 0; d < 3; ++d, ++idx)
- triangulation[idx] = points[m][d];
- }
- return triangulation;
- }
-
- // Tessellate as in the triangle-triangle intersection case: First
- // sort points using a Graham scan, then connect to form triangles.
-
- // Use the center of the points and point no 0 as reference for the
- // angle calculation
- Point pointscenter = points[0];
- for (std::size_t m = 1; m < points.size(); ++m)
- pointscenter += points[m];
- pointscenter /= points.size();
-
- std::vector<std::pair<double, std::size_t>> order;
- Point ref = points[0]-pointscenter;
- ref /= ref.norm();
-
- // Calculate and store angles
- for (std::size_t m = 1; m < points.size(); ++m)
- {
- const Point v = points[m] - pointscenter;
- const double frac = ref.dot(v) / v.norm();
- double alpha;
- if (frac <= -1)
- alpha = DOLFIN_PI;
- else if (frac >= 1)
- alpha = 0;
- else
- {
- alpha = acos(frac);
- if (v.dot(n.cross(ref)) < 0)
- alpha = 2*DOLFIN_PI-alpha;
- }
- order.push_back(std::make_pair(alpha, m));
- }
-
- // Sort angles
- std::sort(order.begin(), order.end());
-
- // Tessellate
- std::vector<Point> cand(3);
- for (std::size_t m = 0; m < order.size()-1; ++m)
- {
- // Candidate triangle
- cand[0] = points[0];
- cand[1] = points[order[m].second];
- cand[2] = points[order[m + 1].second];
-
- // Include triangle if determinant is sufficiently large
- const double det = ((cand[2] - cand[1]).cross(cand[1] - cand[0])).norm();
- if (det > tri_det_tol)
- {
- for (std::size_t n = 0; n < 3; ++n)
- for (std::size_t d = 0; d < 3; ++d)
- triangulation.push_back(cand[n][d]);
- }
- }
-
- return triangulation;
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntersectionTriangulation::triangulate_intersection
-(const MeshEntity &cell,
- const std::vector<double> &triangulation,
- std::size_t tri_tdim)
-{
- // Compute the triangulation of the intersection of the cell and the
- // simplices of the flat triangulation vector with topology tdim.
-
- std::vector<double> total_triangulation;
-
- // Get dimensions (geometrical dimension assumed to be the same)
- const std::size_t cell_tdim = cell.mesh().topology().dim();
- const std::size_t gdim = cell.mesh().geometry().dim();
-
- // Store cell as std::vector<Point>
- // FIXME: Store as Point& ?
- std::vector<Point> simplex_cell(cell_tdim+1);
- const MeshGeometry& geometry = cell.mesh().geometry();
- const unsigned int* vertices = cell.entities(0);
- for (std::size_t j = 0; j < cell_tdim+1; ++j)
- simplex_cell[j] = geometry.point(vertices[j]);
-
- // Simplex in triangulation
- std::vector<Point> simplex(tri_tdim+1);
- const std::size_t offset = (tri_tdim+1)*gdim;
-
- // Loop over all simplices
- for (std::size_t i = 0; i < triangulation.size()/offset; ++i)
- {
- // Store simplices as std::vector<Point>
- for (std::size_t j = 0; j < tri_tdim+1; ++j)
- for (std::size_t d = 0; d < gdim; ++d)
- simplex[j][d] = triangulation[offset*i+gdim*j+d];
-
- // Compute intersection
- std::vector<double> local_triangulation
- = triangulate_intersection(simplex_cell, cell_tdim,
- simplex, tri_tdim,
- gdim);
-
- // Add these to the net triangulation
- total_triangulation.insert(total_triangulation.end(),
- local_triangulation.begin(),
- local_triangulation.end());
- }
-
- return total_triangulation;
-}
-//-----------------------------------------------------------------------------
-void
-IntersectionTriangulation::triangulate_intersection
-(const MeshEntity &cell,
- const std::vector<double> &triangulation,
- const std::vector<Point>& normals,
- std::vector<double>& intersection_triangulation,
- std::vector<Point>& intersection_normals,
- std::size_t tri_tdim)
-{
- // Compute the triangulation of the intersection of the cell and the
- // simplices of the flat triangulation vector with topology tdim.
-
- // FIXME: clear or not?
- // intersection_triangulation.clear();
- // intersection_normals.clear();
-
- // Get dimensions (geometrical dimension assumed to be the same)
- const std::size_t cell_tdim = cell.mesh().topology().dim();
- const std::size_t gdim = cell.mesh().geometry().dim();
-
- // Store cell as std::vector<Point>
- // FIXME: Store as Point& ?
- std::vector<Point> simplex_cell(cell_tdim+1);
- const MeshGeometry& geometry = cell.mesh().geometry();
- const unsigned int* vertices = cell.entities(0);
- for (std::size_t j = 0; j < cell_tdim+1; ++j)
- simplex_cell[j] = geometry.point(vertices[j]);
-
- // Simplex in triangulation
- std::vector<Point> simplex(tri_tdim+1);
- const std::size_t offset = (tri_tdim+1)*gdim;
-
- // Loop over all simplices
- for (std::size_t i = 0; i < triangulation.size()/offset; ++i)
- {
- // Store simplices as std::vector<Point>
- for (std::size_t j = 0; j < tri_tdim+1; ++j)
- for (std::size_t d = 0; d < gdim; ++d)
- simplex[j][d] = triangulation[offset*i+gdim*j+d];
-
- // Compute intersection
- std::vector<double> local_triangulation
- = triangulate_intersection(simplex_cell, cell_tdim,
- simplex, tri_tdim,
- gdim);
-
- // Add these to the net triangulation
- intersection_triangulation.insert(intersection_triangulation.end(),
- local_triangulation.begin(),
- local_triangulation.end());
-
- // Add the normal
- intersection_normals.resize(intersection_normals.size() + local_triangulation.size()/offset,
- normals[i]);
- }
-
-}
-//-----------------------------------------------------------------------------
-bool
-IntersectionTriangulation::intersection_edge_edge(const Point& a,
- const Point& b,
- const Point& c,
- const Point& d,
- Point& pt)
-{
- // Check if two edges are the same
- const double same_point_tol = DOLFIN_EPS_LARGE;
- if ((a - c).norm() < same_point_tol and
- (b - d).norm() < same_point_tol)
- return false;
- if ((a - d).norm() < same_point_tol and
- (b - c).norm() < same_point_tol)
- return false;
-
- // Tolerance for orthogonality
- const double orth_tol = DOLFIN_EPS_LARGE;
-
- // Tolerance for coplanarity
- const double coplanar_tol = DOLFIN_EPS_LARGE;
-
- const Point L1 = b - a;
- const Point L2 = d - c;
- const Point ca = c - a;
- const Point n = L1.cross(L2);
-
- // Check if L1 and L2 are coplanar (what if they're overlapping?)
- if (std::abs(ca.dot(n)) > coplanar_tol)
- return false;
-
- // Find orthogonal plane with normal n1
- const Point n1 = n.cross(L1);
- const double n1dotL2 = n1.dot(L2);
-
- // If we have orthogonality
- if (std::abs(n1dotL2) > orth_tol)
- {
- const double t = n1.dot(a - c) / n1dotL2;
-
- // Find orthogonal plane with normal n2
- const Point n2 = n.cross(L2);
- const double n2dotL1 = n2.dot(L1);
- if (t >= 0 and
- t <= 1 and
- std::abs(n2dotL1) > orth_tol)
- {
- const double s = n2.dot(c - a) / n2dotL1;
- if (s >= 0 and
- s <= 1)
- {
- pt = a + s*L1;
- return true;
- }
- }
- }
-
- return false;
-}
-//-----------------------------------------------------------------------------
-bool
-IntersectionTriangulation::intersection_face_edge(const Point& r,
- const Point& s,
- const Point& t,
- const Point& a,
- const Point& b,
- Point& pt)
-{
- // This standard edge face intersection test is as follows:
- // - Check if end points of the edge (a,b) on opposite side of plane
- // given by the face (r,s,t)
- // - If we have sign change, compute intersection with plane.
- // - Check if computed point is on triangle given by face.
-
- // If the edge and the face are in the same plane, we return false
- // and leave this to the edge-edge intersection test.
-
- // Tolerance for edge and face in plane (topologically 2D problem)
- const double top_2d_tol = DOLFIN_EPS_LARGE;
-
- // Compute normal
- const Point rs = s - r;
- const Point rt = t - r;
- Point n = rs.cross(rt);
- n /= n.norm();
-
- // Check sign change (note that if either dot product is zero it's
- // orthogonal)
- const double da = n.dot(a - r);
- const double db = n.dot(b - r);
-
- // Note: if da and db we may have edge intersection (detected in
- // other routine)
- if (da*db > 0)
- return false;
-
- // Face and edge are in topological 2d: taken care of in edge-edge
- // intersection or point in simplex.
- const double sum = std::abs(da) + std::abs(db);
- if (sum < top_2d_tol)
- return false;
-
- // Calculate intersection
- pt = a + std::abs(da) / sum * (b - a);
-
- // Check if point is in triangle by calculating and checking
- // barycentric coords.
- const double d00 = rs.squared_norm();
- const double d01 = rs.dot(rt);
- const double d11 = rt.squared_norm();
- const Point e2 = pt-r;
- const double d20 = e2.dot(rs);
- const double d21 = e2.dot(rt);
- const double invdet = 1. / (d00*d11 - d01*d01);
- const double v = (d11*d20 - d01*d21)*invdet;
- if (v < 0.)
- return false;
-
- const double w = (d00*d21 - d01*d20)*invdet;
- if (w < 0.)
- return false;
-
- if (v+w > 1.)
- return false;
-
- return true;
-}
-//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/IntersectionTriangulation.h b/dolfin/geometry/IntersectionTriangulation.h
deleted file mode 100644
index c76c03f..0000000
--- a/dolfin/geometry/IntersectionTriangulation.h
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright (C) 2014 Anders Logg and August Johansson
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2014-02-03
-// Last changed: 2014-05-28
-
-#include <vector>
-#include <dolfin/log/log.h>
-
-#ifndef __INTERSECTION_TRIANGULATION_H
-#define __INTERSECTION_TRIANGULATION_H
-
-namespace dolfin
-{
-
- // Forward declarations
- class MeshEntity;
-
- /// This class implements algorithms for computing triangulations of
- /// pairwise intersections of simplices.
-
- class IntersectionTriangulation
- {
- public:
-
- /// Compute triangulation of intersection of two entities
- ///
- /// @param entity_0 (_MeshEntity_)
- /// The first entity.
- /// @param entity_1 (_MeshEntity_)
- /// The second entity.
- ///
- /// @return std::vector<double>
- /// A flattened array of simplices of dimension
- /// num_simplices x num_vertices x gdim =
- /// num_simplices x (tdim + 1) x gdim
- static std::vector<double>
- triangulate_intersection(const MeshEntity& entity_0,
- const MeshEntity& entity_1);
-
- /// Compute triangulation of intersection of two intervals
- ///
- /// @param interval_0 (_MeshEntity_)
- /// The first interval.
- /// @param interval_1 (_MeshEntity_)
- /// The second interval.
- ///
- /// @return std::vector<double>
- /// A flattened array of simplices of dimension
- /// num_simplices x num_vertices x gdim =
- /// num_simplices x (tdim + 1) x gdim
- static std::vector<double>
- triangulate_intersection_interval_interval(const MeshEntity& interval_0,
- const MeshEntity& interval_1);
-
- /// Compute triangulation of intersection of a triangle and an interval
- ///
- /// @param triangle (_MeshEntity_)
- /// The triangle.
- /// @param interval (_MeshEntity_)
- /// The interval.
- ///
- /// @return std::vector<double>
- /// A flattened array of simplices of dimension
- /// num_simplices x num_vertices x gdim =
- /// num_simplices x (tdim + 1) x gdim
- static std::vector<double>
- triangulate_intersection_triangle_interval(const MeshEntity& triangle,
- const MeshEntity& interval);
-
- /// Compute triangulation of intersection of two triangles
- ///
- /// @param triangle_0 (_MeshEntity_)
- /// The first triangle.
- /// @param triangle_1 (_MeshEntity_)
- /// The second triangle.
- ///
- /// @return std::vector<double>
- /// A flattened array of simplices of dimension
- /// num_simplices x num_vertices x gdim =
- /// num_simplices x (tdim + 1) x gdim
- static std::vector<double>
- triangulate_intersection_triangle_triangle(const MeshEntity& triangle_0,
- const MeshEntity& triangle_1);
-
- /// Compute triangulation of intersection of a tetrahedron and a triangle
- ///
- /// @param tetrahedron (_MeshEntity_)
- /// The tetrahedron.
- /// @param triangle (_MeshEntity_)
- /// The triangle
- ///
- /// @return std::vector<double>
- /// A flattened array of simplices of dimension
- /// num_simplices x num_vertices x gdim =
- /// num_simplices x (tdim + 1) x gdim
- static std::vector<double>
- triangulate_intersection_tetrahedron_triangle(const MeshEntity& tetrahedron,
- const MeshEntity& triangle);
-
- /// Compute triangulation of intersection of two tetrahedra
- ///
- /// @param tetrahedron_0 (_MeshEntity_)
- /// The first tetrahedron.
- /// @param tetrahedron_1 (_MeshEntity_)
- /// The second tetrahedron.
- ///
- /// @return std::vector<double>
- /// A flattened array of simplices of dimension
- /// num_simplices x num_vertices x gdim =
- /// num_simplices x (tdim + 1) x gdim
- static std::vector<double>
- triangulate_intersection_tetrahedron_tetrahedron(const MeshEntity& tetrahedron_0,
- const MeshEntity& tetrahedron_1);
-
- /// Function for general intersection computation of two simplices
- /// with different topological dimension but the same geometrical
- /// dimension
- static std::vector<double>
- triangulate_intersection(const std::vector<Point>& s0,
- std::size_t tdim0,
- const std::vector<Point>& s1,
- std::size_t tdim1,
- std::size_t gdim);
-
- /// Function for computing the intersection of a cell with a flat
- /// vector of simplices with topological dimension tdim. The
- /// geometrical dimension is assumed to be the same as for the
- /// cell.
- static std::vector<double>
- triangulate_intersection(const MeshEntity& cell,
- const std::vector<double> &triangulation,
- std::size_t tdim);
-
- /// Function for computing the intersection of a cell with a flat
- /// vector of simplices with topological dimension tdim. The
- /// geometrical dimension is assumed to be the same as for the
- /// cell. The corresponding normals are also saved.
- static void
- triangulate_intersection(const MeshEntity& cell,
- const std::vector<double>& triangulation,
- const std::vector<Point>& normals,
- std::vector<double>& intersection_triangulation,
- std::vector<Point>& intersection_normals,
- std::size_t tdim);
-
- private:
-
- // Function for computing the intersection of two triangles given
- // by std::vector<Point>.
- static std::vector<double>
- triangulate_intersection_interval_interval(const std::vector<Point>& interval_0,
- const std::vector<Point>& interval_1,
- std::size_t gdim);
-
- // Function for computing the intersection of a triangle and an interval
- // by std::vector<Point>.
- static std::vector<double>
- triangulate_intersection_triangle_interval(const std::vector<Point>& triangle,
- const std::vector<Point>& interval,
- std::size_t gdim);
-
- // Function for computing the intersection of two triangles given
- // by std::vector<Point>.
- static std::vector<double>
- triangulate_intersection_triangle_triangle(const std::vector<Point>& tri_0,
- const std::vector<Point>& tri_1);
-
- // Function for computing the intersection of two tetrahedra given
- // by std::vector<Point>.
- static std::vector<double>
- triangulate_intersection_tetrahedron_tetrahedron(const std::vector<Point>& tet_0,
- const std::vector<Point>& tet_1);
-
- // Function for computing the intersection of a tetrahedron with a
- // triangle given by std::vector<Point>.
- static std::vector<double>
- triangulate_intersection_tetrahedron_triangle(const std::vector<Point>& tet,
- const std::vector<Point>& tri);
-
- // Helper function
- static bool intersection_edge_edge(const Point& a,
- const Point& b,
- const Point& c,
- const Point& d,
- Point& pt);
-
- // Helper function
- static bool intersection_face_edge(const Point& r,
- const Point& s,
- const Point& t,
- const Point& a,
- const Point& b,
- Point& pt);
-
- };
-
-}
-
-#endif
diff --git a/dolfin/geometry/Point.h b/dolfin/geometry/Point.h
index 2dc6ad9..dc2ca33 100644
--- a/dolfin/geometry/Point.h
+++ b/dolfin/geometry/Point.h
@@ -19,7 +19,7 @@
// Modified by Andre Massing 2009
//
// First added: 2006-06-12
-// Last changed: 2014-06-06
+// Last changed: 2017-09-28
#ifndef __POINT_H
#define __POINT_H
@@ -193,6 +193,14 @@ namespace dolfin
const Point& operator= (const Point& p)
{ _x = {{p._x[0], p._x[1], p._x[2]}}; return *this; }
+ /// Equal to operator
+ bool operator== (const Point& p) const
+ { return _x == p._x; }
+
+ /// Not equal to operator
+ bool operator!= (const Point& p) const
+ { return _x != p._x; }
+
/// Compute squared distance to given point
///
/// @param p (Point)
diff --git a/dolfin/geometry/SimplexQuadrature.cpp b/dolfin/geometry/SimplexQuadrature.cpp
index 5c56b91..d0de5df 100644
--- a/dolfin/geometry/SimplexQuadrature.cpp
+++ b/dolfin/geometry/SimplexQuadrature.cpp
@@ -14,43 +14,73 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2014-02-24
-// Last changed: 2014-04-25
#include <dolfin/log/log.h>
#include <dolfin/mesh/Cell.h>
#include <dolfin/mesh/Mesh.h>
#include <dolfin/mesh/MeshGeometry.h>
#include "SimplexQuadrature.h"
+#include "predicates.h"
using namespace dolfin;
//-----------------------------------------------------------------------------
+SimplexQuadrature::SimplexQuadrature(std::size_t tdim, std::size_t order)
+{
+ // Create and store quadrature rule for reference simplex
+ switch (tdim)
+ {
+ case 1:
+ setup_qr_reference_interval(order);
+ break;
+ case 2:
+ setup_qr_reference_triangle(order);
+ break;
+ case 3:
+ setup_qr_reference_tetrahedron(order);
+ break;
+ default:
+ dolfin_error("SimplexQuadrature.cpp",
+ "setup quadrature rule for reference simplex",
+ "Only implemented for topological dimension 1, 2, 3");
+ }
+
+}
+//-----------------------------------------------------------------------------
std::pair<std::vector<double>, std::vector<double>>
SimplexQuadrature::compute_quadrature_rule(const Cell& cell,
- std::size_t order)
+ std::size_t order) const
{
// Extract dimensions
const std::size_t tdim = cell.mesh().topology().dim();
const std::size_t gdim = cell.mesh().geometry().dim();
// Get vertex coordinates
- std::vector<double> coordinates;
- cell.get_vertex_coordinates(coordinates);
+ std::vector<double> x;
+ cell.get_coordinate_dofs(x);
+
+ // Convert to std::vector<Point>
+ std::vector<Point> s(tdim + 1);
+ for (std::size_t t = 0; t < tdim + 1; ++t)
+ for (std::size_t d = 0; d < gdim; ++d)
+ s[t][d] = x[gdim*t + d];
// Call function to compute quadrature rule
- return compute_quadrature_rule(&coordinates[0], tdim, gdim, order);
+ return compute_quadrature_rule(s, gdim, order);
}
//-----------------------------------------------------------------------------
std::pair<std::vector<double>, std::vector<double>>
- SimplexQuadrature::compute_quadrature_rule(const double* coordinates,
- std::size_t tdim,
+ SimplexQuadrature::compute_quadrature_rule(const std::vector<Point>& coordinates,
std::size_t gdim,
- std::size_t order)
+ std::size_t order) const
{
+ std::size_t tdim = coordinates.size() - 1;
+
switch (tdim)
{
+ case 0:
+ // FIXME: should we return empty qr or should we have detected this earlier?
+ break;
case 1:
return compute_quadrature_rule_interval(coordinates, gdim, order);
break;
@@ -71,549 +101,3591 @@ std::pair<std::vector<double>, std::vector<double>>
}
//-----------------------------------------------------------------------------
std::pair<std::vector<double>, std::vector<double>>
-SimplexQuadrature::compute_quadrature_rule_interval(const double* coordinates,
- std::size_t gdim,
- std::size_t order)
+ SimplexQuadrature::compute_quadrature_rule_interval(const std::vector<Point>& coordinates,
+ std::size_t gdim,
+ std::size_t order) const
{
+ log(PROGRESS, "Create quadrature rule using given interval coordinates");
+
std::pair<std::vector<double>, std::vector<double>> quadrature_rule;
- // Weights and points in local coordinates on [-1, 1]
- std::vector<double> w, p;
+ // Find the determinant of the Jacobian (inspired by ufc_geometry.h)
+ double det = -1;
- switch (order)
+ switch (gdim)
{
case 1:
- // Assign weight 2, point 0
- w.assign(1, 2.);
- p.assign(1, 0.);
-
- break;
+ {
+ det = coordinates[1].x() - coordinates[0].x();
+ break;
+ }
case 2:
- // Assign weights 1.
- w.assign(2, 1.);
-
- // Assign points corresponding to -1/sqrt(3) and 1/sqrt(3)
- p.resize(2);
- p[0] = -1./std::sqrt(3);
- p[1] = 1./std::sqrt(3);
-
- break;
- case 3:
- // Assign weights
- w = { 5./9, 8./9, 5./9 };
-
- // Assign points
- p = { -std::sqrt(3./5), 0., std::sqrt(3./5) };
-
- break;
- case 4:
- // Assign weights
- w.resize(4);
- w[0] = (18 - std::sqrt(30)) / 36;
- w[1] = (18 + std::sqrt(30)) / 36;
- w[2] = w[1];
- w[3] = w[0];
-
- // Assign points
- p.resize(4);
- p[0] = -std::sqrt(3./7 + 2./7*std::sqrt(6./5));
- p[1] = -std::sqrt(3./7 - 2./7*std::sqrt(6./5));
- p[2] = -p[1];
- p[3] = -p[0];
-
- break;
- case 5:
- // Assign weights
- w = {
- 0.2369268850561890875142640,
- 0.4786286704993664680412915,
- 0.5688888888888888888888889,
- 0.4786286704993664680412915,
- 0.2369268850561890875142640 };
-
- // Assign points
- p = {
- -0.9061798459386639927976269,
- -0.5384693101056830910363144,
- 0.0000000000000000000000000,
- 0.5384693101056830910363144,
- 0.9061798459386639927976269 };
-
- break;
- case 6:
- // Assign weights
- w = {0.1713244923791703450402961,
- 0.3607615730481386075698335,
- 0.4679139345726910473898703,
- 0.4679139345726910473898703,
- 0.3607615730481386075698335,
- 0.1713244923791703450402961};
-
- // Assign points
- p = {
- -0.9324695142031520278123016,
- -0.6612093864662645136613996,
- -0.2386191860831969086305017,
- 0.2386191860831969086305017 ,
- 0.6612093864662645136613996 ,
- 0.9324695142031520278123016
- };
-
+ {
+ const std::array<double, 2> J = {{coordinates[1].x() - coordinates[0].x(),
+ coordinates[1].y() - coordinates[0].y()}};
+ const double det2 = J[0]*J[0] + J[1]*J[1];
+ det = std::sqrt(det2);
break;
- default:
- dolfin_error("SimplexQuadrature.cpp",
- "compute quadrature rule for interval",
- "Not implemented for order ",order);
}
-
- // Find the determinant of the Jacobian (inspired by ufc_geometry.h)
- double det;
-
- switch (gdim)
+ case 3:
{
- case 1:
- det = coordinates[1] - coordinates[0];
+ const std::array<double, 3> J = {{coordinates[1].x() - coordinates[0].x(),
+ coordinates[1].y() - coordinates[0].y(),
+ coordinates[1].z() - coordinates[0].z()}};
+ const double det2 = J[0]*J[0] + J[1]*J[1];
+ det = std::sqrt(det2);
break;
-
- case 2:
- {
- const double J[] = {coordinates[2] - coordinates[0],
- coordinates[3] - coordinates[1]};
- const double det2 = J[0]*J[0] + J[1]*J[1];
- det = std::sqrt(det2);
- break;
- }
- case 3:
- {
- const double J[] = {coordinates[3] - coordinates[0],
- coordinates[4] - coordinates[1],
- coordinates[5] - coordinates[2]};
- const double det2 = J[0]*J[0] + J[1]*J[1] + J[2]*J[2];
- det = std::sqrt(det2);
- break;
- }
+ }
default:
dolfin_error("SimplexQuadrature.cpp",
"compute quadrature rule for interval",
- "Not implemented for dimension ", gdim);
+ "Not implemented for dimension %d", gdim);
}
- // Map (local) quadrature points
- quadrature_rule.first.resize(gdim*p.size());
- for (std::size_t i = 0; i < p.size(); ++i)
+ // Map (local) quadrature points (note that _p is a
+ // std::vector<std::vector<double> >)
+ quadrature_rule.first.resize(gdim*_p[0].size());
+ for (std::size_t i = 0; i < _p[0].size(); ++i)
{
for (std::size_t d = 0; d < gdim; ++d)
{
quadrature_rule.first[d + i*gdim]
- = 0.5*(coordinates[d]*(1 - p[i]) + coordinates[gdim + d]*(1 + p[i]));
+ = 0.5*(coordinates[0][d]*(1. - _p[0][i])
+ + coordinates[1][d]*(1. + _p[0][i]));
+ dolfin_assert(std::isfinite(quadrature_rule.first[d + i*gdim]));
}
}
+ dolfin_assert(det >= 0);
// Store weights
- quadrature_rule.second.assign(w.size(), 0.5*std::abs(det));
- for (std::size_t i = 0; i < w.size(); ++i)
- quadrature_rule.second[i] *= w[i];
+ quadrature_rule.second.assign(_w.size(), 0.5*std::abs(det));
+ for (std::size_t i = 0; i < _w.size(); ++i)
+ {
+ quadrature_rule.second[i] *= _w[i];
+ dolfin_assert(std::isfinite(quadrature_rule.second[i]));
+ }
+ dolfin_assert(quadrature_rule.first.size() == gdim*quadrature_rule.second.size());
return quadrature_rule;
}
//-----------------------------------------------------------------------------
std::pair<std::vector<double>, std::vector<double>>
-SimplexQuadrature::compute_quadrature_rule_triangle(const double* coordinates,
+SimplexQuadrature::compute_quadrature_rule_triangle(const std::vector<Point>& coordinates,
std::size_t gdim,
- std::size_t order)
+ std::size_t order) const
{
+ log(PROGRESS, "Create quadrature rule using given triangle coordinates");
+
std::pair<std::vector<double>, std::vector<double>> quadrature_rule;
- // Weights and points in local coordinates on triangle [0,0], [1,0]
- // and [0,1]
- std::vector<double> w;
- std::vector<std::vector<double>> p;
+ // Find the determinant of the Jacobian (inspired by ufc_geometry.h)
+ double det = 0; // To keep compiler happy
- switch (order)
+ switch (gdim)
{
- case 1:
- // Assign weight 1 and midpoint
- w.assign(1, 1.);
- p.assign(1, std::vector<double>(3, 1./3));
-
- break;
case 2:
- // Assign weight 1/3
- w.assign(3, 1./3);
-
- // Assign points corresponding to 2/3, 1/6, 1/6
- p.assign(3, std::vector<double>(3, 1./6));
- p[0][0] = p[1][1] = p[2][2] = 2./3;
+ {
+ det = orient2d(coordinates[0], coordinates[1], coordinates[2]);
break;
+ }
case 3:
- // Assign weights
- w.resize(4);
- w[0] = -27./48;
- w[1] = w[2] = w[3] = 25./48;
-
- // Assign points
- p.resize(4);
- p[0] = { 1./3, 1./3, 1./3 };
- p[1] = { 0.2, 0.2, 0.6 };
- p[2] = { 0.2, 0.6, 0.2 };
- p[3] = { 0.6, 0.2, 0.2 };
+ {
+ const std::array<double, 6> J = {{coordinates[1].x() - coordinates[0].x(),
+ coordinates[2].x() - coordinates[0].x(),
+ coordinates[1].y() - coordinates[0].y(),
+ coordinates[2].y() - coordinates[0].y(),
+ coordinates[1].z() - coordinates[0].z(),
+ coordinates[2].z() - coordinates[0].z()}};
+ const double d_0 = J[2]*J[5] - J[4]*J[3];
+ const double d_1 = J[4]*J[1] - J[0]*J[5];
+ const double d_2 = J[0]*J[3] - J[2]*J[1];
+ const double det2 = d_0*d_0 + d_1*d_1 + d_2*d_2;
+ det = std::sqrt(det2);
break;
- case 4:
- // Assign weights
- w = { 0.223381589678011,
- 0.223381589678011,
- 0.223381589678011,
- 0.109951743655322,
- 0.109951743655322,
- 0.109951743655322 };
-
- // Assign points
- p.resize(6);
- p[0] = { 0.445948490915965, 0.445948490915965, 0.10810301816807 };
- p[1] = { 0.445948490915965, 0.10810301816807, 0.445948490915965 };
- p[2] = { 0.10810301816807, 0.445948490915965, 0.445948490915965 };
- p[3] = { 0.091576213509771, 0.091576213509771, 0.816847572980458 };
- p[4] = { 0.091576213509771, 0.816847572980459, 0.09157621350977 };
- p[5] = { 0.816847572980459, 0.091576213509771, 0.09157621350977 };
+ }
+ default:
+ dolfin_error("SimplexQuadrature.cpp",
+ "compute quadrature rule for triangle",
+ "Not implemented for dimension ", gdim);
+ }
- break;
- case 5:
- // Assign weights
- w = {0.225,
- 0.132394152788506,
- 0.132394152788506,
- 0.132394152788506,
- 0.125939180544827,
- 0.125939180544827,
- 0.125939180544827 };
+ // Store points
+ quadrature_rule.first.resize(gdim*_p.size());
+ for (std::size_t i = 0; i < _p.size(); ++i)
+ {
+ for (std::size_t d = 0; d < gdim; ++d)
+ {
+ quadrature_rule.first[d + i*gdim]
+ = _p[i][0]*coordinates[0][d]
+ + _p[i][1]*coordinates[1][d]
+ + (1. - _p[i][0] - _p[i][1])*coordinates[2][d];
+ dolfin_assert(std::isfinite(quadrature_rule.first[d + i*gdim]));
+ }
+ }
- // Assign points
- p.resize(7);
- p[0] = { 0.3333333333333335, 0.3333333333333335, 0.3333333333333330 };
- p[1] = { 0.4701420641051150, 0.4701420641051150, 0.0597158717897700 };
- p[2] = { 0.4701420641051150, 0.0597158717897700, 0.4701420641051151 };
- p[3] = { 0.0597158717897700, 0.4701420641051150, 0.4701420641051151 };
- p[4] = { 0.1012865073234560, 0.1012865073234560, 0.7974269853530880 };
- p[5] = { 0.1012865073234560, 0.7974269853530870, 0.1012865073234570 };
- p[6] = { 0.7974269853530870, 0.1012865073234560, 0.1012865073234570 };
+ // Store weights
+ quadrature_rule.second.assign(_w.size(), 0.5*std::abs(det));
+ for (std::size_t i = 0; i < _w.size(); ++i)
+ {
+ quadrature_rule.second[i] *= _w[i];
+ dolfin_assert(std::isfinite(quadrature_rule.second[i]));
+ }
- break;
- case 6:
- // Assign weights
- w = { 0.1167862757263790,
- 0.1167862757263790,
- 0.1167862757263790,
- 0.0508449063702070,
- 0.0508449063702070,
- 0.0508449063702070,
- 0.0828510756183740,
- 0.0828510756183740,
- 0.0828510756183740,
- 0.0828510756183740,
- 0.0828510756183740,
- 0.0828510756183740 };
+ dolfin_assert(quadrature_rule.first.size() == gdim*quadrature_rule.second.size());
- // Assign points
- p.resize(12);
- p[0] = { 0.2492867451709100, 0.2492867451709100, 0.5014265096581800 };
- p[1] = { 0.2492867451709100, 0.5014265096581790, 0.2492867451709110 };
- p[2] = { 0.5014265096581790, 0.2492867451709100, 0.2492867451709110 };
- p[3] = { 0.0630890144915020, 0.0630890144915020, 0.8738219710169960 };
- p[4] = { 0.0630890144915020, 0.8738219710169960, 0.0630890144915019 };
- p[5] = { 0.8738219710169960, 0.0630890144915020, 0.0630890144915019 };
- p[6] = { 0.3103524510337840, 0.6365024991213990, 0.0531450498448169 };
- p[7] = { 0.6365024991213990, 0.0531450498448170, 0.3103524510337841 };
- p[8] = { 0.0531450498448170, 0.3103524510337840, 0.6365024991213990 };
- p[9] = { 0.3103524510337840, 0.0531450498448170, 0.6365024991213990 };
- p[10] = { 0.6365024991213990, 0.3103524510337840, 0.0531450498448169 };
- p[11] = { 0.0531450498448170, 0.6365024991213990, 0.3103524510337841 };
+ return quadrature_rule;
+}
+//-----------------------------------------------------------------------------
+std::pair<std::vector<double>, std::vector<double>>
+SimplexQuadrature::compute_quadrature_rule_tetrahedron(const std::vector<Point>& coordinates,
+ std::size_t gdim,
+ std::size_t order) const
+{
+ log(PROGRESS, "Create quadrature rule using given tetrahedron coordinates");
- break;
- default:
- dolfin_error("SimplexQuadrature.cpp",
- "compute quadrature rule for triangle",
- "Not implemented for order ", order);
- }
+ std::pair<std::vector<double>, std::vector<double>> quadrature_rule;
- // Find the determinant of the Jacobian (inspired by ufc_geometry.h)
+ // Find the determinant of the Jacobian (from ufc_geometry.h)
double det = 0; // To keep compiler happy
switch (gdim)
{
- case 2:
- {
- const double J[] = {coordinates[2] - coordinates[0],
- coordinates[4] - coordinates[0],
- coordinates[3] - coordinates[1],
- coordinates[5] - coordinates[1]};
- det = J[0]*J[3] - J[1]*J[2];
- break;
- }
case 3:
- {
- const double J[] = {coordinates[3] - coordinates[0],
- coordinates[6] - coordinates[0],
- coordinates[4] - coordinates[1],
- coordinates[7] - coordinates[1],
- coordinates[5] - coordinates[2],
- coordinates[8] - coordinates[2]};
- const double d_0 = J[2]*J[5] - J[4]*J[3];
- const double d_1 = J[4]*J[1] - J[0]*J[5];
- const double d_2 = J[0]*J[3] - J[2]*J[1];
- const double det2 = d_0*d_0 + d_1*d_1 + d_2*d_2;
- det = std::sqrt(det2);
-
- break;
- }
+ {
+ const std::array<double, 9> J = {{coordinates[1].x() - coordinates[0].x(),
+ coordinates[2].x() - coordinates[0].x(),
+ coordinates[3].x() - coordinates[0].x(),
+ coordinates[1].y() - coordinates[0].y(),
+ coordinates[2].y() - coordinates[0].y(),
+ coordinates[3].y() - coordinates[0].y(),
+ coordinates[1].z() - coordinates[0].z(),
+ coordinates[2].z() - coordinates[0].z(),
+ coordinates[3].z() - coordinates[0].z()}};
+ const std::array<double, 3> d = {{J[4]*J[8] - J[5]*J[7],
+ J[2]*J[7] - J[1]*J[8],
+ J[1]*J[5] - J[2]*J[4]}};
+ det = J[0]*d[0] + J[3]*d[1] + J[6]*d[2];
+ break;
+ }
default:
dolfin_error("SimplexQuadrature.cpp",
- "compute quadrature rule for triangle",
+ "compute quadrature rule for tetrahedron",
"Not implemented for dimension ", gdim);
}
// Store points
- quadrature_rule.first.resize(gdim*p.size());
- for (std::size_t i = 0; i < p.size(); ++i)
+ quadrature_rule.first.resize(gdim*_p.size());
+ for (std::size_t i = 0; i < _p.size(); ++i)
+ {
for (std::size_t d = 0; d < gdim; ++d)
+ {
quadrature_rule.first[d + i*gdim]
- = p[i][0]*coordinates[d]
- + p[i][1]*coordinates[gdim + d]
- + p[i][2]*coordinates[2*gdim + d];
+ = _p[i][0]*coordinates[0][d]
+ + _p[i][1]*coordinates[1][d]
+ + _p[i][2]*coordinates[2][d]
+ + (1. - _p[i][0] - _p[i][1] - _p[i][2])*coordinates[3][d];
+ }
+ }
// Store weights
- quadrature_rule.second.assign(w.size(), 0.5*std::abs(det));
- for (std::size_t i = 0; i < w.size(); ++i)
- quadrature_rule.second[i] *= w[i];
+ quadrature_rule.second.assign(_w.size(), std::abs(det)/6.0);
+ for (std::size_t i = 0; i < _w.size(); ++i)
+ quadrature_rule.second[i] *= _w[i];
+
+ dolfin_assert(quadrature_rule.first.size() == gdim*quadrature_rule.second.size());
return quadrature_rule;
}
//-----------------------------------------------------------------------------
-std::pair<std::vector<double>, std::vector<double>>
- SimplexQuadrature::compute_quadrature_rule_tetrahedron(
- const double* coordinates,
- std::size_t gdim,
- std::size_t order)
+std::vector<std::size_t>
+SimplexQuadrature::compress(std::pair<std::vector<double>, std::vector<double>>& qr,
+ std::size_t gdim,
+ std::size_t quadrature_order)
{
- std::pair<std::vector<double>, std::vector<double>> quadrature_rule;
+ // Polynomial degree N that can be integrated exactly using the
+ // qr_base
+ const std::size_t N = quadrature_order;
+
+ // By construction the compressed quadrature rule will not have more
+ // than choose(N + gdim, gdim) points.
+ const std::size_t N_compressed_min = choose(N + gdim, gdim);
+ if (qr.second.size() <= N_compressed_min)
+ {
+ // We cannot improve this rule. Return empty vector
+ return std::vector<std::size_t>();
+ }
+
+ log(PROGRESS, "Compressing %d quadrature points down to %d",
+ qr.second.size(), N_compressed_min);
+
+ // Copy the input qr since we'll overwrite the input
+ const std::pair<std::vector<double>, std::vector<double>> qr_input = qr;
+
+ // Create Vandermonde-type matrix using a basis of Chebyshev
+ // polynomials of the first kind
+ const Eigen::MatrixXd V = Chebyshev_Vandermonde_matrix(qr_input, gdim, N);
+
+ // A QR decomposition selects the subset of N columns (geometrically
+ // the N columns with same volume as spanned by all M columns).
+ Eigen::ColPivHouseholderQR<Eigen::MatrixXd> QR(V);
+ Eigen::MatrixXd Q = QR.householderQ();
+
+ // We do not need the full Q matrix but only what's known as the
+ // "economy size" decomposition
+ Q *= Eigen::MatrixXd::Identity(V.rows(), std::min(V.rows(), V.cols()));
+
+ // We'll use Q^T
+ Q.transposeInPlace();
+
+ // Compute weights in the new basis
+ Eigen::Map<const Eigen::VectorXd> w_base(qr_input.second.data(),
+ qr_input.second.size());
+ const Eigen::VectorXd nu = Q*w_base;
+
+ // Compute new weights
+ const Eigen::VectorXd w_new = Q.colPivHouseholderQr().solve(nu);
+
+ // Construct new qr using the non-zero weights. First find the
+ // indices for these weights.
+ std::vector<std::size_t> indices;
+ for (int i = 0; i < w_new.size(); ++i)
+ {
+ if (std::abs(w_new[i]) > 0.0)
+ indices.push_back(i);
+ }
+
+ // Resize qr and overwrite the points and weights
+ dolfin_assert(indices.size() <= N_compressed_min);
+ qr.first.resize(gdim*indices.size());
+ qr.second.resize(indices.size());
+
+ for (std::size_t i = 0; i < indices.size(); ++i)
+ {
+ // Save points
+ for (std::size_t d = 0; d < gdim; ++d)
+ qr.first[gdim*i + d] = qr_input.first[gdim*indices[i] + d];
- // Weights and points in local coordinates on tetrahedron [0,0,0],
- // [1,0,0], [0,1,0] and [0,0,1]
- std::vector<double> w;
- std::vector<std::vector<double>> p;
+ // Save weights
+ qr.second[i] = w_new[indices[i]];
+ }
+
+ // Return indices. These are useful for mapping additional data, for
+ // example the normals.
+ return indices;
+}
+//-----------------------------------------------------------------------------
+void SimplexQuadrature::setup_qr_reference_interval(std::size_t order)
+{
+ // Create quadrature rule with points on reference element [-1, 1].
+ _p.resize(1);
+ legendre_compute_glr(order, _p[0], _w);
+}
+//-----------------------------------------------------------------------------
+void SimplexQuadrature::setup_qr_reference_triangle(std::size_t order)
+{
+ // Create quadrature rule with points on reference triangle [0, 0],
+ // [1, 0] and [0, 1]
+ return dunavant_rule(order, _p, _w);
+}
+//-----------------------------------------------------------------------------
+void SimplexQuadrature::setup_qr_reference_tetrahedron(std::size_t order)
+{
+ // FIXME: Replace these hard coded rules by a general function
switch (order)
{
case 1:
// Assign weight 1 and midpoint
- w.assign(1, 1.);
- p.assign(1, std::vector<double>(4, 0.25));
+ _w.assign(1, 1.);
+ _p.assign(1, std::vector<double>(3, 0.25));
break;
case 2:
- // Assign weight 0.25
- w.assign(4, 0.25);
+ // Assign weights
+ _w.assign(4, 0.25);
- // Assign points corresponding to 0.585410196624969,
- // 0.138196601125011, 0.138196601125011 and 0.138196601125011
- p.assign(4, std::vector<double>(4, 0.138196601125011));
- p[0][0] = p[1][1] = p[2][2] = p[3][3] = 0.585410196624969;
+ // Assign points
+ _p.assign(4, std::vector<double>(3, 0.138196601125011));
+ _p[0][0] = _p[1][1] = _p[2][2] = 0.585410196624969;
break;
case 3:
// Assign weights
- w = { -4./5,
- 9./20,
- 9./20,
- 9./20,
- 9./20 };
+ _w = { -4./5.,
+ 9./20.,
+ 9./20.,
+ 9./20.,
+ 9./20. };
// Assign points
- p.resize(5);
- p[0] = { 0.25, 0.25, 0.25, 0.25 };
- p[1] = { 1./6, 1./6, 1./6, 0.5 };
- p[2] = { 1./6, 1./6, 0.5, 1./6 };
- p[3] = { 1./6, 0.5, 1./6, 1./6 };
- p[4] = { 0.5, 1./6, 1./6, 1./6 };
+ _p = { { 0.25, 0.25, 0.25 },
+ { 1./6., 1./6., 1./6. },
+ { 1./6., 1./6., 0.5, },
+ { 1./6., 0.5, 1./6. },
+ { 0.5, 1./6., 1./6. } };
break;
case 4:
// Assign weights
- w = { -0.0789333333333330,
- 0.0457333333333335,
- 0.0457333333333335,
- 0.0457333333333335,
- 0.0457333333333335,
- 0.1493333333333332,
- 0.1493333333333332,
- 0.1493333333333332,
- 0.1493333333333332,
- 0.1493333333333332,
- 0.1493333333333332 };
+ // FIXME: Find new rule to avoid negative weight
+ _w = { -0.0789333333333330,
+ 0.0457333333333335,
+ 0.0457333333333335,
+ 0.0457333333333335,
+ 0.0457333333333335,
+ 0.1493333333333332,
+ 0.1493333333333332,
+ 0.1493333333333332,
+ 0.1493333333333332,
+ 0.1493333333333332,
+ 0.1493333333333332 };
// Assign points
- p.resize(11);
- p[0] = { 0.2500000000000000, 0.2500000000000000, 0.2500000000000000, 0.2500000000000000 };
- p[1] = { 0.0714285714285715, 0.0714285714285715, 0.0714285714285715, 0.7857142857142855 };
- p[2] = { 0.0714285714285715, 0.0714285714285715, 0.7857142857142855, 0.0714285714285715 };
- p[3] = { 0.0714285714285715, 0.7857142857142855, 0.0714285714285715, 0.0714285714285715 };
- p[4] = { 0.7857142857142855, 0.0714285714285715, 0.0714285714285715, 0.0714285714285715 };
- p[5] = { 0.3994035761667990, 0.3994035761667990, 0.1005964238332010, 0.1005964238332010 };
- p[6] = { 0.3994035761667990, 0.1005964238332010, 0.3994035761667990, 0.1005964238332010 };
- p[7] = { 0.1005964238332010, 0.3994035761667990, 0.3994035761667990, 0.1005964238332010 };
- p[8] = { 0.3994035761667990, 0.1005964238332010, 0.1005964238332010, 0.3994035761667990 };
- p[9] = { 0.1005964238332010, 0.3994035761667990, 0.1005964238332010, 0.3994035761667990 };
- p[10] = { 0.1005964238332010, 0.1005964238332010, 0.3994035761667990, 0.3994035761667990 };
+ _p = { { 0.2500000000000000, 0.2500000000000000, 0.2500000000000000 },
+ { 0.0714285714285715, 0.0714285714285715, 0.0714285714285715 },
+ { 0.0714285714285715, 0.0714285714285715, 0.7857142857142855 },
+ { 0.0714285714285715, 0.7857142857142855, 0.0714285714285715 },
+ { 0.7857142857142855, 0.0714285714285715, 0.0714285714285715 },
+ { 0.3994035761667990, 0.3994035761667990, 0.1005964238332010 },
+ { 0.3994035761667990, 0.1005964238332010, 0.3994035761667990 },
+ { 0.1005964238332010, 0.3994035761667990, 0.3994035761667990 },
+ { 0.3994035761667990, 0.1005964238332010, 0.1005964238332010 },
+ { 0.1005964238332010, 0.3994035761667990, 0.1005964238332010 },
+ { 0.1005964238332010, 0.1005964238332010, 0.3994035761667990 } };
break;
case 5:
// Assign weights
- w = { 0.0734930431163618,
- 0.0734930431163618,
- 0.0734930431163618,
- 0.0734930431163618,
- 0.1126879257180158,
- 0.1126879257180158,
- 0.1126879257180158,
- 0.1126879257180158,
- 0.0425460207770813,
- 0.0425460207770813,
- 0.0425460207770813,
- 0.0425460207770813,
- 0.0425460207770813,
- 0.0425460207770813 };
+ _w = { 0.0734930431163618,
+ 0.0734930431163618,
+ 0.0734930431163618,
+ 0.0734930431163618,
+ 0.1126879257180158,
+ 0.1126879257180158,
+ 0.1126879257180158,
+ 0.1126879257180158,
+ 0.0425460207770813,
+ 0.0425460207770813,
+ 0.0425460207770813,
+ 0.0425460207770813,
+ 0.0425460207770813,
+ 0.0425460207770813 };
// Assign points
- p.resize(14);
- p[0] = { 0.0927352503108910, 0.0927352503108910, 0.0927352503108910, 0.7217942490673269 };
- p[1] = { 0.7217942490673265, 0.0927352503108910, 0.0927352503108910, 0.0927352503108915 };
- p[2] = { 0.0927352503108910, 0.7217942490673265, 0.0927352503108910, 0.0927352503108915 };
- p[3] = { 0.0927352503108910, 0.0927352503108910, 0.7217942490673265, 0.0927352503108915 };
- p[4] = { 0.3108859192633005, 0.3108859192633005, 0.3108859192633005, 0.0673422422100984 };
- p[5] = { 0.0673422422100980, 0.3108859192633005, 0.3108859192633005, 0.3108859192633010 };
- p[6] = { 0.3108859192633005, 0.0673422422100980, 0.3108859192633005, 0.3108859192633010 };
- p[7] = { 0.3108859192633005, 0.3108859192633005, 0.0673422422100980, 0.3108859192633010 };
- p[8] = { 0.4544962958743505, 0.4544962958743505, 0.0455037041256495, 0.0455037041256495 };
- p[9] = { 0.4544962958743505, 0.0455037041256495, 0.4544962958743505, 0.0455037041256495 };
- p[10] = { 0.0455037041256495, 0.4544962958743505, 0.4544962958743505, 0.0455037041256495 };
- p[11] = { 0.4544962958743505, 0.0455037041256495, 0.0455037041256495, 0.4544962958743505 };
- p[12] = { 0.0455037041256495, 0.4544962958743505, 0.0455037041256495, 0.4544962958743505 };
- p[13] = { 0.0455037041256495, 0.0455037041256495, 0.4544962958743505, 0.4544962958743505 };
+ _p = { { 0.0927352503108910, 0.0927352503108910, 0.0927352503108910 },
+ { 0.7217942490673265, 0.0927352503108910, 0.0927352503108910 },
+ { 0.0927352503108910, 0.7217942490673265, 0.0927352503108910 },
+ { 0.0927352503108910, 0.0927352503108910, 0.7217942490673265 },
+ { 0.3108859192633005, 0.3108859192633005, 0.3108859192633005 },
+ { 0.0673422422100980, 0.3108859192633005, 0.3108859192633005 },
+ { 0.3108859192633005, 0.0673422422100980, 0.3108859192633005 },
+ { 0.3108859192633005, 0.3108859192633005, 0.0673422422100980 },
+ { 0.4544962958743505, 0.4544962958743505, 0.0455037041256495 },
+ { 0.4544962958743505, 0.0455037041256495, 0.4544962958743505 },
+ { 0.0455037041256495, 0.4544962958743505, 0.4544962958743505 },
+ { 0.4544962958743505, 0.0455037041256495, 0.0455037041256495 },
+ { 0.0455037041256495, 0.4544962958743505, 0.0455037041256495 },
+ { 0.0455037041256495, 0.0455037041256495, 0.4544962958743505 } };
break;
case 6:
// Assign weights
- w = { 0.0399227502581678,
- 0.0399227502581678,
- 0.0399227502581678,
- 0.0399227502581678,
- 0.0100772110553205,
- 0.0100772110553205,
- 0.0100772110553205,
- 0.0100772110553205,
- 0.0553571815436550,
- 0.0553571815436550,
- 0.0553571815436550,
- 0.0553571815436550,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855,
- 0.0482142857142855 };
+ _w = { 0.0399227502581678,
+ 0.0399227502581678,
+ 0.0399227502581678,
+ 0.0399227502581678,
+ 0.0100772110553205,
+ 0.0100772110553205,
+ 0.0100772110553205,
+ 0.0100772110553205,
+ 0.0553571815436550,
+ 0.0553571815436550,
+ 0.0553571815436550,
+ 0.0553571815436550,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855,
+ 0.0482142857142855 };
// Assign points
- p.resize(24);
- p[0] = { 0.2146028712591520, 0.2146028712591520, 0.2146028712591520, 0.3561913862225440 };
- p[1] = { 0.3561913862225440, 0.2146028712591520, 0.2146028712591520, 0.2146028712591520 };
- p[2] = { 0.2146028712591520, 0.3561913862225440, 0.2146028712591520, 0.2146028712591520 };
- p[3] = { 0.2146028712591520, 0.2146028712591520, 0.3561913862225440, 0.2146028712591520 };
- p[4] = { 0.0406739585346115, 0.0406739585346115, 0.0406739585346115, 0.8779781243961655 };
- p[5] = { 0.8779781243961660, 0.0406739585346115, 0.0406739585346115, 0.0406739585346112 };
- p[6] = { 0.0406739585346115, 0.8779781243961660, 0.0406739585346115, 0.0406739585346112 };
- p[7] = { 0.0406739585346115, 0.0406739585346115, 0.8779781243961660, 0.0406739585346111 };
- p[8] = { 0.3223378901422755, 0.3223378901422755, 0.3223378901422755, 0.0329863295731734 };
- p[9] = { 0.0329863295731735, 0.3223378901422755, 0.3223378901422755, 0.3223378901422754 };
- p[10] = { 0.3223378901422755, 0.0329863295731735, 0.3223378901422755, 0.3223378901422754 };
- p[11] = { 0.3223378901422755, 0.3223378901422755, 0.0329863295731735, 0.3223378901422754 };
- p[12] = { 0.0636610018750175, 0.0636610018750175, 0.2696723314583160, 0.6030056647916490 };
- p[13] = { 0.0636610018750175, 0.2696723314583160, 0.0636610018750175, 0.6030056647916490 };
- p[14] = { 0.0636610018750175, 0.0636610018750175, 0.6030056647916490, 0.2696723314583160 };
- p[15] = { 0.0636610018750175, 0.6030056647916490, 0.0636610018750175, 0.2696723314583160 };
- p[16] = { 0.0636610018750175, 0.2696723314583160, 0.6030056647916490, 0.0636610018750174 };
- p[17] = { 0.0636610018750175, 0.6030056647916490, 0.2696723314583160, 0.0636610018750174 };
- p[18] = { 0.2696723314583160, 0.0636610018750175, 0.0636610018750175, 0.6030056647916490 };
- p[19] = { 0.2696723314583160, 0.0636610018750175, 0.6030056647916490, 0.0636610018750174 };
- p[20] = { 0.2696723314583160, 0.6030056647916490, 0.0636610018750175, 0.0636610018750174 };
- p[21] = { 0.6030056647916490, 0.0636610018750175, 0.2696723314583160, 0.0636610018750174 };
- p[22] = { 0.6030056647916490, 0.0636610018750175, 0.0636610018750175, 0.2696723314583160 };
- p[23] = { 0.6030056647916490, 0.2696723314583160, 0.0636610018750175, 0.0636610018750174 };
+ _p = { { 0.2146028712591520, 0.2146028712591520, 0.2146028712591520 },
+ { 0.3561913862225440, 0.2146028712591520, 0.2146028712591520 },
+ { 0.2146028712591520, 0.3561913862225440, 0.2146028712591520 },
+ { 0.2146028712591520, 0.2146028712591520, 0.3561913862225440 },
+ { 0.0406739585346115, 0.0406739585346115, 0.0406739585346115 },
+ { 0.8779781243961660, 0.0406739585346115, 0.0406739585346115 },
+ { 0.0406739585346115, 0.8779781243961660, 0.0406739585346115 },
+ { 0.0406739585346115, 0.0406739585346115, 0.8779781243961660 },
+ { 0.3223378901422755, 0.3223378901422755, 0.3223378901422755 },
+ { 0.0329863295731735, 0.3223378901422755, 0.3223378901422755 },
+ { 0.3223378901422755, 0.0329863295731735, 0.3223378901422755 },
+ { 0.3223378901422755, 0.3223378901422755, 0.0329863295731735 },
+ { 0.0636610018750175, 0.0636610018750175, 0.2696723314583160 },
+ { 0.0636610018750175, 0.2696723314583160, 0.0636610018750175 },
+ { 0.0636610018750175, 0.0636610018750175, 0.6030056647916490 },
+ { 0.0636610018750175, 0.6030056647916490, 0.0636610018750175 },
+ { 0.0636610018750175, 0.2696723314583160, 0.6030056647916490 },
+ { 0.0636610018750175, 0.6030056647916490, 0.2696723314583160 },
+ { 0.2696723314583160, 0.0636610018750175, 0.0636610018750175 },
+ { 0.2696723314583160, 0.0636610018750175, 0.6030056647916490 },
+ { 0.2696723314583160, 0.6030056647916490, 0.0636610018750175 },
+ { 0.6030056647916490, 0.0636610018750175, 0.2696723314583160 },
+ { 0.6030056647916490, 0.0636610018750175, 0.0636610018750175 },
+ { 0.6030056647916490, 0.2696723314583160, 0.0636610018750175 } };
break;
default:
dolfin_error("SimplexQuadrature.cpp",
- "compute quadrature rule for triangle",
+ "compute quadrature rule for tetrahedron",
"Not implemented for order ", order);
}
+}
+//-----------------------------------------------------------------------------
+Eigen::MatrixXd SimplexQuadrature::Chebyshev_Vandermonde_matrix
+(const std::pair<std::vector<double>, std::vector<double>>& qr,
+ std::size_t gdim,
+ std::size_t N)
+{
+ // Create the Chebyshev basis matrix for each dimension separately
+ std::vector<std::vector<Eigen::VectorXd>> T(gdim);
+ Eigen::VectorXd x(qr.second.size());
+
+ for (std::size_t d = 0; d < gdim; ++d)
+ {
+ // Extract coordinates in one dimension
+ for (std::size_t i = 0; i < qr.second.size(); ++i)
+ x(i) = qr.first[i*gdim + d];
+
+ // Map points to [-1, 1]
+ const double xmin = x.minCoeff();
+ const double xmax = x.maxCoeff();
+ const double hx = xmax - xmin;
+ const Eigen::VectorXd xmap = (2./hx) * x - ((xmin+xmax)/hx) * Eigen::VectorXd::Ones(x.size());
+
+ // Evaluate the basis
+ T[d] = Chebyshev_polynomial(xmap, N);
+ }
- // Find the determinant of the Jacobian (from ufc_geometry.h)
- double det = 0; // To keep compiler happy
+ // Find the order of the polynomials in graded lexicographic
+ // ordering
+ const std::vector<std::vector<std::size_t>> P = grlex(gdim, N);
+
+ // Setup the Vandermonde type matrix
+ const std::size_t n_cols = P.size();
+ Eigen::MatrixXd V(Eigen::MatrixXd::Ones(qr.second.size(), n_cols));
+
+ // The first column is always [1, 1, ..., 1], hence we can start from 1
+ for (std::size_t i = 1; i < n_cols; ++i)
+ {
+ // Pick out the correct order of polynomial from the P
+ // matrix. Start with dimension 0.
+ const std::size_t d = 0;
+ const std::size_t grlex_order = P[i][d];
+ Eigen::VectorXd V_col = T[d][grlex_order];
+
+ // Do a .* style multiplication for each other dimension
+ for (std::size_t d = 1; d < gdim; ++d)
+ {
+ const std::size_t grlex_order = P[i][d];
+ V_col = V_col.cwiseProduct(T[d][grlex_order]);
+ }
+ V.col(i) = V_col;
+ }
+ return V;
+}
+//-----------------------------------------------------------------------------
+std::vector<Eigen::VectorXd>
+SimplexQuadrature::Chebyshev_polynomial(const Eigen::VectorXd& x,
+ std::size_t N)
+{
+ // Create Chebyshev polynomial of the first kind of order N on [-1, 1]
+ //
+ // T_0(x) = 1
+ // T_1(x) = x
+ // T_{k}(x) = 2 * x * T_{k-1}(x) - T_{k-2}(x), k = 2, ..., N
+
+ // Store in a matrix T such that (T)_ij = T_i(x_j). We don't use
+ // Eigen::MatrixXd, because we want to slice out the rows later.
+ std::vector<Eigen::VectorXd> T(N + 1, Eigen::VectorXd(x.size()));
+
+ // Reccurence construction
+ T[0] = Eigen::VectorXd::Ones(x.size());
+
+ if (N >= 1)
+ {
+ T[1] = x;
+ for (std::size_t k = 2; k <= N; ++k)
+ T[k] = 2*x.cwiseProduct(T[k-1]) - T[k-2];
+ }
+
+ return T;
+}
+//-----------------------------------------------------------------------------
+std::vector<std::vector<std::size_t>>
+SimplexQuadrature::grlex(std::size_t gdim, std::size_t N)
+{
+ // Generate a matrix with numbers in graded lexicographic ordering,
+ // i.e. if N = 3 and dim = 2, P should be
+ // P = [ 0 0
+ // 0 1
+ // 1 0
+ // 0 2
+ // 1 1
+ // 2 0
+ // 0 3
+ // 1 2
+ // 2 1
+ // 3 0 ]
+
+ const std::size_t n_rows = choose(N + gdim, gdim);
+ std::vector<std::vector<std::size_t>> P(n_rows, std::vector<std::size_t>(gdim));
+
+ // FIXME: Make this a dimension independent loop
switch (gdim)
{
+ case 2:
+ for (std::size_t sum = 0, row = 0; sum <= N; ++sum)
+ for (std::size_t xi = 0; xi <= N; ++xi)
+ for (std::size_t yi = 0; yi <= N; ++yi)
+ if (xi + yi == sum)
+ {
+ P[row][0] = xi;
+ P[row][1] = yi;
+ row++;
+ }
+ break;
case 3:
+ for (std::size_t sum = 0, row = 0; sum <= N; ++sum)
+ for (std::size_t xi = 0; xi <= N; ++xi)
+ for (std::size_t yi = 0; yi <= N; ++yi)
+ for (std::size_t zi = 0; zi <= N; ++zi)
+ if (xi + yi + zi == sum)
+ {
+ P[row][0] = xi;
+ P[row][1] = yi;
+ P[row][2] = zi;
+ row++;
+ }
+ break;
+ default:
+ dolfin_assert(false);
+ }
+
+ return P;
+}
+//-----------------------------------------------------------------------------
+std::size_t
+SimplexQuadrature::choose(std::size_t n,
+ std::size_t k)
+{
+ // Compute the number of combinations n over k
+ if (k == 0)
+ return 1;
+ return (n * choose(n - 1, k - 1)) / k;
+}
+//-----------------------------------------------------------------------------
+void SimplexQuadrature::dunavant_rule(std::size_t rule,
+ std::vector<std::vector<double> >& p,
+ std::vector<double>& w)
+{
+ //****************************************************************************80
+ //
+ // Purpose:
+ //
+ // DUNAVANT_RULE returns the points and weights of a Dunavant rule.
+ //
+ // Licensing:
+ //
+ // This code is distributed under the GNU LGPL license.
+ //
+ // Modified:
+ //
+ // 11 December 2006
+ //
+ // Author:
+ //
+ // John Burkardt
+ //
+ // Reference:
+ //
+ // David Dunavant,
+ // High Degree Efficient Symmetrical Gaussian Quadrature Rules
+ // for the Triangle,
+ // International Journal for Numerical Methods in Engineering,
+ // Volume 21, 1985, pages 1129-1148.
+ //
+ // James Lyness, Dennis Jespersen,
+ // Moderate Degree Symmetric Quadrature Rules for the Triangle,
+ // Journal of the Institute of Mathematics and its Applications,
+ // Volume 15, Number 1, February 1975, pages 19-32.
+ //
+ // Parameters:
+ //
+ // Input, int RULE, the index of the rule.
+ //
+ // Input, int ORDER_NUM, the order (number of points) of the rule.
+ //
+ // Output, double XY[2*ORDER_NUM], the points of the rule.
+ //
+ // Output, double W[ORDER_NUM], the weights of the rule.
+ //
+
+ // Get the suborder information
+ const std::size_t suborder_num = dunavant_suborder_num(rule);
+ std::vector<double> suborder_xyz(3*suborder_num);
+ std::vector<double> suborder_w(suborder_num);;
+ const std::vector<std::size_t> suborder = dunavant_suborder(rule, suborder_num);
+ dunavant_subrule(rule, suborder_num, suborder_xyz, suborder_w);
+
+ // Resize p and w
+ const std::size_t order_num = dunavant_order_num(rule);
+ p.resize(order_num, std::vector<double>(2));
+ w.resize(order_num);
+
+ // Expand the suborder information to a full order rule
+ std::size_t o = 0;
+
+ for (std::size_t s = 0; s < suborder_num; s++)
+ {
+ if (suborder[s] == 1)
{
- const double J[] = {coordinates[3] - coordinates[0],
- coordinates[6] - coordinates[0],
- coordinates[9] - coordinates[0],
- coordinates[4] - coordinates[1],
- coordinates[7] - coordinates[1],
- coordinates[10] - coordinates[1],
- coordinates[5] - coordinates[2],
- coordinates[8] - coordinates[2],
- coordinates[11] - coordinates[2]};
- double d[9];
- d[0*3 + 0] = J[4]*J[8] - J[5]*J[7];
- // d[0*3 + 1] = J[5]*J[6] - J[3]*J[8];
- // d[0*3 + 2] = J[3]*J[7] - J[4]*J[6];
- d[1*3 + 0] = J[2]*J[7] - J[1]*J[8];
- // d[1*3 + 1] = J[0]*J[8] - J[2]*J[6];
- // d[1*3 + 2] = J[1]*J[6] - J[0]*J[7];
- d[2*3 + 0] = J[1]*J[5] - J[2]*J[4];
- // d[2*3 + 1] = J[2]*J[3] - J[0]*J[5];
- // d[2*3 + 2] = J[0]*J[4] - J[1]*J[3];
-
- det = J[0]*d[0*3 + 0] + J[3]*d[1*3 + 0] + J[6]*d[2*3 + 0];
- break;
+ p[o][0] = suborder_xyz[0+s*3];
+ p[o][1] = suborder_xyz[1+s*3];
+ w[o] = suborder_w[s];
+ o = o + 1;
+ }
+ else if (suborder[s] == 3)
+ {
+ for (std::size_t k = 0; k < 3; k++)
+ {
+ p[o][0] = suborder_xyz[i4_wrap(k, 0,2) + s*3];
+ p[o][1] = suborder_xyz[i4_wrap(k+1,0,2) + s*3];
+ w[o] = suborder_w[s];
+ o = o + 1;
+ }
+ }
+ else if (suborder[s] == 6)
+ {
+ for (std::size_t k = 0; k < 3; k++)
+ {
+ p[o][0] = suborder_xyz[i4_wrap(k, 0,2) + s*3];
+ p[o][1] = suborder_xyz[i4_wrap(k+1,0,2) + s*3];
+ w[o] = suborder_w[s];
+ o = o + 1;
+ }
+
+ for (std::size_t k = 0; k < 3; k++)
+ {
+ p[o][0] = suborder_xyz[i4_wrap(k+1,0,2) + s*3];
+ p[o][1] = suborder_xyz[i4_wrap(k, 0,2) + s*3];
+ w[o] = suborder_w[s];
+ o = o + 1;
+ }
+ }
+ else
+ {
+ dolfin_error("SimplexQuadrature.cpp",
+ "compute quadrature rule for triangle",
+ "Dunavant rule not implemented for suborder ", suborder[s]);
}
- default:
- dolfin_error("SimplexQuadrature.cpp",
- "compute quadrature rule for tetrahedron",
- "Not implemented for dimension ", gdim);
}
- // Store points
- quadrature_rule.first.resize(gdim*p.size());
- for (std::size_t i = 0; i < p.size(); ++i)
- for (std::size_t d = 0; d < gdim; ++d)
- quadrature_rule.first[d + i*gdim]
- = p[i][0]*coordinates[d]
- + p[i][1]*coordinates[gdim + d]
- + p[i][2]*coordinates[2*gdim + d]
- + p[i][3]*coordinates[3*gdim + d];
+}
+//****************************************************************************80
- // Store weights
- quadrature_rule.second.assign(w.size(), std::abs(det) / 6.);
- for (std::size_t i = 0; i < w.size(); ++i)
- quadrature_rule.second[i] *= w[i];
+std::size_t SimplexQuadrature::dunavant_order_num(std::size_t rule)
- return quadrature_rule;
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_ORDER_NUM returns the order of a Dunavant rule for the triangle.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int RULE, the index of the rule.
+//
+// Output, int DUNAVANT_ORDER_NUM, the order (number of points) of the rule.
+//
+{
+ std::size_t order;
+ std::size_t order_num;
+ std::size_t suborder_num;
+
+ suborder_num = dunavant_suborder_num(rule);
+
+ std::vector<std::size_t> suborder = dunavant_suborder(rule, suborder_num);
+
+ order_num = 0;
+ for (order = 0; order < suborder_num; order++)
+ {
+ order_num = order_num + suborder[order];
+ }
+
+ return order_num;
+}
+//****************************************************************************80
+
+std::vector<std::size_t> SimplexQuadrature::dunavant_suborder(int rule, int suborder_num)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBORDER returns the suborders for a Dunavant rule.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int RULE, the index of the rule.
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, int DUNAVANT_SUBORDER[SUBORDER_NUM], the suborders of the rule.
+//
+{
+ std::vector<std::size_t> suborder(suborder_num);
+
+ if (rule == 1)
+ {
+ suborder[0] = 1;
+ }
+ else if (rule == 2)
+ {
+ suborder[0] = 3;
+ }
+ else if (rule == 3)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ }
+ else if (rule == 4)
+ {
+ suborder[0] = 3;
+ suborder[1] = 3;
+ }
+ else if (rule == 5)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ }
+ else if (rule == 6)
+ {
+ suborder[0] = 3;
+ suborder[1] = 3;
+ suborder[2] = 6;
+ }
+ else if (rule == 7)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 6;
+ }
+ else if (rule == 8)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 6;
+ }
+ else if (rule == 9)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 6;
+ }
+ else if (rule == 10)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 6;
+ suborder[4] = 6;
+ suborder[5] = 6;
+ }
+ else if (rule == 11)
+ {
+ suborder[0] = 3;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 6;
+ suborder[6] = 6;
+ }
+ else if (rule == 12)
+ {
+ suborder[0] = 3;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 6;
+ suborder[6] = 6;
+ suborder[7] = 6;
+ }
+ else if (rule == 13)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 3;
+ suborder[7] = 6;
+ suborder[8] = 6;
+ suborder[9] = 6;
+ }
+ else if (rule == 14)
+ {
+ suborder[0] = 3;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 6;
+ suborder[7] = 6;
+ suborder[8] = 6;
+ suborder[9] = 6;
+ }
+ else if (rule == 15)
+ {
+ suborder[0] = 3;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 6;
+ suborder[7] = 6;
+ suborder[8] = 6;
+ suborder[9] = 6;
+ suborder[10] = 6;
+ }
+ else if (rule == 16)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 3;
+ suborder[7] = 3;
+ suborder[8] = 6;
+ suborder[9] = 6;
+ suborder[10] = 6;
+ suborder[11] = 6;
+ suborder[12] = 6;
+ }
+ else if (rule == 17)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 3;
+ suborder[7] = 3;
+ suborder[8] = 3;
+ suborder[9] = 6;
+ suborder[10] = 6;
+ suborder[11] = 6;
+ suborder[12] = 6;
+ suborder[13] = 6;
+ suborder[14] = 6;
+ }
+ else if (rule == 18)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 3;
+ suborder[7] = 3;
+ suborder[8] = 3;
+ suborder[9] = 3;
+ suborder[10] = 6;
+ suborder[11] = 6;
+ suborder[12] = 6;
+ suborder[13] = 6;
+ suborder[14] = 6;
+ suborder[15] = 6;
+ suborder[16] = 6;
+ }
+ else if (rule == 19)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 3;
+ suborder[7] = 3;
+ suborder[8] = 3;
+ suborder[9] = 6;
+ suborder[10] = 6;
+ suborder[11] = 6;
+ suborder[12] = 6;
+ suborder[13] = 6;
+ suborder[14] = 6;
+ suborder[15] = 6;
+ suborder[16] = 6;
+ }
+ else if (rule == 20)
+ {
+ suborder[0] = 1;
+ suborder[1] = 3;
+ suborder[2] = 3;
+ suborder[3] = 3;
+ suborder[4] = 3;
+ suborder[5] = 3;
+ suborder[6] = 3;
+ suborder[7] = 3;
+ suborder[8] = 3;
+ suborder[9] = 3;
+ suborder[10] = 3;
+ suborder[11] = 6;
+ suborder[12] = 6;
+ suborder[13] = 6;
+ suborder[14] = 6;
+ suborder[15] = 6;
+ suborder[16] = 6;
+ suborder[17] = 6;
+ suborder[18] = 6;
+ }
+ else
+ {
+ dolfin_error("SimplexQuadrature.cpp",
+ "compute quadrature rule for triangle",
+ "dunavant_suborder not implemented for rule ", rule);
+ }
+
+ return suborder;
+}
+//****************************************************************************80
+
+std::size_t SimplexQuadrature::dunavant_suborder_num(int rule)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBORDER_NUM returns the number of suborders for a Dunavant rule.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int RULE, the index of the rule.
+//
+// Output, int DUNAVANT_SUBORDER_NUM, the number of suborders of the rule.
+//
+{
+ std::size_t suborder_num = 0;
+
+ if (rule == 1)
+ {
+ suborder_num = 1;
+ }
+ else if (rule == 2)
+ {
+ suborder_num = 1;
+ }
+ else if (rule == 3)
+ {
+ suborder_num = 2;
+ }
+ else if (rule == 4)
+ {
+ suborder_num = 2;
+ }
+ else if (rule == 5)
+ {
+ suborder_num = 3;
+ }
+ else if (rule == 6)
+ {
+ suborder_num = 3;
+ }
+ else if (rule == 7)
+ {
+ suborder_num = 4;
+ }
+ else if (rule == 8)
+ {
+ suborder_num = 5;
+ }
+ else if (rule == 9)
+ {
+ suborder_num = 6;
+ }
+ else if (rule == 10)
+ {
+ suborder_num = 6;
+ }
+ else if (rule == 11)
+ {
+ suborder_num = 7;
+ }
+ else if (rule == 12)
+ {
+ suborder_num = 8;
+ }
+ else if (rule == 13)
+ {
+ suborder_num = 10;
+ }
+ else if (rule == 14)
+ {
+ suborder_num = 10;
+ }
+ else if (rule == 15)
+ {
+ suborder_num = 11;
+ }
+ else if (rule == 16)
+ {
+ suborder_num = 13;
+ }
+ else if (rule == 17)
+ {
+ suborder_num = 15;
+ }
+ else if (rule == 18)
+ {
+ suborder_num = 17;
+ }
+ else if (rule == 19)
+ {
+ suborder_num = 17;
+ }
+ else if (rule == 20)
+ {
+ suborder_num = 19;
+ }
+ else
+ {
+ dolfin_error("SimplexQuadrature.cpp",
+ "compute quadrature rule for triangle",
+ "dunavant_suborder_num not implemented for rule ", rule);
+ }
+
+ return suborder_num;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule(std::size_t rule,
+ std::size_t suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE returns a compressed Dunavant rule.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int RULE, the index of the rule.
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ if (rule == 1)
+ {
+ dunavant_subrule_01(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 2)
+ {
+ dunavant_subrule_02(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 3)
+ {
+ dunavant_subrule_03(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 4)
+ {
+ dunavant_subrule_04(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 5)
+ {
+ dunavant_subrule_05(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 6)
+ {
+ dunavant_subrule_06(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 7)
+ {
+ dunavant_subrule_07(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 8)
+ {
+ dunavant_subrule_08(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 9)
+ {
+ dunavant_subrule_09(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 10)
+ {
+ dunavant_subrule_10(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 11)
+ {
+ dunavant_subrule_11(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 12)
+ {
+ dunavant_subrule_12(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 13)
+ {
+ dunavant_subrule_13(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 14)
+ {
+ dunavant_subrule_14(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 15)
+ {
+ dunavant_subrule_15(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 16)
+ {
+ dunavant_subrule_16(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 17)
+ {
+ dunavant_subrule_17(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 18)
+ {
+ dunavant_subrule_18(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 19)
+ {
+ dunavant_subrule_19(suborder_num, suborder_xyz, suborder_w);
+ }
+ else if (rule == 20)
+ {
+ dunavant_subrule_20(suborder_num, suborder_xyz, suborder_w);
+ }
+ else
+ {
+ dolfin_error("SimplexQuadrature.cpp",
+ "compute quadrature rule for triangle",
+ "dunavant_subrule not implemented for rule ", rule);
+ }
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+void SimplexQuadrature::dunavant_subrule_01(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_01 returns a compressed Dunavant rule 1.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_01[3*1] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333
+ };
+ double suborder_w_rule_01[1] = {
+ 1.000000000000000
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_01[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_01[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_01[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_01[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_02(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_02 returns a compressed Dunavant rule 2.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_02[3*1] = {
+ 0.666666666666667, 0.166666666666667, 0.166666666666667
+ };
+ double suborder_w_rule_02[1] = {
+ 0.333333333333333
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_02[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_02[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_02[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_02[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_03(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_03 returns a compressed Dunavant rule 3.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_03[3*2] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.600000000000000, 0.200000000000000, 0.200000000000000
+ };
+ double suborder_w_rule_03[2] = {
+ -0.562500000000000,
+ 0.520833333333333
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_03[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_03[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_03[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_03[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_04(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_04 returns a compressed Dunavant rule 4.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_04[3*2] = {
+ 0.108103018168070, 0.445948490915965, 0.445948490915965,
+ 0.816847572980459, 0.091576213509771, 0.091576213509771
+ };
+ double suborder_w_rule_04[2] = {
+ 0.223381589678011,
+ 0.109951743655322
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_04[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_04[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_04[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_04[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_05(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_05 returns a compressed Dunavant rule 5.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_05[3*3] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.059715871789770, 0.470142064105115, 0.470142064105115,
+ 0.797426985353087, 0.101286507323456, 0.101286507323456
+ };
+ double suborder_w_rule_05[3] = {
+ 0.225000000000000,
+ 0.132394152788506,
+ 0.125939180544827
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_05[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_05[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_05[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_05[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_06(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_06 returns a compressed Dunavant rule 6.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_06[3*3] = {
+ 0.501426509658179, 0.249286745170910, 0.249286745170910,
+ 0.873821971016996, 0.063089014491502, 0.063089014491502,
+ 0.053145049844817, 0.310352451033784, 0.636502499121399
+ };
+ double suborder_w_rule_06[3] = {
+ 0.116786275726379,
+ 0.050844906370207,
+ 0.082851075618374
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_06[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_06[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_06[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_06[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_07(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_07 returns a compressed Dunavant rule 7.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_07[3*4] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.479308067841920, 0.260345966079040, 0.260345966079040,
+ 0.869739794195568, 0.065130102902216, 0.065130102902216,
+ 0.048690315425316, 0.312865496004874, 0.638444188569810
+ };
+ double suborder_w_rule_07[4] = {
+ -0.149570044467682,
+ 0.175615257433208,
+ 0.053347235608838,
+ 0.077113760890257
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_07[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_07[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_07[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_07[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_08(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_08 returns a compressed Dunavant rule 8.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_08[3*5] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.081414823414554, 0.459292588292723, 0.459292588292723,
+ 0.658861384496480, 0.170569307751760, 0.170569307751760,
+ 0.898905543365938, 0.050547228317031, 0.050547228317031,
+ 0.008394777409958, 0.263112829634638, 0.728492392955404
+ };
+ double suborder_w_rule_08[5] = {
+ 0.144315607677787,
+ 0.095091634267285,
+ 0.103217370534718,
+ 0.032458497623198,
+ 0.027230314174435
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_08[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_08[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_08[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_08[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_09(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_09 returns a compressed Dunavant rule 9.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_09[3*6] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.020634961602525, 0.489682519198738, 0.489682519198738,
+ 0.125820817014127, 0.437089591492937, 0.437089591492937,
+ 0.623592928761935, 0.188203535619033, 0.188203535619033,
+ 0.910540973211095, 0.044729513394453, 0.044729513394453,
+ 0.036838412054736, 0.221962989160766, 0.741198598784498
+ };
+ double suborder_w_rule_09[6] = {
+ 0.097135796282799,
+ 0.031334700227139,
+ 0.077827541004774,
+ 0.079647738927210,
+ 0.025577675658698,
+ 0.043283539377289
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_09[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_09[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_09[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_09[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_10(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_10 returns a compressed Dunavant rule 10.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_10[3*6] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.028844733232685, 0.485577633383657, 0.485577633383657,
+ 0.781036849029926, 0.109481575485037, 0.109481575485037,
+ 0.141707219414880, 0.307939838764121, 0.550352941820999,
+ 0.025003534762686, 0.246672560639903, 0.728323904597411,
+ 0.009540815400299, 0.066803251012200, 0.923655933587500
+ };
+ double suborder_w_rule_10[6] = {
+ 0.090817990382754,
+ 0.036725957756467,
+ 0.045321059435528,
+ 0.072757916845420,
+ 0.028327242531057,
+ 0.009421666963733
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_10[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_10[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_10[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_10[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_11(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_11 returns a compressed Dunavant rule 11.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_11[3*7] = {
+ -0.069222096541517, 0.534611048270758, 0.534611048270758,
+ 0.202061394068290, 0.398969302965855, 0.398969302965855,
+ 0.593380199137435, 0.203309900431282, 0.203309900431282,
+ 0.761298175434837, 0.119350912282581, 0.119350912282581,
+ 0.935270103777448, 0.032364948111276, 0.032364948111276,
+ 0.050178138310495, 0.356620648261293, 0.593201213428213,
+ 0.021022016536166, 0.171488980304042, 0.807489003159792
+ };
+ double suborder_w_rule_11[7] = {
+ 0.000927006328961,
+ 0.077149534914813,
+ 0.059322977380774,
+ 0.036184540503418,
+ 0.013659731002678,
+ 0.052337111962204,
+ 0.020707659639141
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_11[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_11[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_11[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_11[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_12(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_12 returns a compressed Dunavant rule 12.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_12[3*8] = {
+ 0.023565220452390, 0.488217389773805, 0.488217389773805,
+ 0.120551215411079, 0.439724392294460, 0.439724392294460,
+ 0.457579229975768, 0.271210385012116, 0.271210385012116,
+ 0.744847708916828, 0.127576145541586, 0.127576145541586,
+ 0.957365299093579, 0.021317350453210, 0.021317350453210,
+ 0.115343494534698, 0.275713269685514, 0.608943235779788,
+ 0.022838332222257, 0.281325580989940, 0.695836086787803,
+ 0.025734050548330, 0.116251915907597, 0.858014033544073
+ };
+ double suborder_w_rule_12[8] = {
+ 0.025731066440455,
+ 0.043692544538038,
+ 0.062858224217885,
+ 0.034796112930709,
+ 0.006166261051559,
+ 0.040371557766381,
+ 0.022356773202303,
+ 0.017316231108659
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_12[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_12[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_12[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_12[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_13(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_13 returns a compressed Dunavant rule 13.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_13[3*10] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.009903630120591, 0.495048184939705, 0.495048184939705,
+ 0.062566729780852, 0.468716635109574, 0.468716635109574,
+ 0.170957326397447, 0.414521336801277, 0.414521336801277,
+ 0.541200855914337, 0.229399572042831, 0.229399572042831,
+ 0.771151009607340, 0.114424495196330, 0.114424495196330,
+ 0.950377217273082, 0.024811391363459, 0.024811391363459,
+ 0.094853828379579, 0.268794997058761, 0.636351174561660,
+ 0.018100773278807, 0.291730066734288, 0.690169159986905,
+ 0.022233076674090, 0.126357385491669, 0.851409537834241
+ };
+ double suborder_w_rule_13[10] = {
+ 0.052520923400802,
+ 0.011280145209330,
+ 0.031423518362454,
+ 0.047072502504194,
+ 0.047363586536355,
+ 0.031167529045794,
+ 0.007975771465074,
+ 0.036848402728732,
+ 0.017401463303822,
+ 0.015521786839045
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_13[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_13[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_13[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_13[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_14(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_14 returns a compressed Dunavant rule 14.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_14[3*10] = {
+ 0.022072179275643, 0.488963910362179, 0.488963910362179,
+ 0.164710561319092, 0.417644719340454, 0.417644719340454,
+ 0.453044943382323, 0.273477528308839, 0.273477528308839,
+ 0.645588935174913, 0.177205532412543, 0.177205532412543,
+ 0.876400233818255, 0.061799883090873, 0.061799883090873,
+ 0.961218077502598, 0.019390961248701, 0.019390961248701,
+ 0.057124757403648, 0.172266687821356, 0.770608554774996,
+ 0.092916249356972, 0.336861459796345, 0.570222290846683,
+ 0.014646950055654, 0.298372882136258, 0.686980167808088,
+ 0.001268330932872, 0.118974497696957, 0.879757171370171
+ };
+ double suborder_w_rule_14[10] = {
+ 0.021883581369429,
+ 0.032788353544125,
+ 0.051774104507292,
+ 0.042162588736993,
+ 0.014433699669777,
+ 0.004923403602400,
+ 0.024665753212564,
+ 0.038571510787061,
+ 0.014436308113534,
+ 0.005010228838501
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_14[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_14[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_14[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_14[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_15(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_15 returns a compressed Dunavant rule 15.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_15[3*11] = {
+ -0.013945833716486, 0.506972916858243, 0.506972916858243,
+ 0.137187291433955, 0.431406354283023, 0.431406354283023,
+ 0.444612710305711, 0.277693644847144, 0.277693644847144,
+ 0.747070217917492, 0.126464891041254, 0.126464891041254,
+ 0.858383228050628, 0.070808385974686, 0.070808385974686,
+ 0.962069659517853, 0.018965170241073, 0.018965170241073,
+ 0.133734161966621, 0.261311371140087, 0.604954466893291,
+ 0.036366677396917, 0.388046767090269, 0.575586555512814,
+ -0.010174883126571, 0.285712220049916, 0.724462663076655,
+ 0.036843869875878, 0.215599664072284, 0.747556466051838,
+ 0.012459809331199, 0.103575616576386, 0.883964574092416
+ };
+ double suborder_w_rule_15[11] = {
+ 0.001916875642849,
+ 0.044249027271145,
+ 0.051186548718852,
+ 0.023687735870688,
+ 0.013289775690021,
+ 0.004748916608192,
+ 0.038550072599593,
+ 0.027215814320624,
+ 0.002182077366797,
+ 0.021505319847731,
+ 0.007673942631049
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_15[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_15[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_15[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_15[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_16(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_16 returns a compressed Dunavant rule 16.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_16[3*13] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.005238916103123, 0.497380541948438, 0.497380541948438,
+ 0.173061122901295, 0.413469438549352, 0.413469438549352,
+ 0.059082801866017, 0.470458599066991, 0.470458599066991,
+ 0.518892500060958, 0.240553749969521, 0.240553749969521,
+ 0.704068411554854, 0.147965794222573, 0.147965794222573,
+ 0.849069624685052, 0.075465187657474, 0.075465187657474,
+ 0.966807194753950, 0.016596402623025, 0.016596402623025,
+ 0.103575692245252, 0.296555596579887, 0.599868711174861,
+ 0.020083411655416, 0.337723063403079, 0.642193524941505,
+ -0.004341002614139, 0.204748281642812, 0.799592720971327,
+ 0.041941786468010, 0.189358492130623, 0.768699721401368,
+ 0.014317320230681, 0.085283615682657, 0.900399064086661
+ };
+ double suborder_w_rule_16[13] = {
+ 0.046875697427642,
+ 0.006405878578585,
+ 0.041710296739387,
+ 0.026891484250064,
+ 0.042132522761650,
+ 0.030000266842773,
+ 0.014200098925024,
+ 0.003582462351273,
+ 0.032773147460627,
+ 0.015298306248441,
+ 0.002386244192839,
+ 0.019084792755899,
+ 0.006850054546542
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_16[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_16[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_16[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_16[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_17(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_17 returns a compressed Dunavant rule 17.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_17[3*15] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.005658918886452, 0.497170540556774, 0.497170540556774,
+ 0.035647354750751, 0.482176322624625, 0.482176322624625,
+ 0.099520061958437, 0.450239969020782, 0.450239969020782,
+ 0.199467521245206, 0.400266239377397, 0.400266239377397,
+ 0.495717464058095, 0.252141267970953, 0.252141267970953,
+ 0.675905990683077, 0.162047004658461, 0.162047004658461,
+ 0.848248235478508, 0.075875882260746, 0.075875882260746,
+ 0.968690546064356, 0.015654726967822, 0.015654726967822,
+ 0.010186928826919, 0.334319867363658, 0.655493203809423,
+ 0.135440871671036, 0.292221537796944, 0.572337590532020,
+ 0.054423924290583, 0.319574885423190, 0.626001190286228,
+ 0.012868560833637, 0.190704224192292, 0.796427214974071,
+ 0.067165782413524, 0.180483211648746, 0.752351005937729,
+ 0.014663182224828, 0.080711313679564, 0.904625504095608
+ };
+ double suborder_w_rule_17[15] = {
+ 0.033437199290803,
+ 0.005093415440507,
+ 0.014670864527638,
+ 0.024350878353672,
+ 0.031107550868969,
+ 0.031257111218620,
+ 0.024815654339665,
+ 0.014056073070557,
+ 0.003194676173779,
+ 0.008119655318993,
+ 0.026805742283163,
+ 0.018459993210822,
+ 0.008476868534328,
+ 0.018292796770025,
+ 0.006665632004165
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_17[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_17[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_17[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_17[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_18(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_18 returns a compressed Dunavant rule 18.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_18[3*17] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.013310382738157, 0.493344808630921, 0.493344808630921,
+ 0.061578811516086, 0.469210594241957, 0.469210594241957,
+ 0.127437208225989, 0.436281395887006, 0.436281395887006,
+ 0.210307658653168, 0.394846170673416, 0.394846170673416,
+ 0.500410862393686, 0.249794568803157, 0.249794568803157,
+ 0.677135612512315, 0.161432193743843, 0.161432193743843,
+ 0.846803545029257, 0.076598227485371, 0.076598227485371,
+ 0.951495121293100, 0.024252439353450, 0.024252439353450,
+ 0.913707265566071, 0.043146367216965, 0.043146367216965,
+ 0.008430536202420, 0.358911494940944, 0.632657968856636,
+ 0.131186551737188, 0.294402476751957, 0.574410971510855,
+ 0.050203151565675, 0.325017801641814, 0.624779046792512,
+ 0.066329263810916, 0.184737559666046, 0.748933176523037,
+ 0.011996194566236, 0.218796800013321, 0.769207005420443,
+ 0.014858100590125, 0.101179597136408, 0.883962302273467,
+ -0.035222015287949, 0.020874755282586, 1.014347260005363
+ };
+ double suborder_w_rule_18[17] = {
+ 0.030809939937647,
+ 0.009072436679404,
+ 0.018761316939594,
+ 0.019441097985477,
+ 0.027753948610810,
+ 0.032256225351457,
+ 0.025074032616922,
+ 0.015271927971832,
+ 0.006793922022963,
+ -0.002223098729920,
+ 0.006331914076406,
+ 0.027257538049138,
+ 0.017676785649465,
+ 0.018379484638070,
+ 0.008104732808192,
+ 0.007634129070725,
+ 0.000046187660794
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_18[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_18[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_18[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_18[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_19(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_19 returns a compressed Dunavant rule 19.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_19[3*17] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ 0.020780025853987, 0.489609987073006, 0.489609987073006,
+ 0.090926214604215, 0.454536892697893, 0.454536892697893,
+ 0.197166638701138, 0.401416680649431, 0.401416680649431,
+ 0.488896691193805, 0.255551654403098, 0.255551654403098,
+ 0.645844115695741, 0.177077942152130, 0.177077942152130,
+ 0.779877893544096, 0.110061053227952, 0.110061053227952,
+ 0.888942751496321, 0.055528624251840, 0.055528624251840,
+ 0.974756272445543, 0.012621863777229, 0.012621863777229,
+ 0.003611417848412, 0.395754787356943, 0.600633794794645,
+ 0.134466754530780, 0.307929983880436, 0.557603261588784,
+ 0.014446025776115, 0.264566948406520, 0.720987025817365,
+ 0.046933578838178, 0.358539352205951, 0.594527068955871,
+ 0.002861120350567, 0.157807405968595, 0.839331473680839,
+ 0.223861424097916, 0.075050596975911, 0.701087978926173,
+ 0.034647074816760, 0.142421601113383, 0.822931324069857,
+ 0.010161119296278, 0.065494628082938, 0.924344252620784
+ };
+ double suborder_w_rule_19[17] = {
+ 0.032906331388919,
+ 0.010330731891272,
+ 0.022387247263016,
+ 0.030266125869468,
+ 0.030490967802198,
+ 0.024159212741641,
+ 0.016050803586801,
+ 0.008084580261784,
+ 0.002079362027485,
+ 0.003884876904981,
+ 0.025574160612022,
+ 0.008880903573338,
+ 0.016124546761731,
+ 0.002491941817491,
+ 0.018242840118951,
+ 0.010258563736199,
+ 0.003799928855302
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_19[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_19[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_19[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_19[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::dunavant_subrule_20(int suborder_num, std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// DUNAVANT_SUBRULE_20 returns a compressed Dunavant rule 20.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 11 December 2006
+//
+// Author:
+//
+// John Burkardt
+//
+// Reference:
+//
+// David Dunavant,
+// High Degree Efficient Symmetrical Gaussian Quadrature Rules
+// for the Triangle,
+// International Journal for Numerical Methods in Engineering,
+// Volume 21, 1985, pages 1129-1148.
+//
+// James Lyness, Dennis Jespersen,
+// Moderate Degree Symmetric Quadrature Rules for the Triangle,
+// Journal of the Institute of Mathematics and its Applications,
+// Volume 15, Number 1, February 1975, pages 19-32.
+//
+// Parameters:
+//
+// Input, int SUBORDER_NUM, the number of suborders of the rule.
+//
+// Output, double SUBORDER_XYZ[3*SUBORDER_NUM],
+// the barycentric coordinates of the abscissas.
+//
+// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights.
+//
+{
+ int s;
+ double suborder_xy_rule_20[3*19] = {
+ 0.333333333333333, 0.333333333333333, 0.333333333333333,
+ -0.001900928704400, 0.500950464352200, 0.500950464352200,
+ 0.023574084130543, 0.488212957934729, 0.488212957934729,
+ 0.089726636099435, 0.455136681950283, 0.455136681950283,
+ 0.196007481363421, 0.401996259318289, 0.401996259318289,
+ 0.488214180481157, 0.255892909759421, 0.255892909759421,
+ 0.647023488009788, 0.176488255995106, 0.176488255995106,
+ 0.791658289326483, 0.104170855336758, 0.104170855336758,
+ 0.893862072318140, 0.053068963840930, 0.053068963840930,
+ 0.916762569607942, 0.041618715196029, 0.041618715196029,
+ 0.976836157186356, 0.011581921406822, 0.011581921406822,
+ 0.048741583664839, 0.344855770229001, 0.606402646106160,
+ 0.006314115948605, 0.377843269594854, 0.615842614456541,
+ 0.134316520547348, 0.306635479062357, 0.559048000390295,
+ 0.013973893962392, 0.249419362774742, 0.736606743262866,
+ 0.075549132909764, 0.212775724802802, 0.711675142287434,
+ -0.008368153208227, 0.146965436053239, 0.861402717154987,
+ 0.026686063258714, 0.137726978828923, 0.835586957912363,
+ 0.010547719294141, 0.059696109149007, 0.929756171556853
+ };
+ double suborder_w_rule_20[19] = {
+ 0.033057055541624,
+ 0.000867019185663,
+ 0.011660052716448,
+ 0.022876936356421,
+ 0.030448982673938,
+ 0.030624891725355,
+ 0.024368057676800,
+ 0.015997432032024,
+ 0.007698301815602,
+ -0.000632060497488,
+ 0.001751134301193,
+ 0.016465839189576,
+ 0.004839033540485,
+ 0.025804906534650,
+ 0.008471091054441,
+ 0.018354914106280,
+ 0.000704404677908,
+ 0.010112684927462,
+ 0.003573909385950
+ };
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_xyz[0+s*3] = suborder_xy_rule_20[0+s*3];
+ suborder_xyz[1+s*3] = suborder_xy_rule_20[1+s*3];
+ suborder_xyz[2+s*3] = suborder_xy_rule_20[2+s*3];
+ }
+
+ for (s = 0; s < suborder_num; s++)
+ {
+ suborder_w[s] = suborder_w_rule_20[s];
+ }
+
+ return;
+}
+//****************************************************************************80
+
+int SimplexQuadrature::i4_modp(int i, int j)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// I4_MODP returns the nonnegative remainder of I4 division.
+//
+// Formula:
+//
+// If
+// NREM = I4_MODP(I, J)
+// NMULT = (I - NREM) / J
+// then
+// I = J * NMULT + NREM
+// where NREM is always nonnegative.
+//
+// Discussion:
+//
+// The MOD function computes a result with the same sign as the
+// quantity being divided. Thus, suppose you had an angle A,
+// and you wanted to ensure that it was between 0 and 360.
+// Then mod(A,360) would do, if A was positive, but if A
+// was negative, your result would be between -360 and 0.
+//
+// On the other hand, I4_MODP(A,360) is between 0 and 360, always.
+//
+// Example:
+//
+// I J MOD I4_MODP I4_MODP Factorization
+//
+// 107 50 7 7 107 = 2 * 50 + 7
+// 107 -50 7 7 107 = -2 * -50 + 7
+// -107 50 -7 43 -107 = -3 * 50 + 43
+// -107 -50 -7 43 -107 = 3 * -50 + 43
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 26 May 1999
+//
+// Author:
+//
+// John Burkardt
+//
+// Parameters:
+//
+// Input, int I, the number to be divided.
+//
+// Input, int J, the number that divides I.
+//
+// Output, int I4_MODP, the nonnegative remainder when I is
+// divided by J.
+//
+{
+ int value;
+
+ if (j == 0)
+ {
+ dolfin_error("SimplexQuadrature.cpp",
+ "compute quadrature rule for triangle",
+ "i4_modp must have non-zero j, which is here ", j);
+ }
+
+ value = i % j;
+
+ if (value < 0)
+ {
+ value = value + abs(j);
+ }
+
+ return value;
+}
+//****************************************************************************80*
+
+int SimplexQuadrature::i4_wrap(int ival, int ilo, int ihi)
+
+//****************************************************************************80*
+//
+// Purpose:
+//
+// I4_WRAP forces an integer to lie between given limits by wrapping.
+//
+// Example:
+//
+// ILO = 4, IHI = 8
+//
+// I Value
+//
+// -2 8
+// -1 4
+// 0 5
+// 1 6
+// 2 7
+// 3 8
+// 4 4
+// 5 5
+// 6 6
+// 7 7
+// 8 8
+// 9 4
+// 10 5
+// 11 6
+// 12 7
+// 13 8
+// 14 4
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 19 August 2003
+//
+// Author:
+//
+// John Burkardt
+//
+// Parameters:
+//
+// Input, int IVAL, an integer value.
+//
+// Input, int ILO, IHI, the desired bounds for the integer value.
+//
+// Output, int I4_WRAP, a "wrapped" version of IVAL.
+//
+{
+ int jhi;
+ int jlo;
+ int value;
+ int wide;
+
+ jlo = std::min(ilo, ihi);
+ jhi = std::max(ilo, ihi);
+
+ wide = jhi + 1 - jlo;
+
+ if (wide == 1)
+ {
+ value = jlo;
+ }
+ else
+ {
+ value = jlo + i4_modp(ival - jlo, wide);
+ }
+
+ return value;
+}
+//-----------------------------------------------------------------------------
+void SimplexQuadrature::legendre_compute_glr(std::size_t n,
+ std::vector<double>& x,
+ std::vector<double>& w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// LEGENDRE_COMPUTE_GLR: Legendre quadrature by the Glaser-Liu-Rokhlin method.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 20 October 2009
+//
+// Author:
+//
+// Original C++ version by Nick Hale.
+// This C++ version by John Burkardt.
+//
+// Reference:
+//
+// Andreas Glaser, Xiangtao Liu, Vladimir Rokhlin,
+// A fast algorithm for the calculation of the roots of special functions,
+// SIAM Journal on Scientific Computing,
+// Volume 29, Number 4, pages 1420-1438, 2007.
+//
+// Parameters:
+//
+// Input, int N, the order.
+//
+// Output, double X[N], the abscissas.
+//
+// Output, double W[N], the weights.
+//
+{
+ x.resize(n);
+ w.resize(n);
+
+ std::size_t i;
+ double p;
+ double pp;
+ double w_sum;
+ //
+ // Get the value and derivative of the N-th Legendre polynomial at 0.
+ //
+ legendre_compute_glr0(n, p, pp);
+ //
+ // If N is odd, then zero is a root.
+ //
+ if (n % 2 == 1)
+ {
+ x[(n-1)/2] = p;
+ w[(n-1)/2] = pp;
+ }
+ //
+ // If N is even, we have to call a function to find the first root.
+ //
+ else
+ {
+ legendre_compute_glr2(p, n, x[n/2], w[n/2]);
+ }
+ //
+ // Get the complete set of roots and derivatives.
+ //
+ legendre_compute_glr1(n, x, w);
+ //
+ // Compute the W.
+ //
+ for (i = 0; i < n; i++)
+ {
+ w[i] = 2.0 /(1.0 - x[i]) /(1.0 + x[i]) / w[i] / w[i];
+ }
+ w_sum = 0.0;
+ for (i = 0; i < n; i++)
+ {
+ w_sum = w_sum + w[i];
+ }
+ for (i = 0; i < n; i++)
+ {
+ w[i] = 2.0 * w[i] / w_sum;
+ }
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::legendre_compute_glr0(std::size_t n, double& p, double& pp)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// LEGENDRE_COMPUTE_GLR0 gets a starting value for the fast algorithm.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 19 October 2009
+//
+// Author:
+//
+// Original C++ version by Nick Hale.
+// This C++ version by John Burkardt.
+//
+// Reference:
+//
+// Andreas Glaser, Xiangtao Liu, Vladimir Rokhlin,
+// A fast algorithm for the calculation of the roots of special functions,
+// SIAM Journal on Scientific Computing,
+// Volume 29, Number 4, pages 1420-1438, 2007.
+//
+// Parameters:
+//
+// Input, int N, the order of the Legendre polynomial.
+//
+// Output, double *P, *PP, the value of the N-th Legendre polynomial
+// and its derivative at 0.
+//
+{
+ double dk;
+ std::size_t k;
+ double pm1;
+ double pm2;
+ double ppm1;
+ double ppm2;
+
+ pm2 = 0.0;
+ pm1 = 1.0;
+ ppm2 = 0.0;
+ ppm1 = 0.0;
+
+ for (k = 0; k < n; k++)
+ {
+ dk = static_cast<double>(k);
+ p = - dk * pm2 /(dk + 1.0);
+ pp = (( 2.0 * dk + 1.0) * pm1 - dk * ppm2) /(dk + 1.0);
+ pm2 = pm1;
+ pm1 = p;
+ ppm2 = ppm1;
+ ppm1 = pp;
+ }
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::legendre_compute_glr1(std::size_t n_,
+ std::vector<double>& x,
+ std::vector<double>& w)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// LEGENDRE_COMPUTE_GLR1 gets the complete set of Legendre points and weights.
+//
+// Discussion:
+//
+// This routine requires that a starting estimate be provided for one
+// root and its derivative. This information will be stored in entry
+// (N+1)/2 if N is odd, or N/2 if N is even, of X and W.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 19 October 2009
+//
+// Author:
+//
+// Original C++ version by Nick Hale.
+// This C++ version by John Burkardt.
+//
+// Reference:
+//
+// Andreas Glaser, Xiangtao Liu, Vladimir Rokhlin,
+// A fast algorithm for the calculation of the roots of special functions,
+// SIAM Journal on Scientific Computing,
+// Volume 29, Number 4, pages 1420-1438, 2007.
+//
+// Parameters:
+//
+// Input, int N, the order of the Legendre polynomial.
+//
+// Input/output, double X[N]. On input, a starting value
+// has been set in one entry. On output, the roots of the Legendre
+// polynomial.
+//
+// Input/output, double W[N]. On input, a starting value
+// has been set in one entry. On output, the derivatives of the Legendre
+// polynomial at the zeros.
+//
+// Local Parameters:
+//
+// Local, int M, the number of terms in the Taylor expansion.
+//
+{
+ double dk;
+ double dn;
+ double h;
+ int j;
+ int k;
+ int l;
+ int m = 30;
+ int n2;
+ int s;
+ // double *u;
+ // double *up;
+ double xp;
+
+ int n = static_cast<int>(n_);
+
+ if (n % 2 == 1)
+ {
+ n2 = (n - 1) / 2 - 1;
+ s = 1;
+ }
+ else
+ {
+ n2 = n / 2 - 1;
+ s = 0;
+ }
+
+ // u = new double[m+2];
+ // up = new double[m+1];
+ std::vector<double> u(m+2);
+ std::vector<double> up(m+1);
+
+ dn = static_cast<double>(n);
+
+ for (j = n2 + 1; j < n - 1; j++)
+ {
+ xp = x[j];
+
+ h = rk2_leg(DOLFIN_PI/2.0, -DOLFIN_PI/2.0, xp, n) - xp;
+
+ u[0] = 0.0;
+ u[1] = 0.0;
+ u[2] = w[j];
+
+ up[0] = 0.0;
+ up[1] = u[2];
+
+ for (k = 0; k <= m - 2; k++)
+ {
+ dk = static_cast<double>(k);
+
+ u[k+3] =
+ (
+ 2.0 * xp *(dk + 1.0) * u[k+2]
+ +(dk *(dk + 1.0) - dn *(dn + 1.0)) * u[k+1] /(dk + 1.0)
+ ) /(1.0 - xp) /(1.0 + xp) /(dk + 2.0);
+
+ up[k+2] = (dk + 2.0) * u[k+3];
+ }
+
+ for (l = 0; l < 5; l++)
+ {
+ h = h - ts_mult(u, h, m) / ts_mult(up, h, m-1);
+ }
+
+ x[j+1] = xp + h;
+ w[j+1] = ts_mult(up, h, m - 1);
+ }
+
+ for (k = 0; k <= n2 + s; k++)
+ {
+ x[k] = - x[n-1-k];
+ w[k] = w[n-1-k];
+ }
+ return;
+}
+//****************************************************************************80
+
+void SimplexQuadrature::legendre_compute_glr2(double pn0, int n, double& x1, double& d1)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// LEGENDRE_COMPUTE_GLR2 finds the first real root.
+//
+// Discussion:
+//
+// This function is only called if N is even.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 19 October 2009
+//
+// Author:
+//
+// Original C++ version by Nick Hale.
+// This C++ version by John Burkardt.
+//
+// Reference:
+//
+// Andreas Glaser, Xiangtao Liu, Vladimir Rokhlin,
+// A fast algorithm for the calculation of the roots of special functions,
+// SIAM Journal on Scientific Computing,
+// Volume 29, Number 4, pages 1420-1438, 2007.
+//
+// Parameters:
+//
+// Input, double PN0, the value of the N-th Legendre polynomial
+// at 0.
+//
+// Input, int N, the order of the Legendre polynomial.
+//
+// Output, double *X1, the first real root.
+//
+// Output, double *D1, the derivative at X1.
+//
+// Local Parameters:
+//
+// Local, int M, the number of terms in the Taylor expansion.
+//
+{
+ double dk;
+ double dn;
+ int k;
+ int l;
+ int m = 30;
+ double t;
+ // double *u;
+ // double *up;
+
+ t = 0.0;
+ x1 = rk2_leg(t, -DOLFIN_PI/2.0, 0.0, n);
+
+ // u = new double[m+2];
+ // up = new double[m+1];
+ std::vector<double> u(m+2);
+ std::vector<double> up(m+1);
+
+ dn = static_cast<double>(n);
+ //
+ // U[0] and UP[0] are never used.
+ // U[M+1] is set, but not used, and UP[M] is set and not used.
+ // What gives?
+ //
+ u[0] = 0.0;
+ u[1] = pn0;
+
+ up[0] = 0.0;
+
+ for (k = 0; k <= m - 2; k = k + 2)
+ {
+ dk = static_cast<double>(k);
+
+ u[k+2] = 0.0;
+ u[k+3] = (dk *(dk + 1.0) - dn *(dn + 1.0)) * u[k+1]
+ / (dk + 1.0) / (dk + 2.0);
+
+ up[k+1] = 0.0;
+ up[k+2] = (dk + 2.0) * u[k+3];
+ }
+
+ for (l = 0; l < 5; l++)
+ {
+ x1 = x1 - ts_mult(u, x1, m) / ts_mult(up, x1, m-1);
+ }
+ d1 = ts_mult(up, x1, m-1);
+
+ return;
+}
+//****************************************************************************80
+
+double SimplexQuadrature::rk2_leg(double t1, double t2, double x, int n)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// RK2_LEG advances the value of X(T) using a Runge-Kutta method.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 22 October 2009
+//
+// Author:
+//
+// Original C++ version by Nick Hale.
+// This C++ version by John Burkardt.
+//
+// Parameters:
+//
+// Input, double T1, T2, the range of the integration interval.
+//
+// Input, double X, the value of X at T1.
+//
+// Input, int N, the number of steps to take.
+//
+// Output, double RK2_LEG, the value of X at T2.
+//
+{
+ double f;
+ double h;
+ int j;
+ double k1;
+ double k2;
+ int m = 10;
+ double snn1;
+ double t;
+
+ h = (t2 - t1) / static_cast<double>(m);
+ snn1 = sqrt(static_cast<double>(n *(n + 1)));
+ t = t1;
+
+ for (j = 0; j < m; j++)
+ {
+ f = (1.0 - x) *(1.0 + x);
+ k1 = - h * f /(snn1 * sqrt(f) - 0.5 * x * sin(2.0 * t));
+ x = x + k1;
+
+ t = t + h;
+
+ f = (1.0 - x) *(1.0 + x);
+ k2 = - h * f /(snn1 * sqrt(f) - 0.5 * x * sin(2.0 * t));
+ x = x + 0.5 *(k2 - k1);
+ }
+ return x;
+}
+//****************************************************************************80
+
+double SimplexQuadrature::ts_mult(std::vector<double>& u, double h, int n)
+
+//****************************************************************************80
+//
+// Purpose:
+//
+// TS_MULT evaluates a polynomial.
+//
+// Licensing:
+//
+// This code is distributed under the GNU LGPL license.
+//
+// Modified:
+//
+// 17 May 2013
+//
+// Author:
+//
+// Original C++ version by Nick Hale.
+// This C++ version by John Burkardt.
+//
+// Parameters:
+//
+// Input, double U[N+1], the polynomial coefficients.
+// U[0] is ignored.
+//
+// Input, double H, the polynomial argument.
+//
+// Input, int N, the number of terms to compute.
+//
+// Output, double TS_MULT, the value of the polynomial.
+//
+{
+ double hk;
+ int k;
+ double ts;
+
+ ts = 0.0;
+ hk = 1.0;
+ for (k = 1; k<= n; k++)
+ {
+ ts = ts + u[k] * hk;
+ hk = hk * h;
+ }
+ return ts;
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/geometry/SimplexQuadrature.h b/dolfin/geometry/SimplexQuadrature.h
index aafaa9f..be36ce3 100644
--- a/dolfin/geometry/SimplexQuadrature.h
+++ b/dolfin/geometry/SimplexQuadrature.h
@@ -16,12 +16,13 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2014-02-24
-// Last changed: 2014-04-28
+// Last changed: 2017-09-22
#ifndef __SIMPLEX_QUADRATURE_H
#define __SIMPLEX_QUADRATURE_H
#include <vector>
+#include <Eigen/Dense>
#include "Point.h"
namespace dolfin
@@ -30,12 +31,22 @@ namespace dolfin
// Forward declarations
class Cell;
- /// Quadrature on simplices
+ /// This class defines quadrature rules for simplices.
class SimplexQuadrature
{
public:
+ /// Create SimplexQuadrature rules for reference simplex
+ ///
+ /// *Arguments*
+ /// tdim (std::size_t)
+ /// The topological dimension of the simplex.
+ /// order (std::size_t)
+ /// The order of convergence of the quadrature rule.
+ ///
+ SimplexQuadrature(std::size_t tdim, std::size_t order);
+
/// Compute quadrature rule for cell.
///
/// *Arguments*
@@ -45,18 +56,18 @@ namespace dolfin
/// The order of convergence of the quadrature rule.
///
/// *Returns*
- /// std::pair<std::vector<double>, std::vector<double> >
+ /// std::pair<std::vector<double>, std::vector<double>>
/// A flattened array of quadrature points and a
/// corresponding array of quadrature weights.
- static std::pair<std::vector<double>, std::vector<double> >
- compute_quadrature_rule(const Cell& cell, std::size_t order);
+ std::pair<std::vector<double>, std::vector<double>>
+ compute_quadrature_rule(const Cell& cell,
+ std::size_t order) const;
/// Compute quadrature rule for simplex.
///
/// *Arguments*
- /// coordinates (double *)
- /// A flattened array of simplex coordinates of
- /// dimension num_vertices x gdim = (tdim + 1)*gdim.
+ /// coordinates (std::vector<Point>)
+ /// Vertex coordinates for the simplex
/// tdim (std::size_t)
/// The topological dimension of the simplex.
/// gdim (std::size_t)
@@ -65,75 +76,225 @@ namespace dolfin
/// The order of convergence of the quadrature rule.
///
/// *Returns*
- /// std::pair<std::vector<double>, std::vector<double> >
+ /// std::pair<std::vector<double>, std::vector<double>>
/// A flattened array of quadrature points and a
/// corresponding array of quadrature weights.
- static std::pair<std::vector<double>, std::vector<double> >
- compute_quadrature_rule(const double* coordinates,
- std::size_t tdim,
- std::size_t gdim,
- std::size_t order);
+ std::pair<std::vector<double>, std::vector<double>>
+ compute_quadrature_rule(const std::vector<Point>& coordinates,
+ std::size_t gdim,
+ std::size_t order) const;
/// Compute quadrature rule for interval.
///
/// *Arguments*
- /// coordinates (double *)
- /// A flattened array of simplex coordinates of
- /// dimension num_vertices x gdim = 2*gdim.
+ /// coordinates (std::vector<Point>)
+ /// Vertex coordinates for the simplex
/// gdim (std::size_t)
/// The geometric dimension.
/// order (std::size_t)
/// The order of convergence of the quadrature rule.
///
/// *Returns*
- /// std::pair<std::vector<double>, std::vector<double> >
+ /// std::pair<std::vector<double>, std::vector<double>>
/// A flattened array of quadrature points and a
/// corresponding array of quadrature weights.
- static std::pair<std::vector<double>, std::vector<double> >
- compute_quadrature_rule_interval(const double* coordinates,
- std::size_t gdim,
- std::size_t order);
+ std::pair<std::vector<double>, std::vector<double>>
+ compute_quadrature_rule_interval(const std::vector<Point>& coordinates,
+ std::size_t gdim,
+ std::size_t order) const;
/// Compute quadrature rule for triangle.
///
/// *Arguments*
- /// coordinates (double *)
- /// A flattened array of simplex coordinates of
- /// dimension num_vertices x gdim = 3*gdim.
+ /// coordinates (std::vector<Point>)
+ /// Vertex coordinates for the simplex
/// gdim (std::size_t)
/// The geometric dimension.
/// order (std::size_t)
/// The order of convergence of the quadrature rule.
///
/// *Returns*
- /// std::pair<std::vector<double>, std::vector<double> >
+ /// std::pair<std::vector<double>, std::vector<double>>
/// A flattened array of quadrature points and a
/// corresponding array of quadrature weights.
- static std::pair<std::vector<double>, std::vector<double> >
- compute_quadrature_rule_triangle(const double* coordinates,
+ std::pair<std::vector<double>, std::vector<double>>
+ compute_quadrature_rule_triangle(const std::vector<Point>& coordinates,
std::size_t gdim,
- std::size_t order);
-
+ std::size_t order) const;
+
/// Compute quadrature rule for tetrahedron.
///
/// *Arguments*
- /// coordinates (double *)
- /// A flattened array of simplex coordinates of
- /// dimension num_vertices x gdim = 4*gdim.
+ /// coordinates (std::vector<Point>)
+ /// Vertex coordinates for the simplex
/// gdim (std::size_t)
/// The geometric dimension.
/// order (std::size_t)
/// The order of convergence of the quadrature rule.
///
/// *Returns*
- /// std::pair<std::vector<double>, std::vector<double> >
+ /// std::pair<std::vector<double>, std::vector<double>>
/// A flattened array of quadrature points and a
/// corresponding array of quadrature weights.
+ std::pair<std::vector<double>, std::vector<double>>
+ compute_quadrature_rule_tetrahedron(const std::vector<Point>& coordinates,
+ std::size_t gdim,
+ std::size_t order) const;
+
+ /// Compress a quadrature rule using algorithms from
+ /// Compression of multivariate discrete measures and applications
+ /// A. Sommariva, M. Vianello
+ /// Numerical Functional Analysis and Optimization
+ /// Volume 36, 2015 - Issue 9
+ ///
+ /// *Arguments*
+ /// qr (std::pair<std::vector<double>, std::vector<double>>)
+ /// The quadrature rule to be compressed
+ /// gdim (std::size_t)
+ /// The geometric dimension
+ /// quadrature_order (std::size_t)
+ /// The order of the quadrature rule
+ ///
+ /// *Returns*
+ /// std::vector<std::size_t>
+ /// The indices of the points that were kept (empty
+ /// if no compression was made)
+ static std::vector<std::size_t>
+ compress(std::pair<std::vector<double>, std::vector<double>>& qr,
+ std::size_t gdim,
+ std::size_t quadrature_order);
+
+ private:
+
+ // Setup quadrature rule on a reference simplex
+ void setup_qr_reference_interval(std::size_t order);
+ void setup_qr_reference_triangle(std::size_t order);
+ void setup_qr_reference_tetrahedron(std::size_t order);
+
+ // Utility function for computing a Vandermonde type matrix in a
+ // Chebyshev basis
+ static Eigen::MatrixXd
+ Chebyshev_Vandermonde_matrix
+ (const std::pair<std::vector<double>, std::vector<double>>& qr,
+ std::size_t gdim, std::size_t N);
+
+ // Utility function for computing a Chebyshev basis
+ static std::vector<Eigen::VectorXd>
+ Chebyshev_polynomial(const Eigen::VectorXd& x, std::size_t N);
+
+ // Utility function for creating a matrix with coefficients in
+ // graded lexicographic order
+ static std::vector<std::vector<std::size_t>>
+ grlex(std::size_t gdim, std::size_t N);
+
+ // Utility function for calculating all combinations (n over k)
+ static std::size_t choose(std::size_t n, std::size_t k);
+
+ // The following code has been copied from
+ //
+ // https://people.sc.fsu.edu/~jburkardt/cpp_src/triangle_dunavant_rule/triangle_dunavant_rule.cpp
+ //
+ // License: LGPL
+
+ // Compute Duanvant quadrature rules for triangle
+
+ static void dunavant_rule(std::size_t order,
+ std::vector<std::vector<double> >& p,
+ std::vector<double>& w);
+ static std::size_t dunavant_order_num(std::size_t rule);
+ static std::vector<std::size_t> dunavant_suborder(int rule, int suborder_num);
+ static std::size_t dunavant_suborder_num(int rule);
+ static void dunavant_subrule(std::size_t rule,
+ std::size_t suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& w);
+ static void dunavant_subrule_01(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_02(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_03(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_04(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_05(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_06(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_07(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_08(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_09(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_10(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_11(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_12(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_13(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_14(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_15(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_16(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_17(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_18(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_19(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static void dunavant_subrule_20(int suborder_num,
+ std::vector<double>& suborder_xyz,
+ std::vector<double>& suborder_w);
+ static int i4_modp(int i, int j);
+ static int i4_wrap(int ival, int ilo, int ihi);
+
+ // The following code has been copied from
+ //
+ // https://people.sc.fsu.edu/~jburkardt/cpp_src/legendre_rule_fast/legendre_rule_fast.cpp
+ //
+ // License: LGPL
+
+ // Compute Gauss-Legendre quadrature rules for line
+
+ static void legendre_compute_glr(std::size_t n,
+ std::vector<double>& x,
+ std::vector<double>& w);
+ static void legendre_compute_glr0(std::size_t n,
+ double& p,
+ double& pp);
+ static void legendre_compute_glr1(std::size_t n,
+ std::vector<double>& x,
+ std::vector<double>& w);
+ static void legendre_compute_glr2(double pn0, int n, double& x1, double& d1);
+ static double ts_mult(std::vector<double>& u, double h, int n);
+ static double rk2_leg(double t1, double t2, double x, int n);
- static std::pair<std::vector<double>, std::vector<double> >
- compute_quadrature_rule_tetrahedron(const double* coordinates,
- std::size_t gdim,
- std::size_t order);
+ // Quadrature rule on reference simplex (points and weights)
+ std::vector<std::vector<double> > _p;
+ std::vector<double> _w;
};
diff --git a/dolfin/geometry/dolfin_geometry.h b/dolfin/geometry/dolfin_geometry.h
index 8efeaf5..d679c55 100644
--- a/dolfin/geometry/dolfin_geometry.h
+++ b/dolfin/geometry/dolfin_geometry.h
@@ -8,6 +8,7 @@
#include <dolfin/geometry/GenericBoundingBoxTree.h>
#include <dolfin/geometry/BoundingBoxTree3D.h>
#include <dolfin/geometry/MeshPointIntersection.h>
+#include <dolfin/geometry/CollisionPredicates.h>
#include <dolfin/geometry/intersect.h>
#endif
diff --git a/dolfin/geometry/intersect.cpp b/dolfin/geometry/intersect.cpp
index bc7364f..d67a197 100644
--- a/dolfin/geometry/intersect.cpp
+++ b/dolfin/geometry/intersect.cpp
@@ -16,9 +16,11 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2013-05-30
-// Last changed: 2013-05-30
+// Last changed: 2017-09-25
#include "MeshPointIntersection.h"
+#include <dolfin/mesh/CellType.h>
+#include <dolfin/mesh/Mesh.h>
#include "intersect.h"
using namespace dolfin;
@@ -27,6 +29,14 @@ using namespace dolfin;
std::shared_ptr<const MeshPointIntersection>
dolfin::intersect(const Mesh& mesh, const Point& point)
{
+ // Intersection is only implemented for simplex meshes
+ if (!mesh.type().is_simplex())
+ {
+ dolfin_error("intersect.cpp",
+ "intersect mesh and point",
+ "Intersection is only implemented for simplex meshes");
+ }
+
return std::shared_ptr<const MeshPointIntersection>
(new MeshPointIntersection(mesh, point));
}
diff --git a/dolfin/geometry/predicates.cpp b/dolfin/geometry/predicates.cpp
new file mode 100644
index 0000000..6b4be8c
--- /dev/null
+++ b/dolfin/geometry/predicates.cpp
@@ -0,0 +1,2372 @@
+#include <dolfin/geometry/Point.h>
+#include "predicates.h"
+
+//-----------------------------------------------------------------------------
+double dolfin::orient1d(double a, double b, double x)
+{
+ if (x > std::max(a, b)) return 1.0;
+ if (x < std::min(a, b)) return -1.0;
+ return 0.0;
+}
+//-----------------------------------------------------------------------------
+double dolfin::orient2d(const Point& a, const Point& b, const Point& c)
+{
+ return dolfin::_orient2d(a.coordinates(),
+ b.coordinates(),
+ c.coordinates());
+}
+//-----------------------------------------------------------------------------
+double dolfin::orient3d(const Point& a, const Point& b, const Point& c, const Point& d)
+{
+ return dolfin::_orient3d(a.coordinates(),
+ b.coordinates(),
+ c.coordinates(),
+ d.coordinates());
+}
+//-----------------------------------------------------------------------------
+
+/*****************************************************************************/
+/* */
+/* Routines for Arbitrary Precision Floating-point Arithmetic */
+/* and Fast Robust Geometric Predicates */
+/* (predicates.c) */
+/* */
+/* May 18, 1996 */
+/* */
+/* Placed in the public domain by */
+/* Jonathan Richard Shewchuk */
+/* School of Computer Science */
+/* Carnegie Mellon University */
+/* 5000 Forbes Avenue */
+/* Pittsburgh, Pennsylvania 15213-3891 */
+/* jrs at cs.cmu.edu */
+/* */
+/* This file contains C implementation of algorithms for exact addition */
+/* and multiplication of floating-point numbers, and predicates for */
+/* robustly performing the orientation and incircle tests used in */
+/* computational geometry. The algorithms and underlying theory are */
+/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */
+/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */
+/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */
+/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */
+/* Discrete & Computational Geometry.) */
+/* */
+/* This file, the paper listed above, and other information are available */
+/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* Using this code: */
+/* */
+/* First, read the short or long version of the paper (from the Web page */
+/* above). */
+/* */
+/* Be sure to call exactinit() once, before calling any of the arithmetic */
+/* functions or geometric predicates. Also be sure to turn on the */
+/* optimizer when compiling this file. */
+/* */
+/* */
+/* Several geometric predicates are defined. Their parameters are all */
+/* points. Each point is an array of two or three floating-point */
+/* numbers. The geometric predicates, described in the papers, are */
+/* */
+/* orient2d(pa, pb, pc) */
+/* orient2dfast(pa, pb, pc) */
+/* orient3d(pa, pb, pc, pd) */
+/* orient3dfast(pa, pb, pc, pd) */
+/* incircle(pa, pb, pc, pd) */
+/* incirclefast(pa, pb, pc, pd) */
+/* insphere(pa, pb, pc, pd, pe) */
+/* inspherefast(pa, pb, pc, pd, pe) */
+/* */
+/* Those with suffix "fast" are approximate, non-robust versions. Those */
+/* without the suffix are adaptive precision, robust versions. There */
+/* are also versions with the suffices "exact" and "slow", which are */
+/* non-adaptive, exact arithmetic versions, which I use only for timings */
+/* in my arithmetic papers. */
+/* */
+/* */
+/* An expansion is represented by an array of floating-point numbers, */
+/* sorted from smallest to largest magnitude (possibly with interspersed */
+/* zeros). The length of each expansion is stored as a separate integer, */
+/* and each arithmetic function returns an integer which is the length */
+/* of the expansion it created. */
+/* */
+/* Several arithmetic functions are defined. Their parameters are */
+/* */
+/* e, f Input expansions */
+/* elen, flen Lengths of input expansions (must be >= 1) */
+/* h Output expansion */
+/* b Input scalar */
+/* */
+/* The arithmetic functions are */
+/* */
+/* grow_expansion(elen, e, b, h) */
+/* grow_expansion_zeroelim(elen, e, b, h) */
+/* expansion_sum(elen, e, flen, f, h) */
+/* expansion_sum_zeroelim1(elen, e, flen, f, h) */
+/* expansion_sum_zeroelim2(elen, e, flen, f, h) */
+/* fast_expansion_sum(elen, e, flen, f, h) */
+/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */
+/* linear_expansion_sum(elen, e, flen, f, h) */
+/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */
+/* scale_expansion(elen, e, b, h) */
+/* scale_expansion_zeroelim(elen, e, b, h) */
+/* compress(elen, e, h) */
+/* */
+/* All of these are described in the long version of the paper; some are */
+/* described in the short version. All return an integer that is the */
+/* length of h. Those with suffix _zeroelim perform zero elimination, */
+/* and are recommended over their counterparts. The procedure */
+/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */
+/* processors that do not use the round-to-even tiebreaking rule) is */
+/* recommended over expansion_sum_zeroelim(). Each procedure has a */
+/* little note next to it (in the code below) that tells you whether or */
+/* not the output expansion may be the same array as one of the input */
+/* expansions. */
+/* */
+/* */
+/* If you look around below, you'll also find macros for a bunch of */
+/* simple unrolled arithmetic operations, and procedures for printing */
+/* expansions (commented out because they don't work with all C */
+/* compilers) and for generating random floating-point numbers whose */
+/* significand bits are all random. Most of the macros have undocumented */
+/* requirements that certain of their parameters should not be the same */
+/* variable; for safety, better to make sure all the parameters are */
+/* distinct variables. Feel free to send email to jrs at cs.cmu.edu if you */
+/* have questions. */
+/* */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/time.h>
+
+/* On some machines, the exact arithmetic routines might be defeated by the */
+/* use of internal extended precision floating-point registers. Sometimes */
+/* this problem can be fixed by defining certain values to be volatile, */
+/* thus forcing them to be stored to memory and rounded off. This isn't */
+/* a great solution, though, as it slows the arithmetic down. */
+/* */
+/* To try this out, write "#define INEXACT volatile" below. Normally, */
+/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */
+
+#define INEXACT /* Nothing */
+/* #define INEXACT volatile */
+
+#define REAL double /* float or double */
+#define REALPRINT doubleprint
+#define REALRAND doublerand
+#define NARROWRAND narrowdoublerand
+#define UNIFORMRAND uniformdoublerand
+
+/* Which of the following two methods of finding the absolute values is */
+/* fastest is compiler-dependent. A few compilers can inline and optimize */
+/* the fabs() call; but most will incur the overhead of a function call, */
+/* which is disastrously slow. A faster way on IEEE machines might be to */
+/* mask the appropriate bit, but that's difficult to do in C. */
+
+#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
+/* #define Absolute(a) fabs(a) */
+
+/* Many of the operations are broken up into two pieces, a main part that */
+/* performs an approximate operation, and a "tail" that computes the */
+/* roundoff error of that operation. */
+/* */
+/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */
+/* Split(), and Two_Product() are all implemented as described in the */
+/* reference. Each of these macros requires certain variables to be */
+/* defined in the calling routine. The variables `bvirt', `c', `abig', */
+/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */
+/* they store the result of an operation that may incur roundoff error. */
+/* The input parameter `x' (or the highest numbered `x_' parameter) must */
+/* also be declared `INEXACT'. */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+ bvirt = x - a; \
+ y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+ x = (REAL) (a + b); \
+ Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Fast_Two_Diff_Tail(a, b, x, y) \
+ bvirt = a - x; \
+ y = bvirt - b
+
+#define Fast_Two_Diff(a, b, x, y) \
+ x = (REAL) (a - b); \
+ Fast_Two_Diff_Tail(a, b, x, y)
+
+#define Two_Sum_Tail(a, b, x, y) \
+ bvirt = (REAL) (x - a); \
+ avirt = x - bvirt; \
+ bround = b - bvirt; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+ x = (REAL) (a + b); \
+ Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+ bvirt = (REAL) (a - x); \
+ avirt = x + bvirt; \
+ bround = bvirt - b; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+ x = (REAL) (a - b); \
+ Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+ c = (REAL) (splitter * a); \
+ abig = (REAL) (c - a); \
+ ahi = c - abig; \
+ alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+ Split(a, ahi, alo); \
+ Split(b, bhi, blo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+ x = (REAL) (a * b); \
+ Two_Product_Tail(a, b, x, y)
+
+/* Two_Product_Presplit() is Two_Product() where one of the inputs has */
+/* already been split. Avoids redundant splitting. */
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+ x = (REAL) (a * b); \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+/* Two_Product_2Presplit() is Two_Product() where both of the inputs have */
+/* already been split. Avoids redundant splitting. */
+
+#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \
+ x = (REAL) (a * b); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+/* Square() can be done more quickly than Two_Product(). */
+
+#define Square_Tail(a, x, y) \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * ahi); \
+ err3 = err1 - ((ahi + ahi) * alo); \
+ y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+ x = (REAL) (a * a); \
+ Square_Tail(a, x, y)
+
+/* Macros for summing expansions of various fixed lengths. These are all */
+/* unrolled versions of Expansion_Sum(). */
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+ Two_Sum(a0, b , _i, x0); \
+ Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+ Two_Diff(a0, b , _i, x0); \
+ Two_Sum( a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+ Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+ Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b , _j, x1, x0); \
+ Two_One_Sum(a3, a2, _j, x4, x3, x2)
+
+#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \
+ Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \
+ Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1)
+
+#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \
+ x1, x0) \
+ Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \
+ Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2)
+
+#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \
+ x3, x2, x1, x0) \
+ Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \
+ Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4)
+
+#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \
+ x6, x5, x4, x3, x2, x1, x0) \
+ Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \
+ _1, _0, x0); \
+ Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \
+ x3, x2, x1)
+
+#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \
+ x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \
+ _2, _1, _0, x1, x0); \
+ Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \
+ x7, x6, x5, x4, x3, x2)
+
+/* Macros for multiplying expansions of various fixed lengths. */
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, x3, x2)
+
+#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, _i, x2); \
+ Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x3); \
+ Fast_Two_Sum(_j, _k, _i, x4); \
+ Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x5); \
+ Fast_Two_Sum(_j, _k, x7, x6)
+
+#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+ Split(a0, a0hi, a0lo); \
+ Split(b0, bhi, blo); \
+ Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \
+ Split(a1, a1hi, a1lo); \
+ Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, _1); \
+ Fast_Two_Sum(_j, _k, _l, _2); \
+ Split(b1, bhi, blo); \
+ Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \
+ Two_Sum(_1, _0, _k, x1); \
+ Two_Sum(_2, _k, _j, _1); \
+ Two_Sum(_l, _j, _m, _2); \
+ Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _n, _0); \
+ Two_Sum(_1, _0, _i, x2); \
+ Two_Sum(_2, _i, _k, _1); \
+ Two_Sum(_m, _k, _l, _2); \
+ Two_Sum(_j, _n, _k, _0); \
+ Two_Sum(_1, _0, _j, x3); \
+ Two_Sum(_2, _j, _i, _1); \
+ Two_Sum(_l, _i, _m, _2); \
+ Two_Sum(_1, _k, _i, x4); \
+ Two_Sum(_2, _i, _k, x5); \
+ Two_Sum(_m, _k, x7, x6)
+
+/* An expansion of length two can be squared more quickly than finding the */
+/* product of two different expansions of length two, and the result is */
+/* guaranteed to have no more than six (rather than eight) components. */
+
+#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \
+ Square(a0, _j, x0); \
+ _0 = a0 + a0; \
+ Two_Product(a1, _0, _k, _1); \
+ Two_One_Sum(_k, _1, _j, _l, _2, x1); \
+ Square(a1, _j, _1); \
+ Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2)
+
+REAL splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */
+REAL epsilon; /* = 2^(-p). Used to estimate roundoff errors. */
+/* A set of coefficients used to calculate maximum roundoff errors. */
+REAL resulterrbound;
+REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
+REAL o3derrboundA, o3derrboundB, o3derrboundC;
+REAL iccerrboundA, iccerrboundB, iccerrboundC;
+REAL isperrboundA, isperrboundB, isperrboundC;
+
+/*****************************************************************************/
+/* */
+/* doubleprint() Print the bit representation of a double. */
+/* */
+/* Useful for debugging exact arithmetic routines. */
+/* */
+/*****************************************************************************/
+
+/*
+void doubleprint(number)
+double number;
+{
+ unsigned long long no;
+ unsigned long long sign, expo;
+ int exponent;
+ int i, bottomi;
+
+ no = *(unsigned long long *) &number;
+ sign = no & 0x8000000000000000ll;
+ expo = (no >> 52) & 0x7ffll;
+ exponent = (int) expo;
+ exponent = exponent - 1023;
+ if (sign) {
+ printf("-");
+ } else {
+ printf(" ");
+ }
+ if (exponent == -1023) {
+ printf(
+ "0.0000000000000000000000000000000000000000000000000000_ ( )");
+ } else {
+ printf("1.");
+ bottomi = -1;
+ for (i = 0; i < 52; i++) {
+ if (no & 0x0008000000000000ll) {
+ printf("1");
+ bottomi = i;
+ } else {
+ printf("0");
+ }
+ no <<= 1;
+ }
+ printf("_%d (%d)", exponent, exponent - 1 - bottomi);
+ }
+}
+*/
+
+/*****************************************************************************/
+/* */
+/* floatprint() Print the bit representation of a float. */
+/* */
+/* Useful for debugging exact arithmetic routines. */
+/* */
+/*****************************************************************************/
+
+/*
+void floatprint(number)
+float number;
+{
+ unsigned no;
+ unsigned sign, expo;
+ int exponent;
+ int i, bottomi;
+
+ no = *(unsigned *) &number;
+ sign = no & 0x80000000;
+ expo = (no >> 23) & 0xff;
+ exponent = (int) expo;
+ exponent = exponent - 127;
+ if (sign) {
+ printf("-");
+ } else {
+ printf(" ");
+ }
+ if (exponent == -127) {
+ printf("0.00000000000000000000000_ ( )");
+ } else {
+ printf("1.");
+ bottomi = -1;
+ for (i = 0; i < 23; i++) {
+ if (no & 0x00400000) {
+ printf("1");
+ bottomi = i;
+ } else {
+ printf("0");
+ }
+ no <<= 1;
+ }
+ printf("_%3d (%3d)", exponent, exponent - 1 - bottomi);
+ }
+}
+*/
+
+/*****************************************************************************/
+/* */
+/* expansion_print() Print the bit representation of an expansion. */
+/* */
+/* Useful for debugging exact arithmetic routines. */
+/* */
+/*****************************************************************************/
+
+/*
+void expansion_print(elen, e)
+int elen;
+REAL *e;
+{
+ int i;
+
+ for (i = elen - 1; i >= 0; i--) {
+ REALPRINT(e[i]);
+ if (i > 0) {
+ printf(" +\n");
+ } else {
+ printf("\n");
+ }
+ }
+}
+*/
+
+/*****************************************************************************/
+/* */
+/* doublerand() Generate a double with random 53-bit significand and a */
+/* random exponent in [0, 511]. */
+/* */
+/*****************************************************************************/
+
+double doublerand()
+{
+ double result;
+ double expo;
+ long a, b, c;
+ long i;
+
+ a = random();
+ b = random();
+ c = random();
+ result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+ for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) {
+ if (c & i) {
+ result *= expo;
+ }
+ }
+ return result;
+}
+
+/*****************************************************************************/
+/* */
+/* narrowdoublerand() Generate a double with random 53-bit significand */
+/* and a random exponent in [0, 7]. */
+/* */
+/*****************************************************************************/
+
+double narrowdoublerand()
+{
+ double result;
+ double expo;
+ long a, b, c;
+ long i;
+
+ a = random();
+ b = random();
+ c = random();
+ result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+ for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) {
+ if (c & i) {
+ result *= expo;
+ }
+ }
+ return result;
+}
+
+/*****************************************************************************/
+/* */
+/* uniformdoublerand() Generate a double with random 53-bit significand. */
+/* */
+/*****************************************************************************/
+
+double uniformdoublerand()
+{
+ double result;
+ long a, b;
+
+ a = random();
+ b = random();
+ result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+ return result;
+}
+
+/*****************************************************************************/
+/* */
+/* floatrand() Generate a float with random 24-bit significand and a */
+/* random exponent in [0, 63]. */
+/* */
+/*****************************************************************************/
+
+float floatrand()
+{
+ float result;
+ float expo;
+ long a, c;
+ long i;
+
+ a = random();
+ c = random();
+ result = (float) ((a - 1073741824) >> 6);
+ for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) {
+ if (c & i) {
+ result *= expo;
+ }
+ }
+ return result;
+}
+
+/*****************************************************************************/
+/* */
+/* narrowfloatrand() Generate a float with random 24-bit significand and */
+/* a random exponent in [0, 7]. */
+/* */
+/*****************************************************************************/
+
+float narrowfloatrand()
+{
+ float result;
+ float expo;
+ long a, c;
+ long i;
+
+ a = random();
+ c = random();
+ result = (float) ((a - 1073741824) >> 6);
+ for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) {
+ if (c & i) {
+ result *= expo;
+ }
+ }
+ return result;
+}
+
+/*****************************************************************************/
+/* */
+/* uniformfloatrand() Generate a float with random 24-bit significand. */
+/* */
+/*****************************************************************************/
+
+float uniformfloatrand()
+{
+ float result;
+ long a;
+
+ a = random();
+ result = (float) ((a - 1073741824) >> 6);
+ return result;
+}
+
+/*****************************************************************************/
+/* */
+/* exactinit() Initialize the variables used for exact arithmetic. */
+/* */
+/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */
+/* floating-point arithmetic. `epsilon' bounds the relative roundoff */
+/* error. It is used for floating-point error analysis. */
+/* */
+/* `splitter' is used to split floating-point numbers into two half- */
+/* length significands for exact multiplication. */
+/* */
+/* I imagine that a highly optimizing compiler might be too smart for its */
+/* own good, and somehow cause this routine to fail, if it pretends that */
+/* floating-point arithmetic is too much like real arithmetic. */
+/* */
+/* Don't change this routine unless you fully understand it. */
+/* */
+/*****************************************************************************/
+
+void dolfin::exactinit()
+{
+ REAL half;
+ REAL check, lastcheck;
+ int every_other;
+
+ every_other = 1;
+ half = 0.5;
+ epsilon = 1.0;
+ splitter = 1.0;
+ check = 1.0;
+ /* Repeatedly divide `epsilon' by two until it is too small to add to */
+ /* one without causing roundoff. (Also check if the sum is equal to */
+ /* the previous sum, for machines that round up instead of using exact */
+ /* rounding. Not that this library will work on such machines anyway. */
+ do {
+ lastcheck = check;
+ epsilon *= half;
+ if (every_other) {
+ splitter *= 2.0;
+ }
+ every_other = !every_other;
+ check = 1.0 + epsilon;
+ } while ((check != 1.0) && (check != lastcheck));
+ splitter += 1.0;
+
+ /* Error bounds for orientation and incircle tests. */
+ resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+ ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+ ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+ ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+ o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+ o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+ o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+ iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+ iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+ iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+ isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
+ isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
+ isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
+}
+
+/*****************************************************************************/
+/* */
+/* grow_expansion() Add a scalar to an expansion. */
+/* */
+/* Sets h = e + b. See the long version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
+/* properties as well. (That is, if e has one of these properties, so */
+/* will h.) */
+/* */
+/*****************************************************************************/
+
+int grow_expansion(int elen, REAL *e, REAL b, REAL *h) /* e and h can be the same. */
+/* int elen; */
+/* REAL *e; */
+/* REAL b; */
+/* REAL *h; */
+{
+ REAL Q;
+ INEXACT REAL Qnew;
+ int eindex;
+ REAL enow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+
+ Q = b;
+ for (eindex = 0; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Sum(Q, enow, Qnew, h[eindex]);
+ Q = Qnew;
+ }
+ h[eindex] = Q;
+ return eindex + 1;
+}
+
+/*****************************************************************************/
+/* */
+/* grow_expansion_zeroelim() Add a scalar to an expansion, eliminating */
+/* zero components from the output expansion. */
+/* */
+/* Sets h = e + b. See the long version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
+/* properties as well. (That is, if e has one of these properties, so */
+/* will h.) */
+/* */
+/*****************************************************************************/
+
+int grow_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) /* e and h can be the same. */
+/* int elen; */
+/* REAL *e; */
+/* REAL b; */
+/* REAL *h; */
+{
+ REAL Q, hh;
+ INEXACT REAL Qnew;
+ int eindex, hindex;
+ REAL enow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+
+ hindex = 0;
+ Q = b;
+ for (eindex = 0; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Sum(Q, enow, Qnew, hh);
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/*****************************************************************************/
+/* */
+/* expansion_sum() Sum two expansions. */
+/* */
+/* Sets h = e + f. See the long version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the nonadjacent property as well. (That is, */
+/* if e has one of these properties, so will h.) Does NOT maintain the */
+/* strongly nonoverlapping property. */
+/* */
+/*****************************************************************************/
+
+int expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* e and h can be the same, but f and h cannot. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q;
+ INEXACT REAL Qnew;
+ int findex, hindex, hlast;
+ REAL hnow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+
+ Q = f[0];
+ for (hindex = 0; hindex < elen; hindex++) {
+ hnow = e[hindex];
+ Two_Sum(Q, hnow, Qnew, h[hindex]);
+ Q = Qnew;
+ }
+ h[hindex] = Q;
+ hlast = hindex;
+ for (findex = 1; findex < flen; findex++) {
+ Q = f[findex];
+ for (hindex = findex; hindex <= hlast; hindex++) {
+ hnow = h[hindex];
+ Two_Sum(Q, hnow, Qnew, h[hindex]);
+ Q = Qnew;
+ }
+ h[++hlast] = Q;
+ }
+ return hlast + 1;
+}
+
+/*****************************************************************************/
+/* */
+/* expansion_sum_zeroelim1() Sum two expansions, eliminating zero */
+/* components from the output expansion. */
+/* */
+/* Sets h = e + f. See the long version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the nonadjacent property as well. (That is, */
+/* if e has one of these properties, so will h.) Does NOT maintain the */
+/* strongly nonoverlapping property. */
+/* */
+/*****************************************************************************/
+
+int expansion_sum_zeroelim1(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* e and h can be the same, but f and h cannot. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q;
+ INEXACT REAL Qnew;
+ int index, findex, hindex, hlast;
+ REAL hnow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+
+ Q = f[0];
+ for (hindex = 0; hindex < elen; hindex++) {
+ hnow = e[hindex];
+ Two_Sum(Q, hnow, Qnew, h[hindex]);
+ Q = Qnew;
+ }
+ h[hindex] = Q;
+ hlast = hindex;
+ for (findex = 1; findex < flen; findex++) {
+ Q = f[findex];
+ for (hindex = findex; hindex <= hlast; hindex++) {
+ hnow = h[hindex];
+ Two_Sum(Q, hnow, Qnew, h[hindex]);
+ Q = Qnew;
+ }
+ h[++hlast] = Q;
+ }
+ hindex = -1;
+ for (index = 0; index <= hlast; index++) {
+ hnow = h[index];
+ if (hnow != 0.0) {
+ h[++hindex] = hnow;
+ }
+ }
+ if (hindex == -1) {
+ return 1;
+ } else {
+ return hindex + 1;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* expansion_sum_zeroelim2() Sum two expansions, eliminating zero */
+/* components from the output expansion. */
+/* */
+/* Sets h = e + f. See the long version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the nonadjacent property as well. (That is, */
+/* if e has one of these properties, so will h.) Does NOT maintain the */
+/* strongly nonoverlapping property. */
+/* */
+/*****************************************************************************/
+
+int expansion_sum_zeroelim2(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* e and h can be the same, but f and h cannot. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q, hh;
+ INEXACT REAL Qnew;
+ int eindex, findex, hindex, hlast;
+ REAL enow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+
+ hindex = 0;
+ Q = f[0];
+ for (eindex = 0; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Sum(Q, enow, Qnew, hh);
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ h[hindex] = Q;
+ hlast = hindex;
+ for (findex = 1; findex < flen; findex++) {
+ hindex = 0;
+ Q = f[findex];
+ for (eindex = 0; eindex <= hlast; eindex++) {
+ enow = h[eindex];
+ Two_Sum(Q, enow, Qnew, hh);
+ Q = Qnew;
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ }
+ h[hindex] = Q;
+ hlast = hindex;
+ }
+ return hlast + 1;
+}
+
+/*****************************************************************************/
+/* */
+/* fast_expansion_sum() Sum two expansions. */
+/* */
+/* Sets h = e + f. See the long version of my paper for details. */
+/* */
+/* If round-to-even is used (as with IEEE 754), maintains the strongly */
+/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */
+/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */
+/* properties. */
+/* */
+/*****************************************************************************/
+
+int fast_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) /* h cannot be e or f. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q;
+ INEXACT REAL Qnew;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ int eindex, findex, hindex;
+ REAL enow, fnow;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ Q = enow;
+ enow = e[++eindex];
+ } else {
+ Q = fnow;
+ fnow = f[++findex];
+ }
+ hindex = 0;
+ if ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Fast_Two_Sum(enow, Q, Qnew, h[0]);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, Q, Qnew, h[0]);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ hindex = 1;
+ while ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Two_Sum(Q, enow, Qnew, h[hindex]);
+ enow = e[++eindex];
+ } else {
+ Two_Sum(Q, fnow, Qnew, h[hindex]);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ hindex++;
+ }
+ }
+ while (eindex < elen) {
+ Two_Sum(Q, enow, Qnew, h[hindex]);
+ enow = e[++eindex];
+ Q = Qnew;
+ hindex++;
+ }
+ while (findex < flen) {
+ Two_Sum(Q, fnow, Qnew, h[hindex]);
+ fnow = f[++findex];
+ Q = Qnew;
+ hindex++;
+ }
+ h[hindex] = Q;
+ return hindex + 1;
+}
+
+/*****************************************************************************/
+/* */
+/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */
+/* components from the output expansion. */
+/* */
+/* Sets h = e + f. See the long version of my paper for details. */
+/* */
+/* If round-to-even is used (as with IEEE 754), maintains the strongly */
+/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */
+/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */
+/* properties. */
+/* */
+/*****************************************************************************/
+
+int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h) /* h cannot be e or f. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q;
+ INEXACT REAL Qnew;
+ INEXACT REAL hh;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ int eindex, findex, hindex;
+ REAL enow, fnow;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ Q = enow;
+ enow = e[++eindex];
+ } else {
+ Q = fnow;
+ fnow = f[++findex];
+ }
+ hindex = 0;
+ if ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Fast_Two_Sum(enow, Q, Qnew, hh);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, Q, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ while ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ } else {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ }
+ while (eindex < elen) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ while (findex < flen) {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/*****************************************************************************/
+/* */
+/* linear_expansion_sum() Sum two expansions. */
+/* */
+/* Sets h = e + f. See either version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. (That is, if e is */
+/* nonoverlapping, h will be also.) */
+/* */
+/*****************************************************************************/
+
+int linear_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) /* h cannot be e or f. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q, q;
+ INEXACT REAL Qnew;
+ INEXACT REAL R;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ int eindex, findex, hindex;
+ REAL enow, fnow;
+ REAL g0;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ g0 = enow;
+ enow = e[++eindex];
+ } else {
+ g0 = fnow;
+ fnow = f[++findex];
+ }
+ if ((eindex < elen) && ((findex >= flen)
+ || ((fnow > enow) == (fnow > -enow)))) {
+ Fast_Two_Sum(enow, g0, Qnew, q);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, g0, Qnew, q);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ for (hindex = 0; hindex < elen + flen - 2; hindex++) {
+ if ((eindex < elen) && ((findex >= flen)
+ || ((fnow > enow) == (fnow > -enow)))) {
+ Fast_Two_Sum(enow, q, R, h[hindex]);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, q, R, h[hindex]);
+ fnow = f[++findex];
+ }
+ Two_Sum(Q, R, Qnew, q);
+ Q = Qnew;
+ }
+ h[hindex] = q;
+ h[hindex + 1] = Q;
+ return hindex + 2;
+}
+
+/*****************************************************************************/
+/* */
+/* linear_expansion_sum_zeroelim() Sum two expansions, eliminating zero */
+/* components from the output expansion. */
+/* */
+/* Sets h = e + f. See either version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. (That is, if e is */
+/* nonoverlapping, h will be also.) */
+/* */
+/*****************************************************************************/
+
+int linear_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h)/* h cannot be e or f. */
+/* int elen; */
+/* REAL *e; */
+/* int flen; */
+/* REAL *f; */
+/* REAL *h; */
+{
+ REAL Q, q, hh;
+ INEXACT REAL Qnew;
+ INEXACT REAL R;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ int eindex, findex, hindex;
+ int count;
+ REAL enow, fnow;
+ REAL g0;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ hindex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ g0 = enow;
+ enow = e[++eindex];
+ } else {
+ g0 = fnow;
+ fnow = f[++findex];
+ }
+ if ((eindex < elen) && ((findex >= flen)
+ || ((fnow > enow) == (fnow > -enow)))) {
+ Fast_Two_Sum(enow, g0, Qnew, q);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, g0, Qnew, q);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ for (count = 2; count < elen + flen; count++) {
+ if ((eindex < elen) && ((findex >= flen)
+ || ((fnow > enow) == (fnow > -enow)))) {
+ Fast_Two_Sum(enow, q, R, hh);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, q, R, hh);
+ fnow = f[++findex];
+ }
+ Two_Sum(Q, R, Qnew, q);
+ Q = Qnew;
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ }
+ if (q != 0) {
+ h[hindex++] = q;
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/*****************************************************************************/
+/* */
+/* scale_expansion() Multiply an expansion by a scalar. */
+/* */
+/* Sets h = be. See either version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
+/* properties as well. (That is, if e has one of these properties, so */
+/* will h.) */
+/* */
+/*****************************************************************************/
+
+int scale_expansion(int elen, REAL *e, REAL b, REAL *h) /* e and h cannot be the same. */
+/* int elen; */
+/* REAL *e; */
+/* REAL b; */
+/* REAL *h; */
+{
+ INEXACT REAL Q;
+ INEXACT REAL sum;
+ INEXACT REAL product1;
+ REAL product0;
+ int eindex, hindex;
+ REAL enow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+
+ Split(b, bhi, blo);
+ Two_Product_Presplit(e[0], b, bhi, blo, Q, h[0]);
+ hindex = 1;
+ for (eindex = 1; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+ Two_Sum(Q, product0, sum, h[hindex]);
+ hindex++;
+ Two_Sum(product1, sum, Q, h[hindex]);
+ hindex++;
+ }
+ h[hindex] = Q;
+ return elen + elen;
+}
+
+/*****************************************************************************/
+/* */
+/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */
+/* eliminating zero components from the */
+/* output expansion. */
+/* */
+/* Sets h = be. See either version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
+/* properties as well. (That is, if e has one of these properties, so */
+/* will h.) */
+/* */
+/*****************************************************************************/
+
+int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) /* e and h cannot be the same. */
+/* int elen; */
+/* REAL *e; */
+/* REAL b; */
+/* REAL *h; */
+{
+ INEXACT REAL Q, sum;
+ REAL hh;
+ INEXACT REAL product1;
+ REAL product0;
+ int eindex, hindex;
+ REAL enow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+
+ Split(b, bhi, blo);
+ Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+ hindex = 0;
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ for (eindex = 1; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+ Two_Sum(Q, product0, sum, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ Fast_Two_Sum(product1, sum, Q, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/*****************************************************************************/
+/* */
+/* compress() Compress an expansion. */
+/* */
+/* See the long version of my paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), then any nonoverlapping expansion is converted to a */
+/* nonadjacent expansion. */
+/* */
+/*****************************************************************************/
+
+int compress(int elen, REAL *e, REAL *h) /* e and h may be the same. */
+/* int elen; */
+/* REAL *e; */
+/* REAL *h; */
+{
+ REAL Q, q;
+ INEXACT REAL Qnew;
+ int eindex, hindex;
+ INEXACT REAL bvirt;
+ REAL enow, hnow;
+ int top, bottom;
+
+ bottom = elen - 1;
+ Q = e[bottom];
+ for (eindex = elen - 2; eindex >= 0; eindex--) {
+ enow = e[eindex];
+ Fast_Two_Sum(Q, enow, Qnew, q);
+ if (q != 0) {
+ h[bottom--] = Qnew;
+ Q = q;
+ } else {
+ Q = Qnew;
+ }
+ }
+ top = 0;
+ for (hindex = bottom + 1; hindex < elen; hindex++) {
+ hnow = h[hindex];
+ Fast_Two_Sum(hnow, Q, Qnew, q);
+ if (q != 0) {
+ h[top++] = q;
+ }
+ Q = Qnew;
+ }
+ h[top] = Q;
+ return top + 1;
+}
+
+/*****************************************************************************/
+/* */
+/* estimate() Produce a one-word estimate of an expansion's value. */
+/* */
+/* See either version of my paper for details. */
+/* */
+/*****************************************************************************/
+
+REAL estimate(int elen, REAL *e)
+/* int elen; */
+/* REAL *e; */
+{
+ REAL Q;
+ int eindex;
+
+ Q = e[0];
+ for (eindex = 1; eindex < elen; eindex++) {
+ Q += e[eindex];
+ }
+ return Q;
+}
+
+/*****************************************************************************/
+/* */
+/* orient2dfast() Approximate 2D orientation test. Nonrobust. */
+/* orient2dexact() Exact 2D orientation test. Robust. */
+/* orient2dslow() Another exact 2D orientation test. Robust. */
+/* orient2d() Adaptive exact 2D orientation test. Robust. */
+/* */
+/* Return a positive value if the points pa, pb, and pc occur */
+/* in counterclockwise order; a negative value if they occur */
+/* in clockwise order; and zero if they are collinear. The */
+/* result is also a rough approximation of twice the signed */
+/* area of the triangle defined by the three points. */
+/* */
+/* Only the first and last routine should be used; the middle two are for */
+/* timings. */
+/* */
+/* The last three use exact arithmetic to ensure a correct answer. The */
+/* result returned is the determinant of a matrix. In orient2d() only, */
+/* this determinant is computed adaptively, in the sense that exact */
+/* arithmetic is used only to the degree it is needed to ensure that the */
+/* returned value has the correct sign. Hence, orient2d() is usually quite */
+/* fast, but will run more slowly when the input points are collinear or */
+/* nearly so. */
+/* */
+/*****************************************************************************/
+
+REAL orient2dfast(REAL *pa, REAL *pb, REAL *pc)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+{
+ REAL acx, bcx, acy, bcy;
+
+ acx = pa[0] - pc[0];
+ bcx = pb[0] - pc[0];
+ acy = pa[1] - pc[1];
+ bcy = pb[1] - pc[1];
+ return acx * bcy - acy * bcx;
+}
+
+REAL orient2dexact(REAL *pa, REAL *pb, REAL *pc)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+{
+ INEXACT REAL axby1, axcy1, bxcy1, bxay1, cxay1, cxby1;
+ REAL axby0, axcy0, bxcy0, bxay0, cxay0, cxby0;
+ REAL aterms[4], bterms[4], cterms[4];
+ INEXACT REAL aterms3, bterms3, cterms3;
+ REAL v[8], w[12];
+ int vlength, wlength;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j;
+ REAL _0;
+
+ Two_Product(pa[0], pb[1], axby1, axby0);
+ Two_Product(pa[0], pc[1], axcy1, axcy0);
+ Two_Two_Diff(axby1, axby0, axcy1, axcy0,
+ aterms3, aterms[2], aterms[1], aterms[0]);
+ aterms[3] = aterms3;
+
+ Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+ Two_Product(pb[0], pa[1], bxay1, bxay0);
+ Two_Two_Diff(bxcy1, bxcy0, bxay1, bxay0,
+ bterms3, bterms[2], bterms[1], bterms[0]);
+ bterms[3] = bterms3;
+
+ Two_Product(pc[0], pa[1], cxay1, cxay0);
+ Two_Product(pc[0], pb[1], cxby1, cxby0);
+ Two_Two_Diff(cxay1, cxay0, cxby1, cxby0,
+ cterms3, cterms[2], cterms[1], cterms[0]);
+ cterms[3] = cterms3;
+
+ vlength = fast_expansion_sum_zeroelim(4, aterms, 4, bterms, v);
+ wlength = fast_expansion_sum_zeroelim(vlength, v, 4, cterms, w);
+
+ return w[wlength - 1];
+}
+
+REAL orient2dslow(REAL *pa, REAL *pb, REAL *pc)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+{
+ INEXACT REAL acx, acy, bcx, bcy;
+ REAL acxtail, acytail;
+ REAL bcxtail, bcytail;
+ REAL negate, negatetail;
+ REAL axby[8], bxay[8];
+ INEXACT REAL axby7, bxay7;
+ REAL deter[16];
+ int deterlen;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j, _k, _l, _m, _n;
+ REAL _0, _1, _2;
+
+ Two_Diff(pa[0], pc[0], acx, acxtail);
+ Two_Diff(pa[1], pc[1], acy, acytail);
+ Two_Diff(pb[0], pc[0], bcx, bcxtail);
+ Two_Diff(pb[1], pc[1], bcy, bcytail);
+
+ Two_Two_Product(acx, acxtail, bcy, bcytail,
+ axby7, axby[6], axby[5], axby[4],
+ axby[3], axby[2], axby[1], axby[0]);
+ axby[7] = axby7;
+ negate = -acy;
+ negatetail = -acytail;
+ Two_Two_Product(bcx, bcxtail, negate, negatetail,
+ bxay7, bxay[6], bxay[5], bxay[4],
+ bxay[3], bxay[2], bxay[1], bxay[0]);
+ bxay[7] = bxay7;
+
+ deterlen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, deter);
+
+ return deter[deterlen - 1];
+}
+
+REAL orient2dadapt(const REAL *pa, const REAL *pb, const REAL *pc, const REAL detsum)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+/* REAL detsum; */
+{
+ INEXACT REAL acx, acy, bcx, bcy;
+ REAL acxtail, acytail, bcxtail, bcytail;
+ INEXACT REAL detleft, detright;
+ REAL detlefttail, detrighttail;
+ REAL det, errbound;
+ REAL B[4], C1[8], C2[12], D[16];
+ INEXACT REAL B3;
+ int C1length, C2length, Dlength;
+ REAL u[4];
+ INEXACT REAL u3;
+ INEXACT REAL s1, t1;
+ REAL s0, t0;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j;
+ REAL _0;
+
+ acx = (REAL) (pa[0] - pc[0]);
+ bcx = (REAL) (pb[0] - pc[0]);
+ acy = (REAL) (pa[1] - pc[1]);
+ bcy = (REAL) (pb[1] - pc[1]);
+
+ Two_Product(acx, bcy, detleft, detlefttail);
+ Two_Product(acy, bcx, detright, detrighttail);
+
+ Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
+ B3, B[2], B[1], B[0]);
+ B[3] = B3;
+
+ det = estimate(4, B);
+ errbound = ccwerrboundB * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+ Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+ Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+ Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+ if ((acxtail == 0.0) && (acytail == 0.0)
+ && (bcxtail == 0.0) && (bcytail == 0.0)) {
+ return det;
+ }
+
+ errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+ det += (acx * bcytail + bcy * acxtail)
+ - (acy * bcxtail + bcx * acytail);
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Product(acxtail, bcy, s1, s0);
+ Two_Product(acytail, bcx, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+ Two_Product(acx, bcytail, s1, s0);
+ Two_Product(acy, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+ Two_Product(acxtail, bcytail, s1, s0);
+ Two_Product(acytail, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+ return(D[Dlength - 1]);
+}
+
+REAL dolfin::_orient2d(const REAL *pa, const REAL *pb, const REAL *pc)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+{
+ REAL detleft, detright, det;
+ REAL detsum, errbound;
+
+ detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+ detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+ det = detleft - detright;
+
+ if (detleft > 0.0) {
+ if (detright <= 0.0) {
+ return det;
+ } else {
+ detsum = detleft + detright;
+ }
+ } else if (detleft < 0.0) {
+ if (detright >= 0.0) {
+ return det;
+ } else {
+ detsum = -detleft - detright;
+ }
+ } else {
+ return det;
+ }
+
+ errbound = ccwerrboundA * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ return orient2dadapt(pa, pb, pc, detsum);
+}
+
+/*****************************************************************************/
+/* */
+/* orient3dfast() Approximate 3D orientation test. Nonrobust. */
+/* orient3dexact() Exact 3D orientation test. Robust. */
+/* orient3dslow() Another exact 3D orientation test. Robust. */
+/* orient3d() Adaptive exact 3D orientation test. Robust. */
+/* */
+/* Return a positive value if the point pd lies below the */
+/* plane passing through pa, pb, and pc; "below" is defined so */
+/* that pa, pb, and pc appear in counterclockwise order when */
+/* viewed from above the plane. Returns a negative value if */
+/* pd lies above the plane. Returns zero if the points are */
+/* coplanar. The result is also a rough approximation of six */
+/* times the signed volume of the tetrahedron defined by the */
+/* four points. */
+/* */
+/* Only the first and last routine should be used; the middle two are for */
+/* timings. */
+/* */
+/* The last three use exact arithmetic to ensure a correct answer. The */
+/* result returned is the determinant of a matrix. In orient3d() only, */
+/* this determinant is computed adaptively, in the sense that exact */
+/* arithmetic is used only to the degree it is needed to ensure that the */
+/* returned value has the correct sign. Hence, orient3d() is usually quite */
+/* fast, but will run more slowly when the input points are coplanar or */
+/* nearly so. */
+/* */
+/*****************************************************************************/
+
+REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+/* REAL *pd; */
+{
+ REAL adx, bdx, cdx;
+ REAL ady, bdy, cdy;
+ REAL adz, bdz, cdz;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adz = pa[2] - pd[2];
+ bdz = pb[2] - pd[2];
+ cdz = pc[2] - pd[2];
+
+ return adx * (bdy * cdz - bdz * cdy)
+ + bdx * (cdy * adz - cdz * ady)
+ + cdx * (ady * bdz - adz * bdy);
+}
+
+REAL orient3dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+/* REAL *pd; */
+{
+ INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
+ INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
+ REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
+ REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
+ REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+ REAL temp8[8];
+ int templen;
+ REAL abc[12], bcd[12], cda[12], dab[12];
+ int abclen, bcdlen, cdalen, dablen;
+ REAL adet[24], bdet[24], cdet[24], ddet[24];
+ int alen, blen, clen, dlen;
+ REAL abdet[48], cddet[48];
+ int ablen, cdlen;
+ REAL deter[96];
+ int deterlen;
+ int i;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j;
+ REAL _0;
+
+ Two_Product(pa[0], pb[1], axby1, axby0);
+ Two_Product(pb[0], pa[1], bxay1, bxay0);
+ Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+ Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+ Two_Product(pc[0], pb[1], cxby1, cxby0);
+ Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+ Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+ Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+ Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+ Two_Product(pd[0], pa[1], dxay1, dxay0);
+ Two_Product(pa[0], pd[1], axdy1, axdy0);
+ Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+ Two_Product(pa[0], pc[1], axcy1, axcy0);
+ Two_Product(pc[0], pa[1], cxay1, cxay0);
+ Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+ Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+ Two_Product(pd[0], pb[1], dxby1, dxby0);
+ Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+ templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
+ cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
+ templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
+ dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
+ for (i = 0; i < 4; i++) {
+ bd[i] = -bd[i];
+ ac[i] = -ac[i];
+ }
+ templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
+ abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
+ templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
+ bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
+
+ alen = scale_expansion_zeroelim(bcdlen, bcd, pa[2], adet);
+ blen = scale_expansion_zeroelim(cdalen, cda, -pb[2], bdet);
+ clen = scale_expansion_zeroelim(dablen, dab, pc[2], cdet);
+ dlen = scale_expansion_zeroelim(abclen, abc, -pd[2], ddet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+ deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+ return deter[deterlen - 1];
+}
+
+REAL orient3dslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+/* REAL *pd; */
+{
+ INEXACT REAL adx, ady, adz, bdx, bdy, bdz, cdx, cdy, cdz;
+ REAL adxtail, adytail, adztail;
+ REAL bdxtail, bdytail, bdztail;
+ REAL cdxtail, cdytail, cdztail;
+ REAL negate, negatetail;
+ INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
+ REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
+ REAL temp16[16], temp32[32], temp32t[32];
+ int temp16len, temp32len, temp32tlen;
+ REAL adet[64], bdet[64], cdet[64];
+ int alen, blen, clen;
+ REAL abdet[128];
+ int ablen;
+ REAL deter[192];
+ int deterlen;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j, _k, _l, _m, _n;
+ REAL _0, _1, _2;
+
+ Two_Diff(pa[0], pd[0], adx, adxtail);
+ Two_Diff(pa[1], pd[1], ady, adytail);
+ Two_Diff(pa[2], pd[2], adz, adztail);
+ Two_Diff(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff(pb[1], pd[1], bdy, bdytail);
+ Two_Diff(pb[2], pd[2], bdz, bdztail);
+ Two_Diff(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff(pc[1], pd[1], cdy, cdytail);
+ Two_Diff(pc[2], pd[2], cdz, cdztail);
+
+ Two_Two_Product(adx, adxtail, bdy, bdytail,
+ axby7, axby[6], axby[5], axby[4],
+ axby[3], axby[2], axby[1], axby[0]);
+ axby[7] = axby7;
+ negate = -ady;
+ negatetail = -adytail;
+ Two_Two_Product(bdx, bdxtail, negate, negatetail,
+ bxay7, bxay[6], bxay[5], bxay[4],
+ bxay[3], bxay[2], bxay[1], bxay[0]);
+ bxay[7] = bxay7;
+ Two_Two_Product(bdx, bdxtail, cdy, cdytail,
+ bxcy7, bxcy[6], bxcy[5], bxcy[4],
+ bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+ bxcy[7] = bxcy7;
+ negate = -bdy;
+ negatetail = -bdytail;
+ Two_Two_Product(cdx, cdxtail, negate, negatetail,
+ cxby7, cxby[6], cxby[5], cxby[4],
+ cxby[3], cxby[2], cxby[1], cxby[0]);
+ cxby[7] = cxby7;
+ Two_Two_Product(cdx, cdxtail, ady, adytail,
+ cxay7, cxay[6], cxay[5], cxay[4],
+ cxay[3], cxay[2], cxay[1], cxay[0]);
+ cxay[7] = cxay7;
+ negate = -cdy;
+ negatetail = -cdytail;
+ Two_Two_Product(adx, adxtail, negate, negatetail,
+ axcy7, axcy[6], axcy[5], axcy[4],
+ axcy[3], axcy[2], axcy[1], axcy[0]);
+ axcy[7] = axcy7;
+
+ temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
+ temp32len = scale_expansion_zeroelim(temp16len, temp16, adz, temp32);
+ temp32tlen = scale_expansion_zeroelim(temp16len, temp16, adztail, temp32t);
+ alen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+ adet);
+
+ temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
+ temp32len = scale_expansion_zeroelim(temp16len, temp16, bdz, temp32);
+ temp32tlen = scale_expansion_zeroelim(temp16len, temp16, bdztail, temp32t);
+ blen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+ bdet);
+
+ temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
+ temp32len = scale_expansion_zeroelim(temp16len, temp16, cdz, temp32);
+ temp32tlen = scale_expansion_zeroelim(temp16len, temp16, cdztail, temp32t);
+ clen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+ cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
+
+ return deter[deterlen - 1];
+}
+
+REAL orient3dadapt(const REAL *pa, const REAL *pb, const REAL *pc, const REAL *pd, REAL permanent)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+/* REAL *pd; */
+/* REAL permanent; */
+{
+ INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+ REAL det, errbound;
+
+ INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ REAL bc[4], ca[4], ab[4];
+ INEXACT REAL bc3, ca3, ab3;
+ REAL adet[8], bdet[8], cdet[8];
+ int alen, blen, clen;
+ REAL abdet[16];
+ int ablen;
+ REAL *finnow, *finother, *finswap;
+ REAL fin1[192], fin2[192];
+ int finlength;
+
+ REAL adxtail, bdxtail, cdxtail;
+ REAL adytail, bdytail, cdytail;
+ REAL adztail, bdztail, cdztail;
+ INEXACT REAL at_blarge, at_clarge;
+ INEXACT REAL bt_clarge, bt_alarge;
+ INEXACT REAL ct_alarge, ct_blarge;
+ REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+ int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+ INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+ INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
+ REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+ REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
+ INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+ INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
+ REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+ REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
+ REAL bct[8], cat[8], abt[8];
+ int bctlen, catlen, abtlen;
+ INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+ INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+ REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+ REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+ REAL u[4], v[12], w[16];
+ INEXACT REAL u3;
+ int vlength, wlength;
+ REAL negate;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j, _k;
+ REAL _0;
+
+ adx = (REAL) (pa[0] - pd[0]);
+ bdx = (REAL) (pb[0] - pd[0]);
+ cdx = (REAL) (pc[0] - pd[0]);
+ ady = (REAL) (pa[1] - pd[1]);
+ bdy = (REAL) (pb[1] - pd[1]);
+ cdy = (REAL) (pc[1] - pd[1]);
+ adz = (REAL) (pa[2] - pd[2]);
+ bdz = (REAL) (pb[2] - pd[2]);
+ cdz = (REAL) (pc[2] - pd[2]);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ alen = scale_expansion_zeroelim(4, bc, adz, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ blen = scale_expansion_zeroelim(4, ca, bdz, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ clen = scale_expansion_zeroelim(4, ab, cdz, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = o3derrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ Two_Diff_Tail(pa[2], pd[2], adz, adztail);
+ Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
+ Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
+
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+ && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)
+ && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) {
+ return det;
+ }
+
+ errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+ det += (adz * ((bdx * cdytail + cdy * bdxtail)
+ - (bdy * cdxtail + cdx * bdytail))
+ + adztail * (bdx * cdy - bdy * cdx))
+ + (bdz * ((cdx * adytail + ady * cdxtail)
+ - (cdy * adxtail + adx * cdytail))
+ + bdztail * (cdx * ady - cdy * adx))
+ + (cdz * ((adx * bdytail + bdy * adxtail)
+ - (ady * bdxtail + bdx * adytail))
+ + cdztail * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if (adxtail == 0.0) {
+ if (adytail == 0.0) {
+ at_b[0] = 0.0;
+ at_blen = 1;
+ at_c[0] = 0.0;
+ at_clen = 1;
+ } else {
+ negate = -adytail;
+ Two_Product(negate, bdx, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ Two_Product(adytail, cdx, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ }
+ } else {
+ if (adytail == 0.0) {
+ Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ negate = -adxtail;
+ Two_Product(negate, cdy, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ } else {
+ Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+ Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+ Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
+ at_blarge, at_b[2], at_b[1], at_b[0]);
+ at_b[3] = at_blarge;
+ at_blen = 4;
+ Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+ Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+ Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
+ at_clarge, at_c[2], at_c[1], at_c[0]);
+ at_c[3] = at_clarge;
+ at_clen = 4;
+ }
+ }
+ if (bdxtail == 0.0) {
+ if (bdytail == 0.0) {
+ bt_c[0] = 0.0;
+ bt_clen = 1;
+ bt_a[0] = 0.0;
+ bt_alen = 1;
+ } else {
+ negate = -bdytail;
+ Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ }
+ } else {
+ if (bdytail == 0.0) {
+ Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ negate = -bdxtail;
+ Two_Product(negate, ady, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ } else {
+ Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+ Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+ Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
+ bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+ bt_c[3] = bt_clarge;
+ bt_clen = 4;
+ Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+ Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+ Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
+ bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+ bt_a[3] = bt_alarge;
+ bt_alen = 4;
+ }
+ }
+ if (cdxtail == 0.0) {
+ if (cdytail == 0.0) {
+ ct_a[0] = 0.0;
+ ct_alen = 1;
+ ct_b[0] = 0.0;
+ ct_blen = 1;
+ } else {
+ negate = -cdytail;
+ Two_Product(negate, adx, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ }
+ } else {
+ if (cdytail == 0.0) {
+ Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ negate = -cdxtail;
+ Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ } else {
+ Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+ Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+ Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
+ ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+ ct_a[3] = ct_alarge;
+ ct_alen = 4;
+ Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+ Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+ Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
+ ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+ ct_b[3] = ct_blarge;
+ ct_blen = 4;
+ }
+ }
+
+ bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+ wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+ catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+ wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+ abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+ if (adztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, bc, adztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdztail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ if (adxtail != 0.0) {
+ if (bdytail != 0.0) {
+ Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (cdztail != 0.0) {
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if (cdytail != 0.0) {
+ negate = -adxtail;
+ Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (bdztail != 0.0) {
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ }
+ if (bdxtail != 0.0) {
+ if (cdytail != 0.0) {
+ Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (adztail != 0.0) {
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if (adytail != 0.0) {
+ negate = -bdxtail;
+ Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (cdztail != 0.0) {
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ }
+ if (cdxtail != 0.0) {
+ if (adytail != 0.0) {
+ Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (bdztail != 0.0) {
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if (bdytail != 0.0) {
+ negate = -cdxtail;
+ Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (adztail != 0.0) {
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ }
+
+ if (adztail != 0.0) {
+ wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdztail != 0.0) {
+ wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdztail != 0.0) {
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ return finnow[finlength - 1];
+}
+
+REAL dolfin::_orient3d(const REAL *pa, const REAL *pb, const REAL *pc, const REAL *pd)
+/* REAL *pa; */
+/* REAL *pb; */
+/* REAL *pc; */
+/* REAL *pd; */
+{
+ REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+ REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ REAL det;
+ REAL permanent, errbound;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adz = pa[2] - pd[2];
+ bdz = pb[2] - pd[2];
+ cdz = pc[2] - pd[2];
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+
+ det = adz * (bdxcdy - cdxbdy)
+ + bdz * (cdxady - adxcdy)
+ + cdz * (adxbdy - bdxady);
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz)
+ + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz)
+ + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
+ errbound = o3derrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return orient3dadapt(pa, pb, pc, pd, permanent);
+}
+
+
+//--- DOLFIN-specific additions ---
+
+#include "predicates.h"
+
+namespace dolfin
+{
+ /// Initialize the predicate
+ PredicateInitialization predicate_initialization;
+}
diff --git a/dolfin/geometry/predicates.h b/dolfin/geometry/predicates.h
new file mode 100644
index 0000000..fa7f248
--- /dev/null
+++ b/dolfin/geometry/predicates.h
@@ -0,0 +1,59 @@
+// This is a DOLFIN header file for predicates.cpp which provides
+//
+// Routines for Arbitrary Precision Floating-point Arithmetic
+// and Fast Robust Geometric Predicates
+//
+// by
+//
+// Jonathan Richard Shewchuk
+//
+// Code is placed in the public domain.
+
+#ifndef __PREDICATES_H
+#define __PREDICATES_H
+
+namespace dolfin
+{
+
+ class Point;
+
+ /// Initialize tolerances for exact arithmetic
+ void exactinit();
+
+ /// Compute relative orientation of point x wrt segment [a, b]
+ double orient1d(double a, double b, double x);
+
+ /// Compute relative orientation of points a, b, c. The orientation
+ /// is such that orient2d(a, b, c) > 0 if a, b, c are ordered
+ /// counter-clockwise.
+ double _orient2d(const double* a, const double* b, const double* c);
+
+ /// Convenience function using dolfin::Point
+ double orient2d(const Point& a, const Point& b, const Point& c);
+
+ /// Compute relative orientation of points a, b, c, d. The
+ /// orientation is such that orient3d(a, b, c, d) > 0 if a, b, c, d
+ /// are oriented according to the left hand rule.
+ double _orient3d(const double* a, const double* b, const double* c, const double* d);
+
+ /// Convenience function using dolfin::Point
+ double orient3d(const Point& a, const Point& b, const Point& c, const Point& d);
+
+ /// Class used for automatic initialization of tolerances at startup.
+ /// A global instance is defined inside predicates.cpp to ensure that
+ /// the constructor and thus exactinit() is called.
+
+ class PredicateInitialization
+ {
+ public:
+
+ PredicateInitialization()
+ {
+ exactinit();
+ }
+
+ };
+
+}
+
+#endif
diff --git a/dolfin/io/HDF5File.cpp b/dolfin/io/HDF5File.cpp
index 252c7ac..5462bd5 100644
--- a/dolfin/io/HDF5File.cpp
+++ b/dolfin/io/HDF5File.cpp
@@ -444,7 +444,7 @@ void HDF5File::write(const Mesh& mesh, std::size_t cell_dim,
global_size[1] = num_cell_points;
const std::int64_t num_cells
- = mpi_io ? mesh.size_global(cell_dim) : mesh.size(cell_dim);
+ = mpi_io ? mesh.num_entities_global(cell_dim) : mesh.num_entities(cell_dim);
dolfin_assert(global_size[0] == num_cells);
const bool mpi_io = _mpi_comm.size() > 1 ? true : false;
write_data(topology_dataset, topological_data, global_size, mpi_io);
@@ -608,10 +608,10 @@ void HDF5File::read_mesh_function(MeshFunction<T>& meshfunction,
"Cell dimension mismatch");
}
- // Ensure size_global(cell_dim) is set
+ // Ensure num_entities_global(cell_dim) is set
DistributedMeshTools::number_entities(*mesh, cell_dim);
- if (num_global_cells != mesh->size_global(cell_dim))
+ if (num_global_cells != mesh->num_entities_global(cell_dim))
{
dolfin_error("HDF5File.cpp",
"read meshfunction topology",
@@ -641,7 +641,7 @@ void HDF5File::read_mesh_function(MeshFunction<T>& meshfunction,
// Now send the read data to each process on the basis of the first
// vertex of the entity, since we do not know the global_index
const std::size_t num_processes = _mpi_comm.size();
- const std::size_t max_vertex = mesh->size_global(0);
+ const std::size_t max_vertex = mesh->num_entities_global(0);
std::vector<std::vector<std::size_t>> send_topology(num_processes);
std::vector<std::vector<T>> send_values(num_processes);
@@ -785,7 +785,7 @@ void HDF5File::write_mesh_function(const MeshFunction<T>& meshfunction,
else
{
// In parallel and not CellFunction
- data_values.reserve(mesh.size(cell_dim));
+ data_values.reserve(mesh.num_entities(cell_dim));
// Drop duplicate data
const std::size_t tdim = mesh.topology().dim();
@@ -925,14 +925,14 @@ void HDF5File::write(const Function& u, const std::string name)
write_data(name + "/cell_dofs", cell_dofs, global_size, mpi_io);
if (_mpi_comm.rank() == _mpi_comm.size() - 1)
x_cell_dofs.push_back(global_size[0]);
- global_size[0] = mesh.size_global(tdim) + 1;
+ global_size[0] = mesh.num_entities_global(tdim) + 1;
write_data(name + "/x_cell_dofs", x_cell_dofs, global_size, mpi_io);
// Save cell ordering - copy to local vector and cut off ghosts
std::vector<std::size_t> cells(mesh.topology().global_indices(tdim).begin(),
mesh.topology().global_indices(tdim).begin() + n_cells);
- global_size[0] = mesh.size_global(tdim);
+ global_size[0] = mesh.num_entities_global(tdim);
write_data(name + "/cells", cells, global_size, mpi_io);
HDF5Interface::add_attribute(_hdf5_file_id, name, "signature",
@@ -1030,7 +1030,7 @@ void HDF5File::read(Function& u, const std::string name)
const std::vector<std::int64_t> dataset_shape =
HDF5Interface::get_dataset_shape(_hdf5_file_id, cells_dataset_name);
const std::size_t num_global_cells = dataset_shape[0];
- if (mesh.size_global(mesh.topology().dim()) != num_global_cells)
+ if (mesh.num_entities_global(mesh.topology().dim()) != num_global_cells)
{
dolfin_error("HDF5File.cpp",
"read Function from file",
@@ -1310,7 +1310,7 @@ void HDF5File::read_mesh_value_collection(MeshValueCollection<T>& mesh_vc,
// Ensure the mesh dimension is initialised
mesh->init(dim);
- std::size_t global_vertex_range = mesh->size_global(0);
+ std::size_t global_vertex_range = mesh->num_entities_global(0);
std::vector<std::size_t> v(num_verts_per_entity);
const std::size_t num_processes = _mpi_comm.size();
diff --git a/dolfin/io/HDF5Utility.cpp b/dolfin/io/HDF5Utility.cpp
index b73e45d..b005fdf 100644
--- a/dolfin/io/HDF5Utility.cpp
+++ b/dolfin/io/HDF5Utility.cpp
@@ -190,7 +190,7 @@ std::vector<std::pair<std::size_t, std::size_t>>
const std::size_t num_processes = MPI::size(mpi_comm);
const std::size_t num_global_cells
- = mesh.size_global(mesh.topology().dim());
+ = mesh.num_entities_global(mesh.topology().dim());
const std::pair<std::size_t, std::size_t> cell_range
= MPI::local_range(mpi_comm, num_global_cells);
@@ -258,7 +258,7 @@ void HDF5Utility::cell_owners_in_range(std::vector<std::pair<std::size_t,
// MPI communicator
const MPI_Comm mpi_comm = mesh.mpi_comm();
- const std::size_t n_global_cells = mesh.size_global(mesh.topology().dim());
+ const std::size_t n_global_cells = mesh.num_entities_global(mesh.topology().dim());
const std::size_t num_processes = MPI::size(mpi_comm);
// Communicate global ownership of cells to matching process
diff --git a/dolfin/io/VTKWriter.cpp b/dolfin/io/VTKWriter.cpp
index ea84367..eff6934 100644
--- a/dolfin/io/VTKWriter.cpp
+++ b/dolfin/io/VTKWriter.cpp
@@ -388,17 +388,17 @@ std::uint8_t VTKWriter::vtk_cell_type(const Mesh& mesh,
// Determine VTK cell type
std::uint8_t vtk_cell_type = 0;
- if (cell_type == CellType::tetrahedron)
+ if (cell_type == CellType::Type::tetrahedron)
vtk_cell_type = 10;
- else if (cell_type == CellType::hexahedron)
+ else if (cell_type == CellType::Type::hexahedron)
vtk_cell_type = 12;
- else if (cell_type == CellType::quadrilateral)
+ else if (cell_type == CellType::Type::quadrilateral)
vtk_cell_type = 9;
- else if (cell_type == CellType::triangle)
+ else if (cell_type == CellType::Type::triangle)
vtk_cell_type = 5;
- else if (cell_type == CellType::interval)
+ else if (cell_type == CellType::Type::interval)
vtk_cell_type = 3;
- else if (cell_type == CellType::point)
+ else if (cell_type == CellType::Type::point)
vtk_cell_type = 1;
else
{
diff --git a/dolfin/io/X3DFile.cpp b/dolfin/io/X3DFile.cpp
index 9578249..aa7dc8c 100644
--- a/dolfin/io/X3DFile.cpp
+++ b/dolfin/io/X3DFile.cpp
@@ -153,7 +153,7 @@ void X3DFile::write_meshfunction(const MeshFunction<std::size_t>& meshfunction)
{
dolfin_error("X3DFile.cpp",
"output meshfunction",
- "Can only output CellFunction at present");
+ "Can only output MeshFunction on cells at present");
}
// Check that X3D type is appropriate
@@ -161,7 +161,7 @@ void X3DFile::write_meshfunction(const MeshFunction<std::size_t>& meshfunction)
{
dolfin_error("X3DFile.cpp",
"output meshfunction",
- "Cannot output CellFunction with Edge mesh");
+ "Cannot output MeshFunction on cells with Edge mesh");
}
// Check that mesh is in 2D or 3D
diff --git a/dolfin/io/XDMFFile.cpp b/dolfin/io/XDMFFile.cpp
index 6ffc0fe..494dd3d 100644
--- a/dolfin/io/XDMFFile.cpp
+++ b/dolfin/io/XDMFFile.cpp
@@ -369,9 +369,9 @@ void XDMFFile::write(const Function& u, const Encoding encoding)
dolfin_assert(data_values.size()%width == 0);
const std::int64_t num_points = (degree == 2) ?
- (mesh.size(0) + mesh.size(1)) : mesh.size_global(0);
+ (mesh.num_entities(0) + mesh.num_entities(1)) : mesh.num_entities_global(0);
const std::int64_t num_values = cell_centred ?
- mesh.size_global(mesh.topology().dim()) : num_points;
+ mesh.num_entities_global(mesh.topology().dim()) : num_points;
add_data_item(_mpi_comm.comm(), attribute_node, h5_id,
"/VisualisationVector/0", data_values, {num_values, width});
@@ -513,7 +513,7 @@ void XDMFFile::write(const Function& u, double time_step,
std::int64_t width = get_padded_width(u);
dolfin_assert(data_values.size()%width == 0);
std::int64_t num_values = cell_centred ?
- mesh.size_global(mesh.topology().dim()) : mesh.size_global(0);
+ mesh.num_entities_global(mesh.topology().dim()) : mesh.num_entities_global(0);
const std::string dataset_name = "/VisualisationVector/"
+ std::to_string(_counter);
@@ -673,7 +673,7 @@ void XDMFFile::write_mesh_value_collection(const MeshValueCollection<T>& mvc,
const std::string dims_str = geometry_data_node.attribute("Dimensions").as_string();
std::vector<std::string> dims_list;
boost::split(dims_list, dims_str, boost::is_any_of(" "));
- const std::int64_t npoints = mesh->size_global(0);
+ const std::int64_t npoints = mesh->num_entities_global(0);
if (boost::lexical_cast<std::int64_t>(dims_list[0]) != npoints
or boost::lexical_cast<std::int64_t>(dims_list[1]) != (int)gdim)
{
@@ -847,7 +847,7 @@ void XDMFFile::read_mesh_value_collection
// Ensure the mesh dimension is initialised
mesh->init(cell_dim);
- const std::size_t global_vertex_range = mesh->size_global(0);
+ const std::size_t global_vertex_range = mesh->num_entities_global(0);
const std::int32_t num_processes = _mpi_comm.size();
// Send entities to processes based on the lowest vertex index
@@ -1136,7 +1136,7 @@ void XDMFFile::add_mesh(MPI_Comm comm, pugi::xml_node& xml_node,
// Add topology node and attributes (including writing data)
const int tdim = mesh.topology().dim();
- const std::int64_t num_global_cells = mesh.size_global(tdim);
+ const std::int64_t num_global_cells = mesh.num_entities_global(tdim);
if (num_global_cells < 1e9)
add_topology_data<std::int32_t>(comm, grid_node, h5_id, path_prefix,
mesh, tdim);
@@ -1272,7 +1272,7 @@ void XDMFFile::add_function(MPI_Comm mpi_comm, pugi::xml_node& xml_node,
if (MPI::rank(mpi_comm) == MPI::size(mpi_comm) - 1)
x_cell_dofs.push_back(num_cell_dofs_global);
- const std::int64_t num_x_cell_dofs_global = mesh.size_global(tdim) + 1;
+ const std::int64_t num_x_cell_dofs_global = mesh.num_entities_global(tdim) + 1;
// Write number of dofs per cell
add_data_item(mpi_comm, fe_attribute_node, h5_id,
@@ -1284,7 +1284,7 @@ void XDMFFile::add_function(MPI_Comm mpi_comm, pugi::xml_node& xml_node,
mesh.topology().global_indices(tdim).begin(),
mesh.topology().global_indices(tdim).begin() + n_cells);
- const std::int64_t num_cells_global = mesh.size_global(tdim);
+ const std::int64_t num_cells_global = mesh.num_entities_global(tdim);
add_data_item(mpi_comm, fe_attribute_node, h5_id,
h5_path + "/cells", cells,
@@ -1545,7 +1545,7 @@ void XDMFFile::build_mesh_quadratic(Mesh& mesh, const CellType& cell_type,
std::vector<std::int32_t> topology_data
= get_dataset<std::int32_t>(mesh.mpi_comm(), topology_dataset_node,
relative_path);
- dolfin_assert(topology_data.size()%num_cells == 0);
+ dolfin_assert(topology_data.size() % num_cells == 0);
const int num_points_per_cell = topology_data.size()/num_cells;
const int num_vertices_per_cell = cell_type.num_entities(0);
const int num_edges_per_cell = cell_type.num_entities(1);
@@ -1825,7 +1825,7 @@ void XDMFFile::add_geometry_data(MPI_Comm comm, pugi::xml_node& xml_node,
const int degree = mesh_geometry.degree();
dolfin_assert(degree == 1 or degree == 2);
const std::int64_t num_points
- = (degree == 1) ? mesh.size_global(0) : (mesh.size(0) + mesh.size(1));
+ = (degree == 1) ? mesh.num_entities_global(0) : (mesh.num_entities(0) + mesh.num_entities(1));
// Add geometry node and attributes
pugi::xml_node geometry_node = xml_node.append_child("Geometry");
@@ -2082,7 +2082,7 @@ std::vector<T> XDMFFile::compute_quadratic_topology(const Mesh& mesh)
const CellType& celltype = mesh.type();
std::size_t npoint = celltype.num_entities(0) + celltype.num_entities(1);
std::vector<T> topology_data;
- topology_data.reserve(npoint*mesh.size(tdim));
+ topology_data.reserve(npoint*mesh.num_entities(tdim));
for (CellIterator c(mesh); !c.end(); ++c)
{
@@ -2486,9 +2486,9 @@ void XDMFFile::read_mesh_function(MeshFunction<T>& meshfunction,
dolfin_assert(cell_dim == meshfunction.dim());
const std::size_t num_entities_global = get_num_cells(topology_node);
- // Ensure size_global(cell_dim) is set and check dataset matches
+ // Ensure num_entities_global(cell_dim) is set and check dataset matches
DistributedMeshTools::number_entities(*mesh, cell_dim);
- dolfin_assert(mesh->size_global(cell_dim) == num_entities_global);
+ dolfin_assert(mesh->num_entities_global(cell_dim) == num_entities_global);
boost::filesystem::path xdmf_filename(_filename);
const boost::filesystem::path parent_path = xdmf_filename.parent_path();
@@ -2533,7 +2533,7 @@ void XDMFFile::remap_meshfunction_data(MeshFunction<T>& meshfunction,
// determined by the lowest global vertex index of the entity
std::vector<std::vector<std::int64_t>> send_topology(num_processes);
std::vector<std::vector<T>> send_values(num_processes);
- const std::size_t max_vertex = mesh->size_global(0);
+ const std::size_t max_vertex = mesh->num_entities_global(0);
for (std::size_t i = 0; i < num_entities ; ++i)
{
std::vector<std::int64_t>
@@ -2719,7 +2719,8 @@ void XDMFFile::write_mesh_function(const MeshFunction<T>& meshfunction,
const std::string mf_name = "/MeshFunction/" + std::to_string(_counter);
- // If adding a CellFunction to an existing Mesh, do not rewrite Mesh
+ // If adding a MeshFunction of topology dimension dim() to an existing Mesh,
+ // do not rewrite Mesh
// FIXME: do some checks on the existing Mesh to make sure it is the same
// as the meshfunction's mesh.
pugi::xml_node grid_node = domain_node.child("Grid");
@@ -2753,7 +2754,7 @@ void XDMFFile::write_mesh_function(const MeshFunction<T>& meshfunction,
// FIXME: remove this once Edge in 3D in parallel works properly
DistributedMeshTools::number_entities(*mesh, cell_dim);
- const std::int64_t num_global_cells = mesh->size_global(cell_dim);
+ const std::int64_t num_global_cells = mesh->num_entities_global(cell_dim);
if (num_global_cells < 1e9)
add_topology_data<std::int32_t>(_mpi_comm.comm(), grid_node, h5_id, mf_name,
*mesh, cell_dim);
@@ -2782,7 +2783,7 @@ void XDMFFile::write_mesh_function(const MeshFunction<T>& meshfunction,
attribute_node.append_attribute("AttributeType") = "Scalar";
attribute_node.append_attribute("Center") = "Cell";
- const std::int64_t num_values = mesh->size_global(cell_dim);
+ const std::int64_t num_values = mesh->num_entities_global(cell_dim);
// Add attribute DataItem node and write data
// Copy values to vector, removing duplicates
@@ -2890,7 +2891,7 @@ std::vector<double> XDMFFile::get_point_data_values(const Function& u)
if (u.value_rank() > 0)
{
// Transpose vector/tensor data arrays
- const std::size_t num_local_vertices = mesh->size(0);
+ const std::size_t num_local_vertices = mesh->num_entities(0);
const std::size_t value_size = u.value_size();
std::vector<double> _data_values(width*num_local_vertices, 0.0);
for (std::size_t i = 0; i < num_local_vertices; i++)
@@ -2922,7 +2923,7 @@ std::vector<double> XDMFFile::get_p2_data_values(const Function& u)
const std::size_t value_size = u.value_size();
const std::size_t value_rank = u.value_rank();
- const std::size_t num_local_points = mesh->size(0) + mesh->size(1);
+ const std::size_t num_local_points = mesh->num_entities(0) + mesh->num_entities(1);
const std::size_t width = get_padded_width(u);
std::vector<double> data_values(width*num_local_points);
std::vector<dolfin::la_index> data_dofs(data_values.size(), 0);
@@ -2958,7 +2959,7 @@ std::vector<double> XDMFFile::get_p2_data_values(const Function& u)
{
const std::size_t v0 = e->entities(0)[0];
const std::size_t v1 = e->entities(0)[1];
- const std::size_t e0 = (e->index() + mesh->size(0))*width;
+ const std::size_t e0 = (e->index() + mesh->num_entities(0))*width;
for (std::size_t i = 0; i != value_size; ++i)
data_values[e0 + i] = (data_values[v0 + i] + data_values[v1 + i])/2.0;
}
@@ -2982,7 +2983,7 @@ std::vector<double> XDMFFile::get_p2_data_values(const Function& u)
}
for (EdgeIterator e(*cell); !e.end(); ++e)
{
- const std::size_t e0 = (e->index() + mesh->size(0))*width;
+ const std::size_t e0 = (e->index() + mesh->num_entities(0))*width;
data_dofs[e0 + i] = dofs[c];
++c;
}
diff --git a/dolfin/io/XMLMesh.cpp b/dolfin/io/XMLMesh.cpp
index b76e3b5..085bded 100644
--- a/dolfin/io/XMLMesh.cpp
+++ b/dolfin/io/XMLMesh.cpp
@@ -483,16 +483,16 @@ void XMLMesh::write_mesh(const Mesh& mesh, pugi::xml_node mesh_node)
switch (_cell_type)
{
- case CellType::interval:
+ case CellType::Type::interval:
cell_node.append_attribute("v0") = (unsigned int) vertices[0];
cell_node.append_attribute("v1") = (unsigned int) vertices[1];
break;
- case CellType::triangle:
+ case CellType::Type::triangle:
cell_node.append_attribute("v0") = (unsigned int) vertices[0];
cell_node.append_attribute("v1") = (unsigned int) vertices[1];
cell_node.append_attribute("v2") = (unsigned int) vertices[2];
break;
- case CellType::tetrahedron:
+ case CellType::Type::tetrahedron:
cell_node.append_attribute("v0") = (unsigned int) vertices[0];
cell_node.append_attribute("v1") = (unsigned int) vertices[1];
cell_node.append_attribute("v2") = (unsigned int) vertices[2];
diff --git a/dolfin/la/EigenVector.cpp b/dolfin/la/EigenVector.cpp
index c4493ef..0925cd3 100644
--- a/dolfin/la/EigenVector.cpp
+++ b/dolfin/la/EigenVector.cpp
@@ -112,23 +112,21 @@ void EigenVector::get_local(double* block, std::size_t m,
//-----------------------------------------------------------------------------
void EigenVector::get_local(std::vector<double>& values) const
{
- values.resize(size());
- for (std::size_t i = 0; i < size(); i++)
- values[i] = (*_x)(i);
+ values.assign(_x->data(), _x->data() + _x->size());
}
//-----------------------------------------------------------------------------
void EigenVector::set_local(const std::vector<double>& values)
{
dolfin_assert(values.size() == size());
- for (std::size_t i = 0; i < size(); i++)
- (*_x)(i) = values[i];
+ Eigen::Map<const Eigen::VectorXd> _values(values.data(), values.size());
+ *_x = _values;
}
//-----------------------------------------------------------------------------
void EigenVector::add_local(const Array<double>& values)
{
dolfin_assert(values.size() == size());
- for (std::size_t i = 0; i < size(); i++)
- (*_x)(i) += values[i];
+ Eigen::Map<const Eigen::VectorXd> _values(values.data(), values.size());
+ *_x += _values;
}
//-----------------------------------------------------------------------------
void EigenVector::gather(GenericVector& x,
diff --git a/dolfin/la/GenericMatrix.cpp b/dolfin/la/GenericMatrix.cpp
index 8befa07..8c7e905 100644
--- a/dolfin/la/GenericMatrix.cpp
+++ b/dolfin/la/GenericMatrix.cpp
@@ -27,7 +27,7 @@
using namespace dolfin;
//-----------------------------------------------------------------------------
-void GenericMatrix::ident_zeros()
+void GenericMatrix::ident_zeros(double tol)
{
// Check size of system
if (size(0) != size(1))
@@ -58,7 +58,7 @@ void GenericMatrix::ident_zeros()
max = std::max(max, std::abs(values[k]));
// Check if row is zero
- if (max < DOLFIN_EPS)
+ if (max < tol)
zero_rows.push_back(global_row);
}
diff --git a/dolfin/la/GenericMatrix.h b/dolfin/la/GenericMatrix.h
index e0c3ae6..4347557 100644
--- a/dolfin/la/GenericMatrix.h
+++ b/dolfin/la/GenericMatrix.h
@@ -30,9 +30,11 @@
#include <tuple>
#include <vector>
+#include <dolfin/common/constants.h>
#include "GenericTensor.h"
#include "GenericLinearOperator.h"
+
namespace dolfin
{
@@ -250,8 +252,7 @@ namespace dolfin
{ set(&value, 1, &ij.first, 1, &ij.second); }
/// Insert one on the diagonal for all zero rows
- virtual void ident_zeros();
-
+ virtual void ident_zeros(double tol=DOLFIN_EPS);
};
}
diff --git a/dolfin/la/PETScObject.h b/dolfin/la/PETScObject.h
index 08df1d7..22868c8 100644
--- a/dolfin/la/PETScObject.h
+++ b/dolfin/la/PETScObject.h
@@ -44,6 +44,7 @@ namespace dolfin
static void petsc_error(int error_code,
std::string filename,
std::string petsc_function);
+
};
}
diff --git a/dolfin/la/SLEPcEigenSolver.cpp b/dolfin/la/SLEPcEigenSolver.cpp
index 1f84040..b301625 100644
--- a/dolfin/la/SLEPcEigenSolver.cpp
+++ b/dolfin/la/SLEPcEigenSolver.cpp
@@ -77,7 +77,7 @@ SLEPcEigenSolver::SLEPcEigenSolver(MPI_Comm comm,
//-----------------------------------------------------------------------------
SLEPcEigenSolver::SLEPcEigenSolver(std::shared_ptr<const PETScMatrix> A,
std::shared_ptr<const PETScMatrix> B)
- : _matA(A), _matB(B), _eps(nullptr)
+ : _eps(nullptr)
{
// TODO: deprecate
@@ -96,9 +96,9 @@ SLEPcEigenSolver::SLEPcEigenSolver(std::shared_ptr<const PETScMatrix> A,
// Set operators
dolfin_assert(_eps);
if (B)
- EPSSetOperators(_eps, _matA->mat(), _matB->mat());
+ EPSSetOperators(_eps, A->mat(), B->mat());
else
- EPSSetOperators(_eps, _matA->mat(), NULL);
+ EPSSetOperators(_eps, A->mat(), NULL);
// Set default parameter values
parameters = default_parameters();
@@ -107,7 +107,7 @@ SLEPcEigenSolver::SLEPcEigenSolver(std::shared_ptr<const PETScMatrix> A,
SLEPcEigenSolver::SLEPcEigenSolver(MPI_Comm comm,
std::shared_ptr<const PETScMatrix> A,
std::shared_ptr<const PETScMatrix> B)
- : _matA(A), _matB(B), _eps(nullptr)
+ : _eps(nullptr)
{
// TODO: deprecate
@@ -127,9 +127,9 @@ SLEPcEigenSolver::SLEPcEigenSolver(MPI_Comm comm,
// Set operators
if (B)
- EPSSetOperators(_eps, _matA->mat(), _matB->mat());
+ EPSSetOperators(_eps, A->mat(), B->mat());
else
- EPSSetOperators(_eps, _matA->mat(), NULL);
+ EPSSetOperators(_eps, A->mat(), NULL);
}
//-----------------------------------------------------------------------------
SLEPcEigenSolver::~SLEPcEigenSolver()
@@ -145,9 +145,9 @@ void SLEPcEigenSolver::set_operators(std::shared_ptr<const PETScMatrix> A,
// Set operators
dolfin_assert(_eps);
if (B)
- EPSSetOperators(_eps, _matA->mat(), _matB->mat());
+ EPSSetOperators(_eps, A->mat(), B->mat());
else
- EPSSetOperators(_eps, _matA->mat(), NULL);
+ EPSSetOperators(_eps, A->mat(), NULL);
}
//-----------------------------------------------------------------------------
void SLEPcEigenSolver::solve()
@@ -164,9 +164,18 @@ void SLEPcEigenSolver::solve()
//-----------------------------------------------------------------------------
void SLEPcEigenSolver::solve(std::size_t n)
{
+#ifdef DEBUG
+ // Get operators
+ Mat A, B;
+ dolfin_assert(_eps);
+ EPSGetOperators(_eps, &A, &B);
+
+ // Wrap operator as short-cut to get size
+ PETScMatrix A_wrapped(A);
+ dolfin_assert(n <= A_wrapped.size(0));
+#endif
+
// Set number of eigenpairs to compute
- dolfin_assert(_matA);
- dolfin_assert(n <= _matA->size(0));
dolfin_assert(_eps);
EPSSetDimensions(_eps, n, PETSC_DECIDE, PETSC_DECIDE);
@@ -250,9 +259,15 @@ void SLEPcEigenSolver::get_eigenpair(double& lr, double& lc,
if (ii < num_computed_eigenvalues)
{
- dolfin_assert(_matA);
- _matA->init_vector(r, 0);
- _matA->init_vector(c, 0);
+ // Get operators
+ Mat A, B;
+ dolfin_assert(_eps);
+ EPSGetOperators(_eps, &A, &B);
+
+ // Wrap operator and initialize r and c
+ PETScMatrix A_wrapped(A);
+ A_wrapped.init_vector(r, 0);
+ A_wrapped.init_vector(c, 0);
// Get eigen pairs
EPSGetEigenpair(_eps, ii, &lr, &lc, r.vec(), c.vec());
diff --git a/dolfin/la/SLEPcEigenSolver.h b/dolfin/la/SLEPcEigenSolver.h
index cee36a0..bb1f93c 100644
--- a/dolfin/la/SLEPcEigenSolver.h
+++ b/dolfin/la/SLEPcEigenSolver.h
@@ -228,9 +228,6 @@ namespace dolfin
// Set tolerance
void set_tolerance(double tolerance, int maxiter);
- // Operators (A x = \lambda x or Ax = \lambda B x)
- std::shared_ptr<const PETScMatrix> _matA, _matB;
-
// SLEPc solver pointer
EPS _eps;
diff --git a/dolfin/log/Logger.cpp b/dolfin/log/Logger.cpp
index f70b34b..2c99c07 100644
--- a/dolfin/log/Logger.cpp
+++ b/dolfin/log/Logger.cpp
@@ -92,7 +92,7 @@ void _monitor_memory_usage(dolfin::Logger* logger)
#endif
//-----------------------------------------------------------------------------
-Logger::Logger() : _active(true), _log_level(INFO), indentation_level(0),
+Logger::Logger() : _active(true), _log_level(INFO), _indentation_level(0),
logstream(&std::cout), _maximum_memory_usage(-1),
_mpi_comm(MPI_COMM_WORLD)
{
@@ -119,7 +119,7 @@ void Logger::log_underline(std::string msg, int log_level) const
std::stringstream s;
s << msg;
s << "\n";
- for (int i = 0; i < indentation_level; i++)
+ for (int i = 0; i < _indentation_level; i++)
s << " ";
for (std::size_t i = 0; i < msg.size(); i++)
s << "-";
@@ -213,12 +213,12 @@ void Logger::begin(std::string msg, int log_level)
{
// Write a message
log(msg, log_level);
- indentation_level++;
+ _indentation_level++;
}
//-----------------------------------------------------------------------------
void Logger::end()
{
- indentation_level--;
+ _indentation_level--;
}
//-----------------------------------------------------------------------------
void Logger::progress(std::string title, double p) const
@@ -226,7 +226,7 @@ void Logger::progress(std::string title, double p) const
std::stringstream line;
line << title << " [";
- const int N = DOLFIN_TERM_WIDTH - title.size() - 12 - 2*indentation_level;
+ const int N = DOLFIN_TERM_WIDTH - title.size() - 12 - 2*_indentation_level;
const int n = static_cast<int>(p*static_cast<double>(N));
for (int i = 0; i < n; i++)
@@ -258,6 +258,11 @@ void Logger::set_log_level(int log_level)
_log_level = log_level;
}
//-----------------------------------------------------------------------------
+void Logger::set_indentation_level(std::size_t indentation_level)
+{
+ _indentation_level = indentation_level;
+}
+//-----------------------------------------------------------------------------
void Logger::register_timing(std::string task,
std::tuple<double, double, double> elapsed)
{
@@ -455,7 +460,7 @@ void Logger::write(int log_level, std::string msg) const
}
// Add indentation
- for (int i = 0; i < indentation_level; i++)
+ for (int i = 0; i < _indentation_level; i++)
msg = " " + msg;
// Write to stream
diff --git a/dolfin/log/Logger.h b/dolfin/log/Logger.h
index 6c5d4de..b531286 100644
--- a/dolfin/log/Logger.h
+++ b/dolfin/log/Logger.h
@@ -101,6 +101,9 @@ namespace dolfin
/// Get log level
inline int get_log_level() const { return _log_level; }
+ /// Set indentation level
+ void set_indentation_level(std::size_t indentation_level);
+
/// Register timing (for later summary)
void register_timing(std::string task,
std::tuple<double, double, double> elapsed);
@@ -157,7 +160,7 @@ namespace dolfin
int _log_level;
// Current indentation level
- int indentation_level;
+ int _indentation_level;
// Optional stream for logging
std::ostream* logstream;
diff --git a/dolfin/log/log.cpp b/dolfin/log/log.cpp
index a8c9f3c..b33568a 100644
--- a/dolfin/log/log.cpp
+++ b/dolfin/log/log.cpp
@@ -182,6 +182,11 @@ void dolfin::set_log_level(int level)
LogManager::logger().set_log_level(level);
}
//-----------------------------------------------------------------------------
+void dolfin::set_indentation_level(std::size_t indentation_level)
+{
+ LogManager::logger().set_indentation_level(indentation_level);
+}
+//-----------------------------------------------------------------------------
void dolfin::set_output_stream(std::ostream& out)
{
LogManager::logger().set_output_stream(out);
diff --git a/dolfin/log/log.h b/dolfin/log/log.h
index 8f3e707..68e9eb5 100644
--- a/dolfin/log/log.h
+++ b/dolfin/log/log.h
@@ -117,6 +117,9 @@ namespace dolfin
/// Set log level
void set_log_level(int level);
+ /// Set indentation level
+ void set_indentation_level(std::size_t indentation_level);
+
/// Set output stream
void set_output_stream(std::ostream& out);
diff --git a/dolfin/mesh/BoundaryComputation.cpp b/dolfin/mesh/BoundaryComputation.cpp
index addd772..313cf9b 100644
--- a/dolfin/mesh/BoundaryComputation.cpp
+++ b/dolfin/mesh/BoundaryComputation.cpp
@@ -419,10 +419,10 @@ void BoundaryComputation::reorder(std::vector<std::size_t>& vertices,
// Check orientation
switch (mesh.type().cell_type())
{
- case CellType::interval:
+ case CellType::Type::interval:
// Do nothing
break;
- case CellType::triangle:
+ case CellType::Type::triangle:
{
dolfin_assert(facet.num_entities(0) == 2);
@@ -439,7 +439,7 @@ void BoundaryComputation::reorder(std::vector<std::size_t>& vertices,
}
}
break;
- case CellType::tetrahedron:
+ case CellType::Type::tetrahedron:
{
dolfin_assert(facet.num_entities(0) == 3);
diff --git a/dolfin/mesh/CMakeLists.txt b/dolfin/mesh/CMakeLists.txt
index cd9a864..d100530 100644
--- a/dolfin/mesh/CMakeLists.txt
+++ b/dolfin/mesh/CMakeLists.txt
@@ -52,6 +52,7 @@ set(HEADERS
set(SOURCES
BoundaryComputation.cpp
BoundaryMesh.cpp
+ Cell.cpp
CellType.cpp
DistributedMeshTools.cpp
DynamicMeshEditor.cpp
diff --git a/dolfin/geometry/intersect.cpp b/dolfin/mesh/Cell.cpp
similarity index 50%
copy from dolfin/geometry/intersect.cpp
copy to dolfin/mesh/Cell.cpp
index bc7364f..a5d8924 100644
--- a/dolfin/geometry/intersect.cpp
+++ b/dolfin/mesh/Cell.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Anders Logg
+// Copyright (C) 2016 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -14,20 +14,32 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2013-05-30
-// Last changed: 2013-05-30
-#include "MeshPointIntersection.h"
-#include "intersect.h"
+#include <dolfin/geometry/IntersectionConstruction.h>
+#include <dolfin/geometry/CollisionPredicates.h>
+#include "Cell.h"
using namespace dolfin;
//-----------------------------------------------------------------------------
-std::shared_ptr<const MeshPointIntersection>
-dolfin::intersect(const Mesh& mesh, const Point& point)
+bool Cell::contains(const Point& point) const
+{
+ return CollisionPredicates::collides(*this, point);
+}
+//-----------------------------------------------------------------------------
+bool Cell::collides(const Point& point) const
+{
+ return CollisionPredicates::collides(*this, point);
+}
+//-----------------------------------------------------------------------------
+bool Cell::collides(const MeshEntity& entity) const
+{
+ return CollisionPredicates::collides(*this, entity);
+}
+//-----------------------------------------------------------------------------
+std::vector<Point>
+Cell::intersection(const MeshEntity& entity) const
{
- return std::shared_ptr<const MeshPointIntersection>
- (new MeshPointIntersection(mesh, point));
+ return IntersectionConstruction::intersection(*this, entity);
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/mesh/Cell.h b/dolfin/mesh/Cell.h
index fee4ead..da7bda9 100644
--- a/dolfin/mesh/Cell.h
+++ b/dolfin/mesh/Cell.h
@@ -26,14 +26,13 @@
#include <memory>
-#include <dolfin/geometry/Point.h>
#include "CellType.h"
#include "Mesh.h"
#include "MeshEntity.h"
#include "MeshEntityIteratorBase.h"
#include "MeshFunction.h"
-#include <dolfin/geometry/CollisionDetection.h>
-#include <dolfin/geometry/IntersectionTriangulation.h>
+#include <ufc.h>
+#include <dolfin/geometry/Point.h>
namespace dolfin
{
@@ -257,8 +256,7 @@ namespace dolfin
///
/// @return bool
/// True iff point is contained in cell.
- bool contains(const Point& point) const
- { return _mesh->type().collides(*this, point); }
+ bool contains(const Point& point) const;
/// Check whether given point collides with cell
///
@@ -267,8 +265,7 @@ namespace dolfin
///
/// @return bool
/// True iff point collides with cell.
- bool collides(const Point& point) const
- { return _mesh->type().collides(*this, point); }
+ bool collides(const Point& point) const;
/// Check whether given entity collides with cell
///
@@ -277,21 +274,19 @@ namespace dolfin
///
/// @return bool
/// True iff entity collides with cell.
- bool collides(const MeshEntity& entity) const
- { return _mesh->type().collides(*this, entity); }
+ bool collides(const MeshEntity& entity) const;
/// Compute triangulation of intersection with given entity
///
/// @param entity
/// The entity with which to intersect.
///
- /// @return std::vector<double>
+ /// @return std::vector<Point>
/// A flattened array of simplices of dimension
/// num_simplices x num_vertices x gdim =
/// num_simplices x (tdim + 1) x gdim
- std::vector<double>
- triangulate_intersection(const MeshEntity& entity) const
- { return _mesh->type().triangulate_intersection(*this, entity); }
+ std::vector<Point>
+ intersection(const MeshEntity& entity) const;
// FIXME: This function is part of a UFC transition
/// Get cell coordinate dofs (not vertex coordinates)
@@ -425,11 +420,19 @@ namespace dolfin
/// Constructor on Mesh
CellFunction(std::shared_ptr<const Mesh> mesh)
- : MeshFunction<T>(mesh, mesh->topology().dim()) {}
+ : MeshFunction<T>(mesh, mesh->topology().dim()) {
+ deprecation("CellFunction<T>(mesh)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, mesh->topology().dim())");
+ }
/// Constructor on Mesh and value
CellFunction(std::shared_ptr<const Mesh> mesh, const T& value)
- : MeshFunction<T>(mesh, mesh->topology().dim(), value) {}
+ : MeshFunction<T>(mesh, mesh->topology().dim(), value) {
+ deprecation("CellFunction<T>(mesh, value)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, mesh->topology().dim(), value)");
+ }
};
}
diff --git a/dolfin/mesh/CellType.cpp b/dolfin/mesh/CellType.cpp
index 8a55ebf..e21f7e5 100644
--- a/dolfin/mesh/CellType.cpp
+++ b/dolfin/mesh/CellType.cpp
@@ -56,17 +56,17 @@ CellType* CellType::create(Type type)
{
switch ( type )
{
- case point:
+ case Type::point:
return new PointCell();
- case interval:
+ case Type::interval:
return new IntervalCell();
- case triangle:
+ case Type::triangle:
return new TriangleCell();
- case tetrahedron:
+ case Type::tetrahedron:
return new TetrahedronCell();
- case quadrilateral:
+ case Type::quadrilateral:
return new QuadrilateralCell();
- case hexahedron:
+ case Type::hexahedron:
return new HexahedronCell();
default:
dolfin_error("CellType.cpp",
@@ -85,17 +85,17 @@ CellType* CellType::create(std::string type)
CellType::Type CellType::string2type(std::string type)
{
if (type == "point")
- return point;
+ return Type::point;
else if (type == "interval")
- return interval;
+ return Type::interval;
else if (type == "triangle")
- return triangle;
+ return Type::triangle;
else if (type == "tetrahedron")
- return tetrahedron;
+ return Type::tetrahedron;
else if (type == "quadrilateral")
- return quadrilateral;
+ return Type::quadrilateral;
else if (type == "hexahedron")
- return hexahedron;
+ return Type::hexahedron;
else
{
dolfin_error("CellType.cpp",
@@ -103,24 +103,24 @@ CellType::Type CellType::string2type(std::string type)
"Unknown cell type (\"%s\")", type.c_str());
}
- return interval;
+ return Type::interval;
}
//-----------------------------------------------------------------------------
std::string CellType::type2string(Type type)
{
switch (type)
{
- case point:
+ case Type::point:
return "point";
- case interval:
+ case Type::interval:
return "interval";
- case triangle:
+ case Type::triangle:
return "triangle";
- case tetrahedron:
+ case Type::tetrahedron:
return "tetrahedron";
- case quadrilateral:
+ case Type::quadrilateral:
return "quadrilateral";
- case hexahedron:
+ case Type::hexahedron:
return "hexahedron";
default:
dolfin_error("CellType.cpp",
@@ -159,8 +159,8 @@ double CellType::h(const MeshEntity& entity) const
// Get the coordinates (Points) of the vertices
const unsigned int* vertices = entity.entities(0);
dolfin_assert(vertices);
- std::array<Point, 4> points;
- dolfin_assert(num_vertices <= 4);
+ std::array<Point, 8> points;
+ dolfin_assert(num_vertices <= 8);
for (int i = 0; i < num_vertices; ++i)
points[i] = geometry.point(vertices[i]);
@@ -178,8 +178,8 @@ double CellType::h(const MeshEntity& entity) const
double CellType::inradius(const Cell& cell) const
{
// Check cell type
- if (_cell_type != interval && _cell_type != triangle
- && _cell_type != tetrahedron)
+ if (_cell_type != Type::interval && _cell_type != Type::triangle
+ && _cell_type != Type::tetrahedron)
{
dolfin_error("Cell.h",
"compute cell inradius",
diff --git a/dolfin/mesh/CellType.h b/dolfin/mesh/CellType.h
index 4f40eb2..4d3d0aa 100644
--- a/dolfin/mesh/CellType.h
+++ b/dolfin/mesh/CellType.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2013 Anders Logg
+// Copyright (C) 2006-2017 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -20,7 +20,7 @@
// Modified by Jan Blechta 2013
//
// First added: 2006-06-05
-// Last changed: 2014-04-24
+// Last changed: 2017-09-26
#ifndef __CELL_TYPE_H
#define __CELL_TYPE_H
@@ -48,7 +48,7 @@ namespace dolfin
public:
/// Enum for different cell types
- enum Type { point, interval, triangle, quadrilateral, tetrahedron, hexahedron };
+ enum class Type : int { point, interval, triangle, quadrilateral, tetrahedron, hexahedron };
/// Constructor
CellType(Type cell_type, Type facet_type);
@@ -79,6 +79,9 @@ namespace dolfin
/// Return type of cell for entity of dimension i
Type entity_type(std::size_t i) const;
+ /// Check if cell is a simplex
+ virtual bool is_simplex() const = 0;
+
/// Return topological dimension of cell
virtual std::size_t dim() const = 0;
@@ -153,14 +156,6 @@ namespace dolfin
/// Check whether given entity collides with cell
virtual bool collides(const Cell& cell, const MeshEntity& entity) const = 0;
- /// Compute triangulation of intersection of two cells
- virtual std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const = 0;
-
- /// Compute triangulation of intersection with given entity
- virtual std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const = 0;
-
/// Return description of cell type
virtual std::string description(bool plural) const = 0;
diff --git a/dolfin/mesh/DistributedMeshTools.cpp b/dolfin/mesh/DistributedMeshTools.cpp
index 12ffe7d..03c345b 100644
--- a/dolfin/mesh/DistributedMeshTools.cpp
+++ b/dolfin/mesh/DistributedMeshTools.cpp
@@ -113,7 +113,7 @@ std::size_t DistributedMeshTools::number_entities(
{
shared_entities.clear();
global_entity_indices = mesh.topology().global_indices(d);
- return mesh.size_global(d);
+ return mesh.num_entities_global(d);
/*
dolfin_error("MeshPartitioning.cpp",
@@ -196,7 +196,7 @@ std::size_t DistributedMeshTools::number_entities(
// Prepare list of global entity numbers. Check later that nothing
// is equal to -1
global_entity_indices
- = std::vector<std::int64_t>(mesh.size(d), -1);
+ = std::vector<std::int64_t>(mesh.num_entities(d), -1);
std::map<Entity, EntityData>::const_iterator it;
diff --git a/dolfin/mesh/DynamicMeshEditor.cpp b/dolfin/mesh/DynamicMeshEditor.cpp
index 59c8ee7..4627ccc 100644
--- a/dolfin/mesh/DynamicMeshEditor.cpp
+++ b/dolfin/mesh/DynamicMeshEditor.cpp
@@ -62,22 +62,22 @@ void DynamicMeshEditor::open(Mesh& mesh, std::string type, std::size_t tdim,
{
if (type == "point")
{
- open(mesh, CellType::point, tdim, gdim, num_global_vertices,
+ open(mesh, CellType::Type::point, tdim, gdim, num_global_vertices,
num_global_cells);
}
else if (type == "interval")
{
- open(mesh, CellType::interval, tdim, gdim, num_global_vertices,
+ open(mesh, CellType::Type::interval, tdim, gdim, num_global_vertices,
num_global_cells);
}
else if (type == "triangle")
{
- open(mesh, CellType::triangle, tdim, gdim, num_global_vertices,
+ open(mesh, CellType::Type::triangle, tdim, gdim, num_global_vertices,
num_global_cells);
}
else if (type == "tetrahedron")
{
- open(mesh, CellType::tetrahedron, tdim, gdim, num_global_vertices,
+ open(mesh, CellType::Type::tetrahedron, tdim, gdim, num_global_vertices,
num_global_cells);
}
else
diff --git a/dolfin/mesh/Edge.h b/dolfin/mesh/Edge.h
index 5ee3a03..94f6f60 100644
--- a/dolfin/mesh/Edge.h
+++ b/dolfin/mesh/Edge.h
@@ -101,11 +101,19 @@ namespace dolfin
/// Constructor on Mesh
EdgeFunction(std::shared_ptr<const Mesh> mesh)
- : MeshFunction<T>(mesh, 1) {}
+ : MeshFunction<T>(mesh, 1) {
+ deprecation("EdgeFunction<T>(mesh)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, 1)");
+ }
/// Constructor on Mesh and value
EdgeFunction(std::shared_ptr<const Mesh> mesh, const T& value)
- : MeshFunction<T>(mesh, 1, value) {}
+ : MeshFunction<T>(mesh, 1, value) {
+ deprecation("EdgeFunction<T>(mesh, value)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, 1, value)");
+ }
};
diff --git a/dolfin/mesh/Face.h b/dolfin/mesh/Face.h
index c25c1d7..e078352 100644
--- a/dolfin/mesh/Face.h
+++ b/dolfin/mesh/Face.h
@@ -68,11 +68,19 @@ namespace dolfin
/// Constructor on Mesh
FaceFunction(std::shared_ptr<const Mesh> mesh)
- : MeshFunction<T>(mesh, 2) {}
+ : MeshFunction<T>(mesh, 2) {
+ deprecation("FaceFunction<T>(mesh)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, 2)");
+ }
/// Constructor on Mesh and value
FaceFunction(std::shared_ptr<const Mesh> mesh, const T& value)
- : MeshFunction<T>(mesh, 2, value) {}
+ : MeshFunction<T>(mesh, 2, value) {
+ deprecation("FaceFunction<T>(mesh, value)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, 2, value)");
+ }
};
diff --git a/dolfin/mesh/Facet.h b/dolfin/mesh/Facet.h
index c42e184..21ebf19 100644
--- a/dolfin/mesh/Facet.h
+++ b/dolfin/mesh/Facet.h
@@ -91,11 +91,19 @@ namespace dolfin
/// Constructor on Mesh
FacetFunction(std::shared_ptr<const Mesh> mesh)
- : MeshFunction<T>(mesh, mesh->topology().dim() - 1) {}
+ : MeshFunction<T>(mesh, mesh->topology().dim() - 1) {
+ deprecation("FacetFunction<T>(mesh)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, mesh->topology().dim() - 1)");
+ }
/// Constructor on Mesh and value
FacetFunction(std::shared_ptr<const Mesh> mesh, const T& value)
- : MeshFunction<T>(mesh, mesh->topology().dim() - 1, value) {}
+ : MeshFunction<T>(mesh, mesh->topology().dim() - 1, value) {
+ deprecation("FacetFunction<T>(mesh, value)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, mesh->topology().dim() - 1, value)");
+ }
};
diff --git a/dolfin/mesh/HexahedronCell.cpp b/dolfin/mesh/HexahedronCell.cpp
index 526f584..94b8559 100644
--- a/dolfin/mesh/HexahedronCell.cpp
+++ b/dolfin/mesh/HexahedronCell.cpp
@@ -241,20 +241,6 @@ bool HexahedronCell::collides(const Cell& cell, const MeshEntity& entity) const
return false;
}
//-----------------------------------------------------------------------------
-std::vector<double>
-HexahedronCell::triangulate_intersection(const Cell& c0, const Cell& c1) const
-{
- dolfin_not_implemented();
- return std::vector<double>();
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-HexahedronCell::triangulate_intersection(const Cell& cell, const MeshEntity& entity) const
-{
- dolfin_not_implemented();
- return std::vector<double>();
-}
-//-----------------------------------------------------------------------------
std::string HexahedronCell::description(bool plural) const
{
if (plural)
diff --git a/dolfin/mesh/HexahedronCell.h b/dolfin/mesh/HexahedronCell.h
index 1830470..4fa8b89 100644
--- a/dolfin/mesh/HexahedronCell.h
+++ b/dolfin/mesh/HexahedronCell.h
@@ -14,7 +14,6 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
#ifndef __HEXAHEDRON_CELL_H
#define __HEXAHEDRON_CELL_H
@@ -25,14 +24,18 @@
namespace dolfin
{
- /// This class implements functionality for triangular meshes.
+ /// This class implements functionality for hexahedral cell meshes.
class HexahedronCell : public CellType
{
public:
/// Specify cell type and facet type
- HexahedronCell() : CellType(hexahedron, quadrilateral) {}
+ HexahedronCell() : CellType(Type::hexahedron, Type::quadrilateral) {}
+
+ /// Check if cell is a simplex
+ bool is_simplex() const
+ { return false; }
/// Return topological dimension of cell
std::size_t dim() const;
@@ -82,14 +85,6 @@ namespace dolfin
/// Check whether given entity collides with cell
bool collides(const Cell& cell, const MeshEntity& entity) const;
- /// Compute triangulation of intersection of two cells
- std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const;
-
- /// Compute triangulation of intersection with given entity
- std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const;
-
/// Return description of cell type
std::string description(bool plural) const;
diff --git a/dolfin/mesh/IntervalCell.cpp b/dolfin/mesh/IntervalCell.cpp
index 35e0d99..71c5bee 100644
--- a/dolfin/mesh/IntervalCell.cpp
+++ b/dolfin/mesh/IntervalCell.cpp
@@ -21,10 +21,11 @@
// Modified by August Johansson 2014
//
// First added: 2006-06-05
-// Last changed: 2014-05-22
+// Last changed: 2016-05-05
#include <algorithm>
#include <dolfin/log/log.h>
+#include <dolfin/geometry/CollisionPredicates.h>
#include "Cell.h"
#include "MeshEditor.h"
#include "MeshEntity.h"
@@ -240,24 +241,12 @@ void IntervalCell::order(Cell& cell,
//-----------------------------------------------------------------------------
bool IntervalCell::collides(const Cell& cell, const Point& point) const
{
- return CollisionDetection::collides(cell, point);
+ return CollisionPredicates::collides(cell, point);
}
//-----------------------------------------------------------------------------
bool IntervalCell::collides(const Cell& cell, const MeshEntity& entity) const
{
- return CollisionDetection::collides(cell, entity);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntervalCell::triangulate_intersection(const Cell& c0, const Cell& c1) const
-{
- return IntersectionTriangulation::triangulate_intersection(c0, c1);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-IntervalCell::triangulate_intersection(const Cell& cell, const MeshEntity& entity) const
-{
- return IntersectionTriangulation::triangulate_intersection(cell, entity);
+ return CollisionPredicates::collides(cell, entity);
}
//-----------------------------------------------------------------------------
std::string IntervalCell::description(bool plural) const
diff --git a/dolfin/mesh/IntervalCell.h b/dolfin/mesh/IntervalCell.h
index c23162a..1197d65 100644
--- a/dolfin/mesh/IntervalCell.h
+++ b/dolfin/mesh/IntervalCell.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2013 Anders Logg
+// Copyright (C) 2006-2017 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -18,7 +18,7 @@
// Modified by Kristoffer Selim 2008
//
// First added: 2006-06-05
-// Last changed: 2014-05-22
+// Last changed: 2017-09-26
#ifndef __INTERVAL_CELL_H
#define __INTERVAL_CELL_H
@@ -36,14 +36,18 @@ namespace dolfin
class MeshEntity;
template<typename T> class MeshFunction;
- /// This class implements functionality for interval meshes.
+ /// This class implements functionality for interval cell meshes.
class IntervalCell : public CellType
{
public:
/// Specify cell type and facet type
- IntervalCell() : CellType(interval, point) {}
+ IntervalCell() : CellType(Type::interval, Type::point) {}
+
+ /// Check if cell is a simplex
+ bool is_simplex() const
+ { return true; }
/// Return topological dimension of cell
std::size_t dim() const;
@@ -105,14 +109,6 @@ namespace dolfin
virtual
bool collides(const Cell& cell, const MeshEntity& entity) const;
- /// Compute triangulation of intersection of two cells
- virtual std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const;
-
- /// Compute triangulation of intersection of two cells
- virtual std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const;
-
/// Return description of cell type
std::string description(bool plural) const;
diff --git a/dolfin/mesh/LocalMeshData.cpp b/dolfin/mesh/LocalMeshData.cpp
index c7375c8..f35f42c 100644
--- a/dolfin/mesh/LocalMeshData.cpp
+++ b/dolfin/mesh/LocalMeshData.cpp
@@ -181,7 +181,7 @@ void LocalMeshData::broadcast_mesh_data(const MPI_Comm mpi_comm)
values.push_back(geometry.num_global_vertices);
values.push_back(topology.num_global_cells);
values.push_back(topology.num_vertices_per_cell);
- values.push_back(topology.cell_type);
+ values.push_back(static_cast<int>(topology.cell_type));
MPI::broadcast(mpi_comm, values);
}
diff --git a/dolfin/mesh/Mesh.cpp b/dolfin/mesh/Mesh.cpp
index 647d4ff..3bb1e4d 100644
--- a/dolfin/mesh/Mesh.cpp
+++ b/dolfin/mesh/Mesh.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2014 Anders Logg
+// Copyright (C) 2006-2016 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -26,7 +26,7 @@
// Modified by Jan Blechta 2013
//
// First added: 2006-05-09
-// Last changed: 2014-08-11
+// Last changed: 2016-05-05
#include <dolfin/ale/ALE.h>
#include <dolfin/common/Array.h>
@@ -273,6 +273,11 @@ dolfin::Mesh Mesh::renumber_by_color() const
return MeshRenumbering::renumber_by_color(*this, coloring_type);
}
//-----------------------------------------------------------------------------
+void Mesh::scale(double factor)
+{
+ MeshTransformation::scale(*this, factor);
+}
+//-----------------------------------------------------------------------------
void Mesh::translate(const Point& point)
{
MeshTransformation::translate(*this, point);
diff --git a/dolfin/mesh/Mesh.h b/dolfin/mesh/Mesh.h
index cbd61e7..fd92593 100644
--- a/dolfin/mesh/Mesh.h
+++ b/dolfin/mesh/Mesh.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2013 Anders Logg
+// Copyright (C) 2006-2016 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -32,7 +32,6 @@
#include <string>
#include <utility>
-#include <dolfin/ale/MeshDisplacement.h>
#include <dolfin/common/Hierarchical.h>
#include <dolfin/common/MPI.h>
#include <dolfin/common/Variable.h>
@@ -202,17 +201,6 @@ namespace dolfin
const std::vector<unsigned int>& cells() const
{ return _topology(_topology.dim(), 0)(); }
- /// Get number of local entities of given topological dimension.
- ///
- /// @param dim (std::size_t)
- /// Topological dimension.
- ///
- /// @return std::size_t
- /// Number of local entities of topological dimension d.
- ///
- std::size_t size(std::size_t dim) const
- { return _topology.size(dim); }
-
/// Get global number of entities of given topological dimension.
///
/// @param dim (std::size_t)
@@ -221,7 +209,7 @@ namespace dolfin
/// @return std::size_t
/// Global number of entities of topological dimension d.
///
- std::size_t size_global(std::size_t dim) const
+ std::size_t num_entities_global(std::size_t dim) const
{ return _topology.size_global(dim); }
/// Get mesh topology.
@@ -348,6 +336,13 @@ namespace dolfin
/// @return Mesh
Mesh renumber_by_color() const;
+ /// Scale mesh coordinates with given factor.
+ ///
+ /// *Arguments*
+ /// factor (double)
+ /// The factor defining the scaling.
+ void scale(double factor);
+
/// Translate mesh according to a given vector.
///
/// @param point (Point)
@@ -405,7 +400,7 @@ namespace dolfin
/// Color the cells of the mesh such that no two neighboring cells
/// share the same color. A colored mesh keeps a
- /// CellFunction<std::size_t> named "cell colors" as mesh data which
+ /// MeshFunction<std::size_t> named "cell colors" as mesh data which
/// holds the colors of the mesh.
///
/// @param coloring_type (std::string)
@@ -419,7 +414,7 @@ namespace dolfin
/// Color the cells of the mesh such that no two neighboring cells
/// share the same color. A colored mesh keeps a
- /// CellFunction<std::size_t> named "cell colors" as mesh data which
+ /// MeshFunction<std::size_t> named "cell colors" as mesh data which
/// holds the colors of the mesh.
///
/// @param coloring_type (std::vector<std::size_t>&)
diff --git a/dolfin/mesh/MeshColoring.cpp b/dolfin/mesh/MeshColoring.cpp
index 4303121..1fe101e 100644
--- a/dolfin/mesh/MeshColoring.cpp
+++ b/dolfin/mesh/MeshColoring.cpp
@@ -120,7 +120,7 @@ MeshColoring::compute_colors(const Mesh& mesh,
return GraphColoring::compute_local_vertex_coloring(graph, colors);
}
//-----------------------------------------------------------------------------
-CellFunction<std::size_t>
+MeshFunction<std::size_t>
MeshColoring::cell_colors(std::shared_ptr<const Mesh> mesh,
std::string coloring_type)
{
@@ -136,7 +136,7 @@ MeshColoring::cell_colors(std::shared_ptr<const Mesh> mesh,
return cell_colors(mesh, _coloring_type);
}
//-----------------------------------------------------------------------------
-CellFunction<std::size_t>
+MeshFunction<std::size_t>
MeshColoring::cell_colors(std::shared_ptr<const Mesh> mesh,
std::vector<std::size_t> coloring_type)
{
@@ -159,7 +159,7 @@ MeshColoring::cell_colors(std::shared_ptr<const Mesh> mesh,
// Colors
const std::vector<std::size_t>& colors = coloring_data->second.first;
- CellFunction<std::size_t> mf(mesh);
+ MeshFunction<std::size_t> mf(mesh, mesh->topology().dim());
dolfin_assert(colors.size() == mesh->num_entities(coloring_type[0]));
for (CellIterator cell(*mesh); !cell.end(); ++cell)
mf[*cell] = colors[cell->index()];
diff --git a/dolfin/mesh/MeshColoring.h b/dolfin/mesh/MeshColoring.h
index 7f4b6aa..9a69e57 100644
--- a/dolfin/mesh/MeshColoring.h
+++ b/dolfin/mesh/MeshColoring.h
@@ -60,12 +60,12 @@ namespace dolfin
/// Return a MeshFunction with the cell colors (used for
/// visualisation)
- static CellFunction<std::size_t>
+ static MeshFunction<std::size_t>
cell_colors(std::shared_ptr<const Mesh> mesh,
std::string coloring_type);
/// Return a MeshFunction with the cell colors (used for visualisation)
- static CellFunction<std::size_t>
+ static MeshFunction<std::size_t>
cell_colors(std::shared_ptr<const Mesh> mesh,
std::vector<std::size_t> coloring_type);
diff --git a/dolfin/mesh/MeshEditor.cpp b/dolfin/mesh/MeshEditor.cpp
index 2b10f7e..f8efe76 100644
--- a/dolfin/mesh/MeshEditor.cpp
+++ b/dolfin/mesh/MeshEditor.cpp
@@ -41,36 +41,6 @@ MeshEditor::~MeshEditor()
// Do nothing
}
//-----------------------------------------------------------------------------
-void MeshEditor::open(Mesh& mesh, std::size_t tdim,
- std::size_t gdim, std::size_t degree)
-{
- switch (tdim)
- {
- case 0:
- open(mesh, CellType::point, tdim, gdim, degree);
- break;
- case 1:
- open(mesh, CellType::interval, tdim, gdim, degree);
- break;
- case 2:
- open(mesh, CellType::triangle, tdim, gdim, degree);
- break;
- case 3:
- open(mesh, CellType::tetrahedron, tdim, gdim, degree);
- break;
- case 4:
- open(mesh, CellType::quadrilateral, tdim, gdim, degree);
- break;
- case 5:
- open(mesh, CellType::hexahedron, tdim, gdim, degree);
- break;
- default:
- dolfin_error("MeshEditor.cpp",
- "open mesh for editing",
- "Unknown cell type of topological dimension %d", tdim);
- }
-}
-//-----------------------------------------------------------------------------
void MeshEditor::open(Mesh& mesh, CellType::Type type, std::size_t tdim,
std::size_t gdim, std::size_t degree)
{
@@ -107,17 +77,17 @@ void MeshEditor::open(Mesh& mesh, std::string type, std::size_t tdim,
std::size_t gdim, std::size_t degree)
{
if (type == "point")
- open(mesh, CellType::point, tdim, gdim, degree);
+ open(mesh, CellType::Type::point, tdim, gdim, degree);
else if (type == "interval")
- open(mesh, CellType::interval, tdim, gdim, degree);
+ open(mesh, CellType::Type::interval, tdim, gdim, degree);
else if (type == "triangle")
- open(mesh, CellType::triangle, tdim, gdim, degree);
+ open(mesh, CellType::Type::triangle, tdim, gdim, degree);
else if (type == "tetrahedron")
- open(mesh, CellType::tetrahedron, tdim, gdim, degree);
+ open(mesh, CellType::Type::tetrahedron, tdim, gdim, degree);
else if (type == "quadrilateral")
- open(mesh, CellType::quadrilateral, tdim, gdim, degree);
+ open(mesh, CellType::Type::quadrilateral, tdim, gdim, degree);
else if (type == "hexahedron")
- open(mesh, CellType::hexahedron, tdim, gdim, degree);
+ open(mesh, CellType::Type::hexahedron, tdim, gdim, degree);
else
{
dolfin_error("MeshEditor.cpp",
diff --git a/dolfin/mesh/MeshEditor.h b/dolfin/mesh/MeshEditor.h
index fe69b82..74436d9 100644
--- a/dolfin/mesh/MeshEditor.h
+++ b/dolfin/mesh/MeshEditor.h
@@ -43,24 +43,6 @@ namespace dolfin
/// Destructor
~MeshEditor();
- /// Open mesh of given topological and geometrical dimension
- ///
- /// @param mesh (_Mesh_)
- /// The mesh to open.
- /// @param tdim (std::size_t)
- /// The topological dimension.
- /// @param gdim (std::size_t)
- /// The geometrical dimension.
- /// @param degree (std::size_t)
- /// The polynomial degree.
- /// @code{.cpp}
- ///
- /// Mesh mesh;
- /// MeshEditor editor;
- /// editor.open(mesh, 2, 2, 1);
- /// @endcode
- void open(Mesh& mesh, std::size_t tdim, std::size_t gdim, std::size_t degree=1);
-
/// Open mesh of given cell type, topological and geometrical dimension
///
/// @param mesh (_Mesh_)
diff --git a/dolfin/mesh/MeshFunction.h b/dolfin/mesh/MeshFunction.h
index db32263..08cbc82 100644
--- a/dolfin/mesh/MeshFunction.h
+++ b/dolfin/mesh/MeshFunction.h
@@ -579,7 +579,7 @@ namespace dolfin
}
_mesh->init(dim);
- init(_mesh, dim, _mesh->size(dim));
+ init(_mesh, dim, _mesh->num_entities(dim));
}
//---------------------------------------------------------------------------
template <typename T>
@@ -601,7 +601,7 @@ namespace dolfin
{
dolfin_assert(mesh);
mesh->init(dim);
- init(mesh, dim, mesh->size(dim));
+ init(mesh, dim, mesh->num_entities(dim));
}
//---------------------------------------------------------------------------
template <typename T>
@@ -612,7 +612,7 @@ namespace dolfin
// Initialize mesh for entities of given dimension
mesh->init(dim);
- dolfin_assert(mesh->size(dim) == size);
+ dolfin_assert(mesh->num_entities(dim) == size);
// Initialize data
if (_size != size)
@@ -652,7 +652,7 @@ namespace dolfin
std::size_t n = std::count(_values.get(), _values.get() + _size, value);
std::vector<std::size_t> indices;
indices.reserve(n);
- for (int i = 0; i < size(); ++i)
+ for (std::size_t i = 0; i < size(); ++i)
{
if (_values[i] == value)
indices.push_back(i);
diff --git a/dolfin/mesh/MeshHierarchy.cpp b/dolfin/mesh/MeshHierarchy.cpp
index fe8a44d..d0fd378 100644
--- a/dolfin/mesh/MeshHierarchy.cpp
+++ b/dolfin/mesh/MeshHierarchy.cpp
@@ -98,7 +98,7 @@ MeshHierarchy::coarsen(const MeshFunction<bool>& coarsen_markers) const
}
// Set up refinement markers to re-refine the parent mesh
- EdgeFunction<bool> edge_markers(parent_mesh, false);
+ MeshFunction<bool> edge_markers(parent_mesh, 1, false);
const std::map<std::size_t, std::size_t>& edge_to_vertex
= *(_relation->edge_to_global_vertex);
@@ -191,8 +191,8 @@ std::shared_ptr<Mesh> MeshHierarchy::rebalance() const
// Cells
- local_mesh_data.topology.num_global_cells = coarse_mesh.size_global(tdim);
- const std::size_t num_local_cells = coarse_mesh.size(tdim);
+ local_mesh_data.topology.num_global_cells = coarse_mesh.num_entities_global(tdim);
+ const std::size_t num_local_cells = coarse_mesh.num_entities(tdim);
local_mesh_data.topology.global_cell_indices.resize(num_local_cells);
local_mesh_data.topology.cell_vertices.resize(boost::extents[num_local_cells][local_mesh_data.topology.num_vertices_per_cell]);
@@ -206,8 +206,8 @@ std::shared_ptr<Mesh> MeshHierarchy::rebalance() const
// Vertices - must be reordered into global order
- const std::size_t num_local_vertices = coarse_mesh.size(0);
- local_mesh_data.geometry.num_global_vertices = coarse_mesh.size_global(0);
+ const std::size_t num_local_vertices = coarse_mesh.num_entities(0);
+ local_mesh_data.geometry.num_global_vertices = coarse_mesh.num_entities_global(0);
local_mesh_data.geometry.vertex_indices.resize(num_local_vertices);
for (VertexIterator v(coarse_mesh); !v.end(); ++v)
local_mesh_data.geometry.vertex_indices[v->index()] = v->global_index();
diff --git a/dolfin/mesh/MeshQuality.cpp b/dolfin/mesh/MeshQuality.cpp
index ba03139..408f035 100644
--- a/dolfin/mesh/MeshQuality.cpp
+++ b/dolfin/mesh/MeshQuality.cpp
@@ -29,11 +29,11 @@
using namespace dolfin;
//-----------------------------------------------------------------------------
-dolfin::CellFunction<double>
+dolfin::MeshFunction<double>
MeshQuality::radius_ratios(std::shared_ptr<const Mesh> mesh)
{
- // Create CellFunction
- CellFunction<double> cf(mesh, 0.0);
+ // Create MeshFunction
+ MeshFunction<double> cf(mesh, mesh->topology().dim(), 0.0);
// Compute radius ration
for (CellIterator cell(*mesh); !cell.end(); ++cell)
@@ -143,14 +143,12 @@ MeshQuality::radius_ratio_matplotlib_histogram(const Mesh& mesh,
//-----------------------------------------------------------------------------
void MeshQuality::dihedral_angles(const Cell& cell, std::vector<double>& dh_angle)
{
- if (cell.type() < 4)
+ if (cell.dim() != 3)
{
dolfin_error("MeshQuality.cpp",
"calculate dihedral angles",
"Only works for 3D cells");
}
- // Check cell type
- // dolfin_assert(cell.type()>=4);
static const std::size_t edges[6][2] = {{2, 3},
{1, 3},
diff --git a/dolfin/mesh/MeshQuality.h b/dolfin/mesh/MeshQuality.h
index cce04ac..c2f164d 100644
--- a/dolfin/mesh/MeshQuality.h
+++ b/dolfin/mesh/MeshQuality.h
@@ -41,13 +41,13 @@ namespace dolfin
/// Compute the radius ratio for all cells.
/// @param mesh (std::shared_ptr<const Mesh>)
- /// @return CellFunction<double>
+ /// @return MeshFunction<double>
/// The cell radius ratio radius ratio geometric_dimension *
/// * inradius / circumradius (geometric_dimension
/// is normalization factor). It has range zero to one.
/// Zero indicates a degenerate element.
///
- static CellFunction<double>
+ static MeshFunction<double>
radius_ratios(std::shared_ptr<const Mesh> mesh);
diff --git a/dolfin/mesh/MeshRenumbering.cpp b/dolfin/mesh/MeshRenumbering.cpp
index dfd67fd..8fb88ae 100644
--- a/dolfin/mesh/MeshRenumbering.cpp
+++ b/dolfin/mesh/MeshRenumbering.cpp
@@ -45,10 +45,10 @@ dolfin::Mesh MeshRenumbering::renumber_by_color(const Mesh& mesh,
// Get some some mesh
const std::size_t tdim = mesh.topology().dim();
const std::size_t gdim = mesh.geometry().dim();
- const std::size_t num_local_vertices = mesh.size(0);
- const std::size_t num_global_vertices = mesh.size_global(0);
- const std::size_t num_local_cells = mesh.size(tdim);
- const std::size_t num_global_cells = mesh.size_global(tdim);
+ const std::size_t num_local_vertices = mesh.num_entities(0);
+ const std::size_t num_global_vertices = mesh.num_entities_global(0);
+ const std::size_t num_local_cells = mesh.num_entities(tdim);
+ const std::size_t num_global_cells = mesh.num_entities_global(tdim);
// Check that requested coloring is a cell coloring
if (coloring_type[0] != tdim)
diff --git a/dolfin/mesh/MeshTopology.cpp b/dolfin/mesh/MeshTopology.cpp
index cb6de76..9b061bd 100644
--- a/dolfin/mesh/MeshTopology.cpp
+++ b/dolfin/mesh/MeshTopology.cpp
@@ -28,13 +28,14 @@
using namespace dolfin;
//-----------------------------------------------------------------------------
-MeshTopology::MeshTopology()
+MeshTopology::MeshTopology() : Variable("topology", "mesh topology")
{
// Do nothing
}
//-----------------------------------------------------------------------------
MeshTopology::MeshTopology(const MeshTopology& topology)
- : coloring(topology.coloring), num_entities(topology.num_entities),
+ : Variable("topology", "mesh topology"),
+ coloring(topology.coloring), num_entities(topology.num_entities),
ghost_offset_index(topology.ghost_offset_index),
global_num_entities(topology.global_num_entities),
_global_indices(topology._global_indices),
diff --git a/dolfin/mesh/MeshTopology.h b/dolfin/mesh/MeshTopology.h
index 364ab8e..bf87c90 100644
--- a/dolfin/mesh/MeshTopology.h
+++ b/dolfin/mesh/MeshTopology.h
@@ -25,6 +25,8 @@
#include <map>
#include <utility>
#include <vector>
+
+#include <dolfin/common/Variable.h>
#include "MeshConnectivity.h"
namespace dolfin
@@ -41,7 +43,7 @@ namespace dolfin
/// i), where dim is the topological dimension and i is the index of
/// the entity within that topological dimension.
- class MeshTopology
+ class MeshTopology : public Variable
{
public:
diff --git a/dolfin/mesh/MeshTransformation.cpp b/dolfin/mesh/MeshTransformation.cpp
index 7475974..1fb4c35 100644
--- a/dolfin/mesh/MeshTransformation.cpp
+++ b/dolfin/mesh/MeshTransformation.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Anders Logg
+// Copyright (C) 2012-2016 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -25,6 +25,22 @@
using namespace dolfin;
//-----------------------------------------------------------------------------
+void MeshTransformation::scale(Mesh& mesh, double factor)
+{
+ // Get mesh geometry
+ MeshGeometry& geometry = mesh.geometry();
+ const std::size_t gdim = geometry.dim();
+
+ // Scale all points
+ std::vector<double> x0(gdim);
+ for (std::size_t i = 0; i < geometry.num_vertices(); i++)
+ {
+ for (std::size_t j = 0; j < gdim; j++)
+ x0[j] = factor*geometry.x(i, j);
+ geometry.set(i, x0.data());
+ }
+}
+//-----------------------------------------------------------------------------
void MeshTransformation::translate(Mesh& mesh, const Point& point)
{
// Get mesh geometry
diff --git a/dolfin/mesh/MeshTransformation.h b/dolfin/mesh/MeshTransformation.h
index 75c1ee3..3892d4a 100644
--- a/dolfin/mesh/MeshTransformation.h
+++ b/dolfin/mesh/MeshTransformation.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Anders Logg
+// Copyright (C) 2012-2016 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -16,7 +16,7 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2012-01-16
-// Last changed: 2013-06-27
+// Last changed: 2016-05-03
#ifndef __MESH_TRANSFORMATION_H
#define __MESH_TRANSFORMATION_H
@@ -34,6 +34,15 @@ class MeshTransformation
{
public:
+ /// Scale mesh coordinates with given factor.
+ ///
+ /// *Arguments*
+ /// mesh (_Mesh_)
+ /// The mesh
+ /// factor (double)
+ /// The factor defining the scaling.
+ static void scale(Mesh& mesh, double factor);
+
/// Translate mesh according to a given vector.
///
/// @param mesh (Mesh)
diff --git a/dolfin/mesh/MultiMesh.cpp b/dolfin/mesh/MultiMesh.cpp
index ef2b0e7..575a8cb 100644
--- a/dolfin/mesh/MultiMesh.cpp
+++ b/dolfin/mesh/MultiMesh.cpp
@@ -15,19 +15,24 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// Modified by August Johansson 2014
+// Modified by August Johansson 2016
+// Modified by Benjamin Kehlet 2016
//
// First added: 2013-08-05
-// Last changed: 2016-03-02
+// Last changed: 2017-10-09
#include <cmath>
-
#include <dolfin/log/log.h>
#include <dolfin/common/NoDeleter.h>
#include <dolfin/geometry/BoundingBoxTree.h>
#include <dolfin/geometry/SimplexQuadrature.h>
+#include <dolfin/geometry/IntersectionConstruction.h>
+#include <dolfin/geometry/ConvexTriangulation.h>
+#include <dolfin/geometry/GeometryPredicates.h>
+
#include "Cell.h"
#include "Facet.h"
+#include "Vertex.h"
#include "BoundaryMesh.h"
#include "MeshFunction.h"
#include "MultiMesh.h"
@@ -35,14 +40,18 @@
using namespace dolfin;
//-----------------------------------------------------------------------------
-MultiMesh::MultiMesh()
+MultiMesh::MultiMesh() : _is_built(false)
{
- // Do nothing
+ // Set parameters
+ parameters = default_parameters();
}
//-----------------------------------------------------------------------------
MultiMesh::MultiMesh(std::vector<std::shared_ptr<const Mesh>> meshes,
- std::size_t quadrature_order)
+ std::size_t quadrature_order) : _is_built(false)
{
+ // Set parameters
+ parameters = default_parameters();
+
// Add and build
for (auto mesh : meshes)
add(mesh);
@@ -50,8 +59,11 @@ MultiMesh::MultiMesh(std::vector<std::shared_ptr<const Mesh>> meshes,
}
//-----------------------------------------------------------------------------
MultiMesh::MultiMesh(std::shared_ptr<const Mesh> mesh_0,
- std::size_t quadrature_order)
+ std::size_t quadrature_order) : _is_built(false)
{
+ // Set parameters
+ parameters = default_parameters();
+
// Add and build
add(mesh_0);
build(quadrature_order);
@@ -59,8 +71,11 @@ MultiMesh::MultiMesh(std::shared_ptr<const Mesh> mesh_0,
//-----------------------------------------------------------------------------
MultiMesh::MultiMesh(std::shared_ptr<const Mesh> mesh_0,
std::shared_ptr<const Mesh> mesh_1,
- std::size_t quadrature_order)
+ std::size_t quadrature_order) : _is_built(false)
{
+ // Set parameters
+ parameters = default_parameters();
+
// Add and build
add(mesh_0);
add(mesh_1);
@@ -70,8 +85,11 @@ MultiMesh::MultiMesh(std::shared_ptr<const Mesh> mesh_0,
MultiMesh::MultiMesh(std::shared_ptr<const Mesh> mesh_0,
std::shared_ptr<const Mesh> mesh_1,
std::shared_ptr<const Mesh> mesh_2,
- std::size_t quadrature_order)
+ std::size_t quadrature_order) : _is_built(false)
{
+ // Set parameters
+ parameters = default_parameters();
+
// Add and build
add(mesh_0);
add(mesh_1);
@@ -102,11 +120,24 @@ MultiMesh::uncut_cells(std::size_t part) const
return _uncut_cells[part];
}
//-----------------------------------------------------------------------------
-const std::vector<unsigned int>&
+const std::vector<unsigned int>
MultiMesh::cut_cells(std::size_t part) const
{
dolfin_assert(part < num_parts());
- return _cut_cells[part];
+
+ // Extract keys from collision map
+ const std::map<unsigned int,std::vector<std::pair<std::size_t,unsigned int>>>&
+ cm = collision_map_cut_cells(part);
+
+ // Vector to store cut cell IDs
+ std::vector<unsigned int> _cut_cells;
+ _cut_cells.reserve(cm.size());
+
+ // Add cell ID if it has any collisions
+ for (const auto& it : cm)
+ if (it.second.size() > 0)
+ _cut_cells.push_back(it.first);
+ return _cut_cells;
}
//-----------------------------------------------------------------------------
const std::vector<unsigned int>&
@@ -118,42 +149,61 @@ MultiMesh::covered_cells(std::size_t part) const
//-----------------------------------------------------------------------------
const std::map<unsigned int,
std::vector<std::pair<std::size_t, unsigned int>>>&
-MultiMesh::collision_map_cut_cells(std::size_t part) const
+ MultiMesh::collision_map_cut_cells(std::size_t part) const
{
dolfin_assert(part < num_parts());
return _collision_maps_cut_cells[part];
}
//-----------------------------------------------------------------------------
-const std::map<unsigned int, quadrature_rule> &
-MultiMesh::quadrature_rule_cut_cells(std::size_t part) const
+const std::map<unsigned int, MultiMesh::quadrature_rule> &
+MultiMesh::quadrature_rules_cut_cells(std::size_t part) const
{
dolfin_assert(part < num_parts());
return _quadrature_rules_cut_cells[part];
}
//-----------------------------------------------------------------------------
-quadrature_rule
-MultiMesh::quadrature_rule_cut_cell(std::size_t part,
- unsigned int cell_index) const
+const MultiMesh::quadrature_rule
+MultiMesh::quadrature_rules_cut_cells(std::size_t part,
+ unsigned int cell_index) const
{
- auto q = quadrature_rule_cut_cells(part);
+ auto q = quadrature_rules_cut_cells(part);
+ dolfin_assert(cell_index < this->part(part)->num_cells());
return q[cell_index];
}
//-----------------------------------------------------------------------------
-const std::map<unsigned int, std::vector<quadrature_rule>>&
- MultiMesh::quadrature_rule_overlap(std::size_t part) const
+const std::map<unsigned int, std::vector<MultiMesh::quadrature_rule>>&
+ MultiMesh::quadrature_rules_overlap(std::size_t part) const
{
dolfin_assert(part < num_parts());
return _quadrature_rules_overlap[part];
}
//-----------------------------------------------------------------------------
-const std::map<unsigned int, std::vector<quadrature_rule>>&
- MultiMesh::quadrature_rule_interface(std::size_t part) const
+const std::vector<MultiMesh::quadrature_rule>
+MultiMesh::quadrature_rules_overlap(std::size_t part,
+ unsigned int cell_index) const
+{
+ auto q = quadrature_rules_overlap(part);
+ dolfin_assert(cell_index < this->part(part)->num_cells());
+ return q[cell_index];
+}
+//-----------------------------------------------------------------------------
+const std::map<unsigned int, std::vector<MultiMesh::quadrature_rule>>&
+ MultiMesh::quadrature_rules_interface(std::size_t part) const
{
dolfin_assert(part < num_parts());
return _quadrature_rules_interface[part];
}
//-----------------------------------------------------------------------------
+const std::vector<MultiMesh::quadrature_rule>
+MultiMesh::quadrature_rules_interface(std::size_t part,
+ unsigned int cell_index) const
+{
+ auto q = quadrature_rules_interface(part);
+ dolfin_assert(cell_index < this->part(part)->num_cells());
+ return q[cell_index];
+}
+//-----------------------------------------------------------------------------
const std::map<unsigned int, std::vector<std::vector<double>>>&
MultiMesh::facet_normals(std::size_t part) const
{
@@ -192,12 +242,12 @@ void MultiMesh::build(std::size_t quadrature_order)
// Build bounding box trees
_build_bounding_box_trees();
- // Build collision maps
+ // Build collision maps, i.e. classify cut, uncut and covered cells
_build_collision_maps();
- // FIXME: For collisions with meshes of same type we get three types
- // of quadrature rules: the cut cell qr, qr of the overlap part and
- // qr of the interface.
+ // For collisions with meshes of same type we get three types of
+ // quadrature rules: the cut cell qr, qr of the overlap part and qr
+ // of the interface.
// Build quadrature rules of the cut cells' overlap. Do this before
// we build the quadrature rules of the cut cells
@@ -206,6 +256,17 @@ void MultiMesh::build(std::size_t quadrature_order)
// Build quadrature rules of the cut cells
_build_quadrature_rules_cut_cells(quadrature_order);
+ // Build quadrature rules and normals of the interface
+ _build_quadrature_rules_interface(quadrature_order);
+
+ // Make sure that cut cells are actually cut
+ // TODO: Check if this needed
+ // TODO: Maybe also keep track of interface cells
+ // _impose_cut_cell_consistency();
+
+ // Mark space as built
+ _is_built = true;
+
end();
}
//-----------------------------------------------------------------------------
@@ -215,7 +276,6 @@ void MultiMesh::clear()
_trees.clear();
_boundary_trees.clear();
_uncut_cells.clear();
- _cut_cells.clear();
_covered_cells.clear();
_collision_maps_cut_cells.clear();
_collision_maps_cut_cells_boundary.clear();
@@ -224,6 +284,156 @@ void MultiMesh::clear()
_quadrature_rules_interface.clear();
}
//-----------------------------------------------------------------------------
+double MultiMesh::compute_area() const
+{
+ // Total area
+ double area = 0.0;
+
+ // Compute contribution from all parts
+ for (std::size_t p = 0; p < num_parts(); p++)
+ {
+ // Get the quadrature rules
+ const auto& quadrature_rules = quadrature_rules_interface(p);
+
+ // Get the collision map
+ const auto& cmap = collision_map_cut_cells(p);
+
+ for (auto it = cmap.begin(); it != cmap.end(); ++it)
+ {
+ // Get the cells that intersect the cut cell. These are the
+ // cutting cells
+ const unsigned int cut_cell_index = it->first;
+ const auto& cutting_cells = it->second;
+
+ // Iterate over cutting cells
+ for (auto jt = cutting_cells.begin(); jt != cutting_cells.end(); jt++)
+ {
+ // Get the quadrature rule for the interface part defined by
+ // the intersection of the cut and cutting cell
+ const std::size_t k = jt - cutting_cells.begin();
+ dolfin_assert(k < quadrature_rules.at(cut_cell_index).size());
+ const auto& qr = quadrature_rules.at(cut_cell_index)[k];
+
+ // Sum over all qr weights
+ for (std::size_t i = 0; i < qr.second.size(); ++i)
+ {
+ area += qr.second[i];
+ }
+ }
+ }
+ }
+
+ return area;
+}
+
+//-----------------------------------------------------------------------------
+double MultiMesh::compute_volume() const
+{
+ // Total volume
+ double volume = 0.0;
+
+ // Compute contribution from all parts
+ for (std::size_t p = 0; p < num_parts(); p++)
+ {
+ // Sum volume of uncut cells (from cell.volume)
+ {
+ const auto& cells = uncut_cells(p);
+ for (auto it = cells.begin(); it != cells.end(); ++it)
+ {
+ const Cell cell(*part(p), *it);
+ volume += cell.volume();
+ }
+ }
+
+ // Sum volume of cut cells (from quadrature rules)
+ {
+ const auto& cells = cut_cells(p);
+ for (auto it = cells.begin(); it != cells.end(); ++it)
+ {
+ const auto& qr = quadrature_rules_cut_cells(p, *it);
+ for (std::size_t i = 0; i < qr.second.size(); ++i)
+ volume += qr.second[i];
+ }
+ }
+ }
+
+ return volume;
+}
+//-----------------------------------------------------------------------------
+std::string MultiMesh::plot_matplotlib(double delta_z,
+ const std::string& filename) const
+{
+ if (num_parts() == 0)
+ dolfin_error("MultiMesh.cpp",
+ "plotting multimesh with matplotlib",
+ "Multimesh is empty. Call MultiMesh.add(mesh) before plotting");
+
+ if (part(0)->geometry().dim() != 2)
+ dolfin_error("MultiMesh.cpp",
+ "plotting multimesh with matplotlib",
+ "Plotting is only implemented in 2D");
+
+ const bool do_3d = delta_z != 0.;
+ std::stringstream ss;
+
+ ss << "def plot_multimesh() :\n";
+ ss << " from mpl_toolkits.mplot3d import Axes3D\n";
+ ss << " from matplotlib import cm\n";
+ ss << " import matplotlib.pyplot as plt\n";
+ ss << " import numpy as np\n";
+ ss << " fig = plt.figure()\n";
+ if (do_3d)
+ ss << " ax = fig.gca(projection='3d')\n";
+ else
+ ss << " ax = fig.gca()\n";
+ ss << " alpha = " << (do_3d ? 0.4 : 1.0) << "\n";
+
+ for (std::size_t p = 0; p < num_parts(); p++)
+ {
+ std::shared_ptr<const Mesh> current = part(p);
+ std::stringstream x, y;
+ x << " x = np.array((";
+ y << " y = np.array((";
+ for (std::size_t i = 0; i < current->num_vertices(); i++)
+ {
+ x << current->coordinates()[i*2] << ", ";
+ y << current->coordinates()[i*2 + 1] << ",";
+ }
+ x << "))\n";
+ y << "))\n";
+ ss << x.str() << y.str();
+
+ ss << " facets = np.array((";
+ for (CellIterator cit(*current); !cit.end(); ++cit)
+ {
+ const unsigned int* vertices = cit->entities(0);
+ ss << "(" << vertices[0] << ", " << vertices[1] << ", " << vertices[2] << "), ";
+ }
+
+ ss << "), dtype=int)\n";
+ if (do_3d)
+ {
+ ss << " z = np.zeros(x.shape) + " << (p*delta_z) << "\n";
+ ss << " ax.plot_trisurf(x, y, z, triangles=facets, alpha=alpha)\n";
+ }
+ else
+ {
+ ss << " z = " << p<< "*np.ones(int(facets.size / 3))\n"
+ << " ax.tripcolor(x, y, facets, facecolors = z, edgecolors = 'k', alpha = alpha, vmin = 0, vmax = " << num_parts()-1 << ")\n";
+ }
+ }
+
+ if (!do_3d)
+ {
+ ss << " ax.axis('tight')\n"
+ << " ax.axis('square')\n";
+ if (!filename.empty())
+ ss << " plt.savefig('" << filename << "')\n";
+ }
+ ss << " plt.show()\n";
+ return ss.str();
+}
+//-----------------------------------------------------------------------------
void MultiMesh::_build_boundary_meshes()
{
begin(PROGRESS, "Building boundary meshes.");
@@ -262,6 +472,7 @@ void MultiMesh::_build_bounding_box_trees()
std::shared_ptr<BoundingBoxTree> boundary_tree(new BoundingBoxTree());
// FIXME: what if the boundary mesh is empty?
+ dolfin_assert(_boundary_meshes[i]->num_vertices()>0);
if (_boundary_meshes[i]->num_vertices()>0)
boundary_tree->build(*_boundary_meshes[i]);
_boundary_trees.push_back(boundary_tree);
@@ -276,7 +487,6 @@ void MultiMesh::_build_collision_maps()
// Clear collision maps
_uncut_cells.clear();
- _cut_cells.clear();
_covered_cells.clear();
_collision_maps_cut_cells.clear();
_collision_maps_cut_cells_boundary.clear();
@@ -293,11 +503,12 @@ void MultiMesh::_build_collision_maps()
// Create vector of markers for cells in part `i` (0, 1, or 2)
std::vector<char> markers(_meshes[i]->num_cells(), 0);
- // Create local array for marking boundary collisions for cells in
- // part `i`. Note that in contrast to the markers above which are
- // global to part `i`, these markers are local to the collision
- // between part `i` and part `j`.
+ // Create local arrays for marking domain and boundary collisions
+ // for cells in part `i`. Note that in contrast to the markers
+ // above which are global to part `i`, these markers are local to
+ // the collision between part `i` and part `j`.
std::vector<bool> collides_with_boundary(_meshes[i]->num_cells());
+ std::vector<bool> collides_with_domain(_meshes[i]->num_cells());
// Create empty collision map for cut cells in part `i`
std::map<unsigned int, std::vector<std::pair<std::size_t, unsigned int>>>
@@ -308,30 +519,37 @@ void MultiMesh::_build_collision_maps()
{
log(PROGRESS, "Computing collisions for mesh %d overlapped by mesh %d.", i, j);
- // Reset boundary collision markers
- std::fill(collides_with_boundary.begin(), collides_with_boundary.end(), false);
-
// Compute domain-boundary collisions
const auto& boundary_collisions = _trees[i]->compute_collisions(*_boundary_trees[j]);
- // Iterate over boundary collisions
- for (auto it = boundary_collisions.first.begin();
- it != boundary_collisions.first.end(); ++it)
- {
- // Mark that cell collides with boundary
- collides_with_boundary[*it] = true;
-
- // Mark as cut cell if not previously covered
- if (markers[*it] != 2)
- {
- // Mark as cut cell
- markers[*it] = 1;
+ // Reset boundary collision markers
+ std::fill(collides_with_boundary.begin(), collides_with_boundary.end(), false);
- // Add empty list of collisions into map if it does not exist
- if (collision_map_cut_cells.find(*it) == collision_map_cut_cells.end())
- {
+ // Iterate over boundary collisions.
+ for (std::size_t k = 0; k < boundary_collisions.first.size(); ++k)
+ {
+ // Get the colliding cell
+ const std::size_t cell_i = boundary_collisions.first[k];
+
+ // Do a careful check if not already marked as colliding
+ if (!collides_with_boundary[cell_i])
+ {
+ const Cell cell(*_meshes[i], cell_i);
+ const Cell boundary_cell(*_boundary_meshes[j], boundary_collisions.second[k]);
+ collides_with_boundary[cell_i] = cell.collides(boundary_cell);
+ }
+
+ // Mark as cut cell if not previously covered
+ if (collides_with_boundary[cell_i] and markers[cell_i] != 2)
+ {
+ // Mark as cut cell
+ markers[cell_i] = 1;
+
+ // Add empty list of collisions into map if it does not exist
+ if (collision_map_cut_cells.find(cell_i) == collision_map_cut_cells.end())
+ {
std::vector<std::pair<std::size_t, unsigned int>> collisions;
- collision_map_cut_cells[*it] = collisions;
+ collision_map_cut_cells[cell_i] = collisions;
}
}
}
@@ -339,34 +557,55 @@ void MultiMesh::_build_collision_maps()
// Compute domain-domain collisions
const auto& domain_collisions = _trees[i]->compute_collisions(*_trees[j]);
+ // Reset domain collision markers
+ std::fill(collides_with_domain.begin(), collides_with_domain.end(), false);
+
// Iterate over domain collisions
dolfin_assert(domain_collisions.first.size() == domain_collisions.second.size());
for (std::size_t k = 0; k < domain_collisions.first.size(); k++)
{
// Get the two colliding cells
- auto cell_i = domain_collisions.first[k];
- auto cell_j = domain_collisions.second[k];
+ const std::size_t cell_i = domain_collisions.first[k];
+ const std::size_t cell_j = domain_collisions.second[k];
// Store collision in collision map if we have a cut cell
if (markers[cell_i] == 1)
{
- auto it = collision_map_cut_cells.find(cell_i);
- dolfin_assert(it != collision_map_cut_cells.end());
- it->second.push_back(std::make_pair(j, cell_j));
+ const Cell cell(*_meshes[i], cell_i);
+ const Cell other_cell(*_meshes[j], cell_j);
+ if (cell.collides(other_cell))
+ {
+ collides_with_domain[cell_i] = true;
+ auto it = collision_map_cut_cells.find(cell_i);
+ dolfin_assert(it != collision_map_cut_cells.end());
+ it->second.emplace_back(j, cell_j);
+ }
}
- // Mark cell as covered if it does not collide with boundary
+ // Possibility to cell as covered if it does not collide with boundary
if (!collides_with_boundary[cell_i])
{
- // Remove from collision map if previously marked as as cut cell
- if (markers[cell_i] == 1)
- {
- dolfin_assert(collision_map_cut_cells.find(cell_i) != collision_map_cut_cells.end());
- collision_map_cut_cells.erase(cell_i);
- }
+ // Detailed check if it is not marked as colliding with domain
+ if (!collides_with_domain[cell_i])
+ {
+ const Cell cell(*_meshes[i], cell_i);
+ const Cell other_cell(*_meshes[j], cell_j);
+ collides_with_domain[cell_i] = cell.collides(other_cell);
+ }
+
+ if (collides_with_domain[cell_i])
+ {
+ // Remove from collision map if previously marked as as cut cell
+ if (markers[cell_i] == 1)
+ {
+ dolfin_assert(collision_map_cut_cells.find(cell_i) != collision_map_cut_cells.end());
+ collision_map_cut_cells.erase(cell_i);
+ }
+
+ // Mark as covered cell (may already be marked)
+ markers[cell_i] = 2;
+ }
- // Mark as covered cell (may already be marked)
- markers[cell_i] = 2;
}
}
}
@@ -392,7 +631,7 @@ void MultiMesh::_build_collision_maps()
// Store data for this mesh
_uncut_cells.push_back(uncut_cells);
- _cut_cells.push_back(cut_cells);
+ //_cut_cells.push_back(cut_cells);
_covered_cells.push_back(covered_cells);
_collision_maps_cut_cells.push_back(collision_map_cut_cells);
@@ -410,297 +649,94 @@ void MultiMesh::_build_quadrature_rules_overlap(std::size_t quadrature_order)
// Clear quadrature rules
_quadrature_rules_overlap.clear();
- _quadrature_rules_interface.clear();
// Resize quadrature rules
_quadrature_rules_overlap.resize(num_parts());
- _quadrature_rules_interface.resize(num_parts());
-
- // Clear and resize facet normals
- _facet_normals.clear();
- _facet_normals.resize(num_parts());
-
- // FIXME: test prebuild map from boundary facets to full mesh cells
- // for all meshes: Loop over all boundary mesh facets to find the
- // full mesh cell which contains the facet. This is done in two
- // steps: Since the facet is on the boundary mesh, we first map this
- // facet to a facet in the full mesh using the
- // boundary_cell_map. Then we use the full_facet_cell_map to find
- // the corresponding cell in the full mesh. This cell is to match
- // the cutting_cell_no.
-
- // Build map from boundary facets to full mesh
- std::vector<std::vector<std::vector<std::pair<std::size_t, std::size_t>>>>
- full_to_bdry(num_parts());
- for (std::size_t part = 0; part < num_parts(); ++part)
- {
- full_to_bdry[part].resize(_meshes[part]->num_cells());
-
- // Get map from boundary mesh to facets of full mesh
- const std::size_t tdim_boundary
- = _boundary_meshes[part]->topology().dim();
- const auto& boundary_cell_map
- = _boundary_meshes[part]->entity_map(tdim_boundary);
-
- // Generate facet to cell connectivity for full mesh
- const std::size_t tdim = _meshes[part]->topology().dim();
- _meshes[part]->init(tdim_boundary, tdim);
- const MeshConnectivity& full_facet_cell_map
- = _meshes[part]->topology()(tdim_boundary, tdim);
-
- for (std::size_t boundary_facet = 0;
- boundary_facet < boundary_cell_map.size(); ++boundary_facet)
- {
- // Find the facet in the full mesh
- const std::size_t full_mesh_facet = boundary_cell_map[boundary_facet];
-
- // Find the cells in the full mesh (for interior facets we
- // can have 2 facets, but here we should only have 1)
- dolfin_assert(full_facet_cell_map.size(full_mesh_facet) == 1);
- const auto& full_cells = full_facet_cell_map(full_mesh_facet);
- full_to_bdry[part][full_cells[0]].push_back(std::make_pair(boundary_facet,
- full_mesh_facet));
- }
- }
// Iterate over all parts
for (std::size_t cut_part = 0; cut_part < num_parts(); cut_part++)
{
+ // Construct quadrature rules on reference simplex
+ const std::size_t tdim = _meshes[cut_part]->topology().dim();
+ const std::size_t gdim = _meshes[cut_part]->geometry().dim();
+ const SimplexQuadrature sq(tdim, quadrature_order);
+
// Iterate over cut cells for current part
- const auto& cmap = collision_map_cut_cells(cut_part);
- for (auto it = cmap.begin(); it != cmap.end(); ++it)
+ for (const auto& c : collision_map_cut_cells(cut_part))
{
// Get cut cell
- const unsigned int cut_cell_index = it->first;
+ const unsigned int cut_cell_index = c.first;
const Cell cut_cell(*(_meshes[cut_part]), cut_cell_index);
- // Get dimensions
- const std::size_t tdim = cut_cell.mesh().topology().dim();
- const std::size_t gdim = cut_cell.mesh().geometry().dim();
+ // Data structure for the first intersections (this is the first
+ // stage in the inclusion exclusion principle). These are the
+ // polyhedra to be used in the exlusion inclusion.
+ std::vector<std::pair<std::size_t, Polyhedron>> initial_polyhedra;
- // Data structure for the volume triangulation of the cut_cell
- std::vector<double> volume_triangulation;
+ // Get the cutting cells
+ const std::vector<std::pair<std::size_t, unsigned int>>& cutting_cells = c.second;
// Data structure for the overlap quadrature rule
- std::vector<quadrature_rule> overlap_qr;
-
- // Data structure for the interface quadrature rule
- std::vector<quadrature_rule> interface_qr;
-
- // Data structure for the facet normals of the interface. The
- // numbering matches the numbering of interface_qr. This means
- // we have one normal for each quadrature point, since this is
- // how the data are grouped during assembly: for each pair of
- // colliding cells, we build a list of quadrature points and a
- // corresponding list of facet normals.
- std::vector<std::vector<double>> interface_n;
+ std::vector<quadrature_rule> overlap_qr(cutting_cells.size());
- // Data structure for the interface triangulation
- std::vector<double> interface_triangulation;
-
- // Data structure for normals to the interface. The numbering
- // should match the numbering of interface_triangulation.
- std::vector<Point> triangulation_normals;
-
- // Iterate over cutting cells
- const auto& cutting_cells = it->second;
- for (auto jt = cutting_cells.begin(); jt != cutting_cells.end(); jt++)
+ // Loop over all cutting cells to construct the polyhedra to be
+ // used in the inclusion-exclusion principle
+ for (const std::pair<std::size_t, unsigned int> cutting : cutting_cells)
{
- // Get cutting part and cutting cell
- const std::size_t cutting_part = jt->first;
- const std::size_t cutting_cell_index = jt->second;
+ // Get cutting part and cutting cell
+ const std::size_t cutting_part = cutting.first;
+ const std::size_t cutting_cell_index = cutting.second;
const Cell cutting_cell(*(_meshes[cutting_part]), cutting_cell_index);
- // Topology of this cut part
- const std::size_t tdim_boundary = _boundary_meshes[cutting_part]->topology().dim();
-
- // Must have the same topology at the moment (FIXME)
- dolfin_assert(cutting_cell.mesh().topology().dim() == tdim);
+ // Only allow same type of cell for now
+ dolfin_assert(cutting_cell.mesh().topology().dim() == tdim);
+ dolfin_assert(cutting_cell.mesh().geometry().dim() == gdim);
- // Data structure for local interface triangulation
- std::vector<double> local_interface_triangulation;
+ // Compute the intersection (a polyhedron)
+ // const Polyhedron polyhedron
+ // = IntersectionTriangulation::triangulate(cut_cell, cutting_cell);
+ const Polyhedron polyhedron =
+ ConvexTriangulation::triangulate(IntersectionConstruction::intersection(cut_cell, cutting_cell), gdim, tdim);
- // Data structure for the local interface normals. The
- // numbering should match the numbering of
- // local_interface_triangulation.
- std::vector<Point> local_triangulation_normals;
+ // dolfin_assert(!ConvexTriangulation::selfintersects(polyhedron));
- // Data structure for the overlap part quadrature rule
- quadrature_rule overlap_part_qr;
+ // FIXME: Flip triangles in polyhedron to maximize minimum angle here?
+ // FIXME: only include large polyhedra
- // Data structure for the interface part quadrature rule
- quadrature_rule interface_part_qr;
+ // Store key and polyhedron but only use polyhedron of same tdim
+ Polyhedron polyhedron_same_tdim;
+ for (const Simplex& s: polyhedron)
+ if (s.size() == tdim + 1 and
+ !GeometryPredicates::is_degenerate(s, gdim))
+ polyhedron_same_tdim.push_back(s);
- // Data structure for the interface part facet normals. The
- // numbering matches the numbering of interface_part_qr.
- std::vector<double> interface_part_n;
-
- // Iterate over boundary cells
- for (auto boundary_cell_index : full_to_bdry[cutting_part][cutting_cell_index])
- {
- // Get the boundary facet as a cell in the boundary mesh
- const Cell boundary_cell(*_boundary_meshes[cutting_part],
- boundary_cell_index.first);
-
- // Get the boundary facet as a facet in the full mesh
- const Facet boundary_facet(*_meshes[cutting_part],
- boundary_cell_index.second);
-
- // Triangulate intersection of cut cell and boundary cell
- const auto triangulation_cut_boundary
- = cut_cell.triangulate_intersection(boundary_cell);
-
- // The normals to triangulation_cut_boundary
- std::vector<Point> normals_cut_boundary;
-
- // Add quadrature rule and normals for triangulation
- if (triangulation_cut_boundary.size())
- {
- dolfin_assert(interface_part_n.size() == interface_part_qr.first.size());
-
- const auto num_qr_points
- = _add_quadrature_rule(interface_part_qr,
- triangulation_cut_boundary,
- tdim_boundary, gdim,
- quadrature_order, 1);
-
- const std::size_t local_facet_index = cutting_cell.index(boundary_facet);
- const Point n = -cutting_cell.normal(local_facet_index);
- for (std::size_t i = 0; i < num_qr_points.size(); ++i)
- {
- _add_normal(interface_part_n,
- n,
- num_qr_points[i],
- gdim);
- normals_cut_boundary.push_back(n);
- }
-
- dolfin_assert(interface_part_n.size() == interface_part_qr.first.size());
- }
+ // Note that this can be empty
+ initial_polyhedra.emplace_back(initial_polyhedra.size(),
+ polyhedron_same_tdim);
+ }
- // Triangulate intersection of boundary cell and previous volume triangulation
- const auto triangulation_boundary_prev_volume
- = IntersectionTriangulation::triangulate_intersection(boundary_cell,
- volume_triangulation,
- tdim);
-
- // Add quadrature rule and normals for triangulation
- if (triangulation_boundary_prev_volume.size())
- {
- dolfin_assert(interface_part_n.size() == interface_part_qr.first.size());
-
- const auto num_qr_points
- = _add_quadrature_rule(interface_part_qr,
- triangulation_boundary_prev_volume,
- tdim_boundary, gdim,
- quadrature_order, -1);
-
- const std::size_t local_facet_index = cutting_cell.index(boundary_facet);
- const Point n = -cutting_cell.normal(local_facet_index);
- for (std::size_t i = 0; i < num_qr_points.size(); ++i)
- _add_normal(interface_part_n,
- n,
- num_qr_points[i],
- gdim);
-
- dolfin_assert(interface_part_n.size() == interface_part_qr.first.size());
- }
+ if (cutting_cells.size() > 0)
+ _inclusion_exclusion_overlap(overlap_qr, sq, initial_polyhedra,
+ tdim, gdim, quadrature_order);
- // Update triangulation
- local_interface_triangulation.insert(local_interface_triangulation.end(),
- triangulation_cut_boundary.begin(),
- triangulation_cut_boundary.end());
+ // Remove any near-trival quadrature rules
+ // TODO: The tolerance here appears to work ok in 2D with few meshes
+ // TODO: It might not be accurate in 3D or a large number of meshes
- // Update interface facet normals
- local_triangulation_normals.insert(local_triangulation_normals.end(),
- normals_cut_boundary.begin(),
- normals_cut_boundary.end());
- }
+ //const double tolerance = DOLFIN_EPS * cut_cell.volume();
+ //for (std::size_t i = 0; i < overlap_qr.size(); i++)
+ // remove_quadrature_rule(overlap_qr[i], tolerance);
- // Triangulate the intersection of the previous interface
- // triangulation and the cutting cell (to remove)
- std::vector<double> triangulation_prev_cutting;
- std::vector<Point> normals_prev_cutting;
- IntersectionTriangulation::triangulate_intersection(cutting_cell,
- interface_triangulation,
- triangulation_normals,
- triangulation_prev_cutting,
- normals_prev_cutting,
- tdim_boundary);
-
- // Add quadrature rule for triangulation
- if (triangulation_prev_cutting.size())
+ if (parameters["compress_volume_quadrature"])
+ {
+ for (std::size_t i = 0; i < overlap_qr.size(); ++i)
{
- dolfin_assert(interface_part_n.size() == interface_part_qr.first.size());
-
- const auto num_qr_points
- = _add_quadrature_rule(interface_part_qr,
- triangulation_prev_cutting,
- tdim_boundary, gdim,
- quadrature_order, -1);
-
- for (std::size_t i = 0; i < num_qr_points.size(); ++i)
- _add_normal(interface_part_n,
- normals_prev_cutting[i],
- num_qr_points[i],
- gdim);
-
- dolfin_assert(interface_part_n.size() == interface_part_qr.first.size());
+ SimplexQuadrature::compress(overlap_qr[i], gdim, quadrature_order);
}
-
- // Update triangulation
- interface_triangulation.insert(interface_triangulation.end(),
- local_interface_triangulation.begin(),
- local_interface_triangulation.end());
-
- // Update normals
- triangulation_normals.insert(triangulation_normals.end(),
- local_triangulation_normals.begin(),
- local_triangulation_normals.end());
-
- // Do the volume segmentation
-
- // Compute volume triangulation of intersection of cut and cutting cells
- const auto triangulation_cut_cutting
- = cut_cell.triangulate_intersection(cutting_cell);
-
- // Compute triangulation of intersection of cutting cell and
- // the (previous) volume triangulation
- const auto triangulation_cutting_prev
- = IntersectionTriangulation::triangulate_intersection(cutting_cell,
- volume_triangulation,
- tdim);
-
- // Add these new triangulations
- volume_triangulation.insert(volume_triangulation.end(),
- triangulation_cut_cutting.begin(),
- triangulation_cut_cutting.end());
-
- // Add quadrature rule with weights corresponding to the two
- // triangulations
- _add_quadrature_rule(overlap_part_qr,
- triangulation_cut_cutting,
- tdim, gdim, quadrature_order, 1);
- _add_quadrature_rule(overlap_part_qr,
- triangulation_cutting_prev,
- tdim, gdim, quadrature_order, -1);
-
- // Add quadrature rule for overlap part
- overlap_qr.push_back(overlap_part_qr);
-
- // Add quadrature rule for interface part
- interface_qr.push_back(interface_part_qr);
-
- // Add facet normal for interface part
- interface_n.push_back(interface_part_n);
}
// Store quadrature rules for cut cell
_quadrature_rules_overlap[cut_part][cut_cell_index] = overlap_qr;
- _quadrature_rules_interface[cut_part][cut_cell_index] = interface_qr;
-
- // Store facet normals for cut cell
- _facet_normals[cut_part][cut_cell_index] = interface_n;
}
}
@@ -718,6 +754,11 @@ void MultiMesh::_build_quadrature_rules_cut_cells(std::size_t quadrature_order)
// Iterate over all parts
for (std::size_t cut_part = 0; cut_part < num_parts(); cut_part++)
{
+ // Construct quadrature rules on reference simplex
+ const std::size_t tdim = _meshes[cut_part]->topology().dim();
+ const std::size_t gdim = _meshes[cut_part]->geometry().dim();
+ const SimplexQuadrature sq(tdim, quadrature_order);
+
// Iterate over cut cells for current part
const auto& cmap = collision_map_cut_cells(cut_part);
for (auto it = cmap.begin(); it != cmap.end(); ++it)
@@ -726,12 +767,8 @@ void MultiMesh::_build_quadrature_rules_cut_cells(std::size_t quadrature_order)
const unsigned int cut_cell_index = it->first;
const Cell cut_cell(*(_meshes[cut_part]), cut_cell_index);
- // Get dimension
- const std::size_t gdim = cut_cell.mesh().geometry().dim();
-
// Compute quadrature rule for the cell itself.
- auto qr = SimplexQuadrature::compute_quadrature_rule(cut_cell,
- quadrature_order);
+ auto qr = sq.compute_quadrature_rule(cut_cell, quadrature_order);
// Get the quadrature rule for the overlapping part
const auto& qr_overlap = _quadrature_rules_overlap[cut_part][cut_cell_index];
@@ -741,6 +778,12 @@ void MultiMesh::_build_quadrature_rules_cut_cells(std::size_t quadrature_order)
for (std::size_t k = 0; k < qr_overlap.size(); k++)
_add_quadrature_rule(qr, qr_overlap[k], gdim, -1);
+ if (parameters["compress_volume_quadrature"])
+ {
+ // Compress
+ SimplexQuadrature::compress(qr, gdim, quadrature_order);
+ }
+
// Store quadrature rule for cut cell
_quadrature_rules_cut_cells[cut_part][cut_cell_index] = qr;
}
@@ -748,36 +791,299 @@ void MultiMesh::_build_quadrature_rules_cut_cells(std::size_t quadrature_order)
end();
}
-//-----------------------------------------------------------------------------
-std::vector<std::size_t>
-MultiMesh::_add_quadrature_rule(quadrature_rule& qr,
- const std::vector<double>& triangulation,
- std::size_t tdim,
- std::size_t gdim,
- std::size_t quadrature_order,
- double factor) const
+//------------------------------------------------------------------------------
+void MultiMesh::_build_quadrature_rules_interface(std::size_t quadrature_order)
{
- // Iterate over simplices in triangulation
- const std::size_t offset = (tdim + 1)*gdim; // coordinates per simplex
- const std::size_t num_simplices = triangulation.size() / offset;
- std::vector<std::size_t> num_points(num_simplices);
+ begin(PROGRESS, "Building quadrature rules of interface.");
+
+ // This is similar to _build_quadrature_rules_overlap, except
+ // - For the edge E_ij, i<j, we only intersect with triangles T_k
+ // where k>i and k!=j
+ // - We note that the sign change is opposite of the other inc exc:
+ // |E_ij \ U_k T_k| = |E_ij| - |E_ij \cap U_k T_k|
+ // = |E_ij| - |U_k E_ij \cap T_k|
+
+ // Clear and resize quadrature rules and normals
+ _quadrature_rules_interface.clear();
+ _quadrature_rules_interface.resize(num_parts());
+ _facet_normals.clear();
+ _facet_normals.resize(num_parts());
+
+ // First we prebuild a map from the boundary facets to full mesh
+ // cells for all meshes: Loop over all boundary mesh facets to find
+ // the full mesh cell which contains the facet. This is done in two
+ // steps: Since the facet is on the boundary mesh, we first map this
+ // facet to a facet in the full mesh using the
+ // boundary_cell_map. Then we use the full_facet_cell_map to find
+ // the corresponding cell in the full mesh. This cell is to match
+ // the cutting_cell_no.
+
+ // Build map from boundary facets to full mesh
+ std::vector<std::vector<std::vector<std::pair<std::size_t, std::size_t>>>>
+ full_to_bdry(num_parts());
+ for (std::size_t part = 0; part < num_parts(); ++part)
+ full_to_bdry[part] = _boundary_facets_to_full_mesh(part);
- for (std::size_t k = 0; k < num_simplices; k++)
+ // Iterate over all parts
+ for (std::size_t cut_part = 0; cut_part < num_parts(); cut_part++)
{
- // Get coordinates for current simplex in triangulation
- const double* x = &triangulation[0] + k*offset;
+ // Topological dimension of bulk
+ const std::size_t tdim_bulk = _meshes[cut_part]->topology().dim();
- // Compute quadrature rule for simplex
- const auto dqr = SimplexQuadrature::compute_quadrature_rule(x,
- tdim,
- gdim,
- quadrature_order);
+ // Construct quadrature rules on reference simplex on interface
+ const std::size_t tdim_interface = tdim_bulk - 1;
+ const std::size_t gdim = _meshes[cut_part]->geometry().dim();
+ const SimplexQuadrature sq(tdim_interface, quadrature_order);
- // Add quadrature rule
- num_points[k] = _add_quadrature_rule(qr, dqr, gdim, factor);
+ // Iterate over cut cells for current part
+ const std::map<unsigned int,
+ std::vector<std::pair<std::size_t,
+ unsigned int>>>&
+ cmap = collision_map_cut_cells(cut_part);
+ for (const auto cut_i: cmap)
+ {
+ // Get cut cell
+ const std::size_t cut_cell_index_i = cut_i.first;
+ const Cell cut_cell_i(*(_meshes[cut_part]), cut_cell_index_i);
+
+ // Get the cutting cells
+ const auto& cutting_cells_j = cut_i.second;
+
+ // Data structures for the interface quadrature rule and the normals
+ const std::size_t num_cutting_cells
+ = std::distance(cutting_cells_j.begin(), cutting_cells_j.end());
+ std::vector<quadrature_rule> interface_qr(num_cutting_cells);
+ std::vector<std::vector<double>> interface_normals(num_cutting_cells);
+
+ // Loop over all cutting cells to construct the polyhedra to be
+ // used in the inclusion-exclusion principle
+ for (std::vector<std::pair<std::size_t, unsigned int>>::const_iterator
+ cutting_j = cutting_cells_j.begin();
+ cutting_j != cutting_cells_j.end(); ++cutting_j)
+ {
+ // Get cutting part and cutting cell
+ const std::size_t cutting_part_j = cutting_j->first;
+ const std::size_t cutting_cell_index_j = cutting_j->second;
+ const Cell cutting_cell_j(*(_meshes[cutting_part_j]), cutting_cell_index_j);
+ const std::size_t local_cutting_cell_j_index = cutting_j - cutting_cells_j.begin();
+ dolfin_assert(cutting_part_j > cut_part);
+
+ // Find and store the cutting cells Tk. These are fed into the
+ // inc exc together with the edge Eij.
+ std::vector<std::pair<std::size_t, Polyhedron> > initial_polygons;
+
+ // Find and save all cutting cells with part number > i
+ // (this is always true), and part number != j.
+ for (const std::pair<size_t, unsigned int>& cutting_k: cut_i.second)
+ {
+ const std::size_t cutting_part_k = cutting_k.first;
+ if (cutting_part_k != cutting_part_j)
+ {
+ const std::size_t cutting_cell_index_k = cutting_k.second;
+ const Cell cutting_cell_k(*(_meshes[cutting_part_k]),
+ cutting_cell_index_k);
+
+ // Store key and the cutting cell as a polygon (this
+ // is really a Simplex, but store as polyhedron to
+ // minimize interface change to inc exc).
+ const MeshGeometry& geometry = _meshes[cutting_part_k]->geometry();
+ const unsigned int* vertices = cutting_cell_k.entities(0);
+ Simplex cutting_cell_k_simplex(tdim_bulk + 1);
+ for (std::size_t i = 0; i < cutting_cell_k_simplex.size(); ++i)
+ cutting_cell_k_simplex[i] = geometry.point(vertices[i]);
+ const Polyhedron cutting_cell_k_polyhedron(1, cutting_cell_k_simplex);
+
+ initial_polygons.emplace_back(initial_polygons.size(),
+ cutting_cell_k_polyhedron);
+ }
+ }
+
+ // Iterate over boundary cells of this cutting cell (for
+ // triangles we have one or two sides that cut). Here we can
+ // optionally use a full (polygon) E_ij used in the E_ij \cap
+ // T_k, or we can only take a part (a simplex) of the Eij.
+
+ // Loop over all Eij parts (i.e. boundary parts of T_j)
+ for (const auto boundary_cell_index_j: full_to_bdry[cutting_part_j][cutting_cell_index_j])
+ {
+ // Get the boundary facet as a cell in the boundary mesh
+ // (remember that this is of one less topological dimension)
+ const Cell boundary_cell_j(*_boundary_meshes[cutting_part_j],
+ boundary_cell_index_j.first);
+ dolfin_assert(boundary_cell_j.mesh().topology().dim() == tdim_interface);
+
+ // Get the normal by constructing a Facet using the full_to_bdry data
+ const Facet boundary_facet_j(*_meshes[cutting_part_j],
+ boundary_cell_index_j.second);
+ const std::size_t local_facet_index = cutting_cell_j.index(boundary_facet_j);
+ const Point facet_normal = cutting_cell_j.normal(local_facet_index);
+
+ // Triangulate intersection of cut cell and boundary cell
+ const std::vector<Point> Eij_part_points
+ = IntersectionConstruction::intersection(cut_cell_i, boundary_cell_j);
+
+ // Check that the triangulation is not part of the cut cell boundary
+ // FIXME: How can we avoid is_degenerate warnings in
+ // _is_overlapped_interface by checking the input?
+ if (Eij_part_points.size() < tdim_interface + 1 or
+ _is_overlapped_interface(Eij_part_points, cut_cell_i, facet_normal))
+ continue;
+
+ const Polyhedron Eij_part =
+ ConvexTriangulation::triangulate(Eij_part_points, gdim, tdim_interface);
+
+ // The intersection should be either empty or one simplex
+ dolfin_assert(Eij_part.size() <= 1);
+
+ if (Eij_part.size())
+ {
+ // Do not include if the point (or line) is degenerate,
+ // neither here nor below
+ if (Eij_part[0].size() == tdim_interface + 1 and
+ !GeometryPredicates::is_degenerate(Eij_part[0], gdim))
+ {
+ const Simplex& Eij = Eij_part[0];
+
+ // Store the |Eij| and normals
+ const std::size_t num_pts
+ = _add_quadrature_rule(interface_qr[local_cutting_cell_j_index],
+ sq, Eij, gdim, quadrature_order, 1.);
+ _add_normal(interface_normals[local_cutting_cell_j_index],
+ facet_normal, num_pts, gdim);
+
+ // No need to run inc exc if there are no cutting cells
+ if (initial_polygons.size())
+ {
+ // Call inclusion exclusion
+ _inclusion_exclusion_interface
+ (interface_qr[local_cutting_cell_j_index],
+ interface_normals[local_cutting_cell_j_index],
+ sq, Eij, facet_normal, initial_polygons,
+ tdim_interface, gdim, quadrature_order);
+ }
+
+ // // Remove any near-trival quadrature rules
+ // // TODO: Investigate the tolerance
+ // double cut_size;
+ // if (Eij.size() == 2)
+ // cut_size = (Eij[1] - Eij[0]).norm();
+ // else if (Eij.size() == 3)
+ // cut_size = (Eij[1] - Eij[0]).cross(Eij[2] - Eij[0]).norm() / 2;
+ //const double tolerance = DOLFIN_EPS * cut_size;
+ //remove_quadrature_rule(interface_qr[local_cutting_cell_j_index], tolerance);
+
+ // TODO: Investigate if we should compress here or below
+ if (parameters["compress_interface_quadrature"])
+ {
+ const std::vector<std::size_t> indices
+ = SimplexQuadrature::compress(interface_qr[local_cutting_cell_j_index], gdim, quadrature_order);
+ if (indices.size())
+ {
+ // Reorder the normals
+ std::vector<double> normals(gdim*indices.size());
+ for (std::size_t j = 0; j < indices.size(); ++j)
+ for (std::size_t d = 0; d < gdim; ++d)
+ normals[gdim*j + d] = interface_normals[local_cutting_cell_j_index][gdim*indices[j] + d];
+ interface_normals[local_cutting_cell_j_index] = normals;
+ }
+ }
+ }
+ }
+ } // end loop over boundary_cell_j
+ } // end loop over cutting_j
+
+ // // TODO: Investigate if we should compress here or below
+ // if (parameters["compress_interface_quadrature"])
+ // {
+ // for (std::size_t i = 0; i < interface_qr.size(); ++i)
+ // {
+ // const std::size_t sz = interface_qr[i].second.size();
+ // const std::vector<std::size_t> indices
+ // = SimplexQuadrature::compress(interface_qr[i], gdim, quadrature_order);
+
+ // if (indices.size())
+ // {
+ // // Reorder the normals
+ // std::vector<double> normals(gdim*indices.size());
+ // for (std::size_t j = 0; j < indices.size(); ++j)
+ // for (std::size_t d = 0; d < gdim; ++d)
+ // normals[gdim*j + d] = interface_normals[i][gdim*indices[j] + d];
+ // interface_normals[i] = normals;
+ // // std::cout<<__FUNCTION__<<" compress " << sz<<' '<<indices.size() << std::endl;
+ // }
+ // }
+ // }
+
+ _quadrature_rules_interface[cut_part][cut_cell_index_i] = interface_qr;
+ _facet_normals[cut_part][cut_cell_index_i] = interface_normals;
+
+ } // end loop over cut_i
+ } // end loop over parts
+
+ end();
+}
+//------------------------------------------------------------------------------
+bool
+MultiMesh::_is_overlapped_interface(std::vector<Point> simplex,
+ const Cell cut_cell,
+ Point simplex_normal) const
+{
+ // Returns true if an interface intersection is overlapped by the cutting cell.
+ // The criterion for the edge being overlapped should be that
+ // (1) The intersection is contained within the cell facets
+ // (2) The (outwards) normals of the cut and the cutting cutting cell are equal.
+
+ // First, use inner products with a tolerance
+ // TODO: Maybe faster to call orient2dfast instead
+ Point simplex_midpoint(0.0, 0.0, 0.0);
+ for (Point p : simplex)
+ simplex_midpoint += p;
+ simplex_midpoint /= simplex.size();
+
+ const unsigned int* vertex_indices = cut_cell.entities(0);
+ for (std::size_t j = 0; j < cut_cell.num_entities(0); j++)
+ {
+ Vertex vertex_j(cut_cell.mesh(), vertex_indices[j]);
+ double c = simplex_normal.dot(vertex_j.point() - simplex_midpoint);
+ if (c > DOLFIN_EPS_LARGE)
+ return false;
+ }
+ // Identify a facet being cut, if any
+ std::size_t tdim = cut_cell.dim();
+ const unsigned int* facet_indices = cut_cell.entities(tdim - 1);
+ for (std::size_t j = 0; j < cut_cell.num_entities(tdim - 1); j++)
+ {
+ Facet facet_j(cut_cell.mesh(), facet_indices[j]);
+ simplex.push_back(facet_j.midpoint());
+ if (GeometryPredicates::is_degenerate(simplex, tdim))
+ {
+ // then we have found the right facet
+ simplex.pop_back();
+ return (simplex_normal.dot(cut_cell.normal(j)) > 0);
+ }
+ simplex.pop_back();
}
+ return false;
+}
+//------------------------------------------------------------------------------
+std::size_t
+MultiMesh::_add_quadrature_rule(quadrature_rule& qr,
+ const SimplexQuadrature& sq,
+ const Simplex& simplex,
+ std::size_t gdim,
+ std::size_t quadrature_order,
+ double factor) const
+{
+ // Compute quadrature rule for simplex
+ const auto dqr = sq.compute_quadrature_rule(simplex,
+ gdim,
+ quadrature_order);
+ // Add quadrature rule
+ const std::size_t num_points = _add_quadrature_rule(qr, dqr, gdim, factor);
+
return num_points;
}
+
//-----------------------------------------------------------------------------
std::size_t MultiMesh::_add_quadrature_rule(quadrature_rule& qr,
const quadrature_rule& dqr,
@@ -788,13 +1094,6 @@ std::size_t MultiMesh::_add_quadrature_rule(quadrature_rule& qr,
dolfin_assert(dqr.first.size() == gdim*dqr.second.size());
const std::size_t num_points = dqr.second.size();
- // Skip if sum of weights is too small
- double wsum = 0.0;
- for (std::size_t i = 0; i < num_points; i++)
- wsum += std::abs(dqr.second[i]);
- if (wsum < DOLFIN_EPS)
- return 0;
-
// Append points and weights
for (std::size_t i = 0; i < num_points; i++)
{
@@ -810,12 +1109,482 @@ std::size_t MultiMesh::_add_quadrature_rule(quadrature_rule& qr,
}
//-----------------------------------------------------------------------------
void MultiMesh::_add_normal(std::vector<double>& normals,
- const Point& normal,
- const std::size_t npts,
- const std::size_t gdim) const
+ const Point& normal,
+ std::size_t npts,
+ std::size_t gdim) const
+{
+ for (std::size_t i = 0; i < npts; ++i)
+ for (std::size_t j = 0; j < gdim; ++j)
+ normals.push_back(-normal[j]);
+}
+//-----------------------------------------------------------------------------
+void MultiMesh::_inclusion_exclusion_overlap
+(std::vector<quadrature_rule>& qr,
+ const SimplexQuadrature& sq,
+ const std::vector<std::pair<std::size_t, Polyhedron> >& initial_polyhedra,
+ std::size_t tdim,
+ std::size_t gdim,
+ std::size_t quadrature_order) const
+{
+ begin(PROGRESS, "The inclusion exclusion principle.");
+
+ // Exclusion-inclusion principle. There are N stages in the
+ // principle, where N = polyhedra.size(). The first stage is
+ // simply the polyhedra themselves A, B, C, ... etc. The second
+ // stage is for the pairwise intersections A \cap B, A \cap C, B
+ // \cap C, etc, with different sign. There are
+ // n_choose_k(N,stage) intersections for each stage.
+
+ // Data structure for storing the previous intersections: the key
+ // and the intersections.
+ const std::size_t N = initial_polyhedra.size();
+ std::vector<std::pair<IncExcKey, Polyhedron> > previous_intersections(N);
+ for (std::size_t i = 0; i < N; ++i)
+ previous_intersections[i]
+ = std::make_pair(IncExcKey(1, initial_polyhedra[i].first),
+ initial_polyhedra[i].second);
+
+ // Do stage = 1 up to stage = polyhedra.size in the
+ // principle. Recall that stage 1 is the pairwise
+ // intersections. There are up to n_choose_k(N,stage)
+ // intersections in each stage (there may be less). The
+ // intersections are found using the polyhedra data and the
+ // previous_intersections data. We only have to intersect if the
+ // key doesn't contain the polyhedron.
+
+ // Add quadrature rules for stage 0
+ for (const std::pair<IncExcKey, Polyhedron>& pol_pair: previous_intersections)
+ for (const Simplex& simplex: pol_pair.second)
+ if (simplex.size() == tdim + 1)
+ {
+ _add_quadrature_rule(qr[pol_pair.first[0]], sq, simplex, gdim,
+ quadrature_order, 1.);
+ }
+
+ // Add quadrature rules for overlap part
+ for (std::size_t stage = 1; stage < N; ++stage)
+ {
+ // Structure for storing new intersections
+ std::vector<std::pair<IncExcKey, Polyhedron> > new_intersections;
+
+ // Loop over all intersections from the previous stage
+ for (const std::pair<IncExcKey, Polyhedron>& previous_polyhedron: previous_intersections)
+ {
+ // Loop over all initial polyhedra.
+ for (const std::pair<std::size_t, Polyhedron>& initial_polyhedron: initial_polyhedra)
+ {
+ // Only check if initial_polyhedron key < previous_polyhedron
+ // key[0]
+ if (initial_polyhedron.first < previous_polyhedron.first[0])
+ {
+ // We want to save the intersection of the previous
+ // polyhedron and the initial polyhedron in one single
+ // polyhedron.
+ Polyhedron new_polyhedron;
+ IncExcKey new_keys;
+
+ // Loop over all simplices in the initial_polyhedron and
+ // the previous_polyhedron and append the intersection of
+ // these to the new_polyhedron
+ bool any_intersections = false;
+
+ for (const Simplex& previous_simplex: previous_polyhedron.second)
+ {
+ for (const Simplex& initial_simplex: initial_polyhedron.second)
+ {
+ // Compute the intersection (a polyhedron)
+ // To save all intersections as a single polyhedron,
+ // we don't call this a polyhedron yet, but rather a
+ // std::vector<Simplex> since we are still filling
+ // the polyhedron with simplices
+
+ // Only allow same types for now
+ if (previous_simplex.size() == tdim + 1 &&
+ initial_simplex.size() == tdim + 1)
+ {
+ const std::vector<Point> intersection_points
+ = IntersectionConstruction::intersection(initial_simplex,
+ previous_simplex,
+ gdim);
+
+ if (!GeometryPredicates::convex_hull_is_degenerate(intersection_points,
+ gdim))
+ {
+
+ const Polyhedron intersection
+ = ConvexTriangulation::triangulate(intersection_points,
+ gdim,
+ tdim);
+
+ // To save all intersections as a single
+ // polyhedron, we don't call this a polyhedron
+ // yet, but rather a std::vector<Simplex> since we
+ // are still filling the polyhedron with simplices
+
+ // FIXME: We could add only if area is sufficiently
+ // large
+ for (const Simplex& simplex: intersection)
+ {
+ if (simplex.size() == tdim + 1 and
+ !GeometryPredicates::is_degenerate(simplex, gdim))
+ {
+ new_polyhedron.push_back(simplex);
+ any_intersections = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (any_intersections)
+ {
+ new_keys.push_back(initial_polyhedron.first);
+ new_keys.insert(new_keys.end(),
+ previous_polyhedron.first.begin(),
+ previous_polyhedron.first.end());
+
+ // FIXME: Test improve quality
+ //maximize_minimum_angle(new_polyhedron);
+
+ // Save data
+ new_intersections.emplace_back(new_keys, new_polyhedron);
+ }
+ }
+ }
+ }
+
+ // Update before next stage
+ previous_intersections = new_intersections;
+
+ // Add quadrature rule with correct sign
+ const double sign = std::pow(-1, stage);
+
+ for (const std::pair<IncExcKey, Polyhedron>& polyhedron: new_intersections)
+ for (const Simplex& simplex: polyhedron.second)
+ if (simplex.size() == tdim + 1)
+ {
+ _add_quadrature_rule(qr[polyhedron.first[0]], sq, simplex, gdim,
+ quadrature_order, sign);
+ }
+
+ } // end loop over stages
+
+ end();
+}
+//------------------------------------------------------------------------------
+void MultiMesh::_inclusion_exclusion_interface
+(quadrature_rule& qr,
+ std::vector<double>& normals,
+ const SimplexQuadrature& sq,
+ const Simplex& Eij,
+ const Point& facet_normal,
+ const std::vector<std::pair<std::size_t, Polyhedron> >& initial_polyhedra,
+ std::size_t tdim_interface,
+ std::size_t gdim,
+ std::size_t quadrature_order) const
+{
+ begin(PROGRESS, "The inclusion exclusion principle for the interface.");
+
+ dolfin_assert(Eij.size() == tdim_interface + 1);
+ const std::size_t tdim_bulk = tdim_interface + 1;
+
+ // Exclusion-inclusion principle. There are N stages in the
+ // principle, where N = polyhedra.size(). The first stage is simply
+ // the polyhedra themselves A, B, C, ... etc. The second stage is
+ // for the pairwise intersections A \cap B, A \cap C, B \cap C, etc,
+ // with different sign. There are n_choose_k(N,stage) intersections
+ // for each stage.
+
+ // Note that for each qr we have a normal.
+
+ // Data structure for storing the previous intersections: the key
+ // and the intersections.
+ const std::size_t N = initial_polyhedra.size();
+ std::vector<std::pair<IncExcKey, Polyhedron> > previous_intersections(N);
+ for (std::size_t i = 0; i < N; ++i)
+ previous_intersections[i]
+ = std::make_pair(IncExcKey(1, initial_polyhedra[i].first),
+ initial_polyhedra[i].second);
+
+ // Do stage = 1 up to stage = polyhedra.size in the
+ // principle. Recall that stage 1 is the pairwise
+ // intersections. There are up to n_choose_k(N,stage) intersections
+ // in each stage (there may be less). The intersections are found
+ // using the polyhedra data and the previous_intersections data. We
+ // only have to intersect if the key doesn't contain the polyhedron.
+
+ // FIXME: We also only have to intersect if the polyhedron and the
+ // previous_intersections are from different meshes.
+
+ // Add quadrature rule for stage 0 and save normals
+ quadrature_rule qr_stage0;
+ std::vector<double> normals_stage0;
+
+ for (const std::pair<IncExcKey, Polyhedron>& pol_pair: previous_intersections)
+ {
+ for (const Simplex& simplex: pol_pair.second)
+ {
+ if (simplex.size() == tdim_bulk + 1 and
+ !GeometryPredicates::is_degenerate(simplex, gdim))
+ {
+
+ const std::vector<Point> Eij_cap_Tk_points
+ = IntersectionConstruction::intersection(Eij, simplex, gdim);
+
+ if (Eij_cap_Tk_points.size())
+ {
+ const Polyhedron Eij_cap_Tk
+ = ConvexTriangulation::triangulate(Eij_cap_Tk_points, gdim, tdim_interface);
+
+ for (const Simplex& s: Eij_cap_Tk)
+ {
+ if (s.size() == tdim_interface + 1 and
+ !GeometryPredicates::is_degenerate(s, gdim))
+ {
+ const std::size_t num_pts
+ = _add_quadrature_rule(qr_stage0, sq, s, gdim,
+ quadrature_order, -1.); // Stage 0 is negative
+ _add_normal(normals_stage0, facet_normal, num_pts, gdim);
+ }
+ }
+ }
+ }
+ }
+ }
+ dolfin_assert(normals_stage0.size() == qr_stage0.first.size());
+
+ // Add quadrature rule and normals
+ qr.first.insert(qr.first.end(), qr_stage0.first.begin(), qr_stage0.first.end());
+ qr.second.insert(qr.second.end(), qr_stage0.second.begin(), qr_stage0.second.end());
+ normals.insert(normals.end(), normals_stage0.begin(), normals_stage0.end());
+
+ for (std::size_t stage = 1; stage < N; ++stage)
+ {
+ // Data structure for storing new intersections
+ std::vector<std::pair<IncExcKey, Polyhedron> > new_intersections;
+
+ // Loop over all intersections from the previous stage
+ for (const std::pair<IncExcKey, Polyhedron>& previous_polyhedron: previous_intersections)
+ {
+ // Loop over all initial polyhedra.
+ for (const std::pair<std::size_t, Polyhedron>& initial_polyhedron: initial_polyhedra)
+ {
+
+ // Check if the initial_polyhedron's key < previous_polyhedron
+ // key[0]
+ if (initial_polyhedron.first < previous_polyhedron.first[0])
+ {
+ // We want to save the intersection of the previous
+ // polyhedron and the initial polyhedron in one single
+ // polyhedron.
+ Polyhedron new_polyhedron;
+ IncExcKey new_keys;
+
+ // Loop over all simplices in the initial_polyhedron and
+ // the previous_polyhedron and append the intersection of
+ // these to the new_polyhedron
+ bool any_intersections = false;
+
+ for (const Simplex& previous_simplex: previous_polyhedron.second)
+ {
+ for (const Simplex& initial_simplex: initial_polyhedron.second)
+ {
+ // Compute the intersection (a polyhedron).
+ // FIXME: Only allow same types for now.
+ if (previous_simplex.size() == tdim_bulk + 1 &&
+ initial_simplex.size() == tdim_bulk + 1)
+ {
+ const std::vector<Point> intersection_points
+ = IntersectionConstruction::intersection(initial_simplex,
+ previous_simplex,
+ gdim);
+ // FIXME: Should we avoid this if statement and let
+ // ConvexTriangulation deal with empty input?
+ if (intersection_points.size())
+ {
+ const Polyhedron intersection
+ = ConvexTriangulation::triangulate(intersection_points,
+ gdim,
+ tdim_bulk);
+
+ if (intersection.size())
+ {
+ // To save all intersections as a single
+ // polyhedron, we don't call this a polyhedron
+ // yet, but rather a std::vector<Simplex> since we
+ // are still filling the polyhedron with simplices
+
+ // FIXME: We could add only if area is sufficiently large
+ for (const Simplex& simplex: intersection)
+ {
+ if (simplex.size() == tdim_bulk + 1 and
+ !GeometryPredicates::is_degenerate(simplex, gdim))
+ {
+ new_polyhedron.push_back(simplex);
+ any_intersections = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ if (any_intersections)
+ {
+ new_keys.push_back(initial_polyhedron.first);
+ new_keys.insert(new_keys.end(),
+ previous_polyhedron.first.begin(),
+ previous_polyhedron.first.end());
+
+ // FIXME: Test improve quality by edge flips
+
+ // Save data
+ new_intersections.emplace_back(new_keys, new_polyhedron);
+ }
+ }
+ }
+ }
+
+ // Update before next stage
+ previous_intersections = new_intersections;
+
+ // Add quadrature rule with correct sign and save normals
+ const double sign = -std::pow(-1, stage);
+ quadrature_rule qr_stage;
+ std::vector<double> normals_stage;
+
+ for (const std::pair<IncExcKey, Polyhedron>& polyhedron: new_intersections)
+ {
+ for (const Simplex& simplex: polyhedron.second)
+ {
+ if (simplex.size() == tdim_bulk + 1)
+ {
+ const std::vector<Point> Eij_cap_Tk_points
+ = IntersectionConstruction::intersection(Eij, simplex, gdim);
+ const Polyhedron Eij_cap_Tk
+ = ConvexTriangulation::triangulate(Eij_cap_Tk_points,
+ gdim,
+ tdim_interface);
+ for (const Simplex& s: Eij_cap_Tk)
+ {
+ if (s.size() == tdim_interface + 1 and
+ !GeometryPredicates::is_degenerate(s, gdim))
+ {
+ const std::size_t num_pts
+ = _add_quadrature_rule(qr_stage, sq, s, gdim,
+ quadrature_order, sign);
+ _add_normal(normals_stage, facet_normal, num_pts, gdim);
+ }
+ }
+ }
+ }
+ }
+ dolfin_assert(normals_stage.size() == qr_stage.first.size());
+
+ // Add quadrature rule and normals
+ qr.first.insert(qr.first.end(), qr_stage.first.begin(), qr_stage.first.end());
+ qr.second.insert(qr.second.end(), qr_stage.second.begin(), qr_stage.second.end());
+ normals.insert(normals.end(), normals_stage.begin(), normals_stage.end());
+ } // end loop over stages
+
+ end();
+}
+//------------------------------------------------------------------------------
+std::vector<std::vector<std::pair<std::size_t, std::size_t>>>
+MultiMesh::_boundary_facets_to_full_mesh(std::size_t part) const
+{
+ std::vector<std::vector<std::pair<std::size_t, std::size_t>>>
+ full_to_bdry(_meshes[part]->num_cells());
+
+ // Get map from boundary mesh to facets of full mesh
+ const std::size_t tdim_boundary
+ = _boundary_meshes[part]->topology().dim();
+ const auto& boundary_cell_map
+ = _boundary_meshes[part]->entity_map(tdim_boundary);
+
+ // Generate facet to cell connectivity for full mesh
+ const std::size_t tdim = _meshes[part]->topology().dim();
+ _meshes[part]->init(tdim_boundary, tdim);
+ const MeshConnectivity& full_facet_cell_map
+ = _meshes[part]->topology()(tdim_boundary, tdim);
+
+ for (std::size_t boundary_facet = 0;
+ boundary_facet < boundary_cell_map.size(); ++boundary_facet)
+ {
+ // Find the facet in the full mesh
+ const std::size_t full_mesh_facet = boundary_cell_map[boundary_facet];
+
+ // Find the cells in the full mesh (for interior facets we
+ // can have 2 facets, but here we should only have 1)
+ dolfin_assert(full_facet_cell_map.size(full_mesh_facet) == 1);
+ const auto& full_cells = full_facet_cell_map(full_mesh_facet);
+ full_to_bdry[full_cells[0]].emplace_back(boundary_facet,
+ full_mesh_facet);
+ }
+
+ return full_to_bdry;
+}
+//-----------------------------------------------------------------------------
+void MultiMesh::_impose_cut_cell_consistency()
+{
+ for (std::size_t part_id = 0; part_id < num_parts(); part_id++)
+ {
+ for (auto cell : cut_cells(part_id))
+ {
+ auto cell_quadrature_rules = _quadrature_rules_interface[part_id][cell];
+ bool has_no_qr = true;
+ for (quadrature_rule qr : cell_quadrature_rules)
+ {
+ if (qr.first.size() > 0)
+ {
+ has_no_qr = false;
+ break;
+ }
+ }
+ if (has_no_qr)
+ {
+ // Decide if cell is overlapped or uncut
+ // TODO: Implement decision to allow update before generating all the quadrature rules
+ // For now, we use cut cell and overlap quadrature
+ double overlap_area = 0;
+ for (auto _qr : _quadrature_rules_overlap[part_id][cell])
+ for (double w : _qr.second)
+ overlap_area += w;
+
+ double cut_cell_area = 0;
+ for (double w : _quadrature_rules_cut_cells[part_id][cell].second)
+ cut_cell_area += w;
+
+ // Append to _cut_cells or _overlapped_cells
+ // One of cut cell area and overlapped cell area should be zero
+ if (overlap_area > cut_cell_area)
+ _covered_cells[part_id].push_back(cell);
+ else
+ {
+ _uncut_cells[part_id].push_back(cell);
+ // Clear collision map
+ _collision_maps_cut_cells[part_id][cell].clear();
+ }
+ }
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+void MultiMesh::remove_quadrature_rule(quadrature_rule& qr,
+ double tolerance)
{
-for (std::size_t i = 0; i < npts; ++i)
- for (std::size_t j = 0; j < gdim; ++j)
- normals.push_back(normal[j]);
+ //const double sum = std::accumulate(qr.second.begin(),
+ // qr.second.end(), 0.0);
+
+ const double sum_of_weights = std::accumulate(qr.second.begin(),
+ qr.second.end(), 0.0);
+ if (std::abs(sum_of_weights) < tolerance)
+ {
+ qr.first.clear();
+ qr.second.clear();
+ }
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/mesh/MultiMesh.h b/dolfin/mesh/MultiMesh.h
index 8403c8a..61e0015 100644
--- a/dolfin/mesh/MultiMesh.h
+++ b/dolfin/mesh/MultiMesh.h
@@ -16,7 +16,7 @@
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// First added: 2014-03-03
-// Last changed: 2016-03-02
+// Last changed: 2017-09-22
#ifndef __MULTI_MESH_H
#define __MULTI_MESH_H
@@ -24,6 +24,7 @@
#include <memory>
#include <vector>
#include <map>
+#include <deque>
#include <dolfin/common/Variable.h>
#include <dolfin/geometry/Point.h>
@@ -36,9 +37,7 @@ namespace dolfin
class Mesh;
class BoundaryMesh;
class BoundingBoxTree;
-
- /// Typedefs
- typedef std::pair<std::vector<double>, std::vector<double> > quadrature_rule;
+ class SimplexQuadrature;
/// This class represents a collection of meshes with arbitrary
/// overlaps. A multimesh may be created from a set of standard
@@ -50,6 +49,18 @@ namespace dolfin
{
public:
+ /// Structure storing a quadrature rule
+ typedef std::pair<std::vector<double>, std::vector<double> > quadrature_rule;
+
+ /// A simplex is a list of points
+ typedef std::vector<Point> Simplex;
+
+ /// A polyhedron is a list of simplices
+ typedef std::vector<Simplex> Polyhedron;
+
+ /// Key to identify polyhedra
+ typedef std::vector<std::size_t> IncExcKey;
+
/// Create empty multimesh
MultiMesh();
@@ -125,7 +136,7 @@ namespace dolfin
/// *Returns*
/// std::vector<unsigned int>
/// List of cut cell indices for given part
- const std::vector<unsigned int>& cut_cells(std::size_t part) const;
+ const std::vector<unsigned int> cut_cells(std::size_t part) const;
/// Return the list of covered cells for given part. The covered
/// cells are defined as all cells that collide with the domain of
@@ -170,7 +181,7 @@ namespace dolfin
/// of a flattened array of quadrature points and a
/// corresponding array of quadrature weights.
const std::map<unsigned int, quadrature_rule >&
- quadrature_rule_cut_cells(std::size_t part) const;
+ quadrature_rules_cut_cells(std::size_t part) const;
/// Return quadrature rule for a given cut cell on the given part
///
@@ -186,12 +197,8 @@ namespace dolfin
/// array of quadrature points and a corresponding array
/// of quadrature weights. An error is raised if the given
/// cell is not in the map.
- ///
- /// Developer note: this function is mainly useful from Python and
- /// could be replaced by a suitable typemap that would make the
- /// previous more general function accessible from Python.
- quadrature_rule
- quadrature_rule_cut_cell(std::size_t part, unsigned int cell_index) const;
+ const quadrature_rule
+ quadrature_rules_cut_cells(std::size_t part, unsigned int cell_index) const;
/// Return quadrature rules for the overlap on the given part.
///
@@ -200,7 +207,7 @@ namespace dolfin
/// The part number
///
/// *Returns*
- /// std::map<unsigned int, std::pair<std::vector<double>, std::vector<double> > >
+ /// std::map<unsigned int, std::vector<std::pair<std::vector<double>, std::vector<double> > > >
/// A map from cell indices of cut cells to quadrature
/// rules. A separate quadrature rule is given for each
/// cutting cell and stored in the same order as in the
@@ -208,7 +215,28 @@ namespace dolfin
/// a pair of an array of quadrature points and a
/// corresponding flattened array of quadrature weights.
const std::map<unsigned int, std::vector<quadrature_rule> >&
- quadrature_rule_overlap(std::size_t part) const;
+ quadrature_rules_overlap(std::size_t part) const;
+
+ /// Return quadrature rules for the overlap for a given cell
+ /// on the given part.
+ ///
+ /// *Arguments*
+ /// part (std::size_t)
+ /// The part number
+ // cell (unsigned int)
+ // The cell index
+ ///
+ /// *Returns*
+ /// std::vector<std::pair<std::vector<double>, std::vector<double> > >
+ /// A vector of quadrature rules on the cut cell. A separate
+ /// quadrature rule is given for each cutting cell and stored
+ /// in the same order as in the collision map.
+ /// A quadrature rule represented as a pair of a flattened
+ /// array of quadrature points and a corresponding array
+ /// of quadrature weights. An error is raised if the given
+ /// cell is not in the map.
+ const std::vector<quadrature_rule>
+ quadrature_rules_overlap(std::size_t part, unsigned int cell) const;
/// Return quadrature rules for the interface on the given part
///
@@ -217,7 +245,7 @@ namespace dolfin
/// The part number
///
/// *Returns*
- /// std::map<unsigned int, std::pair<std::vector<double>, std::vector<double> > >
+ /// std::map<unsigned int, std::vector<std::pair<std::vector<double>, std::vector<double> > > >
/// A map from cell indices of cut cells to quadrature
/// rules on an interface part cutting through the cell.
/// A separate quadrature rule is given for each cutting
@@ -226,7 +254,35 @@ namespace dolfin
/// an array of quadrature points and a corresponding
/// flattened array of quadrature weights.
const std::map<unsigned int, std::vector<quadrature_rule> >&
- quadrature_rule_interface(std::size_t part) const;
+ quadrature_rules_interface(std::size_t part) const;
+
+
+ /// Return quadrature rules for the interface of a given cut cell
+ /// on the given part
+ ///
+ /// *Arguments*
+ /// part (std::size_t)
+ /// The part number
+ /// cell (unsigned int)
+ /// The cell index
+ ///
+ /// *Returns*
+ /// std::vector<std::pair<std::vector<double>, std::vector<double> > >
+ /// A vector of quadrature rules on the cut cell. A separate
+ /// quadrature rule is given for each cutting cell and stored
+ /// in the same order as in the collision map.
+ /// Each quadrature rule represented as a pair of a flattened
+ /// array of quadrature points and a corresponding array
+ /// of quadrature weights. An error is raised if the given
+ /// cell is not in the map.
+ ///
+ /// Developer note: this function is mainly useful from Python and
+ /// could be replaced by a suitable typemap that would make the
+ /// previous more general function accessible from Python.
+ const std::vector<quadrature_rule>
+ quadrature_rules_interface(std::size_t part,
+ unsigned int cell_index) const;
+
/// Return facet normals for the interface on the given part
///
@@ -283,11 +339,44 @@ namespace dolfin
/// Build multimesh
void build(std::size_t quadrature_order=2);
+ /// Check whether multimesh has been built
+ bool is_built() const { return _is_built; }
+
/// Clear multimesh
void clear();
+ /// Default parameter values
+ static Parameters default_parameters()
+ {
+ Parameters p("multimesh");
+
+ //p.add("quadrature_order", 1);
+ p.add("compress_volume_quadrature", false);
+ p.add("compress_interface_quadrature", false);
+
+ return p;
+ }
+
+ //--- The functions below are mainly useful for testing/debugging ---
+
+ /// Compute total interface area or the total volume of multimesh
+ /// by summing up quadrature weights. If the area or volume of
+ /// the domain mesh is known, this is a good test to verify that
+ /// the mesh-mesh intersections and quadrature are correct.
+ double compute_area() const;
+
+ /// Corresponding function for volume
+ double compute_volume() const;
+
+ /// Create matplotlib string to plot 2D multimesh (small meshes only)
+ std::string plot_matplotlib(double delta_z=1,
+ const std::string& filename="") const;
+
private:
+ // Flag for whether multimesh has been built
+ bool _is_built;
+
// List of meshes
std::vector<std::shared_ptr<const Mesh> > _meshes;
@@ -311,17 +400,6 @@ namespace dolfin
// j = the cell number (in the list of uncut cells)
std::vector<std::vector<unsigned int> > _uncut_cells;
- // Cell indices for all cut cells for all parts. Access data by
- //
- // c = _cut_cells[i][j]
- //
- // where
- //
- // c = cell index for a cut cell
- // i = the part (mesh) number
- // j = the cell number (in the list of cut cells)
- std::vector<std::vector<unsigned int> > _cut_cells;
-
// Cell indices for all covered cells for all parts. Access data by
//
// c = _covered_cells[i][j]
@@ -437,15 +515,23 @@ namespace dolfin
// Build quadrature rules for the overlap
void _build_quadrature_rules_overlap(std::size_t quadrature_order);
- // Add quadrature rule for simplices in the triangulation
- // array. Returns the number of points generated for each simplex.
- std::vector<std::size_t>
- _add_quadrature_rule(quadrature_rule& qr,
- const std::vector<double>& triangulation,
- std::size_t tdim,
- std::size_t gdim,
- std::size_t quadrature_order,
- double factor) const;
+ // Build quadrature rules and normals for the interface
+ void _build_quadrature_rules_interface(std::size_t quadrature_order);
+
+ // Help function to determine if interface intersection is
+ // (exactly) overlapped by a cutting cell
+ bool _is_overlapped_interface(std::vector<Point> simplex,
+ const Cell cut_cell,
+ Point simplex_normal) const;
+
+ // Add quadrature rule for simplices in polyhedron. Returns the
+ // number of points generated for each simplex.
+ std::size_t _add_quadrature_rule(quadrature_rule& qr,
+ const SimplexQuadrature& sq,
+ const Simplex& simplex,
+ std::size_t gdim,
+ std::size_t quadrature_order,
+ double factor) const;
// Add quadrature rule to existing quadrature rule (append dqr to
// qr). Returns number of points added.
@@ -457,10 +543,50 @@ namespace dolfin
// Append normal to list of normals npts times
void _add_normal(std::vector<double>& normals,
const Point& normal,
- const std::size_t npts,
- const std::size_t gdim) const;
+ std::size_t npts,
+ std::size_t gdim) const;
+
+ // Plot multimesh
+ void _plot() const;
+
+ // Inclusion-exclusion for overlap
+ void _inclusion_exclusion_overlap
+ (std::vector<quadrature_rule>& qr,
+ const SimplexQuadrature& sq,
+ const std::vector<std::pair<std::size_t, Polyhedron> >& initial_polyhedra,
+ std::size_t tdim,
+ std::size_t gdim,
+ std::size_t quadrature_order) const;
+
+ // Inclusion-exclusion for interface
+ void _inclusion_exclusion_interface
+ (quadrature_rule& qr,
+ std::vector<double>& normals,
+ const SimplexQuadrature& sq,
+ const Simplex& Eij,
+ const Point& facet_normal,
+ const std::vector<std::pair<std::size_t, Polyhedron> >& initial_polygons,
+ std::size_t tdim,
+ std::size_t gdim,
+ std::size_t quadrature_order) const;
+
+ // Construct and return mapping from boundary facets to full mesh
+ std::vector<std::vector<std::pair<std::size_t, std::size_t> > >
+ _boundary_facets_to_full_mesh(std::size_t part) const;
+
+ // Impose consistency of _cut_cells, so that only the cells with
+ // a nontrivial interface quadrature rule are classified as cut.
+ void _impose_cut_cell_consistency();
+
+ // Remove quadrature rule if the sum of the weights is less than a
+ // tolerance
+ static void remove_quadrature_rule(quadrature_rule& qr,
+ double tolerance);
};
+
+
}
+
#endif
diff --git a/dolfin/mesh/PointCell.cpp b/dolfin/mesh/PointCell.cpp
index 1c3441b..1449efd 100644
--- a/dolfin/mesh/PointCell.cpp
+++ b/dolfin/mesh/PointCell.cpp
@@ -20,9 +20,10 @@
// Modified by August Johansson 2014
//
// First added: 2007-12-12
-// Last changed: 2014-02-13
+// Last changed: 2016-05-05
#include <dolfin/log/log.h>
+#include <dolfin/geometry/CollisionPredicates.h>
#include "Cell.h"
#include "Facet.h"
#include "MeshEditor.h"
@@ -154,24 +155,12 @@ void PointCell::order(
//-----------------------------------------------------------------------------
bool PointCell::collides(const Cell& cell, const Point& point) const
{
- return CollisionDetection::collides(cell, point);
+ return CollisionPredicates::collides(cell, point);
}
//-----------------------------------------------------------------------------
bool PointCell::collides(const Cell& cell, const MeshEntity& entity) const
{
- return CollisionDetection::collides(cell, entity);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-PointCell::triangulate_intersection(const Cell& c0, const Cell& c1) const
-{
- return IntersectionTriangulation::triangulate_intersection(c0, c1);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-PointCell::triangulate_intersection(const Cell& cell, const MeshEntity& entity) const
-{
- return IntersectionTriangulation::triangulate_intersection(cell, entity);
+ return CollisionPredicates::collides(cell, entity);
}
//-----------------------------------------------------------------------------
std::string PointCell::description(bool plural) const
diff --git a/dolfin/mesh/PointCell.h b/dolfin/mesh/PointCell.h
index b27a52b..2e68b98 100644
--- a/dolfin/mesh/PointCell.h
+++ b/dolfin/mesh/PointCell.h
@@ -19,25 +19,30 @@
// Modified by Kristoffer Selim 2008
//
// First added: 2007-12-12
-// Last changed: 2014-01-06
+// Last changed: 2016-05-05
#ifndef __POINT_CELL_H
#define __POINT_CELL_H
+#include <vector>
#include <boost/multi_array.hpp>
#include "CellType.h"
namespace dolfin
{
- /// This class implements functionality for triangular meshes.
+ /// This class implements functionality for point cell meshes.
class PointCell : public CellType
{
public:
/// Specify cell type and facet type
- PointCell() : CellType(point, point) {}
+ PointCell() : CellType(Type::point, Type::point) {}
+
+ /// Check if cell is a simplex
+ bool is_simplex() const
+ { return true; }
/// Return topological dimension of cell
std::size_t dim() const;
@@ -92,14 +97,6 @@ namespace dolfin
/// Check whether given entity collides with cell
bool collides(const Cell& cell, const MeshEntity& entity) const;
- /// Compute triangulation of intersection of two cells
- virtual std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const;
-
- /// Compute triangulation of intersection with given entity
- virtual std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const;
-
/// Return description of cell type
std::string description(bool plural) const;
diff --git a/dolfin/mesh/QuadrilateralCell.cpp b/dolfin/mesh/QuadrilateralCell.cpp
index 39bb526..dc03685 100644
--- a/dolfin/mesh/QuadrilateralCell.cpp
+++ b/dolfin/mesh/QuadrilateralCell.cpp
@@ -288,20 +288,6 @@ bool QuadrilateralCell::collides(const Cell& cell, const MeshEntity& entity) con
return false;
}
//-----------------------------------------------------------------------------
-std::vector<double>
-QuadrilateralCell::triangulate_intersection(const Cell& c0, const Cell& c1) const
-{
- dolfin_not_implemented();
- return std::vector<double>();
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-QuadrilateralCell::triangulate_intersection(const Cell& cell, const MeshEntity& entity) const
-{
- dolfin_not_implemented();
- return std::vector<double>();
-}
-//-----------------------------------------------------------------------------
std::string QuadrilateralCell::description(bool plural) const
{
if (plural)
diff --git a/dolfin/mesh/QuadrilateralCell.h b/dolfin/mesh/QuadrilateralCell.h
index 8a2f5ad..52fb7b7 100644
--- a/dolfin/mesh/QuadrilateralCell.h
+++ b/dolfin/mesh/QuadrilateralCell.h
@@ -14,7 +14,6 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
#ifndef __QUADRILATERAL_CELL_H
#define __QUADRILATERAL_CELL_H
@@ -25,14 +24,18 @@
namespace dolfin
{
- /// This class implements functionality for triangular meshes.
+ /// This class implements functionality for quadrilaterial cells.
class QuadrilateralCell : public CellType
{
public:
/// Specify cell type and facet type
- QuadrilateralCell() : CellType(quadrilateral, interval) {}
+ QuadrilateralCell() : CellType(Type::quadrilateral, Type::interval) {}
+
+ /// Check if cell is a simplex
+ bool is_simplex() const
+ { return false; }
/// Return topological dimension of cell
std::size_t dim() const;
@@ -82,14 +85,6 @@ namespace dolfin
/// Check whether given entity collides with cell
bool collides(const Cell& cell, const MeshEntity& entity) const;
- /// Compute triangulation of intersection of two cells
- std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const;
-
- /// Compute triangulation of intersection with given entity
- std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const;
-
/// Return description of cell type
std::string description(bool plural) const;
diff --git a/dolfin/mesh/TetrahedronCell.cpp b/dolfin/mesh/TetrahedronCell.cpp
index 58d0e14..4bc192c 100644
--- a/dolfin/mesh/TetrahedronCell.cpp
+++ b/dolfin/mesh/TetrahedronCell.cpp
@@ -22,13 +22,14 @@
// Modified by August Johansson 2014
//
// First added: 2006-06-05
-// Last changed: 2014-02-13
+// Last changed: 2016-05-05
#include <algorithm>
#include <cmath>
#include <boost/multi_array.hpp>
#include <dolfin/log/log.h>
+#include <dolfin/geometry/CollisionPredicates.h>
#include "Cell.h"
#include "Facet.h"
#include "MeshEditor.h"
@@ -528,24 +529,12 @@ void TetrahedronCell::order(
//-----------------------------------------------------------------------------
bool TetrahedronCell::collides(const Cell& cell, const Point& point) const
{
- return CollisionDetection::collides(cell, point);
+ return CollisionPredicates::collides(cell, point);
}
//-----------------------------------------------------------------------------
bool TetrahedronCell::collides(const Cell& cell, const MeshEntity& entity) const
{
- return CollisionDetection::collides(cell, entity);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-TetrahedronCell::triangulate_intersection(const Cell& c0, const Cell& c1) const
-{
- return IntersectionTriangulation::triangulate_intersection(c0, c1);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-TetrahedronCell::triangulate_intersection(const Cell& cell, const MeshEntity& entity) const
-{
- return IntersectionTriangulation::triangulate_intersection(cell, entity);
+ return CollisionPredicates::collides(cell, entity);
}
//-----------------------------------------------------------------------------
std::string TetrahedronCell::description(bool plural) const
diff --git a/dolfin/mesh/TetrahedronCell.h b/dolfin/mesh/TetrahedronCell.h
index 0ff2fbb..04acda3 100644
--- a/dolfin/mesh/TetrahedronCell.h
+++ b/dolfin/mesh/TetrahedronCell.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2013 Anders Logg
+// Copyright (C) 2006-2017 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -20,7 +20,7 @@
// Modified by Kristoffer Selim, 2008.
//
// First added: 2006-06-05
-// Last changed: 2014-01-31
+// Last changed: 2017-09-26
#ifndef __TETRAHEDRON_CELL_H
#define __TETRAHEDRON_CELL_H
@@ -35,14 +35,18 @@ namespace dolfin
class Cell;
- /// This class implements functionality for tetrahedral meshes.
+ /// This class implements functionality for tetrahedral cell meshes.
class TetrahedronCell : public CellType
{
public:
/// Specify cell type and facet type
- TetrahedronCell() : CellType(tetrahedron, triangle) {}
+ TetrahedronCell() : CellType(Type::tetrahedron, Type::triangle) {}
+
+ /// Check if cell is a simplex
+ bool is_simplex() const
+ { return true; }
/// Return topological dimension of cell
std::size_t dim() const;
@@ -92,14 +96,6 @@ namespace dolfin
/// Check whether given entity collides with cell
bool collides(const Cell& cell, const MeshEntity& entity) const;
- /// Compute triangulation of intersection of two cells
- virtual std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const;
-
- /// Compute triangulation of intersection with given entity
- virtual std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const;
-
/// Return description of cell type
std::string description(bool plural) const;
diff --git a/dolfin/mesh/TopologyComputation.cpp b/dolfin/mesh/TopologyComputation.cpp
index 277e233..39b9c27 100644
--- a/dolfin/mesh/TopologyComputation.cpp
+++ b/dolfin/mesh/TopologyComputation.cpp
@@ -383,12 +383,12 @@ void TopologyComputation::compute_from_map(Mesh& mesh,
.entity_type(d0)));
MeshConnectivity& connectivity = mesh.topology()(d0, d1);
- connectivity.init(mesh.size(d0), cell_type->num_entities(d1));
+ connectivity.init(mesh.num_entities(d0), cell_type->num_entities(d1));
// Make a map from the sorted d1 entity vertices to the d1 entity index
boost::unordered_map<std::vector<unsigned int>, unsigned int>
entity_to_index;
- entity_to_index.reserve(mesh.size(d1));
+ entity_to_index.reserve(mesh.num_entities(d1));
const std::size_t num_verts_d1 = mesh.type().num_vertices(d1);
std::vector<unsigned int> key(num_verts_d1);
diff --git a/dolfin/mesh/TriangleCell.cpp b/dolfin/mesh/TriangleCell.cpp
index 987f9a7..c22f213 100644
--- a/dolfin/mesh/TriangleCell.cpp
+++ b/dolfin/mesh/TriangleCell.cpp
@@ -23,11 +23,12 @@
// Modified by August Johansson 2014
//
// First added: 2006-06-05
-// Last changed: 2014-05-22
+// Last changed: 2016-05-05
#include <algorithm>
#include <cmath>
#include <dolfin/log/log.h>
+#include <dolfin/geometry/CollisionPredicates.h>
#include "Cell.h"
#include "MeshEditor.h"
#include "MeshEntity.h"
@@ -449,24 +450,12 @@ void TriangleCell::order(
//-----------------------------------------------------------------------------
bool TriangleCell::collides(const Cell& cell, const Point& point) const
{
- return CollisionDetection::collides(cell, point);
+ return CollisionPredicates::collides(cell, point);
}
//-----------------------------------------------------------------------------
bool TriangleCell::collides(const Cell& cell, const MeshEntity& entity) const
{
- return CollisionDetection::collides(cell, entity);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-TriangleCell::triangulate_intersection(const Cell& c0, const Cell& c1) const
-{
- return IntersectionTriangulation::triangulate_intersection(c0, c1);
-}
-//-----------------------------------------------------------------------------
-std::vector<double>
-TriangleCell::triangulate_intersection(const Cell& cell, const MeshEntity& entity) const
-{
- return IntersectionTriangulation::triangulate_intersection(cell, entity);
+ return CollisionPredicates::collides(cell, entity);
}
//-----------------------------------------------------------------------------
std::string TriangleCell::description(bool plural) const
diff --git a/dolfin/mesh/TriangleCell.h b/dolfin/mesh/TriangleCell.h
index 57412ca..29879e8 100644
--- a/dolfin/mesh/TriangleCell.h
+++ b/dolfin/mesh/TriangleCell.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2013 Anders Logg
+// Copyright (C) 2006-2017 Anders Logg
//
// This file is part of DOLFIN.
//
@@ -19,7 +19,7 @@
// Modified by Jan Blechta 2013
//
// First added: 2006-06-05
-// Last changed: 2014-05-22
+// Last changed: 2017-09-26
#ifndef __TRIANGLE_CELL_H
#define __TRIANGLE_CELL_H
@@ -38,7 +38,11 @@ namespace dolfin
public:
/// Specify cell type and facet type
- TriangleCell() : CellType(triangle, interval) {}
+ TriangleCell() : CellType(Type::triangle, Type::interval) {}
+
+ /// Check if cell is a simplex
+ bool is_simplex() const
+ { return true; }
/// Return topological dimension of cell
std::size_t dim() const;
@@ -97,14 +101,6 @@ namespace dolfin
/// Check whether given entity collides with cell
bool collides(const Cell& cell, const MeshEntity& entity) const;
- /// Compute triangulation of intersection of two cells
- std::vector<double>
- triangulate_intersection(const Cell& c0, const Cell& c1) const;
-
- /// Compute triangulation of intersection with given entity
- std::vector<double>
- triangulate_intersection(const Cell& cell, const MeshEntity& entity) const;
-
/// Return description of cell type
std::string description(bool plural) const;
diff --git a/dolfin/mesh/Vertex.h b/dolfin/mesh/Vertex.h
index e5befb7..f040a7b 100644
--- a/dolfin/mesh/Vertex.h
+++ b/dolfin/mesh/Vertex.h
@@ -71,11 +71,19 @@ namespace dolfin
/// Constructor on Mesh
VertexFunction(std::shared_ptr<const Mesh> mesh)
- : MeshFunction<T>(mesh, 0) {}
+ : MeshFunction<T>(mesh, 0) {
+ deprecation("VertexFunction<T>(mesh)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, 0)");
+ }
/// Constructor on Mesh and value
VertexFunction(std::shared_ptr<const Mesh> mesh, const T& value)
- : MeshFunction<T>(mesh, 0, value) {}
+ : MeshFunction<T>(mesh, 0, value) {
+ deprecation("VertexFunction<T>(mesh, value)",
+ "2017.2.0",
+ "Use MeshFunction<T>(mesh, 0, value)");
+ }
};
diff --git a/dolfin/refinement/BisectionRefinement1D.cpp b/dolfin/refinement/BisectionRefinement1D.cpp
index ea24dc7..63fe635 100644
--- a/dolfin/refinement/BisectionRefinement1D.cpp
+++ b/dolfin/refinement/BisectionRefinement1D.cpp
@@ -99,7 +99,7 @@ void BisectionRefinement1D::refine(Mesh& refined_mesh,
const Mesh& mesh, bool redistribute)
{
auto _mesh = reference_to_no_delete_pointer(mesh);
- const CellFunction<bool> cell_markers(_mesh, true);
+ const MeshFunction<bool> cell_markers(_mesh, _mesh->topology().dim(), true);
BisectionRefinement1D::refine(refined_mesh, mesh, cell_markers, redistribute);
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/refinement/LocalMeshCoarsening.cpp b/dolfin/refinement/LocalMeshCoarsening.cpp
index c0bdae7..9becf1f 100644
--- a/dolfin/refinement/LocalMeshCoarsening.cpp
+++ b/dolfin/refinement/LocalMeshCoarsening.cpp
@@ -49,15 +49,16 @@ void LocalMeshCoarsening::coarsen_mesh_by_edge_collapse(Mesh& mesh,
log(TRACE, "Coarsen simplicial mesh by edge collapse.");
// Get size of old mesh
- //const std::size_t num_vertices = mesh.size(0);
- const std::size_t num_cells = mesh.size(mesh.topology().dim());
+ const std::size_t num_cells = mesh.num_entities(mesh.topology().dim());
// Check cell marker
if ( cell_marker.size() != num_cells )
+ {
dolfin_error("LocalMeshCoarsening.cpp",
"coarsen mesh by collapsing edges",
"Number of cell markers (%d) does not match number of cells (%d)",
cell_marker.size(), num_cells);
+ }
// Generate cell - edge connectivity if not generated
mesh.init(mesh.topology().dim(), 1);
@@ -196,11 +197,8 @@ bool LocalMeshCoarsening::coarsen_cell(Mesh& mesh, Mesh& coarse_mesh,
std::vector<int>& old2new_cell,
bool coarsen_boundary)
{
- cout << "coarsen_cell: " << cellid << endl;
- cout << "num_cells: " << mesh.num_cells() << endl;
-
- const std::size_t num_vertices = mesh.size(0);
- const std::size_t num_cells = mesh.size(mesh.topology().dim());
+ const std::size_t num_vertices = mesh.num_entities(0);
+ const std::size_t num_cells = mesh.num_entities(mesh.topology().dim());
auto _mesh = reference_to_no_delete_pointer(mesh);
diff --git a/dolfin/refinement/ParallelRefinement.cpp b/dolfin/refinement/ParallelRefinement.cpp
index 5ac85c3..9930e9d 100644
--- a/dolfin/refinement/ParallelRefinement.cpp
+++ b/dolfin/refinement/ParallelRefinement.cpp
@@ -138,7 +138,7 @@ void ParallelRefinement::update_logical_edgefunction()
// Clear marked_for_update vectors
marked_for_update = std::vector<std::vector<std::size_t>>(mpi_size);
- // Flatten received values and set EdgeFunction true at each index
+ // Flatten received values and set edges MeshFunction true at each index
// received
for (auto const &local_index : received_values)
marked_edges[local_index] = true;
@@ -194,7 +194,7 @@ void ParallelRefinement::create_new_vertices()
const std::size_t num_new_vertices = n;
const std::size_t global_offset
= MPI::global_offset(_mesh.mpi_comm(), num_new_vertices, true)
- + _mesh.size_global(0);
+ + _mesh.num_entities_global(0);
// If they are shared, then the new global vertex index needs to be
// sent off-process. Add offset to map, and collect up any shared
@@ -254,7 +254,15 @@ void ParallelRefinement::build_local(Mesh& new_mesh) const
dolfin_assert(new_cell_topology.size()%num_cell_vertices == 0);
const std::size_t num_cells = new_cell_topology.size()/num_cell_vertices;
- ed.open(new_mesh, tdim, gdim);
+ CellType::Type cell_type;
+ if (tdim == 3)
+ cell_type = CellType::Type::tetrahedron;
+ else if (tdim == 2)
+ cell_type = CellType::Type::triangle;
+ else
+ cell_type = CellType::Type::interval;
+
+ ed.open(new_mesh, cell_type, tdim, gdim);
ed.init_vertices(num_vertices);
std::size_t i = 0;
for (auto p = new_vertex_coordinates.begin();
diff --git a/dolfin/refinement/ParallelRefinement.h b/dolfin/refinement/ParallelRefinement.h
index db59156..a89171f 100644
--- a/dolfin/refinement/ParallelRefinement.h
+++ b/dolfin/refinement/ParallelRefinement.h
@@ -29,15 +29,15 @@ namespace dolfin
// Forward declarations
class Mesh;
- template<typename T> class EdgeFunction;
template<typename T> class MeshFunction;
/// Data structure and methods for refining meshes in parallel
/// ParallelRefinement encapsulates two main features:
- /// a distributed EdgeFunction, which can be updated
- /// across processes, and storage for local mesh data,
- /// which can be used to construct the new Mesh
+ /// a distributed MeshFunction defined over the mesh edes,
+ /// which can be updated across processes,
+ /// and storage for local mesh data, which can be used
+ /// to construct the new Mesh
class ParallelRefinement
{
diff --git a/dolfin/refinement/RegularCutRefinement.cpp b/dolfin/refinement/RegularCutRefinement.cpp
index 475f462..ce17f9f 100644
--- a/dolfin/refinement/RegularCutRefinement.cpp
+++ b/dolfin/refinement/RegularCutRefinement.cpp
@@ -260,7 +260,8 @@ void RegularCutRefinement::refine_marked(Mesh& refined_mesh,
// Initialize mesh editor
const std::size_t num_vertices = mesh.num_vertices() + marked_edges.size();
MeshEditor editor;
- editor.open(refined_mesh, mesh.topology().dim(), mesh.geometry().dim());
+ editor.open(refined_mesh, mesh.type().cell_type(),
+ mesh.topology().dim(), mesh.geometry().dim());
editor.init_vertices_global(num_vertices, num_vertices);
editor.init_cells_global(num_cells, num_cells);
diff --git a/dolfin/refinement/refine.cpp b/dolfin/refinement/refine.cpp
index 37c0d0d..263a581 100644
--- a/dolfin/refinement/refine.cpp
+++ b/dolfin/refinement/refine.cpp
@@ -71,8 +71,8 @@ void dolfin::refine(Mesh& refined_mesh, const Mesh& mesh, bool redistribute)
}
// Report the number of refined cells
- const std::size_t n0 = mesh.size_global(D);
- const std::size_t n1 = refined_mesh.size_global(D);
+ const std::size_t n0 = mesh.num_entities_global(D);
+ const std::size_t n1 = refined_mesh.num_entities_global(D);
log(TRACE, "Number of cells increased from %d to %d (%.1f%% increase).",
n0, n1, 100.0 * (static_cast<double>(n1) / static_cast<double>(n0) - 1.0));
@@ -114,8 +114,8 @@ void dolfin::refine(Mesh& refined_mesh, const Mesh& mesh,
}
// Report the number of refined cells
- const std::size_t n0 = mesh.size_global(D);
- const std::size_t n1 = refined_mesh.size_global(D);
+ const std::size_t n0 = mesh.num_entities_global(D);
+ const std::size_t n1 = refined_mesh.num_entities_global(D);
log(TRACE, "Number of cells increased from %d to %d (%.1f%% increase).",
n0, n1, 100.0 * (static_cast<double>(n1) / static_cast<double>(n0) - 1.0));
@@ -138,9 +138,11 @@ void dolfin::p_refine(Mesh& refined_mesh, const Mesh& mesh)
"Currently only linear -> quadratic is supported");
}
- if (mesh.type().cell_type() != CellType::triangle
- and mesh.type().cell_type() != CellType::tetrahedron
- and mesh.type().cell_type() != CellType::interval)
+ const CellType::Type cell_type = mesh.type().cell_type();
+
+ if (cell_type != CellType::Type::triangle
+ and cell_type != CellType::Type::tetrahedron
+ and cell_type != CellType::Type::interval)
{
dolfin_error("refine.cpp",
"increase polynomial degree of mesh",
@@ -150,14 +152,14 @@ void dolfin::p_refine(Mesh& refined_mesh, const Mesh& mesh)
const std::size_t tdim = mesh.topology().dim();
const std::size_t gdim = mesh.geometry().dim();
- editor.open(refined_mesh, tdim, gdim, 2);
+ editor.open(refined_mesh, cell_type, tdim, gdim, 2);
// Copy over mesh
- editor.init_vertices_global(mesh.size(0), mesh.size_global(0));
+ editor.init_vertices_global(mesh.num_entities(0), mesh.num_entities_global(0));
for (VertexIterator v(mesh); !v.end(); ++v)
editor.add_vertex(v->index(), v->point());
- editor.init_cells_global(mesh.size(tdim), mesh.size_global(tdim));
+ editor.init_cells_global(mesh.num_entities(tdim), mesh.num_entities_global(tdim));
std::vector<std::size_t> verts(tdim + 1);
for (CellIterator c(mesh); !c.end(); ++c)
{
diff --git a/dolfin/refinement/refine.h b/dolfin/refinement/refine.h
index 46506ca..ab3acbf 100644
--- a/dolfin/refinement/refine.h
+++ b/dolfin/refinement/refine.h
@@ -83,7 +83,7 @@ namespace dolfin
/// The locally refined mesh.
///
/// @code{.cpp}
- /// CellFunction<bool> cell_markers(mesh);
+ /// MeshFunction<bool> cell_markers(mesh, mesh->topology().dim());
/// cell_markers.set_all(false);
/// Point origin(0.0, 0.0, 0.0);
/// for (CellIterator cell(mesh); !cell.end(); ++cell)
diff --git a/dolfin/swig/la/post.i b/dolfin/swig/la/post.i
index 9230ea5..e8b6b71 100644
--- a/dolfin/swig/la/post.i
+++ b/dolfin/swig/la/post.i
@@ -149,6 +149,13 @@ def la_index_dtype():
def array(self):
"Return a numpy array representation of the local part of a Vector"
+ # Annoying deprecation warning that cannot be turned off, sorry!
+ import warnings
+ with warnings.catch_warnings():
+ # Changes default warning filter inside this context manager only
+ warnings.simplefilter('always')
+ warnings.warn("GenericVector.array() is being deprecated, "
+ "use GenericVector.get_local()", DeprecationWarning)
return self.get_local()
def __contains__(self, value):
@@ -349,10 +356,6 @@ def la_index_dtype():
def __len__(self):
return self.size()
- def __iter__(self):
- for i in range(self.size()):
- yield self[i]
-
def __add__(self, other):
"""x.__add__(y) <==> x+y"""
from numpy import isscalar
@@ -466,7 +469,7 @@ def la_index_dtype():
return NotImplemented
def __iter__(self):
- return iter(self.array())
+ return iter(self.get_local())
import sys
if sys.version_info[0] == 2:
diff --git a/dolfin/swig/mesh/post.i b/dolfin/swig/mesh/post.i
index 0bdf613..42928d6 100644
--- a/dolfin/swig/mesh/post.i
+++ b/dolfin/swig/mesh/post.i
@@ -500,6 +500,48 @@ def ufl_domain(self):
}
//-----------------------------------------------------------------------------
+// Extend MultiMesh interface with some ufl_* methods
+//-----------------------------------------------------------------------------
+%extend dolfin::MultiMesh
+{
+%pythoncode
+%{
+def ufl_id(self):
+ "Returns an id that UFL can use to decide if two objects are the same."
+ return self.id()
+
+def mpi_comm(self):
+ return self.part(0).mpi_comm()
+
+def type(self):
+ return self.part(0).type()
+
+def ufl_cell(self):
+ """Returns the ufl cell of the mesh."""
+ import ufl
+ gdim = self.part(0).geometry().dim()
+ cellname = self.type().description(False)
+ return ufl.Cell(cellname, geometric_dimension=gdim)
+
+def ufl_coordinate_element(self):
+ "Return the finite element of the coordinate vector field of this domain."
+ import ufl
+ cell = self.ufl_cell()
+ degree = self.part(0).geometry().degree()
+ return ufl.VectorElement("Lagrange", cell, degree, dim=cell.geometric_dimension())
+
+def ufl_domain(self):
+ """Returns the ufl domain corresponding to the mesh."""
+ import ufl
+ # Cache object to avoid recreating it a lot
+ if not hasattr(self, "_ufl_domain"):
+ self._ufl_domain = ufl.Mesh(self.ufl_coordinate_element(), ufl_id=self.ufl_id(), cargo=self)
+ return self._ufl_domain
+%}
+}
+
+
+//-----------------------------------------------------------------------------
// Modifying the interface of Hierarchical
//-----------------------------------------------------------------------------
%pythoncode %{
diff --git a/dolfin/swig/mesh/pre.i b/dolfin/swig/mesh/pre.i
index 47c3e97..b2bb413 100644
--- a/dolfin/swig/mesh/pre.i
+++ b/dolfin/swig/mesh/pre.i
@@ -352,3 +352,94 @@ FORWARD_DECLARE_MESHFUNCTIONS(std::size_t, Sizet)
%ignore dolfin::MeshPartitioning::build_distributed_mesh(Mesh&, const std::vector<std::size_t>&);
%ignore dolfin::MeshPartitioning::build_distributed_mesh(Mesh&, const LocalMeshData&);
%ignore dolfin::MeshPartitioning::build_distributed_value_collection;
+
+//-----------------------------------------------------------------------------
+// Ignores for MultiMesh
+//-----------------------------------------------------------------------------
+%ignore dolfin::plot(const MultiMesh&);
+%ignore dolfin::plot(std::shared_ptr<const MultiMesh>);
+
+//-----------------------------------------------------------------------------
+// Add typemap functions for MultiMesh quadrature rules
+//-----------------------------------------------------------------------------
+typedef std::pair<std::vector<double>, std::vector<double> > quadrature_rule;
+%{
+typedef std::pair<std::vector<double>, std::vector<double> > quadrature_rule;
+%}
+%fragment("convert_dolfin_quadrature_rule", "header"){
+SWIGINTERNINLINE PyObject * convert_dolfin_quadrature_rule(quadrature_rule qr)
+{
+ // Typemap Function for dolfin::quadrature_rule
+ npy_intp n0 = qr.first.size();
+ npy_intp n1 = qr.second.size();
+
+ PyArrayObject *x0 = reinterpret_cast<PyArrayObject*>(PyArray_SimpleNew(1, &n0, NPY_DOUBLE));
+ PyArrayObject *x1 = reinterpret_cast<PyArrayObject*>(PyArray_SimpleNew(1, &n1, NPY_DOUBLE));
+
+ double* data0 = static_cast<double*>(PyArray_DATA(x0));
+ double* data1 = static_cast<double*>(PyArray_DATA(x1));
+
+ std::copy(qr.first.begin(), qr.first.end(), data0);
+ std::copy(qr.second.begin(), qr.second.end(), data1);
+
+ PyObject * result;
+ result = Py_BuildValue("OO", x0, x1);
+ return result;
+}
+}
+%fragment("convert_dolfin_quadrature_rule_vector", "header"){
+SWIGINTERNINLINE PyObject * convert_dolfin_quadrature_rule(std::vector<quadrature_rule> qr_vector)
+{
+ // Typemap function for std::vec<quadrature_rule>
+ PyObject * result = PyList_New(qr_vector.size());
+ for (std::size_t j = 0; j < qr_vector.size(); j++)
+ {
+ PyObject * py_qr_j = convert_dolfin_quadrature_rule(qr_vector[j]);
+ PyList_SetItem(result, j, py_qr_j);
+ }
+ return result;
+}
+}
+// Force fragments to be instantiated
+%fragment("convert_dolfin_quadrature_rule");
+%fragment("convert_dolfin_quadrature_rule_vector");
+//-----------------------------------------------------------------------------
+// Modifying MultiMesh interface
+//-----------------------------------------------------------------------------
+%define EXTEND_MULTIMESH_QUADRATURE_RULE(cell_type)
+
+%extend dolfin::MultiMesh
+{
+PyObject* quadrature_rules_##cell_type(std::size_t part)
+{
+ PyObject* ret = PyDict_New();
+ auto qr_map = ($self)->quadrature_rules_##cell_type (part);
+
+ for (auto it = qr_map.begin(); it != qr_map.end(); it++)
+ {
+ PyObject* key = SWIG_From_dec(unsigned int)(it->first);
+ PyObject* val = convert_dolfin_quadrature_rule(it->second);
+ if (val != Py_None)
+ PyDict_SetItem(ret, key, val);
+ Py_XDECREF(key);
+ Py_XDECREF(val);
+ }
+ return ret;
+}
+PyObject* quadrature_rules_##cell_type(std::size_t part, unsigned int cell)
+{
+ auto qr_map = ($self)->quadrature_rules_##cell_type(part);
+ auto qr = qr_map[cell];
+ return convert_dolfin_quadrature_rule(qr);
+}
+}
+%ignore dolfin::MultiMesh::quadrature_rules_##cell_type;
+dolfin::MultiMesh::quadrature_rules_##cell_type(std::size_t part);
+
+
+%enddef
+
+EXTEND_MULTIMESH_QUADRATURE_RULE(cut_cells)
+EXTEND_MULTIMESH_QUADRATURE_RULE(interface)
+EXTEND_MULTIMESH_QUADRATURE_RULE(overlap)
+
diff --git a/dolfin/swig/shared_ptr_classes.i b/dolfin/swig/shared_ptr_classes.i
index c347ab5..f63ea48 100644
--- a/dolfin/swig/shared_ptr_classes.i
+++ b/dolfin/swig/shared_ptr_classes.i
@@ -113,6 +113,7 @@
%shared_ptr(dolfin::Hierarchical<dolfin::Mesh>)
%shared_ptr(dolfin::BoundaryMesh)
%shared_ptr(dolfin::Mesh)
+%shared_ptr(dolfin::MeshTopology)
%shared_ptr(dolfin::SubMesh)
%shared_ptr(dolfin::UnitTetrahedronMesh)
%shared_ptr(dolfin::UnitCubeMesh)
diff --git a/dolfin/swig/typemaps/std_map.i b/dolfin/swig/typemaps/std_map.i
index d9f22a1..60626d2 100644
--- a/dolfin/swig/typemaps/std_map.i
+++ b/dolfin/swig/typemaps/std_map.i
@@ -243,3 +243,30 @@ MAP_OUT_TYPEMAPS(int, unsigned int, uint, NPY_UINT)
// Append the output to $result
%append_output(ret);
}
+
+
+// Add 'out' typemap for cut cell collisions (std::map<uint, std::vector<std::pair<size_t, uint>>>)
+%typemap(out) const std::map<unsigned int, std::vector<std::pair<std::size_t, unsigned int> > >& \
+ (std::map<unsigned int, std::vector<std::pair<std::size_t, unsigned int> > >::const_iterator it,
+ std::vector<std::pair<std::size_t, unsigned int> >::const_iterator jt,
+ PyObject* item0, PyObject* item1, PyObject* item2)
+{
+ $result = PyDict_New();
+ int size;
+ for (it=$1->begin(); it!=$1->end(); ++it)
+ {
+ item0 = SWIG_From_dec(unsigned int)(it->first);
+ item1 = PyList_New(it->second.size());
+
+ int j = 0;
+ for (jt = it->second.begin(); jt!= it->second.end(); jt++, j++)
+ {
+ item2 = Py_BuildValue("ii", (*jt).first, (*jt).second);
+ PyList_SetItem(item1, j, item2);
+ }
+ PyDict_SetItem($result, item0, item1);
+ Py_XDECREF(item0);
+ Py_XDECREF(item1);
+ }
+}
+
diff --git a/dolfin/swig/typemaps/std_vector.i b/dolfin/swig/typemaps/std_vector.i
index 0f79e36..210eaba 100644
--- a/dolfin/swig/typemaps/std_vector.i
+++ b/dolfin/swig/typemaps/std_vector.i
@@ -471,6 +471,24 @@ const std::vector<TYPE>& ARG_NAME
}
%enddef
+%define OUT_TYPEMAP_STD_VECTOR_OF_SMALL_DOLFIN_TYPES(TYPE)
+%typemap (out) std::vector<TYPE>
+{
+ PyObject* l = PyList_New(0);
+
+ const std::vector<TYPE>& v = $1;
+ for (const TYPE& o : v)
+ {
+ PyObject* resultobj = SWIG_NewPointerObj(new TYPE(o), $descriptor(TYPE*), SWIG_POINTER_OWN );
+ PyList_Append(l, resultobj);
+ // FIXME: Py_DECREF here?
+ }
+
+ $result = l;
+}
+
+%enddef
+
//-----------------------------------------------------------------------------
// Macro for defining an in typemap for const std::vector<std::vector<TYPE> >&
// where TYPE is a primitive
@@ -744,7 +762,9 @@ OUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES_REFERENCE(std::size_t, size_t)
// This typemap handles dolfin::la_index, which can be a 32 or 64 bit integer
OUT_TYPEMAP_STD_VECTOR_OF_PRIMITIVES_REFERENCE(dolfin::la_index, dolfin_index)
-IN_TYPEMAP_STD_VECTOR_OF_SMALL_DOLFIN_TYPES(Point)
+IN_TYPEMAP_STD_VECTOR_OF_SMALL_DOLFIN_TYPES(dolfin::Point)
+OUT_TYPEMAP_STD_VECTOR_OF_SMALL_DOLFIN_TYPES(dolfin::Point)
+
IN_TYPEMAP_STD_VECTOR_OF_SMALL_DOLFIN_TYPES(MeshEntity)
#if (DOLFIN_SIZE_T==4)
IN_TYPEMAP_STD_VECTOR_OF_STD_VECTOR_OF_PRIMITIVES(std::size_t, INT32, facets,
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 7488b98..a5c3cf2 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -8,10 +8,12 @@ find_package(pybind11 REQUIRED CONFIG HINTS ${PYBIND11_DIR} ${PYBIND11_ROOT}
find_package(DOLFIN REQUIRED)
include(${DOLFIN_USE_FILE})
-# Check for mpi4py
-#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
-# ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
-#find_package(MPI4PY)
+# Strict compiler flags
+#include(CheckCXXCompilerFlag)
+#CHECK_CXX_COMPILER_FLAG("-Wall -Werror -pedantic" HAVE_PEDANTIC)
+#if (HAVE_PEDANTIC)
+# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -pedantic")
+#endif()
# Create the binding library
pybind11_add_module(cpp SHARED
@@ -32,12 +34,26 @@ pybind11_add_module(cpp SHARED
src/io.cpp
src/la.cpp
src/nls.cpp
- src/refinement.cpp)
-
-#if (MPI4PY_FOUND)
-# target_include_directories(cpp PRIVATE ${MPI4PY_INCLUDE_DIR})
-# add_definitions(-DHAS_MPI4PY)
-#endif()
+ src/refinement.cpp
+ src/MPICommWrapper.cpp)
# Add DOLFIN libraries and other config
target_link_libraries(cpp PRIVATE pybind11::module dolfin)
+
+# Add to CMake search path
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+# Check for petsc4py
+find_package(PETSc4py)
+if (PETSC4PY_FOUND)
+ target_include_directories(cpp PRIVATE ${PETSC4PY_INCLUDE_DIRS})
+ target_compile_definitions(cpp PRIVATE HAS_PYBIND11_PETSC4PY)
+endif()
+
+# Check for mpi4py
+find_package(MPI4PY)
+if (MPI4PY_FOUND)
+ target_include_directories(cpp PRIVATE ${MPI4PY_INCLUDE_DIR})
+ target_compile_definitions(cpp PRIVATE HAS_PYBIND11_MPI4PY)
+endif()
diff --git a/python/cmake/FindPETSc4py.cmake b/python/cmake/FindPETSc4py.cmake
new file mode 100644
index 0000000..66dab9a
--- /dev/null
+++ b/python/cmake/FindPETSc4py.cmake
@@ -0,0 +1,112 @@
+# - Try to find petsc4py
+# Once done this will define
+#
+# PETSC4PY_FOUND - system has petsc4py
+# PETSC4PY_INCLUDE_DIRS - include directories for petsc4py
+# PETSC4PY_VERSION - version of petsc4py
+# PETSC4PY_VERSION_MAJOR - first number in PETSC4PY_VERSION
+# PETSC4PY_VERSION_MINOR - second number in PETSC4PY_VERSION
+
+# Based on FindNumPy.cmake
+
+#=============================================================================
+# Copyright (C) 2013 Lawrence Mitchell
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+message(STATUS "Checking for package 'PETSc4Py'")
+
+if(PETSC4PY_INCLUDE_DIRS)
+ # In cache already
+ set(PETSC4PY_FIND_QUIETLY TRUE)
+endif(PETSC4PY_INCLUDE_DIRS)
+
+execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} -c "import petsc4py; print(petsc4py.get_include())"
+ OUTPUT_VARIABLE PETSC4PY_INCLUDE_DIRS
+ RESULT_VARIABLE PETSC4PY_NOT_FOUND
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+if(PETSC4PY_INCLUDE_DIRS)
+ set(PETSC4PY_FOUND TRUE)
+ set(PETSC4PY_INCLUDE_DIRS ${PETSC4PY_INCLUDE_DIRS} CACHE STRING "petsc4py include path")
+else(PETSC4PY_INCLUDE_DIRS)
+ set(PETSC4PY_FOUND FALSE)
+endif(PETSC4PY_INCLUDE_DIRS)
+
+if(PETSC4PY_FOUND)
+ execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} -c "import petsc4py; print(petsc4py.__version__)"
+ OUTPUT_VARIABLE PETSC4PY_VERSION
+ RESULT_VARIABLE PETSC4PY_NOT_FOUND
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ string(REPLACE "." ";" PETSC4PY_VERSION_LIST ${PETSC4PY_VERSION})
+ list(GET PETSC4PY_VERSION_LIST 0 PETSC4PY_VERSION_MAJOR)
+ list(GET PETSC4PY_VERSION_LIST 1 PETSC4PY_VERSION_MINOR)
+ if(NOT PETSC4PY_FIND_QUIETLY)
+ message(STATUS "petsc4py version ${PETSC4PY_VERSION} found")
+ endif(NOT PETSC4PY_FIND_QUIETLY)
+else(PETSC4PY_FOUND)
+ if(PETSC4PY_FIND_REQUIRED)
+ message(FATAL_ERROR "petsc4py missing")
+ endif(PETSC4PY_FIND_REQUIRED)
+endif(PETSC4PY_FOUND)
+
+mark_as_advanced(PETSC4PY_INCLUDE_DIRS, PETSC4PY_VERSION, PETSC4PY_VERSION_MAJOR, PETSC4PY_VERSION_MINOR)
+
+if (PETSc4py_FIND_VERSION)
+ # Check if version found is >= required version
+ if (NOT "${PETSC4PY_VERSION}" VERSION_LESS "${PETSc4py_FIND_VERSION}")
+ set(PETSC4PY_VERSION_OK TRUE)
+ endif()
+else()
+ # No specific version requested
+ set(PETSC4PY_VERSION_OK TRUE)
+endif()
+mark_as_advanced(PETSC4PY_VERSION_OK)
+
+# Standard package handling
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PETSc4py
+ "PETSc4py could not be found. Be sure to set PYTHONPATH appropriately."
+ PETSC4PY_INCLUDE_DIRS PETSC4PY_VERSION PETSC4PY_VERSION_OK)
+
+# Check petsc4py.i for PETSC_INT
+#if(PETSC4PY_INCLUDE_DIRS)
+# file(STRINGS "${PETSC4PY_INCLUDE_DIRS}/petsc4py/petsc4py.i" PETSC4PY_INT)
+# string(REGEX MATCH "SWIG_TYPECHECK_INT[0-9]+" PETSC4PY_INT "${PETSC4PY_INT}")
+# string(REPLACE "SWIG_TYPECHECK_INT" "" PETSC4PY_INT "${PETSC4PY_INT}")
+# math(EXPR PETSC_INT "${PETSC_INT_SIZE}*8")
+# if(NOT PETSC_INT STREQUAL PETSC4PY_INT)
+# message(STATUS "PETSC_INT = ${PETSC4PY_INT} ${PETSC_INT}")
+# message(STATUS " - does not match")
+# set(PETSC4PY_FOUND FALSE)
+# endif()
+#endif()
diff --git a/python/doc/Makefile b/python/doc/Makefile
new file mode 100644
index 0000000..812b55f
--- /dev/null
+++ b/python/doc/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+SPHINXPROJ = FEniCSProject
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
diff --git a/python/doc/source/api.rst b/python/doc/source/api.rst
index 7b40afa..8fb0dce 100644
--- a/python/doc/source/api.rst
+++ b/python/doc/source/api.rst
@@ -4,8 +4,27 @@ Python API reference
.. autosummary::
:toctree: _autogenerated
+ dolfin.cpp.adaptivity
+ dolfin.cpp.ale
dolfin.cpp.common
+ dolfin.cpp.fem
dolfin.cpp.function
+ dolfin.cpp.generation
+ dolfin.cpp.geometry
+ dolfin.cpp.graph
+ dolfin.cpp.io
+ dolfin.cpp.log
+ dolfin.cpp.math
dolfin.cpp.mesh
- dolfin.cpp.la
+ dolfin.cpp.multistage
+ dolfin.cpp.parameter
dolfin.cpp.refinement
+
+ dolfin.common
+ dolfin.fem
+ dolfin.function
+ dolfin.io
+ dolfin.jit
+ dolfin.la
+ dolfin.multistage
+ dolfin.parameter
diff --git a/python/dolfin/__init__.py b/python/dolfin/__init__.py
index a99a85a..e28b5ce 100644
--- a/python/dolfin/__init__.py
+++ b/python/dolfin/__init__.py
@@ -31,12 +31,15 @@ del sys
# del sys
# Import cpp modules
+from .cpp import __version__
+
from .cpp.common import (Variable, has_debug, has_hdf5, has_scotch,
- has_hdf5_parallel, has_mpi, has_petsc,
- has_parmetis, has_slepc, git_commit_hash,
- DOLFIN_EPS, DOLFIN_PI, TimingClear,
- TimingType, timing, timings, list_timings,
- dump_timings_to_xml)
+ has_hdf5_parallel, has_mpi, has_mpi4py,
+ has_petsc, has_petsc4py, has_parmetis,
+ has_slepc, has_slepc4py, git_commit_hash,
+ DOLFIN_EPS, DOLFIN_PI, TimingClear, TimingType,
+ timing, timings, list_timings, dump_timings_to_xml,
+ SubSystemsManager)
if has_hdf5():
from .cpp.adaptivity import TimeSeries
@@ -46,7 +49,7 @@ from .cpp.ale import ALE
from .cpp import MPI
from .cpp.function import (Expression, Constant, FunctionAXPY,
LagrangeInterpolator, FunctionAssigner,
- assign)
+ assign, MultiMeshFunction, MultiMeshFunctionSpace)
from .cpp.fem import (FiniteElement, DofMap, Assembler,
get_coordinates, create_mesh, set_coordinates,
vertex_to_dof_map, dof_to_vertex_map,
@@ -55,10 +58,12 @@ from .cpp.fem import (FiniteElement, DofMap, Assembler,
NonlinearVariationalSolver,
SparsityPatternBuilder)
-from .cpp.geometry import (BoundingBoxTree, Point,
- MeshPointIntersection, intersect)
+from .cpp.geometry import (BoundingBoxTree,
+ Point,
+ MeshPointIntersection,
+ intersect)
from .cpp.generation import (IntervalMesh, BoxMesh, RectangleMesh,
- UnitDiscMesh, UnitQuadMesh, UnitHexMesh,
+ UnitDiscMesh,
UnitTriangleMesh, UnitCubeMesh,
UnitSquareMesh, UnitIntervalMesh,
SphericalShellMesh)
@@ -97,7 +102,7 @@ from .cpp.mesh import (Mesh, MeshTopology, MeshGeometry, MeshEntity,
entities, vertices, SubDomain, BoundaryMesh,
MeshEditor, MeshQuality, SubMesh,
DomainBoundary, PeriodicBoundaryComputation,
- MeshTransformation, SubsetIterator)
+ MeshTransformation, SubsetIterator, MultiMesh)
from .cpp.nls import (NonlinearProblem, NewtonSolver, OptimisationProblem)
from .cpp.refinement import refine
@@ -121,8 +126,8 @@ from .fem.norms import norm, errornorm
from .fem.dirichletbc import DirichletBC, AutoSubDomain
from .fem.interpolation import interpolate
from .fem.projection import project
-from .fem.solving import (solve, LocalSolver,
- LinearVariationalProblem,
+from .fem.solvers import LocalSolver
+from .fem.solving import (solve, LinearVariationalProblem,
NonlinearVariationalProblem)
from .fem.formmanipulations import (derivative, adjoint, increase_order, tear)
@@ -136,11 +141,12 @@ from .function.function import Function
from .function.argument import (TestFunction, TrialFunction,
TestFunctions, TrialFunctions)
from .function.constant import Constant
-from .function.specialfunctions import (FacetNormal, CellSize,
- SpatialCoordinate, CellVolume,
- Circumradius, FacetArea,
- MeshCoordinates)
-from .function.expression import Expression, UserExpression
+from .function.specialfunctions import (MeshCoordinates, FacetArea, FacetNormal,
+ CellVolume, SpatialCoordinate, CellNormal,
+ CellDiameter, Circumradius,
+ MinCellEdgeLength, MaxCellEdgeLength,
+ MinFacetEdgeLength, MaxFacetEdgeLength)
+from .function.expression import Expression, UserExpression, CompiledExpression
# experimental
from .jit.pybind11jit import compile_cpp_code
@@ -148,9 +154,7 @@ from .jit.pybind11jit import compile_cpp_code
from .la import as_backend_type, la_index_dtype
from .mesh.ale import (compute_vertex_map, compute_edge_map,
init_parent_edge_indices)
-from .mesh.meshfunction import (MeshFunction, CellFunction,
- FacetFunction, FaceFunction,
- EdgeFunction, VertexFunction)
+from .mesh.meshfunction import (MeshFunction)
from .mesh.meshvaluecollection import MeshValueCollection
from .mesh.subdomain import CompiledSubDomain
@@ -166,7 +170,7 @@ from ufl import (FiniteElement, TensorElement, VectorElement,
split, cross, inner, dot, grad, curl, dx, div,
Measure, det, pi, sin, cos, tan, acos, asin, atan,
ln, exp, sqrt, bessel_I, bessel_J, bessel_K,
- bessel_Y, Dx, ds, dS, dP, interval, triangle,
+ bessel_Y, Dx, ds, dS, dP, dX, dC, interval, triangle,
tetrahedron, quadrilateral, hexahedron, avg, jump,
sym, tr, Identity, variable, diff, as_vector,
as_tensor, as_matrix, system, outer, dev, skew,
@@ -174,11 +178,6 @@ from ufl import (FiniteElement, TensorElement, VectorElement,
from ufl.formoperators import action
-# FIXME
-def has_petsc4py():
- return False
-
-
# FIXME: remove after transition
def has_pybind11():
return True
@@ -193,6 +192,7 @@ def mpi_comm_self():
def mpi_comm_world():
return MPI.comm_world
+
# FIXME: remove all these after transition
TimingClear_clear = TimingClear.clear
TimingClear_keep = TimingClear.keep
@@ -209,6 +209,12 @@ TensorLayout.Sparsity_SPARSE = TensorLayout.Sparsity.SPARSE
TensorLayout.Ghosts_GHOSTED = TensorLayout.Ghosts.GHOSTED
TensorLayout.Ghosts_UNGHOSTED = TensorLayout.Ghosts.UNGHOSTED
+CellType.Type_interval = CellType.Type.interval
+CellType.Type_triangle = CellType.Type.triangle
+CellType.Type_tetrahedron = CellType.Type.tetrahedron
+CellType.Type_quadrilateral = CellType.Type.quadrilateral
+CellType.Type_hexahedron = CellType.Type.hexahedron
+
if has_linear_algebra_backend('PETSc'):
PETScKrylovSolver.norm_type_default_norm = PETScKrylovSolver.norm_type.default_norm
PETScKrylovSolver.norm_type_natural = PETScKrylovSolver.norm_type.natural
diff --git a/python/dolfin/common/plotting.py b/python/dolfin/common/plotting.py
index 7b898bd..dddc99b 100644
--- a/python/dolfin/common/plotting.py
+++ b/python/dolfin/common/plotting.py
@@ -17,7 +17,6 @@
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
import os
-from distutils.version import StrictVersion
import dolfin
import dolfin.cpp as cpp
@@ -116,7 +115,7 @@ def mplot_function(ax, f, **kwargs):
if fvec.size() == mesh.num_cells():
# DG0 cellwise function
- C = fvec.array() # NB! Assuming here dof ordering matching cell numbering
+ C = fvec.get_local() # NB! Assuming here dof ordering matching cell numbering
if gdim == 2 and tdim == 2:
return ax.tripcolor(mesh2triang(mesh), C, **kwargs)
elif gdim == 3 and tdim == 2: # surface in 3d
@@ -129,13 +128,13 @@ def mplot_function(ax, f, **kwargs):
x = mesh.coordinates()[:, 0]
nv = len(x)
# Insert duplicate points to get piecewise constant plot
- xp = np.zeros(2*nv-2)
+ xp = np.zeros(2 * nv - 2)
xp[0] = x[0]
xp[-1] = x[-1]
- xp[1:2*nv-3:2] = x[1:-1]
- xp[2:2*nv-2:2] = x[1:-1]
+ xp[1:2 * nv - 3:2] = x[1:-1]
+ xp[2:2 * nv - 2:2] = x[1:-1]
Cp = np.zeros(len(xp))
- Cp[0:len(Cp)-1:2] = C
+ Cp[0:len(Cp) - 1:2] = C
Cp[1:len(Cp):2] = C
return ax.plot(xp, Cp, *kwargs)
# elif tdim == 1: # FIXME: Plot embedded line
@@ -198,11 +197,11 @@ def mplot_function(ax, f, **kwargs):
# Vector function, interpolated to vertices
w0 = f.compute_vertex_values(mesh)
nv = mesh.num_vertices()
- if len(w0) != gdim*nv:
+ if len(w0) != gdim * nv:
raise AttributeError('Vector length must match geometric dimension.')
X = mesh.coordinates()
X = [X[:, i] for i in range(gdim)]
- U = [w0[i*nv: (i + 1)*nv] for i in range(gdim)]
+ U = [w0[i * nv: (i + 1) * nv] for i in range(gdim)]
# Compute magnitude
C = U[0]**2
@@ -214,14 +213,6 @@ def mplot_function(ax, f, **kwargs):
if mode == "glyphs":
args = X + U + [C]
if gdim == 3:
- # 3d quiver plot works only since matplotlib 1.4
- import matplotlib
- if StrictVersion(matplotlib.__version__) < '1.4':
- cpp.warning('Matplotlib version %s does not support 3d '
- 'quiver plot. Continuing without plotting...'
- % matplotlib.__version__)
- return
-
length = kwargs.pop("length", 0.1)
return ax.quiver(*args, length=length, **kwargs)
else:
@@ -276,7 +267,7 @@ def _plot_matplotlib(obj, mesh, kwargs):
# Avoid importing pyplot until used
try:
import matplotlib.pyplot as plt
- except:
+ except Exception:
cpp.warning("matplotlib.pyplot not available, cannot plot.")
return
@@ -436,6 +427,7 @@ def plot(object, *args, **kwargs):
"piecewise linears.")
object = project(object, mesh=mesh)
mesh = object.function_space().mesh()
+ object = object._cpp_object
except Exception as e:
msg = "Don't know how to plot given object:\n %s\n" \
"and projection failed:\n %s" % (str(object), str(e))
diff --git a/python/dolfin/common/timer.py b/python/dolfin/common/timer.py
index f6f3d2b..1652b4c 100644
--- a/python/dolfin/common/timer.py
+++ b/python/dolfin/common/timer.py
@@ -51,7 +51,7 @@ class Timer(cpp.common.Timer):
may be printed using functions ``timing``, ``timings``,
``list_timings``, ``dump_timings_to_xml``, e.g.::
- list_timings(TimingClear_keep, [TimingType_wall, TimingType_user])
+ list_timings(TimingClear.keep, [TimingType.wall, TimingType.user])
"""
def __enter__(self):
@@ -72,9 +72,9 @@ def timed(task):
do_foo()
- list_timings(TimingClear_keep, [TimingType_wall, TimingType_user])
+ list_timings(TimingClear.keep, [TimingType.wall, TimingType.user])
- t = timing("Do Foo", TimingClear_clear)
+ t = timing("Do Foo", TimingClear.clear)
print("Do foo wall time: %s" % t[1])
"""
def decorator(func):
diff --git a/python/dolfin/fem/adaptivesolving.py b/python/dolfin/fem/adaptivesolving.py
index 73d2375..a1782f8 100644
--- a/python/dolfin/fem/adaptivesolving.py
+++ b/python/dolfin/fem/adaptivesolving.py
@@ -21,16 +21,16 @@ Adaptive*VariationalSolver classes
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-__all__ = ["AdaptiveLinearVariationalSolver",
- "AdaptiveNonlinearVariationalSolver",
- "generate_error_control", "generate_error_control_forms"]
-
import dolfin.cpp as cpp
from dolfin.fem.form import Form
from dolfin.fem.solving import LinearVariationalProblem
from dolfin.fem.solving import NonlinearVariationalProblem
from dolfin.fem.errorcontrolgenerator import DOLFINErrorControlGenerator
+__all__ = ["AdaptiveLinearVariationalSolver",
+ "AdaptiveNonlinearVariationalSolver",
+ "generate_error_control", "generate_error_control_forms"]
+
class AdaptiveLinearVariationalSolver(cpp.adaptivity.AdaptiveLinearVariationalSolver):
@@ -207,9 +207,7 @@ def generate_error_control_forms(problem, goal):
assert len(form.ufl_domains()) == 1, "Error control got as input a form with more than one domain!"
else:
- cpp.dolfin_error("adaptivesolving.py",
- "generate forms required for error control",
- "Unknown problem type (\"%s\")" % str(problem))
+ raise RuntimeError("Generate forms required for error control. Unknown problem type (\"{}\")".format(problem))
# Extract unknown Function from problem
u = problem.u_ufl
diff --git a/python/dolfin/fem/assembling.py b/python/dolfin/fem/assembling.py
index a51a971..ada8b2a 100644
--- a/python/dolfin/fem/assembling.py
+++ b/python/dolfin/fem/assembling.py
@@ -130,7 +130,7 @@ def assemble(form, tensor=None, form_compiler_parameters=None,
.. code-block:: python
# MeshFunction marking boundary parts
- boundary_markers = FacetFunction("size_t", mesh)
+ boundary_markers = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
# ... fill values in boundary_markers
# Measures with references to cell and boundary markers
@@ -161,8 +161,8 @@ def assemble(form, tensor=None, form_compiler_parameters=None,
.. code-block:: python
# MeshFunctions marking boundary and cell parts
- boundary_markers = FacetFunction("size_t", mesh)
- cell_markers = CellFunction("size_t", mesh)
+ boundary_markers = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
+ cell_markers = MeshFunction("size_t", mesh, mesh.topology().dim())
# ... fill values in boundary_markers
# Measures with references to cell and boundary markers
diff --git a/python/dolfin/fem/form.py b/python/dolfin/fem/form.py
index db44995..936f788 100644
--- a/python/dolfin/fem/form.py
+++ b/python/dolfin/fem/form.py
@@ -8,8 +8,8 @@
# version.
import ufl
-import ffc
import dolfin.cpp as cpp
+from dolfin.jit.jit import ffc_jit
class Form(cpp.fem.Form):
@@ -41,7 +41,8 @@ class Form(cpp.fem.Form):
# FIXME: add paths if dict entry already exists
form_compiler_parameters["external_include_dirs"] = d["include_dirs"]
- ufc_form = ffc.jit(form, form_compiler_parameters)
+ ufc_form = ffc_jit(form, form_compiler_parameters=form_compiler_parameters,
+ mpi_comm=mesh.mpi_comm())
ufc_form = cpp.fem.make_ufc_form(ufc_form[0])
function_spaces = [func.function_space()._cpp_object for func in form.arguments()]
diff --git a/python/dolfin/fem/formmanipulations.py b/python/dolfin/fem/formmanipulations.py
index dc7b4cd..f85519f 100644
--- a/python/dolfin/fem/formmanipulations.py
+++ b/python/dolfin/fem/formmanipulations.py
@@ -18,7 +18,6 @@
import ufl
import ufl.algorithms.elementtransformations
-import dolfin.cpp as cpp
from dolfin.function.functionspace import FunctionSpace
from dolfin.function.function import Function
from dolfin.function.argument import Argument
@@ -35,13 +34,10 @@ def adjoint(form, reordered_arguments=None):
# Extract form arguments
arguments = form.arguments()
if any(arg.part() is not None for arg in arguments):
- cpp.dolfin_error("formmanipulation.py",
- "compute adjoint of form",
- "parts not supported")
+ raise RuntimeError("Compute adjoint of form, parts not supported")
+
if not (len(arguments) == 2):
- cpp.dolfin_error("formmanipulation.py",
- "compute adjoint of form",
- "Form is not bilinear")
+ raise RuntimeError("Compute adjoint of form, form is not bilinear")
# Define new Argument(s) in the same spaces (NB: Order does not
# matter anymore here because number is absolute)
@@ -66,24 +62,16 @@ def derivative(form, u, du=None, coefficient_derivatives=None):
number = max([-1] + [arg.number() for arg in form_arguments]) + 1
if any(arg.part() is not None for arg in form_arguments):
- cpp.dolfin_error("formmanipulation.py",
- "compute derivative of form",
- "Cannot automatically create new Argument using "
- "parts, please supply one")
+ raise RuntimeError("Compute derivative of form, cannot automatically create new Argument using parts, please supply one")
part = None
if isinstance(u, Function):
V = u.function_space()
du = Argument(V, number, part)
elif isinstance(u, (list, tuple)) and all(isinstance(w, Function) for w in u):
- cpp.dolfin_error("formmanipulation.py",
- "take derivative of form w.r.t. a tuple of Coefficients",
- "Take derivative w.r.t. a single Coefficient on "
- "a mixed space instead.")
+ raise RuntimeError("Taking derivative of form w.r.t. a tuple of Coefficients. Take derivative w.r.t. a single Coefficient on a mixed space instead.")
else:
- cpp.dolfin_error("formmanipulation.py",
- "compute derivative of form w.r.t. '%s'" % u,
- "Supply Function as a Coefficient")
+ raise RuntimeError("Computing derivative of form w.r.t. '{}'. Supply Function as a Coefficient".format(u))
return ufl.derivative(form, u, du, coefficient_derivatives)
diff --git a/python/dolfin/fem/norms.py b/python/dolfin/fem/norms.py
index 8ad069b..54869d3 100644
--- a/python/dolfin/fem/norms.py
+++ b/python/dolfin/fem/norms.py
@@ -122,42 +122,26 @@ def norm(v, norm_type="L2", mesh=None):
elif isinstance(v, ufl.Coefficient):
if norm_type.lower() == "l2":
- M = v**2*dx
+ M = v**2 * dx
elif norm_type.lower() == "h1":
- M = (v**2 + grad(v)**2)*dx
+ M = (v**2 + grad(v)**2) * dx
elif norm_type.lower() == "h10":
- M = grad(v)**2*dx
+ M = grad(v)**2 * dx
elif norm_type.lower() == "hdiv":
- M = (v**2 + div(v)**2)*dx
+ M = (v**2 + div(v)**2) * dx
elif norm_type.lower() == "hdiv0":
- M = div(v)**2*dx
+ M = div(v)**2 * dx
elif norm_type.lower() == "hcurl":
- M = (v**2 + curl(v)**2)*dx
+ M = (v**2 + curl(v)**2) * dx
elif norm_type.lower() == "hcurl0":
- M = curl(v)**2*dx
- # else:
- # cpp.dolfin_error("norms.py",
- # "compute norm",
- # "Unknown norm type (\"%s\") for functions"
- # % str(norm_type))
- # else:
- # cpp.dolfin_error("norms.py",
- # "compute norm",
- # "Unknown object type. Must be a vector or a function")
-
- # Assemble value
- r = assemble(M)
-
- # Check value
- if r < 0.0:
- pass
- # cpp.dolfin_error("norms.py",
- # "compute norm",
- # "Square of norm is negative, might be a round-off error")
- elif r == 0.0:
- return 0.0
+ M = curl(v)**2 * dx
+ else:
+ raise ValueError("Unknown norm type {}".format(str(norm_type)))
else:
- return sqrt(r)
+ raise TypeError("Do not know how to compute norm of {}".format(str(v)))
+
+ # Assemble value and return
+ return sqrt(assemble(M))
def errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None):
@@ -230,23 +214,18 @@ def errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None):
if hasattr(u, "_cpp_object") and mesh is None:
mesh = u._cpp_object.function_space().mesh()
if mesh is None:
- cpp.dolfin_error("norms.py",
- "compute error norm",
- "Missing mesh")
+ raise RuntimeError("Cannot compute error norm. Missing mesh.")
# Get rank
if not u.ufl_shape == uh.ufl_shape:
- cpp.dolfin_error("norms.py",
- "compute error norm",
- "Value shapes don't match")
+ raise RuntimeError("Cannot compute error norm. Value shapes do not match.")
+
shape = u.ufl_shape
rank = len(shape)
# Check that uh is associated with a finite element
if uh.ufl_element().degree() is None:
- cpp.dolfin_error("norms.py",
- "compute error norm",
- "Function uh must have a finite element")
+ raise RuntimeError("Cannot compute error norm. Function uh must have a finite element.")
# Degree for interpolation space. Raise degree with respect to uh.
degree = uh.ufl_element().degree() + degree_rise
diff --git a/python/dolfin/fem/problem.py b/python/dolfin/fem/problem.py
new file mode 100644
index 0000000..b8f203e
--- /dev/null
+++ b/python/dolfin/fem/problem.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2011-2017 Anders Logg and Garth N. Wells
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin.cpp as cpp
+from dolfin.fem.form import Form
+
+
+class LinearVariationalProblem(cpp.fem.LinearVariationalProblem):
+
+ def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None):
+ """Create linear variational problem a(u, v) = L(v).
+
+ An optional argument bcs may be passed to specify boundary
+ conditions.
+
+ Another optional argument form_compiler_parameters may be
+ specified to pass parameters to the form compiler.
+
+ """
+
+ if bcs is None:
+ bcs = []
+ elif not isinstance(bcs, (list, tuple)):
+ bcs = [bcs]
+
+ # Store input UFL forms and solution Function
+ self.a_ufl = a
+ self.L_ufl = L
+ self.u_ufl = u
+
+ # Store form compiler parameters
+ form_compiler_parameters = form_compiler_parameters or {}
+ self.form_compiler_parameters = form_compiler_parameters
+
+ # Wrap forms (and check if linear form L is empty)
+ if L.empty():
+ L = cpp.fem.Form(1, 0)
+ else:
+ L = Form(L, form_compiler_parameters=form_compiler_parameters)
+ a = Form(a, form_compiler_parameters=form_compiler_parameters)
+
+ # Initialize C++ base class
+ cpp.fem.LinearVariationalProblem.__init__(self, a, L, u._cpp_object, bcs)
+
+
+class NonlinearVariationalProblem(cpp.fem.NonlinearVariationalProblem):
+
+ def __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None):
+ """Create nonlinear variational problem F(u; v) = 0.
+
+ Optional arguments bcs and J may be passed to specify boundary
+ conditions and the Jacobian J = dF/du.
+
+ Another optional argument form_compiler_parameters may be
+ specified to pass parameters to the form compiler.
+
+ """
+
+ if bcs is None:
+ bcs = []
+ elif not isinstance(bcs, (list, tuple)):
+ bcs = [bcs]
+
+ # Store input UFL forms and solution Function
+ self.F_ufl = F
+ self.J_ufl = J
+ self.u_ufl = u
+
+ # Store form compiler parameters
+ form_compiler_parameters = form_compiler_parameters or {}
+ self.form_compiler_parameters = form_compiler_parameters
+
+ # Wrap forms
+ F = Form(F, form_compiler_parameters=form_compiler_parameters)
+ if J is not None:
+ J = Form(J, form_compiler_parameters=form_compiler_parameters)
+
+ # Initialize C++ base class
+ cpp.fem.NonlinearVariationalProblem.__init__(self, F, u._cpp_object, bcs, J)
diff --git a/python/dolfin/fem/projection.py b/python/dolfin/fem/projection.py
index 42f8f76..176b13b 100644
--- a/python/dolfin/fem/projection.py
+++ b/python/dolfin/fem/projection.py
@@ -102,8 +102,8 @@ def project(v, V=None, bcs=None, mesh=None,
# Define variational problem for projection
w = TestFunction(V)
Pv = TrialFunction(V)
- a = ufl.inner(w, Pv)*dx
- L = ufl.inner(w, v)*dx
+ a = ufl.inner(w, Pv) * dx
+ L = ufl.inner(w, v) * dx
# Assemble linear system
A, b = assemble_system(a, L, bcs=bcs,
@@ -142,10 +142,7 @@ def _extract_function_space(expression, mesh):
break
if mesh is None:
- cpp.dolfin_error("projection.py",
- "extract function space",
- "Unable to project expression, can't find "
- "a suitable mesh.")
+ raise RuntimeError("Unable to project expression, cannot find a suitable mesh.")
# Create function space
shape = expression.ufl_shape
@@ -156,8 +153,6 @@ def _extract_function_space(expression, mesh):
elif len(shape) == 2:
V = TensorFunctionSpace(mesh, "Lagrange", 1, shape=shape)
else:
- cpp.dolfin_error("projection.py",
- "extract function space",
- "Unhandled rank, shape is %s." % (shape,))
+ raise RuntimeError("Unhandled rank, shape is {}.".format((shape,)))
return V
diff --git a/python/dolfin/fem/solvers.py b/python/dolfin/fem/solvers.py
new file mode 100644
index 0000000..2638963
--- /dev/null
+++ b/python/dolfin/fem/solvers.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2011 Anders Logg
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin.cpp as cpp
+from dolfin.fem.form import Form
+
+__all__ = ["LocalSolver"]
+
+
+class LocalSolver(cpp.fem.LocalSolver):
+
+ def __init__(self, a, L=None, solver_type=cpp.fem.LocalSolver.SolverType.LU):
+ """Create a local (cell-wise) solver for a linear variational problem
+ a(u, v) = L(v).
+
+ """
+
+ # Store input UFL forms and solution Function
+ self.a_ufl = a
+ self.L_ufl = L
+
+ # Wrap as DOLFIN forms
+ a = Form(a)
+ if L is None:
+ # Initialize C++ base class
+ cpp.fem.LocalSolver.__init__(self, a, solver_type)
+ else:
+ if L.empty():
+ L = cpp.fem.Form(1, 0)
+ else:
+ L = Form(L)
+
+ # Initialize C++ base class
+ cpp.fem.LocalSolver.__init__(self, a, L, solver_type)
diff --git a/python/dolfin/fem/solving.py b/python/dolfin/fem/solving.py
index 0626aba..255a16c 100644
--- a/python/dolfin/fem/solving.py
+++ b/python/dolfin/fem/solving.py
@@ -27,54 +27,22 @@ from dolfin.function.function import Function
from dolfin.fem.form import Form
import dolfin.fem.formmanipulations as formmanipulations
from dolfin.fem.formmanipulations import derivative
+import dolfin.la.solver
+from dolfin.fem.problem import LinearVariationalProblem, NonlinearVariationalProblem
__all__ = ["LinearVariationalProblem",
"LinearVariationalSolver",
- "LocalSolver",
"NonlinearVariationalProblem",
"NonlinearVariationalSolver",
"solve"]
-# Problem classes need special handling since they involve JIT
-# compilation
-
-
-class LinearVariationalProblem(cpp.fem.LinearVariationalProblem):
-
- def __init__(self, a, L, u, bcs=None,
- form_compiler_parameters=None):
- """
- Create linear variational problem a(u, v) = L(v).
-
- An optional argument bcs may be passed to specify boundary
- conditions.
-
- Another optional argument form_compiler_parameters may be
- specified to pass parameters to the form compiler.
- """
- # Extract and check arguments
- u = _extract_u(u)
- bcs = _extract_bcs(bcs)
+# FIXME: The code is this file is outrageously convolute because one
+# function an do a number of unrelated operations, depending in the
+# arguments passed.
- # Store input UFL forms and solution Function
- self.a_ufl = a
- self.L_ufl = L
- self.u_ufl = u
-
- # Store form compiler parameters
- form_compiler_parameters = form_compiler_parameters or {}
- self.form_compiler_parameters = form_compiler_parameters
-
- # Wrap forms (and check if linear form L is empty)
- if L.empty():
- L = cpp.fem.Form(1, 0)
- else:
- L = Form(L, form_compiler_parameters=form_compiler_parameters)
- a = Form(a, form_compiler_parameters=form_compiler_parameters)
-
- # Initialize C++ base class
- cpp.fem.LinearVariationalProblem.__init__(self, a, L, u._cpp_object, bcs)
+# Problem classes need special handling since they involve JIT
+# compilation
class LocalSolver(cpp.fem.LocalSolver):
@@ -104,50 +72,12 @@ class LocalSolver(cpp.fem.LocalSolver):
cpp.fem.LocalSolver.__init__(self, a, L, solver_type)
-class NonlinearVariationalProblem(cpp.fem.NonlinearVariationalProblem):
-
- # Reuse C++ doc-string
- __doc__ = cpp.fem.NonlinearVariationalProblem.__doc__
-
- def __init__(self, F, u, bcs=None, J=None,
- form_compiler_parameters=None):
- """
- Create nonlinear variational problem F(u; v) = 0.
-
- Optional arguments bcs and J may be passed to specify boundary
- conditions and the Jacobian J = dF/du.
-
- Another optional argument form_compiler_parameters may be
- specified to pass parameters to the form compiler.
- """
-
- # Extract and check arguments
- u = _extract_u(u)
- bcs = _extract_bcs(bcs)
-
- # Store input UFL forms and solution Function
- self.F_ufl = F
- self.J_ufl = J
- self.u_ufl = u
-
- # Store form compiler parameters
- form_compiler_parameters = form_compiler_parameters or {}
- self.form_compiler_parameters = form_compiler_parameters
-
- # Wrap forms
- F = Form(F, form_compiler_parameters=form_compiler_parameters)
- if J is not None:
- J = Form(J, form_compiler_parameters=form_compiler_parameters)
-
- # Initialize C++ base class
- cpp.fem.NonlinearVariationalProblem.__init__(self, F, u._cpp_object, bcs, J)
-
# FIXME: The import here are here to avoid a circular dependency
# (ugly, should fix)
# Solver classes are imported directly
-from dolfin.cpp.fem import LinearVariationalSolver, NonlinearVariationalSolver
-from dolfin.fem.adaptivesolving import AdaptiveLinearVariationalSolver
-from dolfin.fem.adaptivesolving import AdaptiveNonlinearVariationalSolver
+from dolfin.cpp.fem import LinearVariationalSolver, NonlinearVariationalSolver # noqa
+from dolfin.fem.adaptivesolving import AdaptiveLinearVariationalSolver # noqa
+from dolfin.fem.adaptivesolving import AdaptiveNonlinearVariationalSolver # noqa
# Solve function handles both linear systems and variational problems
@@ -292,13 +222,9 @@ def solve(*args, **kwargs):
# Default case, just call the wrapped C++ solve function
else:
if kwargs:
- raise RuntimeError("Not expecting keyword arguments when solving linear algebra problem")
- # cpp.dolfin_error("solving.py",
- # "solve linear algebra problem",
- # "Not expecting keyword arguments when solving "
- # "linear algebra problem")
+ raise RuntimeError("Not expecting keyword arguments when solving linear algebra problem.")
- return cpp.la.solve(*args)
+ return dolfin.la.solver.solve(*args)
def _solve_varproblem(*args, **kwargs):
@@ -351,9 +277,7 @@ def _solve_varproblem_adaptive(*args, **kwargs):
# Check that we received the goal functional
if M is None:
- cpp.dolfin_error("solving.py",
- "solve variational problem adaptively",
- "Missing goal functional")
+ raise RuntimeError("Cannot solve variational problem adaptively. Missing goal functional")
# Solve linear variational problem
if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form):
@@ -395,26 +319,14 @@ def _extract_args(*args, **kwargs):
"form_compiler_parameters", "solver_parameters"]
for kwarg in kwargs.keys():
if kwarg not in valid_kwargs:
- raise RuntimeError("Illegal keyword argument")
- # cpp.dolfin_error("solving.py",
- # "solve variational problem",
- # "Illegal keyword argument \"%s\"; valid keywords are %s" %
- # (kwarg,
- # ", ".join("\"%s\"" % kwarg for kwarg in valid_kwargs)))
+ raise RuntimeError("Solve variational problem. Illegal keyword argument \'{}\'.".format(kwarg))
# Extract equation
if not len(args) >= 2:
- raise RuntimeError("Missing argument")
- # cpp.dolfin_error("solving.py",
- # "solve variational problem",
- # "Missing arguments, expecting solve(lhs == rhs, "
- # "u, bcs=bcs), where bcs is optional")
+ raise RuntimeError("Solve variational problem. Missing arguments, expecting solve(lhs == rhs, u, bcs=bcs), where bcs is optional")
+
if len(args) > 3:
- raise RuntimeError("Too many arguments")
- # cpp.dolfin_error("solving.py",
- # "solve variational problem",
- # "Too many arguments, expecting solve(lhs == rhs, "
- # "u, bcs=bcs), where bcs is optional")
+ raise RuntimeError("Solve variational problem. Too many arguments, expecting solve(lhs == rhs, u, bcs=bcs), where bcs is optional")
# Extract equation
eq = _extract_eq(args[0])
@@ -433,23 +345,17 @@ def _extract_args(*args, **kwargs):
# Extract Jacobian
J = kwargs.get("J", None)
if J is not None and not isinstance(J, ufl.Form):
- cpp.dolfin_error("solving.py",
- "solve variational problem",
- "Expecting Jacobian J to be a UFL Form")
+ raise RuntimeError("Solve variational problem. Expecting Jacobian J to be a UFL Form.")
# Extract tolerance
tol = kwargs.get("tol", None)
if tol is not None and not (isinstance(tol, (float, int)) and tol >= 0.0):
- cpp.dolfin_error("solving.py",
- "solve variational problem",
- "Expecting tolerance tol to be a non-negative number")
+ raise RuntimeError("Solve variational problem. Expecting tolerance tol to be a non-negative number.")
# Extract functional
M = kwargs.get("M", None)
if M is not None and not isinstance(M, ufl.Form):
- cpp.dolfin_error("solving.py",
- "solve variational problem",
- "Expecting goal functional M to be a UFL Form")
+ raise RuntimeError("Solve variational problem. Expecting goal functional M to be a UFL Form.")
# Extract parameters
form_compiler_parameters = kwargs.get("form_compiler_parameters", {})
@@ -461,9 +367,8 @@ def _extract_args(*args, **kwargs):
def _extract_eq(eq):
"Extract and check argument eq"
if not isinstance(eq, ufl.classes.Equation):
- cpp.dolfin_error("solving.py",
- "solve variational problem",
- "Expecting first argument to be an Equation")
+ raise RuntimeError("Solve variational problem. Expecting first argument to be an Equation.")
+
return eq
@@ -477,7 +382,7 @@ def _extract_u(u):
if isinstance(u, Function):
return u
- raise RuntimeError("Expecting second argument to be a Function")
+ raise RuntimeError("Expecting second argument to be a Function.")
# cpp.dolfin_error("solving.py",
# "solve variational problem",
# "Expecting second argument to be a Function")
@@ -492,8 +397,6 @@ def _extract_bcs(bcs):
bcs = [bcs]
for bc in bcs:
if not isinstance(bc, cpp.fem.DirichletBC):
- raise RuntimeError("Unable to extract boundary condition arguments")
- # cpp.dolfin_error("solving.py",
- # "solve variational problem",
- # "Unable to extract boundary condition arguments")
+ raise RuntimeError("solve variational problem. Unable to extract boundary condition arguments")
+
return bcs
diff --git a/python/dolfin/function/argument.py b/python/dolfin/function/argument.py
index ab6b371..5b045ac 100644
--- a/python/dolfin/function/argument.py
+++ b/python/dolfin/function/argument.py
@@ -19,15 +19,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-__all__ = ["TestFunction", "TrialFunction", "Argument",
- "TestFunctions", "TrialFunctions"]
-
-import types
import ufl
-import dolfin.cpp as cpp
from .functionspace import FunctionSpace
-#--- Subclassing of ufl.{Basis, Trial, Test}Function ---
+__all__ = ["TestFunction", "TrialFunction", "Argument",
+ "TestFunctions", "TrialFunctions"]
+
+# --- Subclassing of ufl.{Basis, Trial, Test}Function ---
# TODO: Update this message to clarify dolfin.FunctionSpace vs
# ufl.FunctionSpace
@@ -38,6 +36,7 @@ element space and is only used in standalone .ufl files, while the
FunctionSpace provides a full discrete function space over a given
mesh and should be used in dolfin programs in Python. """
+
class Argument(ufl.Argument):
"""UFL value: Representation of an argument to a form.
@@ -93,7 +92,7 @@ def TrialFunction(V, part=None):
return Argument(V, 1, part)
-#--- TestFunctions and TrialFunctions ---
+# --- TestFunctions and TrialFunctions ---
def Arguments(V, number):
"""UFL value: Create an Argument in a mixed space, and return a
diff --git a/python/dolfin/function/constant.py b/python/dolfin/function/constant.py
index e50c4d2..2c8e6e0 100644
--- a/python/dolfin/function/constant.py
+++ b/python/dolfin/function/constant.py
@@ -87,6 +87,8 @@ class Constant(ufl.Coefficient):
self.rename(name, "a Constant")
def assign(self, x):
+ if isinstance(x, Constant):
+ x = x._cpp_object
return self._cpp_object.assign(x)
def cell(self):
diff --git a/python/dolfin/function/expression.py b/python/dolfin/function/expression.py
index dbc9582..c460970 100644
--- a/python/dolfin/function/expression.py
+++ b/python/dolfin/function/expression.py
@@ -7,11 +7,6 @@
# either version 3 of the License, or (at your option) any later
# version.
-
-__all__ = ["UserExpression"]
-
-import hashlib
-from functools import reduce
import types
import numpy
import ufl
@@ -21,6 +16,8 @@ from ufl.utils.indexflattening import (flatten_multiindex,
import dolfin.cpp as cpp
import dolfin.function.jit as jit
+__all__ = ["UserExpression"]
+
def _select_element(family, cell, degree, value_shape):
"""Select finite element type for cases where user has not provided a
@@ -54,6 +51,7 @@ class _InterfaceExpression(cpp.function.Expression):
# Wrap eval functions
def wrapped_eval(self, values, x):
self.user_expression.eval(values, x)
+
def wrapped_eval_cell(self, values, x, cell):
self.user_expression.eval_cell(values, x, cell)
@@ -97,7 +95,7 @@ class BaseExpression(ufl.Coefficient):
def ufl_evaluate(self, x, component, derivatives):
"""Function used by ufl to evaluate the Expression"""
- assert derivatives == () # TODO: Handle derivatives
+ assert derivatives == () # TODO: Handle derivatives
if component:
shape = self.ufl_shape
@@ -112,8 +110,8 @@ class BaseExpression(ufl.Coefficient):
# Scalar evaluation
return self(*x)
- #def __call__(self, x):
- # return self._cpp_object(x)
+ # def __call__(self, x):
+ # return self._cpp_object(x)
def __call__(self, *args, **kwargs):
# GNW: This function is copied from the old DOLFIN Python
# code. It is far too complicated. There is no need to provide
@@ -143,7 +141,7 @@ class BaseExpression(ufl.Coefficient):
if not isinstance(values, numpy.ndarray):
raise TypeError("expected a NumPy array for 'values'")
if len(values) != value_size or not numpy.issubdtype(values.dtype, 'd'):
- raise TypeError("expected a double NumPy array of length"\
+ raise TypeError("expected a double NumPy array of length"
" %d for return values." % value_size)
values_provided = True
else:
@@ -171,7 +169,7 @@ class BaseExpression(ufl.Coefficient):
# Convert it to an 1D numpy array
try:
x = numpy.fromiter(x, 'd')
- except (TypeError, ValueError, AssertionError) as e:
+ except (TypeError, ValueError, AssertionError):
raise TypeError("expected scalar arguments for the coordinates")
if len(x) == 0:
@@ -186,7 +184,7 @@ class BaseExpression(ufl.Coefficient):
pass
else:
if len(x) != dim:
- raise TypeError("expected the geometry argument to be of "\
+ raise TypeError("expected the geometry argument to be of "
"length %d" % dim)
# The actual evaluation
@@ -238,7 +236,7 @@ class UserExpression(BaseExpression):
domain = kwargs.pop("domain", None)
name = kwargs.pop("name", None)
label = kwargs.pop("label", None)
- mpi_comm = kwargs.pop("mpi_comm", None)
+ # mpi_comm = kwargs.pop("mpi_comm", None)
if (len(kwargs) > 0):
raise RuntimeError("Invalid keyword argument")
@@ -289,9 +287,65 @@ class ExpressionParameters(object):
return key in self._params
def update(self, params):
- for k,v in dict(params).items():
+ for k, v in dict(params).items():
self[k] = v
+
+class CompiledExpression(BaseExpression):
+ """Wrap a compiled module of type cpp.Expression"""
+
+ def __init__(self, cpp_module=None, **kwargs):
+
+ # Remove arguments that are used in Expression creation
+ element = kwargs.pop("element", None)
+ degree = kwargs.pop("degree", None)
+ cell = kwargs.pop("cell", None)
+ domain = kwargs.pop("domain", None)
+ name = kwargs.pop("name", None)
+ label = kwargs.pop("label", None)
+ # mpi_comm = kwargs.pop("mpi_comm", None)
+
+ if not isinstance(cpp_module, cpp.function.Expression):
+ raise RuntimeError("Must supply compiled C++ Expression module to CompiledExpression")
+ else:
+ self._cpp_object = cpp_module
+
+ params = kwargs
+ for k, val in params.items():
+ if not isinstance(k, str):
+ raise KeyError("User Parameter key must be a string")
+ if not hasattr(self._cpp_object, k):
+ raise AttributeError("Compiled module does not have attribute %s", k)
+ setattr(self._cpp_object, k, val)
+
+ if element and degree:
+ raise RuntimeError("Cannot specify an element and a degree for Expressions.")
+
+ # Deduce element type if not provided
+ if element is None:
+ if degree is None:
+ raise KeyError("Must supply element or degree")
+ value_shape = tuple(self.value_dimension(i)
+ for i in range(self.value_rank()))
+ if domain is not None and cell is None:
+ cell = domain.ufl_cell()
+ element = _select_element(family=None, cell=cell, degree=degree,
+ value_shape=value_shape)
+
+ BaseExpression.__init__(self, cell=cell, element=element, domain=domain,
+ name=name, label=label)
+
+ def __getattr__(self, name):
+ if hasattr(self._cpp_object, name):
+ return getattr(self._cpp_object, name)
+
+ def __setattr__(self, name, value):
+ if name.startswith("_"):
+ super().__setattr__(name, value)
+ elif hasattr(self._cpp_object, name):
+ setattr(self._cpp_object, name, value)
+
+
class Expression(BaseExpression):
"""JIT Expressions"""
@@ -308,13 +362,17 @@ class Expression(BaseExpression):
domain = kwargs.pop("domain", None)
name = kwargs.pop("name", None)
label = kwargs.pop("label", None)
- mpi_comm = kwargs.pop("mpi_comm", None)
+ # FIXME: feed mpi_comm through to JIT
+ # mpi_comm = kwargs.pop("mpi_comm", None)
- if cpp_code is not None:
+ if not isinstance(cpp_code, (str, tuple, list)):
+ raise RuntimeError("Must supply C++ code to Expression. You may want to use UserExpression")
+ else:
params = kwargs
for k in params:
if not isinstance(k, str):
- raise KeyError("User Parameter key must be a string")
+ raise KeyError("User parameter key must be a string")
+
self._cpp_object = jit.compile_expression(cpp_code, params)
self._parameters = ExpressionParameters(self._cpp_object, params)
diff --git a/python/dolfin/function/function.py b/python/dolfin/function/function.py
index 608e2a2..57a9807 100644
--- a/python/dolfin/function/function.py
+++ b/python/dolfin/function/function.py
@@ -19,10 +19,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-import types
import numpy as np
import ufl
from ufl.classes import ComponentTensor, Sum, Product, Division
+from ufl.utils.indexflattening import shape_to_strides, flatten_multiindex
import dolfin.cpp as cpp
import dolfin.la as la
from dolfin.function.functionspace import FunctionSpace
@@ -33,6 +33,7 @@ from dolfin.function.constant import Constant
def _assign_error():
raise RuntimeError("Expected only linear combinations of Functions in the same FunctionSpaces")
+
def _check_mul_and_division(e, linear_comb, scalar_weight=1.0, multi_index=None):
"""
Utility func for checking division and multiplication of a Function
@@ -41,12 +42,12 @@ def _check_mul_and_division(e, linear_comb, scalar_weight=1.0, multi_index=None)
from ufl.constantvalue import ScalarValue
from ufl.classes import ComponentTensor, MultiIndex, Indexed
from ufl.algebra import Division, Product, Sum
- #ops = e.ufl_operands
+ # ops = e.ufl_operands
# FIXME: What should be checked!?
# martinal: This code has never done anything sensible,
# but I don't know what it was supposed to do so I can't fix it.
- #same_multi_index = lambda x, y: (x.ufl_free_indices == y.ufl_free_indices \
+ # same_multi_index = lambda x, y: (x.ufl_free_indices == y.ufl_free_indices \
# and x.ufl_index_dimensions == y.ufl_index_dimensions)
assert isinstance(scalar_weight, float)
@@ -55,9 +56,9 @@ def _check_mul_and_division(e, linear_comb, scalar_weight=1.0, multi_index=None)
if isinstance(e, Product):
for i, op in enumerate(e.ufl_operands):
if isinstance(op, ScalarValue) or \
- (isinstance(op, Constant) and op.value_size()==1):
+ (isinstance(op, Constant) and op.value_size() == 1):
scalar = op
- expr = e.ufl_operands[1-i]
+ expr = e.ufl_operands[1 - i]
break
else:
_assign_error()
@@ -65,8 +66,8 @@ def _check_mul_and_division(e, linear_comb, scalar_weight=1.0, multi_index=None)
scalar_weight *= float(scalar)
elif isinstance(e, Division):
expr, scalar = e.ufl_operands
- if not (isinstance(scalar, ScalarValue) or \
- isinstance(scalar, Constant) and scalar.value_rank()==1):
+ if not (isinstance(scalar, ScalarValue) or
+ isinstance(scalar, Constant) and scalar.value_rank() == 1):
_assign_error()
scalar_weight /= float(scalar)
else:
@@ -82,7 +83,7 @@ def _check_mul_and_division(e, linear_comb, scalar_weight=1.0, multi_index=None)
# Unpack Indexed and check equality with passed multi_index
expr, multi_index2 = expr.ufl_operands
assert isinstance(multi_index2, MultiIndex)
- #if not same_multi_index(multi_index, multi_index2):
+ # if not same_multi_index(multi_index, multi_index2):
# _assign_error()
if isinstance(expr, Function):
@@ -92,7 +93,7 @@ def _check_mul_and_division(e, linear_comb, scalar_weight=1.0, multi_index=None)
# If componentTensor we need to unpack the MultiIndices
if isinstance(expr, ComponentTensor):
expr, multi_index = expr.ufl_operands
- #if not same_multi_index(multi_index, multi_index2):
+ # if not same_multi_index(multi_index, multi_index2):
# _error()
if isinstance(expr, (Product, Division)):
@@ -136,7 +137,7 @@ def _check_and_extract_functions(e, linear_comb=None, scalar_weight=1.0,
# If not Product or Division we expect Sum
elif isinstance(e, Sum):
for op in e.ufl_operands:
- linear_comb = _check_and_extract_functions(op, linear_comb, \
+ linear_comb = _check_and_extract_functions(op, linear_comb,
scalar_weight, multi_index)
else:
@@ -162,7 +163,7 @@ def _check_and_contract_linear_comb(expr, self, multi_index):
# Check if the exact same Function is already present
ind = funcs.index(func)
weights[ind] += weight
- except:
+ except Exception:
funcs.append(func)
weights.append(weight)
@@ -195,7 +196,7 @@ class Function(ufl.Coefficient):
raise RuntimeError("No subfunctions to extract")
if not i < num_sub_spaces:
raise RuntimeError("Can only extract subfunctions "
- "with i = 0..%d"% num_sub_spaces)
+ "with i = 0..%d" % num_sub_spaces)
self._cpp_object = cpp.function.Function(other._cpp_object, i)
ufl.Coefficient.__init__(self, self.function_space().ufl_function_space(),
count=self._cpp_object.id())
@@ -203,7 +204,9 @@ class Function(ufl.Coefficient):
raise TypeError("expected one or two arguments when "
"instantiating from another Function")
elif isinstance(args[0], cpp.function.Function):
- raise RuntimeError("Construction from a cpp function not implemented yet")
+ self._cpp_object = args[0]
+ ufl.Coefficient.__init__(self, self.function_space().ufl_function_space(),
+ count=self._cpp_object.id())
elif isinstance(args[0], FunctionSpace):
V = args[0]
@@ -251,14 +254,14 @@ class Function(ufl.Coefficient):
"""Function used by ufl to evaluate the Expression"""
# FIXME: same as dolfin.expression.Expression version. Find
# way to re-use.
- assert derivatives == () # TODO: Handle derivatives
+ assert derivatives == () # TODO: Handle derivatives
if component:
shape = self.ufl_shape
assert len(shape) == len(component)
- value_size = product(shape)
+ value_size = ufl.product(shape)
index = flatten_multiindex(component, shape_to_strides(shape))
- values = numpy.zeros(value_size)
+ values = np.zeros(value_size)
# FIXME: use a function with a return value
self(*x, values=values)
return values[index]
@@ -274,7 +277,7 @@ class Function(ufl.Coefficient):
# Deprecate as many options as possible, and maybe share with
# dolfin.expression.Expresssion.
- if len(args)==0:
+ if len(args) == 0:
raise TypeError("expected at least 1 argument")
# Test for ufl restriction
@@ -295,9 +298,9 @@ class Function(ufl.Coefficient):
if not isinstance(values, np.ndarray):
raise TypeError("expected a NumPy array for 'values'")
if len(values) != value_size or \
- not np.issubdtype(values.dtype, 'd'):
- raise TypeError("expected a double NumPy array of length"\
- " %d for return values."%value_size)
+ not np.issubdtype(values.dtype, 'd'):
+ raise TypeError("expected a double NumPy array of length"
+ " %d for return values." % value_size)
values_provided = True
else:
values_provided = False
@@ -320,15 +323,15 @@ class Function(ufl.Coefficient):
# Convert it to an 1D numpy array
try:
x = np.fromiter(x, 'd')
- except (TypeError, ValueError, AssertionError) as e:
+ except (TypeError, ValueError, AssertionError):
raise TypeError("expected scalar arguments for the coordinates")
if len(x) == 0:
raise TypeError("coordinate argument too short")
if len(x) != dim:
- raise TypeError("expected the geometry argument to be of "\
- "length %d"%dim)
+ raise TypeError("expected the geometry argument to be of "
+ "length %d" % dim)
# The actual evaluation
self._cpp_object.eval(values, x)
@@ -339,7 +342,7 @@ class Function(ufl.Coefficient):
return values
- #def _assign(self, u):
+ # def _assign(self, u):
# if isinstance(u, cpp.function.FunctionAXPY):
# self._cpp_object._assign(u)
@@ -361,8 +364,11 @@ class Function(ufl.Coefficient):
else:
self._cpp_object.interpolate(u)
- def compute_vertex_values(self, mesh):
- return self._cpp_object.compute_vertex_values(mesh)
+ def compute_vertex_values(self, mesh=None):
+ if mesh is not None:
+ return self._cpp_object.compute_vertex_values(mesh)
+ else:
+ return self._cpp_object.compute_vertex_values()
def set_allow_extrapolation(self, value):
self._cpp_object.set_allow_extrapolation(value)
@@ -492,7 +498,7 @@ class Function(ufl.Coefficient):
if num_sub_spaces == 1:
raise RuntimeError("No subfunctions to extract")
if not i < num_sub_spaces:
- raise RuntimeError("Can only extract subfunctions with i = 0..%d" \
+ raise RuntimeError("Can only extract subfunctions with i = 0..%d"
% num_sub_spaces)
# Create and instantiate the Function
@@ -503,7 +509,6 @@ class Function(ufl.Coefficient):
else:
return Function(self, i, name='%s-%d' % (str(self), i))
-
def split(self, deepcopy=False):
"""Extract any sub functions.
diff --git a/python/dolfin/function/functionspace.py b/python/dolfin/function/functionspace.py
index aad8f38..0127c8b 100644
--- a/python/dolfin/function/functionspace.py
+++ b/python/dolfin/function/functionspace.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
-"""Main module for DOLFIN"""
# Copyright (C) 2017 Chris N. Richardson and Garth N. Wells
#
@@ -7,16 +6,14 @@
# either version 3 of the License, or (at your option) any later
# version.
-import types
-import ffc
import ufl
import dolfin.cpp as cpp
-from . import function
+from dolfin.jit.jit import ffc_jit
class FunctionSpace(ufl.FunctionSpace):
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs):
"""Create finite element function space."""
if len(args) == 1:
@@ -25,10 +22,10 @@ class FunctionSpace(ufl.FunctionSpace):
self._init_from_cpp(*args, **kwargs)
else:
if len(args) == 0 or not isinstance(args[0], cpp.mesh.Mesh):
- #cpp.dolfin_error("functionspace.py",
- # "create function space",
- # "Illegal argument, not a mesh: "
- # + str(args[0]))
+ # cpp.dolfin_error("functionspace.py",
+ # "create function space",
+ # "Illegal argument, not a mesh: "
+ # + str(args[0]))
pass
elif len(args) == 2:
self._init_from_ufl(*args, **kwargs)
@@ -42,7 +39,8 @@ class FunctionSpace(ufl.FunctionSpace):
ufl.FunctionSpace.__init__(self, mesh.ufl_domain(), element)
# Compile dofmap and element
- ufc_element, ufc_dofmap = ffc.jit(element, parameters=None)
+ ufc_element, ufc_dofmap = ffc_jit(element, form_compiler_parameters=None,
+ mpi_comm=mesh.mpi_comm())
ufc_element = cpp.fem.make_ufc_finite_element(ufc_element)
# Create DOLFIN element and dofmap
@@ -118,8 +116,8 @@ class FunctionSpace(ufl.FunctionSpace):
if self.num_sub_spaces() == 1:
raise ValueError("no SubSpaces to extract")
if i >= self.num_sub_spaces():
- raise ValueError("Can only extract SubSpaces with i = 0 ... %d" % \
- (self.num_sub_spaces() - 1))
+ raise ValueError("Can only extract SubSpaces with i = 0 ... %d" %
+ (self.num_sub_spaces() - 1))
assert hasattr(self.ufl_element(), "sub_elements")
# Extend with the python layer
@@ -131,19 +129,16 @@ class FunctionSpace(ufl.FunctionSpace):
def contains(self, V):
"Check whether a function is in the FunctionSpace"
return self._cpp_object.contains(V._cpp_object)
- #if isinstance(u, cpp.function.Function):
- # return u._in(self)
- #elif isinstance(u, function.Function):
- # return u._cpp_object._in(self)
- #return False
def __contains__(self, u):
"Check whether a function is in the FunctionSpace"
- if isinstance(u, cpp.function.Function):
+ try:
return u._in(self._cpp_object)
- elif isinstance(u, function.Function):
- return u._cpp_object._in(self._cpp_object)
- return False
+ except AttributeError:
+ try:
+ return u._cpp_object._in(self._cpp_object)
+ except Exception as e:
+ raise RuntimeError("Unable to check if object is in FunctionSpace ({})".format(e))
def __eq__(self, other):
"Comparison for equality."
diff --git a/python/dolfin/function/jit.py b/python/dolfin/function/jit.py
index 9ed4bdb..33163c9 100644
--- a/python/dolfin/function/jit.py
+++ b/python/dolfin/function/jit.py
@@ -7,7 +7,7 @@
# either version 3 of the License, or (at your option) any later
# version.
-import dolfin.cpp as cpp
+from dolfin.cpp.log import log, LogLevel
from dolfin.jit.jit import compile_class, _math_header
@@ -27,6 +27,7 @@ def jit_generate(class_data, module_name, signature, parameters):
#endif
#include <dolfin/function/Expression.h>
+#include <dolfin/math/basic.h>
#include <Eigen/Dense>
{math_header}
@@ -85,6 +86,8 @@ extern "C" DLL_EXPORT dolfin::Expression * create_{classname}()
_get_props = """ if (name == "{key_name}") return {name};"""
_set_props = """ if (name == "{key_name}") {{ {name} = _value; return; }}"""
+ log(LogLevel.TRACE, "Calling dijitso just-in-time (JIT) compiler for Expression.")
+
statements = class_data["statements"]
statement = ""
if isinstance(statements, str):
@@ -111,9 +114,9 @@ extern "C" DLL_EXPORT dolfin::Expression * create_{classname}()
elif hasattr(value, "_cpp_object"):
members += "std::shared_ptr<dolfin::GenericFunction> generic_function_{key};\n".format(key=k)
set_generic_function += _set_props.format(key_name=k,
- name="generic_function_"+k)
+ name="generic_function_" + k)
get_generic_function += _get_props.format(key_name=k,
- name="generic_function_"+k)
+ name="generic_function_" + k)
value_size = value._cpp_object.value_size()
if value_size == 1:
diff --git a/python/dolfin/function/specialfunctions.py b/python/dolfin/function/specialfunctions.py
index 3408915..a4ddf1d 100644
--- a/python/dolfin/function/specialfunctions.py
+++ b/python/dolfin/function/specialfunctions.py
@@ -21,14 +21,16 @@ SpecialFunctions.h).
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-__all__ = ["MeshCoordinates", "FacetArea", "FacetNormal", "CellSize",
- "CellVolume", "SpatialCoordinate", "CellNormal",
- "Circumradius", "MinFacetEdgeLength", "MaxFacetEdgeLength"]
-
import ufl
import dolfin.cpp as cpp
from dolfin.function.expression import BaseExpression
+__all__ = ["MeshCoordinates", "FacetArea", "FacetNormal",
+ "CellVolume", "SpatialCoordinate", "CellNormal",
+ "CellDiameter", "Circumradius",
+ "MinCellEdgeLength", "MaxCellEdgeLength",
+ "MinFacetEdgeLength", "MaxFacetEdgeLength"]
+
def _mesh2domain(mesh):
"Deprecation mechanism for symbolic geometry."
@@ -44,6 +46,7 @@ class MeshCoordinates(BaseExpression):
vertex.
"""
+
# Initialize C++ part
self._cpp_object = cpp.function.MeshCoordinates(mesh)
@@ -78,14 +81,15 @@ class FacetArea(BaseExpression):
# Initialize UFL part
# NB! This is defined as a piecewise constant function for
# each cell, not for each facet!
- ufl_element = ufl.FiniteElement("Discontinuous Lagrange", mesh.ufl_cell(), 0)
- super().__init__(domain=mesh.ufl_domain(), element=ufl_element, label="FacetArea")
+ ufl_element = ufl.FiniteElement("Discontinuous Lagrange",
+ mesh.ufl_cell(), 0)
+ super().__init__(domain=mesh.ufl_domain(),
+ element=ufl_element, label="FacetArea")
# Simple definition of FacetNormal via UFL
def FacetNormal(mesh):
- """
- Return symbolic facet normal for given mesh.
+ """Return symbolic facet normal for given mesh.
*Arguments*
mesh
@@ -104,9 +108,12 @@ def FacetNormal(mesh):
return ufl.FacetNormal(_mesh2domain(mesh))
-# Simple definition of CellSize via UFL
-def CellSize(mesh):
- """Return function cell size for given mesh.
+# Simple definition of CellDiameter via UFL
+def CellDiameter(mesh):
+ """Return function cell diameter for given mesh.
+
+ Note that diameter of cell :math:`K` is defined as
+ :math:`\sup_{\mathbf{x,y}\in K} |\mathbf{x-y}|`.
*Arguments*
mesh
@@ -117,11 +124,11 @@ def CellSize(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- h = CellSize(mesh)
+ h = CellDiameter(mesh)
"""
- return 2.0*ufl.Circumradius(_mesh2domain(mesh))
+ return ufl.CellDiameter(_mesh2domain(mesh))
# Simple definition of CellVolume via UFL
@@ -157,7 +164,7 @@ def SpatialCoordinate(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = SpatialCoordinate(mesh)
+ x = SpatialCoordinate(mesh)
"""
@@ -177,7 +184,7 @@ def CellNormal(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = CellNormal(mesh)
+ n = CellNormal(mesh)
"""
@@ -197,17 +204,59 @@ def Circumradius(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = Circumradius(mesh)
+ R = Circumradius(mesh)
"""
return ufl.Circumradius(_mesh2domain(mesh))
+# Simple definition of MinCellEdgeLength via UFL
+def MinCellEdgeLength(mesh):
+ """Return symbolic minimum cell edge length of a cell
+ for given mesh.
+
+ *Arguments*
+ mesh
+ a :py:class:`Mesh <dolfin.cpp.Mesh>`.
+
+ *Example of usage*
+
+ .. code-block:: python
+
+ mesh = UnitSquare(4,4)
+ mince = MinCellEdgeLength(mesh)
+
+ """
+
+ return ufl.MinCellEdgeLength(_mesh2domain(mesh))
+
+
+# Simple definition of MaxCellEdgeLength via UFL
+def MaxCellEdgeLength(mesh):
+ """Return symbolic maximum cell edge length of a cell
+ for given mesh.
+
+ *Arguments*
+ mesh
+ a :py:class:`Mesh <dolfin.cpp.Mesh>`.
+
+ *Example of usage*
+
+ .. code-block:: python
+
+ mesh = UnitSquare(4,4)
+ maxce = MaxCellEdgeLength(mesh)
+
+ """
+
+ return ufl.MaxCellEdgeLength(_mesh2domain(mesh))
+
+
# Simple definition of MinFacetEdgeLength via UFL
def MinFacetEdgeLength(mesh):
- """Return symbolic minimum facet edge length of a cell for given
- mesh.
+ """Return symbolic minimum facet edge length of a cell
+ for given mesh.
*Arguments*
mesh
@@ -227,8 +276,8 @@ def MinFacetEdgeLength(mesh):
# Simple definition of MaxFacetEdgeLength via UFL
def MaxFacetEdgeLength(mesh):
- """
- Return symbolic maximum facet edge length of a cell for given mesh.
+ """Return symbolic maximum facet edge length of a cell
+ for given mesh.
*Arguments*
mesh
@@ -239,7 +288,7 @@ def MaxFacetEdgeLength(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = MaxFacetEdgeLength(mesh)
+ maxfe = MaxFacetEdgeLength(mesh)
"""
diff --git a/python/dolfin/jit/__init__.py b/python/dolfin/jit/__init__.py
index 54036f2..66e9d05 100644
--- a/python/dolfin/jit/__init__.py
+++ b/python/dolfin/jit/__init__.py
@@ -16,7 +16,7 @@ def get_pybind_include():
# Get include paths from module
import pybind11
return [pybind11.get_include(True), pybind11.get_include()]
- except:
+ except Exception:
pass
# Look in /usr/local/include and /usr/include
diff --git a/python/dolfin/jit/jit.py b/python/dolfin/jit/jit.py
index b2b08a6..58aa64c 100644
--- a/python/dolfin/jit/jit.py
+++ b/python/dolfin/jit/jit.py
@@ -5,6 +5,99 @@ import hashlib
import dijitso
import dolfin.cpp as cpp
+from dolfin.cpp import MPI
+from functools import wraps
+import ffc
+from dolfin.cpp.parameter import parameters
+
+
+# Copied over from site-packages
+def mpi_jit_decorator(local_jit, *args, **kwargs):
+ """A decorator for jit compilation
+
+ Use this function as a decorator to any jit compiler function. In
+ a parallel run, this function will first call the jit compilation
+ function on the first process. When this is done, and the module
+ is in the cache, it will call the jit compiler on the remaining
+ processes, which will then use the cached module.
+
+ *Example*
+ .. code-block:: python
+
+ def jit_something(something):
+ ....
+
+ """
+ @wraps(local_jit)
+ def mpi_jit(*args, **kwargs):
+
+ # FIXME: should require mpi_comm to be explicit
+ # and not default to comm_world?
+ mpi_comm = kwargs.pop("mpi_comm", MPI.comm_world)
+
+ # Just call JIT compiler when running in serial
+ if MPI.size(mpi_comm) == 1:
+ return local_jit(*args, **kwargs)
+
+ # Default status (0 == ok, 1 == fail)
+ status = 0
+
+ # Compile first on process 0
+ root = MPI.rank(mpi_comm) == 0
+ if root:
+ try:
+ output = local_jit(*args, **kwargs)
+ except Exception as e:
+ status = 1
+ error_msg = str(e)
+
+ # TODO: This would have lower overhead if using the dijitso.jit
+ # features to inject a waiting callback instead of waiting out here.
+ # That approach allows all processes to first look in the cache,
+ # introducing a barrier only on cache miss.
+ # There's also a sketch in dijitso of how to make only one
+ # process per physical cache directory do the compilation.
+
+ # Wait for the compiling process to finish and get status
+ # TODO: Would be better to broadcast the status from root but this works.
+ global_status = MPI.max(mpi_comm, status)
+
+ if global_status == 0:
+ # Success, call jit on all other processes
+ # (this should just read the cache)
+ if not root:
+ output = local_jit(*args, **kwargs)
+ else:
+ # Fail simultaneously on all processes,
+ # to allow catching the error without deadlock
+ if not root:
+ error_msg = "Compilation failed on root node."
+ cpp.dolfin_error("jit.py",
+ "perform just-in-time compilation of form",
+ error_msg)
+ return output
+
+ # Return the decorated jit function
+ return mpi_jit
+
+
+# Wrap FFC JIT compilation with decorator
+ at mpi_jit_decorator
+def ffc_jit(ufl_form, form_compiler_parameters=None):
+
+ # Prepare form compiler parameters with overrides from dolfin and kwargs
+ p = ffc.default_jit_parameters()
+ p.update(dict(parameters["form_compiler"]))
+ p.update(form_compiler_parameters or {})
+ return ffc.jit(ufl_form, parameters=p)
+
+
+# Wrap dijitso JIT compilation with decorator
+ at mpi_jit_decorator
+def dijitso_jit(*args, **kwargs):
+ return dijitso.jit(*args, **kwargs)
+
+
_cpp_math_builtins = [
# <cmath> functions: from http://www.cplusplus.com/reference/cmath/
"cos", "sin", "tan", "acos", "asin", "atan", "atan2",
@@ -61,7 +154,7 @@ def compile_class(cpp_data):
# Make a string representing the properties (and distinguish float/GenericFunction)
# by adding '*' for GenericFunction
property_str = ''
- for k,v in properties.items():
+ for k, v in properties.items():
property_str += str(k)
if hasattr(v, '_cpp_object') and isinstance(v._cpp_object, cpp.function.GenericFunction):
property_str += '*'
@@ -71,10 +164,10 @@ def compile_class(cpp_data):
module_name = "dolfin_" + name + "_" + module_hash
try:
- module, signature = dijitso.jit(cpp_data, module_name, params,
+ module, signature = dijitso_jit(cpp_data, module_name, params,
generate=cpp_data['jit_generate'])
submodule = dijitso.extract_factory_function(module, "create_" + module_name)()
- except:
+ except Exception:
raise RuntimeError("Unable to compile C++ code with dijitso")
if name == 'expression':
diff --git a/python/dolfin/jit/pybind11jit.py b/python/dolfin/jit/pybind11jit.py
index 255d81a..a9d08e1 100644
--- a/python/dolfin/jit/pybind11jit.py
+++ b/python/dolfin/jit/pybind11jit.py
@@ -5,14 +5,18 @@ import dijitso
import pkgconfig
import re
-import dolfin.cpp as cpp
+from dolfin.cpp.log import log, LogLevel
from . import get_pybind_include
-from dolfin.function.expression import BaseExpression, _select_element
+
+from dolfin.jit.jit import dijitso_jit
def jit_generate(cpp_code, module_name, signature, parameters):
- # Split code on reserved word "SIGNATURE" which will be replaced by the module signature
+ log(LogLevel.TRACE, "Calling dijitso just-in-time (JIT) compiler for pybind11 code.")
+
+ # Split code on reserved word "SIGNATURE" which will be replaced
+ # by the module signature
# This must occur only once in the code
split_cpp_code = re.split('SIGNATURE', cpp_code)
if len(split_cpp_code) < 2:
@@ -27,9 +31,12 @@ def jit_generate(cpp_code, module_name, signature, parameters):
return code_h, code_c, depends
+
def compile_cpp_code(cpp_code):
- """Compile a user C(++) string to a Python object with pybind11. Note
- this is still experimental.
+ """Compile a user C(++) string and expose as a Python object with
+ pybind11.
+
+ Note: this is experimental
"""
@@ -37,7 +44,7 @@ def compile_cpp_code(cpp_code):
raise RuntimeError("Could not find DOLFIN pkg-config file. Please make sure appropriate paths are set.")
# Get pkg-config data for DOLFIN
- d = pkgconfig.parse('dolfin')
+ dolfin_pc = pkgconfig.parse('dolfin')
# Set compiler/build options
# FIXME: need to locate Python libs and pybind11
@@ -47,32 +54,35 @@ def compile_cpp_code(cpp_code):
params['cache']['lib_prefix'] = ""
params['cache']['lib_basename'] = ""
params['cache']['lib_loader'] = "import"
- params['build']['include_dirs'] = d["include_dirs"] + get_pybind_include() + [sysconfig.get_config_var("INCLUDEDIR") + "/" + pyversion]
- params['build']['libs'] = d["libraries"] + [ pyversion ]
- params['build']['lib_dirs'] = d["library_dirs"] + [sysconfig.get_config_var("LIBDIR")]
+
+ # Include path and library info from DOLFIN (dolfin.pc)
+ params['build']['include_dirs'] = dolfin_pc["include_dirs"] + get_pybind_include() + [sysconfig.get_config_var("INCLUDEDIR") + "/" + pyversion]
+ params['build']['libs'] = dolfin_pc["libraries"] + [pyversion]
+ params['build']['lib_dirs'] = dolfin_pc["library_dirs"] + [sysconfig.get_config_var("LIBDIR")]
+
params['build']['cxxflags'] += ('-fno-lto',)
- # enable all define macros from DOLFIN
+ # Enable all macros from dolfin.pc
dmacros = ()
- for dm in d['define_macros']:
+ for dm in dolfin_pc['define_macros']:
if len(dm[1]) == 0:
- dmacros += ('-D'+dm[0],)
+ dmacros += ('-D' + dm[0],)
else:
- dmacros += ('-D'+dm[0]+'='+dm[1],)
+ dmacros += ('-D' + dm[0] + '=' + dm[1],)
params['build']['cxxflags'] += dmacros
# This seems to be needed by OSX but not in Linux
# FIXME: probably needed for other libraries too
- if cpp.common.has_petsc():
- import os
- params['build']['libs'] += ['petsc']
- params['build']['lib_dirs'] += [os.environ["PETSC_DIR"] + "/lib"]
+ # if cpp.common.has_petsc():
+ # import os
+ # params['build']['libs'] += ['petsc']
+ # params['build']['lib_dirs'] += [os.environ["PETSC_DIR"] + "/lib"]
module_hash = hashlib.md5(cpp_code.encode('utf-8')).hexdigest()
module_name = "dolfin_cpp_module_" + module_hash
- module, signature = dijitso.jit(cpp_code, module_name, params,
+ module, signature = dijitso_jit(cpp_code, module_name, params,
generate=jit_generate)
return module
diff --git a/python/dolfin/la/__init__.py b/python/dolfin/la/__init__.py
index ca2a0f7..2b7b4df 100644
--- a/python/dolfin/la/__init__.py
+++ b/python/dolfin/la/__init__.py
@@ -14,7 +14,7 @@ def as_backend_type(x):
type.
"""
- if isinstance(x, cpp.la.Vector) or isinstance(x, cpp.la.Matrix):
+ if isinstance(x, cpp.la.Vector) or isinstance(x, cpp.la.Matrix) or isinstance(x, cpp.la.LinearOperator):
return x.instance()
else:
return x
@@ -22,44 +22,48 @@ def as_backend_type(x):
# Extend GenericVector
def __gt__(self, value):
+ "Returns a boolean array with > status for all elements"
if np.isscalar(value):
- return self.array() > value
- if np.isinstance(value, dolfin.cpp.la.GenericVector):
- return self.array() > value.array()
- return NotImplemented
+ return self.get_local() > value
+ else:
+ return self.get_local() > value.get_local()
+
cpp.la.GenericVector.__gt__ = __gt__
del __gt__
def __ge__(self, value):
- if isscalar(value):
- return self.array() >= value
- if isinstance(value, dolfin.cpp.la.GenericVector):
- return self.array() >= value.array()
- return NotImplemented
+ "Returns a boolean array with >= status for all elements"
+ if np.isscalar(value):
+ return self.get_local() >= value
+ else:
+ return self.get_local() >= value.get_local()
+
cpp.la.GenericVector.__ge__ = __ge__
del __ge__
def __lt__(self, value):
+ "Returns a boolean array with < status for all elements"
if np.isscalar(value):
- return self.array() < value
- if isinstance(value, dolfin.cpp.la.GenericVector):
- return self.array() < value.array()
- return NotImplemented
+ return self.get_local() < value
+ else:
+ return self.get_local() < value.get_local()
+
cpp.la.GenericVector.__lt__ = __lt__
del __lt__
def __le__(self, value):
+ "Returns a boolean array with <= status for all elements"
if np.isscalar(value):
- return self.array() <= value
- if isinstance(value, dolfin.cpp.la.GenericVector):
- return self.array() <= value.array()
- return NotImplemented
+ return self.get_local() <= value
+ else:
+ return self.get_local() <= value.get_local()
+
cpp.la.GenericVector.__le__ = __le__
del __le__
@@ -67,10 +71,10 @@ del __le__
def __eq__(self, value):
if np.isscalar(value):
- return self.array() == value
- if isinstance(value, dolfin.cpp.la.GenericVector):
- return self.array() == value.array()
- return NotImplemented
+ return self.get_local() == value
+ else:
+ return self.get_local() == value.get_local()
+
cpp.la.GenericVector.__eq__ = __eq__
del __eq__
@@ -79,5 +83,7 @@ del __eq__
def __iter__(self):
for i in range(self.local_size()):
yield self[i]
+
+
cpp.la.GenericVector.__iter__ = __iter__
del __iter__
diff --git a/python/dolfin/la/solver.py b/python/dolfin/la/solver.py
new file mode 100644
index 0000000..7ed2a6e
--- /dev/null
+++ b/python/dolfin/la/solver.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+"""This module provides a small Python layer for solving linear
+sytems.
+
+"""
+
+# Copyright (C) 2011-2017 Ander Logg and Garth N. Wells
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin.cpp as cpp
+
+
+def solve(A, x, b, method="default", preconditioner="default"):
+ """Solve linear system Ax = b.
+
+ A linear system Ax = b may be solved by calling solve(A, x, b),
+ where A is a matrix and x and b are vectors. Optional arguments
+ may be passed to specify the solver method and preconditioner.
+ Some examples are given below:
+
+ .. code-block:: python
+
+ solve(A, x, b)
+ solve(A, x, b, "lu")
+ solve(A, x, b, "gmres", "ilu")
+
+ Possible values for the solver method and preconditioner depend
+ on which linear algebra backend is used and how that has been
+ configured.
+
+ To list all available LU methods, run the following command:
+
+ .. code-block:: python
+
+ list_lu_solver_methods()
+
+ To list all available Krylov methods, run the following command:
+
+ .. code-block:: python
+
+ list_krylov_solver_methods()
+
+ To list all available preconditioners, run the following command:
+
+ .. code-block:: python
+
+ list_krylov_solver_preconditioners()
+
+ To list all available solver methods, including LU methods, Krylov
+ methods and, possibly, other methods, run the following command:
+
+ .. code-block:: python
+
+ list_linear_solver_methods()
+
+ """
+
+ return cpp.la.solve(A, x, b, method, preconditioner)
diff --git a/python/dolfin/mesh/__init__.py b/python/dolfin/mesh/__init__.py
index 7ee5070..f3a5302 100644
--- a/python/dolfin/mesh/__init__.py
+++ b/python/dolfin/mesh/__init__.py
@@ -4,6 +4,7 @@ from . import svgtools
# Functions to extend cpp.mesh.Mesh with
+
def ufl_cell(self):
return ufl.Cell(self.cell_name(),
geometric_dimension=self.geometry().dim())
@@ -19,6 +20,7 @@ def ufl_coordinate_element(self):
return ufl.VectorElement("Lagrange", cell, degree,
dim=cell.geometric_dimension())
+
def ufl_domain(self):
"""Returns the ufl domain corresponding to the mesh."""
# Cache object to avoid recreating it a lot
@@ -41,6 +43,7 @@ def _repr_html_(self):
def _repr_svg_(self):
return svgtools.mesh2svg(self)
+
# Extend cpp.mesh.Mesh class, and clean-up
cpp.mesh.Mesh.ufl_cell = ufl_cell
cpp.mesh.Mesh.ufl_coordinate_element = ufl_coordinate_element
diff --git a/python/dolfin/mesh/ale.py b/python/dolfin/mesh/ale.py
index bd895f0..02e26af 100644
--- a/python/dolfin/mesh/ale.py
+++ b/python/dolfin/mesh/ale.py
@@ -19,10 +19,12 @@
# First added: 2009-02-12
# Last changed: 2012-09-28
+from dolfin.cpp.mesh import Vertex, edges
+from dolfin.cpp.log import info
+from dolfin import cpp
+
__all__ = ["compute_vertex_map", "compute_edge_map", "init_parent_edge_indices"]
-from dolfin.cpp.mesh import Vertex
-from dolfin import cpp
def compute_vertex_map(mesh0, mesh1):
"""
@@ -43,9 +45,7 @@ def compute_vertex_map(mesh0, mesh1):
# Check arguments
if not isinstance(mesh0, cpp.mesh.Mesh) or not isinstance(mesh1, cpp.mesh.Mesh):
- cpp.dolfin_error("ale.py",
- "compute vertex map",
- "Expected 'Mesh' as argument")
+ raise RuntimeError("Compute vertex map. Expected 'Mesh' as argument.")
# Get parent vertex numbers
vertices0 = mesh0.data().array("parent_vertex_indices", 0)
@@ -53,9 +53,7 @@ def compute_vertex_map(mesh0, mesh1):
# Check mappings
if vertices0 is None or vertices1 is None:
- cpp.dolfin_error("ale.py",
- "compute vertex map",
- "Parent vertex indices are missing")
+ raise RuntimeError("Compute vertex map. Parent vertex indices are missing")
# Compute parent-to-local mapping for mesh1
parent_to_local_mesh1 = {}
@@ -71,6 +69,7 @@ def compute_vertex_map(mesh0, mesh1):
return vertex_map
+
def compute_edge_map(mesh0, mesh1):
"""
Compute map from edges of mesh0 to vertices of mesh1.
@@ -90,19 +89,15 @@ def compute_edge_map(mesh0, mesh1):
# Check arguments
if not isinstance(mesh0, cpp.mesh.Mesh) or not isinstance(mesh1, cpp.mesh.Mesh):
- cpp.dolfin_error("ale.py",
- "compute edge map",
- "Expected 'Mesh' as argument")
+ raise RuntimeError("Compute edge map. Expected 'Mesh' as argument.")
# Get parent vertex numbers
vertices0 = mesh0.data().array("parent_vertex_indices", 0)
vertices1 = mesh1.data().array("parent_vertex_indices", 0)
# Check mappings
- if len(vertices0) == 0 or len(vertices1) == 0:
- cpp.dolfin_error("ale.py",
- "compute edge map",
- "Parent vertex indices are missing")
+ if len(vertices0) == 0 or len(vertices1) == 0:
+ raise RuntimeError("Compute edge map. Parent vertex indices are missing.")
# Initialize edges
mesh0.init(1)
@@ -132,14 +127,13 @@ def compute_edge_map(mesh0, mesh1):
return edge_map
+
def init_parent_edge_indices(submesh, mesh):
"Initialize data 'parent_edge_indices' for submesh."
# Check arguments
if not isinstance(submesh, cpp.mesh.Mesh) or not isinstance(mesh, cpp.mesh.Mesh):
- cpp.dolfin_error("ale.py",
- "initialize parent edge indices",
- "Expected 'Mesh' as argument")
+ raise RuntimeError("Initialize parent edge indices. Expected 'Mesh' as argument")
# Check if edge map has already been computed
if not submesh.data().exists("parent_edge_indices", 1):
@@ -148,9 +142,7 @@ def init_parent_edge_indices(submesh, mesh):
# Get parent vertex numbers
parent_vertex_indices = submesh.data().array("parent_vertex_indices", 0)
if parent_vertex_indices is None:
- cpp.dolfin_error("ale.py",
- "initialize parent edge indices",
- "Parent vertex indice are missing")
+ raise RuntimeError("Initialize parent edge indices. Parent vertex indice are missing.")
# Make sure that we have edges for both meshes
submesh.init(1)
@@ -177,9 +169,7 @@ def init_parent_edge_indices(submesh, mesh):
# Check intersection
common_edges = edges0.intersection(edges1)
if not len(common_edges) == 1:
- cpp.dolfin_error("ale.py",
- "initialize parent edge indices",
- "Parent vertices do not share exactly one common edge")
+ raise RuntimeError("Initialize parent edge indices. Parent vertices do not share exactly one common edge")
parent_edge_index = list(common_edges)[0]
# Set value
diff --git a/python/dolfin/mesh/meshfunction.py b/python/dolfin/mesh/meshfunction.py
index 183f9a1..203e4c0 100644
--- a/python/dolfin/mesh/meshfunction.py
+++ b/python/dolfin/mesh/meshfunction.py
@@ -1,6 +1,6 @@
-
import dolfin.cpp as cpp
+
_meshfunction_types = {"bool": cpp.mesh.MeshFunctionBool,
"size_t": cpp.mesh.MeshFunctionSizet,
"int": cpp.mesh.MeshFunctionInt,
@@ -16,60 +16,3 @@ class MeshFunction(object):
return fn(mesh, dim, value)
else:
return fn(mesh, dim)
-
-
-class VertexFunction(object):
- def __new__(cls, value_type, mesh, value=None):
- if value_type not in _meshfunction_types.keys():
- raise KeyError("MeshFunction type not recognised")
- fn = _meshfunction_types[value_type]
- if value is not None:
- return fn(mesh, 0, value)
- else:
- return fn(mesh, 0)
-
-
-class EdgeFunction(object):
- def __new__(cls, value_type, mesh, value=None):
- if value_type not in _meshfunction_types.keys():
- raise KeyError("MeshFunction type not recognised")
- fn = _meshfunction_types[value_type]
- if value is not None:
- return fn(mesh, 1, value)
- else:
- return fn(mesh, 1)
-
-
-class FaceFunction(object):
- def __new__(cls, value_type, mesh, value=None):
- if value_type not in _meshfunction_types.keys():
- raise KeyError("MeshFunction type not recognised")
- fn = _meshfunction_types[value_type]
- if value is not None:
- return fn(mesh, 2, value)
- else:
- return fn(mesh, 2)
-
-
-class FacetFunction(object):
- def __new__(cls, value_type, mesh, value=None):
- if value_type not in _meshfunction_types.keys():
- raise KeyError("MeshFunction type not recognised")
- fn = _meshfunction_types[value_type]
- tdim = mesh.topology().dim() - 1
- if value is not None:
- return fn(mesh, tdim, value)
- else:
- return fn(mesh, tdim)
-
-
-class CellFunction(object):
- def __new__(cls, value_type, mesh, value=None):
- if value_type not in _meshfunction_types.keys():
- raise KeyError("MeshFunction type not recognised")
- fn = _meshfunction_types[value_type]
- tdim = mesh.topology().dim()
- if value is not None:
- return fn(mesh, tdim, value)
- else:
- return fn(mesh, tdim)
diff --git a/python/dolfin/mesh/subdomain.py b/python/dolfin/mesh/subdomain.py
index e68c80b..4e539e5 100644
--- a/python/dolfin/mesh/subdomain.py
+++ b/python/dolfin/mesh/subdomain.py
@@ -1,8 +1,23 @@
-import hashlib
-import types
-import dijitso
-import ffc
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 Chris N. Richardson and Garth N. Wells
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
import dolfin.cpp as cpp
+from dolfin.cpp.log import log, LogLevel
from dolfin.jit.jit import compile_class, _math_header
@@ -68,6 +83,8 @@ extern "C" DLL_EXPORT dolfin::SubDomain * create_{classname}()
_set_prop = """ if (name == "{name}") {name} = value;\n"""
_get_prop = """ if (name == "{name}") return {name};\n"""
+ log(LogLevel.TRACE, "Calling dijitso just-in-time (JIT) compiler for SubDomain.")
+
inside_code = class_data['statements'][0]
members = ""
@@ -75,8 +92,8 @@ extern "C" DLL_EXPORT dolfin::SubDomain * create_{classname}()
set_props = ""
for k in class_data['properties']:
members += " double " + k + ";\n"
- get_props += _get_prop.format(name = k)
- set_props += _set_prop.format(name = k)
+ get_props += _get_prop.format(name=k)
+ set_props += _set_prop.format(name=k)
classname = signature
code_c = template_code.format(inside=inside_code,
diff --git a/python/dolfin/mesh/svgtools.py b/python/dolfin/mesh/svgtools.py
index f55ac72..b8b870a 100644
--- a/python/dolfin/mesh/svgtools.py
+++ b/python/dolfin/mesh/svgtools.py
@@ -45,15 +45,15 @@ def mesh2svg(mesh, display_width=800.0):
# Compute mesh size
strokewidth = 2
- mesh_origin = [x[:,k].min() for k in range(d)]
- x_min = [x[:,k].min() for k in range(d)]
- x_max = [x[:,k].max() for k in range(d)]
+ # mesh_origin = [x[:,k].min() for k in range(d)]
+ x_min = [x[:, k].min() for k in range(d)]
+ x_max = [x[:, k].max() for k in range(d)]
if d == 1:
- mesh_width = x_max[0]-x_min[0]
+ mesh_width = x_max[0] - x_min[0]
mesh_height = 0
elif d == 2:
- mesh_width = x_max[0]-x_min[0]
- mesh_height = x_max[1]-x_min[1]
+ mesh_width = x_max[0] - x_min[0]
+ mesh_height = x_max[1] - x_min[1]
# Compute display scaling
scale = float(display_width / mesh_width)
@@ -61,38 +61,38 @@ def mesh2svg(mesh, display_width=800.0):
strokewidth)
# Add padding to include vertex circles
- display_padding = 10*strokewidth
- display_width = 2*display_padding + display_width
- display_height = 2*display_padding + display_height
+ display_padding = 10 * strokewidth
+ display_width = 2 * display_padding + display_width
+ display_height = 2 * display_padding + display_height
# Build list of screen coordinate vertices
vertices = []
if d == 1:
- vertices = [(display_padding + int(scale*(x[i,0] - x_min[0])),
+ vertices = [(display_padding + int(scale * (x[i, 0] - x_min[0])),
display_padding)
for i in range(num_vertices)]
elif d == 2:
# Mirror y-axis because of svg coordinate system
- vertices = [(display_padding + int(scale*(x[i,0] - x_min[0])),
- display_padding + int(scale*(x_max[1] - x[i,1])))
+ vertices = [(display_padding + int(scale * (x[i, 0] - x_min[0])),
+ display_padding + int(scale * (x_max[1] - x[i, 1])))
for i in range(num_vertices)]
# Build list of edges
if cellname == "interval":
# Build list of unique edges in 1D case
- edges = [(c[i,0], c[i,1]) for i in range(num_cells)]
- elif cellname == "triangle": # Should in principle work for quadrilateral as well
+ edges = [(c[i, 0], c[i, 1]) for i in range(num_cells)]
+ elif cellname == "triangle": # Should in principle work for quadrilateral as well
# Build list of unique edges in 2D case
edges = set()
for i in range(num_cells):
for j in range(nv):
- e = (c[i,j], c[i,(j+1)%nv])
+ e = (c[i, j], c[i, (j + 1) % nv])
edges.add(tuple(sorted(e)))
edges = sorted(edges)
else:
edges = []
# Build lines for all edges
- lines = [(vertices[e0], vertices[e1]) for e0,e1 in edges]
+ lines = [(vertices[e0], vertices[e1]) for e0, e1 in edges]
# Render svg code
radius = strokewidth
diff --git a/python/dolfin/multistage/__init__.py b/python/dolfin/multistage/__init__.py
index a687677..2712a52 100644
--- a/python/dolfin/multistage/__init__.py
+++ b/python/dolfin/multistage/__init__.py
@@ -5,13 +5,13 @@ from dolfin.multistage import multistagescheme
from dolfin.multistage import multistagesolvers
from dolfin.multistage import rushlarsenschemes
-from .multistagescheme import *
-from .multistagesolvers import *
-from .rushlarsenschemes import *
+from .multistagescheme import * # noqa
+from .multistagesolvers import * # noqa
+from .rushlarsenschemes import * # noqa
# NOTE: The automatic documentation system in DOLFIN requires to _not_
# define classes or functions within this file. Use separate modules
# for that purpose.
__all__ = multistagescheme.__all__ + multistagesolvers.__all__ + \
- rushlarsenschemes.__all__
+ rushlarsenschemes.__all__
diff --git a/python/dolfin/multistage/factorize.py b/python/dolfin/multistage/factorize.py
index 82816ef..efbc505 100644
--- a/python/dolfin/multistage/factorize.py
+++ b/python/dolfin/multistage/factorize.py
@@ -26,16 +26,18 @@ component.
# First added: 2014-11-28
# Last changed: 2014-11-28
-from ufl import *
+# from ufl import *
+from ufl import as_ufl
from ufl.corealg.multifunction import MultiFunction
from ufl.corealg.map_dag import map_expr_dag
-from ufl.classes import Argument, MultiIndex, Indexed, FixedIndex
+from ufl.classes import Argument, FixedIndex
from ufl.log import error as ufl_error
__all__ = ["extract_tested_expressions"]
# TODO: "factorization" is not the right term
+
class ScalarFactorizer(MultiFunction):
def __init__(self):
MultiFunction.__init__(self)
@@ -47,7 +49,7 @@ class ScalarFactorizer(MultiFunction):
self._arg = expr
elif self._arg is not expr:
ufl_error("Expecting only one Argument in this algorithm implementation.")
- return { component: self._one }
+ return {component: self._one}
def argument(self, e):
if e.ufl_shape != ():
@@ -113,13 +115,13 @@ class ScalarFactorizer(MultiFunction):
ufl_error("Expecting only one Argument in this algorithm. Products of Arguments are not allowed.")
elif a_is_dict:
c = {}
- for k,v in a.items():
- c[k] = v*b
+ for k, v in a.items():
+ c[k] = v * b
return c
elif b_is_dict:
c = {}
- for k,v in b.items():
- c[k] = v*a
+ for k, v in b.items():
+ c[k] = v * a
return c
else:
return e
@@ -129,7 +131,7 @@ class ScalarFactorizer(MultiFunction):
ufl_error("Cannot divide by Arguments.")
if isinstance(a, dict):
c = {}
- for k,v in a.items():
+ for k, v in a.items():
c[k] = v / b
return c
else:
diff --git a/python/dolfin/multistage/multistagescheme.py b/python/dolfin/multistage/multistagescheme.py
index e34dc51..6ad174d 100644
--- a/python/dolfin/multistage/multistagescheme.py
+++ b/python/dolfin/multistage/multistagescheme.py
@@ -32,9 +32,7 @@ import dolfin.cpp as cpp
from dolfin.function.constant import Constant
from dolfin.function.expression import Expression
from dolfin.function.function import Function
-from dolfin.function.argument import TestFunction
from dolfin.fem.formmanipulations import derivative, adjoint
-from dolfin.multistage.factorize import extract_tested_expressions
from ufl import action as ufl_action
from dolfin.fem.form import Form
import ufl.algorithms
@@ -51,16 +49,16 @@ def safe_adjoint(x):
def safe_action(x, y):
x = expand_derivatives(x)
if x.integrals() == ():
- return x # form is empty, return anyway
+ return x # form is empty, return anyway
else:
return ufl_action(x, y)
def _check_abc(a, b, c):
- if not (isinstance(a, np.ndarray) and (len(a) == 1 or \
- (len(a.shape)==2 and a.shape[0] == a.shape[1]))):
+ if not (isinstance(a, np.ndarray) and (len(a) == 1 or
+ (len(a.shape) == 2 and a.shape[0] == a.shape[1]))):
raise TypeError("Expected an m x m numpy array as the first argument")
- if not (isinstance(b, np.ndarray) and len(b.shape) in [1,2]):
+ if not (isinstance(b, np.ndarray) and len(b.shape) in [1, 2]):
raise TypeError("Expected a 1 or 2 dimensional numpy array as the second argument")
if not (isinstance(c, np.ndarray) and len(c.shape) == 1):
raise TypeError("Expected a 1 dimensional numpy array as the third argument")
@@ -75,24 +73,24 @@ def _check_abc(a, b, c):
# If b is a matrix we expect it to have two rows
if len(b.shape) == 2:
if not (b.shape[0] == 2 and b.shape[1] == size):
- raise ValueError("Expected a 2 row matrix with the same number "\
+ raise ValueError("Expected a 2 row matrix with the same number "
"of collumns as the first dimension of the a matrix.")
elif len(b) != size:
- raise ValueError("Expected the length of the b vector to have the "\
+ raise ValueError("Expected the length of the b vector to have the "
"same size as the first dimension of the a matrix.")
if len(c) != size:
- raise ValueError("Expected the length of the c vector to have the "\
+ raise ValueError("Expected the length of the c vector to have the "
"same size as the first dimension of the a matrix.")
# Check if the method is singly diagonally implicit
sigma = -1
for i in range(size):
# If implicit
- if a[i,i] != 0:
+ if a[i, i] != 0:
if sigma == -1:
- sigma = a[i,i]
- elif sigma != a[i,i]:
+ sigma = a[i, i]
+ elif sigma != a[i, i]:
raise ValueError("Expected only singly diagonally implicit "
"schemes. (Same value on the diagonal of 'a'.)")
@@ -193,7 +191,7 @@ def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
# Get test function
arguments = rhs_form.arguments()
- coefficients = rhs_form.coefficients()
+ # coefficients = rhs_form.coefficients()
v = arguments[0]
# Create time step
@@ -204,7 +202,7 @@ def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
ufl_stage_forms = []
# Stage solutions
- k = [Function(solution.function_space(), name="k_%d"%i) for i in range(size)]
+ k = [Function(solution.function_space(), name="k_%d" % i) for i in range(size)]
jacobian_indices = []
@@ -216,12 +214,12 @@ def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
for i, ki in enumerate(k):
# Check whether the stage is explicit
- explicit = a[i,i] == 0
+ explicit = a[i, i] == 0
# Evaluation arguments for the ith stage
- evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
- for j in range(i+1)], zero_)
- time = time_ + dt*c[i]
+ evalargs = y_ + dt * sum([float(a[i, j]) * k[j]
+ for j in range(i + 1)], zero_)
+ time = time_ + dt * c[i]
replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions,
time_, dt, c[i])
@@ -235,7 +233,7 @@ def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
jacobian_indices.append(-1)
else:
# Create a F=0 form and differentiate it
- stage_form -= ufl.inner(ki, v)*DX
+ stage_form -= ufl.inner(ki, v) * DX
stage_forms = [stage_form, derivative(stage_form, ki)]
jacobian_indices.append(0)
ufl_stage_forms.append(stage_forms)
@@ -244,25 +242,25 @@ def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
# Only one last stage
if len(b.shape) == 1:
- last_stage = Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \
- zip(b, k)], zero_), v)*DX)
+ last_stage = Form(ufl.inner(y_ + sum([dt * float(bi) * ki for bi, ki in
+ zip(b, k)], zero_), v) * DX)
else:
# FIXME: Add support for adaptivity in RKSolver and
# MultiStageScheme
- last_stage = [Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \
- zip(b[0,:], k)], zero_), v)*DX),
- Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \
- zip(b[1,:], k)], zero_), v)*DX)]
+ last_stage = [Form(ufl.inner(y_ + sum([dt * float(bi) * ki for bi, ki in
+ zip(b[0, :], k)], zero_), v) * DX),
+ Form(ufl.inner(y_ + sum([dt * float(bi) * ki for bi, ki in
+ zip(b[1, :], k)], zero_), v) * DX)]
# Create the Function holding the solution at end of time step
- #k.append(solution.copy())
+ # k.append(solution.copy())
# Generate human form of MultiStageScheme
human_form = []
for i in range(size):
- kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \
- "%s*"% a[i,j], j) \
- for j in range(size) if a[i,j] != 0)
+ kterm = " + ".join("%sh*k_%s" % ("" if a[i, j] == 1.0 else
+ "%s*" % a[i, j], j)
+ for j in range(size) if a[i, j] != 0)
if c[i] in [0.0, 1.0]:
cih = " + h" if c[i] == 1.0 else ""
else:
@@ -271,18 +269,18 @@ def _butcher_scheme_generator(a, b, c, time, solution, rhs_form):
if len(kterm) == 0:
human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih})
else:
- human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \
- {"i": i, "cih": cih, "kterm": kterm})
+ human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" %
+ {"i": i, "cih": cih, "kterm": kterm})
- parentheses = "(%s)" if np.sum(b>0) > 1 else "%s"
- human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\
- "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \
+ parentheses = "(%s)" if np.sum(b > 0) > 1 else "%s"
+ human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(
+ "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i)
for i in range(size) if b[i] > 0)))
human_form = "\n".join(human_form)
return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
- k, dt, human_form, None
+ k, dt, human_form, None
def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
@@ -316,7 +314,7 @@ def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
# Get test function
arguments = rhs_form.arguments()
- coefficients = rhs_form.coefficients()
+ # coefficients = rhs_form.coefficients()
v = arguments[0]
# Create time step
@@ -327,8 +325,8 @@ def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
ufl_stage_forms = []
# Stage solutions
- k = [Function(solution.function_space(), name="k_%d"%i) for i in range(size)]
- kdot = [Function(solution.function_space(), name="kdot_%d"%i) \
+ k = [Function(solution.function_space(), name="k_%d" % i) for i in range(size)]
+ kdot = [Function(solution.function_space(), name="kdot_%d" % i)
for i in range(size)]
# Create the stage forms
@@ -343,12 +341,12 @@ def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
for i, ki in enumerate(k):
# Check whether the stage is explicit
- explicit = a[i,i] == 0
+ explicit = a[i, i] == 0
# Evaluation arguments for the ith stage
- evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
- for j in range(i+1)], zero_)
- time = time_ + dt*c[i]
+ evalargs = y_ + dt * sum([float(a[i, j]) * k[j]
+ for j in range(i + 1)], zero_)
+ time = time_ + dt * c[i]
replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions,
time_, dt, c[i])
@@ -366,7 +364,7 @@ def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
jacobian_indices.append(-1)
else:
# Create a F=0 form and differentiate it
- stage_form_implicit = stage_form - ufl.inner(ki, v)*DX
+ stage_form_implicit = stage_form - ufl.inner(ki, v) * DX
stage_forms = [stage_form_implicit, derivative(stage_form_implicit, ki)]
jacobian_indices.append(0)
@@ -376,14 +374,14 @@ def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
# And now the tangent linearisation:
stage_form_tlm = safe_action(derivative(stage_form, y_), perturbation) + \
- sum([dt*float(a[i,j]) * safe_action(derivative(\
- forward_forms[j], y_), kdot[j]) for j in range(i+1)])
+ sum([dt * float(a[i, j]) * safe_action(derivative(
+ forward_forms[j], y_), kdot[j]) for j in range(i + 1)])
if explicit:
stage_forms_tlm = [stage_form_tlm]
jacobian_indices.append(-1)
else:
# Create a F=0 form and differentiate it
- stage_form_tlm -= ufl.inner(kdot[i], v)*DX
+ stage_form_tlm -= ufl.inner(kdot[i], v) * DX
stage_forms_tlm = [stage_form_tlm, derivative(stage_form_tlm, kdot[i])]
jacobian_indices.append(1)
@@ -393,44 +391,44 @@ def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form,
# Only one last stage
if len(b.shape) == 1:
- last_stage = Form(ufl.inner(perturbation + sum(\
- [dt*float(bi)*kdoti for bi, kdoti in zip(b, kdot)], zero_), v)*DX)
+ last_stage = Form(ufl.inner(perturbation + sum(
+ [dt * float(bi) * kdoti for bi, kdoti in zip(b, kdot)], zero_), v) * DX)
else:
raise Exception("Not sure what to do here")
human_form = []
for i in range(size):
- kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \
- "%s*"% a[i,j], j) \
- for j in range(size) if a[i,j] != 0)
+ kterm = " + ".join("%sh*k_%s" % ("" if a[i, j] == 1.0 else
+ "%s*" % a[i, j], j)
+ for j in range(size) if a[i, j] != 0)
if c[i] in [0.0, 1.0]:
cih = " + h" if c[i] == 1.0 else ""
else:
cih = " + %s*h" % c[i]
- kdotterm = " + ".join("%(a)sh*action(derivative(f(t_n%(cih)s, y_n + "\
- "%(kterm)s), kdot_%(i)s" % \
- {"a": ("" if a[i,j] == 1.0 else "%s*"% a[i,j], j),
+ kdotterm = " + ".join("%(a)sh*action(derivative(f(t_n%(cih)s, y_n + "
+ "%(kterm)s), kdot_%(i)s" %
+ {"a": ("" if a[i, j] == 1.0 else "%s*" % a[i, j], j),
"i": i,
"cih": cih,
- "kterm": kterm} \
- for j in range(size) if a[i,j] != 0)
+ "kterm": kterm}
+ for j in range(size) if a[i, j] != 0)
if len(kterm) == 0:
human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih})
- human_form.append("kdot_%(i)s = action(derivative("\
- "f(t_n%(cih)s, y_n), y_n), ydot_n)" % \
+ human_form.append("kdot_%(i)s = action(derivative("
+ "f(t_n%(cih)s, y_n), y_n), ydot_n)" %
{"i": i, "cih": cih})
else:
- human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \
- {"i": i, "cih": cih, "kterm": kterm})
- human_form.append("kdot_%(i)s = action(derivative(f(t_n%(cih)s, "\
- "y_n + %(kterm)s), y_n) + %(kdotterm)s" % \
- {"i": i, "cih": cih, "kterm": kterm, "kdotterm": kdotterm})
-
- parentheses = "(%s)" if np.sum(b>0) > 1 else "%s"
- human_form.append("ydot_{n+1} = ydot_n + h*" + parentheses % (" + ".join(\
- "%skdot_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \
+ human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" %
+ {"i": i, "cih": cih, "kterm": kterm})
+ human_form.append("kdot_%(i)s = action(derivative(f(t_n%(cih)s, "
+ "y_n + %(kterm)s), y_n) + %(kdotterm)s" %
+ {"i": i, "cih": cih, "kterm": kterm, "kdotterm": kdotterm})
+
+ parentheses = "(%s)" if np.sum(b > 0) > 1 else "%s"
+ human_form.append("ydot_{n+1} = ydot_n + h*" + parentheses % (" + ".join(
+ "%skdot_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i)
for i in range(size) if b[i] > 0)))
human_form = "\n".join(human_form)
@@ -469,7 +467,7 @@ def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
# Get test function
arguments = rhs_form.arguments()
- coefficients = rhs_form.coefficients()
+ # coefficients = rhs_form.coefficients()
v = arguments[0]
# Create time step
@@ -480,8 +478,8 @@ def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
ufl_stage_forms = []
# Stage solutions
- k = [Function(solution.function_space(), name="k_%d"%i) for i in range(size)]
- kbar = [Function(solution.function_space(), name="kbar_%d"%i) \
+ k = [Function(solution.function_space(), name="k_%d" % i) for i in range(size)]
+ kbar = [Function(solution.function_space(), name="kbar_%d" % i)
for i in range(size)]
# Create the stage forms
@@ -497,14 +495,14 @@ def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
for i, ki in enumerate(k):
# Check whether the stage is explicit
- explicit = a[i,i] == 0
+ explicit = a[i, i] == 0
# Evaluation arguments for the ith stage
- evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \
- for j in range(i+1)], zero_)
- time = time_ + dt*c[i]
+ evalargs = y_ + dt * sum([float(a[i, j]) * k[j]
+ for j in range(i + 1)], zero_)
+ time = time_ + dt * c[i]
- replace_dict = _replace_dict_time_dependent_expression(\
+ replace_dict = _replace_dict_time_dependent_expression(
time_dep_expressions, time_, dt, c[i])
replace_dict[y_] = evalargs
@@ -518,8 +516,8 @@ def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
jacobian_indices.append(-1)
else:
# Create a F=0 form and differentiate it
- stage_form_implicit = stage_form - ufl.inner(ki, v)*DX
- stage_forms = [stage_form_implicit, derivative(\
+ stage_form_implicit = stage_form - ufl.inner(ki, v) * DX
+ stage_forms = [stage_form_implicit, derivative(
stage_form_implicit, ki)]
jacobian_indices.append(0)
@@ -530,18 +528,18 @@ def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
for i, kbari in reversed(list(enumerate(kbar))):
# Check whether the stage is explicit
- explicit = a[i,i] == 0
+ explicit = a[i, i] == 0
# And now the adjoint linearisation:
- stage_form_adm = ufl.inner(dt * b[i] * adj, v)*DX + sum(\
- [dt * float(a[j,i]) * safe_action(safe_adjoint(derivative(\
+ stage_form_adm = ufl.inner(dt * b[i] * adj, v) * DX + sum(
+ [dt * float(a[j, i]) * safe_action(safe_adjoint(derivative(
forward_forms[j], y_)), kbar[j]) for j in range(i, size)])
if explicit:
stage_forms_adm = [stage_form_adm]
jacobian_indices.append(-1)
else:
# Create a F=0 form and differentiate it
- stage_form_adm -= ufl.inner(kbar[i], v)*DX
+ stage_form_adm -= ufl.inner(kbar[i], v) * DX
stage_forms_adm = [stage_form_adm, derivative(stage_form_adm, kbari)]
jacobian_indices.append(1)
@@ -551,16 +549,16 @@ def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj):
# Only one last stage
if len(b.shape) == 1:
- last_stage = Form(ufl.inner(adj, v)*DX + sum(\
- [safe_action(safe_adjoint(derivative(forward_forms[i], y_)), kbar[i]) \
+ last_stage = Form(ufl.inner(adj, v) * DX + sum(
+ [safe_action(safe_adjoint(derivative(forward_forms[i], y_)), kbar[i])
for i in range(size)]))
else:
raise Exception("Not sure what to do here")
human_form = "unimplemented"
- return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage,\
- stage_solutions, dt, human_form, adj
+ return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
+ stage_solutions, dt, human_form, adj
class MultiStageScheme(cpp.multistage.MultiStageScheme):
@@ -633,6 +631,7 @@ class MultiStageScheme(cpp.multistage.MultiStageScheme):
def stage_solutions(self):
"Return the stage solutions"
return self._stage_solutions
+
def to_tlm(self, perturbation):
raise NotImplementedError("'to_tlm:' implement in derived classes")
@@ -649,8 +648,8 @@ class ButcherMultiStageScheme(MultiStageScheme):
bcs = bcs or []
time = time or Constant(0.0)
ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \
- stage_solutions, dt, human_form, contraction = \
- generator(a, b, c, time, solution, rhs_form)
+ stage_solutions, dt, human_form, contraction = \
+ generator(a, b, c, time, solution, rhs_form)
# Store data
self.a = a
@@ -725,7 +724,7 @@ class ExplicitMidPoint(ButcherMultiStageScheme):
"""Explicit 2nd order scheme"""
def __init__(self, rhs_form, solution, t=None, bcs=None):
- a = np.array([[0, 0],[0.5, 0.0]])
+ a = np.array([[0, 0], [0.5, 0.0]])
b = np.array([0., 1])
c = np.array([0, 0.5])
ButcherMultiStageScheme.__init__(self, rhs_form, solution, t, bcs,
@@ -735,7 +734,7 @@ class ExplicitMidPoint(ButcherMultiStageScheme):
class CN2(ButcherMultiStageScheme):
"""Semi-implicit 2nd order scheme"""
def __init__(self, rhs_form, solution, t=None, bcs=None):
- a = np.array([[0, 0],[0.5, 0.5]])
+ a = np.array([[0, 0], [0.5, 0.5]])
b = np.array([0.5, 0.5])
c = np.array([0, 1.0])
@@ -750,7 +749,7 @@ class ERK4(ButcherMultiStageScheme):
[0.5, 0, 0, 0],
[0, 0.5, 0, 0],
[0, 0, 1, 0]])
- b = np.array([1./6, 1./3, 1./3, 1./6])
+ b = np.array([1. / 6, 1. / 3, 1. / 3, 1. / 6])
c = np.array([0, 0.5, 0.5, 1])
ButcherMultiStageScheme.__init__(self, rhs_form, solution, t, bcs,
a, b, c, 4)
@@ -765,11 +764,11 @@ class ESDIRK3(ButcherMultiStageScheme):
"""
def __init__(self, rhs_form, solution, t=None, bcs=None):
- a = np.array([[0, 0, 0, 0 ],
- [0.435866521500000, 0.435866521500000, 0, 0 ],
- [0.490563388419108, 0.073570090080892, 0.435866521500000, 0 ],
- [0.308809969973036, 1.490563388254108, -1.235239879727145, 0.435866521500000 ]])
- b = a[-1,:].copy()
+ a = np.array([[0.000000000000000, 0.000000000000000, 0.000000000000000, 0.00000000000000],
+ [0.435866521500000, 0.435866521500000, 0.000000000000000, 0.00000000000000],
+ [0.490563388419108, 0.073570090080892, 0.435866521500000, 0.00000000000000],
+ [0.308809969973036, 1.490563388254108, -1.235239879727145, 0.435866521500000]])
+ b = a[-1, :].copy()
c = a.sum(1)
ButcherMultiStageScheme.__init__(self, rhs_form, solution, t, bcs, a, b, c, 3)
@@ -783,13 +782,13 @@ class ESDIRK4(ButcherMultiStageScheme):
"""
def __init__(self, rhs_form, solution, t=None, bcs=None):
- a = np.array([[0, 0, 0, 0, 0],
- [0.435866521500000, 0.4358665215, 0, 0, 0 ],
- [0.140737774731968, -0.108365551378832, 0.435866521500000, 0, 0 ],
- [0.102399400616089, -0.376878452267324, 0.838612530151233, 0.435866521500000, 0 ],
- [0.157024897860995, 0.117330441357768, 0.616678030391680, -0.326899891110444, 0.435866521500000 ]])
+ a = np.array([[0.000000000000000, 0.0000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000],
+ [0.435866521500000, 0.4358665215000000, 0.000000000000000, 0.000000000000000, 0.000000000000000],
+ [0.140737774731968, -0.108365551378832, 0.435866521500000, 0.000000000000000, 0.000000000000000],
+ [0.102399400616089, -0.376878452267324, 0.838612530151233, 0.435866521500000, 0.000000000000000],
+ [0.157024897860995, 0.1173304413577680, 0.616678030391680, -0.326899891110444, 0.435866521500000]])
- b = a[-1,:].copy()
+ b = a[-1, :].copy()
c = a.sum(1)
ButcherMultiStageScheme.__init__(self, rhs_form, solution, t, bcs, a, b, c, 4)
@@ -803,7 +802,7 @@ BackwardEuler = BDF1
ERK = ERK1
RK4 = ERK4
-__all__ = [name for name, attr in list(globals().items()) \
+__all__ = [name for name, attr in list(globals().items())
if isinstance(attr, type) and issubclass(attr, MultiStageScheme)]
__all__.append("MultiStageScheme")
diff --git a/python/dolfin/multistage/multistagesolvers.py b/python/dolfin/multistage/multistagesolvers.py
index f0ae073..935a6e0 100644
--- a/python/dolfin/multistage/multistagesolvers.py
+++ b/python/dolfin/multistage/multistagesolvers.py
@@ -26,6 +26,7 @@ import dolfin.cpp as cpp
__all__ = ["PointIntegralSolver", "RKSolver"]
+
class PointIntegralSolver(cpp.multistage.PointIntegralSolver):
def __init__(self, scheme):
"""
diff --git a/python/dolfin/multistage/rushlarsenschemes.py b/python/dolfin/multistage/rushlarsenschemes.py
index ad26618..9a7bf34 100644
--- a/python/dolfin/multistage/rushlarsenschemes.py
+++ b/python/dolfin/multistage/rushlarsenschemes.py
@@ -22,30 +22,26 @@ PointIntegralSolver
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-import numpy as np
import functools
import ufl
-import dolfin.cpp as cpp
from dolfin.function.constant import Constant
from dolfin.function.function import Function
-from dolfin.function.argument import TestFunction
from dolfin.fem.formmanipulations import derivative
from dolfin.multistage.factorize import extract_tested_expressions
-from ufl import action as ufl_action
from dolfin.fem.form import Form
from dolfin.multistage.multistagescheme import (MultiStageScheme,
_check_form, _time_dependent_expressions,
_replace_dict_time_dependent_expression, safe_action,
safe_adjoint)
from dolfin import DOLFIN_EPS
-from dolfin import assemble
from dolfin import TrialFunction
import ufl.algorithms
from ufl.algorithms import (expand_derivatives, expand_indices,
extract_coefficients)
+
def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms,
system_size, y0, stage_solution, dt, time, a, c,
v, DX, time_dep_expressions):
@@ -62,9 +58,9 @@ def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms,
# If we have time dependent expressions
if time_dep_expressions and abs(float(c)) > DOLFIN_EPS:
time_ = time
- time = time + dt*float(c)
+ time = time + dt * float(c)
- repl.update(_replace_dict_time_dependent_expression(time_dep_expressions, \
+ repl.update(_replace_dict_time_dependent_expression(time_dep_expressions,
time_, dt, float(c)))
repl[time_] = time
@@ -78,7 +74,7 @@ def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms,
for ind in range(system_size):
# forward euler step
- fe_du_i = rhs_exprs[ind]*dt*float(a)
+ fe_du_i = rhs_exprs[ind] * dt * float(a)
# If exact integration
if linear_terms[ind]:
@@ -88,8 +84,8 @@ def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms,
# diff_rhs_exprs[ind] is never 1.0e-16! Let's get rid of
# this when the conditional fixes land properly in UFL.
eps = Constant(1.0e-16)
- rl_du_i = rhs_exprs[ind]/(diff_rhs_exprs[ind] + eps)*(\
- ufl.exp(diff_rhs_exprs[ind]*dt) - 1.0)
+ rl_du_i = rhs_exprs[ind] / (diff_rhs_exprs[ind] + eps) * (
+ ufl.exp(diff_rhs_exprs[ind] * dt) - 1.0)
# If safe guard
if safe_guard:
@@ -103,9 +99,9 @@ def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms,
if repl:
du_i = ufl.replace(du_i, repl)
- rl_ufl_form += (y0[ind] + du_i)*v[ind]
+ rl_ufl_form += (y0[ind] + du_i) * v[ind]
- return rl_ufl_form*DX
+ return rl_ufl_form * DX
def _find_linear_terms(rhs_exprs, u):
@@ -117,15 +113,15 @@ def _find_linear_terms(rhs_exprs, u):
uu = [Constant(1.0) for _ in rhs_exprs]
if len(rhs_exprs) > 1:
- repl = {u:ufl.as_vector(uu)}
+ repl = {u: ufl.as_vector(uu)}
else:
- repl = {u:uu[0]}
+ repl = {u: uu[0]}
linear_terms = []
for i, ui in enumerate(uu):
comp_i_s = expand_indices(ufl.replace(rhs_exprs[i], repl))
- linear_terms.append(ui in extract_coefficients(comp_i_s) and \
- ui not in extract_coefficients(\
+ linear_terms.append(ui in extract_coefficients(comp_i_s) and
+ ui not in extract_coefficients(
expand_derivatives(ufl.diff(comp_i_s, ui))))
return linear_terms
@@ -158,8 +154,8 @@ def _rush_larsen_scheme_generator(rhs_form, solution, time, order, generalized):
dt = Constant(0.1)
# Get test function
- arguments = rhs_form.arguments()
- coefficients = rhs_form.coefficients()
+ # arguments = rhs_form.arguments()
+ # coefficients = rhs_form.coefficients()
# Get time dependent expressions
time_dep_expressions = _time_dependent_expressions(rhs_form, time)
@@ -167,7 +163,7 @@ def _rush_larsen_scheme_generator(rhs_form, solution, time, order, generalized):
# Extract rhs expressions from form
rhs_integrand = rhs_form.integrals()[0].integrand()
rhs_exprs, v = extract_tested_expressions(rhs_integrand)
- vector_rhs = len(v.ufl_shape)>0 and v.ufl_shape[0]>1
+ vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1
system_size = v.ufl_shape[0] if vector_rhs else 1
@@ -186,7 +182,7 @@ def _rush_larsen_scheme_generator(rhs_form, solution, time, order, generalized):
# Takes time!
if vector_rhs:
- diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))\
+ diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))
for ind in range(system_size)]
else:
diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[0]))]
@@ -233,7 +229,7 @@ def _rush_larsen_scheme_generator(rhs_form, solution, time, order, generalized):
str(order))
return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \
- stage_solutions, dt, dt_stage_offsets, human_form, None
+ stage_solutions, dt, dt_stage_offsets, human_form, None
def _rush_larsen_scheme_generator_tlm(rhs_form, solution, time, order,
@@ -267,8 +263,8 @@ def _rush_larsen_scheme_generator_tlm(rhs_form, solution, time, order,
dt = Constant(0.1)
# Get test function
- arguments = rhs_form.arguments()
- coefficients = rhs_form.coefficients()
+ # arguments = rhs_form.arguments()
+ # coefficients = rhs_form.coefficients()
# Get time dependent expressions
time_dep_expressions = _time_dependent_expressions(rhs_form, time)
@@ -276,7 +272,7 @@ def _rush_larsen_scheme_generator_tlm(rhs_form, solution, time, order,
# Extract rhs expressions from form
rhs_integrand = rhs_form.integrals()[0].integrand()
rhs_exprs, v = extract_tested_expressions(rhs_integrand)
- vector_rhs = len(v.ufl_shape)>0 and v.ufl_shape[0]>1
+ vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1
system_size = v.ufl_shape[0] if vector_rhs else 1
@@ -295,7 +291,7 @@ def _rush_larsen_scheme_generator_tlm(rhs_form, solution, time, order,
# Takes time!
if vector_rhs:
- diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))\
+ diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))
for ind in range(system_size)]
soln = solution
else:
@@ -344,8 +340,8 @@ def _rush_larsen_scheme_generator_tlm(rhs_form, solution, time, order,
dt, time, 1.0, 0.5, v, DX,
time_dep_expressions)
- rl_ufl_form = safe_action(derivative(y_one_form, soln, trial), perturbation) + \
- safe_action(derivative(y_one_form, stage_solutions[0], trial), stage_solutions[1])
+ rl_ufl_form = safe_action(derivative(y_one_form, soln, trial), perturbation) + \
+ safe_action(derivative(y_one_form, stage_solutions[0], trial), stage_solutions[1])
ufl_stage_forms.append([y_half_form])
ufl_stage_forms.append([y_dot_half_form])
@@ -361,7 +357,7 @@ def _rush_larsen_scheme_generator_tlm(rhs_form, solution, time, order,
str(order))
return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \
- stage_solutions, dt, dt_stage_offsets, human_form, perturbation
+ stage_solutions, dt, dt_stage_offsets, human_form, perturbation
def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
@@ -395,8 +391,8 @@ def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
dt = Constant(0.1)
# Get test function
- arguments = rhs_form.arguments()
- coefficients = rhs_form.coefficients()
+ # arguments = rhs_form.arguments()
+ # coefficients = rhs_form.coefficients()
# Get time dependent expressions
time_dep_expressions = _time_dependent_expressions(rhs_form, time)
@@ -404,7 +400,7 @@ def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
# Extract rhs expressions from form
rhs_integrand = rhs_form.integrals()[0].integrand()
rhs_exprs, v = extract_tested_expressions(rhs_integrand)
- vector_rhs = len(v.ufl_shape)>0 and v.ufl_shape[0]>1
+ vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1
system_size = v.ufl_shape[0] if vector_rhs else 1
@@ -423,7 +419,7 @@ def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
# Takes time!
if vector_rhs:
- diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))\
+ diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind]))
for ind in range(system_size)]
soln = solution
else:
@@ -476,8 +472,8 @@ def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
y_bar_half_form = safe_action(safe_adjoint(derivative(y_one_form,
stage_solutions[0], trial)), perturbation)
- rl_ufl_form = safe_action(safe_adjoint(derivative(y_one_form, soln, trial)), perturbation) + \
- safe_action(safe_adjoint(derivative(y_half_form, soln, trial)), stage_solutions[2])
+ rl_ufl_form = safe_action(safe_adjoint(derivative(y_one_form, soln, trial)), perturbation) + \
+ safe_action(safe_adjoint(derivative(y_half_form, soln, trial)), stage_solutions[2])
ufl_stage_forms.append([y_half_form])
ufl_stage_forms.append([y_one_form])
@@ -493,7 +489,7 @@ def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order,
str(order))
return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \
- stage_solutions, dt, dt_stage_offsets, human_form, perturbation
+ stage_solutions, dt, dt_stage_offsets, human_form, perturbation
class RushLarsenScheme(MultiStageScheme):
@@ -509,12 +505,12 @@ class RushLarsenScheme(MultiStageScheme):
# FIXME: What with bcs?
bcs = []
time = time or Constant(0.0)
- if order not in [1,2]:
+ if order not in [1, 2]:
raise ValueError("Expected order to be either 1 or 2")
rhs_form, ufl_stage_forms, linear_terms, dofin_stage_forms, last_stage, \
- stage_solutions, dt, dt_stage_offsets, human_form, contraction = \
- generator(rhs_form, solution, time, order, generalized)
+ stage_solutions, dt, dt_stage_offsets, human_form, contraction = \
+ generator(rhs_form, solution, time, order, generalized)
self.linear_terms = linear_terms
@@ -597,7 +593,7 @@ class GRL2(RushLarsenScheme):
RushLarsenScheme.__init__(self, rhs_form, solution, t, 2, True)
-__all__ = [name for name, attr in list(globals().items()) \
+__all__ = [name for name, attr in list(globals().items())
if isinstance(attr, type) and issubclass(attr, MultiStageScheme)]
__all__.append("MultiStageScheme")
diff --git a/python/dolfin/parameter/__init__.py b/python/dolfin/parameter/__init__.py
index d13f8b3..a0f5340 100644
--- a/python/dolfin/parameter/__init__.py
+++ b/python/dolfin/parameter/__init__.py
@@ -8,6 +8,8 @@
# version.
import dolfin.cpp as cpp
+from ffc import default_jit_parameters
+from dolfin.cpp.parameter import parameters, Parameters
# Extend cpp.Parameters with a __getitem__ method
@@ -19,8 +21,8 @@ def __getitem__(self, key):
# FIXME: I think we want to return the parameter set rather than a copy?
p = self._get_parameter_set(key)
return p
- #np = cpp.parameter.Parameters(p)
- #return np
+ # np = cpp.parameter.Parameters(p)
+ # return np
else:
raise RuntimeError("Invalid parameter: {}".format(key))
@@ -43,12 +45,7 @@ def update(self, params):
# Extend the cpp.parameter.Parameters class and clean-up
cpp.parameter.Parameters.__getitem__ = __getitem__
cpp.parameter.Parameters.update = update
-del __getitem__, update
-
-
-# Import global form compiler parameters from FFC
-from ffc import default_jit_parameters
-from dolfin.cpp.parameter import parameters, Parameters
+del __getitem__, update
def ffc_default_parameters():
diff --git a/python/dolfin_utils/meshconvert/meshconvert.py b/python/dolfin_utils/meshconvert/meshconvert.py
index 385546e..5a54fb5 100644
--- a/python/dolfin_utils/meshconvert/meshconvert.py
+++ b/python/dolfin_utils/meshconvert/meshconvert.py
@@ -867,7 +867,7 @@ def diffpack2xml(ifilename, ofilename):
xml_writer.write_footer_vertices(ofile)
xml_writer.write_header_cells(ofile, num_cells)
- # Output unique vertex markers as individual VertexFunctions
+ # Output unique vertex markers as individual MeshFunctions on vertices
unique_vertex_markers.difference_update([0])
for unique_marker in unique_vertex_markers:
ofile_marker = open(ofilename.replace(".xml", "") + \
diff --git a/python/dolfin_utils/test/fixtures.py b/python/dolfin_utils/test/fixtures.py
index 59f7d93..7f6db83 100644
--- a/python/dolfin_utils/test/fixtures.py
+++ b/python/dolfin_utils/test/fixtures.py
@@ -41,14 +41,15 @@ def fixture(func):
NOTE: Probably does not work with yield fixtures or
maybe just ignores post-yield code
- This is the preferred decorator for writing fixtures
- involving objects which might have collective destructors.
- If in a need for using ``pytest.fixture`` directly, do::
+ This is the preferred decorator for writing fixtures involving
+ objects which might have collective destructors. If in a need for
+ using ``pytest.fixture`` directly, do::
yield result_of_fixture
gc.collect()
MPI.barrier(MPI.comm_world)
+
"""
def wrapper(func, *args, **kwargs):
@@ -74,24 +75,35 @@ def fixture(func):
def gc_barrier():
- """Internal utility to easily switch on and off calls to
- gc.collect() and MPI.barrier(world) in all fixtures here.
- Helps make the tests deterministic when debugging.
+ """Internal utility to easily switch on and off calls to gc.collect()
+ and MPI.barrier(world) in all fixtures here. Helps make the tests
+ deterministic when debugging.
+
"""
gc.collect()
if MPI.size(MPI.comm_world) > 1:
MPI.barrier(MPI.comm_world)
+ at pytest.fixture
+def worker_id(request):
+ """Returns thread id when running with pytest-xdist in parallel."""
+ if hasattr(request.config, 'slaveinput'):
+ return request.config.slaveinput['slaveid']
+ else:
+ return 'master'
+
+
@pytest.yield_fixture(scope="function")
def gc_barrier_fixture():
"""Function decorator to call gc.collect() and
MPI.barrier(world) before and after a test. Helps
make the tests deterministic when debugging.
- NOTE: This decorator is not needed now for writing tests,
- as there is ``gc.collect()`` call in ``conftest.py`` on
- teardown of every test.
+ NOTE: This decorator is not needed now for writing tests, as there
+ is ``gc.collect()`` call in ``conftest.py`` on teardown of every
+ test.
+
"""
gc_barrier()
yield
@@ -117,7 +129,10 @@ def filedir(request):
@pytest.fixture(scope="module")
def rootdir(request):
- "Return the root directory of the repository. Assumes run from within repository filetree."
+ """Return the root directory of the repository. Assumes run from
+ within repository filetree.
+
+ """
gc_barrier()
d = os.path.dirname(os.path.abspath(request.module.__file__))
t = ''
@@ -128,7 +143,10 @@ def rootdir(request):
@pytest.fixture(scope="module")
def datadir(request):
- "Return the directory of the shared test data. Assumes run from within repository filetree."
+ """Return the directory of the shared test data. Assumes run from
+ within repository filetree.
+
+ """
d = os.path.dirname(os.path.abspath(request.module.__file__))
t = os.path.join(d, "data")
while not os.path.isdir(t):
@@ -144,7 +162,8 @@ def _create_tempdir(request):
# Construct name test_foo_tempdir from name test_foo.py
testfilename = os.path.basename(testfile)
- outputname = testfilename.replace(".py", "_tempdir")
+ outputname = testfilename.replace(".py",
+ "_tempdir_{}".format(worker_id(request)))
# Get function name test_something from test_foo.py
function = request.function.__name__
@@ -164,19 +183,22 @@ def _create_tempdir(request):
# Delete and re-create directory on root node
if MPI.rank(MPI.comm_world) == 0:
- # First time visiting this basepath, delete the old and create a new
+ # First time visiting this basepath, delete the old and create
+ # a new
if basepath not in _create_tempdir._basepaths:
_create_tempdir._basepaths.add(basepath)
if os.path.exists(basepath):
shutil.rmtree(basepath)
- # Make sure we have the base path test_foo_tempdir for this test_foo.py file
+ # Make sure we have the base path test_foo_tempdir for
+ # this test_foo.py file
if not os.path.exists(basepath):
os.mkdir(basepath)
# Delete path from old test run
if os.path.exists(path):
shutil.rmtree(path)
- # Make sure we have the path for this test execution: e.g. test_foo_tempdir/test_something__3
+ # Make sure we have the path for this test execution:
+ # e.g. test_foo_tempdir/test_something__3
if not os.path.exists(path):
os.mkdir(path)
MPI.barrier(MPI.comm_world)
@@ -194,12 +216,13 @@ def tempdir(request):
Deletes and re-creates directory from previous test runs but lets
the directory stay after the test run for eventual inspection.
- Returns the directory name, derived from the test file and function
- plus a sequence number to work with parameterized tests.
+ Returns the directory name, derived from the test file and
+ function plus a sequence number to work with parameterized tests.
Does NOT change the current directory.
MPI safe (assuming MPI.comm_world context).
+
"""
gc_barrier()
return _create_tempdir(request)
@@ -212,12 +235,14 @@ def cd_tempdir(request):
Deletes and re-creates directory from previous test runs but lets
the directory stay after the test run for eventual inspection.
- Returns the directory name, derived from the test file and function
- plus a sequence number to work with parameterized tests.
+ Returns the directory name, derived from the test file and
+ function plus a sequence number to work with parameterized tests.
- Changes the current directory to the tempdir and resets cwd afterwards.
+ Changes the current directory to the tempdir and resets cwd
+ afterwards.
MPI safe (assuming MPI.comm_world context).
+
"""
gc_barrier()
cwd = os.getcwd()
@@ -238,9 +263,9 @@ def pushpop_parameters():
# TODO: Rename set_parameters_fixture to e.g. use_parameter_values
def set_parameters_fixture(paramname, values, key=lambda x: x):
- """Return a fixture that sets and resets a global parameter
- to each of a list of values before and after each test run.
- Allows paramname="foo.bar.var" meaning parameters["foo"]["bar"]["var"].
+ """Return a fixture that sets and resets a global parameter to each of
+ a list of values before and after each test run. Allows
+ paramname="foo.bar.var" meaning parameters["foo"]["bar"]["var"].
Usage:
repr = set_parameters_fixture("form_compiler.representation", ["quadrature", "uflacs"])
@@ -260,6 +285,7 @@ def set_parameters_fixture(paramname, values, key=lambda x: x):
assert parameters["linear_algebra_backend"] == my_fixture2[0]
Try it and see.
+
"""
global parameters
def _pushpop(request):
@@ -277,9 +303,9 @@ def set_parameters_fixture(paramname, values, key=lambda x: x):
yield request.param # Let test run
parameters[names[0]][names[1]][names[2]] = prev # Reset value
else:
- prev = parameters[paramname] # Remember original value
- parameters[paramname] = key(request.param) # Set value
- yield request.param # Let test run
- parameters[paramname] = prev # Reset value
+ prev = parameters[paramname] # Remember original value
+ parameters[paramname] = key(request.param) # Set value
+ yield request.param # Let test run
+ parameters[paramname] = prev # Reset value
return pytest.yield_fixture(scope="function", params=values)(_pushpop)
diff --git a/python/setup.cfg b/python/setup.cfg
new file mode 100644
index 0000000..e44b810
--- /dev/null
+++ b/python/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+ignore = E501
diff --git a/python/setup.py b/python/setup.py
index c574e1a..3da0818 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -50,7 +50,7 @@ class CMakeBuild(build_ext):
if "CIRCLECI" in os.environ:
build_args += ['--', '-j2']
else:
- num_build_threads = max(1, multiprocessing.cpu_count() - 2)
+ num_build_threads = max(1, multiprocessing.cpu_count() - 1)
build_args += ['--', '-j' + str(num_build_threads)]
env = os.environ.copy()
@@ -65,7 +65,7 @@ class CMakeBuild(build_ext):
setup(name='dolfin',
version='0.0.1',
author='FEniCS Project',
- description='Experimental DOLFIN pybind11 interface',
+ description='DOLFIN Python interface (via pybind11)',
long_description='',
packages=["dolfin",
"dolfin.common",
@@ -83,8 +83,8 @@ setup(name='dolfin',
ext_modules=[CMakeExtension('dolfin.cpp')],
cmdclass=dict(build_ext=CMakeBuild),
install_requires=["numpy",
- "ffc",
- "ufl",
"pkgconfig",
- "dijitso"],
+ "fenics-ffc",
+ "fenics-ufl",
+ "fenics-dijitso"],
zip_safe=False)
diff --git a/dolfin/geometry/intersect.cpp b/python/src/MPICommWrapper.cpp
similarity index 56%
copy from dolfin/geometry/intersect.cpp
copy to python/src/MPICommWrapper.cpp
index bc7364f..70dc1eb 100644
--- a/dolfin/geometry/intersect.cpp
+++ b/python/src/MPICommWrapper.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Anders Logg
+// Copyright (C) 2017 Tormod Landet
//
// This file is part of DOLFIN.
//
@@ -14,20 +14,30 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2013-05-30
-// Last changed: 2013-05-30
-#include "MeshPointIntersection.h"
-#include "intersect.h"
+#include "MPICommWrapper.h"
-using namespace dolfin;
+using namespace dolfin_wrappers;
//-----------------------------------------------------------------------------
-std::shared_ptr<const MeshPointIntersection>
-dolfin::intersect(const Mesh& mesh, const Point& point)
+MPICommWrapper::MPICommWrapper() : _comm(MPI_COMM_NULL)
+{
+ // Do nothing
+}
+//-----------------------------------------------------------------------------
+MPICommWrapper::MPICommWrapper(MPI_Comm comm) : _comm(comm)
+{
+ // Do nothing
+}
+//-----------------------------------------------------------------------------
+MPICommWrapper& MPICommWrapper::operator=(const MPI_Comm comm)
+{
+ this->_comm = comm;
+ return *this;
+}
+//-----------------------------------------------------------------------------
+MPI_Comm MPICommWrapper::get() const
{
- return std::shared_ptr<const MeshPointIntersection>
- (new MeshPointIntersection(mesh, point));
+ return _comm;
}
//-----------------------------------------------------------------------------
diff --git a/dolfin/la/PETScObject.h b/python/src/MPICommWrapper.h
similarity index 50%
copy from dolfin/la/PETScObject.h
copy to python/src/MPICommWrapper.h
index 08df1d7..77eb013 100644
--- a/dolfin/la/PETScObject.h
+++ b/python/src/MPICommWrapper.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2008 Garth N. Wells
+// Copyright (C) 2017 Tormod Landet
//
// This file is part of DOLFIN.
//
@@ -15,37 +15,40 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-#ifndef __PETSC_OBJECT_H
-#define __PETSC_OBJECT_H
+#ifndef __MPI_COMM_WRAPPER_H
+#define __MPI_COMM_WRAPPER_H
-#ifdef HAS_PETSC
+#include <dolfin/common/MPI.h>
-#include <string>
-#include <dolfin/common/SubSystemsManager.h>
-
-namespace dolfin
+namespace dolfin_wrappers
{
- /// This class calls SubSystemsManager to initialise PETSc.
- ///
- /// All PETSc objects must be derived from this class.
+ /// This class wraps the MPI_Comm type for use in the pybind11
+ /// generation of python wrappers. MPI_Comm is either a pointer or
+ /// an int (MPICH vs OpenMPI) and this cannot be wrapped in a type
+ /// safe way with pybind11.
- class PETScObject
+ class MPICommWrapper
{
public:
- /// Constructor. Ensures that PETSc has been initialised.
- PETScObject() { SubSystemsManager::init_petsc(); }
+ MPICommWrapper();
+
+ /// Wrap a MPI_Comm object
+ MPICommWrapper(MPI_Comm comm);
+
+ /// Assignment operator
+ MPICommWrapper& operator=(const MPI_Comm comm);
- /// Destructor
- virtual ~PETScObject() {}
+ /// Get the underlying MPI communicator
+ MPI_Comm get() const;
+
+ private:
+
+ // The underlying communicator
+ MPI_Comm _comm;
- /// Print error message for PETSc calls that return an error
- static void petsc_error(int error_code,
- std::string filename,
- std::string petsc_function);
};
}
#endif
-#endif
diff --git a/python/src/adaptivity.cpp b/python/src/adaptivity.cpp
index bd83f74..10989b0 100644
--- a/python/src/adaptivity.cpp
+++ b/python/src/adaptivity.cpp
@@ -31,6 +31,7 @@
#include <dolfin/fem/Form.h>
#include <dolfin/fem/LinearVariationalProblem.h>
#include <dolfin/fem/NonlinearVariationalProblem.h>
+#include <dolfin/function/Function.h>
#include <dolfin/la/GenericVector.h>
#include <dolfin/mesh/Mesh.h>
@@ -45,9 +46,10 @@ namespace dolfin_wrappers
{
#ifdef HAS_HDF5
// dolfin::TimesSeries
- py::class_<dolfin::TimeSeries, std::shared_ptr<dolfin::TimeSeries>>(m, "TimeSeries")
+ py::class_<dolfin::TimeSeries, std::shared_ptr<dolfin::TimeSeries>, dolfin::Variable>(m, "TimeSeries")
.def(py::init<std::string>())
- .def(py::init<MPI_Comm, std::string>())
+ .def(py::init([](const MPICommWrapper comm, const std::string &arg)
+ { return std::unique_ptr<dolfin::TimeSeries>(new dolfin::TimeSeries(comm.get(), arg)); }))
.def("store", (void (dolfin::TimeSeries::*)(const dolfin::GenericVector&, double)) &dolfin::TimeSeries::store)
.def("store", (void (dolfin::TimeSeries::*)(const dolfin::Mesh&, double)) &dolfin::TimeSeries::store)
.def("retrieve", (void (dolfin::TimeSeries::*)(dolfin::GenericVector&, double, bool) const) &dolfin::TimeSeries::retrieve,
@@ -111,7 +113,5 @@ namespace dolfin_wrappers
.def(py::init<std::shared_ptr<dolfin::NonlinearVariationalProblem>,
std::shared_ptr<dolfin::Form>,
std::shared_ptr<dolfin::ErrorControl>>());
-
}
-
}
diff --git a/python/src/ale.cpp b/python/src/ale.cpp
index 6eb1fd1..e2f3462 100644
--- a/python/src/ale.cpp
+++ b/python/src/ale.cpp
@@ -52,7 +52,5 @@ namespace dolfin_wrappers
auto _disp = disp.attr("_cpp_object").cast<const dolfin::GenericFunction*>();
dolfin::ALE::move(mesh, *_disp);
});
-
}
-
}
diff --git a/python/src/casters.h b/python/src/casters.h
index 7508af5..19dae45 100644
--- a/python/src/casters.h
+++ b/python/src/casters.h
@@ -38,7 +38,8 @@ namespace pybind11
template <typename... Ts>
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
- // Specifies the function used to visit the variant -- `apply_visitor` instead of `visit`
+ // Specifies the function used to visit the variant --
+ // `apply_visitor` instead of `visit`
template <>
struct visit_helper<boost::variant> {
template <typename... Args>
diff --git a/python/src/common.cpp b/python/src/common.cpp
index c218949..186774b 100644
--- a/python/src/common.cpp
+++ b/python/src/common.cpp
@@ -32,6 +32,7 @@
#include <dolfin/log/Table.h>
#include "casters.h"
+#include "MPICommWrapper.h"
namespace py = pybind11;
@@ -54,11 +55,38 @@ namespace dolfin_wrappers
m.def("has_hdf5", &dolfin::has_hdf5);
m.def("has_hdf5_parallel", &dolfin::has_hdf5_parallel);
m.def("has_mpi", &dolfin::has_mpi);
+ m.def("has_mpi4py", []()
+ {
+ #ifdef HAS_PYBIND11_MPI4PY
+ return true;
+ #else
+ return false;
+ #endif
+ }, "Return `True` if DOLFIN is configured with mpi4py");
m.def("has_parmetis", &dolfin::has_parmetis);
m.def("has_scotch", &dolfin::has_scotch);
- m.def("has_petsc", &dolfin::has_petsc);
- m.def("has_slepc", &dolfin::has_slepc, "Return `True` if DOLFIN is configured with SLEPc");
- m.def("git_commit_hash", &dolfin::git_commit_hash, "Get git hash for this build.");
+ m.def("has_petsc", &dolfin::has_petsc,
+ "Return `True` if DOLFIN is configured with PETSc");
+ m.def("has_slepc", &dolfin::has_slepc,
+ "Return `True` if DOLFIN is configured with SLEPc");
+ m.def("has_petsc4py", []()
+ {
+ #ifdef HAS_PYBIND11_PETSC4PY
+ return true;
+ #else
+ return false;
+ #endif
+ }, "Return `True` if DOLFIN is configured with petsc4py");
+ m.def("has_slepc4py", []()
+ {
+ #ifdef HAS_PYBIND11_SLEPC4PY
+ return true;
+ #else
+ return false;
+ #endif
+ }, "Return `True` if DOLFIN is configured with slepc4py");
+ m.def("git_commit_hash", &dolfin::git_commit_hash,
+ "Returns git hash for this build.");
m.def("sizeof_la_index", &dolfin::sizeof_la_index);
m.attr("DOLFIN_EPS") = DOLFIN_EPS;
@@ -97,71 +125,92 @@ namespace dolfin_wrappers
});
m.def("dump_timings_to_xml", &dolfin::dump_timings_to_xml);
+ // dolfin::SubSystemsManager
+ py::class_<dolfin::SubSystemsManager, std::unique_ptr<dolfin::SubSystemsManager, py::nodelete>>
+ (m, "SubSystemsManager")
+ .def_static("init_petsc", (void (*)()) &dolfin::SubSystemsManager::init_petsc)
+ .def_static("init_petsc", [](std::vector<std::string> args)
+ {
+ std::vector<char*> argv(args.size());
+ for (std::size_t i = 0; i < args.size(); ++i)
+ argv[i] = const_cast<char*>(args[i].data());
+ dolfin::SubSystemsManager::init_petsc(args.size(), argv.data());
+ })
+ .def_static("finalize", &dolfin::SubSystemsManager::finalize)
+ .def_static("responsible_mpi", &dolfin::SubSystemsManager::responsible_mpi)
+ .def_static("responsible_petsc", &dolfin::SubSystemsManager::responsible_petsc)
+ .def_static("mpi_initialized", &dolfin::SubSystemsManager::mpi_initialized)
+ .def_static("mpi_finalized", &dolfin::SubSystemsManager::mpi_finalized);
+
}
// Interface for MPI
void mpi(py::module& m)
{
- /*
- #ifdef HAS_MPI4PY
- dolfin::SubSystemsManager::init_mpi();
- import_mpi4py();
+
+ #ifndef HAS_PYBIND11_MPI4PY
+ // Expose the MPICommWrapper directly since we cannot cast it to
+ // mpi4py
+ py::class_<MPICommWrapper>(m, "MPICommWrapper",
+ "DOLFIN is compiled without support for mpi4py. This object can be "
+ "passed into DOLFIN as an MPI communicator, but is not an mpi4py comm.")
+ .def("underlying_comm", [](MPICommWrapper self)
+ { return (std::uintptr_t) self.get(); },
+ "Return the underlying MPI_Comm cast to std::uintptr_t. "
+ "The return value may or may not make sense depending on the MPI implementation.");
#endif
- */
// dolfin::MPI
py::class_<dolfin::MPI>(m, "MPI", "MPI utilities")
-#ifdef OPEN_MPI
.def_property_readonly_static("comm_world", [](py::object)
- { return reinterpret_cast<std::uintptr_t>(MPI_COMM_WORLD); })
+ { return MPICommWrapper(MPI_COMM_WORLD); })
.def_property_readonly_static("comm_self", [](py::object)
- { return reinterpret_cast<std::uintptr_t>(MPI_COMM_SELF); })
+ { return MPICommWrapper(MPI_COMM_SELF); })
.def_property_readonly_static("comm_null", [](py::object)
- { return reinterpret_cast<std::uintptr_t>(MPI_COMM_NULL); })
-#else
- .def_property_readonly_static("comm_world", [](py::object) { return MPI_COMM_WORLD; })
- .def_property_readonly_static("comm_self", [](py::object) { return MPI_COMM_SELF; })
- .def_property_readonly_static("comm_null", [](py::object) { return MPI_COMM_NULL; })
-#endif
- .def_static("init", [](){ dolfin::SubSystemsManager::init_mpi(); }, "Initialise MPI")
- .def_static("barrier", &dolfin::MPI::barrier)
- .def_static("rank", &dolfin::MPI::rank)
- .def_static("size", &dolfin::MPI::size)
- .def_static("local_range", (std::pair<std::int64_t, std::int64_t> (*)(MPI_Comm, std::int64_t))
- &dolfin::MPI::local_range)
- .def_static("max", &dolfin::MPI::max<double>)
- .def_static("min", &dolfin::MPI::min<double>)
- .def_static("sum", &dolfin::MPI::sum<double>)
- .def_static("min", &dolfin::MPI::min<dolfin::Table>)
- .def_static("max", &dolfin::MPI::max<dolfin::Table>)
- .def_static("sum", &dolfin::MPI::sum<dolfin::Table>)
- .def_static("avg", &dolfin::MPI::avg<dolfin::Table>)
- /*
-#ifdef HAS_MPI4PY
- .def("to_mpi4py_comm", [](py::object obj){
- // If object is already a mpi4py communicator, return
- if (PyObject_TypeCheck(obj.ptr(), &PyMPIComm_Type))
- return obj;
-
- MPI_Comm comm_new;
- #ifdef OPEN_MPI
- std::uintptr_t c = obj.cast<std::uintptr_t>();
- MPI_Comm_dup(reinterpret_cast<MPI_Comm>(c), &comm_new);
- #else
- auto value = PyLong_AsLong(obj.ptr());
- MPI_Comm_dup(value, &comm_new);
- #endif
-
- // Create wrapper for conversion to mpi4py
- dolfin_wrappers::mpi_communicator mpi_comm;
- mpi_comm.comm = comm_new;
-
- return py::cast(mpi_comm);
- },
- "Convert a plain MPI communicator into a mpi4py communicator")
-#endif
- */
- ;
- }
-
+ { return MPICommWrapper(MPI_COMM_NULL); })
+ .def_static("init", (void (*)()) &dolfin::SubSystemsManager::init_mpi,
+ "Initialise MPI")
+ .def_static("init",
+ [](std::vector<std::string> args, int required_thread_level)->int
+ {
+ std::vector<char*> argv(args.size());
+ for (std::size_t i = 0; i < args.size(); ++i)
+ argv[i] = const_cast<char*>(args[i].data());
+ return dolfin::SubSystemsManager::
+ init_mpi(args.size(), argv.data(), required_thread_level);
+ },
+ "Initialise MPI with command-line args and required level "
+ "of thread support. Return provided thread level.")
+ .def_static("responsible", &dolfin::SubSystemsManager::responsible_mpi,
+ "Return true if DOLFIN initialised MPI (and is therefore "
+ "responsible for finalization)")
+ .def_static("initialized", &dolfin::SubSystemsManager::mpi_initialized,
+ "Check if MPI has been initialised")
+ .def_static("finalized", &dolfin::SubSystemsManager::mpi_finalized,
+ "Check if MPI has been finalized")
+ .def_static("barrier", [](const MPICommWrapper comm)
+ { return dolfin::MPI::barrier(comm.get()); })
+ .def_static("rank", [](const MPICommWrapper comm)
+ { return dolfin::MPI::rank(comm.get()); })
+ .def_static("size", [](const MPICommWrapper comm)
+ { return dolfin::MPI::size(comm.get()); })
+ .def_static("local_range", [](MPICommWrapper comm, std::int64_t N)
+ { return dolfin::MPI::local_range(comm.get(), N); })
+ // templated for double
+ .def_static("max", [](const MPICommWrapper comm, double value)
+ { return dolfin::MPI::max(comm.get(), value); })
+ .def_static("min", [](const MPICommWrapper comm, double value)
+ { return dolfin::MPI::min(comm.get(), value); })
+ .def_static("sum", [](const MPICommWrapper comm, double value)
+ { return dolfin::MPI::sum(comm.get(), value); })
+ // templated for dolfin::Table
+ .def_static("max", [](const MPICommWrapper comm, dolfin::Table value)
+ { return dolfin::MPI::max(comm.get(), value); })
+ .def_static("min", [](const MPICommWrapper comm, dolfin::Table value)
+ { return dolfin::MPI::min(comm.get(), value); })
+ .def_static("sum", [](const MPICommWrapper comm, dolfin::Table value)
+ { return dolfin::MPI::sum(comm.get(), value); })
+ .def_static("avg", [](const MPICommWrapper comm, dolfin::Table value)
+ { return dolfin::MPI::avg(comm.get(), value); });
+ }
}
diff --git a/python/src/dolfin.cpp b/python/src/dolfin.cpp
index 1690f5c..8dde3d3 100644
--- a/python/src/dolfin.cpp
+++ b/python/src/dolfin.cpp
@@ -15,6 +15,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#include <iostream>
#include <pybind11/pybind11.h>
#include <dolfin/log/log.h>
@@ -52,7 +53,8 @@ namespace dolfin_wrappers
PYBIND11_MODULE(cpp, m)
{
// Create module for C++ wrappers
- m.doc() ="DOLFIN Python interface";
+ m.doc() = "DOLFIN Python interface";
+ m.attr("__version__") = DOLFIN_VERSION;
// Create common submodule [common]
py::module common = m.def_submodule("common", "Common module");
diff --git a/python/src/fem.cpp b/python/src/fem.cpp
index ff6e3d5..4454fc1 100644
--- a/python/src/fem.cpp
+++ b/python/src/fem.cpp
@@ -25,8 +25,11 @@
#include <pybind11/stl.h>
#include <pybind11/cast.h>
-#include <ufc.h>
+#ifdef HAS_PYBIND11_PETSC4PY
+#include <petsc4py/petsc4py.h>
+#endif
+#include <ufc.h>
#include <dolfin/fem/fem_utils.h>
#include <dolfin/fem/assemble.h>
#include <dolfin/fem/assemble_local.h>
@@ -41,18 +44,19 @@
#include <dolfin/fem/LocalSolver.h>
#include <dolfin/fem/NonlinearVariationalProblem.h>
#include <dolfin/fem/NonlinearVariationalSolver.h>
-#include <dolfin/fem/PointSource.h>
-#include <dolfin/fem/SystemAssembler.h>
#include <dolfin/fem/PETScDMCollection.h>
+#include <dolfin/fem/PointSource.h>
#include <dolfin/fem/SparsityPatternBuilder.h>
-#include <dolfin/function/FunctionSpace.h>
+#include <dolfin/fem/SystemAssembler.h>
#include <dolfin/function/GenericFunction.h>
-#include <dolfin/mesh/Mesh.h>
-#include <dolfin/mesh/SubDomain.h>
-#include <dolfin/la/GenericTensor.h>
+#include <dolfin/function/FunctionSpace.h>
+#include <dolfin/function/Function.h>
#include <dolfin/la/GenericMatrix.h>
#include <dolfin/la/GenericVector.h>
+#include <dolfin/la/GenericTensor.h>
#include <dolfin/la/SparsityPattern.h>
+#include <dolfin/mesh/Mesh.h>
+#include <dolfin/mesh/SubDomain.h>
#include "casters.h"
@@ -62,12 +66,6 @@ namespace dolfin_wrappers
{
void fem(py::module& m)
{
-#ifdef HAS_PETSC4PY
- int ierr = import_petsc4py();
- if (ierr != 0)
- throw std::runtime_error("Failed to import petsc4py");
-#endif
-
// UFC objects
py::class_<ufc::finite_element, std::shared_ptr<ufc::finite_element>>
(m, "ufc_finite_element", "UFC finite element object");
@@ -172,8 +170,10 @@ namespace dolfin_wrappers
.def("signature", &dolfin::FiniteElement::signature);
// dolfin::GenericDofMap
- py::class_<dolfin::GenericDofMap, std::shared_ptr<dolfin::GenericDofMap>>
+ py::class_<dolfin::GenericDofMap, std::shared_ptr<dolfin::GenericDofMap>, dolfin::Variable>
(m, "GenericDofMap", "DOLFIN DofMap object")
+ .def("global_dimension", &dolfin::GenericDofMap::global_dimension,
+ "The dimension of the global finite element function space")
.def("index_map", &dolfin::GenericDofMap::index_map)
.def("neighbours", &dolfin::GenericDofMap::neighbours)
.def("off_process_owner", &dolfin::GenericDofMap::off_process_owner)
@@ -187,16 +187,24 @@ namespace dolfin_wrappers
&dolfin::GenericDofMap::entity_dofs)
.def("entity_closure_dofs", (std::vector<dolfin::la_index>(dolfin::GenericDofMap::*)(const dolfin::Mesh&, std::size_t) const)
&dolfin::GenericDofMap::entity_closure_dofs)
- .def("entity_dofs", (std::vector<dolfin::la_index>(dolfin::GenericDofMap::*)(const dolfin::Mesh&,
+ .def("entity_dofs", (std::vector<dolfin::la_index>(dolfin::GenericDofMap::*)(const dolfin::Mesh&,
std::size_t,
const std::vector<std::size_t>&) const)
&dolfin::GenericDofMap::entity_dofs)
- .def("entity_closure_dofs", (std::vector<dolfin::la_index>(dolfin::GenericDofMap::*)(const dolfin::Mesh&,
+ .def("entity_closure_dofs", (std::vector<dolfin::la_index>(dolfin::GenericDofMap::*)(const dolfin::Mesh&,
std::size_t,
const std::vector<std::size_t>&) const)
&dolfin::GenericDofMap::entity_closure_dofs)
.def("num_entity_dofs", &dolfin::GenericDofMap::num_entity_dofs)
.def("tabulate_local_to_global_dofs", &dolfin::GenericDofMap::tabulate_local_to_global_dofs)
+ .def("local_to_global_index", &dolfin::GenericDofMap::local_to_global_index)
+ .def("local_to_global_unowned",
+ [](dolfin::GenericDofMap& self) {
+ return Eigen::Map<const Eigen::Matrix<std::size_t, Eigen::Dynamic, 1>>(
+ self.local_to_global_unowned().data(),
+ self.local_to_global_unowned().size()); },
+ py::return_value_policy::reference_internal,
+ "Return view into unowned part of local-to-global map")
.def("clear_sub_map_data", &dolfin::GenericDofMap::clear_sub_map_data)
.def("tabulate_entity_dofs", [](const dolfin::GenericDofMap& instance, std::size_t entity_dim,
std::size_t cell_entity_index)
@@ -233,7 +241,7 @@ namespace dolfin_wrappers
py::arg("init")=true, py::arg("finalize")=true);
// dolfin::DirichletBC
- py::class_<dolfin::DirichletBC, std::shared_ptr<dolfin::DirichletBC>>
+ py::class_<dolfin::DirichletBC, std::shared_ptr<dolfin::DirichletBC>, dolfin::Variable>
(m, "DirichletBC", "DOLFIN DirichletBC object")
.def(py::init<const dolfin::DirichletBC&>())
.def(py::init<std::shared_ptr<const dolfin::FunctionSpace>,
@@ -265,6 +273,10 @@ namespace dolfin_wrappers
&dolfin::DirichletBC::apply)
.def("apply", (void (dolfin::DirichletBC::*)(dolfin::GenericMatrix&, dolfin::GenericVector&) const)
&dolfin::DirichletBC::apply)
+ .def("apply", (void (dolfin::DirichletBC::*)(dolfin::GenericVector&, const dolfin::GenericVector&) const)
+ &dolfin::DirichletBC::apply)
+ .def("apply", (void (dolfin::DirichletBC::*)(dolfin::GenericMatrix&, dolfin::GenericVector&, const dolfin::GenericVector&) const)
+ &dolfin::DirichletBC::apply)
.def("user_subdomain", &dolfin::DirichletBC::user_sub_domain)
.def("set_value", &dolfin::DirichletBC::set_value)
.def("set_value", [](dolfin::DirichletBC& self, py::object value)
diff --git a/python/src/function.cpp b/python/src/function.cpp
index 71ea458..2c176ac 100644
--- a/python/src/function.cpp
+++ b/python/src/function.cpp
@@ -32,6 +32,8 @@
#include <dolfin/function/FunctionAssigner.h>
#include <dolfin/function/FunctionAXPY.h>
#include <dolfin/function/FunctionSpace.h>
+#include <dolfin/function/MultiMeshFunction.h>
+#include <dolfin/function/MultiMeshFunctionSpace.h>
#include <dolfin/function/LagrangeInterpolator.h>
#include <dolfin/function/SpecialFunctions.h>
#include <dolfin/fem/FiniteElement.h>
@@ -90,7 +92,20 @@ namespace dolfin_wrappers
{ std::vector<double> values;
self.compute_vertex_values(values, mesh);
return py::array_t<double>(values.size(), values.data());
- })
+ }, "Compute values at all mesh vertices")
+ .def("compute_vertex_values", [](dolfin::GenericFunction& self)
+ {
+ auto V = self.function_space();
+ if (!V)
+ throw py::value_error("GenericFunction has no function space. You must supply a mesh.");
+ auto mesh = V->mesh();
+ if (!mesh)
+ throw py::value_error("GenericFunction has no function space mesh. You must supply a mesh.");
+ std::vector<double> values;
+ self.compute_vertex_values(values, *mesh);
+ // FIXME: this causes a copy, we should rewrite the C++ interface to use Eigen when SWIG is removed
+ return py::array_t<double>(values.size(), values.data());
+ }, "Compute values at all mesh vertices by using the mesh function.function_space().mesh()")
.def("function_space", &dolfin::GenericFunction::function_space);
// Create dolfin::Expression from a JIT pointer
@@ -424,6 +439,18 @@ namespace dolfin_wrappers
}
});
+ py::class_<dolfin::MultiMeshFunction>(m, "MultiMeshFunction")
+ .def(py::init<std::shared_ptr<dolfin::MultiMeshFunctionSpace>>())
+ .def("vector", static_cast<std::shared_ptr<dolfin::GenericVector>(dolfin::MultiMeshFunction::*)()>(&dolfin::MultiMeshFunction::vector));
+
+ py::class_<dolfin::MultiMeshFunctionSpace, std::shared_ptr<dolfin::MultiMeshFunctionSpace>>
+ (m, "MultiMeshFunctionSpace")
+ .def(py::init<std::shared_ptr<dolfin::MultiMesh>>())
+ .def("add", &dolfin::MultiMeshFunctionSpace::add)
+ .def("build", static_cast<void(dolfin::MultiMeshFunctionSpace::*)()>(&dolfin::MultiMeshFunctionSpace::build));
+
+
+
// dolfin::assign interface
m.def("assign", [](py::object v0, py::object v1)
{
diff --git a/python/src/generation.cpp b/python/src/generation.cpp
index 2d7ed55..44807d7 100644
--- a/python/src/generation.cpp
+++ b/python/src/generation.cpp
@@ -15,12 +15,15 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#include <array>
#include <iostream>
#include <memory>
+#include <string>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <dolfin/geometry/Point.h>
+#include <dolfin/mesh/CellType.h>
#include <dolfin/generation/BoxMesh.h>
#include <dolfin/generation/UnitTriangleMesh.h>
#include <dolfin/generation/UnitCubeMesh.h>
@@ -28,8 +31,6 @@
#include <dolfin/generation/SphericalShellMesh.h>
#include <dolfin/generation/UnitSquareMesh.h>
#include <dolfin/generation/UnitIntervalMesh.h>
-#include <dolfin/generation/UnitQuadMesh.h>
-#include <dolfin/generation/UnitHexMesh.h>
#include <dolfin/generation/IntervalMesh.h>
#include "casters.h"
@@ -44,64 +45,104 @@ namespace dolfin_wrappers
// dolfin::IntervalMesh
py::class_<dolfin::IntervalMesh, std::shared_ptr<dolfin::IntervalMesh>, dolfin::Mesh>(m, "IntervalMesh")
.def(py::init<std::size_t, double, double>())
- .def(py::init<MPI_Comm, std::size_t, double, double>());
+ .def(py::init([](const MPICommWrapper comm, std::size_t n, double a, double b)
+ { return std::unique_ptr<dolfin::IntervalMesh>(new dolfin::IntervalMesh(comm.get(), n, a, b)); }));
// dolfin::UnitIntervalMesh
py::class_<dolfin::UnitIntervalMesh, std::shared_ptr<dolfin::UnitIntervalMesh>,
dolfin::IntervalMesh, dolfin::Mesh>(m, "UnitIntervalMesh")
.def(py::init<std::size_t>())
- .def(py::init<MPI_Comm, std::size_t>())
- .def_static("create", [](std::size_t n){ return dolfin::UnitIntervalMesh::create(n); });
+ .def(py::init([](const MPICommWrapper comm, std::size_t n)
+ { return std::unique_ptr<dolfin::UnitIntervalMesh>(new dolfin::UnitIntervalMesh(comm.get(), n)); }))
+ .def_static("create", [](std::size_t n)
+ { return dolfin::UnitIntervalMesh::create(n); });
// dolfin::RectangleMesh
py::class_<dolfin::RectangleMesh, std::shared_ptr<dolfin::RectangleMesh>, dolfin::Mesh>(m, "RectangleMesh")
+ .def_static("create", [](std::array<dolfin::Point, 2> p, std::array<std::size_t, 2> n,
+ dolfin::CellType::Type cell_type, std::string diagonal)
+ { return dolfin::RectangleMesh::create(p, n, cell_type, diagonal); },
+ py::arg("p"), py::arg("n"), py::arg("cell_type"), py::arg("diagonal")="right")
+ .def_static("create", [](const MPICommWrapper comm, std::array<dolfin::Point, 2> p,
+ std::array<std::size_t, 2> n, dolfin::CellType::Type cell_type,
+ std::string diagonal)
+ { return dolfin::RectangleMesh::create(comm.get(), p, n, cell_type, diagonal); },
+ py::arg("comm"), py::arg("p"), py::arg("n"), py::arg("cell_type"),
+ py::arg("diagonal")="right")
+ // Remove
.def(py::init<dolfin::Point, dolfin::Point, std::size_t, std::size_t, std::string>(),
py::arg("p0"), py::arg("p1"), py::arg("nx"), py::arg("ny"), py::arg("diagonal")="right")
- .def(py::init<MPI_Comm, dolfin::Point, dolfin::Point, std::size_t, std::size_t, std::string>(),
- py::arg("comm"), py::arg("p0"), py::arg("p1"), py::arg("nx"), py::arg("ny"),
- py::arg("diagonal")="right");
+ .def(py::init([](const MPICommWrapper comm, const dolfin::Point& p0, const dolfin::Point& p1,
+ std::size_t nx, std::size_t ny, std::string diagonal="right")
+ { return std::unique_ptr<dolfin::RectangleMesh>(new dolfin::RectangleMesh(comm.get(), p0, p1, nx, ny, diagonal)); }),
+ py::arg("comm"), py::arg("p0"), py::arg("p1"), py::arg("nx"), py::arg("ny"), py::arg("diagonal")="right");
// dolfin::UnitSquareMesh
py::class_<dolfin::UnitSquareMesh, std::shared_ptr<dolfin::UnitSquareMesh>, dolfin::Mesh>(m, "UnitSquareMesh")
- .def(py::init<std::size_t, std::size_t>())
- .def(py::init<MPI_Comm, std::size_t, std::size_t>())
- .def(py::init<std::size_t, std::size_t, std::string>())
- .def(py::init<MPI_Comm, std::size_t, std::size_t, std::string>());
+ .def(py::init<std::size_t, std::size_t, std::string>(), py::arg("nx"), py::arg("ny"), py::arg("diagonal")="right")
+ .def(py::init([](const MPICommWrapper comm, std::size_t nx, std::size_t ny, std::string diagonal="right")
+ { return std::unique_ptr<dolfin::UnitSquareMesh>(new dolfin::UnitSquareMesh(comm.get(), nx, ny, diagonal)); }),
+ py::arg("comm"), py::arg("nx"), py::arg("ny"), py::arg("diagonal")="right")
+ .def_static("create", [](std::array<std::size_t, 2> n, dolfin::CellType::Type cell_type,
+ std::string diagonal)
+ { return dolfin::UnitSquareMesh::create(n, cell_type, diagonal); },
+ py::arg("n"), py::arg("cell_type"), py::arg("diagonal")="right")
+ .def_static("create", [](const MPICommWrapper comm, std::array<std::size_t, 2> n,
+ dolfin::CellType::Type cell_type, std::string diagonal="right")
+ { return dolfin::UnitSquareMesh::create(comm.get(), n, cell_type, diagonal); },
+ py::arg("comm"), py::arg("n"), py::arg("cell_type"), py::arg("diagonal")="right")
+ // Remove below for 2018.1 release
+ .def_static("create", [](std::size_t nx, std::size_t ny, dolfin::CellType::Type cell_type)
+ { return dolfin::UnitSquareMesh::create({nx, ny}, cell_type); },
+ py::arg("nx"), py::arg("ny"), py::arg("cell_type"))
+ .def_static("create", [](const MPICommWrapper comm, std::size_t nx, std::size_t ny,
+ dolfin::CellType::Type cell_type)
+ { return dolfin::UnitSquareMesh::create(comm.get(), {nx, ny}, cell_type); },
+ py::arg("comm"), py::arg("nx"), py::arg("ny"), py::arg("cell_type"));
// dolfin::UnitCubeMesh
py::class_<dolfin::UnitCubeMesh, std::shared_ptr<dolfin::UnitCubeMesh>, dolfin::Mesh>(m, "UnitCubeMesh")
.def(py::init<std::size_t, std::size_t, std::size_t>())
- .def(py::init<MPI_Comm, std::size_t, std::size_t, std::size_t>());
+ .def(py::init([](const MPICommWrapper comm, std::size_t nx, std::size_t ny, std::size_t nz)
+ { return std::unique_ptr<dolfin::UnitCubeMesh>(new dolfin::UnitCubeMesh(comm.get(), nx, ny, nz)); }),
+ py::arg("comm"), py::arg("nx"), py::arg("ny"), py::arg("nz"))
+ .def_static("create", [](std::array<std::size_t, 3> n, dolfin::CellType::Type cell_type)
+ { return dolfin::UnitCubeMesh::create(n, cell_type); },
+ py::arg("n"), py::arg("cell_type"))
+ .def_static("create", [](const MPICommWrapper comm, std::array<std::size_t, 3> n,
+ dolfin::CellType::Type cell_type)
+ { return dolfin::UnitCubeMesh::create(comm.get(), n, cell_type); },
+ py::arg("comm"), py::arg("n"), py::arg("cell_type"))
+ // Remove below for 2018.1 release
+ .def_static("create", [](std::size_t nx, std::size_t ny, std::size_t nz,
+ dolfin::CellType::Type cell_type)
+ { return dolfin::UnitCubeMesh::create({nx, ny, nz}, cell_type); },
+ py::arg("nx"), py::arg("ny"), py::arg("nz"), py::arg("cell_type"))
+ .def_static("create", [](const MPICommWrapper comm, std::size_t nx, std::size_t ny, std::size_t nz,
+ dolfin::CellType::Type cell_type)
+ { return dolfin::UnitCubeMesh::create(comm.get(), {nx, ny, nz}, cell_type); },
+ py::arg("comm"), py::arg("nx"), py::arg("ny"), py::arg("nz"), py::arg("cell_type"));
// dolfin::UnitDiscMesh
py::class_<dolfin::UnitDiscMesh>(m, "UnitDiscMesh")
- .def_static("create", &dolfin::UnitDiscMesh::create);
+ .def_static("create", [](const MPICommWrapper comm, std::size_t n, std::size_t degree, std::size_t gdim)
+ { return dolfin::UnitDiscMesh::create(comm.get(), n, degree, gdim); });
// dolfin::SphericalShellMesh
py::class_<dolfin::SphericalShellMesh>(m, "SphericalShellMesh")
- .def_static("create", &dolfin::SphericalShellMesh::create);
+ .def_static("create", [](const MPICommWrapper comm, std::size_t degree)
+ { return dolfin::SphericalShellMesh::create(comm.get(), degree); });
// dolfin::UnitTriangleMesh
py::class_<dolfin::UnitTriangleMesh>(m, "UnitTriangleMesh")
.def_static("create", &dolfin::UnitTriangleMesh::create);
- // dolfin::UnitQuadMesh
- py::class_<dolfin::UnitQuadMesh>(m, "UnitQuadMesh")
- .def_static("create", [](std::size_t nx, std::size_t ny)
- { return dolfin::UnitQuadMesh::create(nx, ny); })
- .def_static("create", [](MPI_Comm comm, std::size_t nx, std::size_t ny)
- { return dolfin::UnitQuadMesh::create(comm, nx, ny); });
-
- // dolfin::UnitHexMesh
- py::class_<dolfin::UnitHexMesh>(m, "UnitHexMesh")
- .def_static("create", [](std::size_t nx, std::size_t ny, std::size_t nz)
- { return dolfin::UnitHexMesh::create(nx, ny, nz); })
- .def_static("create", [](MPI_Comm comm, std::size_t nx, std::size_t ny, std::size_t nz)
- { return dolfin::UnitHexMesh::create(comm, nx, ny, nz); });
-
// dolfin::BoxMesh
py::class_<dolfin::BoxMesh, std::shared_ptr<dolfin::BoxMesh>, dolfin::Mesh>(m, "BoxMesh")
.def(py::init<const dolfin::Point&, const dolfin::Point&, std::size_t, std::size_t, std::size_t>())
- .def(py::init<MPI_Comm, const dolfin::Point&, const dolfin::Point&, std::size_t, std::size_t, std::size_t>());
+ .def(py::init([](const MPICommWrapper comm, const dolfin::Point& p0, const dolfin::Point& p1,
+ std::size_t nx, std::size_t ny, std::size_t nz)
+ { return std::unique_ptr<dolfin::BoxMesh>(new dolfin::BoxMesh(comm.get(), p0, p1, nx, ny, nz)); }),
+ py::arg("comm"), py::arg("p0"), py::arg("p1"), py::arg("nx"), py::arg("ny"), py::arg("nz"));
}
}
diff --git a/python/src/geometry.cpp b/python/src/geometry.cpp
index 17cc268..f4b3d70 100644
--- a/python/src/geometry.cpp
+++ b/python/src/geometry.cpp
@@ -26,6 +26,8 @@
#include <dolfin/geometry/intersect.h>
#include <dolfin/geometry/BoundingBoxTree.h>
#include <dolfin/geometry/MeshPointIntersection.h>
+#include <dolfin/geometry/CollisionPredicates.h>
+#include <dolfin/geometry/IntersectionConstruction.h>
#include <dolfin/geometry/Point.h>
#include <dolfin/mesh/Mesh.h>
@@ -55,7 +57,7 @@ namespace dolfin_wrappers
.def("compute_entity_collisions",
(std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
(dolfin::BoundingBoxTree::*)(const dolfin::BoundingBoxTree&) const)
- &dolfin::BoundingBoxTree::compute_entity_collisions)
+ &dolfin::BoundingBoxTree::compute_entity_collisions)
.def("compute_first_collision", &dolfin::BoundingBoxTree::compute_first_collision)
.def("compute_first_entity_collision", &dolfin::BoundingBoxTree::compute_first_entity_collision)
.def("compute_closest_entity", &dolfin::BoundingBoxTree::compute_closest_entity);
@@ -116,10 +118,12 @@ namespace dolfin_wrappers
})
.def(py::self + py::self)
.def(py::self - py::self)
+ .def(py::self == py::self)
.def(py::self * float())
.def(py::self / float())
- .def("array", [](dolfin::Point& self)
- { return Eigen::Map<Eigen::Vector3d>(self.coordinates()); })
+ .def("array",
+ [](dolfin::Point& self) { return Eigen::Vector3d(self.coordinates()); },
+ "Return copy of coordinate array")
.def("norm", &dolfin::Point::norm)
.def("x", &dolfin::Point::x)
.def("y", &dolfin::Point::y)
@@ -132,6 +136,27 @@ namespace dolfin_wrappers
(m, "MeshPointIntersection")
.def("intersected_cells", &dolfin::MeshPointIntersection::intersected_cells);
+ // These classes are wrapped only to be able to write tests in python.
+ // They are not imported into the dolfin namespace in python, but must be accessed through
+ // dolfin.cpp.geometry
+ py::class_<dolfin::CollisionPredicates>(m, "CollisionPredicates")
+ .def_static("collides_segment_point_2d",
+ &dolfin::CollisionPredicates::collides_segment_point_2d)
+ .def_static("collides_triangle_point_2d",
+ &dolfin::CollisionPredicates::collides_triangle_point_2d)
+ .def_static("collides_triangle_triangle_2d",
+ &dolfin::CollisionPredicates::collides_triangle_triangle_2d)
+ .def_static("collides_segment_segment_2d",
+ &dolfin::CollisionPredicates::collides_segment_segment_2d);
+
+ py::class_<dolfin::IntersectionConstruction>(m, "IntersectionConstruction")
+ .def_static("intersection_triangle_triangle_2d",
+ &dolfin::IntersectionConstruction::intersection_triangle_triangle_2d)
+ .def_static("intersection_segment_segment_2d",
+ &dolfin::IntersectionConstruction::intersection_segment_segment_2d)
+ .def_static("intersection_triangle_segment_2d",
+ &dolfin::IntersectionConstruction::intersection_triangle_segment_2d);
+
// dolfin/geometry free functions
m.def("intersect", &dolfin::intersect);
diff --git a/python/src/io.cpp b/python/src/io.cpp
index 0d35030..96272f1 100644
--- a/python/src/io.cpp
+++ b/python/src/io.cpp
@@ -48,7 +48,9 @@ namespace dolfin_wrappers
py::class_<dolfin::File, std::shared_ptr<dolfin::File>>(m, "File")
.def(py::init<std::string>())
.def(py::init<std::string, std::string>())
- .def(py::init<MPI_Comm, std::string>())
+ .def(py::init([](const MPICommWrapper comm, std::string filename)
+ { return std::unique_ptr<dolfin::File>(new dolfin::File(comm.get(), filename)); }),
+ py::arg("comm"), py::arg("filename"))
//
.def("write", (void (dolfin::File::*)(const dolfin::Parameters&)) &dolfin::File::write)
//
@@ -225,8 +227,11 @@ namespace dolfin_wrappers
.def("type_str", &dolfin::HDF5Attribute::type_str);
// dolfin::HDF5File
- py::class_<dolfin::HDF5File, std::shared_ptr<dolfin::HDF5File>> (m, "HDF5File")
- .def(py::init<MPI_Comm, std::string, std::string>())
+ py::class_<dolfin::HDF5File, std::shared_ptr<dolfin::HDF5File>,
+ dolfin::Variable> (m, "HDF5File")
+ .def(py::init([](const MPICommWrapper comm, const std::string filename, const std::string file_mode)
+ { return std::unique_ptr<dolfin::HDF5File>(new dolfin::HDF5File(comm.get(), filename, file_mode)); }),
+ py::arg("comm"), py::arg("filename"), py::arg("file_mode"))
.def("__enter__", [](dolfin::HDF5File& self){ return &self; })
.def("__exit__", [](dolfin::HDF5File& self, py::args args, py::kwargs kwargs){ self.close(); })
.def("close", &dolfin::HDF5File::close)
@@ -301,16 +306,20 @@ namespace dolfin_wrappers
}, py::arg("u"), py::arg("name"), py::arg("t"))
.def("set_mpi_atomicity", &dolfin::HDF5File::set_mpi_atomicity)
.def("get_mpi_atomicity", &dolfin::HDF5File::get_mpi_atomicity)
- // attributes
+ // others
+ .def("has_dataset", &dolfin::HDF5File::has_dataset)
.def("attributes", &dolfin::HDF5File::attributes);
#endif
// dolfin::XDMFFile
- py::class_<dolfin::XDMFFile, std::shared_ptr<dolfin::XDMFFile>> xdmf_file(m, "XDMFFile");
+ py::class_<dolfin::XDMFFile, std::shared_ptr<dolfin::XDMFFile>,
+ dolfin::Variable> xdmf_file(m, "XDMFFile");
xdmf_file
- .def(py::init<MPI_Comm, std::string>())
+ .def(py::init([](const MPICommWrapper comm, std::string filename)
+ { return std::unique_ptr<dolfin::XDMFFile>(new dolfin::XDMFFile(comm.get(), filename)); }),
+ py::arg("comm"), py::arg("filename"))
.def(py::init<std::string>())
.def("__enter__", [](dolfin::XDMFFile& self){ return &self; })
.def("__exit__", [](dolfin::XDMFFile& self, py::args args, py::kwargs kwargs){ self.close(); });
@@ -436,7 +445,7 @@ namespace dolfin_wrappers
{
if (color.ndim() != 1 or color.shape(0) != 3)
throw pybind11::type_error("Color must be a 1D array or length 3");
- self.set_diffuse_color({*color.data(0), *color.data(1), *color.data(2)});
+ self.set_diffuse_color({{*color.data(0), *color.data(1), *color.data(2)}});
});
// dolfin::X3DOM
diff --git a/python/src/la.cpp b/python/src/la.cpp
index b93801c..0706777 100644
--- a/python/src/la.cpp
+++ b/python/src/la.cpp
@@ -22,6 +22,10 @@
#include <pybind11/stl.h>
#include <pybind11/operators.h>
+#ifdef HAS_PYBIND11_PETSC4PY
+#include <petsc4py/petsc4py.h>
+#endif
+
#include "casters.h"
#include <dolfin/common/Array.h>
@@ -44,9 +48,10 @@
#include <dolfin/la/EigenFactory.h>
#include <dolfin/la/EigenMatrix.h>
#include <dolfin/la/EigenVector.h>
+#include <dolfin/la/PETScFactory.h>
#include <dolfin/la/PETScKrylovSolver.h>
#include <dolfin/la/PETScLUSolver.h>
-#include <dolfin/la/PETScFactory.h>
+#include <dolfin/la/PETScLinearOperator.h>
#include <dolfin/la/PETScMatrix.h>
#include <dolfin/la/PETScOptions.h>
#include <dolfin/la/PETScPreconditioner.h>
@@ -72,29 +77,68 @@ namespace
template<typename T>
void check_indices(const py::array_t<T>& x, std::int64_t local_size)
{
- for (std::size_t i = 0; i < x.size(); ++i)
+ for (std::int64_t i = 0; i < (std::int64_t) x.size(); ++i)
{
std::int64_t _x = *(x.data() + i);
if (_x < 0 or !(_x < local_size))
throw py::index_error("Vector index out of range");
}
}
+
+ // Linear operator trampoline class
+ template<typename LinearOperatorBase>
+ class PyLinearOperator : public LinearOperatorBase
+ {
+ using LinearOperatorBase::LinearOperatorBase;
+
+ // pybdind11 has some issues when passing by reference (due to
+ // the return value policy), so the below is non-standard. See
+ // https://github.com/pybind/pybind11/issues/250.
+
+ std::size_t size(std::size_t dim) const
+ { PYBIND11_OVERLOAD_PURE(std::size_t, LinearOperatorBase, size, ); }
+
+ void mult(const dolfin::GenericVector& x, dolfin::GenericVector& y) const
+ { PYBIND11_OVERLOAD_INT(void, LinearOperatorBase, "mult", &x, &y); }
+ };
+
+ // Linear operator trampoline class (with pure virtual 'mult'
+ // function)
+ template<typename LinearOperatorBase>
+ class PyLinearOperatorPure : public LinearOperatorBase
+ {
+ using LinearOperatorBase::LinearOperatorBase;
+
+ std::size_t size(std::size_t dim) const
+ { PYBIND11_OVERLOAD_PURE(std::size_t, LinearOperatorBase, size, ); }
+
+ void mult(const dolfin::GenericVector& x, dolfin::GenericVector& y) const
+ {
+ PYBIND11_OVERLOAD_INT(void, LinearOperatorBase, "mult", &x, &y);
+ py::pybind11_fail("Tried to call pure virtual function \'mult\'");
+ }
+ };
+
}
namespace dolfin_wrappers
{
+ using RowMatrixXd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
+
void la(py::module& m)
{
-#ifdef HAS_PETSC4PY
- int ierr = import_petsc4py();
- if (ierr != 0)
- throw std::runtime_error("Failed to import petsc4py");
-#endif
-
// dolfin::IndexMap
py::class_<dolfin::IndexMap, std::shared_ptr<dolfin::IndexMap>> index_map(m, "IndexMap");
- index_map.def("size", &dolfin::IndexMap::size);
- index_map.def("local_range", &dolfin::IndexMap::local_range);
+ index_map.def("size", &dolfin::IndexMap::size)
+ .def("block_size", &dolfin::IndexMap::block_size)
+ .def("local_range", &dolfin::IndexMap::local_range)
+ .def("local_to_global_unowned",
+ [](dolfin::IndexMap& self) {
+ return Eigen::Map<const Eigen::Matrix<std::size_t, Eigen::Dynamic, 1>>(
+ self.local_to_global_unowned().data(),
+ self.local_to_global_unowned().size()); },
+ py::return_value_policy::reference_internal,
+ "Return view into unowned part of local-to-global map");
// dolfin::IndexMap enums
py::enum_<dolfin::IndexMap::MapSize>(index_map, "MapSize")
@@ -157,7 +201,8 @@ namespace dolfin_wrappers
});
// dolfin::TensorLayout
- py::class_<dolfin::TensorLayout, std::shared_ptr<dolfin::TensorLayout>> tensor_layout(m, "TensorLayout");
+ py::class_<dolfin::TensorLayout, std::shared_ptr<dolfin::TensorLayout>,
+ dolfin::Variable> tensor_layout(m, "TensorLayout");
// dolfin::TensorLayout enums
py::enum_<dolfin::TensorLayout::Sparsity>(tensor_layout, "Sparsity")
@@ -168,36 +213,49 @@ namespace dolfin_wrappers
.value("UNGHOSTED", dolfin::TensorLayout::Ghosts::UNGHOSTED);
tensor_layout
- .def(py::init<MPI_Comm, std::size_t, dolfin::TensorLayout::Sparsity>())
- .def(py::init<MPI_Comm, std::vector<std::shared_ptr<const dolfin::IndexMap>>,
- std::size_t, dolfin::TensorLayout::Sparsity, dolfin::TensorLayout::Ghosts>())
+ .def(py::init([](const MPICommWrapper comm, std::size_t primary_dim,
+ dolfin::TensorLayout::Sparsity sparsity_pattern)
+ { return std::unique_ptr<dolfin::TensorLayout>(new dolfin::TensorLayout(comm.get(), primary_dim, sparsity_pattern)); }))
+ .def(py::init([](const MPICommWrapper comm,
+ std::vector<std::shared_ptr<const dolfin::IndexMap>> index_maps,
+ std::size_t primary_dim, dolfin::TensorLayout::Sparsity sparsity_pattern,
+ dolfin::TensorLayout::Ghosts ghosted)
+ { return std::unique_ptr<dolfin::TensorLayout>(new dolfin::TensorLayout(comm.get(), index_maps, primary_dim,
+ sparsity_pattern, ghosted)); }))
.def("init", &dolfin::TensorLayout::init)
.def("sparsity_pattern", (std::shared_ptr<dolfin::SparsityPattern> (dolfin::TensorLayout::*)()) &dolfin::TensorLayout::sparsity_pattern);
// dolfin::LinearAlgebraObject
py::class_<dolfin::LinearAlgebraObject, std::shared_ptr<dolfin::LinearAlgebraObject>,
dolfin::Variable>(m, "LinearAlgebraObject")
- .def("mpi_comm", &dolfin::GenericLinearOperator::mpi_comm);
+ .def("mpi_comm", [](dolfin::LinearAlgebraObject& self)
+ { return MPICommWrapper(self.mpi_comm()); });
// dolfin::GenericLinearOperator
py::class_<dolfin::GenericLinearOperator, std::shared_ptr<dolfin::GenericLinearOperator>,
- dolfin::LinearAlgebraObject>
- (m, "GenericLinearOperator", "DOLFIN GenericLinearOperator object")
- .def("mult", &dolfin::GenericLinearOperator::mult);
+ PyLinearOperatorPure<dolfin::GenericLinearOperator>, dolfin::LinearAlgebraObject>
+ (m, "GenericLinearOperator", "GenericLinearOperator object");
// dolfin::GenericTensor
py::class_<dolfin::GenericTensor, std::shared_ptr<dolfin::GenericTensor>,
dolfin::LinearAlgebraObject>
(m, "GenericTensor", "DOLFIN GenericTensor object")
.def("init", &dolfin::GenericTensor::init)
+ .def("empty", &dolfin::GenericTensor::empty)
+ .def("factory", &dolfin::GenericTensor::factory)
+ .def("local_range", &dolfin::GenericTensor::local_range)
+ .def("rank", &dolfin::GenericTensor::rank)
+ .def("size", &dolfin::GenericTensor::size)
+ .def("str", &dolfin::GenericTensor::str)
.def("zero", &dolfin::GenericTensor::zero);
// dolfin::GenericMatrix
py::class_<dolfin::GenericMatrix, std::shared_ptr<dolfin::GenericMatrix>,
dolfin::GenericTensor, dolfin::GenericLinearOperator>
- (m, "GenericMatrix", "DOLFIN GenericMatrix object")
+ (m, "GenericMatrix", py::dynamic_attr(), "DOLFIN GenericMatrix object")
.def("init_vector", &dolfin::GenericMatrix::init_vector)
.def("axpy", &dolfin::GenericMatrix::axpy)
+ .def("mult", &dolfin::GenericMatrix::mult)
.def("transpmult", &dolfin::GenericMatrix::transpmult)
// __ifoo__
.def("__imul__", &dolfin::GenericMatrix::operator*=, "Multiply by a scalar")
@@ -235,7 +293,7 @@ namespace dolfin_wrappers
{
if (x.ndim() != 1)
throw py::index_error("NumPy must be a 1D array for multiplication by a GenericMatrix");
- if (x.size() != self.size(1))
+ if ((std::size_t) x.size() != self.size(1))
throw py::index_error("Length of array must match number of matrix columns");
auto _x = self.factory().create_vector(self.mpi_comm());
@@ -261,9 +319,34 @@ namespace dolfin_wrappers
.def("norm", &dolfin::GenericMatrix::norm)
.def("nnz", &dolfin::GenericMatrix::nnz)
.def("size", &dolfin::GenericMatrix::size)
+ .def("apply", &dolfin::GenericMatrix::apply)
.def("get_diagonal", &dolfin::GenericMatrix::get_diagonal)
.def("set_diagonal", &dolfin::GenericMatrix::set_diagonal)
- .def("ident_zeros", &dolfin::GenericMatrix::ident_zeros)
+ .def("ident_zeros", &dolfin::GenericMatrix::ident_zeros, py::arg("tol") = DOLFIN_EPS)
+ .def("ident", [](dolfin::GenericMatrix& self, std::vector<dolfin::la_index> rows)
+ { self.ident(rows.size(), rows.data()); }, py::arg("rows"))
+ .def("get", [](dolfin::GenericMatrix& self, Eigen::Ref<RowMatrixXd> block,
+ const std::vector<dolfin::la_index> rows,
+ const std::vector<dolfin::la_index> cols)
+ {
+ if ((std::size_t) block.rows() != rows.size())
+ throw py::value_error("Block must have the same number of rows as len(rows)");
+ if ((std::size_t) block.cols() != cols.size())
+ throw py::value_error("Block must have the same number of columns as len(cols)");
+ self.get((double *) block.data(), rows.size(), rows.data(),
+ cols.size(), cols.data());
+ }, py::arg("block"), py::arg("rows"), py::arg("cols"))
+ .def("set", [](dolfin::GenericMatrix& self, const Eigen::Ref<const RowMatrixXd> block,
+ const std::vector<dolfin::la_index> rows,
+ const std::vector<dolfin::la_index> cols)
+ {
+ if ((std::size_t) block.rows() != rows.size())
+ throw py::value_error("Block must have the same number of rows as len(rows)");
+ if ((std::size_t) block.cols() != cols.size())
+ throw py::value_error("Block must have the same number of columns as len(cols)");
+ self.set((const double *) block.data(), rows.size(), rows.data(),
+ cols.size(), cols.data());
+ }, py::arg("block"), py::arg("rows"), py::arg("cols"))
.def("getrow", [](const dolfin::GenericMatrix& instance, std::size_t row)
{
std::vector<double> values;
@@ -272,7 +355,7 @@ namespace dolfin_wrappers
auto _columns = py::array_t<std::size_t>(columns.size(), columns.data());
auto _values = py::array_t<double>(values.size(), values.data());
return std::make_pair(_columns, _values);
- })
+ }, py::arg("row"))
.def("array", [](const dolfin::GenericMatrix& instance)
{
// FIXME: This function is highly dubious. It assumes a
@@ -299,7 +382,7 @@ namespace dolfin_wrappers
// dolfin::GenericVector
py::class_<dolfin::GenericVector, std::shared_ptr<dolfin::GenericVector>,
dolfin::GenericTensor>
- (m, "GenericVector", "DOLFIN GenericVector object")
+ (m, "GenericVector", py::dynamic_attr(), "DOLFIN GenericVector object")
.def("init", (void (dolfin::GenericVector::*)(std::size_t)) &dolfin::GenericVector::init)
.def("init", (void (dolfin::GenericVector::*)(const dolfin::TensorLayout&)) &dolfin::GenericVector::init)
.def("init", (void (dolfin::GenericVector::*)(std::pair<std::size_t, std::size_t>)) &dolfin::GenericVector::init)
@@ -366,7 +449,7 @@ namespace dolfin_wrappers
{
if (indices.ndim() != 1)
throw py::index_error("Indices must be a 1D array");
- if (indices.size() != self.local_size())
+ if ((std::size_t) indices.size() != self.local_size())
throw py::index_error("Indices size mismatch");
check_indices(indices, self.local_size());
@@ -376,7 +459,7 @@ namespace dolfin_wrappers
// Extract filtered values
std::vector<double> filtered;
- for (std::size_t i = 0; i < indices.size(); ++i)
+ for (std::size_t i = 0; i < (std::size_t) indices.size(); ++i)
{
bool e = *(indices.data() + i);
if (e)
@@ -474,13 +557,19 @@ namespace dolfin_wrappers
.def("__len__", [](dolfin::GenericVector& self) { return self.local_size(); })
.def("size", (std::size_t (dolfin::GenericVector::*)() const) &dolfin::GenericVector::size)
//
- .def("get_local", [](const dolfin::GenericVector& instance, const std::vector<long>& rows)
+ .def("get_local", [](const dolfin::GenericVector& instance,
+ const std::vector<dolfin::la_index>& rows)
{
- std::vector<dolfin::la_index> _rows(rows.begin(), rows.end());
py::array_t<double> data(rows.size());
- instance.get_local(data.mutable_data(), _rows.size(), _rows.data());
+ instance.get_local(data.mutable_data(), rows.size(), rows.data());
return data;
})
+ .def("get_local", [](const dolfin::GenericVector& instance)
+ {
+ std::vector<double> values;
+ instance.get_local(values);
+ return py::array_t<double>(values.size(), values.data());
+ })
.def("set_local", [](dolfin::GenericVector& instance, std::vector<double> values)
{
std::vector<dolfin::la_index> indices(values.size());
@@ -527,23 +616,16 @@ namespace dolfin_wrappers
.def("local_range", (std::pair<std::int64_t, std::int64_t> (dolfin::GenericVector::*)() const) &dolfin::GenericVector::local_range)
.def("owns_index", &dolfin::GenericVector::owns_index)
.def("apply", &dolfin::GenericVector::apply)
- .def("array", [](const dolfin::GenericVector& instance)
- {
- std::vector<double> values;
- instance.get_local(values);
- return py::array_t<double>(values.size(), values.data());
- })
.def_property_readonly("__array_priority__", [](const dolfin::GenericVector& self){ return 0; });
-
-
// dolfin::Matrix
py::class_<dolfin::Matrix, std::shared_ptr<dolfin::Matrix>, dolfin::GenericMatrix>
(m, "Matrix", "DOLFIN Matrix object")
.def(py::init<>())
.def(py::init<const dolfin::Matrix&>()) // Remove? (use copy instead)
.def(py::init<const dolfin::GenericMatrix&>()) // Remove? (use copy instead)
- .def(py::init<MPI_Comm>()) // This comes last of constructors so pybind11 attempts it lasts (avoid OpenMPI comm casting problems)
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::Matrix>(new dolfin::Matrix(comm.get())); }))
// Enabling the below messes up the operators because pybind11
// then fails to try the GenericMatrix __mul__ operators
/*
@@ -568,8 +650,10 @@ namespace dolfin_wrappers
.def(py::init<>())
.def(py::init<const dolfin::Vector&>())
.def(py::init<const dolfin::GenericVector&>())
- .def(py::init<MPI_Comm>())
- .def(py::init<MPI_Comm, std::size_t>())
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::Vector>(new dolfin::Vector(comm.get())); }))
+ .def(py::init([](const MPICommWrapper comm, std::size_t N)
+ { return std::unique_ptr<dolfin::Vector>(new dolfin::Vector(comm.get(), N)); }))
.def("min", &dolfin::Vector::min)
.def("max", &dolfin::Vector::max)
.def("abs", &dolfin::Vector::abs)
@@ -619,40 +703,21 @@ namespace dolfin_wrappers
py::class_<dolfin::Scalar, std::shared_ptr<dolfin::Scalar>, dolfin::GenericTensor>
(m, "Scalar")
.def(py::init<>())
- .def(py::init<MPI_Comm>())
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::Scalar>(new dolfin::Scalar(comm.get())); }))
.def("add_local_value", &dolfin::Scalar::add_local_value)
.def("apply", &dolfin::Scalar::apply)
- .def("mpi_comm", &dolfin::Scalar::mpi_comm)
+ .def("mpi_comm", [](dolfin::Scalar& self)
+ { return MPICommWrapper(self.mpi_comm()); })
.def("get_scalar_value", &dolfin::Scalar::get_scalar_value);
- class PyLinearOperator : public dolfin::LinearOperator
- {
- // dolfin::LinearOperator trampoline class
-
- using dolfin::LinearOperator::LinearOperator;
-
- // pybdind11 has some issues when passing by reference (due to
- // the return value policy), so the below is non-standard. See
- // https://github.com/pybind/pybind11/issues/250.
-
- std::size_t size(std::size_t dim) const
- {
- PYBIND11_OVERLOAD_PURE(std::size_t, dolfin::LinearOperator, size, );
- }
-
- void mult(const dolfin::GenericVector& x, dolfin::GenericVector& y) const
- {
- PYBIND11_OVERLOAD_INT(void, dolfin::LinearOperator, "mult", &x, &y);
- py::pybind11_fail("Tried to call pure virtual function dolfin::LinearOpertor::mult");
- }
- };
-
// dolfin::LinearOperator
py::class_<dolfin::LinearOperator, std::shared_ptr<dolfin::LinearOperator>,
- PyLinearOperator, dolfin::GenericLinearOperator>
+ PyLinearOperatorPure<dolfin::LinearOperator>, dolfin::GenericLinearOperator>
(m, "LinearOperator")
- //.def(py::init<>())
- .def(py::init<const dolfin::GenericVector&, const dolfin::GenericVector&>());
+ .def(py::init<const dolfin::GenericVector&, const dolfin::GenericVector&>())
+ .def("instance", (std::shared_ptr<dolfin::LinearAlgebraObject>(dolfin::LinearOperator::*)())
+ &dolfin::LinearOperator::shared_instance);
// dolfin::GenericLinearAlgebraFactory
py::class_<dolfin::GenericLinearAlgebraFactory, std::shared_ptr<dolfin::GenericLinearAlgebraFactory>>
@@ -663,26 +728,32 @@ namespace dolfin_wrappers
(m, "DefaultFactory", "DOLFIN DefaultFactory object")
.def(py::init<>())
.def_static("factory", &dolfin::DefaultFactory::factory)
- .def("create_matrix", &dolfin::DefaultFactory::create_matrix)
- .def("create_vector", &dolfin::DefaultFactory::create_vector);
+ .def("create_matrix", [](const dolfin::DefaultFactory &self, const MPICommWrapper comm)
+ { return self.create_matrix(comm.get()); })
+ .def("create_vector", [](const dolfin::DefaultFactory &self, const MPICommWrapper comm)
+ { return self.create_vector(comm.get()); });
// dolfin::EigenFactory
py::class_<dolfin::EigenFactory, std::shared_ptr<dolfin::EigenFactory>,
dolfin::GenericLinearAlgebraFactory>
(m, "EigenFactory", "DOLFIN EigenFactory object")
.def("instance", &dolfin::EigenFactory::instance)
- .def("create_matrix", &dolfin::EigenFactory::create_matrix)
- .def("create_vector", &dolfin::EigenFactory::create_vector);
+ .def("create_matrix", [](const dolfin::EigenFactory &self, const MPICommWrapper comm)
+ { return self.create_matrix(comm.get()); })
+ .def("create_vector", [](const dolfin::EigenFactory &self, const MPICommWrapper comm)
+ { return self.create_vector(comm.get()); });
// dolfin::EigenVector
py::class_<dolfin::EigenVector, std::shared_ptr<dolfin::EigenVector>,
dolfin::GenericVector>
(m, "EigenVector", "DOLFIN EigenVector object")
.def(py::init<>())
- .def(py::init<MPI_Comm>())
- .def(py::init<MPI_Comm, std::size_t>())
- //.def("array", (std::shared_ptr<Eigen::VectorXd> (dolfin::EigenVector::*)()) &dolfin::EigenVector::vec);
- .def("array", [](dolfin::EigenVector& self) -> Eigen::Ref<Eigen::VectorXd> { return *self.vec(); } );
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::EigenVector>(new dolfin::EigenVector(comm.get())); }))
+ .def(py::init([](const MPICommWrapper comm, std::size_t N)
+ { return std::unique_ptr<dolfin::EigenVector>(new dolfin::EigenVector(comm.get(), N)); }))
+ .def("array_view", [](dolfin::EigenVector& self) -> Eigen::Ref<Eigen::VectorXd> { return *self.vec(); },
+ "Return a writable numpy array view of the data in the EigenVector");
// dolfin::EigenMatrix
py::class_<dolfin::EigenMatrix, std::shared_ptr<dolfin::EigenMatrix>,
@@ -732,43 +803,67 @@ namespace dolfin_wrappers
.def_static("clear", (void (*)(std::string)) &dolfin::PETScOptions::clear)
.def_static("clear", (void (*)()) &dolfin::PETScOptions::clear);
+ // dolfin::PETScObject
py::class_<dolfin::PETScObject, std::shared_ptr<dolfin::PETScObject>>(m, "PETScObject");
// dolfin::PETScFactory
py::class_<dolfin::PETScFactory, std::shared_ptr<dolfin::PETScFactory>,
- dolfin::GenericLinearAlgebraFactory>
+ dolfin::GenericLinearAlgebraFactory>
(m, "PETScFactory", "DOLFIN PETScFactory object")
.def("instance", &dolfin::PETScFactory::instance)
- .def("create_matrix", &dolfin::PETScFactory::create_matrix)
- .def("create_vector", &dolfin::PETScFactory::create_vector);
+ .def("create_matrix", [](const dolfin::PETScFactory &self, const MPICommWrapper comm)
+ { return self.create_matrix(comm.get()); })
+ .def("create_vector", [](const dolfin::PETScFactory &self, const MPICommWrapper comm)
+ { return self.create_vector(comm.get()); });
// dolfin::PETScVector
py::class_<dolfin::PETScVector, std::shared_ptr<dolfin::PETScVector>,
dolfin::GenericVector, dolfin::PETScObject>
(m, "PETScVector", "DOLFIN PETScVector object")
.def(py::init<>())
- .def(py::init<MPI_Comm>())
- .def(py::init<MPI_Comm, std::size_t>())
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::PETScVector>(new dolfin::PETScVector(comm.get())); }))
+ .def(py::init([](const MPICommWrapper comm, std::size_t N)
+ { return std::unique_ptr<dolfin::PETScVector>(new dolfin::PETScVector(comm.get(), N)); }))
+ .def(py::init<Vec>())
.def("get_options_prefix", &dolfin::PETScVector::get_options_prefix)
.def("set_options_prefix", &dolfin::PETScVector::set_options_prefix)
- .def("update_ghost_values", &dolfin::PETScVector::update_ghost_values);
+ .def("update_ghost_values", &dolfin::PETScVector::update_ghost_values)
+ .def("vec", &dolfin::PETScVector::vec, "Return underlying PETSc Vec object");
// dolfin::PETScBaseMatrix
py::class_<dolfin::PETScBaseMatrix, std::shared_ptr<dolfin::PETScBaseMatrix>,
- dolfin::PETScObject, dolfin::Variable>(m, "PETScBaseMatrix");
+ dolfin::PETScObject, dolfin::Variable>(m, "PETScBaseMatrix")
+ .def("size", (std::size_t (dolfin::PETScBaseMatrix::*)(std::size_t) const) &dolfin::PETScBaseMatrix::size)
+ .def("mat", &dolfin::PETScBaseMatrix::mat, "Return underlying PETSc Mat object");
+
+ // dolfin::PETScLinearOperator
+ py::class_<dolfin::PETScLinearOperator, std::shared_ptr<dolfin::PETScLinearOperator>,
+ PyLinearOperator<dolfin::PETScLinearOperator>, dolfin::PETScBaseMatrix,
+ dolfin::GenericLinearOperator>
+ (m, "PETScLinearOperator", "PETScLinearOperator object")
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::PETScLinearOperator>(new dolfin::PETScLinearOperator(comm.get())); }))
+ .def("size", &dolfin::PETScLinearOperator::size)
+ .def("mult", &dolfin::PETScLinearOperator::mult)
+ .def("mpi_comm", [](dolfin::PETScLinearOperator& self)
+ { return MPICommWrapper(self.mpi_comm()); });
// dolfin::PETScMatrix
py::class_<dolfin::PETScMatrix, std::shared_ptr<dolfin::PETScMatrix>,
dolfin::GenericMatrix, dolfin::PETScBaseMatrix>
(m, "PETScMatrix", "DOLFIN PETScMatrix object")
.def(py::init<>())
- .def(py::init<MPI_Comm>())
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::PETScMatrix>(new dolfin::PETScMatrix(comm.get())); }))
+ .def(py::init<Mat>())
.def("get_options_prefix", &dolfin::PETScMatrix::get_options_prefix)
.def("set_options_prefix", &dolfin::PETScMatrix::set_options_prefix)
.def("set_nullspace", &dolfin::PETScMatrix::set_nullspace)
.def("set_near_nullspace", &dolfin::PETScMatrix::set_near_nullspace);
- py::class_<dolfin::PETScPreconditioner, std::shared_ptr<dolfin::PETScPreconditioner>>
+ py::class_<dolfin::PETScPreconditioner, std::shared_ptr<dolfin::PETScPreconditioner>,
+ dolfin::Variable>
(m, "PETScPreconditioner", "DOLFIN PETScPreconditioner object")
.def(py::init<std::string>(), py::arg("type")="default")
.def("preconditioners", &dolfin::PETScPreconditioner::preconditioners);
@@ -779,18 +874,23 @@ namespace dolfin_wrappers
// dolfin::TpetraFactory
py::class_<dolfin::TpetraFactory, std::shared_ptr<dolfin::TpetraFactory>,
- dolfin::GenericLinearAlgebraFactory>
+ dolfin::GenericLinearAlgebraFactory>
(m, "TpetraFactory", "DOLFIN TpetraFactory object")
.def("instance", &dolfin::TpetraFactory::instance)
- .def("create_matrix", &dolfin::TpetraFactory::create_matrix)
- .def("create_vector", &dolfin::TpetraFactory::create_vector);
+ .def("create_matrix", [](const dolfin::TpetraFactory &self, const MPICommWrapper comm)
+ { return self.create_matrix(comm.get()); })
+ .def("create_vector", [](const dolfin::TpetraFactory &self, const MPICommWrapper comm)
+ { return self.create_vector(comm.get()); });
// dolfin::TpetraVector
py::class_<dolfin::TpetraVector, std::shared_ptr<dolfin::TpetraVector>,
dolfin::GenericVector>
(m, "TpetraVector", "DOLFIN TpetraVector object")
- .def(py::init<MPI_Comm>(), py::arg("comm")=MPI_COMM_WORLD)
- .def(py::init<MPI_Comm, std::size_t>());
+ .def(py::init([](const MPICommWrapper comm=MPICommWrapper(MPI_COMM_WORLD))
+ { return std::unique_ptr<dolfin::TpetraVector>(new dolfin::TpetraVector(comm.get())); }),
+ py::arg("comm")=MPICommWrapper(MPI_COMM_WORLD))
+ .def(py::init([](const MPICommWrapper comm, std::size_t N)
+ { return std::unique_ptr<dolfin::TpetraVector>(new dolfin::TpetraVector(comm.get(), N)); }));
// dolfin::TpetraMatrix
py::class_<dolfin::TpetraMatrix, std::shared_ptr<dolfin::TpetraMatrix>,
@@ -810,7 +910,7 @@ namespace dolfin_wrappers
// dolfin::BelosKrylovSolver
py::class_<dolfin::BelosKrylovSolver, std::shared_ptr<dolfin::BelosKrylovSolver>,
- dolfin::GenericLinearSolver>
+ dolfin::GenericLinearSolver>
(m, "BelosKrylovSolver", "Belos KrylovSolver")
.def(py::init<std::string, std::shared_ptr<dolfin::TrilinosPreconditioner>>())
.def("set_operator", &dolfin::BelosKrylovSolver::set_operator)
@@ -826,14 +926,16 @@ namespace dolfin_wrappers
// dolfin::LUSolver
py::class_<dolfin::LUSolver, std::shared_ptr<dolfin::LUSolver>,
- dolfin::GenericLinearSolver>
- (m, "LUSolver", "DOLFIN LUSolver object")
+ dolfin::GenericLinearSolver>
+ (m, "LUSolver", "DOLFIN LUSolver object")
.def(py::init<>())
.def(py::init<std::shared_ptr<const dolfin::GenericLinearOperator>, std::string>(),
py::arg("A"), py::arg("method")="default")
- .def(py::init<MPI_Comm, std::shared_ptr<const dolfin::GenericLinearOperator>,
- std::string>(),
- py::arg("comm"), py::arg("A"), py::arg("method") = "default")
+ .def(py::init([](const MPICommWrapper comm,
+ std::shared_ptr<const dolfin::GenericLinearOperator> A,
+ std::string method="default")
+ { return std::unique_ptr<dolfin::LUSolver>(new dolfin::LUSolver(comm.get(), A, method)); }),
+ py::arg("comm"), py::arg("A"), py::arg("method") = "default")
.def("set_operator", &dolfin::LUSolver::set_operator)
.def("solve", (std::size_t (dolfin::LUSolver::*)(dolfin::GenericVector&,
const dolfin::GenericVector&))
@@ -846,19 +948,30 @@ namespace dolfin_wrappers
#ifdef HAS_PETSC
// dolfin::PETScLUSolver
py::class_<dolfin::PETScLUSolver, std::shared_ptr<dolfin::PETScLUSolver>,
- dolfin::GenericLinearSolver>
+ dolfin::GenericLinearSolver>
(m, "PETScLUSolver", "DOLFIN PETScLUSolver object")
- .def(py::init<MPI_Comm, std::string>(), py::arg("comm"), py::arg("method")="default")
.def(py::init<std::string>(), py::arg("method")="default")
- .def(py::init<MPI_Comm, std::shared_ptr<const dolfin::PETScMatrix>, std::string>(),
- py::arg("comm"), py::arg("A"), py::arg("method")="default")
+ .def(py::init([](const MPICommWrapper comm,
+ std::string method="default")
+ { return std::unique_ptr<dolfin::PETScLUSolver>(new dolfin::PETScLUSolver(comm.get(), method)); }),
+ py::arg("comm"), py::arg("method") = "default")
+ .def(py::init([](const MPICommWrapper comm,
+ std::shared_ptr<const dolfin::PETScMatrix> A,
+ std::string method="default")
+ { return std::unique_ptr<dolfin::PETScLUSolver>(new dolfin::PETScLUSolver(comm.get(), A, method)); }),
+ py::arg("comm"), py::arg("A"), py::arg("method") = "default")
.def(py::init<std::shared_ptr<const dolfin::PETScMatrix>, std::string>(),
py::arg("A"), py::arg("method")="default")
.def("get_options_prefix", &dolfin::PETScLUSolver::get_options_prefix)
.def("set_options_prefix", &dolfin::PETScLUSolver::set_options_prefix)
.def("solve", (std::size_t (dolfin::PETScLUSolver::*)(dolfin::GenericVector&, const dolfin::GenericVector&))
- &dolfin::PETScLUSolver::solve);
- #endif
+ &dolfin::PETScLUSolver::solve)
+ .def("solve", (std::size_t (dolfin::PETScLUSolver::*)(const dolfin::GenericLinearOperator&,
+ dolfin::GenericVector&,
+ const dolfin::GenericVector&))
+ &dolfin::PETScLUSolver::solve)
+ .def("ksp", &dolfin::PETScLUSolver::ksp);
+#endif
// dolfin::KrylovSolver
py::class_<dolfin::KrylovSolver, std::shared_ptr<dolfin::KrylovSolver>,
@@ -869,13 +982,20 @@ namespace dolfin_wrappers
.def(py::init<std::shared_ptr<const dolfin::GenericLinearOperator>,
std::string, std::string>(), py::arg("A"),
py::arg("method")="default", py::arg("preconditioner")="default")
- .def(py::init<MPI_Comm, std::shared_ptr<const dolfin::GenericLinearOperator>,
- std::string, std::string>(), py::arg("comm"), py::arg("A"),
- py::arg("method")="default", py::arg("preconditioner")="default")
+ .def(py::init([](const MPICommWrapper comm,
+ std::shared_ptr<const dolfin::GenericLinearOperator> A,
+ std::string method="default",
+ std::string preconditioner="default")
+ { return std::unique_ptr<dolfin::KrylovSolver>(new dolfin::KrylovSolver(comm.get(), A,
+ method, preconditioner)); }),
+ py::arg("comm"), py::arg("A"), py::arg("method") = "default", py::arg("preconditioner")="default")
.def("set_operator", &dolfin::KrylovSolver::set_operator)
.def("set_operators", &dolfin::KrylovSolver::set_operators)
.def("solve", (std::size_t (dolfin::KrylovSolver::*)(dolfin::GenericVector&,
const dolfin::GenericVector&))
+ &dolfin::KrylovSolver::solve)
+ .def("solve", (std::size_t (dolfin::KrylovSolver::*)(const dolfin::GenericLinearOperator&,
+ dolfin::GenericVector&, const dolfin::GenericVector&))
&dolfin::KrylovSolver::solve);
#ifdef HAS_PETSC
@@ -901,6 +1021,9 @@ namespace dolfin_wrappers
&dolfin::PETScKrylovSolver::set_operators)
.def("solve", (std::size_t (dolfin::PETScKrylovSolver::*)(dolfin::GenericVector&, const dolfin::GenericVector&))
&dolfin::PETScKrylovSolver::solve)
+ .def("solve", (std::size_t (dolfin::PETScKrylovSolver::*)(const dolfin::GenericLinearOperator&,
+ dolfin::GenericVector&, const dolfin::GenericVector&))
+ &dolfin::PETScKrylovSolver::solve)
.def("set_from_options", &dolfin::PETScKrylovSolver::set_from_options)
.def("set_reuse_preconditioner", &dolfin::PETScKrylovSolver::set_reuse_preconditioner)
.def("set_dm", &dolfin::PETScKrylovSolver::set_dm)
@@ -917,15 +1040,18 @@ namespace dolfin_wrappers
#ifdef HAS_SLEPC
// dolfin::SLEPcEigenSolver
- py::class_<dolfin::SLEPcEigenSolver, std::shared_ptr<dolfin::SLEPcEigenSolver>, dolfin::Variable>(m, "SLEPcEigenSolver")
+ py::class_<dolfin::SLEPcEigenSolver, std::shared_ptr<dolfin::SLEPcEigenSolver>,
+ dolfin::Variable>(m, "SLEPcEigenSolver")
.def(py::init<std::shared_ptr<const dolfin::PETScMatrix>>())
.def(py::init<std::shared_ptr<const dolfin::PETScMatrix>, std::shared_ptr<const dolfin::PETScMatrix>>())
// FIXME: The below must come after the other
// constructors. Check the MPI_Comm caster raises appropriate
// exceptions for pybind11 to move onto next interface.
- .def(py::init<MPI_Comm>())
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::SLEPcEigenSolver>(new dolfin::SLEPcEigenSolver(comm.get())); }))
.def("set_options_prefix", &dolfin::SLEPcEigenSolver::set_options_prefix)
.def("set_from_options", &dolfin::SLEPcEigenSolver::set_from_options)
+ .def("set_operators", &dolfin::SLEPcEigenSolver::set_operators)
.def("get_options_prefix", &dolfin::SLEPcEigenSolver::get_options_prefix)
.def("get_number_converged", &dolfin::SLEPcEigenSolver::get_number_converged)
.def("set_deflation_space", &dolfin::SLEPcEigenSolver::set_deflation_space)
@@ -960,7 +1086,8 @@ namespace dolfin_wrappers
.def("__getitem__", &dolfin::VectorSpaceBasis::operator[]);
// test_nullspace.h
- m.def("in_nullspace", &dolfin::in_nullspace, py::arg("A"), py::arg("x"), py::arg("type")="right");
+ m.def("in_nullspace", &dolfin::in_nullspace, py::arg("A"), py::arg("x"),
+ py::arg("type")="right");
// la free functions
m.def("has_linear_algebra_backend", &dolfin::has_linear_algebra_backend);
diff --git a/python/src/log.cpp b/python/src/log.cpp
index a792d52..15e5192 100644
--- a/python/src/log.cpp
+++ b/python/src/log.cpp
@@ -33,8 +33,19 @@ namespace dolfin_wrappers
{
void log(py::module& m)
{
+
+ // dolfin::LogLevel enums
+ py::enum_<dolfin::LogLevel>(m, "LogLevel", py::arithmetic())
+ .value("DEBUG", dolfin::LogLevel::DBG)
+ .value("TRACE", dolfin::LogLevel::TRACE)
+ .value("PROGRESS", dolfin::LogLevel::PROGRESS)
+ .value("INFO", dolfin::LogLevel::INFO)
+ .value("WARNING", dolfin::LogLevel::WARNING)
+ .value("ERROR", dolfin::LogLevel::ERROR)
+ .value("CRITICAL", dolfin::LogLevel::CRITICAL);
+
// dolfin::Table
- py::class_<dolfin::Table, std::shared_ptr<dolfin::Table>>(m, "Table")
+ py::class_<dolfin::Table, std::shared_ptr<dolfin::Table>, dolfin::Variable>(m, "Table")
.def(py::init<std::string>())
.def("str", &dolfin::Table::str);
@@ -49,15 +60,7 @@ namespace dolfin_wrappers
py::arg("scheme"), py::arg("verbose")=false);
m.def("set_log_level", &dolfin::set_log_level);
m.def("get_log_level", &dolfin::get_log_level);
+ m.def("log", [](dolfin::LogLevel level, std::string s){ dolfin::log(level, s); });
- // dolfin::LogLevel enums
- py::enum_<dolfin::LogLevel>(m, "LogLevel")
- .value("DEBUG", dolfin::LogLevel::DBG)
- .value("TRACE", dolfin::LogLevel::TRACE)
- .value("PROGRESS", dolfin::LogLevel::PROGRESS)
- .value("INFO", dolfin::LogLevel::INFO)
- .value("WARNING", dolfin::LogLevel::WARNING)
- .value("ERROR", dolfin::LogLevel::ERROR)
- .value("CRITICAL", dolfin::LogLevel::CRITICAL);
}
}
diff --git a/python/src/mesh.cpp b/python/src/mesh.cpp
index 34988ed..2acf261 100644
--- a/python/src/mesh.cpp
+++ b/python/src/mesh.cpp
@@ -48,6 +48,8 @@
#include <dolfin/mesh/DomainBoundary.h>
#include <dolfin/mesh/PeriodicBoundaryComputation.h>
#include <dolfin/mesh/MeshTransformation.h>
+#include <dolfin/mesh/MultiMesh.h>
+#include <dolfin/function/Expression.h>
#include "casters.h"
@@ -90,32 +92,39 @@ namespace dolfin_wrappers
.def("degree", &dolfin::MeshGeometry::degree, "Degree");
// dolfin::MeshTopology class
- py::class_<dolfin::MeshTopology, std::shared_ptr<dolfin::MeshTopology>>
+ py::class_<dolfin::MeshTopology, std::shared_ptr<dolfin::MeshTopology>, dolfin::Variable>
(m, "MeshTopology", "DOLFIN MeshTopology object")
.def("dim", &dolfin::MeshTopology::dim, "Topological dimension")
+ .def("init", (void (dolfin::MeshTopology::*)(std::size_t)) &dolfin::MeshTopology::init)
+ .def("init", (void (dolfin::MeshTopology::*)(std::size_t, std::size_t, std::size_t))
+ &dolfin::MeshTopology::init)
.def("__call__", (const dolfin::MeshConnectivity& (dolfin::MeshTopology::*)(std::size_t, std::size_t) const)
&dolfin::MeshTopology::operator())
.def("size", &dolfin::MeshTopology::size)
.def("hash", &dolfin::MeshTopology::hash)
+ .def("init_global_indices", &dolfin::MeshTopology::init_global_indices)
.def("have_global_indices", &dolfin::MeshTopology::have_global_indices)
.def("ghost_offset", &dolfin::MeshTopology::ghost_offset)
.def("cell_owner", (const std::vector<unsigned int>& (dolfin::MeshTopology::*)() const) &dolfin::MeshTopology::cell_owner)
+ .def("set_global_index", &dolfin::MeshTopology::set_global_index)
.def("global_indices", [](const dolfin::MeshTopology& self, int dim)
{ auto& indices = self.global_indices(dim); return py::array_t<std::int64_t>(indices.size(), indices.data()); })
.def("have_shared_entities", &dolfin::MeshTopology::have_shared_entities)
.def("shared_entities",
(std::map<std::int32_t, std::set<unsigned int> >&(dolfin::MeshTopology::*)(unsigned int))
- &dolfin::MeshTopology::shared_entities);
+ &dolfin::MeshTopology::shared_entities)
+ .def("str", &dolfin::MeshTopology::str);
// dolfin::Mesh
- py::class_<dolfin::Mesh, std::shared_ptr<dolfin::Mesh>>(m, "Mesh",
- py::dynamic_attr(),
- "DOLFIN Mesh object")
+ py::class_<dolfin::Mesh, std::shared_ptr<dolfin::Mesh>, dolfin::Variable>
+ (m, "Mesh", py::dynamic_attr(), "DOLFIN Mesh object")
.def(py::init<>())
.def(py::init<std::string>())
.def(py::init<const dolfin::Mesh&>())
- .def(py::init<MPI_Comm>())
- .def(py::init<MPI_Comm, std::string>()) // Put MPI constructors last to avoid casting problems
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::Mesh>(new dolfin::Mesh(comm.get())); }))
+ .def(py::init([](const MPICommWrapper comm, const std::string filename)
+ { return std::unique_ptr<dolfin::Mesh>(new dolfin::Mesh(comm.get(), filename)); }))
.def("bounding_box_tree", &dolfin::Mesh::bounding_box_tree)
.def("cells", [](const dolfin::Mesh& self)
{
@@ -141,6 +150,7 @@ namespace dolfin_wrappers
&dolfin::Mesh::data, "Data associated with a mesh")
.def("geometry", (dolfin::MeshGeometry& (dolfin::Mesh::*)()) &dolfin::Mesh::geometry,
py::return_value_policy::reference, "Mesh geometry")
+ .def("hash", &dolfin::Mesh::hash)
.def("hmax", &dolfin::Mesh::hmax)
.def("hmin", &dolfin::Mesh::hmin)
.def("id", &dolfin::Mesh::id)
@@ -154,27 +164,30 @@ namespace dolfin_wrappers
auto _o = o.attr("_cpp_object").cast<dolfin::Expression*>();
self.init_cell_orientations(*_o);
})
- .def("mpi_comm", &dolfin::Mesh::mpi_comm)
- .def("num_entities", &dolfin::Mesh::num_entities, "Number of mesh entities")
+ .def("mpi_comm", [](dolfin::Mesh& self)
+ { return MPICommWrapper(self.mpi_comm()); })
+ .def("num_entities", &dolfin::Mesh::num_entities,
+ "Number of mesh entities")
.def("num_vertices", &dolfin::Mesh::num_vertices, "Number of vertices")
.def("num_edges", &dolfin::Mesh::num_edges, "Number of edges")
.def("num_faces", &dolfin::Mesh::num_faces, "Number of faces")
.def("num_facets", &dolfin::Mesh::num_facets, "Number of facets")
.def("num_cells", &dolfin::Mesh::num_cells, "Number of cells")
.def("ordered", &dolfin::Mesh::ordered)
- .def("size", &dolfin::Mesh::size)
.def("rmax", &dolfin::Mesh::rmax)
.def("rmin", &dolfin::Mesh::rmin)
.def("rotate", (void (dolfin::Mesh::*)(double, std::size_t, const dolfin::Point&))
&dolfin::Mesh::rotate)
.def("rotate", (void (dolfin::Mesh::*)(double, std::size_t)) &dolfin::Mesh::rotate,
py::arg("angle"), py::arg("axis")=2)
- .def("size_global", &dolfin::Mesh::size_global)
+ .def("num_entities_global", &dolfin::Mesh::num_entities_global)
.def("smooth", &dolfin::Mesh::smooth, py::arg("num_iterations")=1)
.def("smooth_boundary", &dolfin::Mesh::smooth_boundary)
- .def("snap_boundary", &dolfin::Mesh::snap_boundary, py::arg("subdomain"), py::arg("harmonic_smoothing")=true)
- .def("topology", (const dolfin::MeshTopology& (dolfin::Mesh::*)() const)
- &dolfin::Mesh::topology, "Mesh topology")
+ .def("snap_boundary", &dolfin::Mesh::snap_boundary, py::arg("subdomain"),
+ py::arg("harmonic_smoothing")=true)
+ .def("topology", (dolfin::MeshTopology& (dolfin::Mesh::*)())
+ &dolfin::Mesh::topology, "Mesh topology",
+ py::return_value_policy::reference_internal)
.def("translate", &dolfin::Mesh::translate)
.def("type", (const dolfin::CellType& (dolfin::Mesh::*)() const) &dolfin::Mesh::type,
py::return_value_policy::reference)
@@ -183,7 +196,8 @@ namespace dolfin_wrappers
{ return dolfin::CellType::type2string(self.type().cell_type()); });
// dolfin::MeshData
- py::class_<dolfin::MeshData, std::shared_ptr<dolfin::MeshData>>(m, "MeshData", "Mesh data object")
+ py::class_<dolfin::MeshData, std::shared_ptr<dolfin::MeshData>, dolfin::Variable>
+ (m, "MeshData", "Mesh data object")
.def("array", [](dolfin::MeshData& self, std::string key, std::size_t i)
{
const std::vector<std::size_t>& a = self.array(key, i);
@@ -208,9 +222,9 @@ namespace dolfin_wrappers
// dolfin::MeshConnectivity class
py::class_<dolfin::MeshConnectivity, std::shared_ptr<dolfin::MeshConnectivity>>
(m, "MeshConnectivity", "DOLFIN MeshConnectivity object")
- .def("__call__", [](const dolfin::MeshConnectivity& self, std::size_t i){
- return Eigen::Map<const Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>>
- (self(i), self.size(i));})
+ .def("__call__", [](const dolfin::MeshConnectivity& self, std::size_t i)
+ {
+ return Eigen::Map<const Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>>(self(i), self.size(i));})
.def("size", (std::size_t (dolfin::MeshConnectivity::*)() const)
&dolfin::MeshConnectivity::size)
.def("size", (std::size_t (dolfin::MeshConnectivity::*)(std::size_t) const)
@@ -230,10 +244,10 @@ namespace dolfin_wrappers
.def("num_global_entities", &dolfin::MeshEntity::num_global_entities,
"Global number of incident entities of given dimension")
.def("entities", [](dolfin::MeshEntity& self, std::size_t dim)
- {
- return Eigen::Map<const Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>>
- (self.entities(dim), self.num_entities(dim));
- })
+ {
+ return Eigen::Map<const Eigen::Matrix<unsigned int, Eigen::Dynamic, 1>>
+ (self.entities(dim), self.num_entities(dim));
+ })
.def("midpoint", &dolfin::MeshEntity::midpoint, "Midpoint of Entity")
.def("sharing_processes", &dolfin::MeshEntity::sharing_processes)
.def("is_shared", &dolfin::MeshEntity::is_shared)
@@ -265,7 +279,8 @@ namespace dolfin_wrappers
py::class_<dolfin::Facet, std::shared_ptr<dolfin::Facet>, dolfin::MeshEntity>
(m, "Facet", "DOLFIN Facet object")
.def(py::init<const dolfin::Mesh&, std::size_t>())
- .def("exterior", &dolfin::Facet::exterior);
+ .def("exterior", &dolfin::Facet::exterior)
+ .def("normal", (dolfin::Point (dolfin::Facet::*)() const) &dolfin::Facet::normal);
// dolfin::Cell
py::class_<dolfin::Cell, std::shared_ptr<dolfin::Cell>, dolfin::MeshEntity>
@@ -281,7 +296,6 @@ namespace dolfin_wrappers
.def("normal", (dolfin::Point (dolfin::Cell::*)(std::size_t) const) &dolfin::Cell::normal)
.def("circumradius", &dolfin::Cell::circumradius)
.def("radius_ratio", &dolfin::Cell::radius_ratio)
- .def("triangulate_intersection", &dolfin::Cell::triangulate_intersection)
.def("volume", &dolfin::Cell::volume)
.def("get_vertex_coordinates", [](const dolfin::Cell& self){
std::vector<double> x;
@@ -316,7 +330,6 @@ namespace dolfin_wrappers
return *self;
});
-
m.def("entities", [](dolfin::Mesh& mesh, std::size_t dim)
{ return dolfin::MeshEntityIterator(mesh, dim); });
m.def("entities", [](dolfin::MeshEntity& meshentity, std::size_t dim)
@@ -377,6 +390,7 @@ namespace dolfin_wrappers
.def("id", &dolfin::MeshFunction<SCALAR>::id) \
.def("ufl_id", &dolfin::MeshFunction<SCALAR>::id) \
.def("mesh", &dolfin::MeshFunction<SCALAR>::mesh) \
+ .def("set_values", &dolfin::MeshFunction<SCALAR>::set_values) \
.def("set_all", &dolfin::MeshFunction<SCALAR>::set_all) \
.def("where_equal", &dolfin::MeshFunction<SCALAR>::where_equal) \
.def("array", [](dolfin::MeshFunction<SCALAR>& self) \
@@ -388,32 +402,6 @@ namespace dolfin_wrappers
MESHFUNCTION_MACRO(std::size_t, Sizet);
#undef MESHFUNCTION_MACRO
-#define MESH_ENTITY_FUNCTION_MACRO(TYPE, SCALAR, SCALAR_NAME) \
- py::class_<dolfin::TYPE<SCALAR>, std::shared_ptr<dolfin::TYPE<SCALAR>>, \
- dolfin::MeshFunction<SCALAR>>(m, #TYPE""#SCALAR_NAME)
-
- MESH_ENTITY_FUNCTION_MACRO(VertexFunction, bool, Bool);
- MESH_ENTITY_FUNCTION_MACRO(VertexFunction, int, Int);
- MESH_ENTITY_FUNCTION_MACRO(VertexFunction, double, Double);
- MESH_ENTITY_FUNCTION_MACRO(VertexFunction, std::size_t, Sizet);
- MESH_ENTITY_FUNCTION_MACRO(EdgeFunction, bool, Bool);
- MESH_ENTITY_FUNCTION_MACRO(EdgeFunction, int, Int);
- MESH_ENTITY_FUNCTION_MACRO(EdgeFunction, double, Double);
- MESH_ENTITY_FUNCTION_MACRO(EdgeFunction, std::size_t, Sizet);
- MESH_ENTITY_FUNCTION_MACRO(FaceFunction, bool, Bool);
- MESH_ENTITY_FUNCTION_MACRO(FaceFunction, int, Int);
- MESH_ENTITY_FUNCTION_MACRO(FaceFunction, double, Double);
- MESH_ENTITY_FUNCTION_MACRO(FaceFunction, std::size_t, Sizet);
- MESH_ENTITY_FUNCTION_MACRO(FacetFunction, bool, Bool);
- MESH_ENTITY_FUNCTION_MACRO(FacetFunction, int, Int);
- MESH_ENTITY_FUNCTION_MACRO(FacetFunction, double, Double);
- MESH_ENTITY_FUNCTION_MACRO(FacetFunction, std::size_t, Sizet);
- MESH_ENTITY_FUNCTION_MACRO(CellFunction, bool, Bool);
- MESH_ENTITY_FUNCTION_MACRO(CellFunction, int, Int);
- MESH_ENTITY_FUNCTION_MACRO(CellFunction, double, Double);
- MESH_ENTITY_FUNCTION_MACRO(CellFunction, std::size_t, Sizet);
-#undef MESH_ENTITY_FUNCTION_MACRO
-
// dolfin::MeshValueCollection
#define MESHVALUECOLLECTION_MACRO(SCALAR, SCALAR_NAME) \
py::class_<dolfin::MeshValueCollection<SCALAR>, \
@@ -456,6 +444,10 @@ namespace dolfin_wrappers
&dolfin::MeshEditor::add_vertex)
.def("add_vertex", (void (dolfin::MeshEditor::*)(std::size_t, const std::vector<double>&))
&dolfin::MeshEditor::add_vertex)
+ .def("add_vertex_global", (void (dolfin::MeshEditor::*)(std::size_t, std::size_t, const dolfin::Point&))
+ &dolfin::MeshEditor::add_vertex_global)
+ .def("add_vertex_global", (void (dolfin::MeshEditor::*)(std::size_t, std::size_t, const std::vector<double>&))
+ &dolfin::MeshEditor::add_vertex_global)
.def("add_cell", (void (dolfin::MeshEditor::*)(std::size_t, const std::vector<std::size_t>&))
&dolfin::MeshEditor::add_cell)
.def("close", &dolfin::MeshEditor::close, py::arg("order") = true);
@@ -525,7 +517,7 @@ namespace dolfin_wrappers
// dolfin::MeshColoring
py::class_<dolfin::MeshColoring>(m, "MeshColoring")
- .def_static("cell_colors", (dolfin::CellFunction<std::size_t> (*)(std::shared_ptr<const dolfin::Mesh>, std::vector<std::size_t>))
+ .def_static("cell_colors", (dolfin::MeshFunction<std::size_t> (*)(std::shared_ptr<const dolfin::Mesh>, std::vector<std::size_t>))
&dolfin::MeshColoring::cell_colors)
.def_static("color_cells", &dolfin::MeshColoring::color_cells);
@@ -536,5 +528,21 @@ namespace dolfin_wrappers
.def_static("rotate", (void (*)(dolfin::Mesh&, double, std::size_t)) &dolfin::MeshTransformation::rotate)
.def_static("rotate", (void (*)(dolfin::Mesh&, double, std::size_t, const dolfin::Point&))
&dolfin::MeshTransformation::rotate);
+
+ py::class_<dolfin::MultiMesh, std::shared_ptr<dolfin::MultiMesh>,
+ dolfin::Variable>(m, "MultiMesh")
+ .def(py::init<>())
+ .def("add", &dolfin::MultiMesh::add)
+ .def("build", &dolfin::MultiMesh::build, py::arg("quadrature_order") = 2)
+ .def("num_parts", &dolfin::MultiMesh::num_parts)
+ .def("compute_volume", &dolfin::MultiMesh::compute_volume)
+ .def("part", &dolfin::MultiMesh::part)
+ .def("cut_cells", &dolfin::MultiMesh::cut_cells)
+ .def("uncut_cells", &dolfin::MultiMesh::uncut_cells)
+ .def("covered_cells", &dolfin::MultiMesh::covered_cells)
+ .def("quadrature_rules_cut_cells",
+ static_cast<const dolfin::MultiMesh::quadrature_rule(dolfin::MultiMesh::*)(std::size_t, unsigned int) const>(&dolfin::MultiMesh::quadrature_rules_cut_cells));
}
+
+
}
diff --git a/python/src/mpi_casters.h b/python/src/mpi_casters.h
index e9f8571..d3d00ec 100644
--- a/python/src/mpi_casters.h
+++ b/python/src/mpi_casters.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Chris Richardson and Garth N. Wells
+// Copyright (C) 2017 Chris Richardson, Garth N. Wells and Tormod Landet
//
// This file is part of DOLFIN.
//
@@ -19,138 +19,62 @@
#define _DOLFIN_PYBIND11_MPI
#include <pybind11/pybind11.h>
+#include "MPICommWrapper.h"
+// Macro for casting between dolfin and mpi4py MPI communicators
#ifdef HAS_MPI
-#include <mpi.h>
-// #ifdef HAS_MPI4PY
-// #include <mpi4py/mpi4py.h>
-// #endif
-#endif
-
-
-#ifdef HAS_MPI
-
-// Tools for managing MPI communicators
+#ifdef HAS_PYBIND11_MPI4PY
+#include <mpi4py/mpi4py.h>
+
+// Import mpi4py on demand
+#define VERIFY_MPI4PY(func) \
+ if (!func) \
+ { \
+ if (import_mpi4py() != 0) \
+ { \
+ std::cout << "ERROR: could not import mpi4py!" << std::endl; \
+ throw std::runtime_error("Error when importing mpi4py"); \
+ } \
+ }
namespace pybind11
{
namespace detail
{
-
-// // Caster used to convert MPI_Comm to a mpi4py communicator
-// template <> class type_caster<dolfin_wrappers::mpi_communicator>
-// {
-// public:
-// PYBIND11_TYPE_CASTER(dolfin_wrappers::mpi_communicator, _("mpi_communicator"));
-
-// // Helper from pybind to C++
-// bool load(handle src, bool)
-// {
-// //std::cout << "***Here I am" << std::endl;
-// return true;
-// }
-
-// // From C++ to Python
-// static handle cast(const dolfin_wrappers::mpi_communicator &src, return_value_policy, handle)
-// {
-// //std::cout << "cpp to Py for comm struct" << std::endl;
-// #ifdef HAS_MPI4PY
-// return PyMPIComm_New(src.comm);
-// #else
-// std::cerr << "Cannot convert MPI communicator to a mpi4py communicator. DOLFIN must be enabled \
-// with mpi4py for this functionality" << std::endl;
-// #endif
-// return nullptr;
-// }
-
-// operator dolfin_wrappers::mpi_communicator()
-// {
-// //std::cout << "****mpi comm op" << std::endl;
-// return value;
-// }
-
-// };
-
-
- // Custom type caster for OpenMPI MPI_Comm, in which MPI_Comm is
- // defined as a typedef of ompi_communicator_t*
- #ifdef OPEN_MPI
- template <> class type_caster<ompi_communicator_t>
- {
- public:
- PYBIND11_TYPE_CASTER(MPI_Comm, _("ompi_communicator_t"));
-
- // Pass communicator from Python to C++
- bool load(handle src, bool)
+ template <> class type_caster<dolfin_wrappers::MPICommWrapper>
{
- PyObject* obj = src.ptr();
- void* v = PyLong_AsVoidPtr(obj);
- value = reinterpret_cast<MPI_Comm>(v);
- if (PyErr_Occurred())
- return false;
-
- return true;
- }
-
- // Cast from C++ to Python (cast to pointer)
- static handle cast(MPI_Comm src, pybind11::return_value_policy policy, handle parent)
- {
- // Return a pybind11::handle (rather than a pybind11::object)
- return pybind11::cast(reinterpret_cast<std::uintptr_t>(src)).release();
- }
-
- operator MPI_Comm()
- { return value; }
+ public:
+ // Define this->value of type MPICommWrapper
+ PYBIND11_TYPE_CASTER(dolfin_wrappers::MPICommWrapper, _("MPICommWrapper"));
+
+ // Python to C++
+ bool load(handle src, bool)
+ {
+ // Simplified version of isinstance(src, mpi4py.MPI.Comm) - avoids segfault
+ // when pybind11 tries to convert some other random type to MPICommWrapper
+ if (not hasattr(src, "Allgather"))
+ return false;
+ VERIFY_MPI4PY(PyMPIComm_Get);
+ value = dolfin_wrappers::MPICommWrapper(*PyMPIComm_Get(src.ptr()));
+ return true;
+ }
+
+ // C++ to Python
+ static handle cast(dolfin_wrappers::MPICommWrapper src,
+ pybind11::return_value_policy policy, handle parent)
+ {
+ VERIFY_MPI4PY(PyMPIComm_New);
+ return pybind11::handle(PyMPIComm_New(src.get()));
+ }
+
+ operator dolfin_wrappers::MPICommWrapper()
+ {
+ return this->value;
+ }
};
-// #else
-
-// // Custom type caster for MPI_Comm, where MPI_Comm is defined
-// // as a typedef of int, when mpi4py is available
-// #ifdef HAS_MPI4PY
-// template <> class type_caster<MPI_Comm>
-// {
-// public:
-// PYBIND11_TYPE_CASTER(MPI_Comm, _("MPI_Comm"));
-
-// // From Python (possibly a mpi4py comm) to C++
-// bool load(handle src, bool)
-// {
-
-// PyObject* obj = src.ptr();
-
-// if (PyObject_TypeCheck(obj, &PyMPIComm_Type))
-// {
-// MPI_Comm *comm_p = PyMPIComm_Get(obj);
-// value = *comm_p;
-// if (PyErr_Occurred())
-// return false;
-// }
-// else if (PyObject_TypeCheck(obj, &PyLong_Type))
-// {
-// //std::cout << "In caster" << std::endl;
-// value = PyLong_AsLong(obj);
-// if (PyErr_Occurred())
-// return false;
-// }
-// else
-// std::cerr << "MPI communicator (MPI_Comm) type is unknown." << std::endl;
-
-// return true;
-// }
-
-// // Cast from C/C++ communicator to Python. Cannot return mpi4py
-// // caster because we cannot distinguish between MPI_Comm and int
-// static handle cast(const MPI_Comm &src, return_value_policy, handle)
-// { return PyLong_FromLong(src); }
-
-// operator MPI_Comm()
-// { return value; }
-// };
-// #endif
- #endif
-
}
}
-#endif
+#endif // HAS_PYBIND11_MPI4PY
+#endif // HAS_MPI
#endif
diff --git a/python/src/multistage.cpp b/python/src/multistage.cpp
index 3e1e616..baf7b3b 100644
--- a/python/src/multistage.cpp
+++ b/python/src/multistage.cpp
@@ -35,7 +35,7 @@ namespace dolfin_wrappers
void multistage(py::module& m)
{
// dolfin::MultiStageScheme
- py::class_<dolfin::MultiStageScheme, std::shared_ptr<dolfin::MultiStageScheme>>
+ py::class_<dolfin::MultiStageScheme, std::shared_ptr<dolfin::MultiStageScheme>, dolfin::Variable>
(m, "MultiStageScheme")
.def(py::init<std::vector<std::vector<std::shared_ptr<const dolfin::Form>>>,
std::shared_ptr<const dolfin::Form>,
@@ -58,7 +58,7 @@ namespace dolfin_wrappers
.def("step_interval", &dolfin::RKSolver::step_interval);
// dolfin::PointIntegralSolver
- py::class_<dolfin::PointIntegralSolver, std::shared_ptr<dolfin::PointIntegralSolver>>
+ py::class_<dolfin::PointIntegralSolver, std::shared_ptr<dolfin::PointIntegralSolver>, dolfin::Variable>
(m, "PointIntegralSolver")
.def(py::init<std::shared_ptr<dolfin::MultiStageScheme>>())
.def("reset_newton_solver", &dolfin::PointIntegralSolver::reset_newton_solver)
diff --git a/python/src/nls.cpp b/python/src/nls.cpp
index ed7afe4..7b533d4 100644
--- a/python/src/nls.cpp
+++ b/python/src/nls.cpp
@@ -95,7 +95,8 @@ namespace dolfin_wrappers
py::class_<dolfin::NewtonSolver, std::shared_ptr<dolfin::NewtonSolver>, PyNewtonSolver,
dolfin::Variable>(m, "NewtonSolver")
.def(py::init<>())
- .def(py::init<MPI_Comm>())
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::NewtonSolver>(new dolfin::NewtonSolver(comm.get())); }))
.def("solve", &dolfin::NewtonSolver::solve)
.def("converged", &PyPublicNewtonSolver::converged)
.def("solver_setup", &PyPublicNewtonSolver::solver_setup)
@@ -106,15 +107,21 @@ namespace dolfin_wrappers
py::class_<dolfin::PETScSNESSolver, std::shared_ptr<dolfin::PETScSNESSolver>,
dolfin::PETScObject>(m, "PETScSNESSolver")
.def(py::init<std::string>(), py::arg("nls_type")="default")
- .def(py::init<MPI_Comm, std::string>(), py::arg("comm"), py::arg("nls_type")="default")
+ .def(py::init([](const MPICommWrapper comm, std::string nls_type="default")
+ { return std::unique_ptr<dolfin::PETScSNESSolver>(
+ new dolfin::PETScSNESSolver(comm.get(), nls_type)); }),
+ py::arg("comm"), py::arg("nls_type") = "default")
.def_readwrite("parameters", &dolfin::PETScSNESSolver::parameters)
+ .def("snes", &dolfin::PETScSNESSolver::snes)
.def("solve", (std::pair<std::size_t, bool> (dolfin::PETScSNESSolver::*)(dolfin::NonlinearProblem&,
dolfin::GenericVector&))
&dolfin::PETScSNESSolver::solve);
// dolfin::TAOLinearBoundSolver
- py::class_<dolfin::TAOLinearBoundSolver>(m, "TAOLinearBoundSolver")
- .def(py::init<MPI_Comm>())
+ py::class_<dolfin::TAOLinearBoundSolver, std::shared_ptr<dolfin::TAOLinearBoundSolver>, dolfin::Variable>
+ (m, "TAOLinearBoundSolver")
+ .def(py::init([](const MPICommWrapper comm)
+ { return std::unique_ptr<dolfin::TAOLinearBoundSolver>(new dolfin::TAOLinearBoundSolver(comm.get())); }))
.def(py::init<std::string, std::string, std::string>(), py::arg("method")="default",
py::arg("ksp_type")="default", py::arg("pc_type")="default")
.def("solve", (std::size_t (dolfin::TAOLinearBoundSolver::*)
@@ -126,9 +133,10 @@ namespace dolfin_wrappers
// dolfin::PETScTAOSolver
py::class_<dolfin::PETScTAOSolver, std::shared_ptr<dolfin::PETScTAOSolver>, dolfin::PETScObject>(m, "PETScTAOSolver")
.def(py::init<>())
- .def(py::init([](MPI_Comm comm, std::string tao_type,
- std::string ksp_type, std::string pc_type)
- { return dolfin::PETScTAOSolver(comm, tao_type, ksp_type, pc_type); }),
+ .def(py::init([](const MPICommWrapper comm, std::string tao_type="default",
+ std::string ksp_type="default", std::string pc_type="default")
+ { return std::unique_ptr<dolfin::PETScTAOSolver>(
+ new dolfin::PETScTAOSolver(comm.get(), tao_type, ksp_type, pc_type)); }),
py::arg("comm"), py::arg("tao_type")="default",
py::arg("ksp_type")="default", py::arg("pc_type")="default")
.def_readwrite("parameters", &dolfin::PETScTAOSolver::parameters)
diff --git a/python/src/parameter.cpp b/python/src/parameter.cpp
index 9cefa29..7e7cb9a 100644
--- a/python/src/parameter.cpp
+++ b/python/src/parameter.cpp
@@ -94,6 +94,13 @@ namespace dolfin_wrappers
py::keep_alive<0, 1>())
.def("items", [](const dolfin::Parameters& p) { return py::make_iterator(p.begin(), p.end()); },
py::keep_alive<0, 1>())
+ .def("keys", [](const dolfin::Parameters& p)
+ {
+ std::vector<std::string> keys;
+ for (auto &q : p)
+ keys.push_back(q.first);
+ return keys;
+ })
// These set_range function should be remove - they're just duplication
.def("set_range", [](dolfin::Parameters& self, std::string name, double min, double max)
{ self[name].set_range(min, max);} )
@@ -188,6 +195,10 @@ namespace dolfin_wrappers
auto param = self.find_parameter_set(key);
*param = other;
})
+ .def("__getitem__", [](dolfin::Parameters& self, std::string key)
+ {
+ return self[key];
+ })
.def("parse", [](dolfin::Parameters& self, py::list argv)
{
if(argv.size() == 0)
@@ -207,7 +218,15 @@ namespace dolfin_wrappers
// dolfin::Parameter
py::class_<dolfin::Parameter, std::shared_ptr<dolfin::Parameter>>(m, "Parameter")
- .def("value", &dolfin::Parameter::value)
+ .def("value", [](const dolfin::Parameter& self)
+ {
+ py::object value;
+ if (self.is_set())
+ value = py::cast(self.value());
+ else
+ value = py::none();
+ return value;
+ })
.def("set_range", (void (dolfin::Parameter::*)(double, double)) &dolfin::Parameter::set_range)
.def("set_range", (void (dolfin::Parameter::*)(int, int)) &dolfin::Parameter::set_range)
.def("set_range", (void (dolfin::Parameter::*)(std::set<std::string>)) &dolfin::Parameter::set_range)
diff --git a/python/src/petsc_casters.h b/python/src/petsc_casters.h
index 3b1eabe..838d032 100644
--- a/python/src/petsc_casters.h
+++ b/python/src/petsc_casters.h
@@ -22,95 +22,88 @@
#include <pybind11/stl.h>
#ifdef HAS_PETSC
-#include <petscksp.h>
#include <petscdm.h>
+#include <petscksp.h>
+#include <petscmat.h>
+#include <petscsnes.h>
+#include <petscvec.h>
-#ifdef HAS_PETSC4PY
+// pybind11 casters for PETSc/petsc4py objects
+#ifdef HAS_PYBIND11_PETSC4PY
#include <petsc4py/petsc4py.h>
-#endif
+// Import petsc4py on demand
+#define VERIFY_PETSC4PY(func) \
+ if (!func) \
+ { \
+ if (import_petsc4py() != 0) \
+ { \
+ std::cout << "ERROR: could not import petsc4py!" << std::endl; \
+ throw std::runtime_error("Error when importing petsc4py"); \
+ } \
+ }
+
+// Macro for casting between dolfin and petsc4py objects
+#define PETSC_CASTER_MACRO(TYPE, NAME) \
+ template <> class type_caster<_p_##TYPE> \
+ { \
+ public: \
+ PYBIND11_TYPE_CASTER(TYPE, _(#NAME)); \
+ bool load(handle src, bool) \
+ { \
+ VERIFY_PETSC4PY(PyPetsc##TYPE##_Get); \
+ if (PyObject_TypeCheck(src.ptr(), &PyPetsc##TYPE##_Type) == 0) \
+ return false; \
+ value = PyPetsc##TYPE##_Get(src.ptr()); \
+ return true; \
+ } \
+ \
+ static handle cast(TYPE src, pybind11::return_value_policy policy, handle parent) \
+ { \
+ VERIFY_PETSC4PY(PyPetsc##TYPE##_New); \
+ return pybind11::handle(PyPetsc##TYPE##_New(src)); \
+ } \
+ \
+ operator TYPE() \
+ { return value; } \
+ }
+#else
+#define PETSC_CASTER_MACRO(TYPE, NAME) \
+ template <> class type_caster<_p_##TYPE> \
+ { \
+ public: \
+ PYBIND11_TYPE_CASTER(TYPE, _(#NAME)); \
+ bool load(handle src, bool) \
+ { \
+ throw std::runtime_error("DOLFIN has not been configured with petsc4py. Accessing underlying PETSc object requires petsc4py"); \
+ return false; \
+ } \
+ \
+ static handle cast(TYPE src, pybind11::return_value_policy policy, handle parent) \
+ { \
+ throw std::runtime_error("DOLFIN has not been configured with petsc4py. Accessing underlying PETSc object requires petsc4py"); \
+ return handle(); \
+ } \
+ \
+ operator TYPE() \
+ { return value; } \
+ }
+#endif
-// pybind11 casters for PETSc/petsc4py objects
namespace pybind11
{
namespace detail
{
- template <> class type_caster<_p_KSP>
- {
- public:
- PYBIND11_TYPE_CASTER(KSP, _("ksp"));
-
- // Pass communicator from Python to C++
- bool load(handle src, bool)
- {
- // FIXME: check reference counting
- //std::cout << "Py to c++" << std::endl;
- #ifdef HAS_PETSC4PY
- value = PyPetscKSP_Get(src.ptr());
- return true;
- #else
- throw std::runtime_error("DOLFIN has not been configured with petsc4py. Accessing underlying PETSc object requires petsc4py");
- return false;
- #endif
- }
-
- // Cast from C++ to Python (cast to pointer)
- static handle cast(KSP src, pybind11::return_value_policy policy, handle parent)
- {
- // FIXME: check reference counting
- #ifdef HAS_PETSC4PY
- //std::cout << "C++ to Python" << std::endl;
- return pybind11::handle(PyPetscKSP_New(src));
- #else
- throw std::runtime_error("DOLFIN has not been configured with petsc4py. Accessing underlying PETSc object requires petsc4py");
- return handle();
- #endif
- }
-
- operator KSP()
- { return value; }
- };
-
- template <> class type_caster<_p_DM>
- {
- public:
- PYBIND11_TYPE_CASTER(DM, _("DM"));
-
- // Pass communicator from Python to C++
- bool load(handle src, bool)
- {
- // FIXME: check reference counting
- #ifdef HAS_PETSC4PY
- //std::cout << "Py to C++ (DM)" << std::endl;
- value = PyPetscDM_Get(src.ptr());
- //std::cout << "Returning" << std::endl;
- return true;
- #else
- throw std::runtime_error("DOLFIN has not been configured with petsc4py. Accessing underlying PETSc object requires petsc4py");
- return false;
- #endif
-
- }
-
- // Cast from C++ to Python (cast to pointer)
- static handle cast(DM src, pybind11::return_value_policy policy, handle parent)
- {
- // FIXME: check reference counting
- #ifdef HAS_PETSC4PY
- //std::cout << "C++ to Python (DM)" << std::endl;
- return pybind11::handle(PyPetscDM_New(src));
- #else
- throw std::runtime_error("DOLFIN has not been configured with petsc4py. Accessing underlying PETSc object requires petsc4py");
- return pybind11::handle();
- #endif
- }
-
- operator DM()
- { return value; }
- };
+ PETSC_CASTER_MACRO(DM, dm);
+ PETSC_CASTER_MACRO(KSP, ksp);
+ PETSC_CASTER_MACRO(Mat, mat);
+ PETSC_CASTER_MACRO(SNES, snes);
+ PETSC_CASTER_MACRO(Vec, vec);
}
}
+#undef PETSC_CASTER_MACRO
+
#endif
#endif
diff --git a/scripts/dolfin-order/mesh0.xml.gz b/scripts/dolfin-order/mesh0.xml.gz
deleted file mode 100644
index 3b62961..0000000
Binary files a/scripts/dolfin-order/mesh0.xml.gz and /dev/null differ
diff --git a/scripts/dolfin-order/mesh1.xml.gz b/scripts/dolfin-order/mesh1.xml.gz
deleted file mode 100644
index 79ef4a5..0000000
Binary files a/scripts/dolfin-order/mesh1.xml.gz and /dev/null differ
diff --git a/scripts/dolfin-plot/mesh.xml.gz b/scripts/dolfin-plot/mesh.xml.gz
deleted file mode 100644
index df60442..0000000
Binary files a/scripts/dolfin-plot/mesh.xml.gz and /dev/null differ
diff --git a/shippable.yml b/shippable.yml
deleted file mode 100644
index 0df9b73..0000000
--- a/shippable.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-language: none
-build:
- pre_ci_boot:
- image_name: quay.io/fenicsproject/dev-env
- image_tag: latest
- pull: true
- ci:
- - apt-get -qq -y --no-install-recommends install cmake
- - pip3 install --upgrade pip
- - pip3 install git+https://bitbucket.org/fenics-project/fiat.git@master
- - pip3 install git+https://bitbucket.org/fenics-project/ufl.git@master
- - pip3 install git+https://bitbucket.org/fenics-project/dijitso.git@master
- - pip3 install git+https://bitbucket.org/fenics-project/instant.git@master
- - pip3 install git+https://bitbucket.org/fenics-project/ffc.git@master
- - mkdir -p build
- - cd build
- - cmake .. -DCMAKE_BUILD_TYPE=Debug -DDOLFIN_ENABLE_TESTING=ON
- - make
- - make install
- - source /usr/local/share/dolfin/dolfin.conf
- - make run_unittests
- - make run run_regressiontests
diff --git a/site-packages/dolfin/common/plotting.py b/site-packages/dolfin/common/plotting.py
index d9b01b8..6ee37cc 100644
--- a/site-packages/dolfin/common/plotting.py
+++ b/site-packages/dolfin/common/plotting.py
@@ -24,7 +24,6 @@
from __future__ import print_function
import os
-from distutils.version import StrictVersion
import dolfin
import dolfin.cpp as cpp
@@ -125,7 +124,7 @@ def mplot_function(ax, f, **kwargs):
if fvec.size() == mesh.num_cells():
# DG0 cellwise function
- C = fvec.array() # NB! Assuming here dof ordering matching cell numbering
+ C = fvec.get_local() # NB! Assuming here dof ordering matching cell numbering
if gdim == 2 and tdim == 2:
return ax.tripcolor(mesh2triang(mesh), C, **kwargs)
elif gdim == 3 and tdim == 2: # surface in 3d
@@ -223,14 +222,6 @@ def mplot_function(ax, f, **kwargs):
if mode == "glyphs":
args = X + U + [C]
if gdim == 3:
- # 3d quiver plot works only since matplotlib 1.4
- import matplotlib
- if StrictVersion(matplotlib.__version__) < '1.4':
- cpp.warning('Matplotlib version %s does not support 3d '
- 'quiver plot. Continuing without plotting...'
- % matplotlib.__version__)
- return
-
length = kwargs.pop("length", 0.1)
return ax.quiver(*args, length=length, **kwargs)
else:
diff --git a/site-packages/dolfin/compilemodules/compilemodule.py b/site-packages/dolfin/compilemodules/compilemodule.py
index cad627a..e4906e8 100644
--- a/site-packages/dolfin/compilemodules/compilemodule.py
+++ b/site-packages/dolfin/compilemodules/compilemodule.py
@@ -47,7 +47,7 @@ __all__ = ["compile_extension_module",
# Bump the interface version if anything changes that invalidates
# cached modules (not required for change in generated code, swig
# version or dolfin version)
-_interface_version = 6
+_interface_version = 8
# Add max_signature_length to parameter system (don't need this in C++)
cpp.parameters.add("max_signature_length", 0)
diff --git a/site-packages/dolfin/fem/assembling.py b/site-packages/dolfin/fem/assembling.py
index d7a37f8..ca9009e 100644
--- a/site-packages/dolfin/fem/assembling.py
+++ b/site-packages/dolfin/fem/assembling.py
@@ -241,12 +241,13 @@ def assemble_multimesh(form,
# Extract multimesh function spaces for arguments
V_multi = [v._V_multi for v in arguments]
- # Exstract number of parts, the multimesh and create the multimesh form
+ # Extract number of parts, the multimesh and create the multimesh form
+ num_parts = None
if rank > 0:
num_parts = V_multi[0].num_parts()
multimesh_form = cpp.MultiMeshForm(*V_multi)
multimesh = V_multi[0].multimesh()
- else:
+ elif len(coefficients) > 0:
for coeff in coefficients:
# Only create these variables once
if isinstance(coeff, MultiMeshFunction):
@@ -254,7 +255,12 @@ def assemble_multimesh(form,
num_parts = coeff.function_space().num_parts()
multimesh_form = cpp.MultiMeshForm(multimesh)
break
- # Developer note: This won't work for assemble_multimesh(Constant(1)*dX)
+
+ if not num_parts:
+ # Handle the case Constant(1)*dx(domain=multimesh)
+ multimesh = form.ufl_domains()[0].ufl_cargo()
+ num_parts = multimesh.num_parts()
+ multimesh_form = cpp.MultiMeshForm(multimesh)
# Build multimesh DOLFIN form
for part in range(num_parts):
@@ -283,6 +289,10 @@ def assemble_multimesh(form,
dolfin_form.set_mesh(multimesh.part(part))
multimesh_form.add(dolfin_form)
+ for i, coeff in enumerate(coefficients):
+ if isinstance(coeff, MultiMeshFunction):
+ multimesh_form.set_multimesh_coefficient(i, coeff)
+
# Build multimesh form
multimesh_form.build()
diff --git a/site-packages/dolfin/fem/form.py b/site-packages/dolfin/fem/form.py
index b375091..00a6181 100644
--- a/site-packages/dolfin/fem/form.py
+++ b/site-packages/dolfin/fem/form.py
@@ -78,6 +78,8 @@ class Form(cpp.Form):
self.subdomains, = list(sd.values()) # Assuming single domain
domain, = list(sd.keys()) # Assuming single domain
mesh = domain.ufl_cargo()
+ if isinstance(mesh, cpp.MultiMesh):
+ mesh = mesh.part(0)
# Having a mesh in the form is now a strict requirement
if mesh is None:
diff --git a/site-packages/dolfin/fem/interpolation.py b/site-packages/dolfin/fem/interpolation.py
index a03a8ee..ce5976c 100644
--- a/site-packages/dolfin/fem/interpolation.py
+++ b/site-packages/dolfin/fem/interpolation.py
@@ -4,7 +4,7 @@
:py:class:`Expression <dolfin.functions.expression.Expression>` onto a
finite element space."""
-# Copyright (C) 2009-2011 Anders Logg
+# Copyright (C) 2009-2017 Anders Logg
#
# This file is part of DOLFIN.
#
@@ -21,29 +21,35 @@ finite element space."""
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
+# Modified by Jørgen S. Dokken, 2017
# First added: 2009-08-29
-# Last changed: 2011-11-15
+# Last changed: 2017-03-17
# Local imports
import dolfin.cpp as cpp
-from dolfin.functions.functionspace import FunctionSpace
+from dolfin.functions.functionspace import FunctionSpace, MultiMeshFunctionSpace
from dolfin.functions.function import Function
+from dolfin.functions.multimeshfunction import MultiMeshFunction
__all__ = ["interpolate"]
def interpolate(v, V):
"""Return interpolation of a given function into a given finite
- element space.
+ element space. Also supports MultiMesh.
*Arguments*
v
a :py:class:`Function <dolfin.functions.function.Function>` or
an :py:class:`Expression <dolfin.functions.expression.Expression>`
+ or an :py:class`MultiMeshFunction <dolfin.functions.multimeshfunction.MultiMeshFunction>`
V
a :py:class:`FunctionSpace (standard, mixed, etc.)
- <dolfin.functions.functionspace.FunctionSpace>`
+ <dolfin.functions.functionspace.FunctionSpace>` or
+ a :py:class:`MultiMeshFunctionSpace
+ <dolfin.cpp.function.MultiMeshFunctionSpace>`.
+
*Example of usage*
@@ -56,10 +62,14 @@ def interpolate(v, V):
"""
# Check arguments
- if not isinstance(V, FunctionSpace):
+ if not isinstance(V, (FunctionSpace, cpp.MultiMeshFunctionSpace)):
cpp.dolfin_error("interpolation.py",
"compute interpolation",
"Illegal function space for interpolation, not a FunctionSpace (%s)" % str(v))
+ if isinstance(V, cpp.MultiMeshFunctionSpace):
+ Pv = MultiMeshFunction(V)
+ Pv.interpolate(v)
+ return Pv
# Compute interpolation
Pv = Function(V)
diff --git a/site-packages/dolfin/fem/norms.py b/site-packages/dolfin/fem/norms.py
index 7a9bbc7..d5f02b5 100644
--- a/site-packages/dolfin/fem/norms.py
+++ b/site-packages/dolfin/fem/norms.py
@@ -4,7 +4,7 @@
<dolfin.functions.function.Function>`, including the standard
:math:`L^2`-norm and other norms."""
-# Copyright (C) 2008-2014 Anders Logg
+# Copyright (C) 2008-2017 Anders Logg
#
# This file is part of DOLFIN.
#
@@ -20,20 +20,25 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# Modified by: Jørgen S. Dokken, 2017
+# First added: 2007
+# Last changed: 2017-03-17
from six import string_types
import ufl
-from ufl import inner, grad, div, curl, dx, FiniteElement, VectorElement, Coefficient
+from ufl import inner, grad, div, curl, dx, FiniteElement, VectorElement, Coefficient, TensorElement
from math import sqrt
import dolfin.cpp as cpp
from dolfin.cpp import GenericVector, GenericFunction, Function, Mesh, error, Vector
-from dolfin.fem.assembling import assemble
+from dolfin.fem.assembling import assemble, assemble_multimesh
from dolfin.fem.interpolation import interpolate
from dolfin.functions.function import Function
+from dolfin.functions.multimeshfunction import MultiMeshFunction
from dolfin.functions.functionspace import FunctionSpace, \
- VectorFunctionSpace, TensorFunctionSpace
+ VectorFunctionSpace, TensorFunctionSpace, MultiMeshFunctionSpace
__all__ = ["norm", "errornorm"]
@@ -45,11 +50,13 @@ def norm(v, norm_type="L2", mesh=None):
*Arguments*
v
a :py:class:`Vector <dolfin.cpp.Vector>` or
- a :py:class:`Function <dolfin.functions.function.Function>`.
+ a :py:class:`Function <dolfin.functions.function.Function>` or
+ a :py:class:`MultiMeshFunction <dolfin.functions.multimeshfunction.MultiMeshFunction>`
norm_type
see below for alternatives.
mesh
- optional :py:class:`Mesh <dolfin.cpp.Mesh>` on
+ optional :py:class:`Mesh <dolfin.cpp.Mesh>` or
+ a :py:class:`MultiMesh <dolfin.cpp.MultiMesh>` on
which to compute the norm.
If the norm type is not specified, the standard :math:`L^2` -norm
@@ -94,48 +101,53 @@ def norm(v, norm_type="L2", mesh=None):
"""
- if not isinstance(v, (GenericVector, GenericFunction)):
+ if not isinstance(v, (GenericVector, GenericFunction, MultiMeshFunction)):
cpp.dolfin_error("norms.py",
"compute norm",
- "expected a GenericVector or GenericFunction")
-
+ "expected a GenericVector ,GenericFunction or a MultiMeshFunction")
# Check arguments
if not isinstance(norm_type, string_types):
cpp.dolfin_error("norms.py",
"compute norm",
"Norm type must be a string, not " +
str(type(norm_type)))
- if mesh is not None and not isinstance(mesh, cpp.Mesh):
+ if mesh is not None and not isinstance(mesh, (cpp.Mesh, cpp.MultiMesh)):
cpp.dolfin_error("norms.py",
"compute norm",
- "Expecting a Mesh, not " + str(type(mesh)))
+ "Expecting a Mesh or a MultiMesh, not " + str(type(mesh)))
# Get mesh from function
if isinstance(v, Function) and mesh is None:
mesh = v.function_space().mesh()
+ elif isinstance(v, MultiMeshFunction) and mesh is None:
+ mesh = v.function_space().multimesh()
# Define integration measure and domain
- dx = ufl.dx(mesh)
-
+ multimesh = False
+ if isinstance(v, MultiMeshFunction):
+ multimesh = True
+ dc = ufl.dx(mesh) + ufl.dC(mesh)
+ else:
+ dc = ufl.dx(mesh)
# Select norm type
if isinstance(v, GenericVector):
return v.norm(norm_type.lower())
-
- elif (isinstance(v, Coefficient) and isinstance(v, GenericFunction)):
+ elif (isinstance(v, Coefficient) and isinstance(v, (GenericFunction,
+ MultiMeshFunction))):
if norm_type.lower() == "l2":
- M = v**2*dx
+ M = v**2*dc
elif norm_type.lower() == "h1":
- M = (v**2 + grad(v)**2)*dx
+ M = (v**2 + grad(v)**2)*dc
elif norm_type.lower() == "h10":
- M = grad(v)**2*dx
+ M = grad(v)**2*dc
elif norm_type.lower() == "hdiv":
- M = (v**2 + div(v)**2)*dx
+ M = (v**2 + div(v)**2)*dc
elif norm_type.lower() == "hdiv0":
- M = div(v)**2*dx
+ M = div(v)**2*dc
elif norm_type.lower() == "hcurl":
- M = (v**2 + curl(v)**2)*dx
+ M = (v**2 + curl(v)**2)*dc
elif norm_type.lower() == "hcurl0":
- M = curl(v)**2*dx
+ M = curl(v)**2*dc
else:
cpp.dolfin_error("norms.py",
"compute norm",
@@ -144,10 +156,13 @@ def norm(v, norm_type="L2", mesh=None):
else:
cpp.dolfin_error("norms.py",
"compute norm",
- "Unknown object type. Must be a vector or a function")
+ "Unknown object type. Must be a vector, function or a multimeshfunction")
# Assemble value
- r = assemble(M, form_compiler_parameters={"representation": "quadrature"})
+ if multimesh:
+ r = assemble_multimesh(M, form_compiler_parameters={"representation": "quadrature"})
+ else:
+ r = assemble(M)
# Check value
if r < 0.0:
@@ -211,20 +226,26 @@ def errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None):
"""
# Check argument
- if not isinstance(u, cpp.GenericFunction):
+ if not isinstance(u, (cpp.GenericFunction, cpp.MultiMeshFunction)):
cpp.dolfin_error("norms.py",
"compute error norm",
- "Expecting a Function or Expression for u")
- if not isinstance(uh, cpp.Function):
+ "Expecting a Function, MultiMeshFunction or Expression for u")
+ if not (isinstance(uh, cpp.Function) or (uh, cpp.MultiMeshFunction)):
cpp.dolfin_error("norms.py",
"compute error norm",
"Expecting a Function for uh")
+ # Test if errornorm is for multimesh
+ multimesh=False
# Get mesh
if isinstance(u, Function) and mesh is None:
mesh = u.function_space().mesh()
if isinstance(uh, Function) and mesh is None:
mesh = uh.function_space().mesh()
+ if isinstance(uh, MultiMeshFunction) and mesh is None:
+ mesh = uh.function_space().multimesh()
+ multimesh=True
+
if mesh is None:
cpp.dolfin_error("norms.py",
"compute error norm",
@@ -253,21 +274,37 @@ def errornorm(u, uh, norm_type="l2", degree_rise=3, mesh=None):
cpp.warning("Degree of exact solution may be inadequate for accurate result in errornorm.")
# Create function space
- if rank == 0:
- V = FunctionSpace(mesh, "Discontinuous Lagrange", degree)
- elif rank == 1:
- V = VectorFunctionSpace(mesh, "Discontinuous Lagrange", degree,
- dim=shape[0])
- elif rank > 1:
- V = TensorFunctionSpace(mesh, "Discontinuous Lagrange", degree,
- shape=shape)
+ if multimesh:
+ if rank == 0:
+ element = FiniteElement("Discontinuous Lagrange", mesh.ufl_cell(),
+ degree)
+ elif rank == 1:
+ element = VectorElement("Discontinuous Lagrange", mesh.ufl_cell(),
+ degree)
+ elif rank == 2:
+ element = TensorElement("Discontinuous Lagrange", mesh.ufl_cell(),
+ degree)
+ V = MultiMeshFunctionSpace(mesh, element)
+ else:
+ if rank == 0:
+ V = FunctionSpace(mesh, "Discontinuous Lagrange", degree)
+ elif rank == 1:
+ V = VectorFunctionSpace(mesh, "Discontinuous Lagrange", degree,
+ dim=shape[0])
+ elif rank > 1:
+ V = TensorFunctionSpace(mesh, "Discontinuous Lagrange", degree,
+ shape=shape)
# Interpolate functions into finite element space
pi_u = interpolate(u, V)
pi_uh = interpolate(uh, V)
# Compute the difference
- e = Function(V)
+ if multimesh:
+ e = MultiMeshFunction(V)
+ else:
+ e = Function(V)
+
e.assign(pi_u)
e.vector().axpy(-1.0, pi_uh.vector())
diff --git a/site-packages/dolfin/functions/multimeshfunction.py b/site-packages/dolfin/functions/multimeshfunction.py
index 6ff8917..d706807 100644
--- a/site-packages/dolfin/functions/multimeshfunction.py
+++ b/site-packages/dolfin/functions/multimeshfunction.py
@@ -24,8 +24,10 @@ __all__ = ["MultiMeshFunction"]
# Import UFL and SWIG-generated extension module (DOLFIN C++)
import ufl
import dolfin.cpp as cpp
+import numpy
from dolfin.functions.functionspace import MultiMeshFunctionSpace
+from dolfin.functions.function import Function
class MultiMeshFunction(ufl.Coefficient, cpp.MultiMeshFunction):
"""This class represents a multimeshfunction
@@ -123,4 +125,67 @@ class MultiMeshFunction(ufl.Coefficient, cpp.MultiMeshFunction):
return self._V
def assign(self, rhs):
- self.vector()[:]= rhs.vector()[:]
+ """
+ Parameters:
+ rhs: A dolfin.MultiMeshFunction
+ """
+ # Assign a MultiMeshFunction into a MultiMeshFunction
+ if isinstance(rhs, MultiMeshFunction):
+ self.vector()[:]= rhs.vector()[:]
+ else:
+ raise TypeError("expected a MultiMeshFunction as argument.")
+
+ def part(self, i, deepcopy=False):
+ f = Function(super(MultiMeshFunction, self).part(i, deepcopy))
+ f.rename(super(MultiMeshFunction, self).name(), super(MultiMeshFunction, self).label())
+ return f
+
+ def parts(self, deepcopy=False):
+ """
+ Generator for MultiMeshFunction
+ """
+ for part in range(self._V.num_parts()):
+ yield self.part(part, deepcopy)
+
+ def interpolate(self, v):
+ """
+ Interpolate function.
+
+ *Arguments*
+ v
+ a :py:class:`MultiMeshFunction <dolfin.functions.function.MultimeshFunction>` or
+ an :py:class:`Expression <dolfin.functions.expression.Expression'>
+ *Example of usage*
+ .. code-block:: python
+
+ V = MultiMeshFunctionSpace(multimesh, "Lagrange", 1)
+ v = MultiMeshFunction(V)
+ w = Expression("sin(pi*x[0])")
+ v.interpolate(w)
+ """
+ # Developer note: This version involves a lot of copying
+ # and should be changed at some point.
+ # Developer note: Interpolate does not set inactive dofs to zero,
+ # and should be fixed
+ # Check argument
+ if isinstance(v, cpp.GenericFunction):
+ for i, vp in enumerate(self.parts(deepcopy=True)):
+ vp.interpolate(v)
+ self.assign_part(i, vp)
+
+ elif isinstance(v, MultiMeshFunction):
+ # Same multimesh required for interpolation
+ # Developer note: Is this test necessary?
+ if self._V.multimesh().id() != v._V.multimesh().id():
+ raise RuntimeError("MultiMeshFunctions must live on same MultiMesh")
+ for i, vp in enumerate(self.parts(deepcopy=True)):
+ vmm = v.part(i, deepcopy=True)
+ vp.interpolate(vmm)
+ self.assign_part(i, vp)
+ else:
+ raise TypeError("Expected an Expression or a MultiMeshFunction.")
+
+ # Set inactive dofs to zero
+ for part in range(self._V.num_parts()):
+ dofs = self._V.dofmap().inactive_dofs(self._V.multimesh(),part)
+ self.vector()[dofs]=0
diff --git a/site-packages/dolfin/functions/specialfunctions.py b/site-packages/dolfin/functions/specialfunctions.py
index c335348..fc2bb23 100644
--- a/site-packages/dolfin/functions/specialfunctions.py
+++ b/site-packages/dolfin/functions/specialfunctions.py
@@ -24,7 +24,9 @@
# Modified by Martin Sandve Alnæs 2013-2014
__all__ = ["MeshCoordinates", "FacetArea", "FacetNormal", "CellSize", "CellVolume",
- 'SpatialCoordinate', 'CellNormal', 'Circumradius', 'MinFacetEdgeLength', 'MaxFacetEdgeLength']
+ 'SpatialCoordinate', 'CellNormal', 'CellDiameter', 'Circumradius',
+ 'MinCellEdgeLength', 'MaxCellEdgeLength',
+ 'MinFacetEdgeLength', 'MaxFacetEdgeLength']
# Import UFL and SWIG-generated extension module (DOLFIN C++)
import ufl
@@ -116,9 +118,12 @@ def FacetNormal(mesh):
return ufl.FacetNormal(_mesh2domain(mesh))
# Simple definition of CellSize via UFL
-def CellSize(mesh):
+def CellDiameter(mesh):
"""
- Return function cell size for given mesh.
+ Return function cell diameter for given mesh.
+
+ Note that diameter of cell :math:`K` is defined as
+ :math:`\sup_{\mathbf{x,y}\in K} |\mathbf{x-y}|`.
*Arguments*
mesh
@@ -129,10 +134,36 @@ def CellSize(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- h = CellSize(mesh)
+ h = CellDiameter(mesh)
+
+ """
+ return ufl.CellDiameter(_mesh2domain(mesh))
+# Simple definition of CellSize via UFL
+def CellSize(mesh):
"""
+ Return function twice the cell circumradius for given mesh.
+
+ This does not generalize to quadrilateral/hexahedral cells
+ and the function name is misleading. The function is hence
+ deprecated. Use ``CellDiameter`` (or ``2*Circumradius`` if
+ you really want that).
+
+ *Arguments*
+ mesh
+ a :py:class:`Mesh <dolfin.cpp.Mesh>`.
+
+ *Example of usage*
+
+ .. code-block:: python
+ mesh = UnitSquare(4,4)
+ D = CellSize(mesh)
+
+ """
+ cpp.deprecation("\"CellSize\"",
+ "2017.2.0",
+ "Use \"CellDiameter\" or \"2*Circumradius\".")
return 2.0*ufl.Circumradius(_mesh2domain(mesh))
# Simple definition of CellVolume via UFL
@@ -169,7 +200,7 @@ def SpatialCoordinate(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = SpatialCoordinate(mesh)
+ x = SpatialCoordinate(mesh)
"""
@@ -189,7 +220,7 @@ def CellNormal(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = CellNormal(mesh)
+ n = CellNormal(mesh)
"""
@@ -209,12 +240,52 @@ def Circumradius(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = Circumradius(mesh)
+ R = Circumradius(mesh)
"""
return ufl.Circumradius(_mesh2domain(mesh))
+# Simple definition of MinCellEdgeLength via UFL
+def MinCellEdgeLength(mesh):
+ """
+ Return symbolic minimum cell edge length of a cell for given mesh.
+
+ *Arguments*
+ mesh
+ a :py:class:`Mesh <dolfin.cpp.Mesh>`.
+
+ *Example of usage*
+
+ .. code-block:: python
+
+ mesh = UnitSquare(4,4)
+ mince = MinCellEdgeLength(mesh)
+
+ """
+
+ return ufl.MinCellEdgeLength(_mesh2domain(mesh))
+
+# Simple definition of MaxCellEdgeLength via UFL
+def MaxCellEdgeLength(mesh):
+ """
+ Return symbolic maximum cell edge length of a cell for given mesh.
+
+ *Arguments*
+ mesh
+ a :py:class:`Mesh <dolfin.cpp.Mesh>`.
+
+ *Example of usage*
+
+ .. code-block:: python
+
+ mesh = UnitSquare(4,4)
+ maxce = MaxCellEdgeLength(mesh)
+
+ """
+
+ return ufl.MaxCellEdgeLength(_mesh2domain(mesh))
+
# Simple definition of MinFacetEdgeLength via UFL
def MinFacetEdgeLength(mesh):
"""
@@ -229,7 +300,7 @@ def MinFacetEdgeLength(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = MinFacetEdgeLength(mesh)
+ minfe = MinFacetEdgeLength(mesh)
"""
@@ -249,7 +320,7 @@ def MaxFacetEdgeLength(mesh):
.. code-block:: python
mesh = UnitSquare(4,4)
- vol = MaxFacetEdgeLength(mesh)
+ maxfe = MaxFacetEdgeLength(mesh)
"""
diff --git a/site-packages/dolfin_utils/meshconvert/abaqus.py b/site-packages/dolfin_utils/meshconvert/abaqus.py
index ef42ef1..2d1d689 100644
--- a/site-packages/dolfin_utils/meshconvert/abaqus.py
+++ b/site-packages/dolfin_utils/meshconvert/abaqus.py
@@ -195,7 +195,7 @@ def convert(ifilename, handler):
mesh = Mesh()
mesh_editor = MeshEditor()
- mesh_editor.open(mesh, 3, 3)
+ mesh_editor.open(mesh, "tetrahedron", 3, 3)
node_ids_order = {}
# Check for gaps in vertex numbering
diff --git a/site-packages/dolfin_utils/meshconvert/meshconvert.py b/site-packages/dolfin_utils/meshconvert/meshconvert.py
index 385546e..5a1623e 100644
--- a/site-packages/dolfin_utils/meshconvert/meshconvert.py
+++ b/site-packages/dolfin_utils/meshconvert/meshconvert.py
@@ -342,7 +342,12 @@ def gmsh2xml(ifilename, handler):
_error("DOLFIN must be installed to handle Gmsh boundary regions")
mesh = Mesh()
mesh_editor = MeshEditor ()
- mesh_editor.open( mesh, highest_dim, highest_dim )
+ if (highest_dim == 2):
+ mesh_editor.open(mesh, "triangle", highest_dim, highest_dim)
+ elif (highest_dim == 3):
+ mesh_editor.open(mesh, "tetrahedron", highest_dim, highest_dim)
+ else:
+ raise RuntimeError
process_facets = True
else:
# TODO: Output a warning or an error here
diff --git a/site-packages/dolfin_utils/test/fixtures.py b/site-packages/dolfin_utils/test/fixtures.py
index 78e89ff..3103d7f 100644
--- a/site-packages/dolfin_utils/test/fixtures.py
+++ b/site-packages/dolfin_utils/test/fixtures.py
@@ -81,6 +81,16 @@ def gc_barrier():
if MPI.size(mpi_comm_world()) > 1:
MPI.barrier(mpi_comm_world())
+
+ at pytest.fixture()
+def worker_id(request):
+ """Returns thread id when running with pytest-xdist in parallel."""
+ if hasattr(request.config, 'slaveinput'):
+ return request.config.slaveinput['slaveid']
+ else:
+ return 'master'
+
+
@pytest.yield_fixture(scope="function")
def gc_barrier_fixture():
"""Function decorator to call gc.collect() and
@@ -112,6 +122,7 @@ def filedir(request):
d = os.path.dirname(os.path.abspath(request.module.__file__))
return d
+
@pytest.fixture(scope="module")
def rootdir(request):
"Return the root directory of the repository. Assumes run from within repository filetree."
@@ -122,15 +133,20 @@ def rootdir(request):
d, t = os.path.split(d)
return d
+
@pytest.fixture(scope="module")
def datadir(request):
- "Return the directory of the shared test data. Assumes run from within repository filetree."
+ """Return the directory of the shared test data. Assumes run from
+ within repository filetree.
+
+ """
d = os.path.dirname(os.path.abspath(request.module.__file__))
t = ''
while t != "test":
d, t = os.path.split(d)
return os.path.join(d, "test", "data")
+
def _create_tempdir(request):
# Get directory name of test_foo.py file
testfile = request.module.__file__
@@ -138,7 +154,7 @@ def _create_tempdir(request):
# Construct name test_foo_tempdir from name test_foo.py
testfilename = os.path.basename(testfile)
- outputname = testfilename.replace(".py", "_tempdir")
+ outputname = testfilename.replace(".py", "_tempdir_{}".format(worker_id(request)))
# Get function name test_something from test_foo.py
function = request.function.__name__
@@ -176,10 +192,12 @@ def _create_tempdir(request):
MPI.barrier(mpi_comm_world())
return path
+
from collections import defaultdict
_create_tempdir._sequencenumber = defaultdict(int)
_create_tempdir._basepaths = set()
+
@pytest.fixture(scope="function")
def tempdir(request):
"""Return a unique directory name for this test function instance.
@@ -193,10 +211,13 @@ def tempdir(request):
Does NOT change the current directory.
MPI safe (assuming mpi_comm_world() context).
+
"""
+
gc_barrier()
return _create_tempdir(request)
+
@pytest.yield_fixture(scope="function")
def cd_tempdir(request):
"""Return a unique directory name for this test function instance.
@@ -218,6 +239,7 @@ def cd_tempdir(request):
yield path
os.chdir(cwd)
+
@pytest.yield_fixture
def pushpop_parameters():
global parameters
diff --git a/test/README b/test/README
index 561b870..9d167db 100644
--- a/test/README
+++ b/test/README
@@ -31,14 +31,3 @@ the LinearOperator.py Python unit tests, do
cd build/test/unit/la/python
python LinearOperator.py
-
-Similarly, to run one of the C++ unit tests, do
-
- cd build/test/unit/la/cpp
- make
- ./test_LinearOperator
-
-Modifications of tests should be made in the source directory, not in
-the build directory. After a modification, simply run cmake (or the
-cmake.local script) to copy the tests to the build folder. Then run
-the tests from the build folder.
diff --git a/test/regression/test.py b/test/regression/test.py
index 1506301..e26132a 100644
--- a/test/regression/test.py
+++ b/test/regression/test.py
@@ -129,6 +129,8 @@ def main():
os.path.join(demodir, 'undocumented', 'mplot', 'cpp'),
os.path.join(demodir, 'undocumented', 'plot', 'cpp'),
os.path.join(demodir, 'undocumented', 'coordinates', 'cpp'),
+ os.path.join(demodir, 'undocumented', 'multimesh-quadrature', 'cpp'),
+ os.path.join(demodir, 'undocumented', 'multimesh-3d', 'cpp'),
os.path.join(demodir, 'documented', 'stokes-mini', 'cpp'),
os.path.join(demodir, 'documented', 'tensor-weighted-poisson', 'cpp'),
os.path.join(demodir, 'documented', 'subdomains-poisson', 'cpp'),
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
deleted file mode 100644
index 027c19d..0000000
--- a/test/unit/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 3.5)
-
-project(googletest-download NONE)
-
-# Using ExternalProject to Build GTest and use the Library
-include(ExternalProject)
-
-# Set the build type if it isn't already
-if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE Release)
-endif()
-
-# Set default ExternalProject root directory
-set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/GoogleTest)
-
-# Add gtest
-ExternalProject_Add(
- googletest
- URL https://github.com/google/googletest/archive/release-1.7.0.tar.gz
- TIMEOUT 60
- CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- -Dgtest_force_shared_crt=ON
- # Disable install step
- INSTALL_COMMAND ""
- # Wrap download, configure and build steps in a script to log output
- LOG_DOWNLOAD ON
- LOG_CONFIGURE ON
- LOG_BUILD ON)
-
-# Specify include dir
-ExternalProject_Get_Property(googletest source_dir)
-include_directories(${source_dir}/include)
diff --git a/test/unit/README b/test/unit/README
index 13687d0..6e065d3 100644
--- a/test/unit/README
+++ b/test/unit/README
@@ -21,6 +21,3 @@ Debugging random test deadlocks under MPI:
while true; do git clean -fdx . && mpirun -n 3 xterm -e gdb -ex r -ex q -args python -m pytest -sv; done
See 'man pytest' for how to use pdb, profiling, coverage, etc.
-
-TODO: Move {function,mesh,io,la,parameter}/cpp to cpp/* and rewrite to google test framework.
-
diff --git a/test/unit/cpp/CMakeLists.txt b/test/unit/cpp/CMakeLists.txt
index 3e50f92..421b2d7 100644
--- a/test/unit/cpp/CMakeLists.txt
+++ b/test/unit/cpp/CMakeLists.txt
@@ -1,90 +1,32 @@
-project(dolfin-tests)
-
-# Require CMake 2.8
cmake_minimum_required(VERSION 3.5)
+project(dolfin-tests)
-# Set special link option, see `cmake --help-policy CMP0003`
-if(COMMAND cmake_policy)
- cmake_policy(SET CMP0003 NEW)
-endif()
-
-# Find DOLFIN config file (not used here, but checks that tests will be able
-# to find it
find_package(DOLFIN REQUIRED)
-
-# Add GoogleTest Directory
-include_directories(${CMAKE_BINARY_DIR}/GoogleTest/src/googletest/include)
-link_directories(${CMAKE_BINARY_DIR}/GoogleTest/src/googletest-build)
-
-# If config file is found, add all demo sub-directories, else print helper
-# message
-if (DOLFIN_FOUND)
-
- # Build list of all cpp directories
- file(GLOB_RECURSE initial_list "*.cpp")
-
- set(added_files "") # Change to targetting cpp files instead
-
- # Add each C++ demo directory only once
- foreach (cpp_test ${initial_list})
-
- # Get directory
- get_filename_component(cpp_lib_dir ${cpp_test} DIRECTORY)
-
- # Get one upper directory
- get_filename_component(cpp_dir ${cpp_lib_dir} DIRECTORY)
-
- # Get last dir name
- get_filename_component(last_component ${cpp_dir} NAME)
-
- # Only process if last dirname is cpp
- if ((${last_component} STREQUAL "cpp"))
- # If you find the cpp lib dir
- # That means you have allocated the cpp files
- # Don't need to recheck the directories
- list(APPEND added_files ${cpp_test})
-
- endif()
- endforeach()
-
- # Set CMake behavior
- cmake_policy(SET CMP0004 NEW)
-
- if (EXISTS ${DOLFIN_USE_FILE})
- include(${DOLFIN_USE_FILE})
-
- # Default build type (can be overridden by user)
- if (NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
- "Choose the type of build, options are: Debug MinSizeRel Release RelWithDebInfo." FORCE)
- endif()
- else()
- # Compiler definitions
- add_definitions(${DOLFIN_CXX_DEFINITIONS})
-
- # Compiler flags
- set(CMAKE_CXX_FLAGS "${DOLFIN_CXX_FLAGS} ${CMAKE_CXX_FLAGS}")
-
- # Include directories
- include_directories(${DOLFIN_INCLUDE_DIRS})
- include_directories(SYSTEM ${DOLFIN_3RD_PARTY_INCLUDE_DIRS})
- endif()
-
- # Google Test uses threads
- if (NOT APPLE)
- set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
- find_package(Threads REQUIRED)
- if (CMAKE_USE_PTHREADS_INIT)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
- endif()
- endif()
-
- add_executable(unittests_cpp ${added_files})
- add_dependencies(unittests_cpp googletest)
- target_link_libraries(unittests_cpp ${DOLFIN_LIBRARIES} gtest gtest_main ${CMAKE_THREAD_LIBS_INIT})
-
-else()
-
- message(STATUS "Could not locate DOLFINConfig.cmake file. Did you do 'make install' for the DOLFIN library and set the appropriate paths (source <build_dir>/dolfin.conf)?")
-
-endif()
+include(${DOLFIN_USE_FILE})
+
+# Prepare "Catch" library for other executables
+set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/catch)
+
+add_library(Catch INTERFACE)
+target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
+
+# Make test executable
+set(TEST_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/common/SubSystemsManager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/function/Expression.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/geometry/ConvexTriangulation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io/XMLMeshData.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io/XMLMeshValueCollection.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/la/LinearOperator.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/la/Vector.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mesh/Mesh.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mesh/MeshColoring.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mesh/MeshFunction.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mesh/MeshValueCollection.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mesh/MultiMesh.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/parameter/Parameters.cpp
+ )
+
+add_executable(unittests_cpp ${TEST_SOURCES})
+target_link_libraries(unittests_cpp PRIVATE Catch dolfin)
diff --git a/test/unit/cpp/catch/catch.hpp b/test/unit/cpp/catch/catch.hpp
new file mode 100644
index 0000000..f7681f4
--- /dev/null
+++ b/test/unit/cpp/catch/catch.hpp
@@ -0,0 +1,11545 @@
+/*
+ * Catch v1.9.6
+ * Generated: 2017-06-27 12:19:54.557875
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic ignored "-Wglobal-constructors"
+# pragma clang diagnostic ignored "-Wvariadic-macros"
+# pragma clang diagnostic ignored "-Wc99-extensions"
+# pragma clang diagnostic ignored "-Wunused-variable"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wc++98-compat"
+# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wvariadic-macros"
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wparentheses"
+
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __cplusplus
+
+# if __cplusplus >= 201103L
+# define CATCH_CPP11_OR_GREATER
+# endif
+
+# if __cplusplus >= 201402L
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+#endif
+
+#ifdef __clang__
+
+# if __has_feature(cxx_nullptr)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# if __has_feature(cxx_noexcept)
+# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# if defined(CATCH_CPP11_OR_GREATER)
+# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+# endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__)
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
+#if (_MSC_VER >= 1600)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+ ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+ ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+ ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+
+#endif
+
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+ ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \
+ ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(CATCH_CPP11_OR_GREATER)
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+# endif
+
+# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+# endif
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+# endif
+
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+# define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#endif
+// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for
+// analytics) because, at time of writing, __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+# define CATCH_NOEXCEPT noexcept
+# define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+# define CATCH_NOEXCEPT throw()
+# define CATCH_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+# define CATCH_NULL nullptr
+#else
+# define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+# define CATCH_OVERRIDE override
+#else
+# define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+# define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+ struct IConfig;
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+#else
+ NonCopyable( NonCopyable const& info );
+ NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+ protected:
+ NonCopyable() {}
+ virtual ~NonCopyable();
+ };
+
+ class SafeBool {
+ public:
+ typedef void (SafeBool::*type)() const;
+
+ static type makeSafe( bool value ) {
+ return value ? &SafeBool::trueValue : 0;
+ }
+ private:
+ void trueValue() const {}
+ };
+
+ template<typename ContainerT>
+ inline void deleteAll( ContainerT& container ) {
+ typename ContainerT::const_iterator it = container.begin();
+ typename ContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete *it;
+ }
+ template<typename AssociativeContainerT>
+ inline void deleteAllValues( AssociativeContainerT& container ) {
+ typename AssociativeContainerT::const_iterator it = container.begin();
+ typename AssociativeContainerT::const_iterator itEnd = container.end();
+ for(; it != itEnd; ++it )
+ delete it->second;
+ }
+
+ bool startsWith( std::string const& s, std::string const& prefix );
+ bool startsWith( std::string const& s, char prefix );
+ bool endsWith( std::string const& s, std::string const& suffix );
+ bool endsWith( std::string const& s, char suffix );
+ bool contains( std::string const& s, std::string const& infix );
+ void toLowerInPlace( std::string& s );
+ std::string toLower( std::string const& s );
+ std::string trim( std::string const& str );
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+ struct pluralise {
+ pluralise( std::size_t count, std::string const& label );
+
+ friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+ std::size_t m_count;
+ std::string m_label;
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo();
+ SourceLineInfo( char const* _file, std::size_t _line );
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SourceLineInfo(SourceLineInfo const& other) = default;
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+# endif
+ bool empty() const;
+ bool operator == ( SourceLineInfo const& other ) const;
+ bool operator < ( SourceLineInfo const& other ) const;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // This is just here to avoid compiler warnings with macro constants and boolean literals
+ inline bool isTrue( bool value ){ return value; }
+ inline bool alwaysTrue() { return true; }
+ inline bool alwaysFalse() { return false; }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+ void seedRng( IConfig const& config );
+ unsigned int rngSeed();
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() {
+ return std::string();
+ }
+ };
+ template<typename T>
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+namespace Catch {
+
+ class NotImplementedException : public std::exception
+ {
+ public:
+ NotImplementedException( SourceLineInfo const& lineInfo );
+ NotImplementedException( NotImplementedException const& ) {}
+
+ virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+ virtual const char* what() const CATCH_NOEXCEPT;
+
+ private:
+ std::string m_what;
+ SourceLineInfo m_lineInfo;
+ };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct IGeneratorInfo {
+ virtual ~IGeneratorInfo();
+ virtual bool moveNext() = 0;
+ virtual std::size_t getCurrentIndex() const = 0;
+ };
+
+ struct IGeneratorsForTest {
+ virtual ~IGeneratorsForTest();
+
+ virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+ virtual bool moveNext() = 0;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ // An intrusive reference counting smart pointer.
+ // T must implement addRef() and release() methods
+ // typically implementing the IShared interface
+ template<typename T>
+ class Ptr {
+ public:
+ Ptr() : m_p( CATCH_NULL ){}
+ Ptr( T* p ) : m_p( p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ Ptr( Ptr const& other ) : m_p( other.m_p ){
+ if( m_p )
+ m_p->addRef();
+ }
+ ~Ptr(){
+ if( m_p )
+ m_p->release();
+ }
+ void reset() {
+ if( m_p )
+ m_p->release();
+ m_p = CATCH_NULL;
+ }
+ Ptr& operator = ( T* p ){
+ Ptr temp( p );
+ swap( temp );
+ return *this;
+ }
+ Ptr& operator = ( Ptr const& other ){
+ Ptr temp( other );
+ swap( temp );
+ return *this;
+ }
+ void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+ T* get() const{ return m_p; }
+ T& operator*() const { return *m_p; }
+ T* operator->() const { return m_p; }
+ bool operator !() const { return m_p == CATCH_NULL; }
+ operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
+
+ private:
+ T* m_p;
+ };
+
+ struct IShared : NonCopyable {
+ virtual ~IShared();
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ template<typename T = IShared>
+ struct SharedImpl : T {
+
+ SharedImpl() : m_rc( 0 ){}
+
+ virtual void addRef() const {
+ ++m_rc;
+ }
+ virtual void release() const {
+ if( --m_rc == 0 )
+ delete this;
+ }
+
+ mutable unsigned int m_rc;
+ };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestCase;
+ class Stream;
+ struct IResultCapture;
+ struct IRunner;
+ struct IGeneratorsForTest;
+ struct IConfig;
+
+ struct IContext
+ {
+ virtual ~IContext();
+
+ virtual IResultCapture* getResultCapture() = 0;
+ virtual IRunner* getRunner() = 0;
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+ virtual bool advanceGeneratorsForCurrentTest() = 0;
+ virtual Ptr<IConfig const> getConfig() const = 0;
+ };
+
+ struct IMutableContext : IContext
+ {
+ virtual ~IMutableContext();
+ virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+ virtual void setRunner( IRunner* runner ) = 0;
+ virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+ };
+
+ IContext& getCurrentContext();
+ IMutableContext& getCurrentMutableContext();
+ void cleanUpContext();
+ Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestCase : IShared {
+ virtual void invoke () const = 0;
+ protected:
+ virtual ~ITestCase();
+ };
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector<TestCase> const& getAllTests() const = 0;
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+ MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+ virtual void invoke() const {
+ C obj;
+ (obj.*m_method)();
+ }
+
+private:
+ virtual ~MethodTestCase() {}
+
+ void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+ NameAndDesc( const char* _name = "", const char* _description= "" )
+ : name( _name ), description( _description )
+ {}
+
+ const char* name;
+ const char* description;
+};
+
+void registerTestCase
+ ( ITestCase* testCase,
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo );
+
+struct AutoReg {
+
+ AutoReg
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+ template<typename C>
+ AutoReg
+ ( void (C::*method)(),
+ char const* className,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ registerTestCase
+ ( new MethodTestCase<C>( method ),
+ className,
+ nameAndDesc,
+ lineInfo );
+ }
+
+ ~AutoReg();
+
+private:
+ AutoReg( AutoReg const& );
+ void operator= ( AutoReg const& );
+};
+
+void registerTestCaseFunction
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc );
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ \
+ struct TestName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#else
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ namespace{ \
+ struct TestCaseName : ClassName{ \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+ void TestCaseName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+ CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+ Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); \
+ CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ inline bool isOk( ResultWas::OfType resultType ) {
+ return ( resultType & ResultWas::FailureBit ) == 0;
+ }
+ inline bool isJustInfo( int flags ) {
+ return flags == ResultWas::Info;
+ }
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+ return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+ }
+
+ inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct DecomposedExpression
+ {
+ virtual ~DecomposedExpression() {}
+ virtual bool isBinaryExpression() const {
+ return false;
+ }
+ virtual void reconstructExpression( std::string& dest ) const = 0;
+
+ // Only simple binary comparisons can be decomposed.
+ // If more complex check is required then wrap sub-expressions in parentheses.
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+ private:
+ DecomposedExpression& operator = (DecomposedExpression const&);
+ };
+
+ struct AssertionInfo
+ {
+ AssertionInfo() {}
+ AssertionInfo( char const * _macroName,
+ SourceLineInfo const& _lineInfo,
+ char const * _capturedExpression,
+ ResultDisposition::Flags _resultDisposition,
+ char const * _secondArg = "");
+
+ char const * macroName;
+ SourceLineInfo lineInfo;
+ char const * capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+ char const * secondArg;
+ };
+
+ struct AssertionResultData
+ {
+ AssertionResultData() : decomposedExpression( CATCH_NULL )
+ , resultType( ResultWas::Unknown )
+ , negated( false )
+ , parenthesized( false ) {}
+
+ void negate( bool parenthesize ) {
+ negated = !negated;
+ parenthesized = parenthesize;
+ if( resultType == ResultWas::Ok )
+ resultType = ResultWas::ExpressionFailed;
+ else if( resultType == ResultWas::ExpressionFailed )
+ resultType = ResultWas::Ok;
+ }
+
+ std::string const& reconstructExpression() const {
+ if( decomposedExpression != CATCH_NULL ) {
+ decomposedExpression->reconstructExpression( reconstructedExpression );
+ if( parenthesized ) {
+ reconstructedExpression.insert( 0, 1, '(' );
+ reconstructedExpression.append( 1, ')' );
+ }
+ if( negated ) {
+ reconstructedExpression.insert( 0, 1, '!' );
+ }
+ decomposedExpression = CATCH_NULL;
+ }
+ return reconstructedExpression;
+ }
+
+ mutable DecomposedExpression const* decomposedExpression;
+ mutable std::string reconstructedExpression;
+ std::string message;
+ ResultWas::OfType resultType;
+ bool negated;
+ bool parenthesized;
+ };
+
+ class AssertionResult {
+ public:
+ AssertionResult();
+ AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ ~AssertionResult();
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ AssertionResult( AssertionResult const& ) = default;
+ AssertionResult( AssertionResult && ) = default;
+ AssertionResult& operator = ( AssertionResult const& ) = default;
+ AssertionResult& operator = ( AssertionResult && ) = default;
+# endif
+
+ bool isOk() const;
+ bool succeeded() const;
+ ResultWas::OfType getResultType() const;
+ bool hasExpression() const;
+ bool hasMessage() const;
+ std::string getExpression() const;
+ std::string getExpressionInMacro() const;
+ bool hasExpandedExpression() const;
+ std::string getExpandedExpression() const;
+ std::string getMessage() const;
+ SourceLineInfo getSourceInfo() const;
+ std::string getTestMacroName() const;
+ void discardDecomposedExpression() const;
+ void expandDecomposedExpression() const;
+
+ protected:
+ AssertionInfo m_info;
+ AssertionResultData m_resultData;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+ namespace Impl {
+
+ template<typename ArgT> struct MatchAllOf;
+ template<typename ArgT> struct MatchAnyOf;
+ template<typename ArgT> struct MatchNotOf;
+
+ class MatcherUntypedBase {
+ public:
+ std::string toString() const {
+ if( m_cachedToString.empty() )
+ m_cachedToString = describe();
+ return m_cachedToString;
+ }
+
+ protected:
+ virtual ~MatcherUntypedBase();
+ virtual std::string describe() const = 0;
+ mutable std::string m_cachedToString;
+ private:
+ MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
+ };
+
+ template<typename ObjectT>
+ struct MatcherMethod {
+ virtual bool match( ObjectT const& arg ) const = 0;
+ };
+ template<typename PtrT>
+ struct MatcherMethod<PtrT*> {
+ virtual bool match( PtrT* arg ) const = 0;
+ };
+
+ template<typename ObjectT, typename ComparatorT = ObjectT>
+ struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
+
+ MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+ MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+ MatchNotOf<ComparatorT> operator ! () const;
+ };
+
+ template<typename ArgT>
+ struct MatchAllOf : MatcherBase<ArgT> {
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (!m_matchers[i]->match(arg))
+ return false;
+ }
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ description += " and ";
+ description += m_matchers[i]->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+ template<typename ArgT>
+ struct MatchAnyOf : MatcherBase<ArgT> {
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (m_matchers[i]->match(arg))
+ return true;
+ }
+ return false;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if( i != 0 )
+ description += " or ";
+ description += m_matchers[i]->toString();
+ }
+ description += " )";
+ return description;
+ }
+
+ MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
+ }
+
+ std::vector<MatcherBase<ArgT> const*> m_matchers;
+ };
+
+ template<typename ArgT>
+ struct MatchNotOf : MatcherBase<ArgT> {
+
+ MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ return !m_underlyingMatcher.match( arg );
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "not " + m_underlyingMatcher.toString();
+ }
+ MatcherBase<ArgT> const& m_underlyingMatcher;
+ };
+
+ template<typename ObjectT, typename ComparatorT>
+ MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+ return MatchAllOf<ComparatorT>() && *this && other;
+ }
+ template<typename ObjectT, typename ComparatorT>
+ MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+ return MatchAnyOf<ComparatorT>() || *this || other;
+ }
+ template<typename ObjectT, typename ComparatorT>
+ MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+ return MatchNotOf<ComparatorT>( *this );
+ }
+
+ } // namespace Impl
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+ // - deprecated: prefer ||, && and !
+ template<typename T>
+ inline Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+ return Impl::MatchNotOf<T>( underlyingMatcher );
+ }
+ template<typename T>
+ inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+ return Impl::MatchAllOf<T>() && m1 && m2;
+ }
+ template<typename T>
+ inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+ return Impl::MatchAllOf<T>() && m1 && m2 && m3;
+ }
+ template<typename T>
+ inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+ return Impl::MatchAnyOf<T>() || m1 || m2;
+ }
+ template<typename T>
+ inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+ return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
+ }
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+namespace Catch {
+
+ struct TestFailureException{};
+
+ template<typename T> class ExpressionLhs;
+
+ struct CopyableStream {
+ CopyableStream() {}
+ CopyableStream( CopyableStream const& other ) {
+ oss << other.oss.str();
+ }
+ CopyableStream& operator=( CopyableStream const& other ) {
+ oss.str(std::string());
+ oss << other.oss.str();
+ return *this;
+ }
+ std::ostringstream oss;
+ };
+
+ class ResultBuilder : public DecomposedExpression {
+ public:
+ ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition,
+ char const* secondArg = "" );
+ ~ResultBuilder();
+
+ template<typename T>
+ ExpressionLhs<T const&> operator <= ( T const& operand );
+ ExpressionLhs<bool> operator <= ( bool value );
+
+ template<typename T>
+ ResultBuilder& operator << ( T const& value ) {
+ m_stream().oss << value;
+ return *this;
+ }
+
+ ResultBuilder& setResultType( ResultWas::OfType result );
+ ResultBuilder& setResultType( bool result );
+
+ void endExpression( DecomposedExpression const& expr );
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
+ AssertionResult build() const;
+ AssertionResult build( DecomposedExpression const& expr ) const;
+
+ void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+ void captureResult( ResultWas::OfType resultType );
+ void captureExpression();
+ void captureExpectedException( std::string const& expectedMessage );
+ void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
+ void handleResult( AssertionResult const& result );
+ void react();
+ bool shouldDebugBreak() const;
+ bool allowThrows() const;
+
+ template<typename ArgT, typename MatcherT>
+ void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
+ void setExceptionGuard();
+ void unsetExceptionGuard();
+
+ private:
+ AssertionInfo m_assertionInfo;
+ AssertionResultData m_data;
+
+ static CopyableStream &m_stream()
+ {
+ static CopyableStream s;
+ return s;
+ }
+
+ bool m_shouldDebugBreak;
+ bool m_shouldThrow;
+ bool m_guardException;
+ };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+ enum Operator {
+ IsEqualTo,
+ IsNotEqualTo,
+ IsLessThan,
+ IsGreaterThan,
+ IsLessThanOrEqualTo,
+ IsGreaterThanOrEqualTo
+ };
+
+ template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } };
+ template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } };
+ template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } };
+ template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } };
+ template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } };
+ template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } };
+ template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+ template<typename T>
+ inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+ // So the compare overloads can be operator agnostic we convey the operator as a template
+ // enum, which is used to specialise an Evaluator for doing the comparison.
+ template<typename T1, typename T2, Operator Op>
+ class Evaluator{};
+
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs) {
+ return bool( opCast( lhs ) == opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsNotEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) != opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) < opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThan> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) > opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) >= opCast( rhs ) );
+ }
+ };
+ template<typename T1, typename T2>
+ struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+ static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+ return bool( opCast( lhs ) <= opCast( rhs ) );
+ }
+ };
+
+ template<Operator Op, typename T1, typename T2>
+ bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // This level of indirection allows us to specialise for integer types
+ // to avoid signed/ unsigned warnings
+
+ // "base" overload
+ template<Operator Op, typename T1, typename T2>
+ bool compare( T1 const& lhs, T2 const& rhs ) {
+ return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+ }
+
+ // unsigned X to int
+ template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+ }
+
+ // unsigned X to long
+ template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+ template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+ return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+ }
+
+ // int to unsigned X
+ template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+ }
+
+ // long to unsigned X
+ template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // pointer to long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+ // pointer to int (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+ // long long to unsigned X
+ template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+ return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+ }
+
+ // unsigned long long to X
+ template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+ template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+ return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+ }
+
+ // pointer to long long (when comparing against NULL)
+ template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+ }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+ // pointer to nullptr_t (when comparing against nullptr)
+ template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+ return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
+ }
+ template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+ return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
+ }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring );
+ std::string toString( NSString * CATCH_ARC_STRONG & nsstring );
+ std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+ extern const std::string unprintableString;
+
+ #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
+ struct BorgType {
+ template<typename T> BorgType( T const& );
+ };
+
+ struct TrueType { char sizer[1]; };
+ struct FalseType { char sizer[2]; };
+
+ TrueType& testStreamable( std::ostream& );
+ FalseType testStreamable( FalseType );
+
+ FalseType operator<<( std::ostream const&, BorgType const& );
+
+ template<typename T>
+ struct IsStreamInsertable {
+ static std::ostream &s;
+ static T const&t;
+ enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+ };
+#else
+ template<typename T>
+ class IsStreamInsertable {
+ template<typename SS, typename TT>
+ static auto test(int)
+ -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
+
+ template<typename, typename>
+ static auto test(...) -> std::false_type;
+
+ public:
+ static const bool value = decltype(test<std::ostream,const T&>(0))::value;
+ };
+#endif
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+ template<typename T,
+ bool IsEnum = std::is_enum<T>::value
+ >
+ struct EnumStringMaker
+ {
+ static std::string convert( T const& ) { return unprintableString; }
+ };
+
+ template<typename T>
+ struct EnumStringMaker<T,true>
+ {
+ static std::string convert( T const& v )
+ {
+ return ::Catch::toString(
+ static_cast<typename std::underlying_type<T>::type>(v)
+ );
+ }
+ };
+#endif
+ template<bool C>
+ struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+ template<typename T>
+ static std::string convert( T const& v )
+ {
+ return EnumStringMaker<T>::convert( v );
+ }
+#else
+ template<typename T>
+ static std::string convert( T const& ) { return unprintableString; }
+#endif
+ };
+
+ template<>
+ struct StringMakerBase<true> {
+ template<typename T>
+ static std::string convert( T const& _value ) {
+ std::ostringstream oss;
+ oss << _value;
+ return oss.str();
+ }
+ };
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template<typename T>
+ inline std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+ Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+ template<typename U>
+ static std::string convert( U* p ) {
+ if( !p )
+ return "NULL";
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+ static std::string convert( R C::* p ) {
+ if( !p )
+ return "NULL";
+ else
+ return Detail::rawMemoryToString( p );
+ }
+};
+
+namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+// static std::string convert( std::vector<T,Allocator> const& v ) {
+// return Detail::rangeToString( v.begin(), v.end() );
+// }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+ return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size<Tuple>::value)
+ >
+ struct ElementPrinter {
+ static void print( const Tuple& tuple, std::ostream& os )
+ {
+ os << ( N ? ", " : " " )
+ << Catch::toString(std::get<N>(tuple));
+ ElementPrinter<Tuple,N+1>::print(tuple,os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct ElementPrinter<Tuple,N,false> {
+ static void print( const Tuple&, std::ostream& ) {}
+ };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+ static std::string convert( const std::tuple<Types...>& tuple )
+ {
+ std::ostringstream os;
+ os << '{';
+ TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+ os << " }";
+ return os.str();
+ }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+ template<typename T>
+ std::string makeString( T const& value ) {
+ return StringMaker<T>::convert( value );
+ }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+ return StringMaker<T>::convert( value );
+}
+
+ namespace Detail {
+ template<typename InputIterator>
+ std::string rangeToString( InputIterator first, InputIterator last ) {
+ std::ostringstream oss;
+ oss << "{ ";
+ if( first != last ) {
+ oss << Catch::toString( *first );
+ for( ++first ; first != last ; ++first )
+ oss << ", " << Catch::toString( *first );
+ }
+ oss << " }";
+ return oss.str();
+ }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
+public:
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+
+ ExpressionLhs& operator = ( const ExpressionLhs& );
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+ operator == ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+ operator != ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+ operator < ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThan>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+ operator > ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThan>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+ operator <= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+ }
+
+ template<typename RhsT>
+ BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+ operator >= ( RhsT const& rhs ) {
+ return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+ }
+
+ BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
+ return captureExpression<Internal::IsEqualTo>( rhs );
+ }
+
+ BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
+ return captureExpression<Internal::IsNotEqualTo>( rhs );
+ }
+
+ void endExpression() {
+ m_truthy = m_lhs ? true : false;
+ m_rb
+ .setResultType( m_truthy )
+ .endExpression( *this );
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ dest = Catch::toString( m_lhs );
+ }
+
+private:
+ template<Internal::Operator Op, typename RhsT>
+ BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+ return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+ }
+
+ template<Internal::Operator Op>
+ BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+ return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
+ }
+
+private:
+ ResultBuilder& m_rb;
+ T m_lhs;
+ bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+ BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+ : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+ BinaryExpression& operator = ( BinaryExpression& );
+
+ void endExpression() const {
+ m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+ .endExpression( *this );
+ }
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string lhs = Catch::toString( m_lhs );
+ std::string rhs = Catch::toString( m_rhs );
+ char delim = lhs.size() + rhs.size() < 40 &&
+ lhs.find('\n') == std::string::npos &&
+ rhs.find('\n') == std::string::npos ? ' ' : '\n';
+ dest.reserve( 7 + lhs.size() + rhs.size() );
+ // 2 for spaces around operator
+ // 2 for operator
+ // 2 for parentheses (conditionally added later)
+ // 1 for negation (conditionally added later)
+ dest = lhs;
+ dest += delim;
+ dest += Internal::OperatorTraits<Op>::getName();
+ dest += delim;
+ dest += rhs;
+ }
+
+private:
+ ResultBuilder& m_rb;
+ LhsT m_lhs;
+ RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+ MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+ : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string matcherAsString = m_matcher.toString();
+ dest = Catch::toString( m_arg );
+ dest += ' ';
+ if( matcherAsString == Detail::unprintableString )
+ dest += m_matcherString;
+ else
+ dest += matcherAsString;
+ }
+
+private:
+ ArgT m_arg;
+ MatcherT m_matcher;
+ char const* m_matcherString;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+ template<typename T>
+ inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
+ return ExpressionLhs<T const&>( *this, operand );
+ }
+
+ inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
+ return ExpressionLhs<bool>( *this, value );
+ }
+
+ template<typename ArgT, typename MatcherT>
+ inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+ char const* matcherString ) {
+ MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+ setResultType( matcher.match( arg ) );
+ endExpression( expr );
+ }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct MessageInfo {
+ MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type );
+
+ std::string macroName;
+ SourceLineInfo lineInfo;
+ ResultWas::OfType type;
+ std::string message;
+ unsigned int sequence;
+
+ bool operator == ( MessageInfo const& other ) const {
+ return sequence == other.sequence;
+ }
+ bool operator < ( MessageInfo const& other ) const {
+ return sequence < other.sequence;
+ }
+ private:
+ static unsigned int globalCount;
+ };
+
+ struct MessageBuilder {
+ MessageBuilder( std::string const& macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType type )
+ : m_info( macroName, lineInfo, type )
+ {}
+
+ template<typename T>
+ MessageBuilder& operator << ( T const& value ) {
+ m_stream << value;
+ return *this;
+ }
+
+ MessageInfo m_info;
+ std::ostringstream m_stream;
+ };
+
+ class ScopedMessage {
+ public:
+ ScopedMessage( MessageBuilder const& builder );
+ ScopedMessage( ScopedMessage const& other );
+ ~ScopedMessage();
+
+ MessageInfo m_info;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ class AssertionResult;
+ struct AssertionInfo;
+ struct SectionInfo;
+ struct SectionEndInfo;
+ struct MessageInfo;
+ class ScopedMessageBuilder;
+ struct Counts;
+
+ struct IResultCapture {
+
+ virtual ~IResultCapture();
+
+ virtual void assertionEnded( AssertionResult const& result ) = 0;
+ virtual bool sectionStarted( SectionInfo const& sectionInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+ virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+ virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+ virtual std::string getCurrentTestName() const = 0;
+ virtual const AssertionResult* getLastResult() const = 0;
+
+ virtual void exceptionEarlyReported() = 0;
+
+ virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+ };
+
+ IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+# define CATCH_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+# define CATCH_PLATFORM_IPHONE
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+# define CATCH_PLATFORM_WINDOWS
+# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+# define CATCH_DEFINES_NOMINMAX
+# endif
+# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# endif
+#endif
+
+#include <string>
+
+namespace Catch{
+
+ bool isDebuggerActive();
+ void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+ // The following code snippet based on:
+ // http://cocoawithlove.com/2008/03/break-into-debugger.html
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_TRAP() \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" )
+ #else
+ #define CATCH_TRAP() __asm__("int $3\n" : : )
+ #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ // If we can use inline assembler, do it because this allows us to break
+ // directly at the location of the failing check instead of breaking inside
+ // raise() called from it, i.e. one stack frame below.
+ #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+ #define CATCH_TRAP() asm volatile ("int $3")
+ #else // Fall back to the generic way.
+ #include <signal.h>
+
+ #define CATCH_TRAP() raise(SIGTRAP)
+ #endif
+#elif defined(_MSC_VER)
+ #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+ #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifdef CATCH_TRAP
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+ #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+ class TestCase;
+
+ struct IRunner {
+ virtual ~IRunner();
+ virtual bool aborting() const = 0;
+ };
+}
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+// This can potentially cause false negative, if the test code catches
+// the exception before it propagates back up to the runner.
+#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ __catchResult.setExceptionGuard(); \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ ( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ __catchResult.unsetExceptionGuard(); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+ __catchResult.setExceptionGuard(); \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
+ __catchResult.unsetExceptionGuard(); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+#else
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+ resultBuilder.react();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ ( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+ if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \
+ INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+ if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( ... ) { \
+ __catchResult.captureExpectedException( matcher ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \
+ if( __catchResult.allowThrows() ) \
+ try { \
+ static_cast<void>(expr); \
+ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+ } \
+ catch( exceptionType ) { \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ } \
+ catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition ); \
+ } \
+ else \
+ __catchResult.captureResult( Catch::ResultWas::Ok ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#else
+ #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+ __catchResult << log + ::Catch::StreamEndStop(); \
+ __catchResult.captureResult( messageType ); \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+ do { \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+ try { \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
+ } catch( ... ) { \
+ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+ } \
+ INTERNAL_CATCH_REACT( __catchResult ) \
+ } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+ struct Counts {
+ Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+ Counts operator - ( Counts const& other ) const {
+ Counts diff;
+ diff.passed = passed - other.passed;
+ diff.failed = failed - other.failed;
+ diff.failedButOk = failedButOk - other.failedButOk;
+ return diff;
+ }
+ Counts& operator += ( Counts const& other ) {
+ passed += other.passed;
+ failed += other.failed;
+ failedButOk += other.failedButOk;
+ return *this;
+ }
+
+ std::size_t total() const {
+ return passed + failed + failedButOk;
+ }
+ bool allPassed() const {
+ return failed == 0 && failedButOk == 0;
+ }
+ bool allOk() const {
+ return failed == 0;
+ }
+
+ std::size_t passed;
+ std::size_t failed;
+ std::size_t failedButOk;
+ };
+
+ struct Totals {
+
+ Totals operator - ( Totals const& other ) const {
+ Totals diff;
+ diff.assertions = assertions - other.assertions;
+ diff.testCases = testCases - other.testCases;
+ return diff;
+ }
+
+ Totals delta( Totals const& prevTotals ) const {
+ Totals diff = *this - prevTotals;
+ if( diff.assertions.failed > 0 )
+ ++diff.testCases.failed;
+ else if( diff.assertions.failedButOk > 0 )
+ ++diff.testCases.failedButOk;
+ else
+ ++diff.testCases.passed;
+ return diff;
+ }
+
+ Totals& operator += ( Totals const& other ) {
+ assertions += other.assertions;
+ testCases += other.testCases;
+ return *this;
+ }
+
+ Counts assertions;
+ Counts testCases;
+ };
+}
+
+#include <string>
+
+namespace Catch {
+
+ struct SectionInfo {
+ SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description = std::string() );
+
+ std::string name;
+ std::string description;
+ SourceLineInfo lineInfo;
+ };
+
+ struct SectionEndInfo {
+ SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+ : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+ {}
+
+ SectionInfo sectionInfo;
+ Counts prevAssertions;
+ double durationInSeconds;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef _MSC_VER
+
+namespace Catch {
+ typedef unsigned long long UInt64;
+}
+#else
+#include <stdint.h>
+namespace Catch {
+ typedef uint64_t UInt64;
+}
+#endif
+
+namespace Catch {
+ class Timer {
+ public:
+ Timer() : m_ticks( 0 ) {}
+ void start();
+ unsigned int getElapsedMicroseconds() const;
+ unsigned int getElapsedMilliseconds() const;
+ double getElapsedSeconds() const;
+
+ private:
+ UInt64 m_ticks;
+ };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+ class Section : NonCopyable {
+ public:
+ Section( SectionInfo const& info );
+ ~Section();
+
+ // This indicates whether the section should be executed or not
+ operator bool() const;
+
+ private:
+ SectionInfo m_info;
+
+ std::string m_name;
+ Counts m_assertions;
+ bool m_sectionIncluded;
+ Timer m_timer;
+ };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define INTERNAL_CATCH_SECTION( ... ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+ #define INTERNAL_CATCH_SECTION( name, desc ) \
+ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+ virtual ~IGenerator() {}
+ virtual T getValue( std::size_t index ) const = 0;
+ virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+ BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+ virtual T getValue( std::size_t index ) const {
+ return m_from+static_cast<int>( index );
+ }
+
+ virtual std::size_t size() const {
+ return static_cast<std::size_t>( 1+m_to-m_from );
+ }
+
+private:
+
+ T m_from;
+ T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+ ValuesGenerator(){}
+
+ void add( T value ) {
+ m_values.push_back( value );
+ }
+
+ virtual T getValue( std::size_t index ) const {
+ return m_values[index];
+ }
+
+ virtual std::size_t size() const {
+ return m_values.size();
+ }
+
+private:
+ std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+ CompositeGenerator() : m_totalSize( 0 ) {}
+
+ // *** Move semantics, similar to auto_ptr ***
+ CompositeGenerator( CompositeGenerator& other )
+ : m_fileInfo( other.m_fileInfo ),
+ m_totalSize( 0 )
+ {
+ move( other );
+ }
+
+ CompositeGenerator& setFileInfo( const char* fileInfo ) {
+ m_fileInfo = fileInfo;
+ return *this;
+ }
+
+ ~CompositeGenerator() {
+ deleteAll( m_composed );
+ }
+
+ operator T () const {
+ size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+ typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+ typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+ for( size_t index = 0; it != itEnd; ++it )
+ {
+ const IGenerator<T>* generator = *it;
+ if( overallIndex >= index && overallIndex < index + generator->size() )
+ {
+ return generator->getValue( overallIndex-index );
+ }
+ index += generator->size();
+ }
+ CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+ return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+ }
+
+ void add( const IGenerator<T>* generator ) {
+ m_totalSize += generator->size();
+ m_composed.push_back( generator );
+ }
+
+ CompositeGenerator& then( CompositeGenerator& other ) {
+ move( other );
+ return *this;
+ }
+
+ CompositeGenerator& then( T value ) {
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( value );
+ add( valuesGen );
+ return *this;
+ }
+
+private:
+
+ void move( CompositeGenerator& other ) {
+ m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() );
+ m_totalSize += other.m_totalSize;
+ other.m_composed.clear();
+ }
+
+ std::vector<const IGenerator<T>*> m_composed;
+ std::string m_fileInfo;
+ size_t m_totalSize;
+};
+
+namespace Generators
+{
+ template<typename T>
+ CompositeGenerator<T> between( T from, T to ) {
+ CompositeGenerator<T> generators;
+ generators.add( new BetweenGenerator<T>( from, to ) );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3 ){
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+ template<typename T>
+ CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+ CompositeGenerator<T> generators;
+ ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+ valuesGen->add( val1 );
+ valuesGen->add( val2 );
+ valuesGen->add( val3 );
+ valuesGen->add( val4 );
+ generators.add( valuesGen );
+ return generators;
+ }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+#include <vector>
+
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ class TestCase;
+ struct ITestCaseRegistry;
+ struct IExceptionTranslatorRegistry;
+ struct IExceptionTranslator;
+ struct IReporterRegistry;
+ struct IReporterFactory;
+ struct ITagAliasRegistry;
+
+ struct IRegistryHub {
+ virtual ~IRegistryHub();
+
+ virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+ };
+
+ struct IMutableRegistryHub {
+ virtual ~IMutableRegistryHub();
+ virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+ virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
+ virtual void registerTest( TestCase const& testInfo ) = 0;
+ virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+ };
+
+ IRegistryHub& getRegistryHub();
+ IMutableRegistryHub& getMutableRegistryHub();
+ void cleanUp();
+ std::string translateActiveException();
+
+}
+
+namespace Catch {
+
+ typedef std::string(*exceptionTranslateFunction)();
+
+ struct IExceptionTranslator;
+ typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
+ struct IExceptionTranslator {
+ virtual ~IExceptionTranslator();
+ virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+ };
+
+ struct IExceptionTranslatorRegistry {
+ virtual ~IExceptionTranslatorRegistry();
+
+ virtual std::string translateActiveException() const = 0;
+ };
+
+ class ExceptionTranslatorRegistrar {
+ template<typename T>
+ class ExceptionTranslator : public IExceptionTranslator {
+ public:
+
+ ExceptionTranslator( std::string(*translateFunction)( T& ) )
+ : m_translateFunction( translateFunction )
+ {}
+
+ virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+ try {
+ if( it == itEnd )
+ throw;
+ else
+ return (*it)->translate( it+1, itEnd );
+ }
+ catch( T& ex ) {
+ return m_translateFunction( ex );
+ }
+ }
+
+ protected:
+ std::string(*m_translateFunction)( T& );
+ };
+
+ public:
+ template<typename T>
+ ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+ getMutableRegistryHub().registerTranslator
+ ( new ExceptionTranslator<T>( translateFunction ) );
+ }
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+ static std::string translatorName( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+ static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+namespace Detail {
+
+ class Approx {
+ public:
+ explicit Approx ( double value )
+ : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+ m_margin( 0.0 ),
+ m_scale( 1.0 ),
+ m_value( value )
+ {}
+
+ Approx( Approx const& other )
+ : m_epsilon( other.m_epsilon ),
+ m_margin( other.m_margin ),
+ m_scale( other.m_scale ),
+ m_value( other.m_value )
+ {}
+
+ static Approx custom() {
+ return Approx( 0 );
+ }
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx operator()( T value ) {
+ Approx approx( static_cast<double>(value) );
+ approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ explicit Approx( T value ): Approx(static_cast<double>(value))
+ {}
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( const T& lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ auto lhs_v = double(lhs);
+ bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( Approx const& lhs, const T& rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( T lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( Approx const& lhs, T rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( T lhs, Approx const& rhs ) {
+ return double(lhs) < rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( Approx const& lhs, T rhs ) {
+ return lhs.m_value < double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( T lhs, Approx const& rhs ) {
+ return double(lhs) > rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( Approx const& lhs, T rhs ) {
+ return lhs.m_value > double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& epsilon( T newEpsilon ) {
+ m_epsilon = double(newEpsilon);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& margin( T newMargin ) {
+ m_margin = double(newMargin);
+ return *this;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ Approx& scale( T newScale ) {
+ m_scale = double(newScale);
+ return *this;
+ }
+
+#else
+
+ Approx operator()( double value ) {
+ Approx approx( value );
+ approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
+ approx.scale( m_scale );
+ return approx;
+ }
+
+ friend bool operator == ( double lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
+ }
+
+ friend bool operator == ( Approx const& lhs, double rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ friend bool operator != ( double lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ friend bool operator != ( Approx const& lhs, double rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ friend bool operator <= ( double lhs, Approx const& rhs ) {
+ return lhs < rhs.m_value || lhs == rhs;
+ }
+
+ friend bool operator <= ( Approx const& lhs, double rhs ) {
+ return lhs.m_value < rhs || lhs == rhs;
+ }
+
+ friend bool operator >= ( double lhs, Approx const& rhs ) {
+ return lhs > rhs.m_value || lhs == rhs;
+ }
+
+ friend bool operator >= ( Approx const& lhs, double rhs ) {
+ return lhs.m_value > rhs || lhs == rhs;
+ }
+
+ Approx& epsilon( double newEpsilon ) {
+ m_epsilon = newEpsilon;
+ return *this;
+ }
+
+ Approx& margin( double newMargin ) {
+ m_margin = newMargin;
+ return *this;
+ }
+
+ Approx& scale( double newScale ) {
+ m_scale = newScale;
+ return *this;
+ }
+#endif
+
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << "Approx( " << Catch::toString( m_value ) << " )";
+ return oss.str();
+ }
+
+ private:
+ double m_epsilon;
+ double m_margin;
+ double m_scale;
+ double m_value;
+ };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+ return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ struct CasedString
+ {
+ CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+ std::string adjustString( std::string const& str ) const;
+ std::string caseSensitivitySuffix() const;
+
+ CaseSensitive::Choice m_caseSensitivity;
+ std::string m_str;
+ };
+
+ struct StringMatcherBase : MatcherBase<std::string> {
+ StringMatcherBase( std::string const& operation, CasedString const& comparator );
+ virtual std::string describe() const CATCH_OVERRIDE;
+
+ CasedString m_comparator;
+ std::string m_operation;
+ };
+
+ struct EqualsMatcher : StringMatcherBase {
+ EqualsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct ContainsMatcher : StringMatcherBase {
+ ContainsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct StartsWithMatcher : StringMatcherBase {
+ StartsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct EndsWithMatcher : StringMatcherBase {
+ EndsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+
+ } // namespace StdString
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace Vector {
+
+ template<typename T>
+ struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+
+ ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ return std::find(v.begin(), v.end(), m_comparator) != v.end();
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ T const& m_comparator;
+ };
+
+ template<typename T>
+ struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+ ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ // !TBD: see note in EqualsMatcher
+ if (m_comparator.size() > v.size())
+ return false;
+ for (size_t i = 0; i < m_comparator.size(); ++i)
+ if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ std::vector<T> const& m_comparator;
+ };
+
+ template<typename T>
+ struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+ EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+ // !TBD: This currently works if all elements can be compared using !=
+ // - a more general approach would be via a compare template that defaults
+ // to using !=. but could be specialised for, e.g. std::vector<T> etc
+ // - then just call that directly
+ if (m_comparator.size() != v.size())
+ return false;
+ for (size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != v[i])
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Equals: " + Catch::toString( m_comparator );
+ }
+ std::vector<T> const& m_comparator;
+ };
+
+ } // namespace Vector
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ template<typename T>
+ Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+ return Vector::ContainsMatcher<T>( comparator );
+ }
+
+ template<typename T>
+ Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+ return Vector::ContainsElementMatcher<T>( comparator );
+ }
+
+ template<typename T>
+ Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+ return Vector::EqualsMatcher<T>( comparator );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+ struct TagAlias {
+ TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+ std::string tag;
+ SourceLineInfo lineInfo;
+ };
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+ // An optional type
+ template<typename T>
+ class Option {
+ public:
+ Option() : nullableValue( CATCH_NULL ) {}
+ Option( T const& _value )
+ : nullableValue( new( storage ) T( _value ) )
+ {}
+ Option( Option const& _other )
+ : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
+ {}
+
+ ~Option() {
+ reset();
+ }
+
+ Option& operator= ( Option const& _other ) {
+ if( &_other != this ) {
+ reset();
+ if( _other )
+ nullableValue = new( storage ) T( *_other );
+ }
+ return *this;
+ }
+ Option& operator = ( T const& _value ) {
+ reset();
+ nullableValue = new( storage ) T( _value );
+ return *this;
+ }
+
+ void reset() {
+ if( nullableValue )
+ nullableValue->~T();
+ nullableValue = CATCH_NULL;
+ }
+
+ T& operator*() { return *nullableValue; }
+ T const& operator*() const { return *nullableValue; }
+ T* operator->() { return nullableValue; }
+ const T* operator->() const { return nullableValue; }
+
+ T valueOr( T const& defaultValue ) const {
+ return nullableValue ? *nullableValue : defaultValue;
+ }
+
+ bool some() const { return nullableValue != CATCH_NULL; }
+ bool none() const { return nullableValue == CATCH_NULL; }
+
+ bool operator !() const { return nullableValue == CATCH_NULL; }
+ operator SafeBool::type() const {
+ return SafeBool::makeSafe( some() );
+ }
+
+ private:
+ T *nullableValue;
+ union {
+ char storage[sizeof(T)];
+
+ // These are here to force alignment for the storage
+ long double dummy1;
+ void (*dummy2)();
+ long double dummy3;
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+ long long dummy4;
+#endif
+ };
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ struct ITagAliasRegistry {
+ virtual ~ITagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+ static ITagAliasRegistry const& get();
+ };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+ struct ITestCase;
+
+ struct TestCaseInfo {
+ enum SpecialProperties{
+ None = 0,
+ IsHidden = 1 << 1,
+ ShouldFail = 1 << 2,
+ MayFail = 1 << 3,
+ Throws = 1 << 4,
+ NonPortable = 1 << 5
+ };
+
+ TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo );
+
+ TestCaseInfo( TestCaseInfo const& other );
+
+ friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
+ bool isHidden() const;
+ bool throws() const;
+ bool okToFail() const;
+ bool expectedToFail() const;
+
+ std::string name;
+ std::string className;
+ std::string description;
+ std::set<std::string> tags;
+ std::set<std::string> lcaseTags;
+ std::string tagsAsString;
+ SourceLineInfo lineInfo;
+ SpecialProperties properties;
+ };
+
+ class TestCase : public TestCaseInfo {
+ public:
+
+ TestCase( ITestCase* testCase, TestCaseInfo const& info );
+ TestCase( TestCase const& other );
+
+ TestCase withName( std::string const& _newName ) const;
+
+ void invoke() const;
+
+ TestCaseInfo const& getTestCaseInfo() const;
+
+ void swap( TestCase& other );
+ bool operator == ( TestCase const& other ) const;
+ bool operator < ( TestCase const& other ) const;
+ TestCase& operator = ( TestCase const& other );
+
+ private:
+ Ptr<ITestCase> test;
+ };
+
+ TestCase makeTestCase( ITestCase* testCase,
+ std::string const& className,
+ std::string const& name,
+ std::string const& description,
+ SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+ at protocol OcFixture
+
+ at optional
+
+-(void) setUp;
+-(void) tearDown;
+
+ at end
+
+namespace Catch {
+
+ class OcMethod : public SharedImpl<ITestCase> {
+
+ public:
+ OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+ virtual void invoke() const {
+ id obj = [[m_cls alloc] init];
+
+ performOptionalSelector( obj, @selector(setUp) );
+ performOptionalSelector( obj, m_sel );
+ performOptionalSelector( obj, @selector(tearDown) );
+
+ arcSafeRelease( obj );
+ }
+ private:
+ virtual ~OcMethod() {}
+
+ Class m_cls;
+ SEL m_sel;
+ };
+
+ namespace Detail{
+
+ inline std::string getAnnotation( Class cls,
+ std::string const& annotationName,
+ std::string const& testCaseName ) {
+ NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+ SEL sel = NSSelectorFromString( selStr );
+ arcSafeRelease( selStr );
+ id value = performOptionalSelector( cls, sel );
+ if( value )
+ return [(NSString*)value UTF8String];
+ return "";
+ }
+ }
+
+ inline size_t registerTestMethods() {
+ size_t noTestMethods = 0;
+ int noClasses = objc_getClassList( CATCH_NULL, 0 );
+
+ Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+ objc_getClassList( classes, noClasses );
+
+ for( int c = 0; c < noClasses; c++ ) {
+ Class cls = classes[c];
+ {
+ u_int count;
+ Method* methods = class_copyMethodList( cls, &count );
+ for( u_int m = 0; m < count ; m++ ) {
+ SEL selector = method_getName(methods[m]);
+ std::string methodName = sel_getName(selector);
+ if( startsWith( methodName, "Catch_TestCase_" ) ) {
+ std::string testCaseName = methodName.substr( 15 );
+ std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+ std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+ const char* className = class_getName( cls );
+
+ getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+ noTestMethods++;
+ }
+ }
+ free(methods);
+ }
+ }
+ return noTestMethods;
+ }
+
+ namespace Matchers {
+ namespace Impl {
+ namespace NSStringMatchers {
+
+ struct StringHolder : MatcherBase<NSString*>{
+ StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+ StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+ StringHolder() {
+ arcSafeRelease( m_substr );
+ }
+
+ virtual bool match( NSString* arg ) const CATCH_OVERRIDE {
+ return false;
+ }
+
+ NSString* m_substr;
+ };
+
+ struct Equals : StringHolder {
+ Equals( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const CATCH_OVERRIDE {
+ return (str != nil || m_substr == nil ) &&
+ [str isEqualToString:m_substr];
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "equals string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct Contains : StringHolder {
+ Contains( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location != NSNotFound;
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "contains string: " + Catch::toString( m_substr );
+ }
+ };
+
+ struct StartsWith : StringHolder {
+ StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == 0;
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "starts with: " + Catch::toString( m_substr );
+ }
+ };
+ struct EndsWith : StringHolder {
+ EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+ virtual bool match( NSString* str ) const {
+ return (str != nil || m_substr == nil ) &&
+ [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "ends with: " + Catch::toString( m_substr );
+ }
+ };
+
+ } // namespace NSStringMatchers
+ } // namespace Impl
+
+ inline Impl::NSStringMatchers::Equals
+ Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+ inline Impl::NSStringMatchers::Contains
+ Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+ inline Impl::NSStringMatchers::StartsWith
+ StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+ inline Impl::NSStringMatchers::EndsWith
+ EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+ } // namespace Matchers
+
+ using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_IMPL
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+class LeakDetector {
+public:
+ LeakDetector() {
+ int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flag |= _CRTDBG_LEAK_CHECK_DF;
+ flag |= _CRTDBG_ALLOC_MEM_DF;
+ _CrtSetDbgFlag(flag);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ // Change this to leaking allocation's number to break there
+ _CrtSetBreakAlloc(-1);
+ }
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_session.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+#include <stdexcept>
+
+namespace Catch
+{
+ class WildcardPattern {
+ enum WildcardPosition {
+ NoWildcard = 0,
+ WildcardAtStart = 1,
+ WildcardAtEnd = 2,
+ WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+ };
+
+ public:
+
+ WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_wildcard( NoWildcard ),
+ m_pattern( adjustCase( pattern ) )
+ {
+ if( startsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 1 );
+ m_wildcard = WildcardAtStart;
+ }
+ if( endsWith( m_pattern, '*' ) ) {
+ m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+ m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+ }
+ }
+ virtual ~WildcardPattern();
+ virtual bool matches( std::string const& str ) const {
+ switch( m_wildcard ) {
+ case NoWildcard:
+ return m_pattern == adjustCase( str );
+ case WildcardAtStart:
+ return endsWith( adjustCase( str ), m_pattern );
+ case WildcardAtEnd:
+ return startsWith( adjustCase( str ), m_pattern );
+ case WildcardAtBothEnds:
+ return contains( adjustCase( str ), m_pattern );
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+ throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ }
+ private:
+ std::string adjustCase( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+ }
+ CaseSensitive::Choice m_caseSensitivity;
+ WildcardPosition m_wildcard;
+ std::string m_pattern;
+ };
+}
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ class TestSpec {
+ struct Pattern : SharedImpl<> {
+ virtual ~Pattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ };
+ class NamePattern : public Pattern {
+ public:
+ NamePattern( std::string const& name )
+ : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
+ virtual ~NamePattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return m_wildcardPattern.matches( toLower( testCase.name ) );
+ }
+ private:
+ WildcardPattern m_wildcardPattern;
+ };
+
+ class TagPattern : public Pattern {
+ public:
+ TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ virtual ~TagPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const {
+ return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+ }
+ private:
+ std::string m_tag;
+ };
+
+ class ExcludedPattern : public Pattern {
+ public:
+ ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+ virtual ~ExcludedPattern();
+ virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ private:
+ Ptr<Pattern> m_underlyingPattern;
+ };
+
+ struct Filter {
+ std::vector<Ptr<Pattern> > m_patterns;
+
+ bool matches( TestCaseInfo const& testCase ) const {
+ // All patterns in a filter must match for the filter to be a match
+ for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) {
+ if( !(*it)->matches( testCase ) )
+ return false;
+ }
+ return true;
+ }
+ };
+
+ public:
+ bool hasFilters() const {
+ return !m_filters.empty();
+ }
+ bool matches( TestCaseInfo const& testCase ) const {
+ // A TestSpec matches if any filter matches
+ for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+ if( it->matches( testCase ) )
+ return true;
+ return false;
+ }
+
+ private:
+ std::vector<Filter> m_filters;
+
+ friend class TestSpecParser;
+ };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+ class TestSpecParser {
+ enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+ Mode m_mode;
+ bool m_exclusion;
+ std::size_t m_start, m_pos;
+ std::string m_arg;
+ std::vector<std::size_t> m_escapeChars;
+ TestSpec::Filter m_currentFilter;
+ TestSpec m_testSpec;
+ ITagAliasRegistry const* m_tagAliases;
+
+ public:
+ TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+ TestSpecParser& parse( std::string const& arg ) {
+ m_mode = None;
+ m_exclusion = false;
+ m_start = std::string::npos;
+ m_arg = m_tagAliases->expandAliases( arg );
+ m_escapeChars.clear();
+ for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+ visitChar( m_arg[m_pos] );
+ if( m_mode == Name )
+ addPattern<TestSpec::NamePattern>();
+ return *this;
+ }
+ TestSpec testSpec() {
+ addFilter();
+ return m_testSpec;
+ }
+ private:
+ void visitChar( char c ) {
+ if( m_mode == None ) {
+ switch( c ) {
+ case ' ': return;
+ case '~': m_exclusion = true; return;
+ case '[': return startNewMode( Tag, ++m_pos );
+ case '"': return startNewMode( QuotedName, ++m_pos );
+ case '\\': return escape();
+ default: startNewMode( Name, m_pos ); break;
+ }
+ }
+ if( m_mode == Name ) {
+ if( c == ',' ) {
+ addPattern<TestSpec::NamePattern>();
+ addFilter();
+ }
+ else if( c == '[' ) {
+ if( subString() == "exclude:" )
+ m_exclusion = true;
+ else
+ addPattern<TestSpec::NamePattern>();
+ startNewMode( Tag, ++m_pos );
+ }
+ else if( c == '\\' )
+ escape();
+ }
+ else if( m_mode == EscapedName )
+ m_mode = Name;
+ else if( m_mode == QuotedName && c == '"' )
+ addPattern<TestSpec::NamePattern>();
+ else if( m_mode == Tag && c == ']' )
+ addPattern<TestSpec::TagPattern>();
+ }
+ void startNewMode( Mode mode, std::size_t start ) {
+ m_mode = mode;
+ m_start = start;
+ }
+ void escape() {
+ if( m_mode == None )
+ m_start = m_pos;
+ m_mode = EscapedName;
+ m_escapeChars.push_back( m_pos );
+ }
+ std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ template<typename T>
+ void addPattern() {
+ std::string token = subString();
+ for( size_t i = 0; i < m_escapeChars.size(); ++i )
+ token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+ m_escapeChars.clear();
+ if( startsWith( token, "exclude:" ) ) {
+ m_exclusion = true;
+ token = token.substr( 8 );
+ }
+ if( !token.empty() ) {
+ Ptr<TestSpec::Pattern> pattern = new T( token );
+ if( m_exclusion )
+ pattern = new TestSpec::ExcludedPattern( pattern );
+ m_currentFilter.m_patterns.push_back( pattern );
+ }
+ m_exclusion = false;
+ m_mode = None;
+ }
+ void addFilter() {
+ if( !m_currentFilter.m_patterns.empty() ) {
+ m_testSpec.m_filters.push_back( m_currentFilter );
+ m_currentFilter = TestSpec::Filter();
+ }
+ }
+ };
+ inline TestSpec parseTestSpec( std::string const& arg ) {
+ return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct Verbosity { enum Level {
+ NoOutput = 0,
+ Quiet,
+ Normal
+ }; };
+
+ struct WarnAbout { enum What {
+ Nothing = 0x00,
+ NoAssertions = 0x01
+ }; };
+
+ struct ShowDurations { enum OrNot {
+ DefaultForReporter,
+ Always,
+ Never
+ }; };
+ struct RunTests { enum InWhatOrder {
+ InDeclarationOrder,
+ InLexicographicalOrder,
+ InRandomOrder
+ }; };
+ struct UseColour { enum YesOrNo {
+ Auto,
+ Yes,
+ No
+ }; };
+
+ class TestSpec;
+
+ struct IConfig : IShared {
+
+ virtual ~IConfig();
+
+ virtual bool allowThrows() const = 0;
+ virtual std::ostream& stream() const = 0;
+ virtual std::string name() const = 0;
+ virtual bool includeSuccessfulResults() const = 0;
+ virtual bool shouldDebugBreak() const = 0;
+ virtual bool warnAboutMissingAssertions() const = 0;
+ virtual int abortAfter() const = 0;
+ virtual bool showInvisibles() const = 0;
+ virtual ShowDurations::OrNot showDurations() const = 0;
+ virtual TestSpec const& testSpec() const = 0;
+ virtual RunTests::InWhatOrder runOrder() const = 0;
+ virtual unsigned int rngSeed() const = 0;
+ virtual UseColour::YesOrNo useColour() const = 0;
+ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+
+ };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+ class StreamBufBase : public std::streambuf {
+ public:
+ virtual ~StreamBufBase() CATCH_NOEXCEPT;
+ };
+}
+
+#include <streambuf>
+#include <ostream>
+#include <fstream>
+#include <memory>
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+
+ struct IStream {
+ virtual ~IStream() CATCH_NOEXCEPT;
+ virtual std::ostream& stream() const = 0;
+ };
+
+ class FileStream : public IStream {
+ mutable std::ofstream m_ofs;
+ public:
+ FileStream( std::string const& filename );
+ virtual ~FileStream() CATCH_NOEXCEPT;
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+
+ class CoutStream : public IStream {
+ mutable std::ostream m_os;
+ public:
+ CoutStream();
+ virtual ~CoutStream() CATCH_NOEXCEPT;
+
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+
+ class DebugOutStream : public IStream {
+ CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
+ mutable std::ostream m_os;
+ public:
+ DebugOutStream();
+ virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+ public: // IStream
+ virtual std::ostream& stream() const CATCH_OVERRIDE;
+ };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <stdexcept>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+ struct ConfigData {
+
+ ConfigData()
+ : listTests( false ),
+ listTags( false ),
+ listReporters( false ),
+ listTestNamesOnly( false ),
+ listExtraInfo( false ),
+ showSuccessfulTests( false ),
+ shouldDebugBreak( false ),
+ noThrow( false ),
+ showHelp( false ),
+ showInvisibles( false ),
+ filenamesAsTags( false ),
+ abortAfter( -1 ),
+ rngSeed( 0 ),
+ verbosity( Verbosity::Normal ),
+ warnings( WarnAbout::Nothing ),
+ showDurations( ShowDurations::DefaultForReporter ),
+ runOrder( RunTests::InDeclarationOrder ),
+ useColour( UseColour::Auto )
+ {}
+
+ bool listTests;
+ bool listTags;
+ bool listReporters;
+ bool listTestNamesOnly;
+ bool listExtraInfo;
+
+ bool showSuccessfulTests;
+ bool shouldDebugBreak;
+ bool noThrow;
+ bool showHelp;
+ bool showInvisibles;
+ bool filenamesAsTags;
+
+ int abortAfter;
+ unsigned int rngSeed;
+
+ Verbosity::Level verbosity;
+ WarnAbout::What warnings;
+ ShowDurations::OrNot showDurations;
+ RunTests::InWhatOrder runOrder;
+ UseColour::YesOrNo useColour;
+
+ std::string outputFilename;
+ std::string name;
+ std::string processName;
+
+ std::vector<std::string> reporterNames;
+ std::vector<std::string> testsOrTags;
+ std::vector<std::string> sectionsToRun;
+ };
+
+ class Config : public SharedImpl<IConfig> {
+ private:
+ Config( Config const& other );
+ Config& operator = ( Config const& other );
+ virtual void dummy();
+ public:
+
+ Config()
+ {}
+
+ Config( ConfigData const& data )
+ : m_data( data ),
+ m_stream( openStream() )
+ {
+ if( !data.testsOrTags.empty() ) {
+ TestSpecParser parser( ITagAliasRegistry::get() );
+ for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+ parser.parse( data.testsOrTags[i] );
+ m_testSpec = parser.testSpec();
+ }
+ }
+
+ virtual ~Config() {}
+
+ std::string const& getFilename() const {
+ return m_data.outputFilename ;
+ }
+
+ bool listTests() const { return m_data.listTests; }
+ bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+ bool listTags() const { return m_data.listTags; }
+ bool listReporters() const { return m_data.listReporters; }
+ bool listExtraInfo() const { return m_data.listExtraInfo; }
+
+ std::string getProcessName() const { return m_data.processName; }
+
+ std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
+ std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
+
+ virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
+
+ bool showHelp() const { return m_data.showHelp; }
+
+ // IConfig interface
+ virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; }
+ virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); }
+ virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
+ virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; }
+ virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; }
+ virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; }
+ virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
+ virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
+ virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
+
+ private:
+
+ IStream const* openStream() {
+ if( m_data.outputFilename.empty() )
+ return new CoutStream();
+ else if( m_data.outputFilename[0] == '%' ) {
+ if( m_data.outputFilename == "%debug" )
+ return new DebugOutStream();
+ else
+ throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+ }
+ else
+ return new FileStream( m_data.outputFilename );
+ }
+ ConfigData m_data;
+
+ CATCH_AUTO_PTR( IStream const ) m_stream;
+ TestSpec m_testSpec;
+ };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Version 0.0.2.4
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include <cctype>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 ),
+ tabChar( '\t' )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+ TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ char tabChar; // If this char is seen the indent is changed to current pos
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ std::string wrappableChars = " [({.,/|\\-";
+ std::size_t indent = _attr.initialIndent != std::string::npos
+ ? _attr.initialIndent
+ : _attr.indent;
+ std::string remainder = _str;
+
+ while( !remainder.empty() ) {
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+ std::size_t tabPos = std::string::npos;
+ std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+ std::size_t pos = remainder.find_first_of( '\n' );
+ if( pos <= width ) {
+ width = pos;
+ }
+ pos = remainder.find_last_of( _attr.tabChar, width );
+ if( pos != std::string::npos ) {
+ tabPos = pos;
+ if( remainder[width] == '\n' )
+ width--;
+ remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+ }
+
+ if( width == remainder.size() ) {
+ spliceLine( indent, remainder, width );
+ }
+ else if( remainder[width] == '\n' ) {
+ spliceLine( indent, remainder, width );
+ if( width <= 1 || remainder.size() != 1 )
+ remainder = remainder.substr( 1 );
+ indent = _attr.indent;
+ }
+ else {
+ pos = remainder.find_last_of( wrappableChars, width );
+ if( pos != std::string::npos && pos > 0 ) {
+ spliceLine( indent, remainder, pos );
+ if( remainder[0] == ' ' )
+ remainder = remainder.substr( 1 );
+ }
+ else {
+ spliceLine( indent, remainder, width-1 );
+ lines.back() += "-";
+ }
+ if( lines.size() == 1 )
+ indent = _attr.indent;
+ if( tabPos != std::string::npos )
+ indent += tabPos;
+ }
+ }
+ }
+
+ void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+ lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+ _remainder = _remainder.substr( _pos );
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+# define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+# define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+# define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
+#include <map>
+#include <stdexcept>
+#include <memory>
+
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+ struct UnpositionalTag {};
+
+ extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+ UnpositionalTag _;
+#endif
+
+ namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+ const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ using namespace Tbc;
+
+ inline bool startsWith( std::string const& str, std::string const& prefix ) {
+ return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+ }
+
+ template<typename T> struct RemoveConstRef{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+ template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+ template<typename T> struct IsBool { static const bool value = false; };
+ template<> struct IsBool<bool> { static const bool value = true; };
+
+ template<typename T>
+ void convertInto( std::string const& _source, T& _dest ) {
+ std::stringstream ss;
+ ss << _source;
+ ss >> _dest;
+ if( ss.fail() )
+ throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+ }
+ inline void convertInto( std::string const& _source, std::string& _dest ) {
+ _dest = _source;
+ }
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( c ) );
+ }
+ inline void convertInto( std::string const& _source, bool& _dest ) {
+ std::string sourceLC = _source;
+ std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
+ if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+ _dest = true;
+ else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+ _dest = false;
+ else
+ throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" );
+ }
+
+ template<typename ConfigT>
+ struct IArgFunction {
+ virtual ~IArgFunction() {}
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
+ IArgFunction() = default;
+ IArgFunction( IArgFunction const& ) = default;
+#endif
+ virtual void set( ConfigT& config, std::string const& value ) const = 0;
+ virtual bool takesArg() const = 0;
+ virtual IArgFunction* clone() const = 0;
+ };
+
+ template<typename ConfigT>
+ class BoundArgFunction {
+ public:
+ BoundArgFunction() : functionObj( CLARA_NULL ) {}
+ BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
+ BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
+ delete functionObj;
+ functionObj = newFunctionObj;
+ return *this;
+ }
+ ~BoundArgFunction() { delete functionObj; }
+
+ void set( ConfigT& config, std::string const& value ) const {
+ functionObj->set( config, value );
+ }
+ bool takesArg() const { return functionObj->takesArg(); }
+
+ bool isSet() const {
+ return functionObj != CLARA_NULL;
+ }
+ private:
+ IArgFunction<ConfigT>* functionObj;
+ };
+
+ template<typename C>
+ struct NullBinder : IArgFunction<C>{
+ virtual void set( C&, std::string const& ) const {}
+ virtual bool takesArg() const { return true; }
+ virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+ };
+
+ template<typename C, typename M>
+ struct BoundDataMember : IArgFunction<C>{
+ BoundDataMember( M C::* _member ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ convertInto( stringValue, p.*member );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+ M C::* member;
+ };
+ template<typename C, typename M>
+ struct BoundUnaryMethod : IArgFunction<C>{
+ BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ typename RemoveConstRef<M>::type value;
+ convertInto( stringValue, value );
+ (p.*member)( value );
+ }
+ virtual bool takesArg() const { return !IsBool<M>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+ void (C::*member)( M );
+ };
+ template<typename C>
+ struct BoundNullaryMethod : IArgFunction<C>{
+ BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+ virtual void set( C& p, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ (p.*member)();
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+ void (C::*member)();
+ };
+
+ template<typename C>
+ struct BoundUnaryFunction : IArgFunction<C>{
+ BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ bool value;
+ convertInto( stringValue, value );
+ if( value )
+ function( obj );
+ }
+ virtual bool takesArg() const { return false; }
+ virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+ void (*function)( C& );
+ };
+
+ template<typename C, typename T>
+ struct BoundBinaryFunction : IArgFunction<C>{
+ BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+ virtual void set( C& obj, std::string const& stringValue ) const {
+ typename RemoveConstRef<T>::type value;
+ convertInto( stringValue, value );
+ function( obj, value );
+ }
+ virtual bool takesArg() const { return !IsBool<T>::value; }
+ virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+ void (*function)( C&, T );
+ };
+
+ } // namespace Detail
+
+ inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+ std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+ for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+ args[i] = argv[i];
+
+ return args;
+ }
+
+ class Parser {
+ enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+ Mode mode;
+ std::size_t from;
+ bool inQuotes;
+ public:
+
+ struct Token {
+ enum Type { Positional, ShortOpt, LongOpt };
+ Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+ Type type;
+ std::string data;
+ };
+
+ Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+ void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
+ const std::string doubleDash = "--";
+ for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+ parseIntoTokens( args[i], tokens);
+ }
+
+ void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+ for( std::size_t i = 0; i < arg.size(); ++i ) {
+ char c = arg[i];
+ if( c == '"' )
+ inQuotes = !inQuotes;
+ mode = handleMode( i, c, arg, tokens );
+ }
+ mode = handleMode( arg.size(), '\0', arg, tokens );
+ }
+ Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ switch( mode ) {
+ case None: return handleNone( i, c );
+ case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+ case ShortOpt:
+ case LongOpt:
+ case SlashOpt: return handleOpt( i, c, arg, tokens );
+ case Positional: return handlePositional( i, c, arg, tokens );
+ default: throw std::logic_error( "Unknown mode" );
+ }
+ }
+
+ Mode handleNone( std::size_t i, char c ) {
+ if( inQuotes ) {
+ from = i;
+ return Positional;
+ }
+ switch( c ) {
+ case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+ case '/': from = i+1; return SlashOpt;
+#endif
+ default: from = i; return Positional;
+ }
+ }
+ Mode handleMaybeShortOpt( std::size_t i, char c ) {
+ switch( c ) {
+ case '-': from = i+1; return LongOpt;
+ default: from = i; return ShortOpt;
+ }
+ }
+
+ Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string optName = arg.substr( from, i-from );
+ if( mode == ShortOpt )
+ for( std::size_t j = 0; j < optName.size(); ++j )
+ tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+ else if( mode == SlashOpt && optName.size() == 1 )
+ tokens.push_back( Token( Token::ShortOpt, optName ) );
+ else
+ tokens.push_back( Token( Token::LongOpt, optName ) );
+ return None;
+ }
+ Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+ if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+ return mode;
+
+ std::string data = arg.substr( from, i-from );
+ tokens.push_back( Token( Token::Positional, data ) );
+ return None;
+ }
+ };
+
+ template<typename ConfigT>
+ struct CommonArgProperties {
+ CommonArgProperties() {}
+ CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+ Detail::BoundArgFunction<ConfigT> boundField;
+ std::string description;
+ std::string detail;
+ std::string placeholder; // Only value if boundField takes an arg
+
+ bool takesArg() const {
+ return !placeholder.empty();
+ }
+ void validate() const {
+ if( !boundField.isSet() )
+ throw std::logic_error( "option not bound" );
+ }
+ };
+ struct OptionArgProperties {
+ std::vector<std::string> shortNames;
+ std::string longName;
+
+ bool hasShortName( std::string const& shortName ) const {
+ return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+ }
+ bool hasLongName( std::string const& _longName ) const {
+ return _longName == longName;
+ }
+ };
+ struct PositionalArgProperties {
+ PositionalArgProperties() : position( -1 ) {}
+ int position; // -1 means non-positional (floating)
+
+ bool isFixedPositional() const {
+ return position != -1;
+ }
+ };
+
+ template<typename ConfigT>
+ class CommandLine {
+
+ struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+ Arg() {}
+ Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+ using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+ std::string dbgName() const {
+ if( !longName.empty() )
+ return "--" + longName;
+ if( !shortNames.empty() )
+ return "-" + shortNames[0];
+ return "positional args";
+ }
+ std::string commands() const {
+ std::ostringstream oss;
+ bool first = true;
+ std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+ for(; it != itEnd; ++it ) {
+ if( first )
+ first = false;
+ else
+ oss << ", ";
+ oss << "-" << *it;
+ }
+ if( !longName.empty() ) {
+ if( !first )
+ oss << ", ";
+ oss << "--" << longName;
+ }
+ if( !placeholder.empty() )
+ oss << " <" << placeholder << ">";
+ return oss.str();
+ }
+ };
+
+ typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
+
+ friend void addOptName( Arg& arg, std::string const& optName )
+ {
+ if( optName.empty() )
+ return;
+ if( Detail::startsWith( optName, "--" ) ) {
+ if( !arg.longName.empty() )
+ throw std::logic_error( "Only one long opt may be specified. '"
+ + arg.longName
+ + "' already specified, now attempting to add '"
+ + optName + "'" );
+ arg.longName = optName.substr( 2 );
+ }
+ else if( Detail::startsWith( optName, "-" ) )
+ arg.shortNames.push_back( optName.substr( 1 ) );
+ else
+ throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+ }
+ friend void setPositionalArg( Arg& arg, int position )
+ {
+ arg.position = position;
+ }
+
+ class ArgBuilder {
+ public:
+ ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+ // Bind a non-boolean data member (requires placeholder string)
+ template<typename C, typename M>
+ void bind( M C::* field, std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+ m_arg->placeholder = placeholder;
+ }
+ // Bind a boolean data member (no placeholder required)
+ template<typename C>
+ void bind( bool C::* field ) {
+ m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+ }
+
+ // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+ template<typename C, typename M>
+ void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+ m_arg->placeholder = placeholder;
+ }
+
+ // Bind a method taking a single, boolean argument (no placeholder string required)
+ template<typename C>
+ void bind( void (C::* unaryMethod)( bool ) ) {
+ m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+ }
+
+ // Bind a method that takes no arguments (will be called if opt is present)
+ template<typename C>
+ void bind( void (C::* nullaryMethod)() ) {
+ m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+ template<typename C>
+ void bind( void (* unaryFunction)( C& ) ) {
+ m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+ }
+
+ // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+ template<typename C, typename T>
+ void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+ m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+ m_arg->placeholder = placeholder;
+ }
+
+ ArgBuilder& describe( std::string const& description ) {
+ m_arg->description = description;
+ return *this;
+ }
+ ArgBuilder& detail( std::string const& detail ) {
+ m_arg->detail = detail;
+ return *this;
+ }
+
+ protected:
+ Arg* m_arg;
+ };
+
+ class OptBuilder : public ArgBuilder {
+ public:
+ OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+ OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+ OptBuilder& operator[]( std::string const& optName ) {
+ addOptName( *ArgBuilder::m_arg, optName );
+ return *this;
+ }
+ };
+
+ public:
+
+ CommandLine()
+ : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+ m_highestSpecifiedArgPosition( 0 ),
+ m_throwOnUnrecognisedTokens( false )
+ {}
+ CommandLine( CommandLine const& other )
+ : m_boundProcessName( other.m_boundProcessName ),
+ m_options ( other.m_options ),
+ m_positionalArgs( other.m_positionalArgs ),
+ m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+ m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+ {
+ if( other.m_floatingArg.get() )
+ m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
+ }
+
+ CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+ m_throwOnUnrecognisedTokens = shouldThrow;
+ return *this;
+ }
+
+ OptBuilder operator[]( std::string const& optName ) {
+ m_options.push_back( Arg() );
+ addOptName( m_options.back(), optName );
+ OptBuilder builder( &m_options.back() );
+ return builder;
+ }
+
+ ArgBuilder operator[]( int position ) {
+ m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+ if( position > m_highestSpecifiedArgPosition )
+ m_highestSpecifiedArgPosition = position;
+ setPositionalArg( m_positionalArgs[position], position );
+ ArgBuilder builder( &m_positionalArgs[position] );
+ return builder;
+ }
+
+ // Invoke this with the _ instance
+ ArgBuilder operator[]( UnpositionalTag ) {
+ if( m_floatingArg.get() )
+ throw std::logic_error( "Only one unpositional argument can be added" );
+ m_floatingArg.reset( new Arg() );
+ ArgBuilder builder( m_floatingArg.get() );
+ return builder;
+ }
+
+ template<typename C, typename M>
+ void bindProcessName( M C::* field ) {
+ m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+ }
+ template<typename C, typename M>
+ void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+ m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+ }
+
+ void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+ typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+ std::size_t maxWidth = 0;
+ for( it = itBegin; it != itEnd; ++it )
+ maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+ for( it = itBegin; it != itEnd; ++it ) {
+ Detail::Text usage( it->commands(), Detail::TextAttributes()
+ .setWidth( maxWidth+indent )
+ .setIndent( indent ) );
+ Detail::Text desc( it->description, Detail::TextAttributes()
+ .setWidth( width - maxWidth - 3 ) );
+
+ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+ std::string usageCol = i < usage.size() ? usage[i] : "";
+ os << usageCol;
+
+ if( i < desc.size() && !desc[i].empty() )
+ os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+ << desc[i];
+ os << "\n";
+ }
+ }
+ }
+ std::string optUsage() const {
+ std::ostringstream oss;
+ optUsage( oss );
+ return oss.str();
+ }
+
+ void argSynopsis( std::ostream& os ) const {
+ for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+ if( i > 1 )
+ os << " ";
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+ if( it != m_positionalArgs.end() )
+ os << "<" << it->second.placeholder << ">";
+ else if( m_floatingArg.get() )
+ os << "<" << m_floatingArg->placeholder << ">";
+ else
+ throw std::logic_error( "non consecutive positional arguments with no floating args" );
+ }
+ // !TBD No indication of mandatory args
+ if( m_floatingArg.get() ) {
+ if( m_highestSpecifiedArgPosition > 1 )
+ os << " ";
+ os << "[<" << m_floatingArg->placeholder << "> ...]";
+ }
+ }
+ std::string argSynopsis() const {
+ std::ostringstream oss;
+ argSynopsis( oss );
+ return oss.str();
+ }
+
+ void usage( std::ostream& os, std::string const& procName ) const {
+ validate();
+ os << "usage:\n " << procName << " ";
+ argSynopsis( os );
+ if( !m_options.empty() ) {
+ os << " [options]\n\nwhere options are: \n";
+ optUsage( os, 2 );
+ }
+ os << "\n";
+ }
+ std::string usage( std::string const& procName ) const {
+ std::ostringstream oss;
+ usage( oss, procName );
+ return oss.str();
+ }
+
+ ConfigT parse( std::vector<std::string> const& args ) const {
+ ConfigT config;
+ parseInto( args, config );
+ return config;
+ }
+
+ std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+ std::string processName = args.empty() ? std::string() : args[0];
+ std::size_t lastSlash = processName.find_last_of( "/\\" );
+ if( lastSlash != std::string::npos )
+ processName = processName.substr( lastSlash+1 );
+ m_boundProcessName.set( config, processName );
+ std::vector<Parser::Token> tokens;
+ Parser parser;
+ parser.parseIntoTokens( args, tokens );
+ return populate( tokens, config );
+ }
+
+ std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ validate();
+ std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+ unusedTokens = populateFixedArgs( unusedTokens, config );
+ unusedTokens = populateFloatingArgs( unusedTokens, config );
+ return unusedTokens;
+ }
+
+ std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ std::vector<std::string> errors;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+ for(; it != itEnd; ++it ) {
+ Arg const& arg = *it;
+
+ try {
+ if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+ ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+ if( arg.takesArg() ) {
+ if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+ errors.push_back( "Expected argument to option: " + token.data );
+ else
+ arg.boundField.set( config, tokens[++i].data );
+ }
+ else {
+ arg.boundField.set( config, "true" );
+ }
+ break;
+ }
+ }
+ catch( std::exception& ex ) {
+ errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+ }
+ }
+ if( it == itEnd ) {
+ if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+ unusedTokens.push_back( token );
+ else if( errors.empty() && m_throwOnUnrecognisedTokens )
+ errors.push_back( "unrecognised option: " + token.data );
+ }
+ }
+ if( !errors.empty() ) {
+ std::ostringstream oss;
+ for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+ it != itEnd;
+ ++it ) {
+ if( it != errors.begin() )
+ oss << "\n";
+ oss << *it;
+ }
+ throw std::runtime_error( oss.str() );
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ std::vector<Parser::Token> unusedTokens;
+ int position = 1;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+ if( it != m_positionalArgs.end() )
+ it->second.boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ if( token.type == Parser::Token::Positional )
+ position++;
+ }
+ return unusedTokens;
+ }
+ std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+ if( !m_floatingArg.get() )
+ return tokens;
+ std::vector<Parser::Token> unusedTokens;
+ for( std::size_t i = 0; i < tokens.size(); ++i ) {
+ Parser::Token const& token = tokens[i];
+ if( token.type == Parser::Token::Positional )
+ m_floatingArg->boundField.set( config, token.data );
+ else
+ unusedTokens.push_back( token );
+ }
+ return unusedTokens;
+ }
+
+ void validate() const
+ {
+ if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+ throw std::logic_error( "No options or arguments specified" );
+
+ for( typename std::vector<Arg>::const_iterator it = m_options.begin(),
+ itEnd = m_options.end();
+ it != itEnd; ++it )
+ it->validate();
+ }
+
+ private:
+ Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+ std::vector<Arg> m_options;
+ std::map<int, Arg> m_positionalArgs;
+ ArgAutoPtr m_floatingArg;
+ int m_highestSpecifiedArgPosition;
+ bool m_throwOnUnrecognisedTokens;
+ };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+ inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+ inline void abortAfterX( ConfigData& config, int x ) {
+ if( x < 1 )
+ throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+ config.abortAfter = x;
+ }
+ inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+ inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
+ inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
+
+ inline void addWarning( ConfigData& config, std::string const& _warning ) {
+ if( _warning == "NoAssertions" )
+ config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+ else
+ throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
+ }
+ inline void setOrder( ConfigData& config, std::string const& order ) {
+ if( startsWith( "declared", order ) )
+ config.runOrder = RunTests::InDeclarationOrder;
+ else if( startsWith( "lexical", order ) )
+ config.runOrder = RunTests::InLexicographicalOrder;
+ else if( startsWith( "random", order ) )
+ config.runOrder = RunTests::InRandomOrder;
+ else
+ throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' );
+ }
+ inline void setRngSeed( ConfigData& config, std::string const& seed ) {
+ if( seed == "time" ) {
+ config.rngSeed = static_cast<unsigned int>( std::time(0) );
+ }
+ else {
+ std::stringstream ss;
+ ss << seed;
+ ss >> config.rngSeed;
+ if( ss.fail() )
+ throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
+ }
+ }
+ inline void setVerbosity( ConfigData& config, int level ) {
+ // !TBD: accept strings?
+ config.verbosity = static_cast<Verbosity::Level>( level );
+ }
+ inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+ config.showDurations = _showDurations
+ ? ShowDurations::Always
+ : ShowDurations::Never;
+ }
+ inline void setUseColour( ConfigData& config, std::string const& value ) {
+ std::string mode = toLower( value );
+
+ if( mode == "yes" )
+ config.useColour = UseColour::Yes;
+ else if( mode == "no" )
+ config.useColour = UseColour::No;
+ else if( mode == "auto" )
+ config.useColour = UseColour::Auto;
+ else
+ throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+ }
+ inline void forceColour( ConfigData& config ) {
+ config.useColour = UseColour::Yes;
+ }
+ inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+ std::ifstream f( _filename.c_str() );
+ if( !f.is_open() )
+ throw std::domain_error( "Unable to load input file: " + _filename );
+
+ std::string line;
+ while( std::getline( f, line ) ) {
+ line = trim(line);
+ if( !line.empty() && !startsWith( line, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + line + '"';
+ addTestOrTags( config, line + ',' );
+ }
+ }
+ }
+
+ inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+ using namespace Clara;
+ CommandLine<ConfigData> cli;
+
+ cli.bindProcessName( &ConfigData::processName );
+
+ cli["-?"]["-h"]["--help"]
+ .describe( "display usage information" )
+ .bind( &ConfigData::showHelp );
+
+ cli["-l"]["--list-tests"]
+ .describe( "list all/matching test cases" )
+ .bind( &ConfigData::listTests );
+
+ cli["-t"]["--list-tags"]
+ .describe( "list all/matching tags" )
+ .bind( &ConfigData::listTags );
+
+ cli["-s"]["--success"]
+ .describe( "include successful tests in output" )
+ .bind( &ConfigData::showSuccessfulTests );
+
+ cli["-b"]["--break"]
+ .describe( "break into debugger on failure" )
+ .bind( &ConfigData::shouldDebugBreak );
+
+ cli["-e"]["--nothrow"]
+ .describe( "skip exception tests" )
+ .bind( &ConfigData::noThrow );
+
+ cli["-i"]["--invisibles"]
+ .describe( "show invisibles (tabs, newlines)" )
+ .bind( &ConfigData::showInvisibles );
+
+ cli["-o"]["--out"]
+ .describe( "output filename" )
+ .bind( &ConfigData::outputFilename, "filename" );
+
+ cli["-r"]["--reporter"]
+// .placeholder( "name[:filename]" )
+ .describe( "reporter to use (defaults to console)" )
+ .bind( &addReporterName, "name" );
+
+ cli["-n"]["--name"]
+ .describe( "suite name" )
+ .bind( &ConfigData::name, "name" );
+
+ cli["-a"]["--abort"]
+ .describe( "abort at first failure" )
+ .bind( &abortAfterFirst );
+
+ cli["-x"]["--abortx"]
+ .describe( "abort after x failures" )
+ .bind( &abortAfterX, "no. failures" );
+
+ cli["-w"]["--warn"]
+ .describe( "enable warnings" )
+ .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+// cli.into( &setVerbosity )
+// .describe( "level of verbosity (0=no output)" )
+// .shortOpt( "v")
+// .longOpt( "verbosity" )
+// .placeholder( "level" );
+
+ cli[_]
+ .describe( "which test or tests to use" )
+ .bind( &addTestOrTags, "test name, pattern or tags" );
+
+ cli["-d"]["--durations"]
+ .describe( "show test durations" )
+ .bind( &setShowDurations, "yes|no" );
+
+ cli["-f"]["--input-file"]
+ .describe( "load test names to run from a file" )
+ .bind( &loadTestNamesFromFile, "filename" );
+
+ cli["-#"]["--filenames-as-tags"]
+ .describe( "adds a tag for the filename" )
+ .bind( &ConfigData::filenamesAsTags );
+
+ cli["-c"]["--section"]
+ .describe( "specify section to run" )
+ .bind( &addSectionToRun, "section name" );
+
+ // Less common commands which don't have a short form
+ cli["--list-test-names-only"]
+ .describe( "list all/matching test cases names only" )
+ .bind( &ConfigData::listTestNamesOnly );
+
+ cli["--list-extra-info"]
+ .describe( "list all/matching test cases with more info" )
+ .bind( &ConfigData::listExtraInfo );
+
+ cli["--list-reporters"]
+ .describe( "list all reporters" )
+ .bind( &ConfigData::listReporters );
+
+ cli["--order"]
+ .describe( "test case order (defaults to decl)" )
+ .bind( &setOrder, "decl|lex|rand" );
+
+ cli["--rng-seed"]
+ .describe( "set a specific seed for random numbers" )
+ .bind( &setRngSeed, "'time'|number" );
+
+ cli["--force-colour"]
+ .describe( "force colourised output (deprecated)" )
+ .bind( &forceColour );
+
+ cli["--use-colour"]
+ .describe( "should output be colourised" )
+ .bind( &setUseColour, "yes|no" );
+
+ return cli;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+# endif
+# else
+# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+ const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+ const unsigned int consoleWidth = 80;
+#endif
+
+ struct TextAttributes {
+ TextAttributes()
+ : initialIndent( std::string::npos ),
+ indent( 0 ),
+ width( consoleWidth-1 )
+ {}
+
+ TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
+ TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
+ TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
+
+ std::size_t initialIndent; // indent of first line, or npos
+ std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
+ std::size_t width; // maximum width of text, including indent. Longer text will wrap
+ };
+
+ class Text {
+ public:
+ Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+ : attr( _attr )
+ {
+ const std::string wrappableBeforeChars = "[({<\t";
+ const std::string wrappableAfterChars = "])}>-,./|\\";
+ const std::string wrappableInsteadOfChars = " \n\r";
+ std::string indent = _attr.initialIndent != std::string::npos
+ ? std::string( _attr.initialIndent, ' ' )
+ : std::string( _attr.indent, ' ' );
+
+ typedef std::string::const_iterator iterator;
+ iterator it = _str.begin();
+ const iterator strEnd = _str.end();
+
+ while( it != strEnd ) {
+
+ if( lines.size() >= 1000 ) {
+ lines.push_back( "... message truncated due to excessive size" );
+ return;
+ }
+
+ std::string suffix;
+ std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
+ iterator itEnd = it+width;
+ iterator itNext = _str.end();
+
+ iterator itNewLine = std::find( it, itEnd, '\n' );
+ if( itNewLine != itEnd )
+ itEnd = itNewLine;
+
+ if( itEnd != strEnd ) {
+ bool foundWrapPoint = false;
+ iterator findIt = itEnd;
+ do {
+ if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
+ itEnd = findIt+1;
+ itNext = findIt+1;
+ foundWrapPoint = true;
+ }
+ else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
+ itEnd = findIt;
+ itNext = findIt;
+ foundWrapPoint = true;
+ }
+ else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
+ itNext = findIt+1;
+ itEnd = findIt;
+ foundWrapPoint = true;
+ }
+ if( findIt == it )
+ break;
+ else
+ --findIt;
+ }
+ while( !foundWrapPoint );
+
+ if( !foundWrapPoint ) {
+ // No good wrap char, so we'll break mid word and add a hyphen
+ --itEnd;
+ itNext = itEnd;
+ suffix = "-";
+ }
+ else {
+ while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
+ --itEnd;
+ }
+ }
+ lines.push_back( indent + std::string( it, itEnd ) + suffix );
+
+ if( indent.size() != _attr.indent )
+ indent = std::string( _attr.indent, ' ' );
+ it = itNext;
+ }
+ }
+
+ typedef std::vector<std::string>::const_iterator const_iterator;
+
+ const_iterator begin() const { return lines.begin(); }
+ const_iterator end() const { return lines.end(); }
+ std::string const& last() const { return lines.back(); }
+ std::size_t size() const { return lines.size(); }
+ std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+ std::string toString() const {
+ std::ostringstream oss;
+ oss << *this;
+ return oss.str();
+ }
+
+ inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+ for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+ it != itEnd; ++it ) {
+ if( it != _text.begin() )
+ _stream << "\n";
+ _stream << *it;
+ }
+ return _stream;
+ }
+
+ private:
+ std::string str;
+ TextAttributes attr;
+ std::vector<std::string> lines;
+ };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+ using Tbc::Text;
+ using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+ struct Colour {
+ enum Code {
+ None = 0,
+
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White,
+
+ // By intention
+ FileName = LightGrey,
+ Warning = Yellow,
+ ResultError = BrightRed,
+ ResultSuccess = BrightGreen,
+ ResultExpectedFailure = Warning,
+
+ Error = BrightRed,
+ Success = Green,
+
+ OriginalExpression = Cyan,
+ ReconstructedExpression = Yellow,
+
+ SecondaryText = LightGrey,
+ Headers = White
+ };
+
+ // Use constructed object for RAII guard
+ Colour( Code _colourCode );
+ Colour( Colour const& other );
+ ~Colour();
+
+ // Use static method for one-shot changes
+ static void use( Code _colourCode );
+
+ private:
+ bool m_moved;
+ };
+
+ inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+
+namespace Catch
+{
+ struct ReporterConfig {
+ explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
+ : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+ ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
+ : m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+ std::ostream& stream() const { return *m_stream; }
+ Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
+
+ private:
+ std::ostream* m_stream;
+ Ptr<IConfig const> m_fullConfig;
+ };
+
+ struct ReporterPreferences {
+ ReporterPreferences()
+ : shouldRedirectStdOut( false )
+ {}
+
+ bool shouldRedirectStdOut;
+ };
+
+ template<typename T>
+ struct LazyStat : Option<T> {
+ LazyStat() : used( false ) {}
+ LazyStat& operator=( T const& _value ) {
+ Option<T>::operator=( _value );
+ used = false;
+ return *this;
+ }
+ void reset() {
+ Option<T>::reset();
+ used = false;
+ }
+ bool used;
+ };
+
+ struct TestRunInfo {
+ TestRunInfo( std::string const& _name ) : name( _name ) {}
+ std::string name;
+ };
+ struct GroupInfo {
+ GroupInfo( std::string const& _name,
+ std::size_t _groupIndex,
+ std::size_t _groupsCount )
+ : name( _name ),
+ groupIndex( _groupIndex ),
+ groupsCounts( _groupsCount )
+ {}
+
+ std::string name;
+ std::size_t groupIndex;
+ std::size_t groupsCounts;
+ };
+
+ struct AssertionStats {
+ AssertionStats( AssertionResult const& _assertionResult,
+ std::vector<MessageInfo> const& _infoMessages,
+ Totals const& _totals )
+ : assertionResult( _assertionResult ),
+ infoMessages( _infoMessages ),
+ totals( _totals )
+ {
+ if( assertionResult.hasMessage() ) {
+ // Copy message into messages list.
+ // !TBD This should have been done earlier, somewhere
+ MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+ builder << assertionResult.getMessage();
+ builder.m_info.message = builder.m_stream.str();
+
+ infoMessages.push_back( builder.m_info );
+ }
+ }
+ virtual ~AssertionStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ AssertionStats( AssertionStats const& ) = default;
+ AssertionStats( AssertionStats && ) = default;
+ AssertionStats& operator = ( AssertionStats const& ) = default;
+ AssertionStats& operator = ( AssertionStats && ) = default;
+# endif
+
+ AssertionResult assertionResult;
+ std::vector<MessageInfo> infoMessages;
+ Totals totals;
+ };
+
+ struct SectionStats {
+ SectionStats( SectionInfo const& _sectionInfo,
+ Counts const& _assertions,
+ double _durationInSeconds,
+ bool _missingAssertions )
+ : sectionInfo( _sectionInfo ),
+ assertions( _assertions ),
+ durationInSeconds( _durationInSeconds ),
+ missingAssertions( _missingAssertions )
+ {}
+ virtual ~SectionStats();
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SectionStats( SectionStats const& ) = default;
+ SectionStats( SectionStats && ) = default;
+ SectionStats& operator = ( SectionStats const& ) = default;
+ SectionStats& operator = ( SectionStats && ) = default;
+# endif
+
+ SectionInfo sectionInfo;
+ Counts assertions;
+ double durationInSeconds;
+ bool missingAssertions;
+ };
+
+ struct TestCaseStats {
+ TestCaseStats( TestCaseInfo const& _testInfo,
+ Totals const& _totals,
+ std::string const& _stdOut,
+ std::string const& _stdErr,
+ bool _aborting )
+ : testInfo( _testInfo ),
+ totals( _totals ),
+ stdOut( _stdOut ),
+ stdErr( _stdErr ),
+ aborting( _aborting )
+ {}
+ virtual ~TestCaseStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestCaseStats( TestCaseStats const& ) = default;
+ TestCaseStats( TestCaseStats && ) = default;
+ TestCaseStats& operator = ( TestCaseStats const& ) = default;
+ TestCaseStats& operator = ( TestCaseStats && ) = default;
+# endif
+
+ TestCaseInfo testInfo;
+ Totals totals;
+ std::string stdOut;
+ std::string stdErr;
+ bool aborting;
+ };
+
+ struct TestGroupStats {
+ TestGroupStats( GroupInfo const& _groupInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : groupInfo( _groupInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ TestGroupStats( GroupInfo const& _groupInfo )
+ : groupInfo( _groupInfo ),
+ aborting( false )
+ {}
+ virtual ~TestGroupStats();
+
+# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestGroupStats( TestGroupStats const& ) = default;
+ TestGroupStats( TestGroupStats && ) = default;
+ TestGroupStats& operator = ( TestGroupStats const& ) = default;
+ TestGroupStats& operator = ( TestGroupStats && ) = default;
+# endif
+
+ GroupInfo groupInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ struct TestRunStats {
+ TestRunStats( TestRunInfo const& _runInfo,
+ Totals const& _totals,
+ bool _aborting )
+ : runInfo( _runInfo ),
+ totals( _totals ),
+ aborting( _aborting )
+ {}
+ virtual ~TestRunStats();
+
+# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ TestRunStats( TestRunStats const& _other )
+ : runInfo( _other.runInfo ),
+ totals( _other.totals ),
+ aborting( _other.aborting )
+ {}
+# else
+ TestRunStats( TestRunStats const& ) = default;
+ TestRunStats( TestRunStats && ) = default;
+ TestRunStats& operator = ( TestRunStats const& ) = default;
+ TestRunStats& operator = ( TestRunStats && ) = default;
+# endif
+
+ TestRunInfo runInfo;
+ Totals totals;
+ bool aborting;
+ };
+
+ class MultipleReporters;
+
+ struct IStreamingReporter : IShared {
+ virtual ~IStreamingReporter();
+
+ // Implementing class must also provide the following static method:
+ // static std::string getDescription();
+
+ virtual ReporterPreferences getPreferences() const = 0;
+
+ virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+ virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+ virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
+ };
+
+ struct IReporterFactory : IShared {
+ virtual ~IReporterFactory();
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+ virtual std::string getDescription() const = 0;
+ };
+
+ struct IReporterRegistry {
+ typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+ typedef std::vector<Ptr<IReporterFactory> > Listeners;
+
+ virtual ~IReporterRegistry();
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
+ virtual FactoryMap const& getFactories() const = 0;
+ virtual Listeners const& getListeners() const = 0;
+ };
+
+ Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+ inline std::size_t listTests( Config const& config ) {
+
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ Catch::cout() << "Matching test cases:\n";
+ else {
+ Catch::cout() << "All available test cases:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::size_t matchedTests = 0;
+ TextAttributes nameAttr, descAttr, tagsAttr;
+ nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+ descAttr.setIndent( 4 );
+ tagsAttr.setIndent( 6 );
+
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ Colour::Code colour = testCaseInfo.isHidden()
+ ? Colour::SecondaryText
+ : Colour::None;
+ Colour colourGuard( colour );
+
+ Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+ if( config.listExtraInfo() ) {
+ Catch::cout() << " " << testCaseInfo.lineInfo << std::endl;
+ std::string description = testCaseInfo.description;
+ if( description.empty() )
+ description = "(NO DESCRIPTION)";
+ Catch::cout() << Text( description, descAttr ) << std::endl;
+ }
+ if( !testCaseInfo.tags.empty() )
+ Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+ }
+
+ if( !config.testSpec().hasFilters() )
+ Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
+ else
+ Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
+ return matchedTests;
+ }
+
+ inline std::size_t listTestsNamesOnly( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( !config.testSpec().hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ std::size_t matchedTests = 0;
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ matchedTests++;
+ TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+ if( startsWith( testCaseInfo.name, '#' ) )
+ Catch::cout() << '"' << testCaseInfo.name << '"';
+ else
+ Catch::cout() << testCaseInfo.name;
+ if ( config.listExtraInfo() )
+ Catch::cout() << "\t@" << testCaseInfo.lineInfo;
+ Catch::cout() << std::endl;
+ }
+ return matchedTests;
+ }
+
+ struct TagInfo {
+ TagInfo() : count ( 0 ) {}
+ void add( std::string const& spelling ) {
+ ++count;
+ spellings.insert( spelling );
+ }
+ std::string all() const {
+ std::string out;
+ for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+ it != itEnd;
+ ++it )
+ out += "[" + *it + "]";
+ return out;
+ }
+ std::set<std::string> spellings;
+ std::size_t count;
+ };
+
+ inline std::size_t listTags( Config const& config ) {
+ TestSpec testSpec = config.testSpec();
+ if( config.testSpec().hasFilters() )
+ Catch::cout() << "Tags for matching test cases:\n";
+ else {
+ Catch::cout() << "All available tags:\n";
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+ }
+
+ std::map<std::string, TagInfo> tagCounts;
+
+ std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+ for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+ it != itEnd;
+ ++it ) {
+ for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
+ tagItEnd = it->getTestCaseInfo().tags.end();
+ tagIt != tagItEnd;
+ ++tagIt ) {
+ std::string tagName = *tagIt;
+ std::string lcaseTagName = toLower( tagName );
+ std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+ if( countIt == tagCounts.end() )
+ countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+ countIt->second.add( tagName );
+ }
+ }
+
+ for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+ countItEnd = tagCounts.end();
+ countIt != countItEnd;
+ ++countIt ) {
+ std::ostringstream oss;
+ oss << " " << std::setw(2) << countIt->second.count << " ";
+ Text wrapper( countIt->second.all(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( oss.str().size() )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+ Catch::cout() << oss.str() << wrapper << '\n';
+ }
+ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+ return tagCounts.size();
+ }
+
+ inline std::size_t listReporters( Config const& /*config*/ ) {
+ Catch::cout() << "Available reporters:\n";
+ IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+ std::size_t maxNameLen = 0;
+ for(it = itBegin; it != itEnd; ++it )
+ maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+ for(it = itBegin; it != itEnd; ++it ) {
+ Text wrapper( it->second->getDescription(), TextAttributes()
+ .setInitialIndent( 0 )
+ .setIndent( 7+maxNameLen )
+ .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+ Catch::cout() << " "
+ << it->first
+ << ':'
+ << std::string( maxNameLen - it->first.size() + 2, ' ' )
+ << wrapper << '\n';
+ }
+ Catch::cout() << std::endl;
+ return factories.size();
+ }
+
+ inline Option<std::size_t> list( Config const& config ) {
+ Option<std::size_t> listedCount;
+ if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) )
+ listedCount = listedCount.valueOr(0) + listTests( config );
+ if( config.listTestNamesOnly() )
+ listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+ if( config.listTags() )
+ listedCount = listedCount.valueOr(0) + listTags( config );
+ if( config.listReporters() )
+ listedCount = listedCount.valueOr(0) + listReporters( config );
+ return listedCount;
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_run_context.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <algorithm>
+#include <string>
+#include <assert.h>
+#include <vector>
+#include <stdexcept>
+
+CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+
+namespace Catch {
+namespace TestCaseTracking {
+
+ struct NameAndLocation {
+ std::string name;
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+ : name( _name ),
+ location( _location )
+ {}
+ };
+
+ struct ITracker : SharedImpl<> {
+ virtual ~ITracker();
+
+ // static queries
+ virtual NameAndLocation const& nameAndLocation() const = 0;
+
+ // dynamic queries
+ virtual bool isComplete() const = 0; // Successfully completed or failed
+ virtual bool isSuccessfullyCompleted() const = 0;
+ virtual bool isOpen() const = 0; // Started but not complete
+ virtual bool hasChildren() const = 0;
+
+ virtual ITracker& parent() = 0;
+
+ // actions
+ virtual void close() = 0; // Successfully complete
+ virtual void fail() = 0;
+ virtual void markAsNeedingAnotherRun() = 0;
+
+ virtual void addChild( Ptr<ITracker> const& child ) = 0;
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
+ virtual void openChild() = 0;
+
+ // Debug/ checking
+ virtual bool isSectionTracker() const = 0;
+ virtual bool isIndexTracker() const = 0;
+ };
+
+ class TrackerContext {
+
+ enum RunState {
+ NotStarted,
+ Executing,
+ CompletedCycle
+ };
+
+ Ptr<ITracker> m_rootTracker;
+ ITracker* m_currentTracker;
+ RunState m_runState;
+
+ public:
+
+ static TrackerContext& instance() {
+ static TrackerContext s_instance;
+ return s_instance;
+ }
+
+ TrackerContext()
+ : m_currentTracker( CATCH_NULL ),
+ m_runState( NotStarted )
+ {}
+
+ ITracker& startRun();
+
+ void endRun() {
+ m_rootTracker.reset();
+ m_currentTracker = CATCH_NULL;
+ m_runState = NotStarted;
+ }
+
+ void startCycle() {
+ m_currentTracker = m_rootTracker.get();
+ m_runState = Executing;
+ }
+ void completeCycle() {
+ m_runState = CompletedCycle;
+ }
+
+ bool completedCycle() const {
+ return m_runState == CompletedCycle;
+ }
+ ITracker& currentTracker() {
+ return *m_currentTracker;
+ }
+ void setCurrentTracker( ITracker* tracker ) {
+ m_currentTracker = tracker;
+ }
+ };
+
+ class TrackerBase : public ITracker {
+ protected:
+ enum CycleState {
+ NotStarted,
+ Executing,
+ ExecutingChildren,
+ NeedsAnotherRun,
+ CompletedSuccessfully,
+ Failed
+ };
+ class TrackerHasName {
+ NameAndLocation m_nameAndLocation;
+ public:
+ TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
+ bool operator ()( Ptr<ITracker> const& tracker ) {
+ return
+ tracker->nameAndLocation().name == m_nameAndLocation.name &&
+ tracker->nameAndLocation().location == m_nameAndLocation.location;
+ }
+ };
+ typedef std::vector<Ptr<ITracker> > Children;
+ NameAndLocation m_nameAndLocation;
+ TrackerContext& m_ctx;
+ ITracker* m_parent;
+ Children m_children;
+ CycleState m_runState;
+ public:
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : m_nameAndLocation( nameAndLocation ),
+ m_ctx( ctx ),
+ m_parent( parent ),
+ m_runState( NotStarted )
+ {}
+ virtual ~TrackerBase();
+
+ virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
+ return m_nameAndLocation;
+ }
+ virtual bool isComplete() const CATCH_OVERRIDE {
+ return m_runState == CompletedSuccessfully || m_runState == Failed;
+ }
+ virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+ return m_runState == CompletedSuccessfully;
+ }
+ virtual bool isOpen() const CATCH_OVERRIDE {
+ return m_runState != NotStarted && !isComplete();
+ }
+ virtual bool hasChildren() const CATCH_OVERRIDE {
+ return !m_children.empty();
+ }
+
+ virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+ m_children.push_back( child );
+ }
+
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
+ Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
+ return( it != m_children.end() )
+ ? it->get()
+ : CATCH_NULL;
+ }
+ virtual ITracker& parent() CATCH_OVERRIDE {
+ assert( m_parent ); // Should always be non-null except for root
+ return *m_parent;
+ }
+
+ virtual void openChild() CATCH_OVERRIDE {
+ if( m_runState != ExecutingChildren ) {
+ m_runState = ExecutingChildren;
+ if( m_parent )
+ m_parent->openChild();
+ }
+ }
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
+ void open() {
+ m_runState = Executing;
+ moveToThis();
+ if( m_parent )
+ m_parent->openChild();
+ }
+
+ virtual void close() CATCH_OVERRIDE {
+
+ // Close any still open children (e.g. generators)
+ while( &m_ctx.currentTracker() != this )
+ m_ctx.currentTracker().close();
+
+ switch( m_runState ) {
+ case NotStarted:
+ case CompletedSuccessfully:
+ case Failed:
+ throw std::logic_error( "Illogical state" );
+
+ case NeedsAnotherRun:
+ break;;
+
+ case Executing:
+ m_runState = CompletedSuccessfully;
+ break;
+ case ExecutingChildren:
+ if( m_children.empty() || m_children.back()->isComplete() )
+ m_runState = CompletedSuccessfully;
+ break;
+
+ default:
+ throw std::logic_error( "Unexpected state" );
+ }
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ virtual void fail() CATCH_OVERRIDE {
+ m_runState = Failed;
+ if( m_parent )
+ m_parent->markAsNeedingAnotherRun();
+ moveToParent();
+ m_ctx.completeCycle();
+ }
+ virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+ m_runState = NeedsAnotherRun;
+ }
+ private:
+ void moveToParent() {
+ assert( m_parent );
+ m_ctx.setCurrentTracker( m_parent );
+ }
+ void moveToThis() {
+ m_ctx.setCurrentTracker( this );
+ }
+ };
+
+ class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
+ public:
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( nameAndLocation, ctx, parent )
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+ parent = &parent->parent();
+
+ SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+ addNextFilters( parentSection.m_filters );
+ }
+ }
+ virtual ~SectionTracker();
+
+ virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+ SectionTracker* section = CATCH_NULL;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isSectionTracker() );
+ section = static_cast<SectionTracker*>( childTracker );
+ }
+ else {
+ section = new SectionTracker( nameAndLocation, ctx, ¤tTracker );
+ currentTracker.addChild( section );
+ }
+ if( !ctx.completedCycle() )
+ section->tryOpen();
+ return *section;
+ }
+
+ void tryOpen() {
+ if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
+ open();
+ }
+
+ void addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+ m_filters.push_back(""); // Root - should never be consulted
+ m_filters.push_back(""); // Test Case - not a section filter
+ m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+ }
+ }
+ void addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+ m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+ }
+ };
+
+ class IndexTracker : public TrackerBase {
+ int m_size;
+ int m_index;
+ public:
+ IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+ : TrackerBase( nameAndLocation, ctx, parent ),
+ m_size( size ),
+ m_index( -1 )
+ {}
+ virtual ~IndexTracker();
+
+ virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
+ static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+ IndexTracker* tracker = CATCH_NULL;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isIndexTracker() );
+ tracker = static_cast<IndexTracker*>( childTracker );
+ }
+ else {
+ tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size );
+ currentTracker.addChild( tracker );
+ }
+
+ if( !ctx.completedCycle() && !tracker->isComplete() ) {
+ if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+ tracker->moveNext();
+ tracker->open();
+ }
+
+ return *tracker;
+ }
+
+ int index() const { return m_index; }
+
+ void moveNext() {
+ m_index++;
+ m_children.clear();
+ }
+
+ virtual void close() CATCH_OVERRIDE {
+ TrackerBase::close();
+ if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+ m_runState = Executing;
+ }
+ };
+
+ inline ITracker& TrackerContext::startRun() {
+ m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
+ m_currentTracker = CATCH_NULL;
+ m_runState = Executing;
+ return *m_rootTracker;
+ }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
+
+} // namespace Catch
+
+CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+ // Report the error condition
+ inline void reportFatal( std::string const& message ) {
+ IContext& context = Catch::getCurrentContext();
+ IResultCapture* resultCapture = context.getResultCapture();
+ resultCapture->handleFatalErrorCondition( message );
+ }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+// #included from: catch_windows_h_proxy.h
+
+#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# define NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+
+# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_WINDOWS_SEH is defined
+
+namespace Catch {
+
+ struct SignalDefs { DWORD id; const char* name; };
+ extern SignalDefs signalDefs[];
+ // There is no 1-1 mapping between signals and windows exceptions.
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ SignalDefs signalDefs[] = {
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
+ { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+ { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
+ };
+
+ struct FatalConditionHandler {
+
+ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+ reportFatal(signalDefs[i].name);
+ }
+ }
+ // If its not an exception we care about, pass it along.
+ // This stops us from eating debugger breaks etc.
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ FatalConditionHandler() {
+ isSet = true;
+ // 32k seems enough for Catch to handle stack overflow,
+ // but the value was found experimentally, so there is no strong guarantee
+ guaranteeSize = 32 * 1024;
+ exceptionHandlerHandle = CATCH_NULL;
+ // Register as first handler in current chain
+ exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+ // Pass in guarantee size to be filled
+ SetThreadStackGuarantee(&guaranteeSize);
+ }
+
+ static void reset() {
+ if (isSet) {
+ // Unregister handler and restore the old guarantee
+ RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+ SetThreadStackGuarantee(&guaranteeSize);
+ exceptionHandlerHandle = CATCH_NULL;
+ isSet = false;
+ }
+ }
+
+ ~FatalConditionHandler() {
+ reset();
+ }
+ private:
+ static bool isSet;
+ static ULONG guaranteeSize;
+ static PVOID exceptionHandlerHandle;
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ ULONG FatalConditionHandler::guaranteeSize = 0;
+ PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
+
+} // namespace Catch
+
+# endif // CATCH_CONFIG_WINDOWS_SEH
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
+#include <signal.h>
+
+namespace Catch {
+
+ struct SignalDefs {
+ int id;
+ const char* name;
+ };
+ extern SignalDefs signalDefs[];
+ SignalDefs signalDefs[] = {
+ { SIGINT, "SIGINT - Terminal interrupt signal" },
+ { SIGILL, "SIGILL - Illegal instruction signal" },
+ { SIGFPE, "SIGFPE - Floating point error signal" },
+ { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+ { SIGTERM, "SIGTERM - Termination request signal" },
+ { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+ };
+
+ struct FatalConditionHandler {
+
+ static bool isSet;
+ static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
+ static stack_t oldSigStack;
+ static char altStackMem[SIGSTKSZ];
+
+ static void handleSignal( int sig ) {
+ std::string name = "<unknown signal>";
+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ SignalDefs &def = signalDefs[i];
+ if (sig == def.id) {
+ name = def.name;
+ break;
+ }
+ }
+ reset();
+ reportFatal(name);
+ raise( sig );
+ }
+
+ FatalConditionHandler() {
+ isSet = true;
+ stack_t sigStack;
+ sigStack.ss_sp = altStackMem;
+ sigStack.ss_size = SIGSTKSZ;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = { 0 };
+
+ sa.sa_handler = handleSignal;
+ sa.sa_flags = SA_ONSTACK;
+ for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+ }
+ }
+
+ ~FatalConditionHandler() {
+ reset();
+ }
+ static void reset() {
+ if( isSet ) {
+ // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, CATCH_NULL);
+ isSet = false;
+ }
+ }
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+ stack_t FatalConditionHandler::oldSigStack = {};
+ char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
+} // namespace Catch
+
+# endif // CATCH_CONFIG_POSIX_SIGNALS
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+ class StreamRedirect {
+
+ public:
+ StreamRedirect( std::ostream& stream, std::string& targetString )
+ : m_stream( stream ),
+ m_prevBuf( stream.rdbuf() ),
+ m_targetString( targetString )
+ {
+ stream.rdbuf( m_oss.rdbuf() );
+ }
+
+ ~StreamRedirect() {
+ m_targetString += m_oss.str();
+ m_stream.rdbuf( m_prevBuf );
+ }
+
+ private:
+ std::ostream& m_stream;
+ std::streambuf* m_prevBuf;
+ std::ostringstream m_oss;
+ std::string& m_targetString;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class RunContext : public IResultCapture, public IRunner {
+
+ RunContext( RunContext const& );
+ void operator =( RunContext const& );
+
+ public:
+
+ explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+ : m_runInfo( _config->name() ),
+ m_context( getCurrentMutableContext() ),
+ m_activeTestCase( CATCH_NULL ),
+ m_config( _config ),
+ m_reporter( reporter ),
+ m_shouldReportUnexpected ( true )
+ {
+ m_context.setRunner( this );
+ m_context.setConfig( m_config );
+ m_context.setResultCapture( this );
+ m_reporter->testRunStarting( m_runInfo );
+ }
+
+ virtual ~RunContext() {
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+ }
+
+ void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+ }
+ void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+ m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+ }
+
+ Totals runTest( TestCase const& testCase ) {
+ Totals prevTotals = m_totals;
+
+ std::string redirectedCout;
+ std::string redirectedCerr;
+
+ TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+ m_reporter->testCaseStarting( testInfo );
+
+ m_activeTestCase = &testCase;
+
+ do {
+ ITracker& rootTracker = m_trackerContext.startRun();
+ assert( rootTracker.isSectionTracker() );
+ static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
+ do {
+ m_trackerContext.startCycle();
+ m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
+ runCurrentTest( redirectedCout, redirectedCerr );
+ }
+ while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
+ }
+ // !TBD: deprecated - this will be replaced by indexed trackers
+ while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+ Totals deltaTotals = m_totals.delta( prevTotals );
+ if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+ deltaTotals.assertions.failed++;
+ deltaTotals.testCases.passed--;
+ deltaTotals.testCases.failed++;
+ }
+ m_totals.testCases += deltaTotals.testCases;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ redirectedCout,
+ redirectedCerr,
+ aborting() ) );
+
+ m_activeTestCase = CATCH_NULL;
+ m_testCaseTracker = CATCH_NULL;
+
+ return deltaTotals;
+ }
+
+ Ptr<IConfig const> config() const {
+ return m_config;
+ }
+
+ private: // IResultCapture
+
+ virtual void assertionEnded( AssertionResult const& result ) {
+ if( result.getResultType() == ResultWas::Ok ) {
+ m_totals.assertions.passed++;
+ }
+ else if( !result.isOk() ) {
+ m_totals.assertions.failed++;
+ }
+
+ // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+ // and should be let to clear themselves out.
+ static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+ // Reset working state
+ m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+ m_lastResult = result;
+ }
+
+ virtual bool sectionStarted (
+ SectionInfo const& sectionInfo,
+ Counts& assertions
+ )
+ {
+ ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
+ if( !sectionTracker.isOpen() )
+ return false;
+ m_activeSections.push_back( §ionTracker );
+
+ m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+ m_reporter->sectionStarting( sectionInfo );
+
+ assertions = m_totals.assertions;
+
+ return true;
+ }
+ bool testForMissingAssertions( Counts& assertions ) {
+ if( assertions.total() != 0 )
+ return false;
+ if( !m_config->warnAboutMissingAssertions() )
+ return false;
+ if( m_trackerContext.currentTracker().hasChildren() )
+ return false;
+ m_totals.assertions.failed++;
+ assertions.failed++;
+ return true;
+ }
+
+ virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+ Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( !m_activeSections.empty() ) {
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+ }
+
+ m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
+ m_messages.clear();
+ }
+
+ virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+ if( m_unfinishedSections.empty() )
+ m_activeSections.back()->fail();
+ else
+ m_activeSections.back()->close();
+ m_activeSections.pop_back();
+
+ m_unfinishedSections.push_back( endInfo );
+ }
+
+ virtual void pushScopedMessage( MessageInfo const& message ) {
+ m_messages.push_back( message );
+ }
+
+ virtual void popScopedMessage( MessageInfo const& message ) {
+ m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+ }
+
+ virtual std::string getCurrentTestName() const {
+ return m_activeTestCase
+ ? m_activeTestCase->getTestCaseInfo().name
+ : std::string();
+ }
+
+ virtual const AssertionResult* getLastResult() const {
+ return &m_lastResult;
+ }
+
+ virtual void exceptionEarlyReported() {
+ m_shouldReportUnexpected = false;
+ }
+
+ virtual void handleFatalErrorCondition( std::string const& message ) {
+ // Don't rebuild the result -- the stringification itself can cause more fatal errors
+ // Instead, fake a result data.
+ AssertionResultData tempResult;
+ tempResult.resultType = ResultWas::FatalErrorCondition;
+ tempResult.message = message;
+ AssertionResult result(m_lastAssertionInfo, tempResult);
+
+ getResultCapture().assertionEnded(result);
+
+ handleUnfinishedSections();
+
+ // Recreate section for test case (as we will lose the one that was in scope)
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+
+ Counts assertions;
+ assertions.failed = 1;
+ SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+ m_reporter->sectionEnded( testCaseSectionStats );
+
+ TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+ Totals deltaTotals;
+ deltaTotals.testCases.failed = 1;
+ m_reporter->testCaseEnded( TestCaseStats( testInfo,
+ deltaTotals,
+ std::string(),
+ std::string(),
+ false ) );
+ m_totals.testCases.failed++;
+ testGroupEnded( std::string(), m_totals, 1, 1 );
+ m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+ }
+
+ public:
+ // !TBD We need to do this another way!
+ bool aborting() const {
+ return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+ }
+
+ private:
+
+ void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+ TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+ SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+ m_reporter->sectionStarting( testCaseSection );
+ Counts prevAssertions = m_totals.assertions;
+ double duration = 0;
+ m_shouldReportUnexpected = true;
+ try {
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+
+ seedRng( *m_config );
+
+ Timer timer;
+ timer.start();
+ if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+ StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+ StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
+ invokeActiveTestCase();
+ }
+ else {
+ invokeActiveTestCase();
+ }
+ duration = timer.getElapsedSeconds();
+ }
+ catch( TestFailureException& ) {
+ // This just means the test was aborted due to failure
+ }
+ catch(...) {
+ // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+ // are reported without translation at the point of origin.
+ if (m_shouldReportUnexpected) {
+ makeUnexpectedResultBuilder().useActiveException();
+ }
+ }
+ m_testCaseTracker->close();
+ handleUnfinishedSections();
+ m_messages.clear();
+
+ Counts assertions = m_totals.assertions - prevAssertions;
+ bool missingAssertions = testForMissingAssertions( assertions );
+
+ if( testCaseInfo.okToFail() ) {
+ std::swap( assertions.failedButOk, assertions.failed );
+ m_totals.assertions.failed -= assertions.failedButOk;
+ m_totals.assertions.failedButOk += assertions.failedButOk;
+ }
+
+ SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+ m_reporter->sectionEnded( testCaseSectionStats );
+ }
+
+ void invokeActiveTestCase() {
+ FatalConditionHandler fatalConditionHandler; // Handle signals
+ m_activeTestCase->invoke();
+ fatalConditionHandler.reset();
+ }
+
+ private:
+
+ ResultBuilder makeUnexpectedResultBuilder() const {
+ return ResultBuilder( m_lastAssertionInfo.macroName,
+ m_lastAssertionInfo.lineInfo,
+ m_lastAssertionInfo.capturedExpression,
+ m_lastAssertionInfo.resultDisposition );
+ }
+
+ void handleUnfinishedSections() {
+ // If sections ended prematurely due to an exception we stored their
+ // infos here so we can tear them down outside the unwind process.
+ for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+ itEnd = m_unfinishedSections.rend();
+ it != itEnd;
+ ++it )
+ sectionEnded( *it );
+ m_unfinishedSections.clear();
+ }
+
+ TestRunInfo m_runInfo;
+ IMutableContext& m_context;
+ TestCase const* m_activeTestCase;
+ ITracker* m_testCaseTracker;
+ ITracker* m_currentSectionTracker;
+ AssertionResult m_lastResult;
+
+ Ptr<IConfig const> m_config;
+ Totals m_totals;
+ Ptr<IStreamingReporter> m_reporter;
+ std::vector<MessageInfo> m_messages;
+ AssertionInfo m_lastAssertionInfo;
+ std::vector<SectionEndInfo> m_unfinishedSections;
+ std::vector<ITracker*> m_activeSections;
+ TrackerContext m_trackerContext;
+ bool m_shouldReportUnexpected;
+ };
+
+ IResultCapture& getResultCapture() {
+ if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+ return *capture;
+ else
+ throw std::logic_error( "No result capture instance" );
+ }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+ // Versioning information
+ struct Version {
+ Version( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber );
+
+ unsigned int const majorVersion;
+ unsigned int const minorVersion;
+ unsigned int const patchNumber;
+
+ // buildNumber is only used if branchName is not null
+ char const * const branchName;
+ unsigned int const buildNumber;
+
+ friend std::ostream& operator << ( std::ostream& os, Version const& version );
+
+ private:
+ void operator=( Version const& );
+ };
+
+ inline Version libraryVersion();
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+ Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+ Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+ if( !reporter ) {
+ std::ostringstream oss;
+ oss << "No reporter registered with name: '" << reporterName << "'";
+ throw std::domain_error( oss.str() );
+ }
+ return reporter;
+ }
+
+ Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+ std::vector<std::string> reporters = config->getReporterNames();
+ if( reporters.empty() )
+ reporters.push_back( "console" );
+
+ Ptr<IStreamingReporter> reporter;
+ for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+ it != itEnd;
+ ++it )
+ reporter = addReporter( reporter, createReporter( *it, config ) );
+ return reporter;
+ }
+ Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+ IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+ for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+ it != itEnd;
+ ++it )
+ reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+ return reporters;
+ }
+
+ Totals runTests( Ptr<Config> const& config ) {
+
+ Ptr<IConfig const> iconfig = config.get();
+
+ Ptr<IStreamingReporter> reporter = makeReporter( config );
+ reporter = addListeners( iconfig, reporter );
+
+ RunContext context( iconfig, reporter );
+
+ Totals totals;
+
+ context.testGroupStarting( config->name(), 1, 1 );
+
+ TestSpec testSpec = config->testSpec();
+ if( !testSpec.hasFilters() )
+ testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+ std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+ for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+ it != itEnd;
+ ++it ) {
+ if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+ totals += context.runTest( *it );
+ else
+ reporter->skipTest( *it );
+ }
+
+ context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+ return totals;
+ }
+
+ void applyFilenamesAsTags( IConfig const& config ) {
+ std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+ for(std::size_t i = 0; i < tests.size(); ++i ) {
+ TestCase& test = const_cast<TestCase&>( tests[i] );
+ std::set<std::string> tags = test.tags;
+
+ std::string filename = test.lineInfo.file;
+ std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+ if( lastSlash != std::string::npos )
+ filename = filename.substr( lastSlash+1 );
+
+ std::string::size_type lastDot = filename.find_last_of( "." );
+ if( lastDot != std::string::npos )
+ filename = filename.substr( 0, lastDot );
+
+ tags.insert( "#" + filename );
+ setTags( test, tags );
+ }
+ }
+
+ class Session : NonCopyable {
+ static bool alreadyInstantiated;
+
+ public:
+
+ struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+ Session()
+ : m_cli( makeCommandLineParser() ) {
+ if( alreadyInstantiated ) {
+ std::string msg = "Only one instance of Catch::Session can ever be used";
+ Catch::cerr() << msg << std::endl;
+ throw std::logic_error( msg );
+ }
+ alreadyInstantiated = true;
+ }
+ ~Session() {
+ Catch::cleanUp();
+ }
+
+ void showHelp( std::string const& processName ) {
+ Catch::cout() << "\nCatch v" << libraryVersion() << "\n";
+
+ m_cli.usage( Catch::cout(), processName );
+ Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+ }
+
+ int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ try {
+ m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+ m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
+ if( m_configData.showHelp )
+ showHelp( m_configData.processName );
+ m_config.reset();
+ }
+ catch( std::exception& ex ) {
+ {
+ Colour colourGuard( Colour::Red );
+ Catch::cerr()
+ << "\nError(s) in input:\n"
+ << Text( ex.what(), TextAttributes().setIndent(2) )
+ << "\n\n";
+ }
+ m_cli.usage( Catch::cout(), m_configData.processName );
+ return (std::numeric_limits<int>::max)();
+ }
+ return 0;
+ }
+
+ void useConfigData( ConfigData const& _configData ) {
+ m_configData = _configData;
+ m_config.reset();
+ }
+
+ int run( int argc, char const* const* const argv ) {
+
+ int returnCode = applyCommandLine( argc, argv );
+ if( returnCode == 0 )
+ returnCode = run();
+ return returnCode;
+ }
+
+ #if defined(WIN32) && defined(UNICODE)
+ int run( int argc, wchar_t const* const* const argv ) {
+
+ char **utf8Argv = new char *[ argc ];
+
+ for ( int i = 0; i < argc; ++i ) {
+ int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
+
+ utf8Argv[ i ] = new char[ bufSize ];
+
+ WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
+ }
+
+ int returnCode = applyCommandLine( argc, utf8Argv );
+ if( returnCode == 0 )
+ returnCode = run();
+
+ for ( int i = 0; i < argc; ++i )
+ delete [] utf8Argv[ i ];
+
+ delete [] utf8Argv;
+
+ return returnCode;
+ }
+ #endif
+
+ int run() {
+ if( m_configData.showHelp )
+ return 0;
+
+ try
+ {
+ config(); // Force config to be constructed
+
+ seedRng( *m_config );
+
+ if( m_configData.filenamesAsTags )
+ applyFilenamesAsTags( *m_config );
+
+ // Handle list request
+ if( Option<std::size_t> listed = list( config() ) )
+ return static_cast<int>( *listed );
+
+ return static_cast<int>( runTests( m_config ).assertions.failed );
+ }
+ catch( std::exception& ex ) {
+ Catch::cerr() << ex.what() << std::endl;
+ return (std::numeric_limits<int>::max)();
+ }
+ }
+
+ Clara::CommandLine<ConfigData> const& cli() const {
+ return m_cli;
+ }
+ std::vector<Clara::Parser::Token> const& unusedTokens() const {
+ return m_unusedTokens;
+ }
+ ConfigData& configData() {
+ return m_configData;
+ }
+ Config& config() {
+ if( !m_config )
+ m_config = new Config( m_configData );
+ return *m_config;
+ }
+ private:
+ Clara::CommandLine<ConfigData> m_cli;
+ std::vector<Clara::Parser::Token> m_unusedTokens;
+ ConfigData m_configData;
+ Ptr<Config> m_config;
+ };
+
+ bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+ struct RandomNumberGenerator {
+ typedef std::ptrdiff_t result_type;
+
+ result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return 1000000; }
+ result_type operator()() const { return std::rand() % max(); }
+#endif
+ template<typename V>
+ static void shuffle( V& vector ) {
+ RandomNumberGenerator rng;
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+ std::shuffle( vector.begin(), vector.end(), rng );
+#else
+ std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+ }
+ };
+
+ inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+ std::vector<TestCase> sorted = unsortedTestCases;
+
+ switch( config.runOrder() ) {
+ case RunTests::InLexicographicalOrder:
+ std::sort( sorted.begin(), sorted.end() );
+ break;
+ case RunTests::InRandomOrder:
+ {
+ seedRng( config );
+ RandomNumberGenerator::shuffle( sorted );
+ }
+ break;
+ case RunTests::InDeclarationOrder:
+ // already in declaration order
+ break;
+ }
+ return sorted;
+ }
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+ return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+ }
+
+ void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+ std::set<TestCase> seenFunctions;
+ for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+ it != itEnd;
+ ++it ) {
+ std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+ if( !prev.second ) {
+ std::ostringstream ss;
+
+ ss << Colour( Colour::Red )
+ << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+ << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
+ << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+
+ throw std::runtime_error(ss.str());
+ }
+ }
+ }
+
+ std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+ std::vector<TestCase> filtered;
+ filtered.reserve( testCases.size() );
+ for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+ it != itEnd;
+ ++it )
+ if( matchTest( *it, testSpec, config ) )
+ filtered.push_back( *it );
+ return filtered;
+ }
+ std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+ return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+ }
+
+ class TestRegistry : public ITestCaseRegistry {
+ public:
+ TestRegistry()
+ : m_currentSortOrder( RunTests::InDeclarationOrder ),
+ m_unnamedCount( 0 )
+ {}
+ virtual ~TestRegistry();
+
+ virtual void registerTest( TestCase const& testCase ) {
+ std::string name = testCase.getTestCaseInfo().name;
+ if( name.empty() ) {
+ std::ostringstream oss;
+ oss << "Anonymous test case " << ++m_unnamedCount;
+ return registerTest( testCase.withName( oss.str() ) );
+ }
+ m_functions.push_back( testCase );
+ }
+
+ virtual std::vector<TestCase> const& getAllTests() const {
+ return m_functions;
+ }
+ virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+ if( m_sortedFunctions.empty() )
+ enforceNoDuplicateTestCases( m_functions );
+
+ if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+ m_sortedFunctions = sortTests( config, m_functions );
+ m_currentSortOrder = config.runOrder();
+ }
+ return m_sortedFunctions;
+ }
+
+ private:
+ std::vector<TestCase> m_functions;
+ mutable RunTests::InWhatOrder m_currentSortOrder;
+ mutable std::vector<TestCase> m_sortedFunctions;
+ size_t m_unnamedCount;
+ std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+ public:
+
+ FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+ virtual void invoke() const {
+ m_fun();
+ }
+
+ private:
+ virtual ~FreeFunctionTestCase();
+
+ TestFunction m_fun;
+ };
+
+ inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+ std::string className = classOrQualifiedMethodName;
+ if( startsWith( className, '&' ) )
+ {
+ std::size_t lastColons = className.rfind( "::" );
+ std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+ if( penultimateColons == std::string::npos )
+ penultimateColons = 1;
+ className = className.substr( penultimateColons, lastColons-penultimateColons );
+ }
+ return className;
+ }
+
+ void registerTestCase
+ ( ITestCase* testCase,
+ char const* classOrQualifiedMethodName,
+ NameAndDesc const& nameAndDesc,
+ SourceLineInfo const& lineInfo ) {
+
+ getMutableRegistryHub().registerTest
+ ( makeTestCase
+ ( testCase,
+ extractClassName( classOrQualifiedMethodName ),
+ nameAndDesc.name,
+ nameAndDesc.description,
+ lineInfo ) );
+ }
+ void registerTestCaseFunction
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ AutoReg::AutoReg
+ ( TestFunction function,
+ SourceLineInfo const& lineInfo,
+ NameAndDesc const& nameAndDesc ) {
+ registerTestCaseFunction( function, lineInfo, nameAndDesc );
+ }
+
+ AutoReg::~AutoReg() {}
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class ReporterRegistry : public IReporterRegistry {
+
+ public:
+
+ virtual ~ReporterRegistry() CATCH_OVERRIDE {}
+
+ virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
+ FactoryMap::const_iterator it = m_factories.find( name );
+ if( it == m_factories.end() )
+ return CATCH_NULL;
+ return it->second->create( ReporterConfig( config ) );
+ }
+
+ void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
+ m_factories.insert( std::make_pair( name, factory ) );
+ }
+ void registerListener( Ptr<IReporterFactory> const& factory ) {
+ m_listeners.push_back( factory );
+ }
+
+ virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
+ return m_factories;
+ }
+ virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+ return m_listeners;
+ }
+
+ private:
+ FactoryMap m_factories;
+ Listeners m_listeners;
+ };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+ public:
+ ~ExceptionTranslatorRegistry() {
+ deleteAll( m_translators );
+ }
+
+ virtual void registerTranslator( const IExceptionTranslator* translator ) {
+ m_translators.push_back( translator );
+ }
+
+ virtual std::string translateActiveException() const {
+ try {
+#ifdef __OBJC__
+ // In Objective-C try objective-c exceptions first
+ @try {
+ return tryTranslators();
+ }
+ @catch (NSException *exception) {
+ return Catch::toString( [exception description] );
+ }
+#else
+ return tryTranslators();
+#endif
+ }
+ catch( TestFailureException& ) {
+ throw;
+ }
+ catch( std::exception& ex ) {
+ return ex.what();
+ }
+ catch( std::string& msg ) {
+ return msg;
+ }
+ catch( const char* msg ) {
+ return msg;
+ }
+ catch(...) {
+ return "Unknown exception";
+ }
+ }
+
+ std::string tryTranslators() const {
+ if( m_translators.empty() )
+ throw;
+ else
+ return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
+ }
+
+ private:
+ std::vector<const IExceptionTranslator*> m_translators;
+ };
+}
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+ class TagAliasRegistry : public ITagAliasRegistry {
+ public:
+ virtual ~TagAliasRegistry();
+ virtual Option<TagAlias> find( std::string const& alias ) const;
+ virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+ void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+ private:
+ std::map<std::string, TagAlias> m_registry;
+ };
+
+} // end namespace Catch
+
+namespace Catch {
+
+ namespace {
+
+ class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+ RegistryHub( RegistryHub const& );
+ void operator=( RegistryHub const& );
+
+ public: // IRegistryHub
+ RegistryHub() {
+ }
+ virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
+ return m_reporterRegistry;
+ }
+ virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
+ return m_testCaseRegistry;
+ }
+ virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
+ return m_exceptionTranslatorRegistry;
+ }
+ virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE {
+ return m_tagAliasRegistry;
+ }
+
+ public: // IMutableRegistryHub
+ virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+ m_reporterRegistry.registerReporter( name, factory );
+ }
+ virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+ m_reporterRegistry.registerListener( factory );
+ }
+ virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
+ m_testCaseRegistry.registerTest( testInfo );
+ }
+ virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
+ m_exceptionTranslatorRegistry.registerTranslator( translator );
+ }
+ virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE {
+ m_tagAliasRegistry.add( alias, tag, lineInfo );
+ }
+
+ private:
+ TestRegistry m_testCaseRegistry;
+ ReporterRegistry m_reporterRegistry;
+ ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+ TagAliasRegistry m_tagAliasRegistry;
+ };
+
+ // Single, global, instance
+ inline RegistryHub*& getTheRegistryHub() {
+ static RegistryHub* theRegistryHub = CATCH_NULL;
+ if( !theRegistryHub )
+ theRegistryHub = new RegistryHub();
+ return theRegistryHub;
+ }
+ }
+
+ IRegistryHub& getRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ IMutableRegistryHub& getMutableRegistryHub() {
+ return *getTheRegistryHub();
+ }
+ void cleanUp() {
+ delete getTheRegistryHub();
+ getTheRegistryHub() = CATCH_NULL;
+ cleanUpContext();
+ }
+ std::string translateActiveException() {
+ return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <sstream>
+
+namespace Catch {
+
+ NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+ : m_lineInfo( lineInfo ) {
+ std::ostringstream oss;
+ oss << lineInfo << ": function ";
+ oss << "not implemented";
+ m_what = oss.str();
+ }
+
+ const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+ return m_what.c_str();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+namespace Catch {
+
+ template<typename WriterF, size_t bufferSize=256>
+ class StreamBufImpl : public StreamBufBase {
+ char data[bufferSize];
+ WriterF m_writer;
+
+ public:
+ StreamBufImpl() {
+ setp( data, data + sizeof(data) );
+ }
+
+ ~StreamBufImpl() CATCH_NOEXCEPT {
+ sync();
+ }
+
+ private:
+ int overflow( int c ) {
+ sync();
+
+ if( c != EOF ) {
+ if( pbase() == epptr() )
+ m_writer( std::string( 1, static_cast<char>( c ) ) );
+ else
+ sputc( static_cast<char>( c ) );
+ }
+ return 0;
+ }
+
+ int sync() {
+ if( pbase() != pptr() ) {
+ m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+ setp( pbase(), epptr() );
+ }
+ return 0;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ FileStream::FileStream( std::string const& filename ) {
+ m_ofs.open( filename.c_str() );
+ if( m_ofs.fail() ) {
+ std::ostringstream oss;
+ oss << "Unable to open file: '" << filename << '\'';
+ throw std::domain_error( oss.str() );
+ }
+ }
+
+ std::ostream& FileStream::stream() const {
+ return m_ofs;
+ }
+
+ struct OutputDebugWriter {
+
+ void operator()( std::string const&str ) {
+ writeToDebugConsole( str );
+ }
+ };
+
+ DebugOutStream::DebugOutStream()
+ : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+ m_os( m_streamBuf.get() )
+ {}
+
+ std::ostream& DebugOutStream::stream() const {
+ return m_os;
+ }
+
+ // Store the streambuf from cout up-front because
+ // cout may get redirected when running tests
+ CoutStream::CoutStream()
+ : m_os( Catch::cout().rdbuf() )
+ {}
+
+ std::ostream& CoutStream::stream() const {
+ return m_os;
+ }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+ std::ostream& cout() {
+ return std::cout;
+ }
+ std::ostream& cerr() {
+ return std::cerr;
+ }
+#endif
+}
+
+namespace Catch {
+
+ class Context : public IMutableContext {
+
+ Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
+ Context( Context const& );
+ void operator=( Context const& );
+
+ public:
+ virtual ~Context() {
+ deleteAllValues( m_generatorsByTestName );
+ }
+
+ public: // IContext
+ virtual IResultCapture* getResultCapture() {
+ return m_resultCapture;
+ }
+ virtual IRunner* getRunner() {
+ return m_runner;
+ }
+ virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+ return getGeneratorsForCurrentTest()
+ .getGeneratorInfo( fileInfo, totalSize )
+ .getCurrentIndex();
+ }
+ virtual bool advanceGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ return generators && generators->moveNext();
+ }
+
+ virtual Ptr<IConfig const> getConfig() const {
+ return m_config;
+ }
+
+ public: // IMutableContext
+ virtual void setResultCapture( IResultCapture* resultCapture ) {
+ m_resultCapture = resultCapture;
+ }
+ virtual void setRunner( IRunner* runner ) {
+ m_runner = runner;
+ }
+ virtual void setConfig( Ptr<IConfig const> const& config ) {
+ m_config = config;
+ }
+
+ friend IMutableContext& getCurrentMutableContext();
+
+ private:
+ IGeneratorsForTest* findGeneratorsForCurrentTest() {
+ std::string testName = getResultCapture()->getCurrentTestName();
+
+ std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+ m_generatorsByTestName.find( testName );
+ return it != m_generatorsByTestName.end()
+ ? it->second
+ : CATCH_NULL;
+ }
+
+ IGeneratorsForTest& getGeneratorsForCurrentTest() {
+ IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+ if( !generators ) {
+ std::string testName = getResultCapture()->getCurrentTestName();
+ generators = createGeneratorsForTest();
+ m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+ }
+ return *generators;
+ }
+
+ private:
+ Ptr<IConfig const> m_config;
+ IRunner* m_runner;
+ IResultCapture* m_resultCapture;
+ std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+ };
+
+ namespace {
+ Context* currentContext = CATCH_NULL;
+ }
+ IMutableContext& getCurrentMutableContext() {
+ if( !currentContext )
+ currentContext = new Context();
+ return *currentContext;
+ }
+ IContext& getCurrentContext() {
+ return getCurrentMutableContext();
+ }
+
+ void cleanUpContext() {
+ delete currentContext;
+ currentContext = CATCH_NULL;
+ }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+// #included from: catch_errno_guard.hpp
+#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED
+
+#include <cerrno>
+
+namespace Catch {
+
+ class ErrnoGuard {
+ public:
+ ErrnoGuard():m_oldErrno(errno){}
+ ~ErrnoGuard() { errno = m_oldErrno; }
+ private:
+ int m_oldErrno;
+ };
+
+}
+
+namespace Catch {
+ namespace {
+
+ struct IColourImpl {
+ virtual ~IColourImpl() {}
+ virtual void use( Colour::Code _colourCode ) = 0;
+ };
+
+ struct NoColourImpl : IColourImpl {
+ void use( Colour::Code ) {}
+
+ static IColourImpl* instance() {
+ static NoColourImpl s_instance;
+ return &s_instance;
+ }
+ };
+
+ } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+# ifdef CATCH_PLATFORM_WINDOWS
+# define CATCH_CONFIG_COLOUR_WINDOWS
+# else
+# define CATCH_CONFIG_COLOUR_ANSI
+# endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+ class Win32ColourImpl : public IColourImpl {
+ public:
+ Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+ GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+ originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+ originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+ }
+
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None: return setTextAttribute( originalForegroundAttributes );
+ case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+ case Colour::Red: return setTextAttribute( FOREGROUND_RED );
+ case Colour::Green: return setTextAttribute( FOREGROUND_GREEN );
+ case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE );
+ case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+ case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+ case Colour::Grey: return setTextAttribute( 0 );
+
+ case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY );
+ case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+ case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+ case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+
+ private:
+ void setTextAttribute( WORD _textAttribute ) {
+ SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+ }
+ HANDLE stdoutHandle;
+ WORD originalForegroundAttributes;
+ WORD originalBackgroundAttributes;
+ };
+
+ IColourImpl* platformColourInstance() {
+ static Win32ColourImpl s_instance;
+
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = !isDebuggerActive()
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? &s_instance
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+ // use POSIX/ ANSI console terminal codes
+ // Thanks to Adam Strzelecki for original contribution
+ // (http://github.com/nanoant)
+ // https://github.com/philsquared/Catch/pull/131
+ class PosixColourImpl : public IColourImpl {
+ public:
+ virtual void use( Colour::Code _colourCode ) {
+ switch( _colourCode ) {
+ case Colour::None:
+ case Colour::White: return setColour( "[0m" );
+ case Colour::Red: return setColour( "[0;31m" );
+ case Colour::Green: return setColour( "[0;32m" );
+ case Colour::Blue: return setColour( "[0;34m" );
+ case Colour::Cyan: return setColour( "[0;36m" );
+ case Colour::Yellow: return setColour( "[0;33m" );
+ case Colour::Grey: return setColour( "[1;30m" );
+
+ case Colour::LightGrey: return setColour( "[0;37m" );
+ case Colour::BrightRed: return setColour( "[1;31m" );
+ case Colour::BrightGreen: return setColour( "[1;32m" );
+ case Colour::BrightWhite: return setColour( "[1;37m" );
+
+ case Colour::Bright: throw std::logic_error( "not a colour" );
+ }
+ }
+ static IColourImpl* instance() {
+ static PosixColourImpl s_instance;
+ return &s_instance;
+ }
+
+ private:
+ void setColour( const char* _escapeCode ) {
+ Catch::cout() << '\033' << _escapeCode;
+ }
+ };
+
+ IColourImpl* platformColourInstance() {
+ ErrnoGuard guard;
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? PosixColourImpl::instance()
+ : NoColourImpl::instance();
+ }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+ static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+ Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+ Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+ Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+ void Colour::use( Code _colourCode ) {
+ static IColourImpl* impl = platformColourInstance();
+ impl->use( _colourCode );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+ struct GeneratorInfo : IGeneratorInfo {
+
+ GeneratorInfo( std::size_t size )
+ : m_size( size ),
+ m_currentIndex( 0 )
+ {}
+
+ bool moveNext() {
+ if( ++m_currentIndex == m_size ) {
+ m_currentIndex = 0;
+ return false;
+ }
+ return true;
+ }
+
+ std::size_t getCurrentIndex() const {
+ return m_currentIndex;
+ }
+
+ std::size_t m_size;
+ std::size_t m_currentIndex;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class GeneratorsForTest : public IGeneratorsForTest {
+
+ public:
+ ~GeneratorsForTest() {
+ deleteAll( m_generatorsInOrder );
+ }
+
+ IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+ std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+ if( it == m_generatorsByName.end() ) {
+ IGeneratorInfo* info = new GeneratorInfo( size );
+ m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+ m_generatorsInOrder.push_back( info );
+ return *info;
+ }
+ return *it->second;
+ }
+
+ bool moveNext() {
+ std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+ std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+ for(; it != itEnd; ++it ) {
+ if( (*it)->moveNext() )
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+ std::vector<IGeneratorInfo*> m_generatorsInOrder;
+ };
+
+ IGeneratorsForTest* createGeneratorsForTest()
+ {
+ return new GeneratorsForTest();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+ AssertionInfo::AssertionInfo( char const * _macroName,
+ SourceLineInfo const& _lineInfo,
+ char const * _capturedExpression,
+ ResultDisposition::Flags _resultDisposition,
+ char const * _secondArg)
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ capturedExpression( _capturedExpression ),
+ resultDisposition( _resultDisposition ),
+ secondArg( _secondArg )
+ {}
+
+ AssertionResult::AssertionResult() {}
+
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ : m_info( info ),
+ m_resultData( data )
+ {}
+
+ AssertionResult::~AssertionResult() {}
+
+ // Result was a success
+ bool AssertionResult::succeeded() const {
+ return Catch::isOk( m_resultData.resultType );
+ }
+
+ // Result was a success, or failure is suppressed
+ bool AssertionResult::isOk() const {
+ return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+ }
+
+ ResultWas::OfType AssertionResult::getResultType() const {
+ return m_resultData.resultType;
+ }
+
+ bool AssertionResult::hasExpression() const {
+ return m_info.capturedExpression[0] != 0;
+ }
+
+ bool AssertionResult::hasMessage() const {
+ return !m_resultData.message.empty();
+ }
+
+ std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) {
+ return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"')
+ ? capturedExpression
+ : std::string(capturedExpression) + ", " + secondArg;
+ }
+
+ std::string AssertionResult::getExpression() const {
+ if( isFalseTest( m_info.resultDisposition ) )
+ return '!' + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+ else
+ return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+ }
+ std::string AssertionResult::getExpressionInMacro() const {
+ if( m_info.macroName[0] == 0 )
+ return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+ else
+ return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )";
+ }
+
+ bool AssertionResult::hasExpandedExpression() const {
+ return hasExpression() && getExpandedExpression() != getExpression();
+ }
+
+ std::string AssertionResult::getExpandedExpression() const {
+ return m_resultData.reconstructExpression();
+ }
+
+ std::string AssertionResult::getMessage() const {
+ return m_resultData.message;
+ }
+ SourceLineInfo AssertionResult::getSourceInfo() const {
+ return m_info.lineInfo;
+ }
+
+ std::string AssertionResult::getTestMacroName() const {
+ return m_info.macroName;
+ }
+
+ void AssertionResult::discardDecomposedExpression() const {
+ m_resultData.decomposedExpression = CATCH_NULL;
+ }
+
+ void AssertionResult::expandDecomposedExpression() const {
+ m_resultData.reconstructExpression();
+ }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+#include <cctype>
+
+namespace Catch {
+
+ inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+ if( startsWith( tag, '.' ) ||
+ tag == "hide" ||
+ tag == "!hide" )
+ return TestCaseInfo::IsHidden;
+ else if( tag == "!throws" )
+ return TestCaseInfo::Throws;
+ else if( tag == "!shouldfail" )
+ return TestCaseInfo::ShouldFail;
+ else if( tag == "!mayfail" )
+ return TestCaseInfo::MayFail;
+ else if( tag == "!nonportable" )
+ return TestCaseInfo::NonPortable;
+ else
+ return TestCaseInfo::None;
+ }
+ inline bool isReservedTag( std::string const& tag ) {
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
+ }
+ inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+ if( isReservedTag( tag ) ) {
+ std::ostringstream ss;
+ ss << Colour(Colour::Red)
+ << "Tag name [" << tag << "] not allowed.\n"
+ << "Tag names starting with non alpha-numeric characters are reserved\n"
+ << Colour(Colour::FileName)
+ << _lineInfo << '\n';
+ throw std::runtime_error(ss.str());
+ }
+ }
+
+ TestCase makeTestCase( ITestCase* _testCase,
+ std::string const& _className,
+ std::string const& _name,
+ std::string const& _descOrTags,
+ SourceLineInfo const& _lineInfo )
+ {
+ bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+ // Parse out tags
+ std::set<std::string> tags;
+ std::string desc, tag;
+ bool inTag = false;
+ for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+ char c = _descOrTags[i];
+ if( !inTag ) {
+ if( c == '[' )
+ inTag = true;
+ else
+ desc += c;
+ }
+ else {
+ if( c == ']' ) {
+ TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+ if( prop == TestCaseInfo::IsHidden )
+ isHidden = true;
+ else if( prop == TestCaseInfo::None )
+ enforceNotReservedTag( tag, _lineInfo );
+
+ tags.insert( tag );
+ tag.clear();
+ inTag = false;
+ }
+ else
+ tag += c;
+ }
+ }
+ if( isHidden ) {
+ tags.insert( "hide" );
+ tags.insert( "." );
+ }
+
+ TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+ return TestCase( _testCase, info );
+ }
+
+ void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+ {
+ testCaseInfo.tags = tags;
+ testCaseInfo.lcaseTags.clear();
+
+ std::ostringstream oss;
+ for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+ oss << '[' << *it << ']';
+ std::string lcaseTag = toLower( *it );
+ testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+ testCaseInfo.lcaseTags.insert( lcaseTag );
+ }
+ testCaseInfo.tagsAsString = oss.str();
+ }
+
+ TestCaseInfo::TestCaseInfo( std::string const& _name,
+ std::string const& _className,
+ std::string const& _description,
+ std::set<std::string> const& _tags,
+ SourceLineInfo const& _lineInfo )
+ : name( _name ),
+ className( _className ),
+ description( _description ),
+ lineInfo( _lineInfo ),
+ properties( None )
+ {
+ setTags( *this, _tags );
+ }
+
+ TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+ : name( other.name ),
+ className( other.className ),
+ description( other.description ),
+ tags( other.tags ),
+ lcaseTags( other.lcaseTags ),
+ tagsAsString( other.tagsAsString ),
+ lineInfo( other.lineInfo ),
+ properties( other.properties )
+ {}
+
+ bool TestCaseInfo::isHidden() const {
+ return ( properties & IsHidden ) != 0;
+ }
+ bool TestCaseInfo::throws() const {
+ return ( properties & Throws ) != 0;
+ }
+ bool TestCaseInfo::okToFail() const {
+ return ( properties & (ShouldFail | MayFail ) ) != 0;
+ }
+ bool TestCaseInfo::expectedToFail() const {
+ return ( properties & (ShouldFail ) ) != 0;
+ }
+
+ TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+ TestCase::TestCase( TestCase const& other )
+ : TestCaseInfo( other ),
+ test( other.test )
+ {}
+
+ TestCase TestCase::withName( std::string const& _newName ) const {
+ TestCase other( *this );
+ other.name = _newName;
+ return other;
+ }
+
+ void TestCase::swap( TestCase& other ) {
+ test.swap( other.test );
+ name.swap( other.name );
+ className.swap( other.className );
+ description.swap( other.description );
+ tags.swap( other.tags );
+ lcaseTags.swap( other.lcaseTags );
+ tagsAsString.swap( other.tagsAsString );
+ std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+ std::swap( lineInfo, other.lineInfo );
+ }
+
+ void TestCase::invoke() const {
+ test->invoke();
+ }
+
+ bool TestCase::operator == ( TestCase const& other ) const {
+ return test.get() == other.test.get() &&
+ name == other.name &&
+ className == other.className;
+ }
+
+ bool TestCase::operator < ( TestCase const& other ) const {
+ return name < other.name;
+ }
+ TestCase& TestCase::operator = ( TestCase const& other ) {
+ TestCase temp( other );
+ swap( temp );
+ return *this;
+ }
+
+ TestCaseInfo const& TestCase::getTestCaseInfo() const
+ {
+ return *this;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+ Version::Version
+ ( unsigned int _majorVersion,
+ unsigned int _minorVersion,
+ unsigned int _patchNumber,
+ char const * const _branchName,
+ unsigned int _buildNumber )
+ : majorVersion( _majorVersion ),
+ minorVersion( _minorVersion ),
+ patchNumber( _patchNumber ),
+ branchName( _branchName ),
+ buildNumber( _buildNumber )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, Version const& version ) {
+ os << version.majorVersion << '.'
+ << version.minorVersion << '.'
+ << version.patchNumber;
+ // branchName is never null -> 0th char is \0 if it is empty
+ if (version.branchName[0]) {
+ os << '-' << version.branchName
+ << '.' << version.buildNumber;
+ }
+ return os;
+ }
+
+ inline Version libraryVersion() {
+ static Version version( 1, 9, 6, "", 0 );
+ return version;
+ }
+
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+ MessageInfo::MessageInfo( std::string const& _macroName,
+ SourceLineInfo const& _lineInfo,
+ ResultWas::OfType _type )
+ : macroName( _macroName ),
+ lineInfo( _lineInfo ),
+ type( _type ),
+ sequence( ++globalCount )
+ {}
+
+ // This may need protecting if threading support is added
+ unsigned int MessageInfo::globalCount = 0;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+ : m_info( builder.m_info )
+ {
+ m_info.message = builder.m_stream.str();
+ getResultCapture().pushScopedMessage( m_info );
+ }
+ ScopedMessage::ScopedMessage( ScopedMessage const& other )
+ : m_info( other.m_info )
+ {}
+
+ ScopedMessage::~ScopedMessage() {
+ if ( !std::uncaught_exception() ){
+ getResultCapture().popScopedMessage(m_info);
+ }
+ }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+ // Deprecated
+ struct IReporter : IShared {
+ virtual ~IReporter();
+
+ virtual bool shouldRedirectStdout() const = 0;
+
+ virtual void StartTesting() = 0;
+ virtual void EndTesting( Totals const& totals ) = 0;
+ virtual void StartGroup( std::string const& groupName ) = 0;
+ virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+ virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+ virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+ virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+ virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+ virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+ virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+ virtual void Aborted() = 0;
+ virtual void Result( AssertionResult const& result ) = 0;
+ };
+
+ class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+ {
+ public:
+ LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+ virtual ~LegacyReporterAdapter();
+
+ virtual ReporterPreferences getPreferences() const;
+ virtual void noMatchingTestCases( std::string const& );
+ virtual void testRunStarting( TestRunInfo const& );
+ virtual void testGroupStarting( GroupInfo const& groupInfo );
+ virtual void testCaseStarting( TestCaseInfo const& testInfo );
+ virtual void sectionStarting( SectionInfo const& sectionInfo );
+ virtual void assertionStarting( AssertionInfo const& );
+ virtual bool assertionEnded( AssertionStats const& assertionStats );
+ virtual void sectionEnded( SectionStats const& sectionStats );
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+ virtual void testRunEnded( TestRunStats const& testRunStats );
+ virtual void skipTest( TestCaseInfo const& );
+
+ private:
+ Ptr<IReporter> m_legacyReporter;
+ };
+}
+
+namespace Catch
+{
+ LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+ : m_legacyReporter( legacyReporter )
+ {}
+ LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+ ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+ return prefs;
+ }
+
+ void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+ void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+ m_legacyReporter->StartTesting();
+ }
+ void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+ m_legacyReporter->StartGroup( groupInfo.name );
+ }
+ void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+ m_legacyReporter->StartTestCase( testInfo );
+ }
+ void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+ m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+ }
+ void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+ // Not on legacy interface
+ }
+
+ bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+ if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+ rb << it->message;
+ rb.setResultType( ResultWas::Info );
+ AssertionResult result = rb.build();
+ m_legacyReporter->Result( result );
+ }
+ }
+ }
+ m_legacyReporter->Result( assertionStats.assertionResult );
+ return true;
+ }
+ void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+ if( sectionStats.missingAssertions )
+ m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+ m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+ }
+ void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+ m_legacyReporter->EndTestCase
+ ( testCaseStats.testInfo,
+ testCaseStats.totals,
+ testCaseStats.stdOut,
+ testCaseStats.stdErr );
+ }
+ void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+ if( testGroupStats.aborting )
+ m_legacyReporter->Aborted();
+ m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+ }
+ void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+ m_legacyReporter->EndTesting( testRunStats.totals );
+ }
+ void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
+ }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+#else
+
+#include <sys/time.h>
+
+#endif
+
+namespace Catch {
+
+ namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+ UInt64 getCurrentTicks() {
+ static UInt64 hz=0, hzo=0;
+ if (!hz) {
+ QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+ }
+ UInt64 t;
+ QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+ return ((t-hzo)*1000000)/hz;
+ }
+#else
+ UInt64 getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t,CATCH_NULL);
+ return static_cast<UInt64>( t.tv_sec ) * 1000000ull + static_cast<UInt64>( t.tv_usec );
+ }
+#endif
+ }
+
+ void Timer::start() {
+ m_ticks = getCurrentTicks();
+ }
+ unsigned int Timer::getElapsedMicroseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ unsigned int Timer::getElapsedMilliseconds() const {
+ return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+ }
+ double Timer::getElapsedSeconds() const {
+ return getElapsedMicroseconds()/1000000.0;
+ }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+#include <cstring>
+#include <cctype>
+
+namespace Catch {
+
+ bool startsWith( std::string const& s, std::string const& prefix ) {
+ return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+ }
+ bool startsWith( std::string const& s, char prefix ) {
+ return !s.empty() && s[0] == prefix;
+ }
+ bool endsWith( std::string const& s, std::string const& suffix ) {
+ return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+ }
+ bool endsWith( std::string const& s, char suffix ) {
+ return !s.empty() && s[s.size()-1] == suffix;
+ }
+ bool contains( std::string const& s, std::string const& infix ) {
+ return s.find( infix ) != std::string::npos;
+ }
+ char toLowerCh(char c) {
+ return static_cast<char>( std::tolower( c ) );
+ }
+ void toLowerInPlace( std::string& s ) {
+ std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+ }
+ std::string toLower( std::string const& s ) {
+ std::string lc = s;
+ toLowerInPlace( lc );
+ return lc;
+ }
+ std::string trim( std::string const& str ) {
+ static char const* whitespaceChars = "\n\r\t ";
+ std::string::size_type start = str.find_first_not_of( whitespaceChars );
+ std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+ }
+
+ bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+ bool replaced = false;
+ std::size_t i = str.find( replaceThis );
+ while( i != std::string::npos ) {
+ replaced = true;
+ str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+ if( i < str.size()-withThis.size() )
+ i = str.find( replaceThis, i+withThis.size() );
+ else
+ i = std::string::npos;
+ }
+ return replaced;
+ }
+
+ pluralise::pluralise( std::size_t count, std::string const& label )
+ : m_count( count ),
+ m_label( label )
+ {}
+
+ std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+ os << pluraliser.m_count << ' ' << pluraliser.m_label;
+ if( pluraliser.m_count != 1 )
+ os << 's';
+ return os;
+ }
+
+ SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){}
+ SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+ : file( _file ),
+ line( _line )
+ {}
+ bool SourceLineInfo::empty() const {
+ return file[0] == '\0';
+ }
+ bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+ }
+ bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+ return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
+ }
+
+ void seedRng( IConfig const& config ) {
+ if( config.rngSeed() != 0 )
+ std::srand( config.rngSeed() );
+ }
+ unsigned int rngSeed() {
+ return getCurrentContext().getConfig()->rngSeed();
+ }
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+ os << info.file << '(' << info.line << ')';
+#else
+ os << info.file << ':' << info.line;
+#endif
+ return os;
+ }
+
+ void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+ std::ostringstream oss;
+ oss << locationInfo << ": Internal Catch error: '" << message << '\'';
+ if( alwaysTrue() )
+ throw std::logic_error( oss.str() );
+ }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+ SectionInfo::SectionInfo
+ ( SourceLineInfo const& _lineInfo,
+ std::string const& _name,
+ std::string const& _description )
+ : name( _name ),
+ description( _description ),
+ lineInfo( _lineInfo )
+ {}
+
+ Section::Section( SectionInfo const& info )
+ : m_info( info ),
+ m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+ {
+ m_timer.start();
+ }
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
+#endif
+ Section::~Section() {
+ if( m_sectionIncluded ) {
+ SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+ if( std::uncaught_exception() )
+ getResultCapture().sectionEndedEarly( endInfo );
+ else
+ getResultCapture().sectionEnded( endInfo );
+ }
+ }
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+ // This indicates whether the section should be executed or not
+ Section::operator bool() const {
+ return m_sectionIncluded;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#ifdef CATCH_PLATFORM_MAC
+
+ #include <assert.h>
+ #include <stdbool.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/sysctl.h>
+
+ namespace Catch{
+
+ // The following function is taken directly from the following technical note:
+ // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive(){
+
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
+ Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+ }
+ } // namespace Catch
+
+#elif defined(CATCH_PLATFORM_LINUX)
+ #include <fstream>
+ #include <string>
+
+ namespace Catch{
+ // The standard POSIX way of detecting a debugger is to attempt to
+ // ptrace() the process, but this needs to be done from a child and not
+ // this process itself to still allow attaching to this process later
+ // if wanted, so is rather heavy. Under Linux we have the PID of the
+ // "debugger" (which doesn't need to be gdb, of course, it could also
+ // be strace, for example) in /proc/$PID/status, so just get it from
+ // there instead.
+ bool isDebuggerActive(){
+ // Libstdc++ has a bug, where std::ifstream sets errno to 0
+ // This way our users can properly assert over errno values
+ ErrnoGuard guard;
+ std::ifstream in("/proc/self/status");
+ for( std::string line; std::getline(in, line); ) {
+ static const int PREFIX_LEN = 11;
+ if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+ // We're traced if the PID is not 0 and no other PID starts
+ // with 0 digit, so it's enough to check for just a single
+ // character.
+ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+ }
+ }
+
+ return false;
+ }
+ } // namespace Catch
+#elif defined(_MSC_VER)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#elif defined(__MINGW32__)
+ extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ namespace Catch {
+ bool isDebuggerActive() {
+ return IsDebuggerPresent() != 0;
+ }
+ }
+#else
+ namespace Catch {
+ inline bool isDebuggerActive() { return false; }
+ }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ ::OutputDebugStringA( text.c_str() );
+ }
+ }
+#else
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ // !TBD: Need a version for Mac/ XCode and other IDEs
+ Catch::cout() << text;
+ }
+ }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+ const std::string unprintableString = "{?}";
+
+ namespace {
+ const int hexThreshold = 255;
+
+ struct Endianness {
+ enum Arch { Big, Little };
+
+ static Arch which() {
+ union _{
+ int asInt;
+ char asChar[sizeof (int)];
+ } u;
+
+ u.asInt = 1;
+ return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+ }
+ };
+ }
+
+ std::string rawMemoryToString( const void *object, std::size_t size )
+ {
+ // Reverse order for little endian architectures
+ int i = 0, end = static_cast<int>( size ), inc = 1;
+ if( Endianness::which() == Endianness::Little ) {
+ i = end-1;
+ end = inc = -1;
+ }
+
+ unsigned char const *bytes = static_cast<unsigned char const *>(object);
+ std::ostringstream os;
+ os << "0x" << std::setfill('0') << std::hex;
+ for( ; i != end; i += inc )
+ os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+ return os.str();
+ }
+}
+
+std::string toString( std::string const& value ) {
+ std::string s = value;
+ if( getCurrentContext().getConfig()->showInvisibles() ) {
+ for(size_t i = 0; i < s.size(); ++i ) {
+ std::string subs;
+ switch( s[i] ) {
+ case '\n': subs = "\\n"; break;
+ case '\t': subs = "\\t"; break;
+ default: break;
+ }
+ if( !subs.empty() ) {
+ s = s.substr( 0, i ) + subs + s.substr( i+1 );
+ ++i;
+ }
+ }
+ }
+ return '"' + s + '"';
+}
+std::string toString( std::wstring const& value ) {
+
+ std::string s;
+ s.reserve( value.size() );
+ for(size_t i = 0; i < value.size(); ++i )
+ s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+ return Catch::toString( s );
+}
+
+std::string toString( const char* const value ) {
+ return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+ return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+ return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+ return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+ std::ostringstream oss;
+ oss << std::setprecision( precision )
+ << std::fixed
+ << value;
+ std::string d = oss.str();
+ std::size_t i = d.find_last_not_of( '0' );
+ if( i != std::string::npos && i != d.size()-1 ) {
+ if( d[i] == '.' )
+ i++;
+ d = d.substr( 0, i+1 );
+ }
+ return d;
+}
+
+std::string toString( const double value ) {
+ return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+ return fpToString( value, 5 ) + 'f';
+}
+
+std::string toString( bool value ) {
+ return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+ if ( value == '\r' )
+ return "'\\r'";
+ if ( value == '\f' )
+ return "'\\f'";
+ if ( value == '\n' )
+ return "'\\n'";
+ if ( value == '\t' )
+ return "'\\t'";
+ if ( '\0' <= value && value < ' ' )
+ return toString( static_cast<unsigned int>( value ) );
+ char chstr[] = "' '";
+ chstr[1] = value;
+ return chstr;
+}
+
+std::string toString( signed char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+ return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+std::string toString( unsigned long long value ) {
+ std::ostringstream oss;
+ oss << value;
+ if( value > Detail::hexThreshold )
+ oss << " (0x" << std::hex << value << ')';
+ return oss.str();
+}
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+ return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+ std::string toString( NSString const * const& nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) {
+ if( !nsstring )
+ return "nil";
+ return "@" + toString([nsstring UTF8String]);
+ }
+ std::string toString( NSObject* const& nsObject ) {
+ return toString( [nsObject description] );
+ }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+ ResultBuilder::ResultBuilder( char const* macroName,
+ SourceLineInfo const& lineInfo,
+ char const* capturedExpression,
+ ResultDisposition::Flags resultDisposition,
+ char const* secondArg )
+ : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ),
+ m_shouldDebugBreak( false ),
+ m_shouldThrow( false ),
+ m_guardException( false )
+ {
+ m_stream().oss.str("");
+ }
+
+ ResultBuilder::~ResultBuilder() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+ if ( m_guardException ) {
+ m_stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+ captureResult( ResultWas::ThrewException );
+ getCurrentContext().getResultCapture()->exceptionEarlyReported();
+ }
+#endif
+ }
+
+ ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+ m_data.resultType = result;
+ return *this;
+ }
+ ResultBuilder& ResultBuilder::setResultType( bool result ) {
+ m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+ return *this;
+ }
+
+ void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
+ AssertionResult result = build( expr );
+ handleResult( result );
+ }
+
+ void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+ m_assertionInfo.resultDisposition = resultDisposition;
+ m_stream().oss << Catch::translateActiveException();
+ captureResult( ResultWas::ThrewException );
+ }
+
+ void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+ setResultType( resultType );
+ captureExpression();
+ }
+
+ void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+ if( expectedMessage.empty() )
+ captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
+ else
+ captureExpectedException( Matchers::Equals( expectedMessage ) );
+ }
+
+ void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
+
+ assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
+ AssertionResultData data = m_data;
+ data.resultType = ResultWas::Ok;
+ data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+
+ std::string actualMessage = Catch::translateActiveException();
+ if( !matcher.match( actualMessage ) ) {
+ data.resultType = ResultWas::ExpressionFailed;
+ data.reconstructedExpression = actualMessage;
+ }
+ AssertionResult result( m_assertionInfo, data );
+ handleResult( result );
+ }
+
+ void ResultBuilder::captureExpression() {
+ AssertionResult result = build();
+ handleResult( result );
+ }
+
+ void ResultBuilder::handleResult( AssertionResult const& result )
+ {
+ getResultCapture().assertionEnded( result );
+
+ if( !result.isOk() ) {
+ if( getCurrentContext().getConfig()->shouldDebugBreak() )
+ m_shouldDebugBreak = true;
+ if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+ m_shouldThrow = true;
+ }
+ }
+
+ void ResultBuilder::react() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+ if (m_shouldDebugBreak) {
+ ///////////////////////////////////////////////////////////////////
+ // To inspect the state during test, you need to go one level up the callstack
+ // To go back to the test and change execution, jump over the throw statement
+ ///////////////////////////////////////////////////////////////////
+ CATCH_BREAK_INTO_DEBUGGER();
+ }
+#endif
+ if( m_shouldThrow )
+ throw Catch::TestFailureException();
+ }
+
+ bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+ bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+ AssertionResult ResultBuilder::build() const
+ {
+ return build( *this );
+ }
+
+ // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
+ // a temporary DecomposedExpression, which in turn holds references to
+ // operands, possibly temporary as well.
+ // It should immediately be passed to handleResult; if the expression
+ // needs to be reported, its string expansion must be composed before
+ // the temporaries are destroyed.
+ AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
+ AssertionResultData data = m_data;
+
+ // Flip bool results if FalseTest flag is set
+ if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
+ data.negate( expr.isBinaryExpression() );
+ }
+
+ data.message = m_stream().oss.str();
+ data.decomposedExpression = &expr; // for lazy reconstruction
+ return AssertionResult( m_assertionInfo, data );
+ }
+
+ void ResultBuilder::reconstructExpression( std::string& dest ) const {
+ dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+ }
+
+ void ResultBuilder::setExceptionGuard() {
+ m_guardException = true;
+ }
+ void ResultBuilder::unsetExceptionGuard() {
+ m_guardException = false;
+ }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+namespace Catch {
+
+ TagAliasRegistry::~TagAliasRegistry() {}
+
+ Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+ std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+ if( it != m_registry.end() )
+ return it->second;
+ else
+ return Option<TagAlias>();
+ }
+
+ std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+ std::string expandedTestSpec = unexpandedTestSpec;
+ for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+ it != itEnd;
+ ++it ) {
+ std::size_t pos = expandedTestSpec.find( it->first );
+ if( pos != std::string::npos ) {
+ expandedTestSpec = expandedTestSpec.substr( 0, pos ) +
+ it->second.tag +
+ expandedTestSpec.substr( pos + it->first.size() );
+ }
+ }
+ return expandedTestSpec;
+ }
+
+ void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
+ std::ostringstream oss;
+ oss << Colour( Colour::Red )
+ << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n"
+ << Colour( Colour::FileName )
+ << lineInfo << '\n';
+ throw std::domain_error( oss.str().c_str() );
+ }
+ if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+ std::ostringstream oss;
+ oss << Colour( Colour::Red )
+ << "error: tag alias, \"" << alias << "\" already registered.\n"
+ << "\tFirst seen at "
+ << Colour( Colour::Red ) << find(alias)->lineInfo << '\n'
+ << Colour( Colour::Red ) << "\tRedefined at "
+ << Colour( Colour::FileName) << lineInfo << '\n';
+ throw std::domain_error( oss.str().c_str() );
+ }
+ }
+
+ ITagAliasRegistry::~ITagAliasRegistry() {}
+
+ ITagAliasRegistry const& ITagAliasRegistry::get() {
+ return getRegistryHub().getTagAliasRegistry();
+ }
+
+ RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+ getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo );
+ }
+
+} // end namespace Catch
+
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_str( adjustString( str ) )
+ {}
+ std::string CasedString::adjustString( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? toLower( str )
+ : str;
+ }
+ std::string CasedString::caseSensitivitySuffix() const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? " (case insensitive)"
+ : std::string();
+ }
+
+ StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+ : m_comparator( comparator ),
+ m_operation( operation ) {
+ }
+
+ std::string StringMatcherBase::describe() const {
+ std::string description;
+ description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+ m_comparator.caseSensitivitySuffix().size());
+ description += m_operation;
+ description += ": \"";
+ description += m_comparator.m_str;
+ description += "\"";
+ description += m_comparator.caseSensitivitySuffix();
+ return description;
+ }
+
+ EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+ bool EqualsMatcher::match( std::string const& source ) const {
+ return m_comparator.adjustString( source ) == m_comparator.m_str;
+ }
+
+ ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+ bool ContainsMatcher::match( std::string const& source ) const {
+ return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+ bool StartsWithMatcher::match( std::string const& source ) const {
+ return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+ bool EndsWithMatcher::match( std::string const& source ) const {
+ return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ } // namespace StdString
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+ typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+ Reporters m_reporters;
+
+public:
+ void add( Ptr<IStreamingReporter> const& reporter ) {
+ m_reporters.push_back( reporter );
+ }
+
+public: // IStreamingReporter
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporters[0]->getPreferences();
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->noMatchingTestCases( spec );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testRunStarting( testRunInfo );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testGroupStarting( groupInfo );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testCaseStarting( testInfo );
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->sectionStarting( sectionInfo );
+ }
+
+ virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->assertionStarting( assertionInfo );
+ }
+
+ // The return value indicates if the messages buffer should be cleared:
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ bool clearBuffer = false;
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ clearBuffer |= (*it)->assertionEnded( assertionStats );
+ return clearBuffer;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->sectionEnded( sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testGroupEnded( testGroupStats );
+ }
+
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->testRunEnded( testRunStats );
+ }
+
+ virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+ it != itEnd;
+ ++it )
+ (*it)->skipTest( testInfo );
+ }
+
+ virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+ return this;
+ }
+
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+ Ptr<IStreamingReporter> resultingReporter;
+
+ if( existingReporter ) {
+ MultipleReporters* multi = existingReporter->tryAsMulti();
+ if( !multi ) {
+ multi = new MultipleReporters;
+ resultingReporter = Ptr<IStreamingReporter>( multi );
+ if( existingReporter )
+ multi->add( existingReporter );
+ }
+ else
+ resultingReporter = existingReporter;
+ multi->add( additionalReporter );
+ }
+ else
+ resultingReporter = additionalReporter;
+
+ return resultingReporter;
+}
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <assert.h>
+
+namespace Catch {
+
+ namespace {
+ // Because formatting using c++ streams is stateful, drop down to C is required
+ // Alternatively we could use stringstream, but its performance is... not good.
+ std::string getFormattedDuration( double duration ) {
+ // Max exponent + 1 is required to represent the whole part
+ // + 1 for decimal point
+ // + 3 for the 3 decimal places
+ // + 1 for null terminator
+ const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+ char buffer[maxDoubleSize];
+
+ // Save previous errno, to prevent sprintf from overwriting it
+ ErrnoGuard guard;
+#ifdef _MSC_VER
+ sprintf_s(buffer, "%.3f", duration);
+#else
+ sprintf(buffer, "%.3f", duration);
+#endif
+ return std::string(buffer);
+ }
+ }
+
+ struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+ StreamingReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ }
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporterPrefs;
+ }
+
+ virtual ~StreamingReporterBase() CATCH_OVERRIDE;
+
+ virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
+
+ virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
+ currentTestRunInfo = _testRunInfo;
+ }
+ virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
+ currentGroupInfo = _groupInfo;
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
+ currentTestCaseInfo = _testInfo;
+ }
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+ m_sectionStack.push_back( _sectionInfo );
+ }
+
+ virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
+ currentTestCaseInfo.reset();
+ }
+ virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
+ currentGroupInfo.reset();
+ }
+ virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
+ currentTestCaseInfo.reset();
+ currentGroupInfo.reset();
+ currentTestRunInfo.reset();
+ }
+
+ virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
+ // Don't do anything with this by default.
+ // It can optionally be overridden in the derived class.
+ }
+
+ Ptr<IConfig const> m_config;
+ std::ostream& stream;
+
+ LazyStat<TestRunInfo> currentTestRunInfo;
+ LazyStat<GroupInfo> currentGroupInfo;
+ LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+ std::vector<SectionInfo> m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+ };
+
+ struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+ template<typename T, typename ChildNodeT>
+ struct Node : SharedImpl<> {
+ explicit Node( T const& _value ) : value( _value ) {}
+ virtual ~Node() {}
+
+ typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+ T value;
+ ChildNodes children;
+ };
+ struct SectionNode : SharedImpl<> {
+ explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+ virtual ~SectionNode();
+
+ bool operator == ( SectionNode const& other ) const {
+ return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+ }
+ bool operator == ( Ptr<SectionNode> const& other ) const {
+ return operator==( *other );
+ }
+
+ SectionStats stats;
+ typedef std::vector<Ptr<SectionNode> > ChildSections;
+ typedef std::vector<AssertionStats> Assertions;
+ ChildSections childSections;
+ Assertions assertions;
+ std::string stdOut;
+ std::string stdErr;
+ };
+
+ struct BySectionInfo {
+ BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ bool operator() ( Ptr<SectionNode> const& node ) const {
+ return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
+ }
+ private:
+ void operator=( BySectionInfo const& );
+ SectionInfo const& m_other;
+ };
+
+ typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+ typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+ typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+ CumulativeReporterBase( ReporterConfig const& _config )
+ : m_config( _config.fullConfig() ),
+ stream( _config.stream() )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = false;
+ }
+ ~CumulativeReporterBase();
+
+ virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+ return m_reporterPrefs;
+ }
+
+ virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+ virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ Ptr<SectionNode> node;
+ if( m_sectionStack.empty() ) {
+ if( !m_rootSection )
+ m_rootSection = new SectionNode( incompleteStats );
+ node = m_rootSection;
+ }
+ else {
+ SectionNode& parentNode = *m_sectionStack.back();
+ SectionNode::ChildSections::const_iterator it =
+ std::find_if( parentNode.childSections.begin(),
+ parentNode.childSections.end(),
+ BySectionInfo( sectionInfo ) );
+ if( it == parentNode.childSections.end() ) {
+ node = new SectionNode( incompleteStats );
+ parentNode.childSections.push_back( node );
+ }
+ else
+ node = *it;
+ }
+ m_sectionStack.push_back( node );
+ m_deepestSection = node;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ assert( !m_sectionStack.empty() );
+ SectionNode& sectionNode = *m_sectionStack.back();
+ sectionNode.assertions.push_back( assertionStats );
+ // AssertionResult holds a pointer to a temporary DecomposedExpression,
+ // which getExpandedExpression() calls to build the expression string.
+ // Our section stack copy of the assertionResult will likely outlive the
+ // temporary, so it must be expanded or discarded now to avoid calling
+ // a destroyed object later.
+ prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
+ return true;
+ }
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ assert( !m_sectionStack.empty() );
+ SectionNode& node = *m_sectionStack.back();
+ node.stats = sectionStats;
+ m_sectionStack.pop_back();
+ }
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+ assert( m_sectionStack.size() == 0 );
+ node->children.push_back( m_rootSection );
+ m_testCases.push_back( node );
+ m_rootSection.reset();
+
+ assert( m_deepestSection );
+ m_deepestSection->stdOut = testCaseStats.stdOut;
+ m_deepestSection->stdErr = testCaseStats.stdErr;
+ }
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+ node->children.swap( m_testCases );
+ m_testGroups.push_back( node );
+ }
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+ node->children.swap( m_testGroups );
+ m_testRuns.push_back( node );
+ testRunEndedCumulative();
+ }
+ virtual void testRunEndedCumulative() = 0;
+
+ virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+ virtual void prepareExpandedExpression( AssertionResult& result ) const {
+ if( result.isOk() )
+ result.discardDecomposedExpression();
+ else
+ result.expandDecomposedExpression();
+ }
+
+ Ptr<IConfig const> m_config;
+ std::ostream& stream;
+ std::vector<AssertionStats> m_assertions;
+ std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+ std::vector<Ptr<TestCaseNode> > m_testCases;
+ std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+ std::vector<Ptr<TestRunNode> > m_testRuns;
+
+ Ptr<SectionNode> m_rootSection;
+ Ptr<SectionNode> m_deepestSection;
+ std::vector<Ptr<SectionNode> > m_sectionStack;
+ ReporterPreferences m_reporterPrefs;
+
+ };
+
+ template<char C>
+ char const* getLineOfChars() {
+ static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+ if( !*line ) {
+ std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+ }
+ return line;
+ }
+
+ struct TestEventListenerBase : StreamingReporterBase {
+ TestEventListenerBase( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+ virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+ return false;
+ }
+ };
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+ template<typename T>
+ class LegacyReporterRegistrar {
+
+ class ReporterFactory : public IReporterFactory {
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new LegacyReporterAdapter( new T( config ) );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ LegacyReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ReporterRegistrar {
+
+ class ReporterFactory : public SharedImpl<IReporterFactory> {
+
+ // *** Please Note ***:
+ // - If you end up here looking at a compiler error because it's trying to register
+ // your custom reporter class be aware that the native reporter interface has changed
+ // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+ // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+ // However please consider updating to the new interface as the old one is now
+ // deprecated and will probably be removed quite soon!
+ // Please contact me via github if you have any questions at all about this.
+ // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+ // no idea who is actually using custom reporters at all (possibly no-one!).
+ // The new interface is designed to minimise exposure to interface changes in the future.
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+
+ virtual std::string getDescription() const {
+ return T::getDescription();
+ }
+ };
+
+ public:
+
+ ReporterRegistrar( std::string const& name ) {
+ getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+ }
+ };
+
+ template<typename T>
+ class ListenerRegistrar {
+
+ class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+ virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+ return new T( config );
+ }
+ virtual std::string getDescription() const {
+ return std::string();
+ }
+ };
+
+ public:
+
+ ListenerRegistrar() {
+ getMutableRegistryHub().registerListener( new ListenerFactory() );
+ }
+ };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+ namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+ namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// Deprecated - use the form without INTERNAL_
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+ namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+
+namespace Catch {
+
+ class XmlEncode {
+ public:
+ enum ForWhat { ForTextNodes, ForAttributes };
+
+ XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+ : m_str( str ),
+ m_forWhat( forWhat )
+ {}
+
+ void encodeTo( std::ostream& os ) const {
+
+ // Apostrophe escaping not necessary if we always use " to write attributes
+ // (see: http://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+ char c = m_str[i];
+ switch( c ) {
+ case '<': os << "<"; break;
+ case '&': os << "&"; break;
+
+ case '>':
+ // See: http://www.w3.org/TR/xml/#syntax
+ if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+ os << ">";
+ else
+ os << c;
+ break;
+
+ case '\"':
+ if( m_forWhat == ForAttributes )
+ os << """;
+ else
+ os << c;
+ break;
+
+ default:
+ // Escape control chars - based on contribution by @espenalb in PR #465 and
+ // by @mrpi PR #588
+ if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
+ // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>( c );
+ }
+ else
+ os << c;
+ }
+ }
+ }
+
+ friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+ xmlEncode.encodeTo( os );
+ return os;
+ }
+
+ private:
+ std::string m_str;
+ ForWhat m_forWhat;
+ };
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ ScopedElement( ScopedElement const& other )
+ : m_writer( other.m_writer ){
+ other.m_writer = CATCH_NULL;
+ }
+
+ ~ScopedElement() {
+ if( m_writer )
+ m_writer->endElement();
+ }
+
+ ScopedElement& writeText( std::string const& text, bool indent = true ) {
+ m_writer->writeText( text, indent );
+ return *this;
+ }
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer;
+ };
+
+ XmlWriter()
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( Catch::cout() )
+ {
+ writeDeclaration();
+ }
+
+ XmlWriter( std::ostream& os )
+ : m_tagIsOpen( false ),
+ m_needsNewline( false ),
+ m_os( os )
+ {
+ writeDeclaration();
+ }
+
+ ~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+ XmlWriter& startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ m_os << m_indent << '<' << name;
+ m_tags.push_back( name );
+ m_indent += " ";
+ m_tagIsOpen = true;
+ return *this;
+ }
+
+ ScopedElement scopedElement( std::string const& name ) {
+ ScopedElement scoped( this );
+ startElement( name );
+ return scoped;
+ }
+
+ XmlWriter& endElement() {
+ newlineIfNecessary();
+ m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ if( m_tagIsOpen ) {
+ m_os << "/>";
+ m_tagIsOpen = false;
+ }
+ else {
+ m_os << m_indent << "</" << m_tags.back() << ">";
+ }
+ m_os << std::endl;
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() )
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+ return *this;
+ }
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+ m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+ return *this;
+ }
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ std::ostringstream oss;
+ oss << attribute;
+ return writeAttribute( name, oss.str() );
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ m_os << m_indent;
+ m_os << XmlEncode( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ XmlWriter& writeComment( std::string const& text ) {
+ ensureTagClosed();
+ m_os << m_indent << "<!--" << text << "-->";
+ m_needsNewline = true;
+ return *this;
+ }
+
+ void writeStylesheetRef( std::string const& url ) {
+ m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+ }
+
+ XmlWriter& writeBlankLine() {
+ ensureTagClosed();
+ m_os << '\n';
+ return *this;
+ }
+
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ m_os << ">" << std::endl;
+ m_tagIsOpen = false;
+ }
+ }
+
+ private:
+ XmlWriter( XmlWriter const& );
+ void operator=( XmlWriter const& );
+
+ void writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ }
+
+ void newlineIfNecessary() {
+ if( m_needsNewline ) {
+ m_os << std::endl;
+ m_needsNewline = false;
+ }
+ }
+
+ bool m_tagIsOpen;
+ bool m_needsNewline;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream& m_os;
+ };
+
+}
+
+namespace Catch {
+ class XmlReporter : public StreamingReporterBase {
+ public:
+ XmlReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_xml(_config.stream()),
+ m_sectionDepth( 0 )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ }
+
+ virtual ~XmlReporter() CATCH_OVERRIDE;
+
+ static std::string getDescription() {
+ return "Reports test results as an XML document";
+ }
+
+ virtual std::string getStylesheetRef() const {
+ return std::string();
+ }
+
+ void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+ m_xml
+ .writeAttribute( "filename", sourceInfo.file )
+ .writeAttribute( "line", sourceInfo.line );
+ }
+
+ public: // StreamingReporterBase
+
+ virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
+ StreamingReporterBase::noMatchingTestCases( s );
+ }
+
+ virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testRunStarting( testInfo );
+ std::string stylesheetRef = getStylesheetRef();
+ if( !stylesheetRef.empty() )
+ m_xml.writeStylesheetRef( stylesheetRef );
+ m_xml.startElement( "Catch" );
+ if( !m_config->name().empty() )
+ m_xml.writeAttribute( "name", m_config->name() );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testGroupStarting( groupInfo );
+ m_xml.startElement( "Group" )
+ .writeAttribute( "name", groupInfo.name );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseStarting(testInfo);
+ m_xml.startElement( "TestCase" )
+ .writeAttribute( "name", trim( testInfo.name ) )
+ .writeAttribute( "description", testInfo.description )
+ .writeAttribute( "tags", testInfo.tagsAsString );
+
+ writeSourceInfo( testInfo.lineInfo );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ m_testCaseTimer.start();
+ m_xml.ensureTagClosed();
+ }
+
+ virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+ StreamingReporterBase::sectionStarting( sectionInfo );
+ if( m_sectionDepth++ > 0 ) {
+ m_xml.startElement( "Section" )
+ .writeAttribute( "name", trim( sectionInfo.name ) )
+ .writeAttribute( "description", sectionInfo.description );
+ writeSourceInfo( sectionInfo.lineInfo );
+ m_xml.ensureTagClosed();
+ }
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
+
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+
+ AssertionResult const& result = assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ if( includeResults ) {
+ // Print any info messages in <Info> tags.
+ for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+ it != itEnd;
+ ++it ) {
+ if( it->type == ResultWas::Info ) {
+ m_xml.scopedElement( "Info" )
+ .writeText( it->message );
+ } else if ( it->type == ResultWas::Warning ) {
+ m_xml.scopedElement( "Warning" )
+ .writeText( it->message );
+ }
+ }
+ }
+
+ // Drop out if result was successful but we're not printing them.
+ if( !includeResults && result.getResultType() != ResultWas::Warning )
+ return true;
+
+ // Print the expression if there is one.
+ if( result.hasExpression() ) {
+ m_xml.startElement( "Expression" )
+ .writeAttribute( "success", result.succeeded() )
+ .writeAttribute( "type", result.getTestMacroName() );
+
+ writeSourceInfo( result.getSourceInfo() );
+
+ m_xml.scopedElement( "Original" )
+ .writeText( result.getExpression() );
+ m_xml.scopedElement( "Expanded" )
+ .writeText( result.getExpandedExpression() );
+ }
+
+ // And... Print a result applicable to each result type.
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ m_xml.startElement( "Exception" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::FatalErrorCondition:
+ m_xml.startElement( "FatalErrorCondition" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ case ResultWas::Info:
+ m_xml.scopedElement( "Info" )
+ .writeText( result.getMessage() );
+ break;
+ case ResultWas::Warning:
+ // Warning will already have been written
+ break;
+ case ResultWas::ExplicitFailure:
+ m_xml.startElement( "Failure" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
+ default:
+ break;
+ }
+
+ if( result.hasExpression() )
+ m_xml.endElement();
+
+ return true;
+ }
+
+ virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::sectionEnded( sectionStats );
+ if( --m_sectionDepth > 0 ) {
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+ e.writeAttribute( "successes", sectionStats.assertions.passed );
+ e.writeAttribute( "failures", sectionStats.assertions.failed );
+ e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+ m_xml.endElement();
+ }
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseEnded( testCaseStats );
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+ e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+ if( !testCaseStats.stdOut.empty() )
+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+ if( !testCaseStats.stdErr.empty() )
+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+
+ m_xml.endElement();
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testGroupEnded( testGroupStats );
+ // TODO: Check testGroupStats.aborting and act accordingly.
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+ .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testRunEnded( testRunStats );
+ m_xml.scopedElement( "OverallResults" )
+ .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+ .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+ .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+ m_xml.endElement();
+ }
+
+ private:
+ Timer m_testCaseTimer;
+ XmlWriter m_xml;
+ int m_sectionDepth;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+ namespace {
+ std::string getCurrentTimestamp() {
+ // Beware, this is not reentrant because of backward compatibility issues
+ // Also, UTC only, again because of backward compatibility (%z is C++11)
+ time_t rawtime;
+ std::time(&rawtime);
+ const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &rawtime);
+#else
+ std::tm* timeInfo;
+ timeInfo = std::gmtime(&rawtime);
+#endif
+
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp);
+ }
+
+ }
+
+ class JunitReporter : public CumulativeReporterBase {
+ public:
+ JunitReporter( ReporterConfig const& _config )
+ : CumulativeReporterBase( _config ),
+ xml( _config.stream() ),
+ m_okToFail( false )
+ {
+ m_reporterPrefs.shouldRedirectStdOut = true;
+ }
+
+ virtual ~JunitReporter() CATCH_OVERRIDE;
+
+ static std::string getDescription() {
+ return "Reports test results in an XML format that looks like Ant's junitreport target";
+ }
+
+ virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
+
+ virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
+ CumulativeReporterBase::testRunStarting( runInfo );
+ xml.startElement( "testsuites" );
+ }
+
+ virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+ suiteTimer.start();
+ stdOutForSuite.str("");
+ stdErrForSuite.str("");
+ unexpectedExceptions = 0;
+ CumulativeReporterBase::testGroupStarting( groupInfo );
+ }
+
+ virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE {
+ m_okToFail = testCaseInfo.okToFail();
+ }
+ virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+ if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+ unexpectedExceptions++;
+ return CumulativeReporterBase::assertionEnded( assertionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+ stdOutForSuite << testCaseStats.stdOut;
+ stdErrForSuite << testCaseStats.stdErr;
+ CumulativeReporterBase::testCaseEnded( testCaseStats );
+ }
+
+ virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+ double suiteTime = suiteTimer.getElapsedSeconds();
+ CumulativeReporterBase::testGroupEnded( testGroupStats );
+ writeGroup( *m_testGroups.back(), suiteTime );
+ }
+
+ virtual void testRunEndedCumulative() CATCH_OVERRIDE {
+ xml.endElement();
+ }
+
+ void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+ TestGroupStats const& stats = groupNode.value;
+ xml.writeAttribute( "name", stats.groupInfo.name );
+ xml.writeAttribute( "errors", unexpectedExceptions );
+ xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "tests", stats.totals.assertions.total() );
+ xml.writeAttribute( "hostname", "tbd" ); // !TBD
+ if( m_config->showDurations() == ShowDurations::Never )
+ xml.writeAttribute( "time", "" );
+ else
+ xml.writeAttribute( "time", suiteTime );
+ xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
+ // Write test cases
+ for( TestGroupNode::ChildNodes::const_iterator
+ it = groupNode.children.begin(), itEnd = groupNode.children.end();
+ it != itEnd;
+ ++it )
+ writeTestCase( **it );
+
+ xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+ xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+ }
+
+ void writeTestCase( TestCaseNode const& testCaseNode ) {
+ TestCaseStats const& stats = testCaseNode.value;
+
+ // All test cases have exactly one section - which represents the
+ // test case itself. That section may have 0-n nested sections
+ assert( testCaseNode.children.size() == 1 );
+ SectionNode const& rootSection = *testCaseNode.children.front();
+
+ std::string className = stats.testInfo.className;
+
+ if( className.empty() ) {
+ if( rootSection.childSections.empty() )
+ className = "global";
+ }
+ writeSection( className, "", rootSection );
+ }
+
+ void writeSection( std::string const& className,
+ std::string const& rootName,
+ SectionNode const& sectionNode ) {
+ std::string name = trim( sectionNode.stats.sectionInfo.name );
+ if( !rootName.empty() )
+ name = rootName + '/' + name;
+
+ if( !sectionNode.assertions.empty() ||
+ !sectionNode.stdOut.empty() ||
+ !sectionNode.stdErr.empty() ) {
+ XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+ if( className.empty() ) {
+ xml.writeAttribute( "classname", name );
+ xml.writeAttribute( "name", "root" );
+ }
+ else {
+ xml.writeAttribute( "classname", className );
+ xml.writeAttribute( "name", name );
+ }
+ xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
+
+ writeAssertions( sectionNode );
+
+ if( !sectionNode.stdOut.empty() )
+ xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+ if( !sectionNode.stdErr.empty() )
+ xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+ }
+ for( SectionNode::ChildSections::const_iterator
+ it = sectionNode.childSections.begin(),
+ itEnd = sectionNode.childSections.end();
+ it != itEnd;
+ ++it )
+ if( className.empty() )
+ writeSection( name, "", **it );
+ else
+ writeSection( className, name, **it );
+ }
+
+ void writeAssertions( SectionNode const& sectionNode ) {
+ for( SectionNode::Assertions::const_iterator
+ it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+ it != itEnd;
+ ++it )
+ writeAssertion( *it );
+ }
+ void writeAssertion( AssertionStats const& stats ) {
+ AssertionResult const& result = stats.assertionResult;
+ if( !result.isOk() ) {
+ std::string elementName;
+ switch( result.getResultType() ) {
+ case ResultWas::ThrewException:
+ case ResultWas::FatalErrorCondition:
+ elementName = "error";
+ break;
+ case ResultWas::ExplicitFailure:
+ elementName = "failure";
+ break;
+ case ResultWas::ExpressionFailed:
+ elementName = "failure";
+ break;
+ case ResultWas::DidntThrowException:
+ elementName = "failure";
+ break;
+
+ // We should never see these here:
+ case ResultWas::Info:
+ case ResultWas::Warning:
+ case ResultWas::Ok:
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ elementName = "internalError";
+ break;
+ }
+
+ XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+ xml.writeAttribute( "message", result.getExpandedExpression() );
+ xml.writeAttribute( "type", result.getTestMacroName() );
+
+ std::ostringstream oss;
+ if( !result.getMessage().empty() )
+ oss << result.getMessage() << '\n';
+ for( std::vector<MessageInfo>::const_iterator
+ it = stats.infoMessages.begin(),
+ itEnd = stats.infoMessages.end();
+ it != itEnd;
+ ++it )
+ if( it->type == ResultWas::Info )
+ oss << it->message << '\n';
+
+ oss << "at " << result.getSourceInfo();
+ xml.writeText( oss.str(), false );
+ }
+ }
+
+ XmlWriter xml;
+ Timer suiteTimer;
+ std::ostringstream stdOutForSuite;
+ std::ostringstream stdErrForSuite;
+ unsigned int unexpectedExceptions;
+ bool m_okToFail;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cfloat>
+#include <cstdio>
+
+namespace Catch {
+
+ struct ConsoleReporter : StreamingReporterBase {
+ ConsoleReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config ),
+ m_headerPrinted( false )
+ {}
+
+ virtual ~ConsoleReporter() CATCH_OVERRIDE;
+ static std::string getDescription() {
+ return "Reports test results as plain lines of text";
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
+ }
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+ // Drop out if result was successful but we're not printing them.
+ if( !includeResults && result.getResultType() != ResultWas::Warning )
+ return false;
+
+ lazyPrint();
+
+ AssertionPrinter printer( stream, _assertionStats, includeResults );
+ printer.print();
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+ m_headerPrinted = false;
+ StreamingReporterBase::sectionStarting( _sectionInfo );
+ }
+ virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
+ if( _sectionStats.missingAssertions ) {
+ lazyPrint();
+ Colour colour( Colour::ResultError );
+ if( m_sectionStack.size() > 1 )
+ stream << "\nNo assertions in section";
+ else
+ stream << "\nNo assertions in test case";
+ stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+ }
+ if( m_config->showDurations() == ShowDurations::Always ) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ if( m_headerPrinted ) {
+ m_headerPrinted = false;
+ }
+ StreamingReporterBase::sectionEnded( _sectionStats );
+ }
+
+ virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
+ StreamingReporterBase::testCaseEnded( _testCaseStats );
+ m_headerPrinted = false;
+ }
+ virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
+ if( currentGroupInfo.used ) {
+ printSummaryDivider();
+ stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+ printTotals( _testGroupStats.totals );
+ stream << '\n' << std::endl;
+ }
+ StreamingReporterBase::testGroupEnded( _testGroupStats );
+ }
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
+ printTotalsDivider( _testRunStats.totals );
+ printTotals( _testRunStats.totals );
+ stream << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream ),
+ stats( _stats ),
+ result( _stats.assertionResult ),
+ colour( Colour::None ),
+ message( result.getMessage() ),
+ messages( _stats.infoMessages ),
+ printInfoMessages( _printInfoMessages )
+ {
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ colour = Colour::Success;
+ passOrFail = "PASSED";
+ //if( result.hasMessage() )
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() ) {
+ colour = Colour::Success;
+ passOrFail = "FAILED - but was ok";
+ }
+ else {
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ }
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "with messages";
+ break;
+ case ResultWas::ThrewException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to unexpected exception with ";
+ if (_stats.infoMessages.size() == 1)
+ messageLabel += "message";
+ if (_stats.infoMessages.size() > 1)
+ messageLabel += "messages";
+ break;
+ case ResultWas::FatalErrorCondition:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to a fatal error condition";
+ break;
+ case ResultWas::DidntThrowException:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "because no exception was thrown where one was expected";
+ break;
+ case ResultWas::Info:
+ messageLabel = "info";
+ break;
+ case ResultWas::Warning:
+ messageLabel = "warning";
+ break;
+ case ResultWas::ExplicitFailure:
+ passOrFail = "FAILED";
+ colour = Colour::Error;
+ if( _stats.infoMessages.size() == 1 )
+ messageLabel = "explicitly with message";
+ if( _stats.infoMessages.size() > 1 )
+ messageLabel = "explicitly with messages";
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ passOrFail = "** internal error **";
+ colour = Colour::Error;
+ break;
+ }
+ }
+
+ void print() const {
+ printSourceInfo();
+ if( stats.totals.assertions.total() > 0 ) {
+ if( result.isOk() )
+ stream << '\n';
+ printResultType();
+ printOriginalExpression();
+ printReconstructedExpression();
+ }
+ else {
+ stream << '\n';
+ }
+ printMessage();
+ }
+
+ private:
+ void printResultType() const {
+ if( !passOrFail.empty() ) {
+ Colour colourGuard( colour );
+ stream << passOrFail << ":\n";
+ }
+ }
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ Colour colourGuard( Colour::OriginalExpression );
+ stream << " ";
+ stream << result.getExpressionInMacro();
+ stream << '\n';
+ }
+ }
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ stream << "with expansion:\n";
+ Colour colourGuard( Colour::ReconstructedExpression );
+ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n';
+ }
+ }
+ void printMessage() const {
+ if( !messageLabel.empty() )
+ stream << messageLabel << ':' << '\n';
+ for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+ it != itEnd;
+ ++it ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || it->type != ResultWas::Info )
+ stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n';
+ }
+ }
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ": ";
+ }
+
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ Colour::Code colour;
+ std::string passOrFail;
+ std::string messageLabel;
+ std::string message;
+ std::vector<MessageInfo> messages;
+ bool printInfoMessages;
+ };
+
+ void lazyPrint() {
+
+ if( !currentTestRunInfo.used )
+ lazyPrintRunInfo();
+ if( !currentGroupInfo.used )
+ lazyPrintGroupInfo();
+
+ if( !m_headerPrinted ) {
+ printTestCaseAndSectionHeader();
+ m_headerPrinted = true;
+ }
+ }
+ void lazyPrintRunInfo() {
+ stream << '\n' << getLineOfChars<'~'>() << '\n';
+ Colour colour( Colour::SecondaryText );
+ stream << currentTestRunInfo->name
+ << " is a Catch v" << libraryVersion() << " host application.\n"
+ << "Run with -? for options\n\n";
+
+ if( m_config->rngSeed() != 0 )
+ stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+ currentTestRunInfo.used = true;
+ }
+ void lazyPrintGroupInfo() {
+ if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+ printClosedHeader( "Group: " + currentGroupInfo->name );
+ currentGroupInfo.used = true;
+ }
+ }
+ void printTestCaseAndSectionHeader() {
+ assert( !m_sectionStack.empty() );
+ printOpenHeader( currentTestCaseInfo->name );
+
+ if( m_sectionStack.size() > 1 ) {
+ Colour colourGuard( Colour::Headers );
+
+ std::vector<SectionInfo>::const_iterator
+ it = m_sectionStack.begin()+1, // Skip first section (test case)
+ itEnd = m_sectionStack.end();
+ for( ; it != itEnd; ++it )
+ printHeaderString( it->name, 2 );
+ }
+
+ SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+ if( !lineInfo.empty() ){
+ stream << getLineOfChars<'-'>() << '\n';
+ Colour colourGuard( Colour::FileName );
+ stream << lineInfo << '\n';
+ }
+ stream << getLineOfChars<'.'>() << '\n' << std::endl;
+ }
+
+ void printClosedHeader( std::string const& _name ) {
+ printOpenHeader( _name );
+ stream << getLineOfChars<'.'>() << '\n';
+ }
+ void printOpenHeader( std::string const& _name ) {
+ stream << getLineOfChars<'-'>() << '\n';
+ {
+ Colour colourGuard( Colour::Headers );
+ printHeaderString( _name );
+ }
+ }
+
+ // if string has a : in first line will set indent to follow it on
+ // subsequent lines
+ void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+ std::size_t i = _string.find( ": " );
+ if( i != std::string::npos )
+ i+=2;
+ else
+ i = 0;
+ stream << Text( _string, TextAttributes()
+ .setIndent( indent+i)
+ .setInitialIndent( indent ) ) << '\n';
+ }
+
+ struct SummaryColumn {
+
+ SummaryColumn( std::string const& _label, Colour::Code _colour )
+ : label( _label ),
+ colour( _colour )
+ {}
+ SummaryColumn addRow( std::size_t count ) {
+ std::ostringstream oss;
+ oss << count;
+ std::string row = oss.str();
+ for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+ while( it->size() < row.size() )
+ *it = ' ' + *it;
+ while( it->size() > row.size() )
+ row = ' ' + row;
+ }
+ rows.push_back( row );
+ return *this;
+ }
+
+ std::string label;
+ Colour::Code colour;
+ std::vector<std::string> rows;
+
+ };
+
+ void printTotals( Totals const& totals ) {
+ if( totals.testCases.total() == 0 ) {
+ stream << Colour( Colour::Warning ) << "No tests ran\n";
+ }
+ else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
+ stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+ stream << " ("
+ << pluralise( totals.assertions.passed, "assertion" ) << " in "
+ << pluralise( totals.testCases.passed, "test case" ) << ')'
+ << '\n';
+ }
+ else {
+
+ std::vector<SummaryColumn> columns;
+ columns.push_back( SummaryColumn( "", Colour::None )
+ .addRow( totals.testCases.total() )
+ .addRow( totals.assertions.total() ) );
+ columns.push_back( SummaryColumn( "passed", Colour::Success )
+ .addRow( totals.testCases.passed )
+ .addRow( totals.assertions.passed ) );
+ columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+ .addRow( totals.testCases.failed )
+ .addRow( totals.assertions.failed ) );
+ columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+ .addRow( totals.testCases.failedButOk )
+ .addRow( totals.assertions.failedButOk ) );
+
+ printSummaryRow( "test cases", columns, 0 );
+ printSummaryRow( "assertions", columns, 1 );
+ }
+ }
+ void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+ for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+ std::string value = it->rows[row];
+ if( it->label.empty() ) {
+ stream << label << ": ";
+ if( value != "0" )
+ stream << value;
+ else
+ stream << Colour( Colour::Warning ) << "- none -";
+ }
+ else if( value != "0" ) {
+ stream << Colour( Colour::LightGrey ) << " | ";
+ stream << Colour( it->colour )
+ << value << ' ' << it->label;
+ }
+ }
+ stream << '\n';
+ }
+
+ static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+ std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+ return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+ }
+ static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+ if( i > j && i > k )
+ return i;
+ else if( j > k )
+ return j;
+ else
+ return k;
+ }
+
+ void printTotalsDivider( Totals const& totals ) {
+ if( totals.testCases.total() > 0 ) {
+ std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+ std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+ std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+ while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )++;
+ while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+ findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+ stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+ stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+ if( totals.testCases.allPassed() )
+ stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+ else
+ stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+ }
+ else {
+ stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+ }
+ stream << '\n';
+ }
+ void printSummaryDivider() {
+ stream << getLineOfChars<'-'>() << '\n';
+ }
+
+ private:
+ bool m_headerPrinted;
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+ struct CompactReporter : StreamingReporterBase {
+
+ CompactReporter( ReporterConfig const& _config )
+ : StreamingReporterBase( _config )
+ {}
+
+ virtual ~CompactReporter();
+
+ static std::string getDescription() {
+ return "Reports test results on a single line, suitable for IDEs";
+ }
+
+ virtual ReporterPreferences getPreferences() const {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ virtual void noMatchingTestCases( std::string const& spec ) {
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+
+ virtual void assertionStarting( AssertionInfo const& ) {}
+
+ virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+ AssertionResult const& result = _assertionStats.assertionResult;
+
+ bool printInfoMessages = true;
+
+ // Drop out if result was successful and we're not printing those
+ if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+ if( result.getResultType() != ResultWas::Warning )
+ return false;
+ printInfoMessages = false;
+ }
+
+ AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+ printer.print();
+
+ stream << std::endl;
+ return true;
+ }
+
+ virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE {
+ if (m_config->showDurations() == ShowDurations::Always) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+ }
+ }
+
+ virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+ printTotals( _testRunStats.totals );
+ stream << '\n' << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+ class AssertionPrinter {
+ void operator= ( AssertionPrinter const& );
+ public:
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+ : stream( _stream )
+ , stats( _stats )
+ , result( _stats.assertionResult )
+ , messages( _stats.infoMessages )
+ , itMessage( _stats.infoMessages.begin() )
+ , printInfoMessages( _printInfoMessages )
+ {}
+
+ void print() {
+ printSourceInfo();
+
+ itMessage = messages.begin();
+
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ printResultType( Colour::ResultSuccess, passedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ if ( ! result.hasExpression() )
+ printRemainingMessages( Colour::None );
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if( result.isOk() )
+ printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+ else
+ printResultType( Colour::Error, failedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "unexpected exception with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::FatalErrorCondition:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "fatal error condition with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "expected exception, got none" );
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType( Colour::None, "info" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType( Colour::None, "warning" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "explicitly" );
+ printRemainingMessages( Colour::None );
+ break;
+ // These cases are here to prevent compiler warnings
+ case ResultWas::Unknown:
+ case ResultWas::FailureBit:
+ case ResultWas::Exception:
+ printResultType( Colour::Error, "** internal error **" );
+ break;
+ }
+ }
+
+ private:
+ // Colour::LightGrey
+
+ static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+ static const char* failedString() { return "FAILED"; }
+ static const char* passedString() { return "PASSED"; }
+#else
+ static const char* failedString() { return "failed"; }
+ static const char* passedString() { return "passed"; }
+#endif
+
+ void printSourceInfo() const {
+ Colour colourGuard( Colour::FileName );
+ stream << result.getSourceInfo() << ':';
+ }
+
+ void printResultType( Colour::Code colour, std::string const& passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ {
+ Colour colourGuard( colour );
+ stream << ' ' << passOrFail;
+ }
+ stream << ':';
+ }
+ }
+
+ void printIssue( std::string const& issue ) const {
+ stream << ' ' << issue;
+ }
+
+ void printExpressionWas() {
+ if( result.hasExpression() ) {
+ stream << ';';
+ {
+ Colour colour( dimColour() );
+ stream << " expression was:";
+ }
+ printOriginalExpression();
+ }
+ }
+
+ void printOriginalExpression() const {
+ if( result.hasExpression() ) {
+ stream << ' ' << result.getExpression();
+ }
+ }
+
+ void printReconstructedExpression() const {
+ if( result.hasExpandedExpression() ) {
+ {
+ Colour colour( dimColour() );
+ stream << " for: ";
+ }
+ stream << result.getExpandedExpression();
+ }
+ }
+
+ void printMessage() {
+ if ( itMessage != messages.end() ) {
+ stream << " '" << itMessage->message << '\'';
+ ++itMessage;
+ }
+ }
+
+ void printRemainingMessages( Colour::Code colour = dimColour() ) {
+ if ( itMessage == messages.end() )
+ return;
+
+ // using messages.end() directly yields compilation error:
+ std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+ const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+ {
+ Colour colourGuard( colour );
+ stream << " with " << pluralise( N, "message" ) << ':';
+ }
+
+ for(; itMessage != itEnd; ) {
+ // If this assertion is a warning ignore any INFO messages
+ if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+ stream << " '" << itMessage->message << '\'';
+ if ( ++itMessage != itEnd ) {
+ Colour colourGuard( dimColour() );
+ stream << " and";
+ }
+ }
+ }
+ }
+
+ private:
+ std::ostream& stream;
+ AssertionStats const& stats;
+ AssertionResult const& result;
+ std::vector<MessageInfo> messages;
+ std::vector<MessageInfo>::const_iterator itMessage;
+ bool printInfoMessages;
+ };
+
+ // Colour, message variants:
+ // - white: No tests ran.
+ // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
+ // - white: Passed [both/all] N test cases (no assertions).
+ // - red: Failed N tests cases, failed M assertions.
+ // - green: Passed [both/all] N tests cases with M assertions.
+
+ std::string bothOrAll( std::size_t count ) const {
+ return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
+ }
+
+ void printTotals( const Totals& totals ) const {
+ if( totals.testCases.total() == 0 ) {
+ stream << "No tests ran.";
+ }
+ else if( totals.testCases.failed == totals.testCases.total() ) {
+ Colour colour( Colour::ResultError );
+ const std::string qualify_assertions_failed =
+ totals.assertions.failed == totals.assertions.total() ?
+ bothOrAll( totals.assertions.failed ) : std::string();
+ stream <<
+ "Failed " << bothOrAll( totals.testCases.failed )
+ << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << qualify_assertions_failed <<
+ pluralise( totals.assertions.failed, "assertion" ) << '.';
+ }
+ else if( totals.assertions.total() == 0 ) {
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.total() )
+ << pluralise( totals.testCases.total(), "test case" )
+ << " (no assertions).";
+ }
+ else if( totals.assertions.failed ) {
+ Colour colour( Colour::ResultError );
+ stream <<
+ "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
+ "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
+ }
+ else {
+ Colour colour( Colour::ResultSuccess );
+ stream <<
+ "Passed " << bothOrAll( totals.testCases.passed )
+ << pluralise( totals.testCases.passed, "test case" ) <<
+ " with " << pluralise( totals.assertions.passed, "assertion" ) << '.';
+ }
+ }
+ };
+
+ INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+ // These are all here to avoid warnings about not having any out of line
+ // virtual methods
+ NonCopyable::~NonCopyable() {}
+ IShared::~IShared() {}
+ IStream::~IStream() CATCH_NOEXCEPT {}
+ FileStream::~FileStream() CATCH_NOEXCEPT {}
+ CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+ DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
+ StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+ IContext::~IContext() {}
+ IResultCapture::~IResultCapture() {}
+ ITestCase::~ITestCase() {}
+ ITestCaseRegistry::~ITestCaseRegistry() {}
+ IRegistryHub::~IRegistryHub() {}
+ IMutableRegistryHub::~IMutableRegistryHub() {}
+ IExceptionTranslator::~IExceptionTranslator() {}
+ IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+ IReporter::~IReporter() {}
+ IReporterFactory::~IReporterFactory() {}
+ IReporterRegistry::~IReporterRegistry() {}
+ IStreamingReporter::~IStreamingReporter() {}
+ AssertionStats::~AssertionStats() {}
+ SectionStats::~SectionStats() {}
+ TestCaseStats::~TestCaseStats() {}
+ TestGroupStats::~TestGroupStats() {}
+ TestRunStats::~TestRunStats() {}
+ CumulativeReporterBase::SectionNode::~SectionNode() {}
+ CumulativeReporterBase::~CumulativeReporterBase() {}
+
+ StreamingReporterBase::~StreamingReporterBase() {}
+ ConsoleReporter::~ConsoleReporter() {}
+ CompactReporter::~CompactReporter() {}
+ IRunner::~IRunner() {}
+ IMutableContext::~IMutableContext() {}
+ IConfig::~IConfig() {}
+ XmlReporter::~XmlReporter() {}
+ JunitReporter::~JunitReporter() {}
+ TestRegistry::~TestRegistry() {}
+ FreeFunctionTestCase::~FreeFunctionTestCase() {}
+ IGeneratorInfo::~IGeneratorInfo() {}
+ IGeneratorsForTest::~IGeneratorsForTest() {}
+ WildcardPattern::~WildcardPattern() {}
+ TestSpec::Pattern::~Pattern() {}
+ TestSpec::NamePattern::~NamePattern() {}
+ TestSpec::TagPattern::~TagPattern() {}
+ TestSpec::ExcludedPattern::~ExcludedPattern() {}
+ Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {}
+
+ void Config::dummy() {}
+
+ namespace TestCaseTracking {
+ ITracker::~ITracker() {}
+ TrackerBase::~TrackerBase() {}
+ SectionTracker::~SectionTracker() {}
+ IndexTracker::~IndexTracker() {}
+ }
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+// Standard C/C++ Win32 Unicode wmain entry point
+extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+#endif
+
+ int result = Catch::Session().run( argc, argv );
+ return ( result < 0xff ? result : 0xff );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+ Catch::registerTestMethods();
+ int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+ [pool drain];
+#endif
+
+ return ( result < 0xff ? result : 0xff );
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+# undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#else
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+ #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+ #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+ #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+ #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#else
+ #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
+ #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+ #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" )
+#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+
+#else
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#else
+#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+ #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+ #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+ #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
+ #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+ #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+ #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" )
+#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(pop)
+# else
+# pragma clang diagnostic pop
+# endif
+#elif defined __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/test/unit/cpp/common/SubSystemsManager.cpp b/test/unit/cpp/common/SubSystemsManager.cpp
index 660dad5..405e799 100644
--- a/test/unit/cpp/common/SubSystemsManager.cpp
+++ b/test/unit/cpp/common/SubSystemsManager.cpp
@@ -21,26 +21,25 @@
// Unit tests for SubSystemsManager
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
-using namespace dolfin;
-
-// Test rewritten using Google Test
-TEST(TestSubSystemsManager, test_petsc_user_init)
+namespace
{
- // Test user initialisation of PETSc
+ void init_petsc()
+ {
+ // Test user initialisation of PETSc
#ifdef HAS_PETSC
- int argc = 0;
- char **argv = NULL;
- PetscInitialize(&argc, &argv, NULL, NULL);
+ int argc = 0;
+ char **argv = NULL;
+ PetscInitialize(&argc, &argv, NULL, NULL);
- UnitSquareMesh(12, 12);
- PETScVector(MPI_COMM_WORLD, 30);
+ dolfin::UnitSquareMesh(12, 12);
+ dolfin::PETScVector(MPI_COMM_WORLD, 30);
#endif
+ }
}
-// Test all
-int SubSystemsManager_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+TEST_CASE( "Initialise PETSc", "[petsc_init]" )
+{
+ CHECK_NOTHROW(init_petsc());
}
diff --git a/test/unit/cpp/function/Expression.cpp b/test/unit/cpp/function/Expression.cpp
index 468b216..b2e6966 100644
--- a/test/unit/cpp/function/Expression.cpp
+++ b/test/unit/cpp/function/Expression.cpp
@@ -15,78 +15,71 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// Modified by Garth N. Wells, 2008.
-// Modified by Johannes Ring, 2009.
-// Modified by Benjamin Kehlet 2012
-//
-// First added: 2007-05-24
-// Last changed: 2014-08-12
-//
// Unit tests for the function library
#include <dolfin.h>
#include "Projection.h"
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-//-----------------------------------------------------------------------------
-TEST(Eval, testArbitraryEval)
+namespace
{
- class F0 : public Expression
+ void arbitrary_eval()
{
- public:
- F0() {}
- void eval(Array<double>& values, const Array<double>& x) const
- { values[0] = sin(3.0*x[0])*sin(3.0*x[1])*sin(3.0*x[2]); }
- };
+ class F0 : public Expression
+ {
+ public:
+ F0() {}
+ void eval(Array<double>& values, const Array<double>& x) const
+ { values[0] = sin(3.0*x[0])*sin(3.0*x[1])*sin(3.0*x[2]); }
+ };
- class F1 : public Expression
- {
- public:
- F1() {}
- void eval(Array<double>& values, const Array<double>& x) const
- { values[0] = 1.0 + 3.0*x[0] + 4.0*x[1] + 0.5*x[2]; }
- };
+ class F1 : public Expression
+ {
+ public:
+ F1() {}
+ void eval(Array<double>& values, const Array<double>& x) const
+ { values[0] = 1.0 + 3.0*x[0] + 4.0*x[1] + 0.5*x[2]; }
+ };
- auto mesh = std::make_shared<UnitCubeMesh>(8, 8, 8);
+ auto mesh = std::make_shared<UnitCubeMesh>(8, 8, 8);
- Array<double> x(3);
- x[0] = 0.31; x[1] = 0.32; x[2] = 0.33;
+ Array<double> x(3);
+ x[0] = 0.31; x[1] = 0.32; x[2] = 0.33;
- Array<double> u0(1);
- Array<double> u1(1);
+ Array<double> u0(1);
+ Array<double> u1(1);
- // User-defined functions (one from finite element space, one not)
- F0 f0;
- auto f1 = std::make_shared<F1>();
+ // User-defined functions (one from finite element space, one not)
+ F0 f0;
+ auto f1 = std::make_shared<F1>();
- // Test evaluation of a user-defined function
- f0.eval(u0, x);
- ASSERT_NEAR(u0[0],
- sin(3.0*x[0])*sin(3.0*x[1])*sin(3.0*x[2]),
- DOLFIN_EPS);
+ // Test evaluation of a user-defined function
+ f0.eval(u0, x);
+ CHECK(u0[0] == Approx(sin(3.0*x[0])*sin(3.0*x[1])*sin(3.0*x[2])));
- // Test for single core only
- if (dolfin::MPI::size(mesh->mpi_comm()) == 1)
- {
+ // Test for single core only
+ if (dolfin::MPI::size(mesh->mpi_comm()) == 1)
+ {
// Test evaluation of a discrete function
- auto V = std::make_shared<Projection::FunctionSpace>(mesh);
- Projection::BilinearForm a(V, V);
- Projection::LinearForm L(V);
- L.f = f1;
- Function g(V);
- solve(a == L, g);
+ auto V = std::make_shared<Projection::FunctionSpace>(mesh);
+ Projection::BilinearForm a(V, V);
+ Projection::LinearForm L(V);
+ L.f = f1;
+ Function g(V);
+ solve(a == L, g);
- const double tol = 1.0e-6;
- f1->eval(u0, x);
- g.eval(u1, x);
- ASSERT_NEAR(u0[0], u1[0], tol);
+ const double tol = 1.0e-6;
+ f1->eval(u0, x);
+ g.eval(u1, x);
+ CHECK(std::abs(u0[0] - u1[0]) < tol);
+ }
}
}
-// Test all
-int Expression_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+//-----------------------------------------------------------------------------
+TEST_CASE( "Test arbitray eval", "[eval]" )
+{
+ CHECK_NOTHROW(arbitrary_eval());
}
diff --git a/test/unit/cpp/geometry/ConvexTriangulation.cpp b/test/unit/cpp/geometry/ConvexTriangulation.cpp
new file mode 100644
index 0000000..973c533
--- /dev/null
+++ b/test/unit/cpp/geometry/ConvexTriangulation.cpp
@@ -0,0 +1,287 @@
+// Copyright (C) 2017 Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// Unit tests for convex triangulation
+
+#include <dolfin/geometry/ConvexTriangulation.h>
+#include <dolfin/geometry/CollisionPredicates.h>
+#include <dolfin/geometry/predicates.h>
+#include <catch.hpp>
+
+using namespace dolfin;
+
+namespace
+{
+ bool pure_triangular(const std::vector<std::vector<Point>>& triangulation, std::size_t dim)
+ {
+ for (const std::vector<Point>& tri : triangulation)
+ {
+
+ if (tri.size() != dim+1)
+ return false;
+ }
+
+ return true;
+ }
+
+ bool has_degenerate(const std::vector<std::vector<Point>>& triangulation, std::size_t dim)
+ {
+ for (const std::vector<Point>& tri : triangulation)
+ {
+ if (orient3d(tri[0], tri[1], tri[2], tri[3]) == 0)
+ return true;
+ }
+ return false;
+ }
+
+ bool triangulation_selfintersects(const std::vector<std::vector<Point>>& triangulation,
+ std::size_t dim)
+ {
+ for (std::size_t i = 0; i < triangulation.size(); i++)
+ {
+ const auto& t1 = triangulation[i];
+ for (std::size_t j = i+1; j < triangulation.size(); j++)
+ {
+ const auto& t2 = triangulation[j];
+
+ // Count number of shared vertices
+ std::size_t shared_vertices = 0;
+ std::set<std::size_t> t1_shared;
+ std::set<std::size_t> t2_shared;
+ for (std::size_t v1 = 0; v1 < dim+1; v1++)
+ {
+ for (std::size_t v2 = 0; v2 < dim+1; v2++)
+ {
+ if (t1[v1] == t2[v2])
+ {
+ shared_vertices++;
+ t1_shared.insert(v1);
+ t2_shared.insert(v2);
+ }
+ }
+ }
+
+ if (dim == 3)
+ {
+ if (shared_vertices == 0 &&
+ CollisionPredicates::collides_tetrahedron_tetrahedron_3d(t1[0], t1[1], t1[2], t1[3],
+ t2[0], t2[1], t2[2], t2[3]))
+ {
+ return true;
+ }
+ else if (shared_vertices > 0)
+ {
+
+ for (std::size_t a = 0; a < dim+1; a++)
+ {
+ // None of the non-shared vertices should collide with the other tet
+ if (t1_shared.count(a) == 0 &&
+ CollisionPredicates::collides_tetrahedron_point_3d(t2[0], t2[1], t2[2], t2[3],
+ t1[a]))
+ {
+ return true;
+ }
+
+ if (t2_shared.count(a) == 0 &&
+ CollisionPredicates::collides_tetrahedron_point_3d(t1[0], t1[1], t1[2], t1[3],
+ t2[a]))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ else if(dim == 2)
+ {
+ if (shared_vertices == 0 &&
+ CollisionPredicates::collides_triangle_triangle_2d(t1[0],
+ t1[1],
+ t1[2],
+ t2[0],
+ t2[1],
+ t2[2]))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ double triangulation_volume(const std::vector<std::vector<dolfin::Point>>& triangulation)
+ {
+ double vol = 0;
+ for (const std::vector<dolfin::Point>& tri : triangulation)
+ {
+ const Point& x0 = tri[0];
+ const Point& x1 = tri[1];
+ const Point& x2 = tri[2];
+ const Point& x3 = tri[3];
+ // Formula for volume from http://mathworld.wolfram.com
+ const double v = (x0[0]*(x1[1]*x2[2] + x3[1]*x1[2] + x2[1]*x3[2]
+ - x2[1]*x1[2] - x1[1]*x3[2] - x3[1]*x2[2])
+ - x1[0]*(x0[1]*x2[2] + x3[1]*x0[2] + x2[1]*x3[2]
+ - x2[1]*x0[2] - x0[1]*x3[2] - x3[1]*x2[2])
+ + x2[0]*(x0[1]*x1[2] + x3[1]*x0[2] + x1[1]*x3[2]
+ - x1[1]*x0[2] - x0[1]*x3[2] - x3[1]*x1[2])
+ - x3[0]*(x0[1]*x1[2] + x1[1]*x2[2] + x2[1]*x0[2]
+ - x1[1]*x0[2] - x2[1]*x1[2] - x0[1]*x2[2]));
+ vol += std::abs(v);
+ }
+
+ return vol/6;
+ }
+}
+
+//-----------------------------------------------------------------------------
+TEST_CASE("Convex triangulation test")
+{
+ SECTION("test trivial case]")
+ {
+ std::vector<Point> input = {{Point(0,0,0),
+ Point(0,0,1),
+ Point(0,1,0),
+ Point(1,0,0)}};
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+ std::size_t expected_size = 1;
+
+ CHECK(tri.size() == expected_size);
+ CHECK(triangulation_volume(tri) == Approx(1.0/6.0));
+ }
+
+ SECTION("test trivial case 2")
+ {
+ std::vector<Point> input = {{Point(0,0,0),
+ Point(0,0,1),
+ Point(0,1,0),
+ Point(0,1,1),
+ Point(1,0,0),
+ Point(1,0,1),
+ Point(1,1,0),
+ Point(1,1,1)}};
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+
+ CHECK(pure_triangular(tri, 3));
+ CHECK_FALSE(has_degenerate(tri, 3));
+ CHECK_FALSE(triangulation_selfintersects(tri, 3));
+ CHECK(triangulation_volume(tri) == Approx(1.0));
+ }
+
+ SECTION("test coplanar points")
+ {
+ std::vector<Point> input {
+ Point(0, 0, 0),
+ Point(0, 0, 1),
+ Point(0, 1, 0),
+ Point(0, 1, 1),
+ Point(1, 0, 0),
+ Point(1, 0, 1),
+ Point(1, 1, 0),
+ Point(1, 1, 1),
+ Point(0.1, 0.1, 0)};
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+
+ CHECK(pure_triangular(tri, 3));
+ CHECK_FALSE(has_degenerate(tri, 3));
+ CHECK_FALSE(triangulation_selfintersects(tri, 3));
+ CHECK(triangulation_volume(tri) == Approx(1.0));
+ }
+
+ SECTION("test coplanar colinear points]")
+ {
+ std::vector<Point> input {
+ Point(0, 0, 0),
+ Point(0, 0, 1),
+ Point(0, 1, 0),
+ Point(0, 1, 1),
+ Point(1, 0, 0),
+ Point(1, 0, 1),
+ Point(1, 1, 0),
+ Point(1, 1, 1),
+ Point(0, 0.1, 0)};
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+
+ CHECK(pure_triangular(tri, 3));
+ CHECK_FALSE(has_degenerate(tri, 3));
+ CHECK_FALSE(triangulation_selfintersects(tri, 3));
+ CHECK(triangulation_volume(tri) == Approx(1.0));
+ }
+
+ SECTION("test failing case")
+ {
+ std::vector<Point> input {
+ Point(0.7, 0.6, 0.1),
+ Point(0.7, 0.6, 0.5),
+ Point(0.1, 0.1, 0.1),
+ Point(0.8333333333333333, 0.8333333333333333, 0),
+ Point(0.1, 0.15, 0.1),
+ Point(0.1, 0.45, 0.1),
+ Point(0.16, 0.15, 0.1),
+ Point(0.61, 0.525, 0.1),
+ Point(0.46, 0.6, 0.100000000000000006) };
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+
+ CHECK(pure_triangular(tri, 3));
+ CHECK_FALSE(has_degenerate(tri, 3));
+ CHECK_FALSE(triangulation_selfintersects(tri, 3));
+ }
+
+ SECTION("test failing case 2")
+ {
+ std::vector<Point> input {
+ Point(0.7, 0.6, 0.5),
+ Point(0.7, 0.1, 0.1),
+ Point(0.8, 0, 0),
+ Point (0.1, 0.1, 0.1),
+ Point(0.16, 0.1, 0.1),
+ Point(0.592, 0.1, 0.1),
+ Point (0.16, 0.1, 0.14),
+ Point (0.52, 0.1, 0.38),
+ Point (0.7, 0.1, 0.38)};
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+
+ CHECK(pure_triangular(tri, 3));
+ CHECK_FALSE(has_degenerate(tri, 3));
+ CHECK_FALSE(triangulation_selfintersects(tri, 3));
+ }
+
+ SECTION("test failing case 3")
+ {
+ std::vector<Point> input {
+ Point (0.495926, 0.512037, 0.144444),
+ Point (0.376482, 0.519121, 0.284321),
+ Point (0.386541, 0.599783, 0.0609262),
+ Point (0.388086, 0.60059, 0.0607155),
+ Point (0.7, 0.6, 0.5),
+ Point (0.504965, 0.504965, 0.0447775),
+ Point (0.833333, 0.833333, 0)};
+
+ std::vector<std::vector<Point>> tri = ConvexTriangulation::triangulate_graham_scan_3d(input);
+
+ CHECK(pure_triangular(tri, 3));
+ CHECK_FALSE(has_degenerate(tri, 3));
+ CHECK_FALSE(triangulation_selfintersects(tri, 3));
+ }
+}
+//-----------------------------------------------------------------------------
diff --git a/test/unit/cpp/io/XMLMeshData.cpp b/test/unit/cpp/io/XMLMeshData.cpp
index 5027374..709ca70 100644
--- a/test/unit/cpp/io/XMLMeshData.cpp
+++ b/test/unit/cpp/io/XMLMeshData.cpp
@@ -14,62 +14,60 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2012-05-25
-// Last changed:
-//
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-//-----------------------------------------------------------------------------
-TEST(XMLMeshDataIO, test_write_read)
+namespace
{
- // XML mesh output is not supported in parallel. Add test for
- // parallel with HDF5 when ready.
- if (dolfin::MPI::size(MPI_COMM_WORLD) == 1)
+ void xml_mesh_data()
{
- const std::size_t value = 10;
+ // XML mesh output is not supported in parallel. Add test for
+ // parallel with HDF5 when ready.
+ if (dolfin::MPI::size(MPI_COMM_WORLD) == 1)
{
- UnitSquareMesh mesh(2, 2);
+ const std::size_t value = 10;
+ {
+ UnitSquareMesh mesh(2, 2);
- // Create some mesh data
- std::vector<std::size_t>& data0 = mesh.data().create_array("v", 0);
- data0.resize(mesh.num_entities(0), value);
+ // Create some mesh data
+ std::vector<std::size_t>& data0 = mesh.data().create_array("v", 0);
+ data0.resize(mesh.num_entities(0), value);
- mesh.init(1);
- std::vector<std::size_t>& data1 = mesh.data().create_array("e", 1);
- data1.resize(mesh.num_entities(1), value);
+ mesh.init(1);
+ std::vector<std::size_t>& data1 = mesh.data().create_array("e", 1);
+ data1.resize(mesh.num_entities(1), value);
- std::vector<std::size_t>& data2 = mesh.data().create_array("c", 2);
- data2.resize(mesh.num_entities(2), value);
+ std::vector<std::size_t>& data2 = mesh.data().create_array("c", 2);
+ data2.resize(mesh.num_entities(2), value);
- File file("mesh_data.xml");
- file << mesh;
- }
+ File file("mesh_data.xml");
+ file << mesh;
+ }
- {
- // Read mesh from file
- Mesh mesh("mesh_data.xml");
+ {
+ // Read mesh from file
+ Mesh mesh("mesh_data.xml");
- // Access mesh data and check
- const std::vector<std::size_t>& data0 = mesh.data().array("v", 0);
- ASSERT_EQ(data0.size(), mesh.num_entities(0));
- ASSERT_EQ(data0[2], value);
- const std::vector<std::size_t>& data1 = mesh.data().array("e", 1);
- ASSERT_EQ(data1.size(), mesh.num_entities(1));
- ASSERT_EQ(data1[2], value);
- const std::vector<std::size_t>& data2 = mesh.data().array("c", 2);
- ASSERT_EQ(data2.size(), mesh.num_entities(2));
- ASSERT_EQ(data2[2], value);
+ // Access mesh data and check
+ const std::vector<std::size_t>& data0 = mesh.data().array("v", 0);
+ CHECK(data0.size() == mesh.num_entities(0));
+ CHECK(data0[2] == value);
+ const std::vector<std::size_t>& data1 = mesh.data().array("e", 1);
+ CHECK(data1.size() == mesh.num_entities(1));
+ CHECK(data1[2] == value);
+ const std::vector<std::size_t>& data2 = mesh.data().array("c", 2);
+ CHECK(data2.size() == mesh.num_entities(2));
+ CHECK(data2[2] == value);
+ }
}
}
}
-// Test all
-int XMLMeshData_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
\ No newline at end of file
+//-----------------------------------------------------------------------------
+TEST_CASE("Test XML mesh data", "[xml_mesh_data]")
+{
+ CHECK_NOTHROW(xml_mesh_data());
+}
diff --git a/test/unit/cpp/io/XMLMeshValueCollection.cpp b/test/unit/cpp/io/XMLMeshValueCollection.cpp
index d177b0e..0e6b48d 100644
--- a/test/unit/cpp/io/XMLMeshValueCollection.cpp
+++ b/test/unit/cpp/io/XMLMeshValueCollection.cpp
@@ -14,28 +14,24 @@
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2007-05-29
-// Last changed: 2012-01-12
-//
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
//-----------------------------------------------------------------------------
-TEST(MeshValueCollectionIO, test_read)
+TEST_CASE("MeshValueCollection IO test", "[!hide][meshvaluecollection_io]")
{
// Create mesh and read file
// Link to the mesh is quite long...
auto mesh = std::make_shared<UnitCubeMesh>(5, 5, 5);
- MeshValueCollection<std::size_t>
- markers(mesh, "./test/unit/cpp/io/xml_value_collection_ref.xml");
+ MeshValueCollection<std::size_t> markers(mesh, "./io/xml_value_collection_ref.xml");
+ //MeshValueCollection<std::size_t>
+ // markers(mesh, "./test/unit/cpp/io/xml_value_collection_ref.xml");
// Check size
- ASSERT_EQ(dolfin::MPI::sum(mesh->mpi_comm(), markers.size()),
- (std::size_t) 6);
+ CHECK(dolfin::MPI::sum(mesh->mpi_comm(), markers.size()) == (std::size_t) 6);
// Check sum of values
const std::map<std::pair<std::size_t, std::size_t>, std::size_t>&
@@ -43,11 +39,6 @@ TEST(MeshValueCollectionIO, test_read)
std::size_t sum = 0;
for (auto it = values.begin(); it != values.end(); ++it)
sum += it->second;
- ASSERT_EQ(dolfin::MPI::sum(mesh->mpi_comm(), sum), (std::size_t) 48);
-}
-// Test all
-int XMLMeshValueCollection_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ CHECK(dolfin::MPI::sum(mesh->mpi_comm(), sum) == (std::size_t) 48);
}
diff --git a/test/unit/cpp/la/LinearOperator.cpp b/test/unit/cpp/la/LinearOperator.cpp
index 9a4da01..28ec42e 100644
--- a/test/unit/cpp/la/LinearOperator.cpp
+++ b/test/unit/cpp/la/LinearOperator.cpp
@@ -15,27 +15,22 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// Modified by Johannes Ring 2012
-//
-// First added: 2012-08-21
-// Last changed: 2012-09-19
-//
// Unit tests for matrix-free linear solvers (LinearOperator)
#include <dolfin.h>
#include "forms/ReactionDiffusion.h"
#include "forms/ReactionDiffusionAction.h"
-
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-// Backends supporting the LinearOperator interface
-static std::vector<std::string> backends = {"PETSc", "Eigen"};
-
//-----------------------------------------------------------------------------
-TEST(TestLinearOperator, test_linear_operator)
+TEST_CASE("Testing LinearOperator", "[linear_operator]")
{
+ // Backends supporting the LinearOperator interface
+ //std::vector<std::string> backends = {"PETSc", "Eigen"};
+ std::vector<std::string> backends = {"PETSc"};
+
// Define linear operator
class MyLinearOperator : public LinearOperator
{
@@ -68,59 +63,53 @@ TEST(TestLinearOperator, test_linear_operator)
};
- // Iterate over backends supporting linear operators
- for (std::size_t i = 0; i < backends.size(); i++)
+ SECTION("using linear operator")
{
- // Check whether backend is available
- if (!has_linear_algebra_backend(backends[i]))
- continue;
-
- // Skip testing Eigen in parallel
- if (dolfin::MPI::size(MPI_COMM_WORLD) > 1
- && backends[i] == "Eigen")
+ // Iterate over backends supporting linear operators
+ for (std::size_t i = 0; i < backends.size(); i++)
{
- info("Not running Eigen test in parallel");
- continue;
+ // Check whether backend is available
+ if (!has_linear_algebra_backend(backends[i]))
+ continue;
+
+ // Skip testing Eigen in parallel
+ if (dolfin::MPI::size(MPI_COMM_WORLD) > 1
+ && backends[i] == "Eigen")
+ {
+ info("Not running Eigen test in parallel");
+ continue;
+ }
+
+ // Set linear algebra backend
+ parameters["linear_algebra_backend"] = backends[i];
+
+ // Compute reference value by solving ordinary linear system
+ auto mesh = std::make_shared<UnitSquareMesh>(8, 8);
+ auto V = std::make_shared<ReactionDiffusion::FunctionSpace>(mesh);
+ ReactionDiffusion::BilinearForm a(V, V);
+ ReactionDiffusion::LinearForm L(V);
+ auto f = std::make_shared<Constant>(1.0);
+ L.f = f;
+ Matrix A;
+ Vector x, b;
+ assemble(A, a);
+ assemble(b, L);
+ solve(A, x, b, "gmres", "none");
+ const double norm_ref = norm(x, "l2");
+
+ //continue;
+
+ // Solve using linear operator defined by form action
+ ReactionDiffusionAction::LinearForm a_action(V);
+ auto u = std::make_shared<Function>(V);
+ a_action.u = u;
+ MyLinearOperator O(a_action, *u);
+ solve(O, x, b, "gmres", "none");
+ const double norm_action = norm(x, "l2");
+
+ // Check results
+ CHECK(norm_ref == Approx(norm_action));
}
-
- // Set linear algebra backend
- parameters["linear_algebra_backend"] = backends[i];
-
- // Compute reference value by solving ordinary linear system
- auto mesh = std::make_shared<UnitSquareMesh>(8, 8);
- auto V = std::make_shared<ReactionDiffusion::FunctionSpace>(mesh);
- ReactionDiffusion::BilinearForm a(V, V);
- ReactionDiffusion::LinearForm L(V);
- auto f = std::make_shared<Constant>(1.0);
- L.f = f;
- Matrix A;
- Vector x, b;
- assemble(A, a);
- assemble(b, L);
- solve(A, x, b, "gmres", "none");
- const double norm_ref = norm(x, "l2");
-
- continue;
-
- // Solve using linear operator defined by form action
- ReactionDiffusionAction::LinearForm a_action(V);
- auto u = std::make_shared<Function>(V);
- a_action.u = u;
- MyLinearOperator O(a_action, *u);
- solve(O, x, b, "gmres", "none");
- const double norm_action = norm(x, "l2");
-
- // Check results
- ASSERT_NEAR(norm_ref, norm_action, 1e-10);
}
}
-
-// Test all
-int LinearOperator_main(int argc, char **argv) {
- // Add backends supporting the LinearOperator interface
- backends.push_back("PETSc");
- backends.push_back("Eigen");
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
//-----------------------------------------------------------------------------
diff --git a/test/unit/cpp/la/Vector.cpp b/test/unit/cpp/la/Vector.cpp
index 31b70b3..570fb3a 100644
--- a/test/unit/cpp/la/Vector.cpp
+++ b/test/unit/cpp/la/Vector.cpp
@@ -15,164 +15,161 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// First added: 2008-09-30
-// Last changed: 2012-08-21
-//
// Unit tests Selected methods for GenericVector
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-//----------------------------------------------------
-void _test_operators(MPI_Comm comm)
+namespace
{
- Vector v(comm, 10), u(comm, 10);
- v = 0.0;
- u = 0.0;
- ASSERT_EQ(v.sum(), 0.0);
+ void _test_operators(MPI_Comm comm)
+ {
+ Vector v(comm, 10), u(comm, 10);
+ v = 0.0;
+ u = 0.0;
+ CHECK(v.sum() == 0.0);
- // operator=(double a)
- v = 1.0;
- ASSERT_EQ(v.sum(), v.size());
+ // operator=(double a)
+ v = 1.0;
+ CHECK(v.sum() == v.size());
- // operator=(const GenericVector& x)
- u = v;
- ASSERT_EQ(u.sum(), u.size());
+ // operator=(const GenericVector& x)
+ u = v;
+ CHECK(u.sum() == u.size());
- // operator+=(const GenericVector& x)
- u += v;
- ASSERT_EQ(u.sum(), 2*u.size());
+ // operator+=(const GenericVector& x)
+ u += v;
+ CHECK(u.sum() == 2*u.size());
- // operator-=(const GenericVector& x)
- u -= v;
- u -= v;
- ASSERT_EQ(u.sum(), 0.0);
+ // operator-=(const GenericVector& x)
+ u -= v;
+ u -= v;
+ CHECK(u.sum() == 0.0);
// operator*=(double a)
- v *= 5.0;
- ASSERT_EQ(v.sum(), v.size()*5.0);
+ v *= 5.0;
+ CHECK(v.sum() == v.size()*5.0);
- // operator/=(double a)
- v /= 2.0;
- ASSERT_EQ(v.sum(), 2.5*v.size());
+ // operator/=(double a)
+ v /= 2.0;
+ CHECK(v.sum() == 2.5*v.size());
- // operator*=(const GenericVector& x)
- u = 2.0;
- v*=u;
- ASSERT_EQ(v.sum(), v.size()*5.0);
+ // operator*=(const GenericVector& x)
+ u = 2.0;
+ v*=u;
+ CHECK(v.sum() == v.size()*5.0);
+ }
}
-//----------------------------------------------------
-TEST(TestVector, test_backends)
-{
- // Eigen
- parameters["linear_algebra_backend"] = "Eigen";
- _test_operators(MPI_COMM_SELF);
- // PETSc
-#ifdef HAS_PETSC
- parameters["linear_algebra_backend"] = "PETSc";
- _test_operators(MPI_COMM_WORLD);
-#endif
-}
-//----------------------------------------------------
-TEST(TestVector, test_init)
+
+TEST_CASE("Test Vector", "[test_vector]")
{
- // Create local and distributed vector layouts
-
- // Create local vector layout
- TensorLayout layout_local(MPI_COMM_SELF, 0, TensorLayout::Sparsity::DENSE);
- std::vector<std::shared_ptr<const IndexMap>> index_maps(1);
- index_maps[0].reset(new IndexMap(MPI_COMM_SELF, 203, 1));
- layout_local.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
-
- // Create distributed vector layout
- TensorLayout layout_distributed(MPI_COMM_WORLD, 0, TensorLayout::Sparsity::DENSE);
- auto lrange = dolfin::MPI::local_range(MPI_COMM_WORLD, 203);
- std::size_t nlocal = lrange.second - lrange.first;
- index_maps[0].reset(new IndexMap(MPI_COMM_SELF, nlocal, 1));
- layout_distributed.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
-
- // Vector
-#ifdef HAS_PETSC
- parameters["linear_algebra_backend"] = "PETSc";
+ SECTION("backends")
{
- Vector x;
- x.init(layout_local);
- ASSERT_EQ(x.size(), (std::size_t) 203);
+ // Eigen
+ parameters["linear_algebra_backend"] = "Eigen";
+ _test_operators(MPI_COMM_SELF);
- Vector y;
- y.init(layout_distributed);
- ASSERT_EQ(x.size(), (std::size_t) 203);
- }
+ // PETSc
+#ifdef HAS_PETSC
+ parameters["linear_algebra_backend"] = "PETSc";
+ _test_operators(MPI_COMM_WORLD);
#endif
-
- // Eigen
- {
- EigenVector x;
- x.init(layout_local);
- ASSERT_EQ(x.size(), (std::size_t) 203);
}
- // PETSc
-#ifdef HAS_PETSC
+ SECTION("init")
{
- PETScVector x;
- x.init(layout_local);
- ASSERT_EQ(x.size(), (std::size_t) 203);
-
- PETScVector y;
- y.init(layout_distributed);
- ASSERT_EQ(y.size(), (std::size_t) 203);
- }
-#endif
-}
-//-----------------------------------------------------------------------------
-TEST(TestVector, test_get_local_empty)
-{
- // Create local and distributed vector layouts
- const std::vector<std::size_t> dims(1, 203);
-
- // Create local vector layout
- TensorLayout layout_local(MPI_COMM_SELF, 0, TensorLayout::Sparsity::DENSE);
- std::vector<std::shared_ptr<const IndexMap>> index_maps(1);
- index_maps[0].reset(new IndexMap(MPI_COMM_SELF, 203, 1));
- layout_local.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
-
- // Create distributed vector layout
- TensorLayout layout_distributed(MPI_COMM_WORLD, 0, TensorLayout::Sparsity::DENSE);
- auto lrange = dolfin::MPI::local_range(MPI_COMM_WORLD, 203);
- std::size_t nlocal = lrange.second - lrange.first;
- index_maps[0].reset(new IndexMap(MPI_COMM_SELF, nlocal, 1));
- layout_distributed.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
-
- // Vector
+ // Create local and distributed vector layouts
+
+ // Create local vector layout
+ TensorLayout layout_local(MPI_COMM_SELF, 0, TensorLayout::Sparsity::DENSE);
+ std::vector<std::shared_ptr<const IndexMap>> index_maps(1);
+ index_maps[0].reset(new IndexMap(MPI_COMM_SELF, 203, 1));
+ layout_local.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
+
+ // Create distributed vector layout
+ TensorLayout layout_distributed(MPI_COMM_WORLD, 0, TensorLayout::Sparsity::DENSE);
+ auto lrange = dolfin::MPI::local_range(MPI_COMM_WORLD, 203);
+ std::size_t nlocal = lrange.second - lrange.first;
+ index_maps[0].reset(new IndexMap(MPI_COMM_SELF, nlocal, 1));
+ layout_distributed.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
+
+ // Vector
#ifdef HAS_PETSC
- parameters["linear_algebra_backend"] = "PETSc";
- {
- Vector x;
- x.init(layout_local);
- ASSERT_EQ(x.size(), (std::size_t) 203);
-
- Vector y;
- y.init(layout_distributed);
- ASSERT_EQ(y.size(), (std::size_t) 203);
-
- //:get_local(double* block, std::size_t m,
- // const dolfin::la_index* rows) const
+ parameters["linear_algebra_backend"] = "PETSc";
+ {
+ Vector x;
+ x.init(layout_local);
+ CHECK(x.size() == (std::size_t) 203);
+
+ Vector y;
+ y.init(layout_distributed);
+ CHECK(x.size() == (std::size_t) 203);
+ }
+#endif
- double* block = NULL;
- dolfin::la_index* rows = NULL;
- x.get_local(block, 0, rows);
- y.get_local(block, 0, rows);
+ // Eigen
+ {
+ EigenVector x;
+ x.init(layout_local);
+ CHECK(x.size() == (std::size_t) 203);
+ }
+ // PETSc
+#ifdef HAS_PETSC
+ {
+ PETScVector x;
+ x.init(layout_local);
+ CHECK(x.size() == (std::size_t) 203);
+
+ PETScVector y;
+ y.init(layout_distributed);
+ CHECK(y.size() == (std::size_t) 203);
+ }
+#endif
}
+
+ SECTION("test_get_local_empty")
+ {
+ // Create local and distributed vector layouts
+ const std::vector<std::size_t> dims(1, 203);
+
+ // Create local vector layout
+ TensorLayout layout_local(MPI_COMM_SELF, 0, TensorLayout::Sparsity::DENSE);
+ std::vector<std::shared_ptr<const IndexMap>> index_maps(1);
+ index_maps[0].reset(new IndexMap(MPI_COMM_SELF, 203, 1));
+ layout_local.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
+
+ // Create distributed vector layout
+ TensorLayout layout_distributed(MPI_COMM_WORLD, 0, TensorLayout::Sparsity::DENSE);
+ auto lrange = dolfin::MPI::local_range(MPI_COMM_WORLD, 203);
+ std::size_t nlocal = lrange.second - lrange.first;
+ index_maps[0].reset(new IndexMap(MPI_COMM_SELF, nlocal, 1));
+ layout_distributed.init(index_maps, TensorLayout::Ghosts::UNGHOSTED);
+
+ // Vector
+#ifdef HAS_PETSC
+ parameters["linear_algebra_backend"] = "PETSc";
+ {
+ Vector x;
+ x.init(layout_local);
+ CHECK(x.size() == (std::size_t) 203);
+
+ Vector y;
+ y.init(layout_distributed);
+ CHECK(y.size() == (std::size_t) 203);
+
+ //:get_local(double* block, std::size_t m,
+ // const dolfin::la_index* rows) const
+
+ double* block = NULL;
+ dolfin::la_index* rows = NULL;
+ x.get_local(block, 0, rows);
+ y.get_local(block, 0, rows);
+ }
#endif
-}
+ }
-// Test all
-int Vector_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
}
diff --git a/test/unit/cpp/main.cpp b/test/unit/cpp/main.cpp
new file mode 100644
index 0000000..1c77b13
--- /dev/null
+++ b/test/unit/cpp/main.cpp
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include "catch/catch.hpp"
diff --git a/test/unit/cpp/mesh/Mesh.cpp b/test/unit/cpp/mesh/Mesh.cpp
index 74bb211..5ad7ab8 100644
--- a/test/unit/cpp/mesh/Mesh.cpp
+++ b/test/unit/cpp/mesh/Mesh.cpp
@@ -15,237 +15,238 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// Modified by Benjamin Kehlet 2012
-//
-// First added: 2007-05-14
-// Last changed: 2012-11-12
-//
// Unit tests for the mesh library
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
//-----------------------------------------------------------------------------
-TEST(SimpleShapesTest, testUnitSquareMesh)
+TEST_CASE("Simple shapes test")
{
- // Create mesh of unit square
- UnitSquareMesh mesh(5, 7);
- ASSERT_EQ(mesh.num_vertices(), (std::size_t) 48);
- ASSERT_EQ(mesh.num_cells(), (std::size_t) 70);
+ SECTION("Test UnitSquareMesh")
+ {
+ // Create mesh of unit square
+ UnitSquareMesh mesh(5, 7);
+ CHECK(mesh.num_vertices() == (std::size_t) 48);
+ CHECK(mesh.num_cells() == (std::size_t) 70);
- // Create mesh of unit square
- auto mesh1 = UnitSquareMesh::create({{5, 7}});
- ASSERT_EQ(mesh1.num_vertices(), (std::size_t) 48);
- ASSERT_EQ(mesh1.num_cells(), (std::size_t) 70);
+ // Create mesh of unit square
+ auto mesh1 = UnitSquareMesh::create({{5, 7}}, CellType::Type::triangle);
+ CHECK(mesh1.num_vertices() == (std::size_t) 48);
+ CHECK(mesh1.num_cells() == (std::size_t) 70);
+ }
+ SECTION("Test UnitCubeMesh")
+ {
+ // Create mesh of unit cube
+ UnitCubeMesh mesh(5, 7, 9);
+ CHECK(mesh.num_vertices() == (std::size_t) 480);
+ CHECK(mesh.num_cells() == (std::size_t) 1890);
+
+ // Create mesh of unit cube
+ auto mesh1 = UnitCubeMesh::create({{5, 7, 9}}, CellType::Type::tetrahedron);
+ CHECK(mesh1.num_vertices() == (std::size_t) 480);
+ CHECK(mesh1.num_cells() == (std::size_t) 1890);
+ }
}
-//-----------------------------------------------------------------------------
-TEST(SimpleShapesTest, testUnitCubeMesh)
-{
- // Create mesh of unit cube
- UnitCubeMesh mesh(5, 7, 9);
- ASSERT_EQ(mesh.num_vertices(), (std::size_t) 480);
- ASSERT_EQ(mesh.num_cells(), (std::size_t) 1890);
-
- // Create mesh of unit cube
- auto mesh1 = UnitCubeMesh::create({{5, 7, 9}});
- ASSERT_EQ(mesh1.num_vertices(), (std::size_t) 480);
- ASSERT_EQ(mesh1.num_cells(), (std::size_t) 1890);
-}
-//-----------------------------------------------------------------------------
-TEST(MeshRefinement, testRefineUnitSquareMesh)
-{
- // Refine mesh of unit square
- UnitSquareMesh mesh0(5, 7);
- Mesh mesh1 = refine(mesh0);
- ASSERT_EQ(mesh1.num_vertices(), (std::size_t) 165);
- ASSERT_EQ(mesh1.num_cells(), (std::size_t) 280);
-}
-//-----------------------------------------------------------------------------
-TEST(MeshRefinement, testRefineUnitCubeMesh)
-{
- // Refine mesh of unit cube
- UnitCubeMesh mesh0(5, 7, 9);
- Mesh mesh1 = refine(mesh0);
- ASSERT_EQ(mesh1.num_vertices(), (std::size_t) 3135);
- ASSERT_EQ(mesh1.num_cells(), (std::size_t) 15120);
-}
-//-----------------------------------------------------------------------------
-TEST(MeshIterators, testVertexIterators)
-{
- // Iterate over vertices
- UnitCubeMesh mesh(5, 5, 5);
- unsigned int n = 0;
- for (VertexIterator v(mesh); !v.end(); ++v)
- n++;
- ASSERT_EQ(n, mesh.num_vertices());
-}
-//-----------------------------------------------------------------------------
-TEST(MeshIterators, testEdgeIterators)
-{
- // Iterate over edges
- UnitCubeMesh mesh(5, 5, 5);
- unsigned int n = 0;
- for (EdgeIterator e(mesh); !e.end(); ++e)
- n++;
- ASSERT_EQ(n, mesh.num_edges());
-}
-//-----------------------------------------------------------------------------
-TEST(MeshIterators, testFaceIterators)
-{
- // Iterate over faces
- UnitCubeMesh mesh(5, 5, 5);
- unsigned int n = 0;
- for (FaceIterator f(mesh); !f.end(); ++f)
- n++;
- ASSERT_EQ(n, mesh.num_faces());
-}
-//-----------------------------------------------------------------------------
-TEST(MeshIterators, testFacetIterators)
-{
- // Iterate over facets
- UnitCubeMesh mesh(5, 5, 5);
- unsigned int n = 0;
- for (FacetIterator f(mesh); !f.end(); ++f)
- n++;
- ASSERT_EQ(n, mesh.num_facets());
-}
-//-----------------------------------------------------------------------------
-TEST(MeshIterators, testCellIterators)
-{
- // Iterate over cells
- UnitCubeMesh mesh(5, 5, 5);
- unsigned int n = 0;
- for (CellIterator c(mesh); !c.end(); ++c)
- n++;
- ASSERT_EQ(n, mesh.num_cells());
+
+TEST_CASE("Mesh refinement")
+{
+ SECTION("Test refine UnitSquareMesh")
+ {
+ // Refine mesh of unit square
+ UnitSquareMesh mesh0(5, 7);
+ Mesh mesh1 = refine(mesh0);
+ CHECK(mesh1.num_vertices() == (std::size_t) 165);
+ CHECK(mesh1.num_cells() == (std::size_t) 280);
+ }
+
+ SECTION("Test refine UnitCubeMesh")
+ {
+ // Refine mesh of unit cube
+ UnitCubeMesh mesh0(5, 7, 9);
+ Mesh mesh1 = refine(mesh0);
+ CHECK(mesh1.num_vertices() == (std::size_t) 3135);
+ CHECK(mesh1.num_cells() == (std::size_t) 15120);
+ }
}
-//-----------------------------------------------------------------------------
-TEST(MeshIterators, testMixedIterators)
-{
- // Iterate over vertices of cells
- UnitCubeMesh mesh(5, 5, 5);
- unsigned int n = 0;
- for (CellIterator c(mesh); !c.end(); ++c)
- for (VertexIterator v(*c); !v.end(); ++v)
+
+TEST_CASE("Mesh iterators")
+{
+ SECTION("Test vertex iterators")
+ {
+ // Iterate over vertices
+ UnitCubeMesh mesh(5, 5, 5);
+ unsigned int n = 0;
+ for (VertexIterator v(mesh); !v.end(); ++v)
n++;
- ASSERT_EQ(n, 4*mesh.num_cells());
-}
-//-----------------------------------------------------------------------------
-TEST(BoundaryExtraction, testBoundaryComputation)
-{
- // Compute boundary of mesh
- UnitCubeMesh mesh(2, 2, 2);
- BoundaryMesh boundary(mesh, "exterior");
- ASSERT_EQ(boundary.num_vertices(), (std::size_t) 26);
- ASSERT_EQ(boundary.num_cells(), (std::size_t) 48);
-}
-//-----------------------------------------------------------------------------
-TEST(BoundaryExtraction, testBoundaryBoundary)
-{
- // Compute boundary of boundary
- //
- // Note that we can't do
- //
- // BoundaryMesh b0(mesh);
- // BoundaryMesh b1(b0);
- //
- // since b1 would then be a copy of b0 (copy
- // constructor in Mesh will be used).
-
- UnitCubeMesh mesh(2, 2, 2);
- BoundaryMesh b0(mesh, "exterior");
- b0.order();
- BoundaryMesh b1(b0, "exterior");
- ASSERT_EQ(b1.num_vertices(), (std::size_t) 0);
- ASSERT_EQ(b1.num_cells(), (std::size_t) 0);
-}
-//-----------------------------------------------------------------------------
-TEST(MeshFunctions, testAssign)
-{
- /// Assign value of mesh function
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- MeshFunction<int> f(mesh, 0);
- f[3] = 10;
- Vertex v(*mesh, 3);
- ASSERT_EQ(f[v], 10);
-}
-//-----------------------------------------------------------------------------
-TEST(InputOutput, testMeshXML2D)
-{
- // Write and read 2D mesh to/from file
- UnitSquareMesh mesh_out(3, 3);
- Mesh mesh_in;
- File file("unitsquare.xml");
- file << mesh_out;
- file >> mesh_in;
- ASSERT_EQ(mesh_in.num_vertices(), (std::size_t) 16);
-}
-//-----------------------------------------------------------------------------
-TEST(InputOutput, testMeshXML3D)
-{
- // Write and read 3D mesh to/from file
- UnitCubeMesh mesh_out(3, 3, 3);
- Mesh mesh_in;
- File file("unitcube.xml");
- file << mesh_out;
- file >> mesh_in;
- ASSERT_EQ(mesh_in.num_vertices(), (std::size_t) 64);
-}
-//-----------------------------------------------------------------------------
-TEST(InputOutput, testMeshFunction)
-{
- // Write and read mesh function to/from file
- auto mesh = std::make_shared<UnitSquareMesh>(1, 1);
- MeshFunction<int> f(mesh, 0);
- f[0] = 2;
- f[1] = 4;
- f[2] = 6;
- f[3] = 8;
- File file("meshfunction.xml");
- file << f;
- MeshFunction<int> g(mesh, 0);
- file >> g;
- for (VertexIterator v(*mesh); !v.end(); ++v)
- ASSERT_EQ(f[*v], g[*v]);
-}
-//-----------------------------------------------
-TEST(PyCCInterface, testGetGeometricalDimension)
-{
- // Get geometrical dimension of mesh
- UnitSquareMesh mesh(5, 5);
- ASSERT_EQ(mesh.geometry().dim(), (std::size_t) 2);
+ CHECK(n == mesh.num_vertices());
+ }
+
+ SECTION("Test edge iterators")
+ {
+ // Iterate over edges
+ UnitCubeMesh mesh(5, 5, 5);
+ unsigned int n = 0;
+ for (EdgeIterator e(mesh); !e.end(); ++e)
+ n++;
+
+ CHECK(n == mesh.num_edges());
+ }
+
+ SECTION("Test face iterators")
+ {
+ // Iterate over faces
+ UnitCubeMesh mesh(5, 5, 5);
+ unsigned int n = 0;
+ for (FaceIterator f(mesh); !f.end(); ++f)
+ n++;
+
+ CHECK(n == mesh.num_faces());
+ }
+
+ SECTION("Test facet iterators")
+ {
+ // Iterate over facets
+ UnitCubeMesh mesh(5, 5, 5);
+ unsigned int n = 0;
+ for (FacetIterator f(mesh); !f.end(); ++f)
+ n++;
+
+ CHECK(n == mesh.num_facets());
+ }
+
+ SECTION("Test cell iterators")
+ {
+ // Iterate over cells
+ UnitCubeMesh mesh(5, 5, 5);
+ unsigned int n = 0;
+ for (CellIterator c(mesh); !c.end(); ++c)
+ n++;
+
+ CHECK(n == mesh.num_cells());
+ }
+
+ SECTION("Test mixed iterators")
+ {
+ // Iterate over vertices of cells
+ UnitCubeMesh mesh(5, 5, 5);
+ unsigned int n = 0;
+ for (CellIterator c(mesh); !c.end(); ++c)
+ for (VertexIterator v(*c); !v.end(); ++v)
+ n++;
+
+ CHECK(n == 4*mesh.num_cells());
+ }
+
+ SECTION("Test boundary computation")
+ {
+ // Compute boundary of mesh
+ UnitCubeMesh mesh(2, 2, 2);
+ BoundaryMesh boundary(mesh, "exterior");
+ CHECK(boundary.num_vertices() == (std::size_t) 26);
+ CHECK(boundary.num_cells() == (std::size_t) 48);
+ }
}
-//-----------------------------------------------------------------------------
-TEST(PyCCInterface, testGetCoordinates)
-{
- // Get coordinates of vertices
- UnitSquareMesh mesh(5, 5);
- ASSERT_EQ(mesh.geometry().num_vertices(), (std::size_t) 36);
+
+TEST_CASE("Boundary extraction")
+{
+ SECTION("Test boundary of boundary")
+ {
+ // Compute boundary of boundary
+ //
+ // Note that we can't do
+ //
+ // BoundaryMesh b0(mesh);
+ // BoundaryMesh b1(b0);
+ //
+ // since b1 would then be a copy of b0 (copy
+ // constructor in Mesh will be used).
+
+ UnitCubeMesh mesh(2, 2, 2);
+ BoundaryMesh b0(mesh, "exterior");
+ b0.order();
+ BoundaryMesh b1(b0, "exterior");
+ CHECK(b1.num_vertices() == (std::size_t) 0);
+ CHECK(b1.num_cells() == (std::size_t) 0);
+ }
+
+ SECTION("Test assign")
+ {
+ /// Assign value of mesh function
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ MeshFunction<int> f(mesh, 0);
+ f[3] = 10;
+ Vertex v(*mesh, 3);
+ CHECK(f[v] == 10);
+ }
}
-//-----------------------------------------------------------------------------
-TEST(PyCCInterface, testGetCells)
-{
- // Get cells of mesh
- UnitSquareMesh mesh(5, 5);
- ASSERT_EQ(mesh.topology().size(2), (std::size_t) 50);
+
+TEST_CASE("InputOutput")
+{
+ SECTION("Test mesh XML 2D")
+ {
+ // Write and read 2D mesh to/from file
+ UnitSquareMesh mesh_out(3, 3);
+ Mesh mesh_in;
+ File file("unitsquare.xml");
+ file << mesh_out;
+ file >> mesh_in;
+ CHECK(mesh_in.num_vertices() == (std::size_t) 16);
+ }
+
+ SECTION("Test mesh XML 3D")
+ {
+ // Write and read 3D mesh to/from file
+ UnitCubeMesh mesh_out(3, 3, 3);
+ Mesh mesh_in;
+ File file("unitcube.xml");
+ file << mesh_out;
+ file >> mesh_in;
+ CHECK(mesh_in.num_vertices() == (std::size_t) 64);
+ }
+
+ SECTION("Test MeshFunction")
+ {
+ // Write and read mesh function to/from file
+ auto mesh = std::make_shared<UnitSquareMesh>(1, 1);
+ MeshFunction<int> f(mesh, 0);
+ f[0] = 2;
+ f[1] = 4;
+ f[2] = 6;
+ f[3] = 8;
+ File file("meshfunction.xml");
+ file << f;
+ MeshFunction<int> g(mesh, 0);
+ file >> g;
+ for (VertexIterator v(*mesh); !v.end(); ++v)
+ CHECK(f[*v] == g[*v]);
+ }
}
-// Test all
-int Mesh_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
+TEST_CASE("PyCCInterface")
+{
+ SECTION("Test get geometrical dimension")
+ {
+ // Get geometrical dimension of mesh
+ UnitSquareMesh mesh(5, 5);
+ CHECK(mesh.geometry().dim() == (std::size_t) 2);
+ }
- // FIXME: Only the following test works in Parallel
- // Failed: SimpleShapes; MeshRefinement; BoundaryExtraction
- // MeshFunctions; InputOutput; PyCCInterface
- if (dolfin::MPI::size(MPI_COMM_WORLD) != 1)
- {
- ::testing::GTEST_FLAG(filter) = "MeshIterators.*";
- }
- return RUN_ALL_TESTS();
+ SECTION("Test get coordinates")
+ {
+ // Get coordinates of vertices
+ UnitSquareMesh mesh(5, 5);
+ CHECK(mesh.geometry().num_vertices() == (std::size_t) 36);
+ }
+ SECTION("Test get cells")
+ {
+ // Get cells of mesh
+ UnitSquareMesh mesh(5, 5);
+ CHECK(mesh.topology().size(2) == (std::size_t) 50);
+ }
}
-//-----------------------------------------------------------------------------
diff --git a/test/unit/cpp/mesh/MeshColoring.cpp b/test/unit/cpp/mesh/MeshColoring.cpp
index d62af9a..1952e3b 100644
--- a/test/unit/cpp/mesh/MeshColoring.cpp
+++ b/test/unit/cpp/mesh/MeshColoring.cpp
@@ -18,46 +18,42 @@
// Unit tests for MeshColoring
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-//-----------------------------------------------------------------------------
-TEST(MeshColoring, test_mesh_coloring)
+TEST_CASE("MeshColoring")
{
- // Create mesh
- auto mesh = std::make_shared<UnitCubeMesh>(24, 24, 24);
-
- // Compute vertex-based coloring
- mesh->color("vertex");
- const MeshFunction<std::size_t> colors_vertex
- = MeshColoring::cell_colors(mesh, "vertex");
-
- // Compute edge-based coloring
- mesh->color("edge");
- const CellFunction<std::size_t> colors_edge
- = MeshColoring::cell_colors(mesh, "edge");
-
- // Compute facet-based coloring
- mesh->color("facet");
- const CellFunction<std::size_t> colors_facet
- = MeshColoring::cell_colors(mesh, "facet");
-
- // Compute facet-based coloring with distance 2
- std::vector<std::size_t> coloring_type
- = {{mesh->topology().dim(),
- mesh->topology().dim() - 1,
- mesh->topology().dim(),
- mesh->topology().dim() - 1,
- mesh->topology().dim()}};
- mesh->color(coloring_type);
- const CellFunction<std::size_t> colors_vertex_2
- = MeshColoring::cell_colors(mesh, coloring_type);
-}
-// Test all
-int MeshColoring_main(int argc, char **argv)
-{
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ SECTION("mesh coloring computation")
+ {
+ // Create mesh
+ auto mesh = std::make_shared<UnitCubeMesh>(24, 24, 24);
+
+ // Compute vertex-based coloring
+ mesh->color("vertex");
+ const MeshFunction<std::size_t> colors_vertex
+ = MeshColoring::cell_colors(mesh, "vertex");
+
+ // Compute edge-based coloring
+ mesh->color("edge");
+ const MeshFunction<std::size_t> colors_edge
+ = MeshColoring::cell_colors(mesh, "edge");
+
+ // Compute facet-based coloring
+ mesh->color("facet");
+ const MeshFunction<std::size_t> colors_facet
+ = MeshColoring::cell_colors(mesh, "facet");
+
+ // Compute facet-based coloring with distance 2
+ std::vector<std::size_t> coloring_type
+ = {{mesh->topology().dim(),
+ mesh->topology().dim() - 1,
+ mesh->topology().dim(),
+ mesh->topology().dim() - 1,
+ mesh->topology().dim()}};
+ mesh->color(coloring_type);
+ const MeshFunction<std::size_t> colors_vertex_2
+ = MeshColoring::cell_colors(mesh, coloring_type);
+ }
}
diff --git a/test/unit/cpp/mesh/MeshFunction.cpp b/test/unit/cpp/mesh/MeshFunction.cpp
index e099363..85526ca 100644
--- a/test/unit/cpp/mesh/MeshFunction.cpp
+++ b/test/unit/cpp/mesh/MeshFunction.cpp
@@ -15,71 +15,64 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// First added: 2013-05-30
-// Last changed:
-//
// Unit tests for MeshFunction
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-//-----------------------------------------------------------------------------
-TEST(MeshFunctions, test_create_from_domains)
+TEST_CASE("MeshFunctions")
{
- // Create mesh
- std::shared_ptr<Mesh> mesh(new UnitSquareMesh(3, 3));
- dolfin_assert(mesh);
+ SECTION("Test create from domains")
+ {
+ // Create mesh
+ std::shared_ptr<Mesh> mesh(new UnitSquareMesh(3, 3));
+ dolfin_assert(mesh);
- const std::size_t D = mesh->topology().dim();
+ const std::size_t D = mesh->topology().dim();
- // Test setting all values
- for (std::size_t d = 0; d <= D; ++d)
- {
- // Create MeshDomains object
- MeshDomains mesh_domains;
- mesh_domains.init(D);
+ // Test setting all values
+ for (std::size_t d = 0; d <= D; ++d)
+ {
+ // Create MeshDomains object
+ MeshDomains mesh_domains;
+ mesh_domains.init(D);
- mesh->init(d);
+ mesh->init(d);
- // Build mesh domain
- std::map<std::size_t, std::size_t>& domain = mesh_domains.markers(d);
- for (std::size_t i = 0; i < mesh->num_entities(d); ++i)
- domain.insert(std::make_pair(i, i));
+ // Build mesh domain
+ std::map<std::size_t, std::size_t>& domain = mesh_domains.markers(d);
+ for (std::size_t i = 0; i < mesh->num_entities(d); ++i)
+ domain.insert(std::make_pair(i, i));
- // Create MeshFunction and test values
- MeshFunction<std::size_t> mf(mesh, d, mesh_domains);
- for (std::size_t i = 0; i < mf.size(); ++i)
- ASSERT_EQ(mf[i], i);
- }
+ // Create MeshFunction and test values
+ MeshFunction<std::size_t> mf(mesh, d, mesh_domains);
+ for (std::size_t i = 0; i < mf.size(); ++i)
+ CHECK(mf[i] == i);
+ }
- // Test setting some values only
- for (std::size_t d = 0; d <= D; ++d)
- {
- // Create MeshDomains object
- MeshDomains mesh_domains;
- mesh_domains.init(D);
+ // Test setting some values only
+ for (std::size_t d = 0; d <= D; ++d)
+ {
+ // Create MeshDomains object
+ MeshDomains mesh_domains;
+ mesh_domains.init(D);
- mesh->init(d);
+ mesh->init(d);
- // Build mesh domain
- std::map<std::size_t, std::size_t>& domain = mesh_domains.markers(d);
- const std::size_t num_entities = mesh->num_entities(d);
- for (std::size_t i = num_entities/2; i < num_entities; ++i)
- domain.insert(std::make_pair(i, i));
+ // Build mesh domain
+ std::map<std::size_t, std::size_t>& domain = mesh_domains.markers(d);
+ const std::size_t num_entities = mesh->num_entities(d);
+ for (std::size_t i = num_entities/2; i < num_entities; ++i)
+ domain.insert(std::make_pair(i, i));
- // Create MeshFunction and test values
- MeshFunction<std::size_t> mf(mesh, d, mesh_domains);
- for (std::size_t i = 0; i < num_entities/2; ++i)
- ASSERT_EQ(mf[i], std::numeric_limits<std::size_t>::max());
- for (std::size_t i = num_entities/2; i < mf.size(); ++i)
- ASSERT_EQ(mf[i], i);
+ // Create MeshFunction and test values
+ MeshFunction<std::size_t> mf(mesh, d, mesh_domains);
+ for (std::size_t i = 0; i < num_entities/2; ++i)
+ CHECK(mf[i] == std::numeric_limits<std::size_t>::max());
+ for (std::size_t i = num_entities/2; i < mf.size(); ++i)
+ CHECK(mf[i] == i);
+ }
}
}
-
-// Test all
-int MeshFunction_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
\ No newline at end of file
diff --git a/test/unit/cpp/mesh/MeshValueCollection.cpp b/test/unit/cpp/mesh/MeshValueCollection.cpp
index eac0477..8e2ef3b 100644
--- a/test/unit/cpp/mesh/MeshValueCollection.cpp
+++ b/test/unit/cpp/mesh/MeshValueCollection.cpp
@@ -15,160 +15,151 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// First added: 2007-05-14
-// Last changed: 2012-01-11
-//
// Unit tests for the mesh library
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-//-----------------------------------------------------------------------------
-TEST(MeshValueCollections, testAssign2DCells)
-{
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- const std::size_t ncells = mesh->num_cells();
- MeshValueCollection<int> f(mesh, 2);
- bool all_new = true;
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
- {
- bool this_new;
- const int value = ncells - cell->index();
- this_new = f.set_value(cell->index(), value);
- all_new = all_new && this_new;
- }
- MeshValueCollection<int> g(mesh, 2);
- g = f;
- ASSERT_EQ(ncells, f.size());
- ASSERT_EQ(ncells, g.size());
- ASSERT_TRUE(all_new);
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
- {
- const int value = ncells - cell->index();
- ASSERT_EQ(value, g.get_value(cell->index(), 0));
- }
-}
-//-----------------------------------------------------------------------------
-TEST(MeshValueCollections, testAssign2DFacets)
+TEST_CASE("MeshValueCollections")
{
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- mesh->init(2,1);
- const std::size_t ncells = mesh->num_cells();
- MeshValueCollection<int> f(mesh, 1);
- bool all_new = true;
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ SECTION("Test assign 2D cells")
{
- const int value = ncells - cell->index();
- for (std::size_t i = 0; i < cell->num_entities(1); ++i)
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ const std::size_t ncells = mesh->num_cells();
+ MeshValueCollection<int> f(mesh, 2);
+ bool all_new = true;
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
{
bool this_new;
- this_new = f.set_value(cell->index(), i, value + i);
+ const int value = ncells - cell->index();
+ this_new = f.set_value(cell->index(), value);
all_new = all_new && this_new;
}
- }
- MeshValueCollection<int> g(mesh, 1);
- g = f;
- ASSERT_EQ(ncells*3, f.size());
- ASSERT_EQ(ncells*3, g.size());
- ASSERT_TRUE(all_new);
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
- {
- for (std::size_t i = 0; i < cell->num_entities(1); ++i)
+ MeshValueCollection<int> g(mesh, 2);
+ g = f;
+ CHECK(ncells == f.size());
+ CHECK(ncells == g.size());
+ CHECK(all_new);
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
{
- const int value = ncells - cell->index() + i;
- ASSERT_EQ(value, g.get_value(cell->index(), i));
+ const int value = ncells - cell->index();
+ CHECK(value == g.get_value(cell->index(), 0));
}
}
-}
-//-----------------------------------------------------------------------------
-TEST(MeshValueCollections, testAssign2DVertices)
-{
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- mesh->init(2, 0);
- const std::size_t ncells = mesh->num_cells();
- MeshValueCollection<int> f(mesh, 0);
- bool all_new = true;
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
+
+ SECTION("Test assign 2D facets")
{
- const int value = ncells - cell->index();
- for (std::size_t i = 0; i < cell->num_entities(0); ++i)
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ mesh->init(2,1);
+ const std::size_t ncells = mesh->num_cells();
+ MeshValueCollection<int> f(mesh, 1);
+ bool all_new = true;
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
{
- bool this_new;
- this_new = f.set_value(cell->index(), i, value+i);
- all_new = all_new && this_new;
+ const int value = ncells - cell->index();
+ for (std::size_t i = 0; i < cell->num_entities(1); ++i)
+ {
+ bool this_new;
+ this_new = f.set_value(cell->index(), i, value + i);
+ all_new = all_new && this_new;
+ }
+ }
+ MeshValueCollection<int> g(mesh, 1);
+ g = f;
+ CHECK(ncells*3 == f.size());
+ CHECK(ncells*3 == g.size());
+ CHECK(all_new);
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ {
+ for (std::size_t i = 0; i < cell->num_entities(1); ++i)
+ {
+ const int value = ncells - cell->index() + i;
+ CHECK(value == g.get_value(cell->index(), i));
+ }
}
}
- MeshValueCollection<int> g(mesh, 0);
- g = f;
- ASSERT_EQ(ncells*3, f.size());
- ASSERT_EQ(ncells*3, g.size());
- ASSERT_TRUE(all_new);
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
+
+ SECTION("Test assign 2D vertices")
{
- for (std::size_t i = 0; i < cell->num_entities(0); ++i)
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ mesh->init(2, 0);
+ const std::size_t ncells = mesh->num_cells();
+ MeshValueCollection<int> f(mesh, 0);
+ bool all_new = true;
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ {
+ const int value = ncells - cell->index();
+ for (std::size_t i = 0; i < cell->num_entities(0); ++i)
+ {
+ bool this_new;
+ this_new = f.set_value(cell->index(), i, value+i);
+ all_new = all_new && this_new;
+ }
+ }
+ MeshValueCollection<int> g(mesh, 0);
+ g = f;
+ CHECK(ncells*3 == f.size());
+ CHECK(ncells*3 == g.size());
+ CHECK(all_new);
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
{
- const int value = ncells - cell->index() + i;
- ASSERT_EQ(value, g.get_value(cell->index(), i));
+ for (std::size_t i = 0; i < cell->num_entities(0); ++i)
+ {
+ const int value = ncells - cell->index() + i;
+ CHECK(value == g.get_value(cell->index(), i));
+ }
}
}
-}
-//-----------------------------------------------------------------------------
-TEST(MeshValueCollections, testMeshFunctionAssign2DCells)
-{
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- const std::size_t ncells = mesh->num_cells();
- MeshFunction<int> f(mesh, 2, 0);
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
- f[cell->index()] = ncells - cell->index();
- MeshValueCollection<int> g(mesh, 2);
- g = f;
- ASSERT_EQ(ncells, f.size());
- ASSERT_EQ(ncells, g.size());
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
+
+ SECTION("Test MeshFunction assign 2D cells")
{
- const int value = ncells - cell->index();
- ASSERT_EQ(value, g.get_value(cell->index(), 0));
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ const std::size_t ncells = mesh->num_cells();
+ MeshFunction<int> f(mesh, 2, 0);
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ f[cell->index()] = ncells - cell->index();
+ MeshValueCollection<int> g(mesh, 2);
+ g = f;
+ CHECK(ncells == f.size());
+ CHECK(ncells == g.size());
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ {
+ const int value = ncells - cell->index();
+ CHECK(value == g.get_value(cell->index(), 0));
+ }
}
-}
-//-----------------------------------------------------------------------------
-TEST(MeshValueCollections, testMeshFunctionAssign2DFacets)
-{
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- mesh->init(1);
- MeshFunction<int> f(mesh, 1, 25);
- MeshValueCollection<int> g(mesh, 1);
- g = f;
- ASSERT_EQ(mesh->num_facets(), f.size());
- ASSERT_EQ(mesh->num_cells()*3, g.size());
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
+
+ SECTION("Test MeshFunction assign 2D facets")
{
- for (std::size_t i = 0; i < cell->num_entities(1); ++i)
- ASSERT_EQ(25, g.get_value(cell->index(), i));
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ mesh->init(1);
+ MeshFunction<int> f(mesh, 1, 25);
+ MeshValueCollection<int> g(mesh, 1);
+ g = f;
+ CHECK(mesh->num_facets() == f.size());
+ CHECK(mesh->num_cells()*3 == g.size());
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ {
+ for (std::size_t i = 0; i < cell->num_entities(1); ++i)
+ CHECK(25 == g.get_value(cell->index(), i));
+ }
}
-}
-//-----------------------------------------------------------------------------
-TEST(MeshValueCollections, testMeshFunctionAssign2DVertices)
-{
- auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
- mesh->init(0);
- MeshFunction<int> f(mesh, 0, 25);
- MeshValueCollection<int> g(mesh, 0);
- g = f;
- ASSERT_EQ(mesh->num_vertices(), f.size());
- ASSERT_EQ(mesh->num_cells()*3, g.size());
- for (CellIterator cell(*mesh); !cell.end(); ++cell)
+
+ SECTION("Test MeshFunction assign 2D vertices")
{
- for (std::size_t i = 0; i < cell->num_entities(0); ++i)
- ASSERT_EQ(25, g.get_value(cell->index(), i));
+ auto mesh = std::make_shared<UnitSquareMesh>(3, 3);
+ mesh->init(0);
+ MeshFunction<int> f(mesh, 0, 25);
+ MeshValueCollection<int> g(mesh, 0);
+ g = f;
+ CHECK(mesh->num_vertices() == f.size());
+ CHECK(mesh->num_cells()*3 == g.size());
+ for (CellIterator cell(*mesh); !cell.end(); ++cell)
+ {
+ for (std::size_t i = 0; i < cell->num_entities(0); ++i)
+ CHECK(25 == g.get_value(cell->index(), i));
+ }
}
}
-
-
-// Test all
-int MeshValueCollection_main(int argc, char **argv) {
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
-
diff --git a/test/unit/cpp/mesh/MultiMesh.cpp b/test/unit/cpp/mesh/MultiMesh.cpp
new file mode 100644
index 0000000..bebef70
--- /dev/null
+++ b/test/unit/cpp/mesh/MultiMesh.cpp
@@ -0,0 +1,91 @@
+// Copyright (C) 2017 August Johansson, Benjamin Kehlet
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// Unit tests for the multimesh functionality
+
+// These tests currently only instantiates and initializes some
+// multimesh objects, ie. the only thing that is tested is that it
+// doesn't crash
+
+#include <dolfin.h>
+#include <catch.hpp>
+
+using namespace dolfin;
+
+TEST_CASE("MultiMesh", "[!hide]")
+{
+ SECTION("Trivial test case 3D")
+ {
+ // DISABLED
+
+ // FIXME: Enable this test when it is working
+
+ std::shared_ptr<Mesh> background(new Mesh(UnitTetrahedronMesh::create()));
+ std::shared_ptr<Mesh> overlapping(new Mesh(UnitTetrahedronMesh::create()));
+ overlapping->translate(Point(.1, .1, .1));
+
+ MultiMesh multimesh(background, overlapping, 1);
+ }
+
+ SECTION("Trivial case 3D 2")
+ {
+ // FIXME: Enable this test when it is working
+
+ std::shared_ptr<Mesh> background = std::make_shared<UnitCubeMesh>(1,1,1);
+
+ std::shared_ptr<Mesh> refmesh = std::make_shared<BoxMesh>(Point(0.394383, 0.783099, 0.197551),
+ Point(0.840188, 0.79844, 0.911647),
+ 1,1,1);
+ const std::vector<double>& refmeshcoords = refmesh->coordinates();
+
+ std::shared_ptr<Mesh> tetmesh(new Mesh(UnitTetrahedronMesh::create()));
+ std::vector<double>& tetmesh_coords = tetmesh->coordinates();
+
+ for (CellIterator cell(*refmesh); !cell.end(); ++cell)
+ {
+ const unsigned int* vertex_indices = cell->entities(0);
+
+ for (int i = 0; i < 4; i++)
+ {
+ tetmesh_coords[i*3] = refmeshcoords[vertex_indices[i]];
+ tetmesh_coords[i*3 + 1] = refmeshcoords[vertex_indices[i] + 1];
+ tetmesh_coords[i*3 + 2] = refmeshcoords[vertex_indices[i] + 2];
+ }
+
+ MultiMesh multimesh(background, tetmesh, 1);
+ }
+ }
+
+ SECTION("Takes forever")
+ {
+ // FIXME: Enable this test when it is working
+
+ std::shared_ptr<Mesh> background(new Mesh(UnitTetrahedronMesh::create()));
+ {
+ std::vector<double>& coords = background->coordinates();
+ coords = { 0, 0, 0,
+ 0, 0, 1,
+ 1, 0, 1,
+ 1, 1, 1 };
+ }
+
+ std::shared_ptr<Mesh> overlapping = std::make_shared<BoxMesh>(Point(0.394383, 0.783099, 0.197551),
+ Point(0.840188, 0.79844, 0.911647),
+ 1,1,1);
+ MultiMesh multimesh(background, overlapping, 1);
+ }
+}
diff --git a/test/unit/cpp/multimesh/MultiMesh.cpp b/test/unit/cpp/multimesh/MultiMesh.cpp
deleted file mode 100644
index 99a46d7..0000000
--- a/test/unit/cpp/multimesh/MultiMesh.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright (C) 2014 August Johansson and Anders Logg
-//
-// This file is part of DOLFIN.
-//
-// DOLFIN is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// DOLFIN is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-//
-// First added: 2014-03-10
-// Last changed: 2015-06-04
-//
-// Unit tests for MultiMesh
-
-#include <dolfin.h>
-#include <dolfin/geometry/SimplexQuadrature.h>
-#include <gtest/gtest.h>
-
-using namespace dolfin;
-
-// This test was commented out in the original file
-// TEST(MultiMeshes, test_multiple_meshes_quadrature) {
-// set_log_level(DBG);
-
-// // Create multimesh from three triangle meshes of the unit square
-
-// // Many meshes, but not more than three overlap => this works
-// UnitCubeMesh mesh_0(11, 12, 13);
-// BoxMesh mesh_1(Point(0.1, 0.1, 0.1), Point(0.9, 0.9, 0.9), 13, 11, 12);
-// BoxMesh mesh_2(Point(0.2, 0.2, 0.2), Point(0.95, 0.95, 0.8), 11, 13, 11);
-// BoxMesh mesh_3(Point(0.94, 0.01, 0.01), Point(0.98, 0.99, 0.99), 1, 11, 11);
-// BoxMesh mesh_4(Point(0.01, 0.01, 0.01), Point(0.02, 0.02, 0.02), 1, 1, 1);
-
-// // // Completely nested 2D: can't do no more than three meshes
-// // UnitSquareMesh mesh_0(1, 1);
-// // RectangleMesh mesh_1(Point(0.1, 0.1), Point(0.9, 0.9, 1, 1);
-// // RectangleMesh mesh_2(Point(0.2, 0.2), Point(0.8, 0.8, 1, 1);
-// // RectangleMesh mesh_3(Point(0.3, 0.3), Point(0.7, 0.7, 1, 1);
-// // RectangleMesh mesh_4(Point(0.4, 0.4), Point(0.6, 0.6, 1, 1);
-
-// // // Completely nested 3D: can't do no more than three meshes
-// // UnitCubeMesh mesh_0(2, 3, 4);
-// // BoxMesh mesh_1(Point(0.1, 0.1, 0.1), Point(0.9, 0.9, 0.9), 4, 3, 2);
-// // BoxMesh mesh_2(Point(0.2, 0.2, 0.2), Point(0.8, 0.8, 0.8), 3, 4, 3);
-// // BoxMesh mesh_3(Point(0.8, 0.01, 0.01), Point(0.9, 0.99, 0.99), 4, 2, 3);
-// // BoxMesh mesh_4(Point(0.01, 0.01, 0.01), Point(0.02, 0.02, 0.02), 1, 1, 1);
-
-// // Build the multimesh
-// MultiMesh multimesh;
-// multimesh.add(mesh_0);
-// multimesh.add(mesh_1);
-// multimesh.add(mesh_2);
-// multimesh.add(mesh_3);
-// multimesh.add(mesh_4);
-// multimesh.build();
-
-// // Exact volume is known
-// const double exact_volume = 1;
-// double volume = 0;
-
-// // Sum contribution from all parts
-// std::cout << "Sum contributions\n";
-// for (std::size_t part = 0; part < multimesh.num_parts(); part++)
-// {
-// std::cout << "% part " << part;
-// double part_volume = 0;
-
-// // Uncut cell volume given by function volume
-// const auto uncut_cells = multimesh.uncut_cells(part);
-// for (auto it = uncut_cells.begin(); it != uncut_cells.end(); ++it)
-// {
-// const Cell cell(*multimesh.part(part), *it);
-// volume += cell.volume();
-// part_volume += cell.volume();
-// }
-
-// std::cout << "\t uncut volume "<< part_volume<<' ';
-
-// // Cut cell volume given by quadrature rule
-// const auto& cut_cells = multimesh.cut_cells(part);
-// for (auto it = cut_cells.begin(); it != cut_cells.end(); ++it)
-// {
-// const auto& qr = multimesh.quadrature_rule_cut_cell(part, *it);
-// for (std::size_t i = 0; i < qr.second.size(); ++i)
-// {
-// volume += qr.second[i];
-// part_volume += qr.second[i];
-// }
-// }
-// std::cout << "\ttotal volume " << part_volume << std::endl;
-// }
-
-// std::cout<<std::setprecision(13) << "exact volume " << exact_volume<<'\n'
-// << "volume " << volume<<std::endl;
-// ASSERT_NEAR(exact_volume, volume, DOLFIN_EPS_LARGE);
-// }
-
-//-----------------------------------------------------------------------------
-TEST(MultiMeshes, test_multiple_meshes_interface_quadrature)
-{
- // // These three meshes are ok
- // UnitSquareMesh mesh_0(1, 1);
- // RectangleMesh mesh_1(Point(0.1, 0.1), Point(0.9, 0.9), 1, 1);
- // RectangleMesh mesh_2(Point(0.2, 0.2), Point(0.8, 0.8), 1, 1);
- // double exact_volume = 4*(0.9-0.1); // mesh0 and mesh1
- // exact_volume += 4*(0.8-0.2); // mesh1 and mesh2
-
- // UnitCubeMesh mesh_0(1, 2, 3);
- // BoxMesh mesh_1(Point(0.1, 0.1, 0.1), Point(0.9, 0.9, 0.9), 2,3,4); //2, 3, 4);
- // BoxMesh mesh_2(Point(-0.1, -0.1, -0.1), Point(0.7, 0.7, 0.7), 4, 3, 2);
- // BoxMesh mesh_3(Point(0.51, 0.51, 0.51), Point( 0.7, 0.7, 0.7), 1, 1, 1); //4, 3, 2);
- // BoxMesh mesh_4(Point(0.3, 0.3, 0.3), Point(0.7, 0.7, 0.7), 1, 1, 1);
- // double exact_volume = 0.8*0.8*6; // for mesh_0 and mesh_1
- // exact_volume += 0.4*0.4*6; // for mesh_1 and mesh_4
-
- auto mesh_0 = std::make_shared<UnitCubeMesh>(1, 1, 1);
- auto mesh_1 = std::make_shared<BoxMesh>(Point(0.1, 0.1, 0.1), Point(0.9, 0.9, 0.9), 1, 1, 1);
- auto mesh_2 = std::make_shared<BoxMesh>(Point(0.2, 0.2, 0.2), Point(0.8, 0.8, 0.8), 1, 1, 1);
- // BoxMesh mesh_3(Point(0.51, 0.51, 0.51), Point(0.7, 0.7, 0.7), 1, 1, 1); //4, 3, 2);
- // BoxMesh mesh_4(Point(0.3, 0.3, 0.3), Point(0.7, 0.7, 0.7), 1, 1, 1);
- double exact_volume = (0.9 - 0.1)*(0.9 - 0.1)*6; // for mesh_0 and mesh_1
- exact_volume += (0.8 - 0.2)*(0.8 - 0.2)*6; // mesh_1 and mesh_2
-
- // UnitCubeMesh mesh_0(1, 1, 1);
- // MeshEditor editor;
- // Mesh mesh_1;
- // editor.open(mesh_1, 3, 3);
- // editor.init_vertices(4);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(0.7, 0.1, -0.1));
- // editor.add_vertex(1, Point(0.7, 0.3, -0.1));
- // editor.add_vertex(2, Point(0.5, 0.1, -0.1));
- // editor.add_vertex(3, Point(0.7, 0.1, 0.1));
- // editor.add_cell(0, 0,1,2,3);
- // editor.close();
-
- // Mesh mesh_2;
- // editor.open(mesh_2, 3,3);
- // editor.init_vertices(4);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(0.7, 0.1, -0.2));
- // editor.add_vertex(1, Point(0.7, 0.3, -0.2));
- // editor.add_vertex(2, Point(0.5, 0.1, -0.2));
- // editor.add_vertex(3, Point(0.7, 0.1, 0.05));
- // editor.add_cell(0, 0,1,2,3);
- // editor.close();
-
- //double exact_volume = 0.8*0.8*6; // for mesh_0 and mesh_1
- //exact_volume += 0.4*0.4*6; // for mesh_1 and mesh_4
-
- // MeshEditor editor;
- // Mesh mesh_0;
- // editor.open(mesh_0, 2, 2);
- // editor.init_vertices(3);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(0.,0.));
- // editor.add_vertex(1, Point(2.,0.));
- // editor.add_vertex(2, Point(1.,2.));
- // editor.add_cell(0, 0,1,2);
- // editor.close();
-
- // Mesh mesh_1;
- // editor.open(mesh_1, 2, 2);
- // editor.init_vertices(3);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(0.,-0.5));
- // editor.add_vertex(1, Point(2.,-0.5));
- // editor.add_vertex(2, Point(1.,1.5));
- // editor.add_cell(0, 0,1,2);
- // editor.close();
-
- // Mesh mesh_2;
- // editor.open(mesh_2, 2, 2);
- // editor.init_vertices(3);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(0.,-1.));
- // editor.add_vertex(1, Point(2.,-1.));
- // editor.add_vertex(2, Point(1.,1.));
- // editor.add_cell(0, 0,1,2);
- // editor.close();
-
- // double exact_volume = 2*std::sqrt(0.75*0.75 + 1.5*1.5); // mesh_0 and mesh_1
- // exact_volume += 2*std::sqrt(0.5*0.5 + 1*1); // mesh_0 and mesh_2
- // exact_volume += 2*std::sqrt(0.75*0.75 + 1.5*1.5); // mesh_1and mesh_2
- // double volume = 0;
-
- // // These three meshes are ok.
- // MeshEditor editor;
- // Mesh mesh_0;
- // editor.open(mesh_0, 2, 2);
- // editor.init_vertices(3);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(0.,0.));
- // editor.add_vertex(1, Point(2.,0.));
- // editor.add_vertex(2, Point(1.,2.));
- // editor.add_cell(0, 0,1,2);
- // editor.close();
-
- // Mesh mesh_1;
- // editor.open(mesh_1, 2, 2);
- // editor.init_vertices(3);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(1.5,-2.));
- // editor.add_vertex(1, Point(4.,0.));
- // editor.add_vertex(2, Point(1.5,2));
- // editor.add_cell(0, 0,1,2);
- // editor.close();
-
- // Mesh mesh_2;
- // editor.open(mesh_2, 2, 2);
- // editor.init_vertices(3);
- // editor.init_cells(1);
- // editor.add_vertex(0, Point(3.,0.5));
- // editor.add_vertex(1, Point(-1.,0.5));
- // editor.add_vertex(2, Point(1.,-1.5));
- // editor.add_cell(0, 0,1,2);
- // editor.close();
-
- // double exact_volume = (1.5-0.25) + (1-0.5); // mesh_0, mesh_1 and mesh_2
- // exact_volume += (3-1.5) + std::sqrt(1.5*1.5 + 1.5*1.5); // mesh_1 and mesh_2
-
- File("mesh_0.xml") << *mesh_0;
- File("mesh_1.xml") << *mesh_1;
- File("mesh_2.xml") << *mesh_2;
-
- // Build the multimesh
- MultiMesh multimesh;
- multimesh.add(mesh_0);
- multimesh.add(mesh_1);
- multimesh.add(mesh_2);
- //multimesh.add(mesh_3);
- //multimesh.add(mesh_4);
- multimesh.build();
-
- // Sum contribution from all parts
- std::cout << "\n\n Sum up\n\n";
- double volume = 0;
- for (std::size_t part = 0; part < multimesh.num_parts(); part++)
- {
- std::cout << "% part " << part << '\n';
- double part_volume = 0;
-
- const auto& quadrature_rules = multimesh.quadrature_rule_interface(part);
-
- // Get collision map
- const auto& cmap = multimesh.collision_map_cut_cells(part);
- for (auto it = cmap.begin(); it != cmap.end(); ++it)
- {
- const unsigned int cut_cell_index = it->first;
-
- // Iterate over cutting cells
- const auto& cutting_cells = it->second;
- for (auto jt = cutting_cells.begin(); jt != cutting_cells.end(); jt++)
- {
- //const std::size_t cutting_part = jt->first;
- //const std::size_t cutting_cell_index = jt->second;
-
- // Get quadrature rule for interface part defined by
- // intersection of the cut and cutting cells
- const std::size_t k = jt - cutting_cells.begin();
- dolfin_assert(k < quadrature_rules.at(cut_cell_index).size());
- const auto& qr = quadrature_rules.at(cut_cell_index)[k];
-
- for (std::size_t j = 0; j < qr.second.size(); ++j)
- {
- volume += qr.second[j];
- part_volume += qr.second[j];
- }
-
- }
- }
-
- std::cout<<"part volume " << part_volume<<std::endl;
- }
-
- std::cout << "exact volume " << exact_volume<<'\n'
- << "volume " << volume<<std::endl;
- ASSERT_NEAR(exact_volume, volume, 10*DOLFIN_EPS_LARGE);
-}
-//-----------------------------------------------------------------------------
-TEST(MultiMeshes, test_assembly)
-{
- // FIXME: Reimplement when functionals are in place again
-}
-
-// Test all
-int MultiMesh_main(int argc, char **argv) {
- // Test not working in parallel
- if (dolfin::MPI::size(MPI_COMM_WORLD) > 1)
- {
- info("Skipping unit test in parallel.");
- info("OK");
- return 0;
- }
-
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
-//-----------------------------------------------------------------------------
diff --git a/test/unit/cpp/parameter/Parameters.cpp b/test/unit/cpp/parameter/Parameters.cpp
index dde817c..61d1283 100644
--- a/test/unit/cpp/parameter/Parameters.cpp
+++ b/test/unit/cpp/parameter/Parameters.cpp
@@ -15,18 +15,20 @@
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
-// First added: 2011-03-28
-// Last changed: 2011-03-31
-//
// Unit tests for the parameter library
#include <dolfin.h>
-#include <gtest/gtest.h>
+#include <catch.hpp>
using namespace dolfin;
-TEST(InputOutput, test_simple)
+TEST_CASE("parameters io", "[test_parameter_io]" )
{
+ SECTION("flat parameters" )
+ {
+ if (dolfin::MPI::size(MPI_COMM_WORLD) > 1)
+ return;
+
// Create some parameters
Parameters p0("test");
p0.add("filename", "foo.txt");
@@ -50,14 +52,17 @@ TEST(InputOutput, test_simple)
bool monitor_convergence(p1["monitor_convergence"]);
// Check values
- ASSERT_EQ(filename, "foo.txt");
- ASSERT_EQ(maxiter, (std::size_t) 100);
- ASSERT_DOUBLE_EQ(tolerance, 0.001);
- ASSERT_TRUE(monitor_convergence);
-}
+ CHECK(filename == "foo.txt");
+ CHECK(maxiter == (std::size_t) 100);
+ CHECK(tolerance == Approx(0.001));
+ CHECK(monitor_convergence);
+ }
+
+ SECTION("nested parameters" )
+ {
+ if (dolfin::MPI::size(MPI_COMM_WORLD) > 1)
+ return;
-TEST(InputOutput, test_nested)
-{
// Create some nested parameters
Parameters p0("test");
Parameters p00("sub0");
@@ -88,25 +93,10 @@ TEST(InputOutput, test_nested)
bool monitor_convergence(p1("sub0")["monitor_convergence"]);
// Check values
- ASSERT_EQ(foo, "bar");
- ASSERT_EQ(filename, "foo.txt");
- ASSERT_EQ(maxiter, (std::size_t) 100);
- ASSERT_DOUBLE_EQ(tolerance, 0.001);
- ASSERT_TRUE(monitor_convergence);
-}
-
-// Test all
-int Parameter_main(int argc, char **argv) {
-
- // Not working in parallel, even if only process 0 writes and
- // others wait for a barrier. Skipping this in parallel for now.
- if (dolfin::MPI::size(MPI_COMM_WORLD) > 1)
- {
- info("Skipping unit test in parallel.");
- info("OK");
- return 0;
- }
-
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ CHECK(foo == "bar");
+ CHECK(filename == "foo.txt");
+ CHECK(maxiter == (std::size_t) 100);
+ CHECK(tolerance == Approx(0.001));
+ CHECK(monitor_convergence);
+ }
}
diff --git a/test/unit/pytest.ini b/test/unit/pytest.ini
index d9d13d9..5bdbf4c 100644
--- a/test/unit/pytest.ini
+++ b/test/unit/pytest.ini
@@ -5,3 +5,6 @@ minversion = 2.4
norecursedirs = .* __pycache__ test_*_tempdir
# Make pytest ignore the book files and other utility .py files
python_files = test_*.py
+# Prevent output being flooded
+filterwarnings =
+ ignore::ffc.quadrature.deprecation.QuadratureRepresentationDeprecationWarning
diff --git a/test/unit/python/adaptivity/test_error_control.py b/test/unit/python/adaptivity/test_error_control.py
index d0c7625..f9dc9cc 100755
--- a/test/unit/python/adaptivity/test_error_control.py
+++ b/test/unit/python/adaptivity/test_error_control.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for error control"""
# Copyright (C) 2011 Marie E. Rognes
diff --git a/test/unit/python/adaptivity/test_time_series.py b/test/unit/python/adaptivity/test_time_series.py
index 02b594b..5996306 100755
--- a/test/unit/python/adaptivity/test_time_series.py
+++ b/test/unit/python/adaptivity/test_time_series.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for TimeSeries"""
# Copyright (C) 2011-2014 Marie E. Rognes
diff --git a/test/unit/python/ale/test_harmonic_smoothing.py b/test/unit/python/ale/test_harmonic_smoothing.py
index a2eb70b..f26b85d 100755
--- a/test/unit/python/ale/test_harmonic_smoothing.py
+++ b/test/unit/python/ale/test_harmonic_smoothing.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit test for HarmonicSmoothing and ALE"""
# Copyright (C) 2013 Jan Blechta
@@ -22,7 +20,7 @@
from __future__ import print_function
import pytest
from dolfin import UnitSquareMesh, BoundaryMesh, Expression, \
- CellFunction, SubMesh, Constant, MPI, MeshQuality,\
+ MeshFunction, SubMesh, Constant, MPI, MeshQuality,\
mpi_comm_world, ALE
from dolfin_utils.test import skip_in_parallel
@@ -63,7 +61,7 @@ def test_ale():
# Make some cell function
# FIXME: Initialization by array indexing is probably
# not a good way for parallel test
- cellfunc = CellFunction('size_t', mesh)
+ cellfunc = MeshFunction('size_t', mesh, mesh.topology().dim())
cellfunc.array()[0:4] = 0
cellfunc.array()[4:] = 1
diff --git a/test/unit/python/common/test_mpi.py b/test/unit/python/common/test_mpi.py
new file mode 100644
index 0000000..39a714a
--- /dev/null
+++ b/test/unit/python/common/test_mpi.py
@@ -0,0 +1,52 @@
+"""Unit tests for MPI facilities"""
+
+# Copyright (C) 2017 Garth N. Wells
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin
+from dolfin import mpi_comm_world, mpi_comm_self
+from dolfin_utils.test import skip_if_not_petsc4py, skip_if_pybind11, skip_if_not_pybind11
+
+
+ at skip_if_pybind11
+ at skip_if_not_petsc4py
+def test_mpi_comm_type_petsc4py():
+ import petsc4py
+ assert isinstance(mpi_comm_world(), petsc4py.PETSc.Comm)
+ assert isinstance(mpi_comm_self(), petsc4py.PETSc.Comm)
+
+
+ at skip_if_not_pybind11
+def test_mpi_comm_wrapper():
+ """
+ Test MPICommWrapper <-> mpi4py.MPI.Comm conversion
+ """
+ if dolfin.has_mpi4py():
+ from mpi4py import MPI
+ w1 = MPI.COMM_WORLD
+ else:
+ w1 = dolfin.MPI.comm_world
+
+ m = dolfin.UnitSquareMesh(w1, 4, 4)
+ w2 = m.mpi_comm()
+
+ if dolfin.has_mpi4py():
+ assert isinstance(w1, MPI.Comm)
+ assert isinstance(w2, MPI.Comm)
+ else:
+ assert isinstance(w1, dolfin.cpp.MPICommWrapper)
+ assert isinstance(w2, dolfin.cpp.MPICommWrapper)
diff --git a/test/unit/python/fem/test_assembler.py b/test/unit/python/fem/test_assembler.py
index 869d67a..65fb2f9 100644
--- a/test/unit/python/fem/test_assembler.py
+++ b/test/unit/python/fem/test_assembler.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for assembly"""
# Copyright (C) 2011-2014 Garth N. Wells
@@ -33,7 +31,8 @@ from dolfin_utils.test import skip_in_parallel, filedir, pushpop_parameters
def test_cell_size_assembly_1D():
mesh = UnitIntervalMesh(10)
- assert round(assemble(CellSize(mesh)*dx) - 0.1, 12) == 0
+ assert round(assemble(2*Circumradius(mesh)*dx) - 0.1, 12) == 0
+ assert round(assemble(CellDiameter(mesh)*dx) - 0.1, 12) == 0
assert round(assemble(CellVolume(mesh)*dx) - 0.1, 12) == 0
@@ -89,7 +88,7 @@ def test_facet_assembly(pushpop_parameters):
# Define normal component, mesh size and right-hand side
n = FacetNormal(mesh)
- h = CellSize(mesh)
+ h = 2*Circumradius(mesh)
h_avg = (h('+') + h('-'))/2
f = Expression("500.0*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)", degree=1)
@@ -118,7 +117,7 @@ def test_ghost_mode_handling(pushpop_parameters):
def _form():
# Return form with trivial interior facet integral
mesh = UnitSquareMesh(10, 10)
- ff = FacetFunction('size_t', mesh, 0)
+ ff = MeshFunction('size_t', mesh, mesh.topology().dim()-1, 0)
AutoSubDomain(lambda x: near(x[0], 0.5)).mark(ff, 1)
return Constant(1.0)*dS(domain=mesh, subdomain_data=ff, subdomain_id=1)
@@ -140,8 +139,8 @@ def test_ghost_mode_handling(pushpop_parameters):
@pytest.mark.parametrize('mesh_factory, facet_area', [((UnitSquareMesh, (4, 4)), 4.0),
((UnitCubeMesh, (2, 2, 2)), 6.0),
- ((UnitQuadMesh.create, (4, 4)), 4.0),
- ((UnitHexMesh.create, (2, 2, 2)), 6.0)])
+ ((UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)), 4.0),
+ ((UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron)), 6.0)])
def test_functional_assembly(mesh_factory, facet_area):
func, args = mesh_factory
mesh = func(*args)
@@ -154,7 +153,7 @@ def test_functional_assembly(mesh_factory, facet_area):
assert round(assemble(M1) - facet_area, 7) == 0
- at pytest.mark.parametrize('mesh_factory', [(UnitCubeMesh, (4, 4, 4)), (UnitHexMesh.create, (4, 4, 4))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitCubeMesh, (4, 4, 4)), (UnitCubeMesh.create, (4, 4, 4, CellType.Type_hexahedron))])
def test_subdomain_and_fulldomain_assembly_meshdomains(mesh_factory):
"""Test assembly over subdomains AND the full domain with markers
stored as part of the mesh.
@@ -228,7 +227,7 @@ def test_subdomain_assembly_form_1():
class Left(SubDomain):
def inside(self, x, on_boundary):
return x[0] < 0.49
- subdomains = CellFunction("size_t", mesh)
+ subdomains = MeshFunction("size_t", mesh, mesh.topology().dim())
subdomains.set_all(0)
left = Left()
left.mark(subdomains, 1)
@@ -236,7 +235,7 @@ def test_subdomain_assembly_form_1():
class RightBoundary(SubDomain):
def inside(self, x, on_boundary):
return x[0] > 0.95
- boundaries = FacetFunction("size_t", mesh)
+ boundaries = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
boundaries.set_all(0)
right = RightBoundary()
right.mark(boundaries, 1)
diff --git a/test/unit/python/fem/test_dirichlet_bc.py b/test/unit/python/fem/test_dirichlet_bc.py
index 2dcdd77..14af470 100644
--- a/test/unit/python/fem/test_dirichlet_bc.py
+++ b/test/unit/python/fem/test_dirichlet_bc.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for Dirichlet boundary conditions"""
# Copyright (C) 2011-2017 Garth N. Wells
@@ -112,12 +110,12 @@ def test_user_meshfunction_domains():
mesh1 = UnitSquareMesh(12, 12)
V = FunctionSpace(mesh0, "CG", 1)
- DirichletBC(V, Constant(0.0), EdgeFunction("size_t", mesh0), 0)
- DirichletBC(V, Constant(0.0), FacetFunction("size_t", mesh0), 0)
+ DirichletBC(V, Constant(0.0), MeshFunction("size_t", mesh0, 1), 0)
+ DirichletBC(V, Constant(0.0), MeshFunction("size_t", mesh0, mesh0.topology().dim()-1), 0)
with pytest.raises(RuntimeError):
- DirichletBC(V, 0.0, CellFunction("size_t", mesh0), 0)
- DirichletBC(V, 0.0, VertexFunction("size_t", mesh0), 0)
- DirichletBC(V, 0.0, FacetFunction("size_t", mesh1), 0)
+ DirichletBC(V, 0.0, MeshFunction("size_t", mesh0, mesh0.topology().dim()), 0)
+ DirichletBC(V, 0.0, MeshFunction("size_t", mesh0, 0), 0)
+ DirichletBC(V, 0.0, MeshFunction("size_t", mesh1, mesh1.topology().dim()-1), 0)
@skip_in_parallel
@@ -272,9 +270,9 @@ def test_nocaching_values():
x.zero()
bc.set_value(Constant(1.0))
bc.apply(x)
- assert numpy.allclose(x.array(), 1.0)
+ assert numpy.allclose(x.get_local(), 1.0)
x.zero()
bc.set_value(Constant(2.0))
bc.apply(x)
- assert numpy.allclose(x.array(), 2.0)
+ assert numpy.allclose(x.get_local(), 2.0)
diff --git a/test/unit/python/fem/test_discrete_operators.py b/test/unit/python/fem/test_discrete_operators.py
index 3623030..28903bf 100644
--- a/test/unit/python/fem/test_discrete_operators.py
+++ b/test/unit/python/fem/test_discrete_operators.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the DiscreteOperator class"""
# Copyright (C) 2015 Garth N. Wells
@@ -36,9 +34,9 @@ def test_gradient():
W = FunctionSpace(mesh, "Nedelec 1st kind H(curl)", 1)
G = DiscreteOperators.build_gradient(W, V)
- num_edges = mesh.size_global(1)
+ num_edges = mesh.num_entities_global(1)
assert G.size(0) == num_edges
- assert G.size(1) == mesh.size_global(0)
+ assert G.size(1) == mesh.num_entities_global(0)
assert round(G.norm("frobenius") - sqrt(2.0*num_edges), 8) == 0.0
meshes = [UnitSquareMesh(11, 6), UnitCubeMesh(4, 3, 7)]
diff --git a/test/unit/python/fem/test_dofmap.py b/test/unit/python/fem/test_dofmap.py
index 2b422ee..c1eccae 100644
--- a/test/unit/python/fem/test_dofmap.py
+++ b/test/unit/python/fem/test_dofmap.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the fem interface"""
# Copyright (C) 2009-2014 Garth N. Wells
@@ -22,8 +20,10 @@
from __future__ import print_function
import pytest
import numpy as np
-from dolfin import *
+import sys
+
+from dolfin import *
from dolfin_utils.test import *
@@ -42,8 +42,8 @@ reorder_dofs = set_parameters_fixture("reorder_dofs_serial", [True, False])
(UnitCubeMesh, (2, 2, 2)),
# cell.contains(Point) does not work correctly
# for quad/hex cells once it is fixed, this test will pass
- xfail((UnitQuadMesh.create, (4, 4))),
- xfail((UnitHexMesh.create, (2, 2, 2)))])
+ xfail((UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral))),
+ xfail((UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron)))])
def test_tabulate_all_coordinates(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
@@ -88,7 +88,7 @@ def test_tabulate_all_coordinates(mesh_factory):
assert all(checked_W)
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)), (UnitQuadMesh.create, (4, 4))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)), (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral))])
def test_tabulate_dofs(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
@@ -118,7 +118,7 @@ def test_tabulate_dofs(mesh_factory):
assert np.array_equal(np.append(dofs1, dofs2), dofs3)
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)), (UnitQuadMesh.create, (4, 4))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)), (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral))])
def test_tabulate_coord_periodic(mesh_factory):
class PeriodicBoundary2(SubDomain):
@@ -167,7 +167,7 @@ def test_tabulate_coord_periodic(mesh_factory):
assert (coord4[sdim:] == coord0).all()
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (5, 5)), (UnitQuadMesh.create, (5, 5))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (5, 5)), (UnitSquareMesh.create, (5, 5, CellType.Type_quadrilateral))])
def test_tabulate_dofs_periodic(mesh_factory):
class PeriodicBoundary2(SubDomain):
@@ -222,7 +222,7 @@ def test_tabulate_dofs_periodic(mesh_factory):
assert np.array_equal(np.append(dofs1, dofs2), dofs3)
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (3, 3)), (UnitQuadMesh.create, (3, 3))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (3, 3)), (UnitSquareMesh.create, (3, 3, CellType.Type_quadrilateral))])
def test_global_dof_builder(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
@@ -237,7 +237,7 @@ def test_global_dof_builder(mesh_factory):
W = FunctionSpace(mesh, R*V)
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (3, 3)), (UnitQuadMesh.create, (3, 3))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (3, 3)), (UnitSquareMesh.create, (3, 3, CellType.Type_quadrilateral))])
def test_dof_to_vertex_map(mesh_factory, reorder_dofs):
func, args = mesh_factory
mesh = func(*args)
@@ -349,7 +349,7 @@ def test_entity_dofs(mesh):
@skip_in_parallel
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (2, 2)), (UnitQuadMesh.create, (2, 2))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (2, 2)), (UnitSquareMesh.create, (2, 2, CellType.Type_quadrilateral))])
def test_entity_closure_dofs(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
@@ -414,7 +414,9 @@ def test_clear_sub_map_data_vector(mesh):
def test_block_size(mesh):
- meshes = [UnitSquareMesh(8, 8), UnitCubeMesh(4, 4, 4), UnitQuadMesh.create(8, 8), UnitHexMesh.create(4, 4, 4)]
+ meshes = [UnitSquareMesh(8, 8), UnitCubeMesh(4, 4, 4),
+ UnitSquareMesh.create(8, 8, CellType.Type_quadrilateral),
+ UnitCubeMesh.create(4, 4, 4, CellType.Type_hexahedron)]
for mesh in meshes:
P2 = FiniteElement("Lagrange", mesh.ufl_cell(), 2)
@@ -444,8 +446,8 @@ def test_block_size_real(mesh):
@pytest.mark.parametrize('mesh_factory', [(UnitIntervalMesh, (8,)),
(UnitSquareMesh, (4, 4)),
(UnitCubeMesh, (2, 2, 2)),
- (UnitQuadMesh.create, (4, 4)),
- (UnitHexMesh.create, (2, 2, 2))])
+ (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)),
+ (UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron))])
def test_mpi_dofmap_stats(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
@@ -460,7 +462,7 @@ def test_mpi_dofmap_stats(mesh_factory):
for owner in V.dofmap().off_process_owner():
assert owner in neighbours
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)), (UnitQuadMesh.create, (4, 4))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)), (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral))])
def test_local_dimension(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
@@ -487,34 +489,82 @@ def test_local_dimension(mesh_factory):
# dofmap.index_map().size('foo')
+# Failures in FFC on quads/hexes
+xfail_ffc = pytest.mark.xfail(raises=Exception, strict=True)
+
@skip_in_parallel
-def test_dofs_dim():
+ at pytest.mark.parametrize('space', [
+ "FunctionSpace(UnitIntervalMesh.create(10), 'P', 1)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'P', 1)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'P', 1)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'Q', 1)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'Q', 1)",
+ "FunctionSpace(UnitIntervalMesh.create(10), 'P', 2)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'P', 2)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'P', 2)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'Q', 2)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'Q', 2)",
+ "FunctionSpace(UnitIntervalMesh.create(10), 'P', 3)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'P', 3)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'P', 3)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'Q', 3)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'Q', 3)",
+ "FunctionSpace(UnitIntervalMesh.create(10), 'DP', 1)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'DP', 1)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'DP', 1)",
+ xfail_ffc("FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'DQ', 1)"),
+ xfail_ffc("FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'DQ', 1)"),
+ "FunctionSpace(UnitIntervalMesh.create(10), 'DP', 2)",
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'DP', 2)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'DP', 2)",
+ xfail_ffc("FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'DQ', 2)"),
+ xfail_ffc("FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'DQ', 2)"),
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'N1curl', 1)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'N1curl', 1)",
+ xfail_ffc("FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'N1curl', 1)"),
+ xfail_ffc("FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'N1curl', 1)"),
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'N1curl', 2)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'N1curl', 2)",
+ xfail_ffc("FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'N1curl', 2)"),
+ xfail_ffc("FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'N1curl', 2)"),
+ "FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_triangle), 'RT', 1)",
+ "FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_tetrahedron), 'RT', 1)",
+ xfail_ffc("FunctionSpace(UnitSquareMesh.create(6, 6, CellType.Type_quadrilateral), 'RT', 1)"),
+ xfail_ffc("FunctionSpace(UnitCubeMesh.create(2, 2, 2, CellType.Type_hexahedron), 'RT', 1)"),
+])
+def test_dofs_dim(space):
"""Test function GenericDofMap::dofs(mesh, dim)"""
- meshes = [UnitIntervalMesh(10),
- UnitSquareMesh(6, 6),
- UnitCubeMesh(2, 2, 2),
- UnitQuadMesh.create(6, 6),
- UnitHexMesh.create(2, 2, 2)]
-
- for mesh in meshes:
- tdim = mesh.topology().dim()
- spaces = [FunctionSpace(mesh, "Discontinuous Lagrange", 1),
- FunctionSpace(mesh, "Discontinuous Lagrange", 2),
- FunctionSpace(mesh, "Lagrange", 1),
- FunctionSpace(mesh, "Lagrange", 2),
- FunctionSpace(mesh, "Lagrange", 3)]
-
- if tdim > 1 and mesh.ufl_cell().cellname() not in ['quadrilateral', 'hexahedron']:
- N1 = "Nedelec 1st kind H(curl)"
- vspaces = [VectorFunctionSpace(mesh, N1, 1),
- VectorFunctionSpace(mesh, N1, 2),
- VectorFunctionSpace(mesh, "RT", 1)]
- spaces = spaces + vspaces
-
- for V in spaces:
- dofmap = V.dofmap()
- for dim in range(0, mesh.topology().dim()):
- edofs = dofmap.dofs(mesh, dim)
- num_mesh_entities = mesh.num_entities(dim)
- dofs_per_entity = dofmap.num_entity_dofs(dim)
- assert len(edofs) == dofs_per_entity*num_mesh_entities
+ V = eval(space)
+ dofmap = V.dofmap()
+ mesh = V.mesh()
+ for dim in range(0, mesh.topology().dim()):
+ edofs = dofmap.dofs(mesh, dim)
+ num_mesh_entities = mesh.num_entities(dim)
+ dofs_per_entity = dofmap.num_entity_dofs(dim)
+ assert len(edofs) == dofs_per_entity*num_mesh_entities
+
+
+ at skip_if_not_pybind11
+def test_readonly_view_local_to_global_unwoned(mesh):
+ """Test that local_to_global_unwoned() returns readonly
+ view into the data; in particular test lifetime of data
+ owner"""
+ V = FunctionSpace(mesh, "P", 1)
+ dofmap = V.dofmap()
+ index_map = dofmap.index_map()
+
+ rc = sys.getrefcount(dofmap)
+ l2gu = dofmap.local_to_global_unowned()
+ assert sys.getrefcount(dofmap) == rc + 1 if l2gu.size else rc
+ assert not l2gu.flags.writeable
+ assert all(l2gu < V.dofmap().global_dimension())
+ del l2gu
+ assert sys.getrefcount(dofmap) == rc
+
+ rc = sys.getrefcount(index_map)
+ l2gu = index_map.local_to_global_unowned()
+ assert sys.getrefcount(index_map) == rc + 1 if l2gu.size else rc
+ assert not l2gu.flags.writeable
+ assert all(l2gu < V.dofmap().global_dimension())
+ del l2gu
+ assert sys.getrefcount(index_map) == rc
diff --git a/test/unit/python/fem/test_dp_assemble.py b/test/unit/python/fem/test_dp_assemble.py
index 6d692af..199a346 100644
--- a/test/unit/python/fem/test_dp_assemble.py
+++ b/test/unit/python/fem/test_dp_assemble.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for dP assembly"""
# Copyright (C) 2014 Johan Hake
@@ -65,7 +63,7 @@ def _create_dp_problem(dim):
# Subdomains
subdomain = AutoSubDomain(lambda x, on_boundary: x[0] <= 0.5)
disjoint_subdomain = AutoSubDomain(lambda x, on_boundary: x[0] > 0.5)
- vertex_domain = VertexFunction("size_t", mesh, 0)
+ vertex_domain = MeshFunction("size_t", mesh, 0, 0)
subdomain.mark(vertex_domain, 1)
bc = DirichletBC(VV, Constant((0, 0)), disjoint_subdomain)
dPP = dP(subdomain_data=vertex_domain)
@@ -94,16 +92,16 @@ def test_vector_assemble(dim):
(u, uu), (v, vv), (U, UU), dPP, bc = _create_dp_problem(dim)
- # In parallel vec.array() will return only local to process values
+ # In parallel vec.get_local() will return only local to process values
vec = assemble(u*v*dPP)
- assert sum(np.absolute(vec.array() - u.vector().array())) < eps
+ assert sum(np.absolute(vec.get_local() - u.vector().get_local())) < eps
vec = assemble(inner(uu, vv)*dP)
- assert sum(np.absolute(vec.array() - uu.vector().array())) < eps
+ assert sum(np.absolute(vec.get_local() - uu.vector().get_local())) < eps
vec = assemble(inner(uu, vv)*dPP(1))
bc.apply(uu.vector())
- assert sum(np.absolute(vec.array() - uu.vector().array())) < eps
+ assert sum(np.absolute(vec.get_local() - uu.vector().get_local())) < eps
def test_matrix_assemble(dim):
@@ -119,7 +117,7 @@ def test_matrix_assemble(dim):
loc_range = u.vector().local_range()
vec_mat = np.zeros_like(mat.array())
vec_mat[range(loc_range[1] - loc_range[0]),
- range(loc_range[0], loc_range[1])] = u.vector().array()
+ range(loc_range[0], loc_range[1])] = u.vector().get_local()
assert np.sum(np.absolute(mat.array() - vec_mat)) < eps
@@ -131,6 +129,6 @@ def test_matrix_assemble(dim):
loc_range = uu.vector().local_range()
vec_mat = np.zeros_like(mat.array())
vec_mat[range(loc_range[1] - loc_range[0]),
- range(loc_range[0], loc_range[1])] = uu.vector().array()
+ range(loc_range[0], loc_range[1])] = uu.vector().get_local()
assert np.sum(np.absolute(mat.array() - vec_mat)) < eps
diff --git a/test/unit/python/fem/test_finite_element.py b/test/unit/python/fem/test_finite_element.py
index 919d836..d6b35a7 100644
--- a/test/unit/python/fem/test_finite_element.py
+++ b/test/unit/python/fem/test_finite_element.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the fem interface"""
# Copyright (C) 2009 Garth N. Wells
@@ -34,10 +32,10 @@ xfail = pytest.mark.xfail(strict=True)
@pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)),
(UnitCubeMesh, (2, 2, 2)),
- (UnitQuadMesh.create, (4, 4)),
+ (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)),
# cell_normal has not been implemented for hex cell
# cell.orientation() does not work
- xfail((UnitHexMesh.create, (2, 2, 2)))])
+ xfail((UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron)))])
def test_evaluate_dofs(mesh_factory):
func, args = mesh_factory
@@ -139,8 +137,8 @@ def test_evaluate_dofs_manifolds_affine():
@pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (4, 4)),
(UnitCubeMesh, (2, 2, 2)),
- (UnitQuadMesh.create, (4, 4)),
- (UnitHexMesh.create, (2, 2, 2))])
+ (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)),
+ (UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron))])
def test_tabulate_coord(mesh_factory):
func, args = mesh_factory
diff --git a/test/unit/python/fem/test_form.py b/test/unit/python/fem/test_form.py
index 8704aec..1975cb0 100644
--- a/test/unit/python/fem/test_form.py
+++ b/test/unit/python/fem/test_form.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the fem interface"""
# Copyright (C) 2011-2014 Johan Hake
@@ -112,7 +110,7 @@ def test_assemble_linear(V1, Q1, square_boundary, V2, Q2, cube_boundary):
w = TestFunction(Q1)
u.vector()[:] = 0.5
facetareas = MPI.sum(square_boundary.mpi_comm(),
- assemble(u*w*dx).array().sum())
+ assemble(u*w*dx).get_local().sum())
assert round(facetareas - 2.0, 7) == 0
u = Function(V2)
@@ -121,7 +119,7 @@ def test_assemble_linear(V1, Q1, square_boundary, V2, Q2, cube_boundary):
a = u*w*dx
b = assemble(a)
facetareas = MPI.sum(cube_boundary.mpi_comm(),
- assemble(u*w*dx).array().sum())
+ assemble(u*w*dx).get_local().sum())
assert round(facetareas - 3.0, 7) == 0
mesh = UnitSquareMesh(8, 8)
@@ -159,7 +157,7 @@ def test_assemble_bilinear_1D_2D(square, V1, square_boundary):
# Assemble over subset of mesh facets
subdomain = CompiledSubDomain("near(x[1], 0.0)")
- bottom = FacetFunction("size_t", square)
+ bottom = MeshFunction("size_t", square, square.topology().dim()-1)
bottom.set_all(0)
subdomain.mark(bottom, 1)
dss = ds(subdomain_data=bottom)
@@ -169,7 +167,7 @@ def test_assemble_bilinear_1D_2D(square, V1, square_boundary):
# Assemble over all cells of submesh created from subset of
# boundary mesh
- bottom2 = CellFunction("size_t", square_boundary)
+ bottom2 = MeshFunction("size_t", square_boundary, square_boundary.topology().dim())
bottom2.set_all(0)
subdomain.mark(bottom2, 1)
BV = FunctionSpace(SubMesh(square_boundary, bottom2, 1), "CG", 1)
@@ -201,7 +199,7 @@ def test_assemble_bilinear_2D_3D(cube, V2, cube_boundary):
# Assemble over subset of mesh facets
subdomain = CompiledSubDomain("near(x[1], 0.0)")
- bottom = FacetFunction("size_t", cube)
+ bottom = MeshFunction("size_t", cube, cube.topology().dim()-1)
bottom.set_all(0)
subdomain.mark(bottom, 1)
dss = ds(subdomain_data=bottom)
@@ -211,7 +209,7 @@ def test_assemble_bilinear_2D_3D(cube, V2, cube_boundary):
# Assemble over all cells of submesh created from subset of
# boundary mesh
- bottom2 = CellFunction("size_t", cube_boundary)
+ bottom2 = MeshFunction("size_t", cube_boundary, cube_boundary.topology().dim())
bottom2.set_all(0)
subdomain.mark(bottom2, 1)
BV = FunctionSpace(SubMesh(cube_boundary, bottom2, 1), "CG", 1)
diff --git a/test/unit/python/fem/test_interior_facet_integral_sides.py b/test/unit/python/fem/test_interior_facet_integral_sides.py
index 40eb60d..9af0596 100644
--- a/test/unit/python/fem/test_interior_facet_integral_sides.py
+++ b/test/unit/python/fem/test_interior_facet_integral_sides.py
@@ -1,14 +1,12 @@
-#!/usr/bin/env py.test
import ufl
from dolfin import *
from dolfin_utils.test import skip_in_parallel
-
@skip_in_parallel
def test_interior_facet_integral_sides():
n = 1
mesh = UnitSquareMesh(n, n)
- markers = CellFunction("size_t", mesh)
+ markers = MeshFunction("size_t", mesh, mesh.topology().dim())
subdomain = AutoSubDomain(lambda x, on_boundary: x[0] > x[1]-DOLFIN_EPS)
V = FunctionSpace(mesh, "DG", 0)
diff --git a/test/unit/python/fem/test_local_assembler.py b/test/unit/python/fem/test_local_assembler.py
index ac6d4a9..a9257b6 100644
--- a/test/unit/python/fem/test_local_assembler.py
+++ b/test/unit/python/fem/test_local_assembler.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for local assembly"""
# Copyright (C) 2015 Tormod Landet
diff --git a/test/unit/python/fem/test_local_solver.py b/test/unit/python/fem/test_local_solver.py
index 8b28535..d810d5b 100644
--- a/test/unit/python/fem/test_local_solver.py
+++ b/test/unit/python/fem/test_local_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for LocalSolver"""
# Copyright (C) 2013 Garth N. Wells
@@ -105,7 +103,7 @@ def test_solve_local_rhs_facet_integrals(ghost_mode):
# Facet function is used here to verify that the proper domains
# of the rhs are used unlike before where the rhs domains were
# taken to be the same as the lhs domains
- marker = FacetFunction("size_t", mesh)
+ marker = MeshFunction("size_t", mesh, mesh.topology().dim()-1, 0)
ds0 = Measure("ds", domain=mesh, subdomain_data=marker, subdomain_id=0)
Vu = VectorFunctionSpace(mesh, 'DG', 1)
diff --git a/test/unit/python/fem/test_manifolds.py b/test/unit/python/fem/test_manifolds.py
index 716c839..b1a19b8 100644
--- a/test/unit/python/fem/test_manifolds.py
+++ b/test/unit/python/fem/test_manifolds.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the solve function on manifolds
embedded in higher dimensional spaces."""
diff --git a/test/unit/python/fem/test_petsc_transfer_matrix.py b/test/unit/python/fem/test_petsc_transfer_matrix.py
index 278aab1..c5af9f4 100644
--- a/test/unit/python/fem/test_petsc_transfer_matrix.py
+++ b/test/unit/python/fem/test_petsc_transfer_matrix.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the fem interface"""
# Copyright (C) 2016 Chris Richardson
diff --git a/test/unit/python/fem/test_point_source.py b/test/unit/python/fem/test_point_source.py
index 98d37a7..5870e16 100644
--- a/test/unit/python/fem/test_point_source.py
+++ b/test/unit/python/fem/test_point_source.py
@@ -59,8 +59,8 @@ def test_pointsource_vector_node(mesh, point):
for v in vertices(mesh):
if near(v.midpoint().distance(point), 0.0):
ind = v2d[v.index()]
- if ind < len(b.array()):
- assert np.round(b.array()[ind]-10.0) == 0
+ if ind < len(b.get_local()):
+ assert np.round(b.get_local()[ind]-10.0) == 0
@pytest.mark.parametrize("mesh", meshes)
@@ -118,8 +118,8 @@ def test_pointsource_vector_fs(mesh, point):
if near(v.midpoint().distance(point), 0.0):
for spc_idx in range(V.num_sub_spaces()):
ind = v2d[v.index()*V.num_sub_spaces() + spc_idx]
- if ind < len(b.array()):
- assert np.round(b.array()[ind] - 10.0) == 0
+ if ind < len(b.get_local()):
+ assert np.round(b.get_local()[ind] - 10.0) == 0
@pytest.mark.parametrize("mesh, point", data)
diff --git a/test/unit/python/fem/test_solving.py b/test/unit/python/fem/test_solving.py
index 2563332..d3481be 100644
--- a/test/unit/python/fem/test_solving.py
+++ b/test/unit/python/fem/test_solving.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the solve function"""
# Copyright (C) 2011 Anders Logg
diff --git a/test/unit/python/fem/test_symbolic_geometry_assembly.py b/test/unit/python/fem/test_symbolic_geometry_assembly.py
index e7aaaf1..c72ea4e 100644
--- a/test/unit/python/fem/test_symbolic_geometry_assembly.py
+++ b/test/unit/python/fem/test_symbolic_geometry_assembly.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env py.test
from dolfin import *
import ufl
import numpy
@@ -251,13 +250,12 @@ def test_manifold_line_geometry(mesh, uflacs_representation_only):
assert uflacs_representation_only == "uflacs"
assert parameters["form_compiler"]["representation"] == "uflacs"
+ gdim = mesh.geometry().dim()
+ tdim = mesh.topology().dim()
+
# Create cell markers and integration measure
- mf = CellFunction("size_t", mesh)
- for i in range(mesh.num_cells()):
- mf[i] = i
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
dx = Measure("dx", domain=mesh, subdomain_data=mf)
- coords = mesh.coordinates()
- cells = mesh.cells()
# Create symbolic geometry for current mesh
x = SpatialCoordinate(mesh)
@@ -268,6 +266,8 @@ def test_manifold_line_geometry(mesh, uflacs_representation_only):
detJ = JacobianDeterminant(mesh)
K = JacobianInverse(mesh)
vol = CellVolume(mesh)
+ h = CellDiameter(mesh)
+ R = Circumradius(mesh)
# Check that length computed via integral doesn't change with
# refinement
@@ -288,19 +288,21 @@ def test_manifold_line_geometry(mesh, uflacs_representation_only):
assert round(assemble((vol-abs(detJ))*dx), 7) == 0.0
assert round(length - assemble(vol/abs(detJ)*dx), 7) == 0.0
- gdim, tdim = J.ufl_shape
+ coords = mesh.coordinates()
+ cells = mesh.cells()
# Checks on each cell separately
- for i in range(mesh.num_cells()):
- mf[i] = 0
- for i in range(mesh.num_cells()):
- mf[i] = 1 # mark this cell
- x0 = Constant(tuple(coords[cells[i][0], :]))
+ for k in range(mesh.num_cells()):
+ # Mark current cell
+ mf.set_all(0)
+ mf[k] = 1
+
+ x0 = Constant(tuple(coords[cells[k][0], :]))
# Integrate x components over a cell and compare with midpoint
# computed from coords
for j in range(gdim):
- xm = 0.5*(coords[cells[i][0], j] + coords[cells[i][1], j])
+ xm = 0.5*(coords[cells[k][0], j] + coords[cells[k][1], j])
assert round(assemble(x[j]/abs(detJ)*dx(1)) - xm, 7) == 0.0
# Jacobian column is pointing away from x0
@@ -311,6 +313,10 @@ def test_manifold_line_geometry(mesh, uflacs_representation_only):
assert round(assemble((X - K*(x-x0))**2*dx(1)), 7) == 0.0
assert round(assemble((K*J - Identity(tdim))**2*dx(1)), 7) == 0.0
+ # Check cell diameter and circumradius
+ assert round(assemble(h/vol*dx(1)) - Cell(mesh, k).h(), 7) == 0.0
+ assert round(assemble(R/vol*dx(1)) - Cell(mesh, k).circumradius(), 7) == 0.0
+
# Jacobian column is orthogonal to cell normal
if gdim == 2:
assert round(assemble(dot(J[:, 0], cn)*dx(1)), 7) == 0.0
@@ -331,7 +337,6 @@ def test_manifold_line_geometry(mesh, uflacs_representation_only):
assert round(assemble(up**2*dx(1)), 7) > 0.0
assert round(assemble((up[0]**2 + up[1]**2)*dx(1)), 7) == 0.0
assert round(assemble(up[2]*dx(1)), 7) > 0.0
- mf[i] = 0 # unmark this cell
@skip_in_parallel
@@ -352,7 +357,7 @@ def test_manifold_dg0_functions(square3d, any_representation):
mesh = square3d
area = sqrt(3.0) # known area of mesh
- mf = CellFunction("size_t", mesh)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
mf[0] = 0
mf[1] = 1
dx = Measure("dx", domain=mesh, subdomain_data=mf)
@@ -366,8 +371,8 @@ def test_manifold_dg0_functions(square3d, any_representation):
u0 = project(1.0, U0)
v0v = (1.0, 2.0, 3.0)
v0 = project(as_vector(v0v), V0)
- assert round(sum(u0.vector().array()) - 2*1, 7) == 0.0
- assert round(sum(v0.vector().array()) - 2*(1+2+3), 7) == 0.0
+ assert round(sum(u0.vector().get_local()) - 2*1, 7) == 0.0
+ assert round(sum(v0.vector().get_local()) - 2*(1+2+3), 7) == 0.0
# Integrate piecewise constant functions over manifold cells
assert round(assemble(u0*dx(0)) - 0.5*area) == 0.0
@@ -380,8 +385,8 @@ def test_manifold_dg0_functions(square3d, any_representation):
# Project x to scalar and vector DG0 spaces on manifold
u0x = project(x[0], U0) # cell averages of x[0]: 2/3, 1/3, sum = 3/3
v0x = project(x, V0) # cell averages of x[:]: (2/3, 1/3, 2/3), (1/3, 2/3, 2/3), sum = 10/3
- assert round(sum(u0x.vector().array()) - 3.0/3.0, 7) == 0.0
- assert round(sum(v0x.vector().array()) - 10.0/3.0, 7) == 0.0
+ assert round(sum(u0x.vector().get_local()) - 3.0/3.0, 7) == 0.0
+ assert round(sum(v0x.vector().get_local()) - 10.0/3.0, 7) == 0.0
# Evaluate in all corners and cell midpoints, value should be the
# same constant everywhere
@@ -402,7 +407,7 @@ def test_manifold_cg1_functions(square3d, any_representation):
mesh = square3d
area = sqrt(3.0) # known area of mesh
- mf = CellFunction("size_t", mesh)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
mf[0] = 0
mf[1] = 1
dx = Measure("dx", domain=mesh, subdomain_data=mf)
@@ -420,9 +425,9 @@ def test_manifold_cg1_functions(square3d, any_representation):
v1 = project(x, V1)
# exact x in vertices is [0,0,0, 1,1,1, 1,0,0, 0,1,0],
# so sum(x[0] for each vertex) is therefore sum(0 1 1 0):
- assert round(sum(u1.vector().array()) - (0+1+1+0), 7) == 0.0
+ assert round(sum(u1.vector().get_local()) - (0+1+1+0), 7) == 0.0
# and sum(x components for each vertex) is sum(1, 3, 1, 1):
- assert round(sum(v1.vector().array()) - (1+3+1+1), 7) == 0.0
+ assert round(sum(v1.vector().get_local()) - (1+3+1+1), 7) == 0.0
# Integrate piecewise constant functions over manifold cells,
# computing midpoint coordinates
@@ -503,11 +508,15 @@ def test_manifold_point_evaluation(square3d, any_representation):
@skip_in_parallel
def test_manifold_symbolic_geometry(square3d, uflacs_representation_only):
mesh = square3d
+ assert mesh.num_cells() == 2
+ gdim = mesh.geometry().dim()
+ tdim = mesh.topology().dim()
+
area = sqrt(3.0) # known area of mesh
A = area/2.0 # area of single cell
Aref = 0.5 # 0.5 is the area of the UFC reference triangle
- mf = CellFunction("size_t", mesh)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
mf[0] = 0
mf[1] = 1
dx = Measure("dx", domain=mesh, subdomain_data=mf)
@@ -550,56 +559,65 @@ def test_manifold_symbolic_geometry(square3d, uflacs_representation_only):
detJ = JacobianDeterminant(mesh) # pseudo-determinant
K = JacobianInverse(mesh) # pseudo-inverse
vol = CellVolume(mesh)
+ h = CellDiameter(mesh)
+ R = Circumradius(mesh)
# This is not currently implemented in uflacs:
# x0 = CellOrigin(mesh)
# But by happy accident, x0 is the same vertex for both our triangles:
x0 = as_vector((0.0, 0.0, 1.0))
- # Check integration area vs detJ
- for k in range(2):
+ # Checks on each cell separately
+ for k in range(mesh.num_cells()):
+ # Mark current cell
+ mf.set_all(0)
+ mf[k] = 1
+
+ # Check integration area vs detJ
# Validate known cell area A
- assert round(assemble(1.0*dx(k)) - A, 7) == 0.0
- assert round(assemble(1.0/A*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble(1.0*dx(1)) - A, 7) == 0.0
+ assert round(assemble(1.0/A*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A*dx(1)) - A**2, 7) == 0.0
# Compare abs(detJ) to A
A2 = Aref*abs(detJ)
- assert round(assemble((A-A2)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A2*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A2*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble((A-A2)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A2*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A2*dx(1)) - A**2, 7) == 0.0
# Validate cell orientation
- assert round(assemble(co*dx(k)) - A*(1 if k == 1 else -1), 7) == 0.0
+ assert round(assemble(co*dx(1)) - A*(1 if k == 1 else -1), 7) == 0.0
# Compare co*detJ to A (detJ is pseudo-determinant with sign
# restored, *co again is equivalent to abs())
A3 = Aref*co*detJ
- assert round(assemble((A-A3)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble((A2-A3)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A3*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A3*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble((A-A3)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble((A2-A3)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A3*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A3*dx(1)) - A**2, 7) == 0.0
# Compare vol to A
A4 = vol
- assert round(assemble((A-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble((A2-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble((A3-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A4*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A4*dx(k)) - A**2, 7) == 0.0
-
- # Check integral of reference coordinate components over reference
- # triangle: \int_0^1 \int_0^{1-x} x dy dx = 1/6
- Xmp = (1.0/6.0, 1.0/6.0)
- for k in range(2):
- for j in range(2):
+ assert round(assemble((A-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble((A2-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble((A3-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A4*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A4*dx(1)) - A**2, 7) == 0.0
+
+ # Check cell diameter and circumradius
+ assert round(assemble(h/vol*dx(1)) - Cell(mesh, k).h(), 7) == 0.0
+ assert round(assemble(R/vol*dx(1)) - Cell(mesh, k).circumradius(), 7) == 0.0
+
+ # Check integral of reference coordinate components over reference
+ # triangle: \int_0^1 \int_0^{1-x} x dy dx = 1/6
+ Xmp = (1.0/6.0, 1.0/6.0)
+ for j in range(tdim):
# Scale by detJ^-1 to get reference cell integral
- assert round(assemble(X[j]/abs(detJ)*dx(k)) - Xmp[j], 7) == 0.0
-
- # Check average of physical coordinate components over each cell:
- xmp = [(2.0/3.0, 1.0/3.0, 2.0/3.0), # midpoint of cell 0
- (1.0/3.0, 2.0/3.0, 2.0/3.0), # midpoint of cell 1
- ]
- for k in range(2):
- for i in range(3):
+ assert round(assemble(X[j]/abs(detJ)*dx(1)) - Xmp[j], 7) == 0.0
+
+ # Check average of physical coordinate components over each cell:
+ xmp = [(2.0/3.0, 1.0/3.0, 2.0/3.0), # midpoint of cell 0
+ (1.0/3.0, 2.0/3.0, 2.0/3.0), # midpoint of cell 1
+ ]
+ for i in range(gdim):
# Scale by A^-1 to get average of x, not integral
- assert round(assemble(x[i]/A*dx(k)) - xmp[k][i], 7) == 0.0
+ assert round(assemble(x[i]/A*dx(1)) - xmp[k][i], 7) == 0.0
# Check affine coordinate relations x=x0+J*X, X=K*(x-x0), K*J=I
assert round(assemble((x - (x0+J*X))**2*dx), 7) == 0.0
@@ -613,7 +631,7 @@ def test_manifold_piola_mapped_functions(square3d, any_representation):
area = sqrt(3.0) # known area of mesh
A = area/2.0
- mf = CellFunction("size_t", mesh)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
mf[0] = 0
mf[1] = 1
dx = Measure("dx", domain=mesh, subdomain_data=mf)
@@ -720,9 +738,7 @@ def test_tetrahedron_symbolic_geometry(uflacs_representation_only):
A = area/6.0 # volume of single cell
Aref = 1.0/6.0 # the volume of the UFC reference tetrahedron
- mf = CellFunction("size_t", mesh)
- for i in range(mesh.num_cells()):
- mf[i] = i
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
dx = Measure("dx", domain=mesh, subdomain_data=mf)
U0 = FunctionSpace(mesh, "DG", 0)
@@ -737,54 +753,62 @@ def test_tetrahedron_symbolic_geometry(uflacs_representation_only):
detJ = JacobianDeterminant(mesh)
K = JacobianInverse(mesh)
vol = CellVolume(mesh)
+ h = CellDiameter(mesh)
+ R = Circumradius(mesh)
- # Check integration area vs detJ
coordinates = mesh.coordinates()
cells = mesh.cells()
+
for k in range(mesh.num_cells()):
+ # Mark current cell
+ mf.set_all(0)
+ mf[k] = 1
+
+ # Check integration area vs detJ
# This is not currently implemented in uflacs:
# x0 = CellOrigin(mesh)
# But we can extract it from the mesh for a given cell k
x0 = as_vector(coordinates[cells[k][0]][:])
# Validate known cell volume A
- assert round(assemble(1.0*dx(k)) - A, 7) == 0.0
- assert round(assemble(1.0/A*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble(1.0*dx(1)) - A, 7) == 0.0
+ assert round(assemble(1.0/A*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A*dx(1)) - A**2, 7) == 0.0
# Compare abs(detJ) to A
A2 = Aref*abs(detJ)
- assert round(assemble((A-A2)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A2*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A2*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble((A-A2)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A2*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A2*dx(1)) - A**2, 7) == 0.0
# Compare vol to A
A4 = vol
- assert round(assemble((A-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble((A2-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A4*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A4*dx(k)) - A**2, 7) == 0.0
-
- # Check integral of reference coordinate components over reference
- # tetrahedron:
- Xmp = (1.0/24.0, 1.0/24.0, 1.0/24.0) # not validated analytically
- for k in range(mesh.num_cells()):
+ assert round(assemble((A-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble((A2-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A4*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A4*dx(1)) - A**2, 7) == 0.0
+
+ # Check integral of reference coordinate components over reference
+ # tetrahedron:
+ Xmp = (1.0/24.0, 1.0/24.0, 1.0/24.0) # not validated analytically
for j in range(tdim):
# Scale by detJ^-1 to get reference cell integral
- assert round(assemble(X[j]/abs(detJ)*dx(k)) - Xmp[j], 7) == 0.0
+ assert round(assemble(X[j]/abs(detJ)*dx(1)) - Xmp[j], 7) == 0.0
- # Check average of physical coordinate components over each cell:
- for k in range(mesh.num_cells()):
+ # Check average of physical coordinate components over each cell:
# Compute average of vertex coordinates extracted from mesh
verts = [coordinates[i][:] for i in cells[k]]
vavg = sum(verts[1:], verts[0])/len(verts)
for i in range(gdim):
# Scale by A^-1 to get average of x, not integral
- assert round(assemble(x[i]/A*dx(k)) - vavg[i], 7) == 0.0
+ assert round(assemble(x[i]/A*dx(1)) - vavg[i], 7) == 0.0
- # Check affine coordinate relations x=x0+J*X, X=K*(x-x0), K*J=I
- for k in range(mesh.num_cells()):
+ # Check affine coordinate relations x=x0+J*X, X=K*(x-x0), K*J=I
x0 = as_vector(coordinates[cells[k][0]][:])
- assert round(assemble((x - (x0+J*X))**2*dx(k)), 7) == 0.0
- assert round(assemble((X - K*(x-x0))**2*dx(k)), 7) == 0.0
- assert round(assemble((K*J - Identity(tdim))**2/A*dx(k)), 7) == 0.0
+ assert round(assemble((x - (x0+J*X))**2*dx(1)), 7) == 0.0
+ assert round(assemble((X - K*(x-x0))**2*dx(1)), 7) == 0.0
+ assert round(assemble((K*J - Identity(tdim))**2/A*dx(1)), 7) == 0.0
+
+ # Check cell diameter and circumradius
+ assert round(assemble(h/vol*dx(1)) - Cell(mesh, k).h(), 7) == 0.0
+ assert round(assemble(R/vol*dx(1)) - Cell(mesh, k).circumradius(), 7) == 0.0
# Some symbolic quantities are only available through uflacs
@@ -799,9 +823,7 @@ def test_triangle_symbolic_geometry(uflacs_representation_only):
A = area/2.0 # volume of single cell
Aref = 1.0/2.0 # the volume of the UFC reference triangle
- mf = CellFunction("size_t", mesh)
- for i in range(mesh.num_cells()):
- mf[i] = i
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
dx = Measure("dx", domain=mesh, subdomain_data=mf)
U0 = FunctionSpace(mesh, "DG", 0)
@@ -816,51 +838,146 @@ def test_triangle_symbolic_geometry(uflacs_representation_only):
detJ = JacobianDeterminant(mesh)
K = JacobianInverse(mesh)
vol = CellVolume(mesh)
+ h = CellDiameter(mesh)
+ R = Circumradius(mesh)
- # Check integration area vs detJ
coordinates = mesh.coordinates()
cells = mesh.cells()
+
for k in range(mesh.num_cells()):
+ # Mark current cell
+ mf.set_all(0)
+ mf[k] = 1
+
+ # Check integration area vs detJ
# This is not currently implemented in uflacs:
# x0 = CellOrigin(mesh)
# But we can extract it from the mesh for a given cell k
x0 = as_vector(coordinates[cells[k][0]][:])
# Validate known cell volume A
- assert round(assemble(1.0*dx(k)) - A, 7) == 0.0
- assert round(assemble(1.0/A*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble(1.0*dx(1)) - A, 7) == 0.0
+ assert round(assemble(1.0/A*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A*dx(1)) - A**2, 7) == 0.0
# Compare abs(detJ) to A
A2 = Aref*abs(detJ)
- assert round(assemble((A-A2)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A2*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A2*dx(k)) - A**2, 7) == 0.0
+ assert round(assemble((A-A2)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A2*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A2*dx(1)) - A**2, 7) == 0.0
# Compare vol to A
A4 = vol
- assert round(assemble((A-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble((A2-A4)**2*dx(k)) - 0.0, 7) == 0.0
- assert round(assemble(1.0/A4*dx(k)) - 1.0, 7) == 0.0
- assert round(assemble(A4*dx(k)) - A**2, 7) == 0.0
-
- # Check integral of reference coordinate components over reference
- # triangle:
- Xmp = (1.0/6.0, 1.0/6.0)
- for k in range(mesh.num_cells()):
+ assert round(assemble((A-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble((A2-A4)**2*dx(1)) - 0.0, 7) == 0.0
+ assert round(assemble(1.0/A4*dx(1)) - 1.0, 7) == 0.0
+ assert round(assemble(A4*dx(1)) - A**2, 7) == 0.0
+
+ # Check integral of reference coordinate components over reference
+ # triangle:
+ Xmp = (1.0/6.0, 1.0/6.0)
for j in range(tdim):
# Scale by detJ^-1 to get reference cell integral
- assert round(assemble(X[j]/abs(detJ)*dx(k)) - Xmp[j], 7) == 0.0
+ assert round(assemble(X[j]/abs(detJ)*dx(1)) - Xmp[j], 7) == 0.0
- # Check average of physical coordinate components over each cell:
- for k in range(mesh.num_cells()):
+ # Check average of physical coordinate components over each cell:
# Compute average of vertex coordinates extracted from mesh
verts = [coordinates[i][:] for i in cells[k]]
vavg = sum(verts[1:], verts[0])/len(verts)
for i in range(gdim):
# Scale by A^-1 to get average of x, not integral
- assert round(assemble(x[i]/A*dx(k)) - vavg[i], 7) == 0.0
+ assert round(assemble(x[i]/A*dx(1)) - vavg[i], 7) == 0.0
- # Check affine coordinate relations x=x0+J*X, X=K*(x-x0), K*J=I
- for k in range(mesh.num_cells()):
+ # Check affine coordinate relations x=x0+J*X, X=K*(x-x0), K*J=I
x0 = as_vector(coordinates[cells[k][0]][:])
- assert round(assemble((x - (x0+J*X))**2*dx(k)), 7) == 0.0
- assert round(assemble((X - K*(x-x0))**2*dx(k)), 7) == 0.0
- assert round(assemble((K*J - Identity(tdim))**2/A*dx(k)), 7) == 0.0
+ assert round(assemble((x - (x0+J*X))**2*dx(1)), 7) == 0.0
+ assert round(assemble((X - K*(x-x0))**2*dx(1)), 7) == 0.0
+ assert round(assemble((K*J - Identity(tdim))**2/A*dx(1)), 7) == 0.0
+
+ # Check cell diameter and circumradius
+ assert round(assemble(h/vol*dx(1)) - Cell(mesh, k).h(), 7) == 0.0
+ assert round(assemble(R/vol*dx(1)) - Cell(mesh, k).circumradius(), 7) == 0.0
+
+
+if has_pybind11():
+ xfail_jit = pytest.mark.xfail(raises=Exception, strict=True)
+else:
+ xfail_jit = pytest.mark.xfail(raises=RuntimeError, strict=True)
+
+ at pytest.mark.parametrize('mesh_factory', [
+ (UnitIntervalMesh, (8,)),
+ (UnitSquareMesh, (4, 4)),
+ (UnitDiscMesh.create, (mpi_comm_world(), 4, 1, 2)),
+ (UnitDiscMesh.create, (mpi_comm_world(), 4, 1, 3)),
+ (SphericalShellMesh.create, (mpi_comm_world(), 1,)),
+ (UnitCubeMesh, (2, 2, 2)),
+ (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)),
+ (UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron)),
+ (line1d, (None,)),
+ (line2d, (None,)),
+ (line3d, (None,)),
+ (rline1d, (None,)),
+ (rline2d, (None,)),
+ (rline3d, (None,)),
+ (square2d, (None,)),
+ (square3d, (None,)),
+ # Tested geometric quantities are not implemented for higher-order cells
+ xfail_jit((UnitDiscMesh.create, (mpi_comm_world(), 4, 2, 2))),
+ xfail_jit((UnitDiscMesh.create, (mpi_comm_world(), 4, 2, 3))),
+ xfail_jit((SphericalShellMesh.create, (mpi_comm_world(), 2,))),
+])
+ at skip_in_parallel
+def test_geometric_quantities(uflacs_representation_only, mesh_factory):
+ func, args = mesh_factory
+ mesh = func(*args)
+
+ tdim = mesh.ufl_cell().topological_dimension()
+
+ cf = MeshFunction('size_t', mesh, mesh.topology().dim(), 0)
+ dx = Measure("dx", domain=mesh, subdomain_data=cf)
+
+ ff = MeshFunction('size_t', mesh, mesh.topology().dim()-1, 0)
+ ds = Measure("ds", domain=mesh, subdomain_data=ff)
+ dS = Measure("dS", domain=mesh, subdomain_data=ff)
+
+ h = CellDiameter(mesh)
+ R = Circumradius(mesh)
+ max_cell_edge = MaxCellEdgeLength(mesh)
+ min_cell_edge = MinCellEdgeLength(mesh)
+ max_facet_edge = MaxFacetEdgeLength(mesh)
+ min_facet_edge = MinFacetEdgeLength(mesh)
+
+ for c in cells(mesh):
+ # Mark current cell for integration
+ cf.set_all(0)
+ cf[c] = 1
+
+ vol = assemble(1*dx(1))
+
+ # Check cell diameter
+ assert numpy.isclose(assemble(h*dx(1))/vol, c.h())
+
+ # Check max/min cell edge length
+ assert numpy.isclose(assemble(max_cell_edge*dx(1))/vol,
+ max(e.length() for e in edges(c)))
+ assert numpy.isclose(assemble(min_cell_edge*dx(1))/vol,
+ min(e.length() for e in edges(c)))
+
+ # Check circumradius if it makes sense
+ if mesh.ufl_domain().is_piecewise_linear_simplex_domain():
+ assert numpy.isclose(assemble(R*dx(1))/vol, c.circumradius())
+
+ # Check max/min facet edge length if it makes sense
+ if tdim >= 3:
+
+ mesh.init(tdim-1, tdim) # for Facet.exterior()
+
+ for f in facets(mesh):
+ # Mark current facet for integration and pick facet measure
+ ff.set_all(0)
+ ff[f] = 1
+ df = ds(1) if f.exterior() else dS(1)
+
+ vol = assemble(1*df)
+
+ assert numpy.isclose(assemble(max_facet_edge*df)/vol,
+ max(e.length() for e in edges(f)))
+ assert numpy.isclose(assemble(min_facet_edge*df)/vol,
+ min(e.length() for e in edges(f)))
diff --git a/test/unit/python/fem/test_system_assembler.py b/test/unit/python/fem/test_system_assembler.py
index 6d83183..de536b7 100644
--- a/test/unit/python/fem/test_system_assembler.py
+++ b/test/unit/python/fem/test_system_assembler.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for class SystemAssembler"""
# Copyright (C) 2011-2013 Garth N. Wells, 2013 Jan Blechta
@@ -131,7 +129,7 @@ def test_facet_assembly():
# Define normal component, mesh size and right-hand side
n = FacetNormal(mesh)
- h = CellSize(mesh)
+ h = 2*Circumradius(mesh)
h_avg = (h('+') + h('-'))/2
f = Expression("500.0*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)", degree=1)
@@ -197,7 +195,7 @@ def test_vertex_assembly():
return 0.25 <= x[0] and x[0] <= 0.75 and near(x[1], 0.5)
# Define domain for point integral
- center_domain = VertexFunction("size_t", mesh, 0)
+ center_domain = MeshFunction("size_t", mesh, 0, 0)
center = AutoSubDomain(center_func)
center.mark(center_domain, 1)
dPP = dP(subdomain_data=center_domain)
@@ -213,7 +211,8 @@ def test_vertex_assembly():
A, b = assemble_system(a, L)
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (20, 20)), (UnitQuadMesh.create, (20, 20))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (20, 20)),
+ (UnitSquareMesh.create, (20, 20, CellType.Type_quadrilateral))])
def test_incremental_assembly(mesh_factory):
for f in [Constant(0.0), Constant(1e4)]:
@@ -254,7 +253,11 @@ def test_incremental_assembly(mesh_factory):
@skip_in_parallel
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (24, 24)), (UnitQuadMesh.create, (24, 24))])
+ at pytest.mark.parametrize('mesh_factory', [
+ (UnitSquareMesh.create, (24, 24, CellType.Type_triangle)),
+ # FFC PR #91 disables (broken) DQ elements
+ pytest.mark.xfail((UnitSquareMesh.create, (24, 24, CellType.Type_quadrilateral)), strict=True, raises=Exception),
+])
def test_domains(mesh_factory):
class RightSubDomain(SubDomain):
@@ -371,7 +374,8 @@ def test_facet_assembly_cellwise_insertion(filedir):
run_test(Mesh(os.path.join(filedir, "gmsh_unit_interval.xml")))
- at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (24, 24)), (UnitQuadMesh.create, (24, 24))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitSquareMesh, (24, 24)),
+ (UnitSquareMesh.create, (24, 24, CellType.Type_quadrilateral))])
def test_non_square_assembly(mesh_factory):
func, args = mesh_factory
mesh = func(*args)
diff --git a/test/unit/python/fem/test_variational_problem.py b/test/unit/python/fem/test_variational_problem.py
index 6acd4bf..6bdd663 100644
--- a/test/unit/python/fem/test_variational_problem.py
+++ b/test/unit/python/fem/test_variational_problem.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Nonlinear- and Linear-VariationalProblem classes"""
# Copyright (C) 2016 Garth N. Wells
@@ -41,9 +39,9 @@ def test_linear_construction():
w = Function(V)
with pytest.raises(TypeError):
problem = LinearVariationalProblem(a, L)
- with pytest.raises(RuntimeError):
+ with pytest.raises(Exception):
problem = LinearVariationalProblem(a, L, [bc])
- with pytest.raises(RuntimeError):
+ with pytest.raises(Exception):
problem = LinearVariationalProblem(a, L, [bc], w)
problem = LinearVariationalProblem(a, L, w, [])
problem = LinearVariationalProblem(a, L, w, [bc])
@@ -64,7 +62,7 @@ def test_nonlinear_construction():
J = derivative(F, u, du)
bc = DirichletBC(V, 0.0, DomainBoundary())
- with pytest.raises(RuntimeError):
+ with pytest.raises(Exception):
problem = NonlinearVariationalProblem(F, u, J)
problem = NonlinearVariationalProblem(F, u)
problem = NonlinearVariationalProblem(F, u, [])
diff --git a/test/unit/python/function/test_constant.py b/test/unit/python/function/test_constant.py
index 3828406..2646b2e 100755
--- a/test/unit/python/function/test_constant.py
+++ b/test/unit/python/function/test_constant.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the function library"""
# Copyright (C) 2007 Anders Logg
@@ -72,7 +70,7 @@ def testGrad():
assert zero == gradient(c3)
- at pytest.mark.parametrize('mesh_factory', [(UnitCubeMesh, (8, 8, 8)), (UnitHexMesh.create, (8, 8, 8))])
+ at pytest.mark.parametrize('mesh_factory', [(UnitCubeMesh, (8, 8, 8)), (UnitCubeMesh.create, (8, 8, 8, CellType.Type_hexahedron))])
def test_compute_vertex_values(mesh_factory):
from numpy import zeros, all, array
@@ -118,3 +116,15 @@ def test_str():
c1 = Constant((1., 2., 3.))
c1.str(False)
c1.str(True)
+
+
+def test_assign():
+ c0 = Constant(1.)
+ assert c0.values() == (1,)
+ c0.assign(Constant(3))
+ assert c0.values() == (3,)
+
+ c1 = Constant([1, 2])
+ assert (c1.values() == (1, 2)).all()
+ c1.assign(Constant([3, 4]))
+ assert (c1.values() == (3, 4)).all()
diff --git a/test/unit/python/function/test_constrained_function_space.py b/test/unit/python/function/test_constrained_function_space.py
index dec863e..0f41f63 100755
--- a/test/unit/python/function/test_constrained_function_space.py
+++ b/test/unit/python/function/test_constrained_function_space.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for FunctionSpace with constrained domain"""
# Copyright (C) 2012-2014 Garth N. Wells
diff --git a/test/unit/python/function/test_expression.py b/test/unit/python/function/test_expression.py
index 0338efc..a3f3767 100755
--- a/test/unit/python/function/test_expression.py
+++ b/test/unit/python/function/test_expression.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the function library"""
# Copyright (C) 2007-2014 Anders Logg
@@ -226,11 +224,11 @@ def test_vector_valued_expression_member_function(mesh):
for f in fs:
u = Expression("f[0] + f[1] + f[2]", f=f, degree=1)
v = interpolate(u, V)
- assert np.allclose(v.vector().array(), 6.0)
+ assert np.allclose(v.vector().get_local(), 6.0)
for g in fs:
u.f = g
v = interpolate(u, V)
- assert np.allclose(v.vector().array(), 6.0)
+ assert np.allclose(v.vector().get_local(), 6.0)
# NOTE: Do we want this to work (attaching MeshFunctions to
@@ -241,7 +239,7 @@ def test_meshfunction_expression():
mesh = UnitSquareMesh(1, 1)
V = FunctionSpace(mesh, "DG", 0)
- c = CellFunction("size_t", mesh)
+ c = MeshFunction("size_t", mesh, mesh.topology().dim())
c[0] = 2
c[1] = 3
e = Expression("(double)c", c=c, degree=0)
@@ -663,7 +661,7 @@ def test_doc_string_complex_compiled_expression(mesh):
}
};'''
- cell_data = CellFunction('size_t', mesh)
+ cell_data = MeshFunction('size_t', mesh, mesh.topology().dim())
cell_data.set_all(3)
CompiledSubDomain("x[0] <= 0.25").mark(cell_data, 0)
CompiledSubDomain("x[0] > 0.25 && x[0] < 0.75").mark(cell_data, 1)
@@ -855,7 +853,7 @@ def test_doc_string_python_expressions(mesh):
def eval(self, values, x):
pass
- cell_data = CellFunction('size_t', square)
+ cell_data = MeshFunction('size_t', square, square.topology().dim())
P1 = FiniteElement("Lagrange", square.ufl_cell(), 1)
f3 = MyExpression2(square, cell_data, element=P1)
diff --git a/test/unit/python/function/test_function.py b/test/unit/python/function/test_function.py
index a6c74aa..906e36f 100755
--- a/test/unit/python/function/test_function.py
+++ b/test/unit/python/function/test_function.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Function class"""
# Copyright (C) 2011-2014 Garth N. Wells
@@ -76,6 +74,10 @@ def test_compute_vertex_values(V, W, mesh):
assert all(u_values == 1)
+ u_values2 = u.compute_vertex_values()
+
+ assert all(u_values == u_values2)
+
def test_assign(V, W):
from ufl.algorithms import replace
@@ -231,7 +233,6 @@ def test_axpy(V, W):
with pytest.raises(RuntimeError):
axpy + u
-
def test_call(R, V, W, mesh):
from numpy import zeros, all, array
u0 = Function(R)
@@ -268,7 +269,6 @@ def test_call(R, V, W, mesh):
with pytest.raises(TypeError):
u0([0, 0])
-
def test_constant_float_conversion():
c = Constant(3.45)
assert float(c) == 3.45
@@ -388,6 +388,25 @@ def test_extrapolation(V, pushpop_parameters):
assert f2.get_allow_extrapolation() is False
+ at skip_in_parallel
+def test_near_evaluations(R, mesh):
+ # Test that we allow point evaluation that are slightly outside
+ parameters["allow_extrapolation"] = False
+
+ u0 = Function(R)
+ u0.vector()[:] = 1.0
+ a = Vertex(mesh, 0).point()
+ offset = 0.99*DOLFIN_EPS
+
+ a_shift_x = Point(a[0] - offset, a[1], a[2])
+ assert round(u0(a) - u0(a_shift_x), 7) == 0
+
+ a_shift_xyz = Point(a[0] - offset / sqrt(3),
+ a[1] - offset / sqrt(3),
+ a[2] - offset / sqrt(3))
+ assert round(u0(a) - u0(a_shift_xyz), 7) == 0
+
+
def test_interpolation_jit_rank1(W):
f = Expression(("1.0", "1.0", "1.0"), degree=0)
w = interpolate(f, W)
diff --git a/test/unit/python/function/test_function_assigner.py b/test/unit/python/function/test_function_assigner.py
index 250aae1..4bf6d9d 100755
--- a/test/unit/python/function/test_function_assigner.py
+++ b/test/unit/python/function/test_function_assigner.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the FunctionAssigner class"""
# Copyright (C) 2013 Johan Hake
@@ -157,30 +155,30 @@ def test_1_1_assigner(w, ww, wr, wrr, q, r, qqv, u0, u1, u2, W, V, WW):
assigner = FunctionAssigner(W.sub(0), V)
assigner.assign(w.sub(0), u0)
- assert np.all(w.sub(0, deepcopy=True).vector().array() == u0.vector().array())
+ assert np.all(w.sub(0, deepcopy=True).vector().get_local() == u0.vector().get_local())
assign(w.sub(2), u2)
- assert np.all(w.sub(2, deepcopy=True).vector().array() == u2.vector().array())
+ assert np.all(w.sub(2, deepcopy=True).vector().get_local() == u2.vector().get_local())
assigner = FunctionAssigner(V, W.sub(2))
assigner.assign(u0, w.sub(2))
- assert np.all(u0.vector().array() == w.sub(2, deepcopy=True).vector().array())
+ assert np.all(u0.vector().get_local() == w.sub(2, deepcopy=True).vector().get_local())
assign(u1, w.sub(1))
- assert np.all(u1.vector().array() == w.sub(1, deepcopy=True).vector().array())
+ assert np.all(u1.vector().get_local() == w.sub(1, deepcopy=True).vector().get_local())
assigner = FunctionAssigner(WW.sub(0), W)
assigner.assign(ww.sub(0), w)
assign(wr.sub(0), w)
- assert np.all(wr.sub(0, deepcopy=True).vector().array() == w.vector().array())
+ assert np.all(wr.sub(0, deepcopy=True).vector().get_local() == w.vector().get_local())
assign(wr.sub(1), r)
- assert np.all(wr.sub(1, deepcopy=True).vector().array() == r.vector().array())
+ assert np.all(wr.sub(1, deepcopy=True).vector().get_local() == r.vector().get_local())
assign(qqv.sub(0).sub(0), q)
- assert np.all(qqv.sub(0).sub(0, deepcopy=True).vector().array() == q.vector().array())
+ assert np.all(qqv.sub(0).sub(0, deepcopy=True).vector().get_local() == q.vector().get_local())
with pytest.raises(RuntimeError):
assign(qqv.sub(0), q)
@@ -196,17 +194,17 @@ def test_N_1_assigner(u0, u1, u2, qq, qqv, rr, w, wrr, r, W, V):
assigner = FunctionAssigner(W, [V,V,V])
assigner.assign(vv, [u0, u1, u2])
- assert np.all(vv.sub(0, deepcopy=True).vector().array() == u0.vector().array())
- assert np.all(vv.sub(1, deepcopy=True).vector().array() == u1.vector().array())
- assert np.all(vv.sub(2, deepcopy=True).vector().array() == u2.vector().array())
+ assert np.all(vv.sub(0, deepcopy=True).vector().get_local() == u0.vector().get_local())
+ assert np.all(vv.sub(1, deepcopy=True).vector().get_local() == u1.vector().get_local())
+ assert np.all(vv.sub(2, deepcopy=True).vector().get_local() == u2.vector().get_local())
assign(qqv, [qq, u1])
- assert np.all(qqv.sub(0, deepcopy=True).vector().array() == qq.vector().array())
- assert np.all(qqv.sub(1, deepcopy=True).vector().array() == u1.vector().array())
+ assert np.all(qqv.sub(0, deepcopy=True).vector().get_local() == qq.vector().get_local())
+ assert np.all(qqv.sub(1, deepcopy=True).vector().get_local() == u1.vector().get_local())
assign(wrr, [w, rr])
- assert np.all(wrr.sub(0, deepcopy=True).vector().array() == w.vector().array())
- assert np.all(wrr.sub(1, deepcopy=True).vector().array() == rr.vector().array())
+ assert np.all(wrr.sub(0, deepcopy=True).vector().get_local() == w.vector().get_local())
+ assert np.all(wrr.sub(1, deepcopy=True).vector().get_local() == rr.vector().get_local())
with pytest.raises(RuntimeError):
assign(qqv, [qq, u1, u1])
@@ -219,11 +217,11 @@ def test_1_N_assigner(u0, u1, u2, w, qq, qqv, V, W):
assigner = FunctionAssigner([V,V,V], W)
assigner.assign([u0, u1, u2], w)
- assert np.all(w.sub(0, deepcopy=True).vector().array() == u0.vector().array())
- assert np.all(w.sub(1, deepcopy=True).vector().array() == u1.vector().array())
- assert np.all(w.sub(2, deepcopy=True).vector().array() == u2.vector().array())
+ assert np.all(w.sub(0, deepcopy=True).vector().get_local() == u0.vector().get_local())
+ assert np.all(w.sub(1, deepcopy=True).vector().get_local() == u1.vector().get_local())
+ assert np.all(w.sub(2, deepcopy=True).vector().get_local() == u2.vector().get_local())
assign([qq, u1], qqv)
- assert np.all(qqv.sub(0, deepcopy=True).vector().array() == qq.vector().array())
- assert np.all(qqv.sub(1, deepcopy=True).vector().array() == u1.vector().array())
+ assert np.all(qqv.sub(0, deepcopy=True).vector().get_local() == qq.vector().get_local())
+ assert np.all(qqv.sub(1, deepcopy=True).vector().get_local() == u1.vector().get_local())
diff --git a/test/unit/python/function/test_function_space.py b/test/unit/python/function/test_function_space.py
index ffb0785..eda294c 100755
--- a/test/unit/python/function/test_function_space.py
+++ b/test/unit/python/function/test_function_space.py
@@ -1,6 +1,4 @@
-#!/usr/bin/env py.test
-
-"""Unit tests for the FunctionSpace class"""
+b"""Unit tests for the FunctionSpace class"""
# Copyright (C) 2011 Johan Hake
#
@@ -27,39 +25,46 @@
import pytest
from dolfin import *
from ufl.log import UFLException
-
from dolfin_utils.test import fixture
+
@fixture
def mesh():
return UnitCubeMesh(8, 8, 8)
+
@fixture
def V(mesh):
return FunctionSpace(mesh, 'CG', 1)
+
@fixture
def W(mesh):
return VectorFunctionSpace(mesh, 'CG', 1)
+
@fixture
def Q(mesh):
W = VectorElement('CG', mesh.ufl_cell(), 1)
V = FiniteElement('CG', mesh.ufl_cell(), 1)
return FunctionSpace(mesh, W*V)
+
@fixture
def f(V):
return Function(V)
+
@fixture
def V2(f):
return f.function_space()
+
@fixture
def g(W):
return Function(W)
+
@fixture
def W2(g):
return g.function_space()
@@ -81,6 +86,7 @@ def test_python_interface(V, V2, W, W2, Q):
assert W.id() == W2.id()
assert V.id() == V2.id()
+
def test_component(V, W, Q):
assert not W.component()
assert not V.component()
@@ -89,12 +95,14 @@ def test_component(V, W, Q):
assert Q.sub(0).component()[0] == 0
assert Q.sub(1).component()[0] == 1
+
def test_equality(V, V2, W, W2):
assert V == V
assert V == V2
assert W == W
assert W == W2
+
def test_inclusion(V, Q):
assert V.contains(V)
assert not Q.contains(V)
@@ -143,10 +151,12 @@ def test_boundary(mesh):
assert Vb.dim() == 768
assert Wb.dim() == 1158
+
def test_not_equal(W, V, W2, V2):
assert W != V
assert W2 != V2
+
def test_sub_equality(W, Q):
assert W.sub(0) == W.sub(0)
assert W.sub(0) != W.sub(1)
@@ -154,11 +164,16 @@ def test_sub_equality(W, Q):
assert W.sub(1) == W.extract_sub_space([1])
assert Q.sub(0) == Q.extract_sub_space([0])
+
def test_in_operator(f, g, V, V2, W, W2):
assert f in V
assert f in V2
assert g in W
assert g in W2
+ if has_pybind11():
+ with pytest.raises(RuntimeError):
+ mesh() in V
+
def test_collapse(W, V):
Vs = W.sub(2)
@@ -173,9 +188,12 @@ def test_collapse(W, V):
f1 = Function(Vc)
assert len(f0.vector()) == len(f1.vector())
+
def test_argument_equality(mesh, V, V2, W, W2):
"""Placed this test here because it's mainly about detecting differing
-function spaces."""
+ function spaces.
+
+ """
mesh2 = UnitCubeMesh(8, 8, 8)
V3 = FunctionSpace(mesh2, 'CG', 1)
W3 = VectorFunctionSpace(mesh2, 'CG', 1)
diff --git a/test/unit/python/function/test_lagrange_interpolator.py b/test/unit/python/function/test_lagrange_interpolator.py
index 23be5e0..1ea794e 100755
--- a/test/unit/python/function/test_lagrange_interpolator.py
+++ b/test/unit/python/function/test_lagrange_interpolator.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for interpolation using LagrangeInterpolator"""
# Copyright (C) 2014 Mikael Mortensen
diff --git a/test/unit/python/function/test_nonmatching_interpolation.py b/test/unit/python/function/test_nonmatching_interpolation.py
index ec66348..285bac2 100755
--- a/test/unit/python/function/test_nonmatching_interpolation.py
+++ b/test/unit/python/function/test_nonmatching_interpolation.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for evaluating functions on non-matching meshes"""
# Copyright (C) 2013 Garth N. Wells
diff --git a/test/unit/python/function/test_special_functions.py b/test/unit/python/function/test_special_functions.py
index efc95cd..e09449d 100755
--- a/test/unit/python/function/test_special_functions.py
+++ b/test/unit/python/function/test_special_functions.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the function library"""
# Copyright (C) 2011 Kristian B. Oelgaard
diff --git a/test/unit/python/geometry/test_bounding_box_tree.py b/test/unit/python/geometry/test_bounding_box_tree.py
index 0a4e382..3d6f14c 100755
--- a/test/unit/python/geometry/test_bounding_box_tree.py
+++ b/test/unit/python/geometry/test_bounding_box_tree.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for BoundingBoxTree"""
# Copyright (C) 2013-2014 Anders Logg
@@ -85,7 +83,6 @@ def test_compute_collisions_point_3d():
if dim != tdim - 1 and dim != tdim - 2:
assert set(entities) == reference[dim]
-
#--- compute_collisions with tree ---
@skip_in_parallel
@@ -172,7 +169,7 @@ def test_compute_collisions_tree_3d():
assert set(entities_A) == references[i][0]
assert set(entities_B) == references[i][1]
-#--- compute_entity_collisions ---
+#--- compute_entity_collisions with point ---
@skip_in_parallel
def test_compute_entity_collisions_1d():
@@ -307,7 +304,7 @@ def test_compute_entity_collisions_tree_3d():
assert set(entities_A) == references[i][0]
assert set(entities_B) == references[i][1]
-#--- compute_first_collision ---
+#--- compute_first_collision with point ---
@skip_in_parallel
def test_compute_first_collision_1d():
@@ -377,7 +374,7 @@ def test_compute_first_collision_3d():
first = tree.compute_first_collision(p)
assert first in reference[mesh.topology().dim()]
-#--- compute_first_entity_collision ---
+#--- compute_first_entity_collision with point ---
@skip_in_parallel
def test_compute_first_entity_collision_1d():
@@ -427,7 +424,7 @@ def test_compute_first_entity_collision_3d():
first = tree.compute_first_entity_collision(p)
assert first in reference
-#--- compute_closest_entity ---
+#--- compute_closest_entity with point ---
@skip_in_parallel
def test_compute_closest_entity_1d():
diff --git a/test/unit/python/geometry/test_collision_detection.py b/test/unit/python/geometry/test_collision_detection.py
index 366fa2e..f125292 100755
--- a/test/unit/python/geometry/test_collision_detection.py
+++ b/test/unit/python/geometry/test_collision_detection.py
@@ -1,6 +1,4 @@
-#!/usr/bin/env py.test
-
-"""Unit tests for the CollisionDetection class"""
+"""Unit tests for the CollisionPredicates class"""
# Copyright (C) 2014 Anders Logg and August Johansson
#
@@ -20,33 +18,32 @@
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
# First added: 2014-02-16
-# Last changed: 2014-05-30
+# Last changed: 2017-09-21
import pytest
from dolfin import *
-from dolfin_utils.test import skip_in_parallel
+from dolfin_utils.test import skip_in_parallel, skip_if_not_pybind11
import numpy as np
-
@skip_in_parallel
-def create_triangular_mesh_3D():
+def create_triangular_mesh_3D(vertices, cells):
editor = MeshEditor()
mesh = Mesh()
- editor.open(mesh, 'triangle', 2, 3)
+ editor.open(mesh,"triangle", 2,3)
editor.init_cells(2)
editor.init_vertices(4)
- editor.add_cell(0, np.array([0,1,2], dtype='uint'))
- editor.add_cell(1, np.array([1,2,3], dtype='uint'))
- editor.add_vertex(0, np.array([0,0,0.5], dtype='float'))
- editor.add_vertex(1, np.array([1,0,0.5], dtype='float'))
- editor.add_vertex(2, np.array([0,1,0.5], dtype='float'))
- editor.add_vertex(3, np.array([1,1,0.5], dtype='float'))
+ editor.add_cell(0, cells[0])
+ editor.add_cell(1, cells[1])
+
+ editor.add_vertex(0, vertices[0])
+ editor.add_vertex(1, vertices[1])
+ editor.add_vertex(2, vertices[2])
+ editor.add_vertex(3, vertices[3])
editor.close()
return mesh;
-
@skip_in_parallel
-def test_inteval_collides_point():
+def test_interval_collides_point():
"""Test if point collide with interval"""
mesh = UnitIntervalMesh(1)
@@ -55,6 +52,71 @@ def test_inteval_collides_point():
assert cell.collides(Point(0.5)) == True
assert cell.collides(Point(1.5)) == False
+ at skip_in_parallel
+def test_segment_collides_point_2D():
+ """Test if segment collide with point in 2D"""
+ mesh = Mesh()
+ editor = MeshEditor()
+ editor.open(mesh, "interval", 1, 2)
+ editor.init_vertices(2)
+ editor.init_cells(1)
+ a = np.array( (1./8., 1./4.), dtype='float')
+ b = np.array( (2./8., 3./4.), dtype='float')
+ editor.add_vertex(0, a)
+ editor.add_vertex(1, b)
+ editor.add_cell(0, np.array( (0,1), dtype='uint'))
+ editor.close()
+ cell = Cell(mesh, 0)
+ mid = Point(1.5/8., 2./4.)
+ mid_average = (a + b) / 2
+ assert cell.contains(mid)
+ assert cell.contains(Point(a[0], a[1]))
+ assert cell.contains(cell.midpoint())
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_point_on_segment():
+ a = Point(1e-30, 0)
+ b = Point(1e-3, 0)
+ c = Point(0, 0)
+ d = Point(-1e-30, 0)
+ q0 = Point(1, 0)
+ q1 = Point(0, 0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, a)
+ assert cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, b)
+ assert cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, c)
+ assert not cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, d)
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_point_on_small_segment():
+ a = Point(1e-30, 0)
+ b = Point(0, 0)
+ c = Point(1e-31, 0)
+ d = Point(-1e-30, 0)
+ q0 = Point(0, 0)
+ q1 = Point(1e-30, 0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, a)
+ assert cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, b)
+ assert cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, c)
+ assert not cpp.geometry.CollisionPredicates.collides_segment_point_2d(q0, q1, d)
+
+ at skip_in_parallel
+def test_segment_collides_point_3D():
+ """Test if segment collide with point in 3D"""
+ mesh = Mesh()
+ editor = MeshEditor()
+ editor.open(mesh, "interval", 1, 3)
+ editor.init_vertices(2)
+ editor.init_cells(1)
+ editor.add_vertex(0, np.array( (1./16., 1./8., 1./4.), dtype='float'))
+ editor.add_vertex(1, np.array( ( 2./16., 3./8., 2./4.), dtype='float'))
+ editor.add_cell(0, np.array( (0,1), dtype='uint') )
+ editor.close()
+ cell = Cell(mesh, 0)
+ mid = Point(1.5/16., 2./8., 1.5/4.)
+ assert cell.contains(mid)
+ assert cell.contains(cell.midpoint())
@skip_in_parallel
def test_triangle_collides_point():
@@ -66,16 +128,36 @@ def test_triangle_collides_point():
assert cell.collides(Point(0.5)) == True
assert cell.collides(Point(1.5)) == False
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_degenerate_triangle_collides_point():
+ """Test a degenerate triangle that does not collide"""
+
+ p0 = Point(-0.10950608157830554745,0.14049391842169450806)
+ p1 = Point(-0.10950608157830354905,0.14049391842169650646)
+ p2 = Point(0.32853262580480108168,0.57853262580480113719)
+ q = Point(3.5952674716233090635e-06,0.25000359526747162331)
+
+ assert cpp.geometry.CollisionPredicates.collides_triangle_point_2d(p0, p1, p2, q) == False
@skip_in_parallel
@pytest.mark.xfail(strict=True, raises=RuntimeError)
def test_quadrilateral_collides_point():
- """Tests if point collide with triangle"""
+ mesh = UnitSquareMesh.create(1, 1, CellType.Type_quadrilateral)
+ cell = Cell(mesh, 0)
+ assert cell.collides(Point(0.5)) == True
+ assert cell.collides(Point(1.5)) == False
- mesh = UnitQuadMesh.create(1, 1)
+ at skip_in_parallel
+ at pytest.mark.xfail(strict=True, raises=RuntimeError)
+def test_hexahedron_collides_point():
+ """Test if point collide with hexahedron"""
+ mesh = UnitCubeMesh.create(1, 1, 1, CellType.Type_hexahedron)
cell = Cell(mesh, 0)
assert cell.collides(Point(0.5)) == True
+ # FIXME: cell.collides(Point) returns True for any 1D, 2D Point
+ # cell.collides(Point) returns False if Point[2] != 0
assert cell.collides(Point(1.5)) == False
@@ -93,48 +175,87 @@ def test_triangle_collides_triangle():
assert c0.collides(c0) == True
assert c0.collides(c1) == True
- # assert c0.collides(c2) == False # touching edges
+ assert c0.collides(c2) == True # touching edges
assert c1.collides(c0) == True
assert c1.collides(c1) == True
- assert c1.collides(c2) == False
- # assert c2.collides(c0) == False # touching edges
- assert c2.collides(c1) == False
+ assert c1.collides(c2) == True
+ assert c2.collides(c0) == True # touching edges
+ assert c2.collides(c1) == True
assert c2.collides(c2) == True
@skip_in_parallel
-def test_tetrahedron_collides_point():
- """Test if point collide with tetrahedron"""
-
- mesh = UnitCubeMesh(1, 1, 1)
- cell = Cell(mesh, 0)
+ at skip_if_not_pybind11
+def test_triangle_triangle_collision() :
+ "Test that has been failing"
+ assert cpp.geometry.CollisionPredicates.collides_triangle_triangle_2d(Point(0.177432070718943, 0.5),
+ Point(0.176638957524249, 0.509972290857582),
+ Point(0.217189283468892, 0.550522616802225),
+ Point(0.333333333333333, 0.52399308981973),
+ Point(0.333333333333333, 0.666666666666667),
+ Point(0.211774439087554, 0.545107772420888))
- assert cell.collides(Point(0.5)) == True
- assert cell.collides(Point(1.5)) == False
@skip_in_parallel
- at pytest.mark.xfail(strict=True, raises=RuntimeError)
-def test_hexahedron_collides_point():
- """Test if point collide with hexahedron"""
+def test_triangle_collides_point_3D():
+ """Test if point collide with triangle (inspired by test_manifold_dg0_functions)"""
+ vertices = [ np.array( (0.0, 0.0, 1.0), dtype='float'),
+ np.array( (1.0, 1.0, 1.0), dtype='float'),
+ np.array( (1.0, 0.0, 0.0), dtype='float'),
+ np.array( (0.0, 1.0, 0.0), dtype='float') ]
+ cells = [ np.array( (0, 1, 2), dtype='uint'),
+ np.array( (0, 1, 3), dtype='uint') ]
+ mesh = create_triangular_mesh_3D(vertices, cells)
+ points = [ Point(0.0, 0.0, 1.0),
+ Point(1.0, 1.0, 1.0),
+ Point(1.0, 0.0, 0.0),
+ Point(0.0, 1.0, 0.0),
+ Point(0.25, 0.5, 0.75),
+ Point(0.5, 0.25, 0.75)
+ ]
+ A = Cell(mesh, 0)
+ B = Cell(mesh, 1)
+ assert A.collides(points[0]) == True
+ assert B.collides(points[0]) == True
+ assert A.collides(points[1]) == True
+ assert B.collides(points[1]) == True
+ assert A.collides(points[2]) == True
+ assert B.collides(points[2]) == False
+ assert A.collides(points[3]) == False
+ assert B.collides(points[3]) == True
+ assert A.collides(points[4]) == False
+ assert B.collides(points[4]) == True
+ assert A.collides(points[5]) == True
+ assert B.collides(points[5]) == False
+
+#@pytest.mark.skipif(True, reason="Not implemented in 3D")
+ at skip_in_parallel
+def test_tetrahedron_collides_point():
+ """Test if point collide with tetrahedron"""
- mesh = UnitHexMesh.create(1, 1, 1)
+ mesh = UnitCubeMesh(1, 1, 1)
cell = Cell(mesh, 0)
assert cell.collides(Point(0.5)) == True
- # FIXME: cell.collides(Point) returns True for any 1D, 2D Point
- # cell.collides(Point) returns False if Point[2] != 0
assert cell.collides(Point(1.5)) == False
-
@skip_in_parallel
+#@pytest.mark.skipif(True, reason="Not implemented in 3D")
def test_tetrahedron_collides_triangle():
"""Test if point collide with tetrahedron"""
tetmesh = UnitCubeMesh(2, 2, 2)
- trimesh = create_triangular_mesh_3D()
+ vertices = [ np.array( (0, 0, 0.5), dtype='float'),
+ np.array( (1, 0, 0.5), dtype='float'),
+ np.array( (0, 1, 0.5), dtype='float'),
+ np.array( (1, 1, 0.5), dtype='float')]
+ cells = [ np.array( (0, 1, 2), dtype='uint'),
+ np.array( (1, 2, 3), dtype='uint')]
+
+ trimesh = create_triangular_mesh_3D(vertices, cells)
dx = Point(0.1, 0.1, -0.1)
- trimesh_shift = create_triangular_mesh_3D()
+ trimesh_shift = create_triangular_mesh_3D(vertices, cells)
trimesh_shift.translate(dx)
tet0 = Cell(tetmesh, 18)
@@ -156,8 +277,8 @@ def test_tetrahedron_collides_triangle():
assert tet1.collides(tri0) == True
assert tri0.collides(tet1) == True
-
@skip_in_parallel
+#@pytest.mark.skipif(True, reason="Not implemented in 3D")
def test_tetrahedron_collides_tetrahedron():
"""Test if point collide with tetrahedron"""
@@ -192,48 +313,3 @@ def test_tetrahedron_collides_tetrahedron():
# touching faces
assert c3.collides(c43) == True
assert c43.collides(c3) == True
-
-
-def _test_collision_robustness_2d(aspect, y, step):
- nx = 10
- ny = int(aspect*nx)
- mesh = UnitSquareMesh(nx, ny, 'crossed')
- bb = mesh.bounding_box_tree()
-
- x = 0.0
- p = Point(x, y)
- while x <= 1.0:
- c = bb.compute_first_entity_collision(Point(x, y))
- assert c < np.uintc(-1)
- x += step
-
-def _test_collision_robustness_3d(aspect, y, z, step):
- nx = nz = 10
- ny = int(aspect*nx)
- mesh = UnitCubeMesh(nx, ny, nz)
- bb = mesh.bounding_box_tree()
-
- x = 0.0
- while x <= 1.0:
- c = bb.compute_first_entity_collision(Point(x, y, z))
- assert c < np.uintc(-1)
- x += step
-
- at skip_in_parallel
- at pytest.mark.slow
-def test_collision_robustness_slow():
- """Test cases from https://bitbucket.org/fenics-project/dolfin/issue/296"""
- _test_collision_robustness_2d( 100, 1e-14, 1e-5)
- _test_collision_robustness_2d( 40, 1e-03, 1e-5)
- _test_collision_robustness_2d( 100, 0.5 + 1e-14, 1e-5)
- _test_collision_robustness_2d(4.43, 0.5, 4.03e-6)
- _test_collision_robustness_3d( 100, 1e-14, 1e-14, 1e-5)
-
- at skip_in_parallel
- at pytest.mark.skipif(True, reason='Very slow test cases')
-def test_collision_robustness_very_slow():
- """Test cases from https://bitbucket.org/fenics-project/dolfin/issue/296"""
- _test_collision_robustness_2d( 10, 1e-16, 1e-7)
- _test_collision_robustness_2d(4.43, 1e-17, 4.03e-6)
- _test_collision_robustness_2d( 40, 0.5, 1e-6)
- _test_collision_robustness_2d( 10, 0.5 + 1e-16, 1e-7)
diff --git a/test/unit/python/geometry/test_collision_segment_segment.py b/test/unit/python/geometry/test_collision_segment_segment.py
new file mode 100755
index 0000000..97bdf0c
--- /dev/null
+++ b/test/unit/python/geometry/test_collision_segment_segment.py
@@ -0,0 +1,137 @@
+"""Unit tests for the CollisionPredicates class"""
+
+# Copyright (C) 2014 Anders Logg and August Johansson
+ #
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# First added: 2014-02-16
+# Last changed: 2016-11-22
+
+import pytest
+from dolfin import *
+from dolfin_utils.test import skip_in_parallel, skip_if_not_pybind11
+import numpy as np
+
+ at skip_in_parallel
+def create_mesh(a, b):
+ editor = MeshEditor()
+ mesh = Mesh()
+ editor.open(mesh, "interval", 1, 2)
+ editor.init_cells(1)
+ editor.init_vertices(2)
+ editor.add_cell(0, np.array( (0, 1), dtype='uint') )
+ editor.add_vertex(0, np.array( (a.x(), a.y()), dtype='float'))
+ editor.add_vertex(1, np.array( (b.x(), b.y()), dtype='float'))
+ editor.close()
+ return mesh;
+
+ at skip_in_parallel
+def test_L_version_1():
+ mesh0 = create_mesh(Point(0., 0.), Point(1., 0.))
+ mesh1 = create_mesh(Point(0., 0.), Point(0., 1.))
+ cell0 = Cell(mesh0, 0)
+ cell1 = Cell(mesh1, 0)
+ assert cell0.collides(cell1) == True
+
+ at skip_in_parallel
+def test_L_version_2():
+ # mesh0 = create_mesh(Point(np.finfo(np.float32).eps, 0.), Point(1., 0.))
+ # mesh0 = create_mesh(Point(eps(), 0.), Point(1., 0.))
+ mesh0 = create_mesh(Point(2.23e-15, 0.), Point(1., 0.))
+ mesh1 = create_mesh(Point(0., 0.), Point(0., 1.))
+ # print(mesh0.str(True))
+ # print(mesh1.str(True))
+ cell0 = Cell(mesh0, 0)
+ cell1 = Cell(mesh1, 0)
+ assert cell0.collides(cell1) == False
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_L_version_3():
+ # mesh0 = create_mesh(Point(np.finfo(np.float32).eps, 0.), Point(1., 0.))
+ # mesh0 = create_mesh(Point(eps(), 0.), Point(1., 0.))
+ a = Point(2.23e-100, 0.) # assume shewchuk works
+ b = Point(1., 0.)
+ c = Point(0., 0.)
+ d = Point(0., 1.)
+
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(a, b, c, d) == False
+
+ at skip_in_parallel
+def test_aligned_version_1():
+ mesh0 = create_mesh(Point(0,0), Point(1,0))
+ mesh1 = create_mesh(Point(1,0), Point(2,0))
+ cell0 = Cell(mesh0, 0)
+ cell1 = Cell(mesh1, 0)
+ assert cell0.collides(cell1) == True
+
+ at skip_in_parallel
+def test_aligned_version_2():
+ mesh0 = create_mesh(Point(0,0), Point(1,0))
+ mesh1 = create_mesh(Point(2,0), Point(3,0))
+ cell0 = Cell(mesh0, 0)
+ cell1 = Cell(mesh1, 0)
+ assert cell0.collides(cell1) == False
+
+ at skip_in_parallel
+def test_collinear_1():
+ p0 = Point(0.05, 0.15)
+ p1 = Point(0.85, 0.95)
+ q0 = Point(0.2875, 0.3875)
+ q1 = Point(0.6125, 0.7125)
+ meshp = create_mesh(p0, p1)
+ meshq = create_mesh(q0, q1)
+ cellp = Cell(meshp, 0)
+ cellq = Cell(meshq, 0)
+ assert cellp.collides(cellq) == True
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_collinear_2():
+ res = cpp.geometry.CollisionPredicates.collides_segment_segment_2d(Point(.5, .3),
+ Point(.5, .4),
+ Point(.5, .5),
+ Point(.5, .6))
+ assert not res
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_segment_segment_2d():
+ # p0 is on segment q0-q1
+ p0 = Point(1e-30, 0)
+ p1 = Point(1, 2)
+ p2 = Point(2, 1)
+ q0 = Point(1, 0)
+ q1 = Point(0, 0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p1, q0, q1)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p1, p0, q0, q1)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p1, q1, q0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p1, p0, q1, q0)
+
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p2, q0, q1)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p2, p0, q0, q1)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p2, q1, q0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p2, p0, q1, q0)
+
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p1, q1, q0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p1, p0, q1, q0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p1, q0, q1)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p1, p0, q0, q1)
+
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p2, q1, q0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p2, p0, q1, q0)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p0, p2, q0, q1)
+ assert cpp.geometry.CollisionPredicates.collides_segment_segment_2d(p2, p0, q0, q1)
diff --git a/test/unit/python/geometry/test_coordinates.py b/test/unit/python/geometry/test_coordinates.py
index 73f4b63..2be99d7 100755
--- a/test/unit/python/geometry/test_coordinates.py
+++ b/test/unit/python/geometry/test_coordinates.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for coordinates interface"""
# Copyright (C) 2016 Jan Blechta
diff --git a/test/unit/python/geometry/test_geometry_issues.py b/test/unit/python/geometry/test_geometry_issues.py
new file mode 100755
index 0000000..4697403
--- /dev/null
+++ b/test/unit/python/geometry/test_geometry_issues.py
@@ -0,0 +1,163 @@
+"""Unit tests for intersection computation"""
+
+# Copyright (C) 2013 Anders Logg
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# First added: 2013-12-09
+# Last changed: 2017-02-16
+
+from __future__ import print_function
+import pytest
+
+from dolfin import *
+from dolfin_utils.test import skip_in_parallel
+import numpy as np
+
+ at skip_in_parallel
+def test_issue_97():
+ "Test from Mikael Mortensen (issue #97)"
+
+ N = 2
+ L = 1000
+ mesh = BoxMesh(Point(0, 0, 0), Point(L, L, L), N, N, N)
+ V = FunctionSpace(mesh, 'CG', 1)
+ v = interpolate(Expression('x[0]', degree=1), V)
+ x = Point(0.5*L, 0.5*L, 0.5*L)
+ vx = v(x)
+
+ at skip_in_parallel
+def test_issue_168():
+ "Test from Torsten Wendav (issue #168)"
+
+ mesh = UnitCubeMesh(14, 14, 14)
+ V = FunctionSpace(mesh, "Lagrange", 1)
+ v = Function(V)
+ x = (0.75, 0.25, 0.125)
+ vx = v(x)
+
+
+ at pytest.mark.skipif(True, reason="Since cell.contains(point) is doing an exact calculation, we cannot assume that the midpoint is exactly in the cell")
+ at skip_in_parallel
+def test_segment_collides_point_3D_2():
+ """Test case by Oyvind from https://bitbucket.org/fenics-project/dolfin/issue/296 for segment point collision in 3D"""
+ mesh = Mesh()
+ editor = MeshEditor()
+ editor.open(mesh, 1, 3)
+ editor.init_vertices(2)
+ editor.init_cells(1)
+ editor.add_vertex(0, np.array( (41.06309891, 63.74219894, 68.10320282), dtype='float') )
+ editor.add_vertex(1, np.array( (41.45830154, 62.61560059, 66.43019867), dtype='float') )
+ editor.add_cell(0, np.array( (0,1), dtype='uint'))
+ editor.close()
+ cell = Cell(mesh, 0)
+ assert cell.contains(cell.midpoint())
+
+
+def _test_collision_robustness_2d(aspect, y, step):
+ nx = 10
+ ny = int(aspect*nx)
+ mesh = UnitSquareMesh(nx, ny, 'crossed')
+ bb = mesh.bounding_box_tree()
+
+ x = 0.0
+ p = Point(x, y)
+ while x <= 1.0:
+ c = bb.compute_first_entity_collision(Point(x, y))
+ assert c < np.uintc(-1)
+ x += step
+
+#@pytest.mark.skipif(True, reason="Not implemented in 3D")
+ at skip_in_parallel
+def _test_collision_robustness_3d(aspect, y, z, step):
+ nx = nz = 10
+ ny = int(aspect*nx)
+ mesh = UnitCubeMesh(nx, ny, nz)
+ bb = mesh.bounding_box_tree()
+
+ x = 0.0
+ while x <= 1.0:
+ c = bb.compute_first_entity_collision(Point(x, y, z))
+ assert c < np.uintc(-1)
+ x += step
+
+ at skip_in_parallel
+ at pytest.mark.slow
+def test_collision_robustness_slow():
+ """Test cases from https://bitbucket.org/fenics-project/dolfin/issue/296"""
+ _test_collision_robustness_2d( 100, 1e-14, 1e-5)
+ _test_collision_robustness_2d( 40, 1e-03, 1e-5)
+ _test_collision_robustness_2d( 100, 0.5 + 1e-14, 1e-5)
+ _test_collision_robustness_2d(4.43, 0.5, 4.03e-6)
+ _test_collision_robustness_3d( 100, 1e-14, 1e-14, 1e-5)
+
+ at skip_in_parallel
+ at pytest.mark.slow
+ at pytest.mark.skipif(True, reason='Very slow test cases')
+def test_collision_robustness_very_slow():
+ """Test cases from https://bitbucket.org/fenics-project/dolfin/issue/296"""
+ _test_collision_robustness_2d( 10, 1e-16, 1e-7)
+ _test_collision_robustness_2d(4.43, 1e-17, 4.03e-6)
+ _test_collision_robustness_2d( 40, 0.5, 1e-6)
+ _test_collision_robustness_2d( 10, 0.5 + 1e-16, 1e-7)
+
+ at skip_in_parallel
+def test_points_on_line():
+ """Test case from https://bitbucket.org/fenics-project/dolfin/issues/790"""
+ big = 1e6
+ p1 = np.array((0.1, 0.06), dtype='float')
+ p3 = np.array((big*2.1, big*0.1), dtype='float')
+ p2 = np.array((0.0, big*3.0), dtype='float')
+ p0 = np.array((big*3.0, 0.0), dtype='float')
+
+ mesh = Mesh()
+ ed = MeshEditor()
+ ed.open(mesh, "triangle", 2, 2)
+ ed.init_cells(3)
+ ed.init_vertices(4)
+ ed.add_vertex(0, p0)
+ ed.add_vertex(1, p1)
+ ed.add_vertex(2, p2)
+ ed.add_vertex(3, p3)
+ ed.add_cell(0, np.array( (2, 3, 0), dtype='uint'))
+ ed.add_cell(1, np.array( (0, 1, 3 ), dtype='uint'))
+ ed.add_cell(2, np.array( (1, 2, 3 ), dtype='uint'))
+ ed.close()
+
+ # xdmf = XDMFFile("a.xdmf")
+ # xdmf.write(mesh)
+
+ # print mesh.cells()
+ # print mesh.coordinates()
+
+ bb = mesh.bounding_box_tree()
+
+ # Find a point on line somewhere between p3 and p1
+ j = 4
+ pq = (p3*j + p1*(50-j))/50.0
+ c = bb.compute_entity_collisions(Point(pq[0], pq[1]))
+ # print pq.str(), c
+
+ # Check that the neighbouring points are in the correct cells
+ cell_numbers = [1, 2, 2, 1, 1, 2, 1, 1, 2]
+ step = 1e-6
+ cnt = 0
+ for i in range(-1, 2):
+ for j in range(-1, 2):
+ pt = Point(pq[0], pq[1]) + Point(step*i, step*j)
+ c = bb.compute_entity_collisions(pt)
+ assert c[0] == cell_numbers[cnt]
+ cnt += 1
diff --git a/test/unit/python/geometry/test_intersection.py b/test/unit/python/geometry/test_intersection.py
index 13ab034..e83f027 100755
--- a/test/unit/python/geometry/test_intersection.py
+++ b/test/unit/python/geometry/test_intersection.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for intersection computation"""
# Copyright (C) 2013-2014 Anders Logg
@@ -23,7 +21,7 @@ from __future__ import print_function
import pytest
from dolfin import intersect
-from dolfin import UnitIntervalMesh, UnitSquareMesh, UnitCubeMesh, BoxMesh, UnitQuadMesh, UnitHexMesh
+from dolfin import (UnitIntervalMesh, UnitSquareMesh, UnitCubeMesh, BoxMesh, CellType)
from dolfin import Point, FunctionSpace, Expression, interpolate
from dolfin import MPI, mpi_comm_world
@@ -68,7 +66,7 @@ def test_mesh_point_2d_quadrilateral():
"Test mesh-point intersection in 2D for quadrilateral mesh"
point = Point(0.1, 0.2)
- mesh = UnitQuadMesh.create(16, 16)
+ mesh = UnitSquareMesh.create(16, 16, CellType.Type_quadrilateral)
intersection = intersect(mesh, point)
@@ -80,7 +78,7 @@ def test_mesh_point_3d_hexahedron():
"Test mesh-point intersection in 3D for hexahedral mesh"
point = Point(0.1, 0.2, 0.3)
- mesh = UnitHexMesh.create(8, 8, 8)
+ mesh = UnitCubeMesh.create(8, 8, 8, CellType.Type_hexahedron)
intersection = intersect(mesh, point)
diff --git a/test/unit/python/geometry/test_intersection_construction.py b/test/unit/python/geometry/test_intersection_construction.py
new file mode 100755
index 0000000..6fd27be
--- /dev/null
+++ b/test/unit/python/geometry/test_intersection_construction.py
@@ -0,0 +1,323 @@
+"""Unit tests for the IntersectionConstruction class"""
+
+# Copyright (C) 2014 Anders Logg and August Johansson
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
+import pytest
+import numpy as np
+from dolfin import *
+from six.moves import xrange as range
+from dolfin_utils.test import skip_in_parallel, skip_if_not_pybind11
+
+def triangulation_to_mesh_2d(triangulation):
+ editor = MeshEditor()
+ mesh = Mesh()
+ editor.open(mesh, 2, 2)
+ num_cells = len(triangulation) // 6
+ num_vertices = len(triangulation) // 2
+ editor.init_cells(num_cells)
+ editor.init_vertices(num_vertices)
+ for i in range(num_cells):
+ editor.add_cell(i, np.array( (3*i, 3*i + 1, 3*i + 2), dtype='uint') )
+ for i in range(num_vertices):
+ editor.add_vertex(i, np.array( (triangulation[2*i], triangulation[2*i + 1]), dtype='float'))
+ editor.close()
+ return mesh
+
+def triangulation_to_mesh_2d_3d(triangulation):
+ editor = MeshEditor()
+ mesh = Mesh()
+ editor.open(mesh,2,3)
+ num_cells = len(triangulation) // 9
+ num_vertices = len(triangulation) // 3
+ editor.init_cells(num_cells)
+ editor.init_vertices(num_vertices)
+ for i in range(num_cells):
+ editor.add_cell(i, np.array( (3*i, 3*i+1, 3*i+2), dtype='uint'))
+ for i in range(num_vertices):
+ editor.add_vertex(i, np.array( (triangulation[3*i], triangulation[3*i+1], triangulation[3*i+2]), dtype='float') )
+ editor.close()
+ return mesh
+
+def triangulation_to_mesh_3d(triangulation):
+ editor = MeshEditor()
+ mesh = Mesh()
+ editor.open(mesh,3,3)
+ num_cells = len(triangulation) // 12
+ num_vertices = len(triangulation) // 3
+ editor.init_cells(num_cells)
+ editor.init_vertices(num_vertices)
+ for i in range(num_cells):
+ editor.add_cell(i, np.array( (4*i, 4*i+1, 4*i+2, 4*i+3), dtype='uint'))
+ for i in range(num_vertices):
+ editor.add_vertex(i, np.array( (triangulation[3*i], triangulation[3*i+1], triangulation[3*i+2]), dtype='float'))
+ editor.close()
+ return mesh
+
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="Missing swig typemap")
+def test_triangulate_intersection_2d():
+
+ # Create two meshes of the unit square
+ mesh_0 = UnitSquareMesh(1, 1)
+ mesh_1 = UnitSquareMesh(1, 1)
+
+ # Translate second mesh randomly
+ #dx = Point(np.random.rand(),np.random.rand())
+ dx = Point(0.278498, 0.546881)
+ mesh_1.translate(dx)
+
+ # Exact volume of intersection
+ exactvolume = (1 - abs(dx[0]))*(1 - abs(dx[1]))
+
+ # Compute triangulation volume
+ volume = 0
+ for c0 in cells(mesh_0):
+ for c1 in cells(mesh_1):
+ intersection = c0.intersection(c1)
+ if len(intersection) >= 3 :
+ triangulation = cpp.geometry.ConvexTriangulation.triangulate(intersection, 2, 2)
+ tmesh = triangulation_to_mesh_2d(triangulation)
+ for t in cells(tmesh):
+ volume += t.volume()
+
+ errorstring = "translation=" + str(dx[0]) + str(" ") + str(dx[1])
+ assert round(volume - exactvolume, 7) == 0, errorstring
+
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="Not implemented in 3D")
+def test_triangulate_intersection_2d_3d():
+
+ # Note: this test will fail if the triangle mesh is aligned
+ # with the tetrahedron mesh
+
+ # Create a unit cube
+ mesh_0 = UnitCubeMesh(1,1,1)
+
+ # Create a 3D surface mesh
+ editor = MeshEditor()
+ mesh_1 = Mesh()
+ editor.open(mesh_1,2,3)
+ editor.init_cells(2)
+ editor.init_vertices(4)
+
+ # Add cells
+ editor.add_cell(0, np.array( (0,1,2), dtype='uint'))
+ editor.add_cell(1, np.array( (1,2,3), dtype='uint'))
+
+ # Add vertices
+ editor.add_vertex(0, np.array( (0, 0, 0.5), dtype='float'))
+ editor.add_vertex(1, np.array( (1, 0, 0.5), dtype='float'))
+ editor.add_vertex(2, np.array( (0, 1, 0.5), dtype='float'))
+ editor.add_vertex(3, np.array( (1, 1, 0.5), dtype='float'))
+ editor.close()
+
+ # Rotate the triangle mesh around y axis
+ angle = 23.46354
+ mesh_1.rotate(angle,1)
+
+ # Exact area
+ exact_volume = 1
+
+ # Compute triangulation
+ volume = 0
+ for c0 in cells(mesh_0):
+ for c1 in cells(mesh_1):
+ intersection = c0.intersection(c1)
+ triangulation = cpp.geometry.ConvexTriangulation.triangulate(intersection, 3, 2)
+ if (triangulation.size>0):
+ tmesh = triangulation_to_mesh_2d_3d(triangulation)
+ for t in cells(tmesh):
+ volume += t.volume()
+
+ errorstring = "rotation angle = " + str(angle)
+ assert round(volume - exact_volume, 7) == 0, errorstring
+
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="Missing swig typemap for call to ConvexTriangulation")
+def test_triangulate_intersection_3d():
+
+ # Create two meshes of the unit cube
+ mesh_0 = UnitCubeMesh(1, 1, 1)
+ mesh_1 = UnitCubeMesh(1, 1, 1)
+
+ # Translate second mesh
+ # dx = Point(np.random.rand(),np.random.rand(),np.random.rand())
+ dx = Point(0.913375, 0.632359, 0.097540)
+
+ mesh_1.translate(dx)
+ exactvolume = (1 - abs(dx[0]))*(1 - abs(dx[1]))*(1 - abs(dx[2]))
+
+ # Compute triangulation
+ volume = 0
+ for c0 in cells(mesh_0):
+ for c1 in cells(mesh_1):
+ intersection = c0.intersection(c1)
+ triangulation = cpp.geometry.ConvexTriangulation.triangulate(intersection, 3, 3)
+ if (triangulation.size>0):
+ tmesh = triangulation_to_mesh_3d(triangulation)
+ for t in cells(tmesh):
+ volume += t.volume()
+
+ errorstring = "translation="
+ errorstring += str(dx[0])+" "+str(dx[1])+" "+str(dx[2])
+ assert round(volume - exactvolume, 7) == 0, errorstring
+
+ at skip_if_not_pybind11
+def test_triangle_triangle_2d_trivial() :
+ " These two triangles intersect in a common edge"
+ res = cpp.geometry.IntersectionConstruction.intersection_triangle_triangle_2d(Point(0.0, 0.0),
+ Point(1.0, 0.0),
+ Point(0.5, 1.0),
+ Point(0.5, 0.5),
+ Point(1.0, 1.5),
+ Point(0.0, 1.5))
+ assert len(res) == 4
+
+ at skip_if_not_pybind11
+def test_triangle_triangle_2d() :
+ " These two triangles intersect in a common edge"
+ res = cpp.geometry.IntersectionConstruction.intersection_triangle_triangle_2d(Point(0.4960412972015322, 0.3953317542541379),
+ Point(0.5, 0.3997044273055517),
+ Point(0.5, 0.4060889538943557),
+ Point(0.4960412972015322, 0.3953317542541379),
+ Point(0.5, 0.4060889538943557),
+ Point(.5, .5))
+ for p in res:
+ print(p[0], p[1])
+
+ assert len(res) == 2
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_parallel_segments_2d():
+ " These two segments should be parallel and the intersection computed accordingly"
+ p0 = Point(0, 0)
+ p1 = Point(1, 0)
+ q0 = Point(0.4, 0)
+ q1 = Point(1.4, 0)
+ intersection = cpp.geometry.IntersectionConstruction.intersection_segment_segment_2d(p0, p1, q0, q1)
+ assert len(intersection) == 2
+
+ at skip_if_not_pybind11
+def test_equal_segments_2d():
+ " These two segments are equal and the intersection computed accordingly"
+ p0 = Point(DOLFIN_PI / 7., 9. / DOLFIN_PI)
+ p1 = Point(9. / DOLFIN_PI, DOLFIN_PI / 7.)
+ q0 = Point(DOLFIN_PI / 7., 9. / DOLFIN_PI)
+ q1 = Point(9. / DOLFIN_PI, DOLFIN_PI / 7.)
+ intersection = cpp.geometry.IntersectionConstruction.intersection_segment_segment_2d(p0, p1, q0, q1)
+ assert len(intersection) == 2
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_triangle_segment_2D_1():
+ "The intersection of a specific triangle and a specific segment"
+ p0 = Point(1e-30, 0)
+ p1 = Point(1, 2)
+ p2 = Point(2, 1)
+ q0 = Point(1, 0)
+ q1 = Point(0, 0)
+ intersection = cpp.geometry.IntersectionConstruction.intersection_triangle_segment_2d(p0, p1, p2, q0, q1)
+ assert len(intersection) == 1
+ intersection = cpp.geometry.IntersectionConstruction.intersection_triangle_segment_2d(p0, p1, p2, q1, q0)
+ assert len(intersection) == 1
+
+def compare_with_cgal(p0, p1, q0, q1, cgal):
+ intersection = cpp.geometry.IntersectionConstruction.intersection_segment_segment_2d(p0, p1, q0, q1)
+
+ #for p in intersection:
+ # print(*p)
+
+ return abs(intersection[0][0] - cgal[0]) < DOLFIN_EPS and \
+ abs(intersection[0][1] - cgal[1]) < DOLFIN_EPS
+
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="This is a case where the intersection currently fails")
+def test_segment_segment_1():
+ "Case that fails CGAL comparison. We get a different intersection point but still correct area."
+ p0 = Point(-0.50000000000000710543,-0.50000000000000710543)
+ p1 = Point(0.99999999999999955591,-2)
+ q0 = Point(0.9142135623730932581,-1.9142135623730944793)
+ q1 = Point(-0.29289321881346941367,-0.70710678118654635149)
+
+ # The intersection should according to CGAL be
+ cgal = Point(0.91066799144849319703, -1.9106679914484945293)
+
+ assert compare_with_cgal(p0, p1, q0, q1, cgal)
+
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="This is a case where the intersection currently fails")
+def test_segment_segment_2():
+ "Case that fails CGAL comparison. We get a different intersection point but still correct area."
+ p0 = Point(0.70710678118654746172,-0.70710678118654746172)
+ p1 = Point(0.70710678118654612945,0.70710678118654612945)
+ q0 = Point(0.70710678118654612945,0.70710678118654113344)
+ q1 = Point(0.70710678118654657354,0.2928932188134645842)
+ cgal = Point(0.70710678118654612945, 0.7071067811865050512)
+ assert compare_with_cgal(p0, p1, q0, q1, cgal)
+
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+#@pytest.mark.skipif(True, reason="This test needs to be updated")
+def test_segment_segment_3():
+ "Case that fails CGAL comparison. We get a different intersection point but still correct area."
+ p0 = Point(0.70710678118654746172,-0.70710678118654746172)
+ p1 = Point(0.70710678118654612945,0.70710678118654612945)
+ q0 = Point(0.70710678118654757274,-0.097631072937819973756)
+ q1 = Point(0.70710678118654257673,-0.1601886205085209236)
+ cgal = Point(0.70710678118654679558, -0.10611057050352221132)
+ assert compare_with_cgal(p0, p1, q0, q1, cgal)
+
+
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="This is a case where the intersection currently fails")
+def test_segment_segment_4():
+ "Case that fails CGAL comparison. We get a different intersection point but still correct area."
+ p0 = Point(0.70710678118654746172,-0.70710678118654746172)
+ p1 = Point(3.5527136788005009294e-14,3.5527136788005009294e-14)
+ q0 = Point(0.35355339059326984508,-0.35355339059327078877)
+ q1 = Point(0.70710678118655057034,-0.70710678118654701763)
+ cgal = Point(0.67572340116162599166, -0.67572340116162288304)
+ assert compare_with_cgal(p0, p1, q0, q1, cgal)
+
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_segment_segment_5():
+ "Case that failed CGAL comparison but passed when scaling the numerator in x = p0 + o / d * v"
+ p0 = Point(1.1429047494274684563e-12,0.5)
+ p1 = Point(0.42146018366139809119,0.9214601836602551721)
+ q0 = Point(0.34292036732279607136,0.8429203673205103442)
+ q1 = Point(0.3429203673205103442,0.8429203673205103442)
+ cgal = Point(0.3429203673216533188,0.8429203673205103442)
+ assert compare_with_cgal(p0, p1, q0, q1, cgal)
+
+
+ at skip_in_parallel
+ at skip_if_not_pybind11
+def test_segment_segment_6():
+ "Test that demonstrates, among other things, that we must check the orientation for p0, p1 in intersection_segment_segment_2d"
+ p0 = Point(0.045342566799435518599,0.41358248517265505662);
+ p1 = Point(0.045342566799434436131,0.41358248517265394639);
+ q0 = Point(1.8601965322712701917e-16,0.5);
+ q1 = Point(1.873501354054951662e-16,0.3499999999999999778);
+
+ intersection = cpp.geometry.IntersectionConstruction.intersection_segment_segment_2d(p0, p1, q0, q1)
+
+ assert len(intersection) == 0
diff --git a/test/unit/python/geometry/test_intersection_triangulation.py b/test/unit/python/geometry/test_intersection_triangulation.py
deleted file mode 100755
index 2471cac..0000000
--- a/test/unit/python/geometry/test_intersection_triangulation.py
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/env py.test
-
-"""Unit tests for the IntersectionTriangulation class"""
-
-# Copyright (C) 2014 Anders Logg and August Johansson
-#
-# This file is part of DOLFIN.
-#
-# DOLFIN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# DOLFIN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-
-import pytest
-import numpy
-from dolfin import *
-from six.moves import xrange as range
-from dolfin_utils.test import skip_in_parallel
-
-def triangulation_to_mesh_2d(triangulation):
- editor = MeshEditor()
- mesh = Mesh()
- editor.open(mesh, 'triangle', 2, 2)
- num_cells = len(triangulation) // 6
- num_vertices = len(triangulation) // 2
- editor.init_cells(num_cells)
- editor.init_vertices(num_vertices)
- for i in range(num_cells):
- editor.add_cell(i, numpy.array([3*i, 3*i + 1, 3*i + 2], dtype='uint'))
- for i in range(num_vertices):
- editor.add_vertex(i, numpy.array([triangulation[2*i], triangulation[2*i + 1]], dtype='float'))
- editor.close()
- return mesh
-
-def triangulation_to_mesh_2d_3d(triangulation):
- editor = MeshEditor()
- mesh = Mesh()
- editor.open(mesh, 'triangle', 2,3)
- num_cells = len(triangulation) // 9
- num_vertices = len(triangulation) // 3
- editor.init_cells(num_cells)
- editor.init_vertices(num_vertices)
- for i in range(num_cells):
- editor.add_cell(i, numpy.array([3*i, 3*i+1, 3*i+2], dtype='uint'))
- for i in range(num_vertices):
- editor.add_vertex(i, numpy.array([triangulation[3*i], triangulation[3*i+1], triangulation[3*i+2]], dtype='float'))
- editor.close()
- return mesh
-
-def triangulation_to_mesh_3d(triangulation):
- editor = MeshEditor()
- mesh = Mesh()
- editor.open(mesh, 'tetrahedron', 3, 3)
- num_cells = len(triangulation) // 12
- num_vertices = len(triangulation) // 3
- editor.init_cells(num_cells)
- editor.init_vertices(num_vertices)
- for i in range(num_cells):
- editor.add_cell(i, numpy.array([4*i, 4*i+1, 4*i+2, 4*i+3], dtype='uint'))
- for i in range(num_vertices):
- editor.add_vertex(i, numpy.array([triangulation[3*i], triangulation[3*i+1], triangulation[3*i+2]], dtype='float'))
- editor.close()
- return mesh
-
- at skip_in_parallel
-def test_triangulate_intersection_2d():
-
- # Create two meshes of the unit square
- mesh_0 = UnitSquareMesh(1, 1)
- mesh_1 = UnitSquareMesh(1, 1)
-
- # Translate second mesh randomly
- #dx = Point(numpy.random.rand(),numpy.random.rand())
- dx = Point(0.278498, 0.546881)
- mesh_1.translate(dx)
-
- exactvolume = (1 - abs(dx[0]))*(1 - abs(dx[1]))
-
- # Compute triangulation
- volume = 0
- for c0 in cells(mesh_0):
- for c1 in cells(mesh_1):
- triangulation = c0.triangulate_intersection(c1)
- if (len(triangulation) > 0):
- tmesh = triangulation_to_mesh_2d(triangulation)
- for t in cells(tmesh):
- volume += t.volume()
-
- errorstring = "translation=" + str(dx[0]) + str(" ") + str(dx[1])
- assert round(volume - exactvolume, 7) == 0, errorstring
-
- at skip_in_parallel
-def test_triangulate_intersection_2d_3d():
-
- # Note: this test will fail if the triangle mesh is aligned
- # with the tetrahedron mesh
-
- # Create a unit cube
- mesh_0 = UnitCubeMesh(1,1,1)
-
- # Create a 3D surface mesh
- editor = MeshEditor()
- mesh_1 = Mesh()
- editor.open(mesh_1, 'triangle', 2, 3)
- editor.init_cells(2)
- editor.init_vertices(4)
- # add cells
- editor.add_cell(0, numpy.array([0,1,2], dtype='uint'))
- editor.add_cell(1, numpy.array([1,2,3], dtype='uint'))
- # add vertices
- editor.add_vertex(0, numpy.array([0,0,0.5], dtype='float'))
- editor.add_vertex(1, numpy.array([1,0,0.5], dtype='float'))
- editor.add_vertex(2, numpy.array([0,1,0.5], dtype='float'))
- editor.add_vertex(3, numpy.array([1,1,0.5], dtype='float'))
- editor.close()
-
- # Rotate the triangle mesh around y axis a random angle in
- # (0,90) degrees
- #angle = numpy.random.rand()*90
- angle = 23.46354
- mesh_1.rotate(angle,1)
-
- # Exact area
- exactvolume = 1
-
- # Compute triangulation
- volume = 0
- for c0 in cells(mesh_0):
- for c1 in cells(mesh_1):
- triangulation = c0.triangulate_intersection(c1)
- if (len(triangulation) > 0):
- tmesh = triangulation_to_mesh_2d_3d(triangulation)
- for t in cells(tmesh):
- volume += t.volume()
-
- errorstring = "rotation angle = " + str(angle)
- assert round(volume - exactvolume, 7) == 0, errorstring
-
-
- at skip_in_parallel
-def test_triangulate_intersection_3d():
-
- # Create two meshes of the unit cube
- mesh_0 = UnitCubeMesh(1, 1, 1)
- mesh_1 = UnitCubeMesh(1, 1, 1)
-
- # Translate second mesh
- # dx = Point(numpy.random.rand(),numpy.random.rand(),numpy.random.rand())
- dx = Point(0.913375, 0.632359, 0.097540)
-
- mesh_1.translate(dx)
- exactvolume = (1 - abs(dx[0]))*(1 - abs(dx[1]))*(1 - abs(dx[2]))
-
- # Compute triangulation
- volume = 0
- for c0 in cells(mesh_0):
- for c1 in cells(mesh_1):
- triangulation = c0.triangulate_intersection(c1)
- if (len(triangulation) > 0):
- tmesh = triangulation_to_mesh_3d(triangulation)
- for t in cells(tmesh):
- volume += t.volume()
-
- errorstring = "translation="
- errorstring += str(dx[0])+" "+str(dx[1])+" "+str(dx[2])
- assert round(volume - exactvolume, 7) == 0, errorstring
diff --git a/test/unit/python/geometry/test_point.py b/test/unit/python/geometry/test_point.py
index bdda9c5..3c43a51 100644
--- a/test/unit/python/geometry/test_point.py
+++ b/test/unit/python/geometry/test_point.py
@@ -22,7 +22,7 @@ import pytest
import numpy as np
from dolfin_utils.test import skip_if_pybind11
-from dolfin import Point
+from dolfin import *
def test_point_getitem():
@@ -69,7 +69,6 @@ def test_point_setitem():
assert np.all(p[:] == (2, 4, 6))
- at skip_if_pybind11
def test_point_array():
p = Point(1, 2, 3)
assert np.all(p.array() == (1, 2, 3))
@@ -77,3 +76,11 @@ def test_point_array():
# Point.array() is a copy, no in-place modification
p.array()[:] += 1000.0
assert np.all(p.array() == (1, 2, 3))
+
+
+def test_point_equality():
+ p = Point(1.23, 2, DOLFIN_PI)
+ q = Point(1.23, 2, DOLFIN_PI)
+ r = Point(1.23+DOLFIN_EPS, 2, DOLFIN_PI)
+ assert p == q
+ assert p != r
diff --git a/test/unit/python/graph/test_graph_build.py b/test/unit/python/graph/test_graph_build.py
index 12b124d..d66bf5f 100755
--- a/test/unit/python/graph/test_graph_build.py
+++ b/test/unit/python/graph/test_graph_build.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for graph building"""
# Copyright (C) 2013 Garth N. Wells
diff --git a/test/unit/python/io/test_HDF5.py b/test/unit/python/io/test_HDF5.py
index ca16059..55d4fde 100755
--- a/test/unit/python/io/test_HDF5.py
+++ b/test/unit/python/io/test_HDF5.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the HDF5 io library"""
# Copyright (C) 2012 Garth N. Wells
@@ -194,7 +192,7 @@ def test_save_and_read_function(tempdir):
hdf5_file = HDF5File(mesh.mpi_comm(), filename, "r")
hdf5_file.read(F1, "/function")
result = F0.vector() - F1.vector()
- assert len(result.array().nonzero()[0]) == 0
+ assert len(result.get_local().nonzero()[0]) == 0
hdf5_file.close()
@skip_if_not_HDF5
@@ -214,9 +212,9 @@ def test_save_and_read_mesh_2D(tempdir):
mesh_file.read(mesh1, "/my_mesh", False)
mesh_file.close()
- assert mesh0.size_global(0) == mesh1.size_global(0)
+ assert mesh0.num_entities_global(0) == mesh1.num_entities_global(0)
dim = mesh0.topology().dim()
- assert mesh0.size_global(dim) == mesh1.size_global(dim)
+ assert mesh0.num_entities_global(dim) == mesh1.num_entities_global(dim)
@skip_if_not_HDF5
@xfail_with_serial_hdf5_in_parallel
@@ -235,9 +233,9 @@ def test_save_and_read_mesh_3D(tempdir):
mesh_file.read(mesh1, "/my_mesh", False)
mesh_file.close()
- assert mesh0.size_global(0) == mesh1.size_global(0)
+ assert mesh0.num_entities_global(0) == mesh1.num_entities_global(0)
dim = mesh0.topology().dim()
- assert mesh0.size_global(dim) == mesh1.size_global(dim)
+ assert mesh0.num_entities_global(dim) == mesh1.num_entities_global(dim)
@skip_if_not_HDF5
@xfail_with_serial_hdf5_in_parallel
diff --git a/test/unit/python/io/test_HDF5_attribute.py b/test/unit/python/io/test_HDF5_attribute.py
index 6582d6e..1c6425b 100755
--- a/test/unit/python/io/test_HDF5_attribute.py
+++ b/test/unit/python/io/test_HDF5_attribute.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Attribute interface of the HDF5 io library"""
# Copyright (C) 2013 Chris Richardson
diff --git a/test/unit/python/io/test_HDF5_series.py b/test/unit/python/io/test_HDF5_series.py
index 4cd619a..5f0260e 100644
--- a/test/unit/python/io/test_HDF5_series.py
+++ b/test/unit/python/io/test_HDF5_series.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the HDF5 io library - timeseries io"""
# Copyright (C) 2014 Chris Richardson
@@ -56,5 +54,5 @@ def test_save_and_read_function_timeseries(tempdir):
timestamp = hdf5_file.attributes(vec_name)["timestamp"]
assert timestamp == t
result = F0.vector() - F1.vector()
- assert len(result.array().nonzero()[0]) == 0
+ assert len(result.get_local().nonzero()[0]) == 0
hdf5_file.close()
diff --git a/test/unit/python/io/test_SVG.py b/test/unit/python/io/test_SVG.py
index 58e81b8..6f5580a 100755
--- a/test/unit/python/io/test_SVG.py
+++ b/test/unit/python/io/test_SVG.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for SVG output"""
# Copyright (C) 2012 Anders Logg
diff --git a/test/unit/python/io/test_X3D.py b/test/unit/python/io/test_X3D.py
index 9135e20..f9cd197 100755
--- a/test/unit/python/io/test_X3D.py
+++ b/test/unit/python/io/test_X3D.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
# Copyright (C) 2013 Garth N. Wells
#
# This file is part of DOLFIN.
@@ -40,13 +38,13 @@ def test_save_mesh3D(cd_tempdir):
def test_save_cell_meshfunction2D(cd_tempdir):
mesh = UnitSquareMesh(16, 16)
- mf = CellFunction("size_t", mesh, 12)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim(), 12)
file = File("cell_mf2D.x3d")
file << mf
def test_save_facet_meshfunction2D(cd_tempdir):
mesh = UnitSquareMesh(16, 16)
- mf = FacetFunction("size_t", mesh, 12)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim()-1, 12)
file = File("facet_mf2D.x3d")
#with pytest.raises(RuntimeError):
# file << mf
@@ -54,14 +52,14 @@ def test_save_facet_meshfunction2D(cd_tempdir):
def test_save_cell_meshfunctio22D(cd_tempdir):
mesh = UnitCubeMesh(16, 16, 16)
- mf = CellFunction("size_t", mesh, 12)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim(), 12)
file = File("cell_mf3D.x3d")
file << mf
def test_save_facet_meshfunction3D(cd_tempdir):
mesh = UnitCubeMesh(16, 16, 16)
- mf = FacetFunction("size_t", mesh, 12)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim()-1, 12)
file = File("facet_mf3D.x3d")
#with pytest.raises(RuntimeError):
# file << mf
diff --git a/test/unit/python/io/test_XDMF.py b/test/unit/python/io/test_XDMF.py
index 13b16e9..01c415e 100755
--- a/test/unit/python/io/test_XDMF.py
+++ b/test/unit/python/io/test_XDMF.py
@@ -18,7 +18,7 @@
import pytest
import os
from dolfin import *
-from dolfin_utils.test import skip_in_parallel, fixture, tempdir
+from dolfin_utils.test import skip_in_parallel, fixture, tempdir, skip_if_not_pybind11
# Supported XDMF file encoding
@@ -55,6 +55,19 @@ def invalid_config(encoding):
def invalid_fe(fe_family, fe_degree):
return (fe_family == "CG" and fe_degree == 0)
+
+ at pytest.fixture
+def worker_id(request):
+ """Return worker ID when using pytest-xdist to run tests in
+ parallell
+
+ """
+ if hasattr(request.config, 'slaveinput'):
+ return request.config.slaveinput['slaveid']
+ else:
+ return 'master'
+
+
@pytest.mark.parametrize("encoding", encodings)
def test_save_and_load_1d_mesh(tempdir, encoding):
if invalid_config(encoding):
@@ -68,9 +81,9 @@ def test_save_and_load_1d_mesh(tempdir, encoding):
mesh2 = Mesh()
with XDMFFile(mpi_comm_world(), filename) as file:
file.read(mesh2)
- assert mesh.size_global(0) == mesh2.size_global(0)
+ assert mesh.num_entities_global(0) == mesh2.num_entities_global(0)
dim = mesh.topology().dim()
- assert mesh.size_global(dim) == mesh2.size_global(dim)
+ assert mesh.num_entities_global(dim) == mesh2.num_entities_global(dim)
@pytest.mark.parametrize("encoding", encodings)
@@ -86,9 +99,9 @@ def test_save_and_load_2d_mesh(tempdir, encoding):
mesh2 = Mesh()
with XDMFFile(mpi_comm_world(), filename) as file:
file.read(mesh2)
- assert mesh.size_global(0) == mesh2.size_global(0)
+ assert mesh.num_entities_global(0) == mesh2.num_entities_global(0)
dim = mesh.topology().dim()
- assert mesh.size_global(dim) == mesh2.size_global(dim)
+ assert mesh.num_entities_global(dim) == mesh2.num_entities_global(dim)
@pytest.mark.parametrize("encoding", encodings)
@@ -96,7 +109,7 @@ def test_save_and_load_2d_quad_mesh(tempdir, encoding):
if invalid_config(encoding):
pytest.skip("XDMF unsupported in current configuration")
filename = os.path.join(tempdir, "mesh_2D_quad.xdmf")
- mesh = UnitQuadMesh.create(32, 32)
+ mesh = UnitSquareMesh.create(32, 32, CellType.Type_quadrilateral)
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.write(mesh, encoding)
@@ -104,9 +117,9 @@ def test_save_and_load_2d_quad_mesh(tempdir, encoding):
mesh2 = Mesh()
with XDMFFile(mpi_comm_world(), filename) as file:
file.read(mesh2)
- assert mesh.size_global(0) == mesh2.size_global(0)
+ assert mesh.num_entities_global(0) == mesh2.num_entities_global(0)
dim = mesh.topology().dim()
- assert mesh.size_global(dim) == mesh2.size_global(dim)
+ assert mesh.num_entities_global(dim) == mesh2.num_entities_global(dim)
@pytest.mark.parametrize("encoding", encodings)
@@ -122,9 +135,9 @@ def test_save_and_load_3d_mesh(tempdir, encoding):
mesh2 = Mesh()
with XDMFFile(mpi_comm_world(), filename) as file:
file.read(mesh2)
- assert mesh.size_global(0) == mesh2.size_global(0)
+ assert mesh.num_entities_global(0) == mesh2.num_entities_global(0)
dim = mesh.topology().dim()
- assert mesh.size_global(dim) == mesh2.size_global(dim)
+ assert mesh.num_entities_global(dim) == mesh2.num_entities_global(dim)
@pytest.mark.parametrize("encoding", encodings)
@@ -171,7 +184,7 @@ def test_save_and_checkpoint_scalar(tempdir, encoding, fe_degree, fe_family,
file.read_checkpoint(u_in, "u_out", 0)
result = u_in.vector() - u_out.vector()
- assert all([near(x, 0.0) for x in result.array()])
+ assert all([near(x, 0.0) for x in result.get_local()])
@pytest.mark.parametrize("encoding", encodings)
@@ -208,7 +221,7 @@ def test_save_and_checkpoint_vector(tempdir, encoding, fe_degree, fe_family,
file.read_checkpoint(u_in, "u_out", 0)
result = u_in.vector() - u_out.vector()
- assert all([near(x, 0.0) for x in result.array()])
+ assert all([near(x, 0.0) for x in result.get_local()])
@pytest.mark.parametrize("encoding", encodings)
@@ -237,7 +250,7 @@ def test_save_and_checkpoint_timeseries(tempdir, encoding):
for i, p in enumerate(times):
result = u_in[i].vector() - u_out[i].vector()
- assert all([near(x, 0.0) for x in result.array()])
+ assert all([near(x, 0.0) for x in result.get_local()])
# test reading last
with XDMFFile(mesh.mpi_comm(), filename) as file:
@@ -245,7 +258,7 @@ def test_save_and_checkpoint_timeseries(tempdir, encoding):
file.read_checkpoint(u_in_last, "u_out", -1)
result = u_out[-1].vector() - u_in_last.vector()
- assert all([near(x, 0.0) for x in result.array()])
+ assert all([near(x, 0.0) for x in result.get_local()])
@pytest.mark.parametrize("encoding", encodings)
@@ -356,7 +369,7 @@ def test_save_1d_mesh(tempdir, encoding):
pytest.skip("XDMF unsupported in current configuration")
filename = os.path.join(tempdir, "mf_1D.xdmf")
mesh = UnitIntervalMesh(32)
- mf = CellFunction("size_t", mesh)
+ mf = MeshFunction("size_t", mesh, mesh.topology().dim())
for cell in cells(mesh):
mf[cell] = cell.index()
@@ -374,7 +387,7 @@ def test_save_2D_cell_function(tempdir, encoding, data_type):
filename = os.path.join(tempdir, "mf_2D_%s.xdmf" % dtype_str)
mesh = UnitSquareMesh(32, 32)
- mf = CellFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, mesh.topology().dim())
mf.rename("cells", "cells")
for cell in cells(mesh):
mf[cell] = dtype(cell.index())
@@ -382,7 +395,7 @@ def test_save_2D_cell_function(tempdir, encoding, data_type):
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.write(mf, encoding)
- mf_in = CellFunction(dtype_str, mesh)
+ mf_in = MeshFunction(dtype_str, mesh, mesh.topology().dim())
with XDMFFile(mesh.mpi_comm(), filename) as xdmf:
xdmf.read(mf_in, "cells")
@@ -401,7 +414,7 @@ def test_save_3D_cell_function(tempdir, encoding, data_type):
dtype_str, dtype = data_type
mesh = UnitCubeMesh(8, 8, 8)
- mf = CellFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, mesh.topology().dim())
mf.rename("cells", "cells")
for cell in cells(mesh):
mf[cell] = dtype(cell.index())
@@ -410,7 +423,7 @@ def test_save_3D_cell_function(tempdir, encoding, data_type):
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.write(mf, encoding)
- mf_in = CellFunction(dtype_str, mesh)
+ mf_in = MeshFunction(dtype_str, mesh, mesh.topology().dim())
with XDMFFile(mesh.mpi_comm(), filename) as xdmf:
xdmf.read(mf_in, "cells")
@@ -428,7 +441,7 @@ def test_save_2D_facet_function(tempdir, encoding, data_type):
dtype_str, dtype = data_type
mesh = UnitSquareMesh(32, 32)
- mf = FacetFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, mesh.topology().dim()-1)
mf.rename("facets", "facets")
if (MPI.size(mesh.mpi_comm()) == 1):
@@ -442,7 +455,7 @@ def test_save_2D_facet_function(tempdir, encoding, data_type):
with XDMFFile(mesh.mpi_comm(), filename) as xdmf:
xdmf.write(mf, encoding)
- mf_in = FacetFunction(dtype_str, mesh)
+ mf_in = MeshFunction(dtype_str, mesh, mesh.topology().dim()-1)
with XDMFFile(mesh.mpi_comm(), filename) as xdmf:
xdmf.read(mf_in, "facets")
@@ -460,7 +473,7 @@ def test_save_3D_facet_function(tempdir, encoding, data_type):
dtype_str, dtype = data_type
mesh = UnitCubeMesh(8, 8, 8)
- mf = FacetFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, mesh.topology().dim()-1)
mf.rename("facets", "facets")
if (MPI.size(mesh.mpi_comm()) == 1):
@@ -474,7 +487,7 @@ def test_save_3D_facet_function(tempdir, encoding, data_type):
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.write(mf, encoding)
- mf_in = FacetFunction(dtype_str, mesh)
+ mf_in = MeshFunction(dtype_str, mesh, mesh.topology().dim()-1)
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.read(mf_in, "facets")
@@ -492,7 +505,7 @@ def test_save_3D_edge_function(tempdir, encoding, data_type):
dtype_str, dtype = data_type
mesh = UnitCubeMesh(8, 8, 8)
- mf = EdgeFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, 1)
mf.rename("edges", "edges")
for edge in edges(mesh):
mf[edge] = dtype(edge.index())
@@ -511,7 +524,7 @@ def test_save_2D_vertex_function(tempdir, encoding, data_type):
dtype_str, dtype = data_type
mesh = UnitSquareMesh(32, 32)
- mf = VertexFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, 0)
mf.rename("vertices", "vertices")
for vertex in vertices(mesh):
mf[vertex] = dtype(vertex.global_index())
@@ -520,7 +533,7 @@ def test_save_2D_vertex_function(tempdir, encoding, data_type):
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.write(mf, encoding)
- mf_in = VertexFunction(dtype_str, mesh)
+ mf_in = MeshFunction(dtype_str, mesh, 0)
with XDMFFile(mesh.mpi_comm(), filename) as xdmf:
xdmf.read(mf_in, "vertices")
@@ -539,14 +552,14 @@ def test_save_3D_vertex_function(tempdir, encoding, data_type):
filename = os.path.join(tempdir, "mf_vertex_3D_%s.xdmf" % dtype_str)
mesh = UnitCubeMesh(8, 8, 8)
- mf = VertexFunction(dtype_str, mesh)
+ mf = MeshFunction(dtype_str, mesh, 0)
for vertex in vertices(mesh):
mf[vertex] = dtype(vertex.index())
with XDMFFile(mesh.mpi_comm(), filename) as file:
file.write(mf, encoding)
-
+ at skip_if_not_pybind11
@pytest.mark.parametrize("encoding", encodings)
def test_save_points_2D(tempdir, encoding):
if invalid_config(encoding):
@@ -567,6 +580,7 @@ def test_save_points_2D(tempdir, encoding):
file.write(points, vals, encoding)
+ at skip_if_not_pybind11
@pytest.mark.parametrize("encoding", encodings)
def test_save_points_3D(tempdir, encoding):
if invalid_config(encoding):
@@ -597,7 +611,7 @@ def test_save_mesh_value_collection(tempdir, encoding, data_type):
mesh = UnitCubeMesh(4, 4, 4)
tdim = mesh.topology().dim()
- meshfn = CellFunction(dtype_str, mesh, 0)
+ meshfn = MeshFunction(dtype_str, mesh, mesh.topology().dim(), False)
meshfn.rename("volume_marker", "Volume Markers")
for c in cells(mesh):
if c.midpoint().y() > 0.1:
@@ -669,11 +683,11 @@ def test_append_and_load_mesh_functions(tempdir, encoding, data_type):
for mesh in meshes:
dim = mesh.topology().dim()
- vf = VertexFunction(dtype_str, mesh)
+ vf = MeshFunction(dtype_str, mesh, 0)
vf.rename("vertices", "vertices")
- ff = FacetFunction(dtype_str, mesh)
+ ff = MeshFunction(dtype_str, mesh, mesh.topology().dim()-1)
ff.rename("facets", "facets")
- cf = CellFunction(dtype_str, mesh)
+ cf = MeshFunction(dtype_str, mesh, mesh.topology().dim())
cf.rename("cells", "cells")
if (MPI.size(mesh.mpi_comm()) == 1):
@@ -700,11 +714,11 @@ def test_append_and_load_mesh_functions(tempdir, encoding, data_type):
xdmf.write(cf, encoding)
with XDMFFile(mesh.mpi_comm(), filename) as xdmf:
- vf_in = VertexFunction(dtype_str, mesh)
+ vf_in = MeshFunction(dtype_str, mesh, 0)
xdmf.read(vf_in, "vertices")
- ff_in = FacetFunction(dtype_str, mesh)
+ ff_in = MeshFunction(dtype_str, mesh, mesh.topology().dim()-1)
xdmf.read(ff_in, "facets")
- cf_in = CellFunction(dtype_str, mesh)
+ cf_in = MeshFunction(dtype_str, mesh, mesh.topology().dim())
xdmf.read(cf_in, "cells")
diff = 0
diff --git a/test/unit/python/io/test_XDMF_cell_output.py b/test/unit/python/io/test_XDMF_cell_output.py
index d76e7e9..1aa0d36 100644
--- a/test/unit/python/io/test_XDMF_cell_output.py
+++ b/test/unit/python/io/test_XDMF_cell_output.py
@@ -41,5 +41,5 @@ def test_xdmf_cell_scalar_ghost(cd_tempdir, ghost_mode):
vec = Vector()
hdf.read(vec, "/VisualisationVector/0", False)
- area = MPI.sum(mesh.mpi_comm(), sum(vec.array()))
+ area = MPI.sum(mesh.mpi_comm(), sum(vec.get_local()))
assert abs(n*n - area) < 1e-9
diff --git a/test/unit/python/io/test_XMLFunction.py b/test/unit/python/io/test_XMLFunction.py
index 55c08de..ab86032 100755
--- a/test/unit/python/io/test_XMLFunction.py
+++ b/test/unit/python/io/test_XMLFunction.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env py.test
"""Unit tests for the XML input/output of Function"""
# Copyright (C) 2014 Matthias Liertzer
@@ -39,4 +38,4 @@ def test_save_and_read_xml_function(cd_tempdir):
xml_file >> F1
result = F0.vector() - F1.vector()
- assert len(result.array().nonzero()[0]) == 0
+ assert len(result.get_local().nonzero()[0]) == 0
diff --git a/test/unit/python/io/test_XML_mesh.py b/test/unit/python/io/test_XML_mesh.py
index a36ae90..d94f67b 100755
--- a/test/unit/python/io/test_XML_mesh.py
+++ b/test/unit/python/io/test_XML_mesh.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for XML input/output of Mesh (class XMLMesh)"
# Copyright (C) 2011 Garth N. Wells
diff --git a/test/unit/python/io/test_XML_mesh_function.py b/test/unit/python/io/test_XML_mesh_function.py
index b86d3af..3c322b7 100755
--- a/test/unit/python/io/test_XML_mesh_function.py
+++ b/test/unit/python/io/test_XML_mesh_function.py
@@ -1,5 +1,3 @@
-#!/us/bin/env py.test
-
"Unit tests for XML input/output of MeshFunction (class XMLMeshFunction)"
# Copyright (C) 2011-2014 Garth N. Wells
diff --git a/test/unit/python/io/test_XML_mesh_value_collection.py b/test/unit/python/io/test_XML_mesh_value_collection.py
index 2274ed9..2d773f1 100755
--- a/test/unit/python/io/test_XML_mesh_value_collection.py
+++ b/test/unit/python/io/test_XML_mesh_value_collection.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for XML input/output of MeshValueCollection"
# Copyright (C) 2011 Anders Logg
diff --git a/test/unit/python/io/test_XML_table.py b/test/unit/python/io/test_XML_table.py
index fce1bb1..11800cf 100755
--- a/test/unit/python/io/test_XML_table.py
+++ b/test/unit/python/io/test_XML_table.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the XML io library for Tables"""
# Copyright (C) 2016 Simon Funke and Marie E. Rognes
diff --git a/test/unit/python/io/test_XML_vector.py b/test/unit/python/io/test_XML_vector.py
index 39068e5..6b36eba 100755
--- a/test/unit/python/io/test_XML_vector.py
+++ b/test/unit/python/io/test_XML_vector.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the XML io library for vectors"""
# Copyright (C) 2011-2014 Garth N. Wells
diff --git a/test/unit/python/io/test_vtk.py b/test/unit/python/io/test_vtk.py
index 84f0da9..750a5a2 100755
--- a/test/unit/python/io/test_vtk.py
+++ b/test/unit/python/io/test_vtk.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
# Copyright (C) 2011 Garth N. Wells
#
# This file is part of DOLFIN.
@@ -28,10 +26,6 @@ def file_options():
return ["ascii", "base64", "compressed"]
@fixture
-def mesh_functions():
- return [CellFunction, FacetFunction, FaceFunction, EdgeFunction, VertexFunction]
-
- at fixture
def mesh_function_types():
return ["size_t", "int", "double", "bool"]
@@ -43,13 +37,12 @@ def type_conv():
def tempfile(tempdir, request):
return os.path.join(tempdir, request.function.__name__)
-def test_save_1d_meshfunctions(tempfile, mesh_functions,
+def test_save_1d_meshfunctions(tempfile,
mesh_function_types, file_options, type_conv):
mesh = UnitIntervalMesh(32)
- for F in mesh_functions:
- if F in [FaceFunction, EdgeFunction]: continue
- for t in mesh_function_types:
- mf = F(t, mesh, type_conv[t](1))
+ for d in range(mesh.topology().dim()+1):
+ for t in mesh_function_types:
+ mf = MeshFunction(t, mesh, mesh.topology().dim()-d, type_conv[t](1))
File(tempfile + "mf.pvd") << mf
f = File(tempfile + "mf.pvd")
f << (mf, 0.)
@@ -57,12 +50,12 @@ def test_save_1d_meshfunctions(tempfile, mesh_functions,
for file_option in file_options:
File(tempfile + "mf.pvd", file_option) << mf
-def test_save_2d_meshfunctions(tempfile, mesh_functions,
+def test_save_2d_meshfunctions(tempfile,
mesh_function_types, file_options, type_conv):
mesh = UnitSquareMesh(32, 32)
- for F in mesh_functions:
+ for d in range(mesh.topology().dim()+1):
for t in mesh_function_types:
- mf = F(t, mesh, type_conv[t](1))
+ mf = MeshFunction(t, mesh, mesh.topology().dim()-d, type_conv[t](1))
File(tempfile + "mf.pvd") << mf
f = File(tempfile + "mf.pvd")
f << (mf, 0.)
@@ -70,12 +63,12 @@ def test_save_2d_meshfunctions(tempfile, mesh_functions,
for file_option in file_options:
File(tempfile + "mf.pvd", file_option) << mf
-def test_save_3d_meshfunctions(tempfile, mesh_functions,
+def test_save_3d_meshfunctions(tempfile,
mesh_function_types, file_options, type_conv):
mesh = UnitCubeMesh(8, 8, 8)
- for F in mesh_functions:
+ for d in range(mesh.topology().dim()+1):
for t in mesh_function_types:
- mf = F(t, mesh, type_conv[t](1))
+ mf = MeshFunction(t, mesh, mesh.topology().dim()-d, type_conv[t](1))
File(tempfile + "mf.pvd") << mf
f = File(tempfile + "mf.pvd")
f << (mf, 0.)
diff --git a/test/unit/python/jit/test_jit.py b/test/unit/python/jit/test_jit.py
index 6224402..785b2a7 100755
--- a/test/unit/python/jit/test_jit.py
+++ b/test/unit/python/jit/test_jit.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the JIT compiler"""
# Copyright (C) 2011 Anders Logg
@@ -21,6 +19,7 @@
import pytest
import platform
+import dolfin
from dolfin import *
from dolfin_utils.test import (skip_if_not_PETSc, skip_if_not_SLEPc,
skip_if_not_MPI, skip_in_serial,
@@ -51,21 +50,62 @@ def test_nasty_jit_caching_bug():
@skip_if_pybind11
@skip_if_not_MPI
def test_mpi_swig():
- from dolfin import compile_extension_module
-
create_transfer_matrix_code = r'''
namespace dolfin
{
void find_exterior_points(MPI_Comm mpi_comm) {}
}'''
- create_transfer_matrix = compile_extension_module(code=create_transfer_matrix_code)
+ compile_extension_module(code=create_transfer_matrix_code)
+
+
+ at skip_if_not_pybind11
+def test_mpi_pybind11():
+ """
+ Test MPICommWrapper <-> mpi4py.MPI.Comm conversion for JIT-ed code
+ """
+ cpp_code = """
+ #include <pybind11/pybind11.h>
+ #include <dolfin_wrappers/MPICommWrapper.h>
+ namespace dolfin
+ {
+ dolfin_wrappers::MPICommWrapper
+ test_comm_passing(const dolfin_wrappers::MPICommWrapper comm)
+ {
+ MPI_Comm c = comm.get();
+ return dolfin_wrappers::MPICommWrapper(c);
+ }
+ }
+ PYBIND11_MODULE(SIGNATURE, m)
+ {
+ m.def("test_comm_passing", &dolfin::test_comm_passing);
+ }
+ """
+
+ # Import MPI_COMM_WORLD
+ if dolfin.has_mpi4py():
+ from mpi4py import MPI
+ w1 = MPI.COMM_WORLD
+ else:
+ w1 = dolfin.MPI.comm_world
+
+ # Compile the JIT module
+ return pytest.xfail('Include path for dolfin_wrappers/* not set up to '
+ 'work in the JIT at the moment')
+ mod = dolfin.compile_cpp_code(cpp_code)
+
+ # Pass a comm into C++ and get a new wrapper of the same comm back
+ w2 = mod.test_comm_passing(w1)
+
+ if dolfin.has_mpi4py():
+ assert isinstance(w2, MPI.Comm)
+ else:
+ assert isinstance(w2, dolfin.cpp.MPICommWrapper)
+ assert w1.underlying_comm() == w2.underlying_comm()
@skip_if_pybind11
@skip_if_not_PETSc
def test_pesc_swig():
- from dolfin import compile_extension_module
-
create_matrix_code = r'''
namespace dolfin
{
@@ -76,14 +116,12 @@ def test_pesc_swig():
}
}
'''
- create_matrix = compile_extension_module(code=create_matrix_code)
+ compile_extension_module(code=create_matrix_code)
@skip_if_pybind11
@skip_if_not_SLEPc
def test_slepc_swig():
- from dolfin import compile_extension_module
-
create_eps_code = r'''
#include <slepc.h>
namespace dolfin
@@ -91,55 +129,89 @@ def test_slepc_swig():
std::shared_ptr<EPS> create_matrix(MPI_Comm comm) {
EPS eps;
EPSCreate(comm, &eps);
- std::shared_ptr<EPS> ptr = std::make_shared<EPS>(eps);
+ std::shared_ptr<EPS> ptr = std::make_shared<EPS>(eps);
return ptr;
}
}
'''
- create_matrix = compile_extension_module(code=create_eps_code)
+ compile_extension_module(code=create_eps_code)
- at skip_if_pybind11
def test_pass_array_int():
import numpy
- code = """
- int test_int_array(const Array<int>& int_arr)
- {
- int ret = 0;
- for (int i = 0; i < int_arr.size(); i++)
+
+ if has_pybind11():
+ code = """
+ #include <Eigen/Core>
+ #include <pybind11/pybind11.h>
+ #include <pybind11/eigen.h>
+ using IntVecIn = Eigen::Ref<const Eigen::VectorXi>;
+ int test_int_array(const IntVecIn arr)
{
- ret += int_arr[i];
+ return arr.sum();
}
- return ret;
- }
- """
- module = compile_extension_module(code=code,
- source_directory='.',
- sources=[],
- include_dirs=["."])
+ PYBIND11_MODULE(SIGNATURE, m)
+ {
+ m.def("test_int_array", &test_int_array);
+ }
+ """
+ module = compile_cpp_code(code)
+ else:
+ code = """
+ int test_int_array(const Array<int>& int_arr)
+ {
+ int ret = 0;
+ for (int i = 0; i < int_arr.size(); i++)
+ {
+ ret += int_arr[i];
+ }
+ return ret;
+ }
+ """
+ module = compile_extension_module(code=code,
+ source_directory='.',
+ sources=[],
+ include_dirs=["."])
arr = numpy.array([1, 2, 4, 8], dtype=numpy.intc)
ans = module.test_int_array(arr)
assert ans == arr.sum() == 15
- at skip_if_pybind11
def test_pass_array_double():
import numpy
- code = """
- double test_double_array(const Array<double>& arr)
- {
- double ret = 0;
- for (int i = 0; i < arr.size(); i++)
+
+ if has_pybind11():
+ code = """
+ #include <Eigen/Core>
+ #include <pybind11/pybind11.h>
+ #include <pybind11/eigen.h>
+ using DoubleVecIn = Eigen::Ref<const Eigen::VectorXd>;
+ int test_double_array(const DoubleVecIn arr)
{
- ret += arr[i];
+ return arr.sum();
}
- return ret;
- }
- """
- module = compile_extension_module(code=code,
- source_directory='.',
- sources=[],
- include_dirs=["."])
+ PYBIND11_MODULE(SIGNATURE, m)
+ {
+ m.def("test_double_array", &test_double_array);
+ }
+ """
+ module = compile_cpp_code(code)
+ else:
+ code = """
+ double test_double_array(const Array<double>& arr)
+ {
+ double ret = 0;
+ for (int i = 0; i < arr.size(); i++)
+ {
+ ret += arr[i];
+ }
+ return ret;
+ }
+ """
+ module = compile_extension_module(code=code,
+ source_directory='.',
+ sources=[],
+ include_dirs=["."])
arr = numpy.array([1, 2, 4, 8], dtype=float)
ans = module.test_double_array(arr)
assert abs(arr.sum() - 15) < 1e-15
@@ -211,12 +283,12 @@ def test_compile_extension_module_pybind11():
ext_module = compile_cpp_code(code)
vec = PETScVector(mpi_comm_world(), 10)
- np_vec = vec.array()
+ np_vec = vec.get_local()
np_vec[:] = arange(len(np_vec))
vec.set_local(np_vec)
ext_module.PETSc_exp(vec)
np_vec[:] = exp(np_vec)
- assert (np_vec == vec.array()).all()
+ assert (np_vec == vec.get_local()).all()
@skip_if_pybind11
@@ -228,12 +300,14 @@ def test_compile_extension_module_kwargs():
assert not m2.__file__ == m0.__file__
+ at skip_if_pybind11
@skip_if_not_petsc4py
@skip_in_serial
def test_mpi_dependent_jiting():
# FIXME: Not a proper unit test...
- from dolfin import Expression, UnitSquareMesh, Function, TestFunction, \
- Form, FunctionSpace, dx, CompiledSubDomain, SubSystemsManager
+ from dolfin import (Expression, UnitSquareMesh, Function,
+ TestFunction, Form, FunctionSpace, dx, CompiledSubDomain,
+ SubSystemsManager)
# Init petsc (needed to initalize petsc and slepc collectively on
# all processes)
diff --git a/test/unit/python/la/test_krylov_solver.py b/test/unit/python/la/test_krylov_solver.py
index e18a1d9..2bd1b3e 100755
--- a/test/unit/python/la/test_krylov_solver.py
+++ b/test/unit/python/la/test_krylov_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the KrylovSolver interface"""
# Copyright (C) 2014 Garth N. Wells
diff --git a/test/unit/python/la/test_la_basic.py b/test/unit/python/la/test_la_basic.py
index acc4297..d4b323c 100755
--- a/test/unit/python/la/test_la_basic.py
+++ b/test/unit/python/la/test_la_basic.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the linear algebra interface"""
# Copyright (C) 2008 Johan Hake
@@ -186,7 +184,7 @@ class TestBasicLaOperations:
else:
assert round(A[lind0] - val1 - val2*100, 7) == 0
- A2 = A.array()
+ A2 = A.get_local()
assert isinstance(A2,ndarray)
assert A2.shape == (n1 - n0, )
@@ -197,7 +195,7 @@ class TestBasicLaOperations:
assert round(MPI.sum(A.mpi_comm(), A2.sum()) - A.sum(), 7) == 0
- B2 = B.array()
+ B2 = B.get_local()
inds = [1,3,6,9,15,20,24,28,32,40,50,60,70,100000]
@@ -240,7 +238,7 @@ class TestBasicLaOperations:
assert (A3==A2).all()
A[:] = A2
- assert (A.array()==A2).all()
+ assert (A.get_local()==A2).all()
H = A.copy()
if not has_pybind11():
@@ -289,7 +287,7 @@ class TestBasicLaOperations:
with pytest.raises(IndexError):
wrong_dim([0,2], slice(0,4,1))
- A2 = A.array()
+ A2 = A.get_local()
A *= B
A2 *= B2
I = A*B
@@ -385,13 +383,13 @@ class TestBasicLaOperations:
if distributed:
return
- v_numpy = v.array()
+ v_numpy = v.get_local()
A_numpy = A.array()
u_numpy = dot(A_numpy, v_numpy)
u_numpy2 = A*v_numpy
- assert absolute(u.array() - u_numpy).sum() < DOLFIN_EPS*len(v)
+ assert absolute(u.get_local() - u_numpy).sum() < DOLFIN_EPS*len(v)
assert absolute(u_numpy2 - u_numpy).sum() < DOLFIN_EPS*len(v)
diff --git a/test/unit/python/la/test_linear_operator.py b/test/unit/python/la/test_linear_operator.py
index aa31927..298f607 100755
--- a/test/unit/python/la/test_linear_operator.py
+++ b/test/unit/python/la/test_linear_operator.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for matrix-free linear solvers (LinearOperator)"
# Copyright (C) 2012 Anders Logg
@@ -80,14 +78,14 @@ def test_linear_operator(backend):
u = Function(V)
a_action = action(a, coefficient=u)
O = MyLinearOperator(a_action, u)
- O = _as_backend_type(O)
- solve(O, x, b, "gmres", "none")
+ Ob = _as_backend_type(O)
+ solve(Ob, x, b, "gmres", "none")
norm_action = norm(x, "l2")
# Check at least that petsc4py interface is available
if backend == 'PETSc' and has_petsc4py() and _as_backend_type == as_backend_type:
from petsc4py import PETSc
- assert isinstance(O.mat(), PETSc.Mat)
+ assert isinstance(Ob.mat(), PETSc.Mat)
# Reset backend
parameters["linear_algebra_backend"] = prev_backend
diff --git a/test/unit/python/la/test_lu_solver.py b/test/unit/python/la/test_lu_solver.py
index 9bb819f..d14dbdf 100755
--- a/test/unit/python/la/test_lu_solver.py
+++ b/test/unit/python/la/test_lu_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the LUSolver interface"""
# Copyright (C) 2014 Garth N. Wells
@@ -60,6 +58,11 @@ def test_lu_solver(backend):
solver.solve(x, b)
assert round(x.norm("l2") - norm, 10) == 0
+ solver = LUSolver()
+ x = Vector()
+ solver.solve(A, x, b)
+ assert round(x.norm("l2") - norm, 10) == 0
+
# Reset backend
parameters["linear_algebra_backend"] = prev_backend
diff --git a/test/unit/python/la/test_matrix.py b/test/unit/python/la/test_matrix.py
index 2b6eab1..0065418 100755
--- a/test/unit/python/la/test_matrix.py
+++ b/test/unit/python/la/test_matrix.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Matrix interface"""
# Copyright (C) 2011-2014 Garth N. Wells
@@ -267,6 +265,42 @@ class TestMatrixForAnyBackend:
assert j < cols.size
assert round(sum(abs(row)) - 1.0, 7) == 0
+ @skip_in_parallel
+ def test_ident(self, use_backend, any_backend):
+ self.backend, self.sub_backend = any_backend
+ if self.backend == 'Tpetra':
+ pytest.skip()
+ A, B = self.assemble_matrices(use_backend)
+ N, M = A.size(0), A.size(1)
+
+ # Make sure rows are not identity from before
+ import numpy
+ ROWS = [2, 4]
+ for row in ROWS:
+ block = numpy.array([[42.0]], dtype=float)
+ A.set(block, numpy.array([row], dtype=numpy.intc),
+ numpy.array([row], dtype=numpy.intc))
+ A.apply("insert")
+
+ def check_row(A, row, idented):
+ cols, vals = A.getrow(row)
+ i = list(cols).index(row)
+ if idented:
+ assert vals[i] == 1
+ vals[i] = 0
+ for c in cols:
+ if c != row:
+ assert (vals == 0).all()
+ else:
+ assert vals[i] == 42.0
+
+ for row in ROWS:
+ check_row(A, row, False)
+ A.ident(numpy.array(ROWS, dtype=numpy.intc))
+ A.apply("insert")
+ for row in ROWS:
+ check_row(A, row, True)
+
def test_setting_getting_diagonal(self, use_backend, any_backend):
self.backend, self.sub_backend = any_backend
@@ -305,6 +339,32 @@ class TestMatrixForAnyBackend:
w.vector()[:] -= b
assert round(w.vector().norm("l2"), 14) == 0
+ @skip_in_parallel
+ def test_get_set(self, use_backend, any_backend):
+ self.backend, self.sub_backend = any_backend
+ if self.backend == 'Tpetra':
+ pytest.skip()
+ A, B = self.assemble_matrices(use_backend)
+ N, M = A.size(0), A.size(1)
+
+ import numpy
+ rows = numpy.array([1, 2], dtype=numpy.intc)
+ cols = numpy.array([1, 2], dtype=numpy.intc)
+
+ block_in = numpy.ones((2, 2), dtype=float) * 42
+ Anp0 = A.array()
+ assert (Anp0[1:3,1:3] != block_in).any()
+
+ A.set(block_in, rows, cols)
+ A.apply("insert")
+
+ Anp1 = A.array()
+ assert (Anp1[1:3,1:3] == block_in).all()
+
+ block_out = numpy.zeros_like(block_in)
+ A.get(block_out, rows, cols)
+ assert (block_out == block_in).all()
+
# def test_create_from_sparsity_pattern(self):
# def test_size(self):
diff --git a/test/unit/python/la/test_mg_solver.py b/test/unit/python/la/test_mg_solver.py
index 6ec7880..19be0d3 100755
--- a/test/unit/python/la/test_mg_solver.py
+++ b/test/unit/python/la/test_mg_solver.py
@@ -20,8 +20,11 @@
from dolfin import *
import pytest
-from dolfin_utils.test import skip_if_not_PETSc, skip_if_not_petsc4py, pushpop_parameters
+from dolfin_utils.test import (skip_if_not_PETSc,
+ skip_if_not_petsc4py, skip_if_pybind11,
+ pushpop_parameters)
+ at skip_if_pybind11 # See https://bitbucket.org/fenics-project/dolfin/issues/938
@skip_if_not_petsc4py
def test_mg_solver_laplace(pushpop_parameters):
diff --git a/test/unit/python/la/test_nullspace.py b/test/unit/python/la/test_nullspace.py
index 54fa8e5..e60680a 100755
--- a/test/unit/python/la/test_nullspace.py
+++ b/test/unit/python/la/test_nullspace.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for nullspace test"
# Copyright (C) 2014 Garth N. Wells
diff --git a/test/unit/python/la/test_petsc4py.py b/test/unit/python/la/test_petsc4py.py
new file mode 100644
index 0000000..5ac5dfd
--- /dev/null
+++ b/test/unit/python/la/test_petsc4py.py
@@ -0,0 +1,97 @@
+"""Unit tests for the pybind11 wrapping of PETSc / petsc4py"""
+
+# Copyright (C) 2017 Tormod Landet
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+
+import gc
+from dolfin import (PETScVector, PETScMatrix, UnitSquareMesh, TrialFunction,
+ TestFunction, FunctionSpace, Function, assemble, dx,
+ parameters, as_backend_type)
+from dolfin_utils.test import skip_if_not_petsc4py, pushpop_parameters
+
+
+ at skip_if_not_petsc4py
+def test_petsc4py_vector(pushpop_parameters):
+ "Test PETScVector <-> petsc4py.PETSc.Vec conversions"
+ parameters["linear_algebra_backend"] = "PETSc"
+
+ # Assemble a test matrix
+ mesh = UnitSquareMesh(4, 4)
+ V = FunctionSpace(mesh, "Lagrange", 1)
+ v = TestFunction(V)
+ a = v*dx
+ b1 = assemble(a)
+
+ # Test conversion dolfin.PETScVector -> petsc4py.PETSc.Vec
+ b1 = as_backend_type(b1)
+ v1 = b1.vec()
+
+ # Copy and scale vector with petsc4py
+ v2 = v1.copy()
+ v2.scale(2.0)
+
+ # Test conversion petsc4py.PETSc.Vec -> PETScVector
+ b2 = PETScVector(v2)
+
+ assert (b1.get_local()*2.0 == b2.get_local()).all()
+
+
+ at skip_if_not_petsc4py
+def test_petsc4py_matrix(pushpop_parameters):
+ "Test PETScMatrix <-> petsc4py.PETSc.Mat conversions"
+ parameters["linear_algebra_backend"] = "PETSc"
+
+ # Assemble a test matrix
+ mesh = UnitSquareMesh(4, 4)
+ V = FunctionSpace(mesh, "Lagrange", 1)
+ u, v = TrialFunction(V), TestFunction(V)
+ a = u*v*dx
+ A1 = assemble(a)
+
+ # Test conversion dolfin.PETScMatrix -> petsc4py.PETSc.Mat
+ A1 = as_backend_type(A1)
+ M1 = A1.mat()
+
+ # Copy and scale matrix with petsc4py
+ M2 = M1.copy()
+ M2.scale(2.0)
+
+ # Test conversion petsc4py.PETSc.Mat -> PETScMatrix
+ A2 = PETScMatrix(M2)
+
+ assert (A1.array()*2.0 == A2.array()).all()
+
+ at skip_if_not_petsc4py
+def test_ref_count(pushpop_parameters):
+ "Test petsc4py reference counting"
+ parameters["linear_algebra_backend"] = "PETSc"
+
+ mesh = UnitSquareMesh(3, 3)
+ V = FunctionSpace(mesh, "P", 1)
+
+ # Check u and x own the vector
+ u = Function(V)
+ x = as_backend_type(u.vector()).vec()
+ assert x.refcount == 2
+
+ # Check decref
+ del u; gc.collect() # destroy u
+ assert x.refcount == 1
+
+ # Check incref
+ vec = PETScVector(x)
+ assert x.refcount == 2
diff --git a/test/unit/python/la/test_scalar.py b/test/unit/python/la/test_scalar.py
index 3b747de..d6c1a5c 100755
--- a/test/unit/python/la/test_scalar.py
+++ b/test/unit/python/la/test_scalar.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Scalar interface"""
# Copyright (C) 2011 Garth N. Wells
diff --git a/test/unit/python/la/test_slepc_solver.py b/test/unit/python/la/test_slepc_solver.py
index 628e300..9d531d5 100755
--- a/test/unit/python/la/test_slepc_solver.py
+++ b/test/unit/python/la/test_slepc_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the KrylovSolver interface"""
# Copyright (C) 2016 Nate Sime
@@ -32,6 +30,14 @@ def k(u, v):
def m(u, v):
return dot(u, v)*dx
+# Wrappers around SLEPcEigenSolver for test_slepc_eigensolver_gen_hermitian
+def SLEPcEigenSolverOperatorsFromInit(K, M):
+ return SLEPcEigenSolver(K, M)
+
+def SLEPcEigenSolverOperatorsFromSetOperators(K, M):
+ slepc_eigen_solver = SLEPcEigenSolver(K.mpi_comm())
+ slepc_eigen_solver.set_operators(K, M)
+ return slepc_eigen_solver
# Fixtures
@@ -82,11 +88,12 @@ def test_set_from_options():
assert solver.get_options_prefix() == prefix
@skip_if_not_PETsc_or_not_slepc
-def test_slepc_eigensolver_gen_hermitian(K_M):
+ at pytest.mark.parametrize("SLEPcEigenSolverWrapper", (SLEPcEigenSolverOperatorsFromInit, SLEPcEigenSolverOperatorsFromSetOperators))
+def test_slepc_eigensolver_gen_hermitian(K_M, SLEPcEigenSolverWrapper):
"Test SLEPc eigen solver"
K, M = K_M
- esolver = SLEPcEigenSolver(K, M)
+ esolver = SLEPcEigenSolverWrapper(K, M)
esolver.parameters["solver"] = "krylov-schur"
esolver.parameters["spectral_transform"] = 'shift-and-invert'
diff --git a/test/unit/python/la/test_solve.py b/test/unit/python/la/test_solve.py
index b295f0a..9d6a762 100755
--- a/test/unit/python/la/test_solve.py
+++ b/test/unit/python/la/test_solve.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the solve interface"""
# Copyright (C) 2011 Garth N. Wells
diff --git a/test/unit/python/la/test_tensor_layout.py b/test/unit/python/la/test_tensor_layout.py
index e044456..b244e1e 100644
--- a/test/unit/python/la/test_tensor_layout.py
+++ b/test/unit/python/la/test_tensor_layout.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for TensorLayout and SparsityPattern interface"""
# Copyright (C) 2015 Jan Blechta
diff --git a/test/unit/python/la/test_vector.py b/test/unit/python/la/test_vector.py
index 3a59aa5..739d017 100755
--- a/test/unit/python/la/test_vector.py
+++ b/test/unit/python/la/test_vector.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Vector interface"""
# Copyright (C) 2011-2014 Garth N. Wells
@@ -136,10 +134,7 @@ class TestVectorForAnyBackend:
from numpy import empty
n = 301
v0 = Vector(mpi_comm_world(), n)
- if has_pybind11():
- data = v0.array()
- else:
- data = v0.get_local()
+ data = v0.get_local()
def test_set_local(self, any_backend):
from numpy import zeros
@@ -389,27 +384,39 @@ class TestVectorForAnyBackend:
# Test for ordinary Vector
v = Vector(mpi_comm_world(), 301)
v = as_backend_type(v)
- array = v.array()
+
if has_pybind11():
- assert array.flags.owndata == False
+ rw_array = v.array_view()
+ assert rw_array.flags.owndata == False
with pytest.raises(Exception):
- array.resize([10])
+ rw_array.resize([10])
+
+ # Check that the array is a writable view
+ rw_array[0] = 42
+ ro_array = v.get_local()
+ assert ro_array[0] == 42
+
+ # Test for as_backend_type Vector
+ v = as_backend_type(v)
+ rw_array2 = v.array_view()
+ assert (rw_array2 == ro_array).all()
+
else:
+ ro_array = v.get_local()
data = v.data()
- assert (data == array).all()
-
+ assert (data == ro_array).all()
- # Test none writeable of a shallow copy of the data
+ # Test that a shallow copy of the data is not writable
data = v.data(False)
def write_data(data):
data[0] = 1
with pytest.raises(Exception):
write_data(data)
- # Test for as_backend_typeed Vector
+ # Test for as_backend_type Vector
v = as_backend_type(v)
data = v.data()
- assert (data==array).all()
+ assert (data == ro_array).all()
# xfail on TypeError
diff --git a/test/unit/python/mesh/test_mesh_data.py b/test/unit/python/log/test_log.py
old mode 100755
new mode 100644
similarity index 68%
copy from test/unit/python/mesh/test_mesh_data.py
copy to test/unit/python/log/test_log.py
index 7145ef4..b071cdc
--- a/test/unit/python/mesh/test_mesh_data.py
+++ b/test/unit/python/log/test_log.py
@@ -1,8 +1,6 @@
-#!/usr/bin/env py.test
+"""Unit tests for the log"""
-"Unit tests for the MeshData class"
-
-# Copyright (C) 2011 Anders Logg
+# Copyright (C) 2017 Tormod Landet
#
# This file is part of DOLFIN.
#
@@ -18,16 +16,14 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2011-08-22
-# Last changed: 2011-08-22
-
-import pytest
-from dolfin import *
+import dolfin
+from dolfin_utils.test import skip_if_not_pybind11
-def test_meshfunction():
- "Test input/output"
- mesh = UnitCubeMesh(3, 3, 3)
- f = mesh.data().create_array("foo", 3)
+ at skip_if_not_pybind11
+def test_log_level_comparable():
+ info = dolfin.LogLevel.INFO
+ warning = dolfin.LogLevel.WARNING
+ assert info < warning
+ assert warning < 1000
diff --git a/test/unit/python/math/test_math.py b/test/unit/python/math/test_math.py
index 6e32fa8..f4c3ba3 100755
--- a/test/unit/python/math/test_math.py
+++ b/test/unit/python/math/test_math.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for basic math functions"""
# Copyright (C) 2011-2014 Martin Alnaes
diff --git a/test/unit/python/mesh/test_boundary_mesh.py b/test/unit/python/mesh/test_boundary_mesh.py
old mode 100755
new mode 100644
index f632f5e..b013bf5
--- a/test/unit/python/mesh/test_boundary_mesh.py
+++ b/test/unit/python/mesh/test_boundary_mesh.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for BoundaryMesh library"
# Copyright (C) 2012 Garth N. Wells
@@ -35,7 +33,7 @@ def test_1D_mesh():
# Create global boundary mesh
bmesh1 = BoundaryMesh(mesh, "exterior")
assert MPI.sum(mesh.mpi_comm(), bmesh1.num_cells()) == 2
- assert bmesh1.size_global(0) == 2
+ assert bmesh1.num_entities_global(0) == 2
assert bmesh1.topology().dim() == 0
@@ -45,7 +43,7 @@ def test_2D_mesh():
# Create global boundary mesh
bmesh1 = BoundaryMesh(mesh, "exterior")
assert MPI.sum(mesh.mpi_comm(), bmesh1.num_cells()) == 4*8
- assert bmesh1.size_global(1) == 4*8
+ assert bmesh1.num_entities_global(1) == 4*8
assert bmesh1.topology().dim() == 1
@@ -55,5 +53,5 @@ def test_3D_mesh():
# Create global boundary mesh
bmesh1 = BoundaryMesh(mesh, "exterior")
assert MPI.sum(mesh.mpi_comm(), bmesh1.num_cells()) == 6*8*8*2
- assert bmesh1.size_global(2) == 6*8*8*2
+ assert bmesh1.num_entities_global(2) == 6*8*8*2
assert bmesh1.topology().dim() == 2
diff --git a/test/unit/python/mesh/test_cell.py b/test/unit/python/mesh/test_cell.py
old mode 100755
new mode 100644
index 90ccf9b..8fee151
--- a/test/unit/python/mesh/test_cell.py
+++ b/test/unit/python/mesh/test_cell.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Cell class"""
# Copyright (C) 2013 Anders Logg
@@ -77,13 +75,13 @@ def test_issue_568():
def test_volume_quadrilateralR2():
- mesh = UnitQuadMesh.create(mpi_comm_self(), 1, 1)
+ mesh = UnitSquareMesh.create(mpi_comm_self(), 1, 1, CellType.Type_quadrilateral)
cell = Cell(mesh, 0)
assert cell.volume() == 1.0
- at pytest.mark.parametrize('coordinates', [[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]],
+ at pytest.mark.parametrize('coordinates', [[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]],
[[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 1.0]]])
def test_volume_quadrilateralR3(coordinates):
diff --git a/test/unit/python/mesh/test_edge.py b/test/unit/python/mesh/test_edge.py
old mode 100755
new mode 100644
index 3831e18..7fe462c
--- a/test/unit/python/mesh/test_edge.py
+++ b/test/unit/python/mesh/test_edge.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Edge class"""
# Copyright (C) 2011 Garth N. Wells
diff --git a/test/unit/python/mesh/test_face.py b/test/unit/python/mesh/test_face.py
old mode 100755
new mode 100644
index dc72cd7..9375bb6
--- a/test/unit/python/mesh/test_face.py
+++ b/test/unit/python/mesh/test_face.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the Face class"""
# Copyright (C) 2011 Garth N. Wells
diff --git a/test/unit/python/mesh/test_mesh_data.py b/test/unit/python/mesh/test_facet.py
old mode 100755
new mode 100644
similarity index 61%
copy from test/unit/python/mesh/test_mesh_data.py
copy to test/unit/python/mesh/test_facet.py
index 7145ef4..d989886
--- a/test/unit/python/mesh/test_mesh_data.py
+++ b/test/unit/python/mesh/test_facet.py
@@ -1,8 +1,6 @@
-#!/usr/bin/env py.test
+"""Unit tests for the Facet class"""
-"Unit tests for the MeshData class"
-
-# Copyright (C) 2011 Anders Logg
+# Copyright (C) 2017 Tormod Landet
#
# This file is part of DOLFIN.
#
@@ -18,16 +16,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2011-08-22
-# Last changed: 2011-08-22
-
-import pytest
-from dolfin import *
+from dolfin import UnitSquareMesh, facets
-def test_meshfunction():
- "Test input/output"
- mesh = UnitCubeMesh(3, 3, 3)
- f = mesh.data().create_array("foo", 3)
+def test_normal():
+ "Test that the normal() method is wrapped"
+ mesh = UnitSquareMesh(4, 4)
+ for facet in facets(mesh):
+ n = facet.normal()
+ nx, ny, nz = n.x(), n.y(), n.z()
+ assert isinstance(nx, float)
+ assert isinstance(ny, float)
+ assert isinstance(nz, float)
diff --git a/test/unit/python/mesh/test_ghost_mesh.py b/test/unit/python/mesh/test_ghost_mesh.py
old mode 100755
new mode 100644
index 269ea96..938c104
--- a/test/unit/python/mesh/test_ghost_mesh.py
+++ b/test/unit/python/mesh/test_ghost_mesh.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for ghosted meshes"
# Copyright (C) 2016 Garth N. Wells
diff --git a/test/unit/python/mesh/test_manifold_point_search.py b/test/unit/python/mesh/test_manifold_point_search.py
old mode 100755
new mode 100644
index 466bddb..b6659bb
--- a/test/unit/python/mesh/test_manifold_point_search.py
+++ b/test/unit/python/mesh/test_manifold_point_search.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env py.test
-import pytest
+
import numpy
from dolfin import *
@@ -30,8 +29,8 @@ def test_manifold_point_search():
mesh.init_cell_orientations(Expression(("0.0", "0.0", "1.0"), degree=0))
bb = mesh.bounding_box_tree()
- p = Point(2.0/3.0, 1.0/3.0, 2.0/3.0)
+ p = Point(0.5, 0.25, 0.75)
assert bb.compute_first_entity_collision(p) == 0
- p = Point(1.0/3.0, 2.0/3.0, 2.0/3.0)
+ p = Point(0.25, 0.5, 0.75)
assert bb.compute_first_entity_collision(p) == 1
diff --git a/test/unit/python/mesh/test_mesh.py b/test/unit/python/mesh/test_mesh.py
old mode 100755
new mode 100644
index 708f7cb..eef93cb
--- a/test/unit/python/mesh/test_mesh.py
+++ b/test/unit/python/mesh/test_mesh.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for the mesh library"
# Copyright (C) 2006 Anders Logg
@@ -26,13 +24,20 @@ import numpy
import six
from six.moves import range
+import sys
+import os
+
+import FIAT
from dolfin import *
from dolfin_utils.test import fixture, set_parameters_fixture
from dolfin_utils.test import skip_in_parallel, xfail_in_parallel
from dolfin_utils.test import cd_tempdir
-import FIAT
+from dolfin_utils.test import skip_if_not_pybind11
-import os
+
+if has_pybind11():
+ CellType.Type_quadrilateral = CellType.Type.quadrilateral
+ CellType.Type_hexahedron = CellType.Type.hexahedron
@fixture
@@ -142,18 +147,17 @@ def test_UFLDomain(interval, square, rectangle, cube, box):
def test_UnitSquareMesh():
"""Create mesh of unit square."""
mesh = UnitSquareMesh(5, 7)
- assert mesh.size_global(0) == 48
- assert mesh.size_global(2) == 70
+ assert mesh.num_entities_global(0) == 48
+ assert mesh.num_entities_global(2) == 70
def test_UnitSquareMeshDistributed():
"""Create mesh of unit square."""
mesh = UnitSquareMesh(mpi_comm_world(), 5, 7)
- assert mesh.size_global(0) == 48
- assert mesh.size_global(2) == 70
- if has_petsc4py():
+ assert mesh.num_entities_global(0) == 48
+ assert mesh.num_entities_global(2) == 70
+ if has_petsc4py() and not has_pybind11():
import petsc4py
- assert isinstance(mpi_comm_world(), petsc4py.PETSc.Comm)
assert isinstance(mesh.mpi_comm(), petsc4py.PETSc.Comm)
assert mesh.mpi_comm() == mpi_comm_world()
@@ -163,9 +167,8 @@ def test_UnitSquareMeshLocal():
mesh = UnitSquareMesh(mpi_comm_self(), 5, 7)
assert mesh.num_vertices() == 48
assert mesh.num_cells() == 70
- if has_petsc4py():
+ if has_petsc4py() and not has_pybind11():
import petsc4py
- assert isinstance(mpi_comm_self(), petsc4py.PETSc.Comm)
assert isinstance(mesh.mpi_comm(), petsc4py.PETSc.Comm)
assert mesh.mpi_comm() == mpi_comm_self()
@@ -173,15 +176,15 @@ def test_UnitSquareMeshLocal():
def test_UnitCubeMesh():
"""Create mesh of unit cube."""
mesh = UnitCubeMesh(5, 7, 9)
- assert mesh.size_global(0) == 480
- assert mesh.size_global(3) == 1890
+ assert mesh.num_entities_global(0) == 480
+ assert mesh.num_entities_global(3) == 1890
def test_UnitCubeMeshDistributed():
"""Create mesh of unit cube."""
mesh = UnitCubeMesh(mpi_comm_world(), 5, 7, 9)
- assert mesh.size_global(0) == 480
- assert mesh.size_global(3) == 1890
+ assert mesh.num_entities_global(0) == 480
+ assert mesh.num_entities_global(3) == 1890
def test_UnitCubeMeshDistributedLocal():
@@ -192,49 +195,49 @@ def test_UnitCubeMeshDistributedLocal():
def test_UnitQuadMesh():
- mesh = UnitQuadMesh.create(mpi_comm_world(), 5, 7)
- assert mesh.size_global(0) == 48
- assert mesh.size_global(2) == 35
+ mesh = UnitSquareMesh.create(5, 7, CellType.Type_quadrilateral)
+ assert mesh.num_entities_global(0) == 48
+ assert mesh.num_entities_global(2) == 35
def test_UnitHexMesh():
- mesh = UnitHexMesh.create(mpi_comm_world(), 5, 7, 9)
- assert mesh.size_global(0) == 480
- assert mesh.size_global(3) == 315
+ mesh = UnitCubeMesh.create(5, 7, 9, CellType.Type_hexahedron)
+ assert mesh.num_entities_global(0) == 480
+ assert mesh.num_entities_global(3) == 315
def test_RefineUnitIntervalMesh():
"""Refine mesh of unit interval."""
mesh = UnitIntervalMesh(20)
- cell_markers = CellFunction("bool", mesh)
+ cell_markers = MeshFunction("bool", mesh, mesh.topology().dim(), False)
cell_markers[0] = (MPI.rank(mesh.mpi_comm()) == 0)
mesh2 = refine(mesh, cell_markers)
- assert mesh2.size_global(0) == 22
- assert mesh2.size_global(1) == 21
+ assert mesh2.num_entities_global(0) == 22
+ assert mesh2.num_entities_global(1) == 21
def test_RefineUnitSquareMesh():
"""Refine mesh of unit square."""
mesh = UnitSquareMesh(5, 7)
mesh = refine(mesh)
- assert mesh.size_global(0) == 165
- assert mesh.size_global(2) == 280
+ assert mesh.num_entities_global(0) == 165
+ assert mesh.num_entities_global(2) == 280
def test_RefineUnitCubeMesh():
"""Refine mesh of unit cube."""
mesh = UnitCubeMesh(5, 7, 9)
mesh = refine(mesh)
- assert mesh.size_global(0) == 3135
- assert mesh.size_global(3) == 15120
+ assert mesh.num_entities_global(0) == 3135
+ assert mesh.num_entities_global(3) == 15120
def test_BoundaryComputation():
"""Compute boundary of mesh."""
mesh = UnitCubeMesh(2, 2, 2)
boundary = BoundaryMesh(mesh, "exterior")
- assert boundary.size_global(0) == 26
- assert boundary.size_global(2) == 48
+ assert boundary.num_entities_global(0) == 26
+ assert boundary.num_entities_global(2) == 48
@xfail_in_parallel
@@ -283,6 +286,14 @@ def test_Read(cd_tempdir):
# assert all(f.array() == f.array())
+def test_hash():
+ h1 = UnitSquareMesh(4, 4).hash()
+ h2 = UnitSquareMesh(4, 5).hash()
+ h3 = UnitSquareMesh(4, 4).hash()
+ assert h1 == h3
+ assert h1 != h2
+
+
@skip_in_parallel
def test_SubsetIterators(mesh):
def inside1(x):
@@ -292,7 +303,7 @@ def test_SubsetIterators(mesh):
return x[0] >= 0.5
sd1 = AutoSubDomain(inside1)
sd2 = AutoSubDomain(inside2)
- cf = CellFunction('size_t', mesh)
+ cf = MeshFunction('size_t', mesh, mesh.topology().dim())
cf.set_all(0)
sd1.mark(cf, 1)
sd2.mark(cf, 2)
@@ -469,8 +480,8 @@ mesh_factories = [
(SphericalShellMesh.create, (mpi_comm_world(), 1,)),
(SphericalShellMesh.create, (mpi_comm_world(), 2,)),
(UnitCubeMesh, (2, 2, 2)),
- (UnitQuadMesh.create, (4, 4)),
- (UnitHexMesh.create, (2, 2, 2)),
+ (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)),
+ (UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron)),
# FIXME: Add mechanism for testing meshes coming from IO
]
@@ -485,8 +496,8 @@ mesh_factories_broken_shared_entities = [
xfail_in_parallel((SphericalShellMesh.create, (mpi_comm_world(), 1,))),
xfail_in_parallel((SphericalShellMesh.create, (mpi_comm_world(), 2,))),
(UnitCubeMesh, (2, 2, 2)),
- (UnitQuadMesh.create, (4, 4)),
- (UnitHexMesh.create, (2, 2, 2)),
+ (UnitSquareMesh.create, (4, 4, CellType.Type_quadrilateral)),
+ (UnitCubeMesh.create, (2, 2, 2, CellType.Type_hexahedron)),
]
# FIXME: Fix this xfail
@@ -494,10 +505,10 @@ def xfail_ghosted_quads_hexes(mesh_factory, ghost_mode):
"""Xfail when mesh_factory on quads/hexes uses
shared_vertex mode. Needs implementing.
"""
- if mesh_factory in [UnitQuadMesh.create, UnitHexMesh.create]:
+ if mesh_factory in [UnitSquareMesh.create, UnitCubeMesh.create]:
if ghost_mode == 'shared_vertex':
pytest.xfail(reason="Missing functionality in '{}' with '' "
- "mode".format(mesh_factory, ghost_mode))
+ "mode".format(mesh_factory, ghost_mode))
@pytest.mark.parametrize('mesh_factory', mesh_factories_broken_shared_entities)
@@ -526,16 +537,16 @@ def test_shared_entities(mesh_factory, ghost_mode):
assert isinstance(sharing, set)
assert (len(sharing) > 0) == e.is_shared()
- n_entities = mesh.size(shared_dim)
- n_global_entities = mesh.size_global(shared_dim)
+ n_entities = mesh.num_entities(shared_dim)
+ n_global_entities = mesh.num_entities_global(shared_dim)
shared_entities = mesh.topology().shared_entities(shared_dim)
# Check that sum(local-shared) = global count
rank = MPI.rank(mesh.mpi_comm())
ct = sum(1 for val in six.itervalues(shared_entities) if list(val)[0] < rank)
- size_global = MPI.sum(mesh.mpi_comm(), mesh.size(shared_dim) - ct)
+ num_entities_global = MPI.sum(mesh.mpi_comm(), mesh.num_entities(shared_dim) - ct)
- assert size_global == mesh.size_global(shared_dim)
+ assert num_entities_global == mesh.num_entities_global(shared_dim)
@pytest.mark.parametrize('mesh_factory', mesh_factories)
@@ -599,7 +610,7 @@ def test_mesh_ufc_ordering(mesh_factory, ghost_mode):
# NOTE: DOLFIN UFC noncompliance!
# DOLFIN has increasing indices only for d-0 incidence
- # with any d; UFC convention for d-d1 with d>d1 is not
+ # with any d; UFC convention for d-d1 with d>d1>0 is not
# respected in DOLFIN
if d1 != 0:
continue
@@ -618,3 +629,25 @@ def test_mesh_ufc_ordering(mesh_factory, ghost_mode):
# Check that d1-subentities of d-entity have increasing indices
assert sorted(subentities_indices) == subentities_indices
+
+
+def test_mesh_topology_reference():
+ """Check that Mesh.topology() returns a reference rather
+ than copy"""
+ mesh = UnitSquareMesh(4, 4)
+ assert mesh.topology().id() == mesh.topology().id()
+
+
+# Reference counting does not work like expected with SWIG,
+# SWIG handles the lifetime differently
+ at skip_if_not_pybind11
+def test_mesh_topology_lifetime():
+ """Check that lifetime of Mesh.topology() is bound to
+ underlying mesh object"""
+ mesh = UnitSquareMesh(4, 4)
+
+ rc = sys.getrefcount(mesh)
+ topology = mesh.topology()
+ assert sys.getrefcount(mesh) == rc + 1
+ del topology
+ assert sys.getrefcount(mesh) == rc
diff --git a/test/unit/python/mesh/test_mesh_coloring.py b/test/unit/python/mesh/test_mesh_coloring.py
old mode 100755
new mode 100644
diff --git a/test/unit/python/mesh/test_mesh_data.py b/test/unit/python/mesh/test_mesh_data.py
old mode 100755
new mode 100644
index 7145ef4..7b1f0d4
--- a/test/unit/python/mesh/test_mesh_data.py
+++ b/test/unit/python/mesh/test_mesh_data.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for the MeshData class"
# Copyright (C) 2011 Anders Logg
diff --git a/test/unit/python/mesh/test_mesh_editor.py b/test/unit/python/mesh/test_mesh_editor.py
old mode 100755
new mode 100644
index ad11b8b..36cc3ba
--- a/test/unit/python/mesh/test_mesh_editor.py
+++ b/test/unit/python/mesh/test_mesh_editor.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for the MeshEditor class"
# Copyright (C) 2006-2011 Anders Logg
diff --git a/test/unit/python/mesh/test_mesh_function.py b/test/unit/python/mesh/test_mesh_function.py
old mode 100755
new mode 100644
index 58052b7..a562fa8
--- a/test/unit/python/mesh/test_mesh_function.py
+++ b/test/unit/python/mesh/test_mesh_function.py
@@ -24,9 +24,9 @@ from six.moves import xrange as range
from dolfin_utils.test import fixture, skip_in_parallel, skip_if_pybind11
- at pytest.fixture(params=range(5))
+ at pytest.fixture(params=range(4))
def name(request):
- names = ["Cell", "Vertex", "Edge", "Face", "Facet"]
+ names = [3, 0, 1, 2]
return names[request.param]
@@ -43,12 +43,12 @@ def mesh():
@fixture
def funcs(mesh):
- names = ["Cell", "Vertex", "Edge", "Face", "Facet"]
+ names = [3, 0, 1, 2]
tps = ['int', 'size_t', 'bool', 'double']
funcs = {}
for tp in tps:
for name in names:
- funcs[(tp, name)] = eval("%sFunction('%s', mesh)" % (name, tp))
+ funcs[(tp, name)] = eval("MeshFunction('%s', mesh, %d)" % (tp, name))
return funcs
@@ -63,13 +63,13 @@ def f(cube):
def test_size(tp, name, funcs, mesh):
- if name is "Vertex":
+ if name == 0:
a = len(funcs[(tp, name)])
b = mesh.num_vertices()
assert a == b
else:
a = len(funcs[(tp, name)])
- b = getattr(mesh, "num_%ss" % name.lower())()
+ b = mesh.num_entities(name)
assert a == b
@@ -146,14 +146,14 @@ def test_Assign(f, cube):
def test_meshfunction_where_equal():
mesh = UnitSquareMesh(2, 2)
- cf = CellFunction("size_t", mesh)
+ cf = MeshFunction("size_t", mesh, mesh.topology().dim())
cf.set_all(1)
cf[0] = 3
cf[3] = 3
assert list(cf.where_equal(3)) == [0, 3]
assert list(cf.where_equal(1)) == [1, 2, 4, 5, 6, 7]
- ff = FacetFunction("size_t", mesh)
+ ff = MeshFunction("size_t", mesh, mesh.topology().dim()-1)
ff.set_all(0)
ff[0] = 1
ff[2] = 3
@@ -162,7 +162,7 @@ def test_meshfunction_where_equal():
assert list(ff.where_equal(3)) == [2, 3]
assert list(ff.where_equal(0)) == [1] + list(range(4, ff.size()))
- vf = VertexFunction("size_t", mesh)
+ vf = MeshFunction("size_t", mesh, 0)
vf.set_all(3)
vf[1] = 1
vf[2] = 1
diff --git a/test/unit/python/mesh/test_mesh_iterator.py b/test/unit/python/mesh/test_mesh_iterator.py
old mode 100755
new mode 100644
index 3d44994..9e1f30e
--- a/test/unit/python/mesh/test_mesh_iterator.py
+++ b/test/unit/python/mesh/test_mesh_iterator.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for MeshIterator and subclasses"
# Copyright (C) 2006-2011 Anders Logg
diff --git a/test/unit/python/mesh/test_mesh_markers.py b/test/unit/python/mesh/test_mesh_markers.py
old mode 100755
new mode 100644
index 63805ec..0996506
--- a/test/unit/python/mesh/test_mesh_markers.py
+++ b/test/unit/python/mesh/test_mesh_markers.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the class MeshMarkers. These unit test actually test
a bit more than that, since they also test marking of meshes and the
interaction between Mesh - MeshDomains - MeshMarkers"""
diff --git a/test/unit/python/mesh/test_mesh_quality.py b/test/unit/python/mesh/test_mesh_quality.py
old mode 100755
new mode 100644
index ae5a28b..9739593
--- a/test/unit/python/mesh/test_mesh_quality.py
+++ b/test/unit/python/mesh/test_mesh_quality.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for the MeshQuality class"
# Copyright (C) 2013 Garth N. Wells
diff --git a/test/unit/python/mesh/test_mesh_transformation.py b/test/unit/python/mesh/test_mesh_transformation.py
old mode 100755
new mode 100644
index af8a8dd..f13f8e1
--- a/test/unit/python/mesh/test_mesh_transformation.py
+++ b/test/unit/python/mesh/test_mesh_transformation.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for the mesh library"
# Copyright (C) 2012 Anders Logg
diff --git a/test/unit/python/mesh/test_mesh_value_collection.py b/test/unit/python/mesh/test_mesh_value_collection.py
old mode 100755
new mode 100644
index 187c141..b8c947e
--- a/test/unit/python/mesh/test_mesh_value_collection.py
+++ b/test/unit/python/mesh/test_mesh_value_collection.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for MeshValueCollection"""
# Copyright (C) 2011 Johan Hake
@@ -98,7 +96,7 @@ def test_assign_2D_vertices():
def test_mesh_function_assign_2D_cells():
mesh = UnitSquareMesh(3, 3)
ncells = mesh.num_cells()
- f = CellFunction("int", mesh)
+ f = MeshFunction("int", mesh, mesh.topology().dim())
for cell in cells(mesh):
f[cell] = ncells - cell.index()
@@ -116,7 +114,7 @@ def test_mesh_function_assign_2D_cells():
h = MeshValueCollection("int", mesh, 2)
global_indices = mesh.topology().global_indices(2)
- ncells_global = mesh.size_global(2)
+ ncells_global = mesh.num_entities_global(2)
for cell in cells(mesh):
if global_indices[cell.index()] in [5, 8, 10]:
continue
@@ -137,7 +135,7 @@ def test_mesh_function_assign_2D_cells():
def test_mesh_function_assign_2D_facets():
mesh = UnitSquareMesh(3, 3)
mesh.init(1)
- f = FacetFunction("int", mesh, 25)
+ f = MeshFunction("int", mesh, mesh.topology().dim()-1, 25)
for cell in cells(mesh):
for i, facet in enumerate(facets(cell)):
assert 25 == f[facet]
@@ -160,7 +158,7 @@ def test_mesh_function_assign_2D_facets():
def test_mesh_function_assign_2D_vertices():
mesh = UnitSquareMesh(3, 3)
mesh.init(0)
- f = VertexFunction("int", mesh, 25)
+ f = MeshFunction("int", mesh, 0, 25)
g = MeshValueCollection("int", mesh, 0)
g.assign(f)
assert mesh.num_vertices() == f.size()
diff --git a/test/unit/python/mesh/test_multi_mesh_integration.py b/test/unit/python/mesh/test_multi_mesh_integration.py
deleted file mode 100755
index 6caa0e6..0000000
--- a/test/unit/python/mesh/test_multi_mesh_integration.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env py.test
-
-"""Unit tests for MultiMesh Integration"""
-
-# Copyright (C) 2014 Anders Logg and August Johansson
-#
-# This file is part of DOLFIN.
-#
-# DOLFIN is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# DOLFIN is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
-#
-# First added: 2014-03-04
-# Last changed: 2015-11-30
-
-from __future__ import print_function
-import numpy
-import pytest
-from dolfin import *
-from six.moves import xrange as range
-
-from dolfin_utils.test import skip_in_parallel, skip_if_pybind11
-
-
-def triangulation_to_mesh_2d(triangulation):
- editor = MeshEditor()
- mesh = Mesh()
- editor.open(mesh, 2, 2)
- num_cells = len(triangulation) / 6
- num_vertices = len(triangulation) / 2
- editor.init_cells(num_cells)
- editor.init_vertices(num_vertices)
- for i in range(num_cells):
- editor.add_cell(i, 3*i, 3*i + 1, 3*i + 2)
- for i in range(num_vertices):
- editor.add_vertex(i, triangulation[2*i], triangulation[2*i + 1])
- editor.close()
- return mesh
-
-
- at skip_in_parallel
- at skip_if_pybind11(reason="Not supported in pybind11")
-def test_integrate():
- # Create two meshes of the unit square
- mesh_0 = UnitSquareMesh(10, 10)
- mesh_1 = UnitSquareMesh(11, 11)
-
- # Translate
- pt = Point(0.632350,0.278498)
- mesh_1.translate(pt)
- exactarea = 2-(1-pt[0])*(1-pt[1])
-
- multimesh = MultiMesh()
- multimesh.add(mesh_0)
- multimesh.add(mesh_1)
- multimesh.build()
-
- for part in range(0, multimesh.num_parts()):
- #print(part)
- covered = multimesh.covered_cells(part)
- uncut = multimesh.uncut_cells(part)
- cut = multimesh.cut_cells(part)
- qr = multimesh.quadrature_rule_cut_cells(part)
- #print("covered")
- #print(covered)
- #print("uncut")
- #print(uncut)
- #print("cut")
- #print(cut)
- #print("quadrature")
- #print(qr)
-
- # area = assemble(L_0) + assemble(L_1)
- # MMA = MultiMeshAssembler()
- # area = MMA.assemble(L_multi)
- # area = assemble(L_multi)
- # assert round(area - exactarea, 7) == 0
-
-
- # # Translate second mesh randomly
- # #dx = Point(numpy.random.rand(),numpy.random.rand())
- # dx = Point(0.278498, 0.546881)
- # mesh_1.translate(dx)
-
- # exactvolume = (1 - abs(dx[0]))*(1 - abs(dx[1]))
-
- # # Compute triangulation volume using the quadrature rules
- # volume = 0
- # for c0 in cells(mesh_0):
- # for c1 in cells(mesh_1):
- # triangulation = c0.triangulate_intersection(c1)
- # if (triangulation.size>0):
- # # compute_quadrature_rule(triangulation,
- # # 2,2,1)
- # compute_quadrature_rule(c0,1)
-
- # tmesh = triangulation_to_mesh_2d(triangulation)
- # for t in cells(tmesh):
- # volume += t.volume()
-
-
-
- # errorstring = "translation=" + str(dx[0]) + str(" ") + str(dx[1])
- # assert round(volume - exactvolume, 7, errorstring)
diff --git a/test/unit/python/mesh/test_periodic_boundary_computation.py b/test/unit/python/mesh/test_periodic_boundary_computation.py
old mode 100755
new mode 100644
index dd9b491..ec516a7
--- a/test/unit/python/mesh/test_periodic_boundary_computation.py
+++ b/test/unit/python/mesh/test_periodic_boundary_computation.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for PeriodicBoundaryComputation"""
# Copyright (C) 2013 Mikael Mortensen
diff --git a/test/unit/python/mesh/test_sub_domain.py b/test/unit/python/mesh/test_sub_domain.py
old mode 100755
new mode 100644
index 947d97c..4432e02
--- a/test/unit/python/mesh/test_sub_domain.py
+++ b/test/unit/python/mesh/test_sub_domain.py
@@ -148,10 +148,10 @@ def test_creation_and_marking():
mesh.init()
for left, right in subdomain_pairs:
- for MeshFunc, f_dim in [(VertexFunction, 0),
- (FacetFunction, dim - 1),
- (CellFunction, dim)]:
- f = MeshFunc("size_t", mesh, 0)
+ for t_dim, f_dim in [(0, 0),
+ (mesh.topology().dim()-1, dim - 1),
+ (mesh.topology().dim(), dim)]:
+ f = MeshFunction("size_t", mesh, t_dim, 0)
left.mark(f, 1)
right.mark(f, 2)
@@ -174,10 +174,10 @@ def test_creation_and_marking():
MPI.sum(mesh.mpi_comm(), float((f.array() == 1).sum())),
])
- for MeshFunc, f_dim in [(VertexFunction, 0),
- (FacetFunction, dim-1),
- (CellFunction, dim)]:
- f = MeshFunc("size_t", mesh, 0)
+ for t_dim, f_dim in [(0, 0),
+ (mesh.topology().dim()-1, dim-1),
+ (mesh.topology().dim(), dim)]:
+ f = MeshFunction("size_t", mesh, t_dim, 0)
empty.mark(f, 1)
every.mark(f, 2)
@@ -296,10 +296,10 @@ def test_creation_and_marking_pybind11():
mesh.init()
for left, right in subdomain_pairs:
- for MeshFunc, f_dim in [(VertexFunction, 0),
- (FacetFunction, dim - 1),
- (CellFunction, dim)]:
- f = MeshFunc("size_t", mesh, 0)
+ for t_dim, f_dim in [(0, 0),
+ (mesh.topology().dim()-1, dim - 1),
+ (mesh.topology().dim(), dim)]:
+ f = MeshFunction("size_t", mesh, t_dim, 0)
left.mark(f, int(1))
right.mark(f, 2)
@@ -322,10 +322,10 @@ def test_creation_and_marking_pybind11():
MPI.sum(mesh.mpi_comm(), float((f.array() == 1).sum())),
])
- for MeshFunc, f_dim in [(VertexFunction, 0),
- (FacetFunction, dim-1),
- (CellFunction, dim)]:
- f = MeshFunc("size_t", mesh, 0)#
+ for t_dim, f_dim in [(0, 0),
+ (mesh.topology().dim()-1, dim-1),
+ (mesh.topology().dim(), dim)]:
+ f = MeshFunction("size_t", mesh, t_dim, 0)#
empty.mark(f, 1)
every.mark(f, 2)
diff --git a/test/unit/python/mesh/test_sub_mesh.py b/test/unit/python/mesh/test_sub_mesh.py
old mode 100755
new mode 100644
index c0e083e..5f43219
--- a/test/unit/python/mesh/test_sub_mesh.py
+++ b/test/unit/python/mesh/test_sub_mesh.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"Unit tests for the mesh library"
# Copyright (C) 2006 Anders Logg
@@ -40,7 +38,7 @@ def test_creation(MeshFunc):
mesh = MeshFunc(*args)
dim_t = mesh.topology().dim()
mesh.domains().init(dim_t)
- domains = CellFunction("size_t", mesh, 0)
+ domains = MeshFunction("size_t", mesh, mesh.topology().dim(), 0)
for cell in cells(mesh):
# Mark half the cells
if cell.index() > mesh.num_cells()/2:
@@ -49,7 +47,7 @@ def test_creation(MeshFunc):
mesh.domains().set_marker((cell.index(), 1), dim_t)
# Create mesh from stored MeshValueCollection and
- # external CellFunction
+ # external MeshFunction
smesh0 = SubMesh(mesh, 1)
smesh1 = SubMesh(mesh, domains, 1)
assert smesh0.num_cells() == smesh1.num_cells()
diff --git a/test/unit/python/meshconvert/test_mesh_converter.py b/test/unit/python/meshconvert/test_mesh_converter.py
index ec82581..ef9a1f6 100755
--- a/test/unit/python/meshconvert/test_mesh_converter.py
+++ b/test/unit/python/meshconvert/test_mesh_converter.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
""" Tests for the meshconvert module."""
# Copyright (C) 2012
@@ -458,7 +456,7 @@ class TestTriangle(Tester):
# test no. 2
from dolfin import MPI, Mesh, MeshFunction, \
edges, Edge, faces, Face, \
- SubsetIterator, facets, CellFunction, mpi_comm_world
+ SubsetIterator, facets, mpi_comm_world
fname = os.path.join(os.path.dirname(__file__), "data", "test_Triangle_3")
dfname = fname+".xml"
@@ -474,9 +472,9 @@ class TestTriangle(Tester):
self.assertEqual(mesh.num_vertices(), 58)
self.assertEqual(mesh.num_cells(), 58)
- # Create a size_t CellFunction and assign the values based on the
+ # Create a size_t MeshFunction and assign the values based on the
# converted Meshfunction
- cf = CellFunction("size_t", mesh)
+ cf = MeshFunction("size_t", mesh, mesh.topology().dim())
cf.array()[mfun.array()==10.0] = 0
cf.array()[mfun.array()==-10.0] = 1
diff --git a/test/unit/python/multimesh/test_compression.py b/test/unit/python/multimesh/test_compression.py
new file mode 100644
index 0000000..7a6ded6
--- /dev/null
+++ b/test/unit/python/multimesh/test_compression.py
@@ -0,0 +1,103 @@
+"""Unit tests for quadrature compression"""
+
+# Copyright (C) 2017 August Johansson
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+##
+# First added: 2016-06-28
+# Last changed: 2017-05-28
+
+from __future__ import print_function
+import pytest
+
+from dolfin import *
+from dolfin_utils.test import skip_in_parallel, skip_if_pybind11
+from numpy import random, sort, any
+
+def build_multimesh_2d(compress_volume, compress_interface):
+
+ # Create multimesh
+ multimesh = MultiMesh()
+ multimesh.parameters["compress_volume_quadrature"] = compress_volume
+ multimesh.parameters["compress_interface_quadrature"] = compress_interface
+
+ # Add background mesh
+ N_x = 2
+ mesh = UnitSquareMesh(N_x, N_x)
+ multimesh.add(mesh)
+
+ # Set seed
+ random.seed(1)
+
+ # Add num_parts-1 random sized and rotated rectangular meshes
+ num_parts = 2
+ while (multimesh.num_parts() < num_parts):
+
+ x0, x1 = sort(random.rand(2))
+ y0, y1 = sort(random.rand(2))
+ if abs(x1 - x0) < DOLFIN_EPS:
+ x1 += DOLFIN_EPS
+ if abs(y1 - y0) < DOLFIN_EPS:
+ y1 += DOLFIN_EPS
+
+ N_x_part = int(max(abs(x1-x0)*N_x, 1))
+ N_y_part = int(max(abs(y1-y0)*N_x, 1))
+ mesh = RectangleMesh(Point(x0, y0), Point(x1, y1),
+ N_x_part, N_y_part)
+
+ # Rotate
+ phi = random.rand()*180
+ mesh.rotate(phi)
+ coords = mesh.coordinates()
+ is_interior = not any(coords < 0) and not any(coords > 1.)
+
+ if is_interior:
+ multimesh.add(mesh)
+
+ multimesh.build()
+ return multimesh
+
+def volume_area(multimesh):
+ volume = multimesh.compute_volume()
+ area = multimesh.compute_area()
+ return volume, area
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_compression_2d():
+ # Reference volume and area
+ multimesh = build_multimesh_2d(False, False)
+ volume, area = volume_area(multimesh)
+
+ # Volume compression
+ multimesh_v = build_multimesh_2d(True, False)
+ volume_v, area_v = volume_area(multimesh)
+
+ # Interface compression
+ multimesh_i = build_multimesh_2d(False, True)
+ volume_i, area_i = volume_area(multimesh_i)
+
+ # Tolerances
+ volume_tol = 1e-10
+ area_tol = 1e-10
+
+ # Assert that the compressed volume / area is close
+ assert abs(volume - volume_v) < volume_tol
+ assert abs(area - area_i) < area_tol
+
+ # Assert that the compressed volume / area does not influence the area / volume
+ assert abs(volume - volume_i) < DOLFIN_EPS
+ assert abs(area - area_v) < DOLFIN_EPS
diff --git a/test/unit/python/multimesh/test_interface_area.py b/test/unit/python/multimesh/test_interface_area.py
new file mode 100755
index 0000000..82a171d
--- /dev/null
+++ b/test/unit/python/multimesh/test_interface_area.py
@@ -0,0 +1,139 @@
+"""Unit tests for multimesh volume computation"""
+
+# Copyright (C) 2016 Anders Logg
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# Modified by August Johansson 2016
+#
+# First added: 2016-11-02
+# Last changed: 2016-11-14
+
+from __future__ import print_function
+import pytest
+
+from dolfin import *
+from dolfin_utils.test import skip_in_parallel, skip_if_pybind11
+
+def compute_area_using_quadrature(multimesh):
+ total_area = 0
+ for part in range(multimesh.num_parts()):
+ part_area = 0
+
+ for cell, cell_rules in multimesh.quadrature_rules_interface(part).items():
+ for qr in cell_rules:
+ weights = qr[1]
+ part_area += sum(weights)
+
+ total_area += part_area
+ return total_area
+
+def create_multimesh_with_meshes_on_diagonal(width, offset, Nx):
+
+ # Mesh width (must be less than 1)
+ assert width < 1
+
+ # Mesh placement (must be less than the width)
+ assert offset < width
+
+ # Background mesh
+ mesh_0 = UnitSquareMesh(Nx, Nx)
+
+ # Create multimesh
+ multimesh = MultiMesh()
+ multimesh.add(mesh_0)
+
+ # Now we have num_parts = 1
+ num_parts = multimesh.num_parts()
+
+ while num_parts*offset + width < 1:
+ a = num_parts*offset
+ b = a + width
+ mesh_top = RectangleMesh(Point(a,a), Point(b,b), Nx, Nx)
+ multimesh.add(mesh_top)
+ num_parts = multimesh.num_parts()
+
+ multimesh.build()
+
+ area = compute_area_using_quadrature(multimesh)
+ exact_area = 0 if multimesh.num_parts() == 1 else 4*width + (multimesh.num_parts()-2)*(2*width + 2*offset)
+ error = abs(area - exact_area)
+ relative_error = error / exact_area
+ tol = max(DOLFIN_EPS_LARGE, multimesh.num_parts()*multimesh.part(0).num_cells()*DOLFIN_EPS)
+
+ print("")
+ print("width = {}, offset = {}, Nx = {}, num_parts = {}".format(width, offset, Nx, multimesh.num_parts()))
+ print("error", error)
+ print("relative error", relative_error)
+ print("tol", tol)
+ return relative_error < tol
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_meshes_on_diagonal():
+ "Place meshes on the diagonal inside a background mesh and check the interface area"
+
+ # for Nx in range(1, 50):
+ # for width_factor in range(1, 100):
+ # width = 3*width_factor/(100*DOLFIN_PI)
+ # for offset_factor in range(1, 100):
+ # offset = offset_factor*DOLFIN_PI / (100*3.2)
+ # if (offset < width):
+ # assert(create_multimesh_with_meshes_on_diagonal(width, offset, Nx))
+
+ width = DOLFIN_PI / 5
+ offset = 0.1111
+ Nx = 1
+ assert(create_multimesh_with_meshes_on_diagonal(width, offset, Nx))
+
+ # width = 1/DOLFIN_PI #0.18888
+ # offset = DOLFIN_PI/100 #1e-10
+ # for Nx in range(1, 50):
+ # assert(create_multimesh_with_meshes_on_diagonal(width, offset, Nx))
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_meshes_with_boundary_edge_overlap_2d():
+ # start with boundary of mesh 1 overlapping edges of mesg 0
+ mesh0 = UnitSquareMesh(4,4)
+ mesh1 = UnitSquareMesh(1,1)
+
+ mesh1_coords = mesh1.coordinates()
+ mesh1_coords *= 0.5
+ mesh1.translate(Point(0.25, 0.25))
+
+ multimesh = MultiMesh()
+ multimesh.add(mesh0)
+ multimesh.add(mesh1)
+ multimesh.build()
+
+ exact_area = 2.0
+
+ area = compute_area_using_quadrature(multimesh)
+ assert abs(area - exact_area) < DOLFIN_EPS_LARGE
+
+ # next translate mesh 1 such that only the horizontal part of the boundary overlaps
+ mesh1.translate(Point(0.1, 0.0))
+ multimesh.build()
+ area = compute_area_using_quadrature(multimesh)
+ assert abs(area - exact_area) < DOLFIN_EPS_LARGE
+
+ # next translate mesh 1 such that no boundaries overlap with edges
+ mesh1.translate(Point(0.0, 0.1))
+ multimesh.build()
+ area = compute_area_using_quadrature(multimesh)
+ assert abs(area - exact_area) < DOLFIN_EPS_LARGE
+
diff --git a/test/unit/python/multimesh/test_multimesh.py b/test/unit/python/multimesh/test_multimesh.py
index 4f08288..7a63c9a 100644
--- a/test/unit/python/multimesh/test_multimesh.py
+++ b/test/unit/python/multimesh/test_multimesh.py
@@ -17,6 +17,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# First added: 2016-06-11
+# Last changed: 2017-07-18
import pytest
from dolfin import *
@@ -25,43 +29,85 @@ import numpy
from dolfin_utils.test import fixture, skip_in_parallel, skip_if_pybind11
-
@fixture
def multimesh():
- mesh_0 = RectangleMesh(Point(0,0), Point(0.6, 1), 40, 20)
- mesh_1 = RectangleMesh(Point(0.5,0), Point(1,1),25,40)
+ mesh_0 = RectangleMesh(Point(0,0), Point(1, 1), 20, 20)
+ mesh_1 = RectangleMesh(Point(numpy.pi/10,numpy.pi/9),
+ Point(numpy.pi/8,numpy.pi/7),5,8)
multimesh = MultiMesh()
multimesh.add(mesh_0)
multimesh.add(mesh_1)
multimesh.build()
return multimesh
-
@fixture
def V(multimesh):
element = FiniteElement("Lagrange", triangle, 1)
return MultiMeshFunctionSpace(multimesh, element)
+ at fixture
+def V_high(multimesh):
+ element = FiniteElement("Lagrange", triangle, 3)
+ return MultiMeshFunctionSpace(multimesh, element)
+
+ at fixture
+def f():
+ return Expression("sin(pi*x[0])*cos(2*pi*x[1])", degree=4)
@fixture
+def f_2():
+ return Expression("x[0]*x[1]",degree=2)
+ at fixture
def v(V):
return MultiMeshFunction(V)
+ at fixture
+def v_high(V_high):
+ return MultiMeshFunction(V_high)
@skip_if_pybind11
@skip_in_parallel
def test_measure_mul(v, multimesh):
assert isinstance(v*dX, ufl.form.Form)
-
@skip_if_pybind11
@skip_in_parallel
def test_assemble_zero(v, multimesh):
assert numpy.isclose(assemble_multimesh(v*dX), 0)
-
@skip_if_pybind11
@skip_in_parallel
def test_assemble_area(v, multimesh):
v.vector()[:] = 1
assert numpy.isclose(assemble_multimesh(v*dX), 1)
+
+ at skip_if_pybind11
+ at skip_in_parallel
+def test_assemble_exterior_facet(v, multimesh):
+ v.vector()[:] = 1
+ assert numpy.isclose(assemble_multimesh(v*ds), 4)
+
+ at skip_if_pybind11
+ at skip_in_parallel
+def test_interpolate(v_high, f):
+ v_high.interpolate(f)
+ assert numpy.isclose(assemble_multimesh(v_high*dX), 0)
+
+ at skip_if_pybind11
+ at skip_in_parallel
+def test_project(f, V_high):
+ v = project(f ,V_high)
+ assert numpy.isclose(assemble_multimesh(v*dX), 0)
+
+ at skip_if_pybind11
+ at skip_in_parallel
+def test_errornorm_L2(f_2, v_high):
+ const = Expression("1", degree=1)
+ v_high.interpolate(f_2)
+ assert numpy.isclose(errornorm(const, v_high, norm_type="L2", degree_rise=3), numpy.sqrt(22)/6)
+
+ at skip_if_pybind11
+ at skip_in_parallel
+def test_errornorm_H1(f, f_2, v_high):
+ v_high.interpolate(f_2)
+ assert numpy.isclose(errornorm(f, v_high, norm_type="H1", degree_rise=3), numpy.sqrt(37./36+5*numpy.pi**2/4))
diff --git a/test/unit/python/multimesh/test_multimesh_cell_types.py b/test/unit/python/multimesh/test_multimesh_cell_types.py
new file mode 100644
index 0000000..608e6a1
--- /dev/null
+++ b/test/unit/python/multimesh/test_multimesh_cell_types.py
@@ -0,0 +1,94 @@
+"""Unit tests for MultiMesh cell types"""
+
+# Copyright (C) 2016 Magne Nordaas
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# First added: 2016-11-28
+# Last changed: 2016-11-28
+
+from dolfin import *
+import pytest
+from dolfin_utils.test import skip_in_parallel, skip_if_pybind11
+
+# test case with interface-edge overlap
+ at pytest.fixture
+def test_case_1(M, N):
+ multimesh = MultiMesh()
+ mesh0 = UnitSquareMesh(M, M)
+ mesh1 = RectangleMesh(Point(0.25, 0.25), Point(0.75, 0.75), N, N)
+ multimesh.add(mesh0)
+ multimesh.add(mesh1)
+ multimesh.build()
+ return multimesh
+
+# test case with squares on the diagonal
+ at pytest.fixture
+def test_case_2(width, offset, Nx):
+
+ # Mesh width (must be less than 1)
+ assert width < 1
+
+ # Mesh placement (must be less than the width)
+ assert offset < width
+
+ # Background mesh
+ mesh_0 = UnitSquareMesh(Nx, Nx)
+
+ # Create multimesh
+ multimesh = MultiMesh()
+ multimesh.add(mesh_0)
+
+ # Now we have num_parts = 1
+ num_parts = multimesh.num_parts()
+
+ while num_parts*offset + width < 1:
+ a = num_parts*offset
+ b = a + width
+ mesh_top = RectangleMesh(Point(a,a), Point(b,b), Nx, Nx)
+ multimesh.add(mesh_top)
+ num_parts = multimesh.num_parts()
+
+ multimesh.build()
+ return multimesh
+
+test_cases = [test_case_1(4,3),
+ test_case_2(DOLFIN_PI/5, 0.1111, 3)]
+
+ at skip_in_parallel
+ at skip_if_pybind11
+ at pytest.mark.parametrize("multimesh", test_cases)
+def test_cut_cell_has_quadrature(multimesh):
+ # Test that every cut cell has a nontrivial interface quadrature rule
+ for part in range(multimesh.num_parts()):
+ for cell in multimesh.cut_cells(part):
+ assert multimesh.quadrature_rules_interface(part, cell)
+
+ at skip_in_parallel
+ at skip_if_pybind11
+ at pytest.mark.parametrize("multimesh", test_cases)
+def test_multimesh_cell_types(multimesh):
+ # Test that every cell in the multimesh is either cut, uncut, or covered
+ for part in range(multimesh.num_parts()):
+ cells = set(range(multimesh.part(part).num_cells()))
+ cut_cells = set(multimesh.cut_cells(part))
+ uncut_cells = set(multimesh.uncut_cells(part))
+ covered_cells = set(multimesh.covered_cells(part))
+
+ assert cut_cells.union(uncut_cells).union(covered_cells) == cells
+ assert cut_cells.intersection(uncut_cells) == set()
+ assert cut_cells.intersection(covered_cells) == set()
+ assert uncut_cells.intersection(covered_cells) == set()
diff --git a/test/unit/python/multimesh/test_multimesh_coefficients.py b/test/unit/python/multimesh/test_multimesh_coefficients.py
new file mode 100644
index 0000000..3605e74
--- /dev/null
+++ b/test/unit/python/multimesh/test_multimesh_coefficients.py
@@ -0,0 +1,113 @@
+"Unit tests for multimesh coefficients"
+
+# Copyright (C) 2006 Magne Nordaas
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# Modified by August Johansson 2017
+#
+# First added: 2017-04-02
+# Last changed: 2017-06-20
+
+from __future__ import print_function
+import pytest
+from dolfin import *
+from dolfin_utils.test import fixture, skip_in_parallel, skip_if_pybind11
+
+ at fixture
+def mesh0():
+ return UnitSquareMesh(2,2)
+
+ at fixture
+def mesh1():
+ return RectangleMesh(Point(0.25, 0.25), Point(0.75, 0.75), 2, 2)
+
+ at fixture
+def multimesh(mesh0, mesh1):
+ multimesh = MultiMesh()
+ multimesh.add(mesh0)
+ multimesh.add(mesh1)
+ multimesh.build()
+ return multimesh
+
+ at fixture
+def V(multimesh):
+ return MultiMeshFunctionSpace(multimesh, "P", 1)
+
+ at fixture
+def V0(mesh0):
+ return FunctionSpace(mesh0, "P", 1)
+
+ at fixture
+def V1(mesh1):
+ return FunctionSpace(mesh1, "P", 1)
+
+ at fixture
+def f(V, V0, V1):
+ f = MultiMeshFunction(V)
+ f.assign_part(0, interpolate(Constant(1.0), V0))
+ f.assign_part(1, interpolate(Constant(2.0), V1))
+ return f
+
+ at fixture
+def g():
+ return Constant(0.5)
+
+ at fixture
+def h(V, V0, V1):
+ h = MultiMeshFunction(V)
+ h.assign_part(0, interpolate(Constant(1.0), V0))
+ h.assign_part(1, interpolate(Constant(1.5), V1))
+ return h
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_dX_integral(f, g, h):
+ f_dX = assemble_multimesh(f * dX)
+ assert abs(f_dX - 1.25) < DOLFIN_EPS_LARGE
+
+ fgh_dX = assemble_multimesh(f*g*h * dX)
+ assert abs(fgh_dX - 0.75) < DOLFIN_EPS_LARGE
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_dI_integral(f, g, h):
+ f_dI0 = assemble_multimesh(f("-") * dI)
+ assert abs(f_dI0 - 4.0) < DOLFIN_EPS_LARGE
+
+ f_dI1 = assemble_multimesh(f("+") * dI)
+ assert abs(f_dI1 - 2.0) < DOLFIN_EPS_LARGE
+
+ fgh_dI0 = assemble_multimesh(f("-")*g("-")*h("-") * dI)
+ assert abs(fgh_dI0 - 3.0) < DOLFIN_EPS_LARGE
+
+ fgh_dI1 = assemble_multimesh(f("+")*g("+")*h("+") * dI)
+ assert abs(fgh_dI1 - 1.0) < DOLFIN_EPS_LARGE
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_dO_integral(f, g, h):
+ f_dO0 = assemble_multimesh(f("-") * dO)
+ assert abs(f_dO0 - 0.50) < DOLFIN_EPS_LARGE
+
+ f_dO1 = assemble_multimesh(f("+") * dO)
+ assert abs(f_dO1 - 0.25) < DOLFIN_EPS_LARGE
+
+ fgh_dO0 = assemble_multimesh(f("-")*g("-")*h("-") * dO)
+ assert abs(fgh_dO0 - 0.375) < DOLFIN_EPS_LARGE
+
+ fgh_dO1 = assemble_multimesh(f("+")*g("+")*h("+") * dO)
+ assert abs(fgh_dO1 - 0.125) < DOLFIN_EPS_LARGE
diff --git a/test/unit/python/geometry/test_issues.py b/test/unit/python/multimesh/test_multimesh_initialization.py
similarity index 54%
rename from test/unit/python/geometry/test_issues.py
rename to test/unit/python/multimesh/test_multimesh_initialization.py
index 0413a23..04c741a 100755
--- a/test/unit/python/geometry/test_issues.py
+++ b/test/unit/python/multimesh/test_multimesh_initialization.py
@@ -1,8 +1,6 @@
-#!/usr/bin/env py.test
+"""Unit tests for multimesh volume computation"""
-"""Unit tests for intersection computation"""
-
-# Copyright (C) 2013 Anders Logg
+# Copyright (C) 2016 Anders Logg
#
# This file is part of DOLFIN.
#
@@ -19,8 +17,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
#
-# First added: 2013-12-09
-# Last changed: 2014-05-30
+# First added: 2016-05-03
+# Last changed: 2016-11-16
from __future__ import print_function
import pytest
@@ -28,26 +26,15 @@ import pytest
from dolfin import *
from dolfin_utils.test import skip_in_parallel
+from math import pi, sin, cos
@skip_in_parallel
-def test_issue_97():
- "Test from Mikael Mortensen (issue #97)"
-
- N = 2
- L = 1000
- mesh = BoxMesh(Point(0, 0, 0), Point(L, L, L), N, N, N)
- V = FunctionSpace(mesh, 'CG', 1)
- v = interpolate(Expression('x[0]', degree=1), V)
- x = Point(0.5*L, 0.5*L, 0.5*L)
- vx = v(x)
-
-
- at skip_in_parallel
-def test_issue_168():
- "Test from Torsten Wendav (issue #168)"
-
- mesh = UnitCubeMesh(14, 14, 14)
- V = FunctionSpace(mesh, "Lagrange", 1)
- v = Function(V)
- x = (0.75, 0.25, 0.125)
- vx = v(x)
+def test_multimesh_init_1():
+ mm = MultiMesh()
+ mesh0 = UnitSquareMesh(1, 1)
+ mesh1 = RectangleMesh(Point(0,0), Point(2,0.5), 1, 1)
+ mm.add(mesh0)
+ mm.add(mesh1)
+ mm.build(2)
+
+ return 1 == 1
diff --git a/test/unit/python/multimesh/test_multimesh_issues.py b/test/unit/python/multimesh/test_multimesh_issues.py
new file mode 100755
index 0000000..afec097
--- /dev/null
+++ b/test/unit/python/multimesh/test_multimesh_issues.py
@@ -0,0 +1,69 @@
+"""Unit tests for reported multimesh issues"""
+
+# Copyright (C) 2016 Anders Logg
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# Modified by August Johansson 2016
+#
+# First added: 2016-05-03
+# Last changed: 2017-06-19
+
+from __future__ import print_function
+import pytest
+
+from dolfin import *
+from dolfin_utils.test import skip_in_parallel, skip_if_pybind11
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_issue_754():
+ N = 3
+ meshes = [UnitSquareMesh(2*N, 2*N),
+ RectangleMesh(Point(0.1, 0.15), Point(0.6, 0.65), N, N),
+ RectangleMesh(Point(0.4, 0.35), Point(0.9, 0.85), N, N)]
+
+ multimesh = MultiMesh()
+ for mesh in meshes:
+ multimesh.add(mesh)
+ multimesh.build()
+
+ V = MultiMeshFunctionSpace(multimesh, "CG", 1)
+ u, v = TrialFunction(V), TestFunction(V)
+
+ a = inner(u,v) * dX
+ b = a + inner(jump(u),jump(v)) * dI
+ A = assemble_multimesh(a)
+ B = assemble_multimesh(b)
+
+ # create multimesh function w such that
+ # w(x) = 1 if x is in the last mesh
+ # = 0 otherwise
+
+ w = MultiMeshFunction(V); x = w.vector()
+ dofs = V.part(V.num_parts()-1).dofmap().dofs() \
+ + sum(V.part(i).dim() for i in range(V.num_parts()-1))
+ w.vector()[dofs] = 1.
+
+ # Compute the area and perimeter of the last mesh
+ a = w.vector().inner(A * w.vector())
+ p = w.vector().inner(B * w.vector()) - a
+
+ # print("Computed area (a) and perimeter (p) of last mesh:")
+ # print(" a = {0:1.4f} (exact value is .25)".format(a))
+ # print(" p = {0:1.4f} (exact value is 2.0)".format(p))
+ assert(abs(a - 0.25) < DOLFIN_EPS_LARGE)
+ assert(abs(p - 2.) < DOLFIN_EPS_LARGE)
diff --git a/test/unit/python/multimesh/test_multimesh_solve.py b/test/unit/python/multimesh/test_multimesh_solve.py
new file mode 100644
index 0000000..3d563ee
--- /dev/null
+++ b/test/unit/python/multimesh/test_multimesh_solve.py
@@ -0,0 +1,125 @@
+"""Unit tests for MultiMesh PDE solvers"""
+
+# Copyright (C) 2017 August Johansson
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# First added: 2017-07-18
+# Last changed: 2017-07-18
+
+import pytest
+from dolfin import *
+
+from dolfin_utils.test import skip_in_parallel, skip_if_pybind11, fixture
+
+ at fixture
+def exactsolution_2d():
+ return Expression("x[0] + x[1]", degree=1)
+
+ at fixture
+def exactsolution_3d():
+ return Expression("x[0] + x[1] + x[2]", degree=1)
+
+ at fixture
+def solve_multimesh_poisson(mesh_0, mesh_1, exactsolution):
+
+ # Build multimesh
+ multimesh = MultiMesh()
+ multimesh.add(mesh_0)
+ multimesh.add(mesh_1)
+ multimesh.build()
+
+ # FEM setup
+ V = MultiMeshFunctionSpace(multimesh, "Lagrange", 1)
+ u = TrialFunction(V)
+ v = TestFunction(V)
+ n = FacetNormal(multimesh)
+ h = 2.0*Circumradius(multimesh)
+ h = (h('+') + h('-')) / 2
+
+ # Set data
+ f = Constant(0.0)
+
+ # Set parameters
+ alpha = 4.0
+ beta = 4.0
+
+ # Define bilinear form
+ a = dot(grad(u), grad(v))*dX \
+ - dot(avg(grad(u)), jump(v, n))*dI \
+ - dot(avg(grad(v)), jump(u, n))*dI \
+ + alpha/h*jump(u)*jump(v)*dI \
+ + beta*dot(jump(grad(u)), jump(grad(v)))*dO
+
+ # Define linear form
+ L = f*v*dX
+
+ # Assemble linear system
+ A = assemble_multimesh(a)
+ b = assemble_multimesh(L)
+
+ # Apply boundary condition
+ bc = MultiMeshDirichletBC(V, exactsolution, DomainBoundary())
+ bc.apply(A, b)
+
+ # Solve
+ uh = MultiMeshFunction(V)
+ solve(A, uh.vector(), b)
+
+ return uh
+
+ at pytest.mark.slow
+ at skip_if_pybind11
+ at skip_in_parallel
+def test_multimesh_poisson_2d():
+ # This tests solves a Poisson problem on two meshes in 2D with u =
+ # x+y as exact solution
+
+ # FIXME: This test is quite slow.
+
+ # Define meshes
+ mesh_0 = UnitSquareMesh(2, 2)
+ mesh_1 = RectangleMesh(Point(0.1*DOLFIN_PI, 0.1*DOLFIN_PI),
+ Point(0.2*DOLFIN_PI, 0.2*DOLFIN_PI),
+ 2, 2)
+
+ # Solve multimesh Poisson
+ uh = solve_multimesh_poisson(mesh_0, mesh_1, exactsolution_2d())
+
+ # Check error
+ assert errornorm(exactsolution_2d(), uh, 'L2', degree_rise=1) < DOLFIN_EPS_LARGE
+
+ at pytest.mark.slow
+ at skip_if_pybind11
+ at skip_in_parallel
+ at pytest.mark.skipif(True, reason="3D not fully implemented")
+def test_multimesh_poisson_3d():
+ # This tests solves a Poisson problem on two meshes in 3D with u =
+ # x+y+z as exact solution
+
+ # FIXME: This test is quite slow.
+
+ # Define meshes
+ mesh_0 = UnitCubeMesh(2, 2, 2)
+ mesh_1 = BoxMesh(Point(0.1*DOLFIN_PI, 0.1*DOLFIN_PI, 0.1*DOLFIN_PI),
+ Point(0.2*DOLFIN_PI, 0.2*DOLFIN_PI, 0.2*DOLFIN_PI),
+ 2, 2, 2)
+
+ # Solve multimesh Poisson
+ uh = solve_multimesh_poisson(mesh_0, mesh_1, exactsolution_3d())
+
+ # Check error
+ assert errornorm(exactsolution_3d(), uh, 'L2', degree_rise=1) < DOLFIN_EPS_LARGE
diff --git a/test/unit/python/multimesh/test_volume.py b/test/unit/python/multimesh/test_volume.py
new file mode 100755
index 0000000..70d4ed0
--- /dev/null
+++ b/test/unit/python/multimesh/test_volume.py
@@ -0,0 +1,206 @@
+"""Unit tests for multimesh volume computation"""
+
+# Copyright (C) 2016 Anders Logg
+#
+# This file is part of DOLFIN.
+#
+# DOLFIN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DOLFIN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+#
+# Modified by August Johansson 2016
+# Modified by Simon Funke 2017
+#
+# First added: 2016-05-03
+# Last changed: 2017-05-25
+
+from __future__ import print_function
+import pytest
+
+from dolfin import *
+from dolfin_utils.test import skip_in_parallel, skip_if_pybind11
+
+def compute_volume(multimesh):
+ # Reference volume computation
+ v0 = multimesh.compute_volume()
+
+ # Create function space for DG volume computation
+ V = MultiMeshFunctionSpace(multimesh, "DG", 0)
+
+ # Create and evaluate volume functional
+ v = TestFunction(V)
+ M = v*dX
+ v1 = sum(assemble(M).array())
+
+ # Alternative volume computation
+ dXmm = dx(domain=multimesh) + dC(domain=multimesh)
+ M = Constant(1.0)*dXmm
+ v2 = assemble_multimesh(M)
+
+ # FIXME: We could be able to tighten the tolerance here
+ assert abs(v0 - v1) / v0 < DOLFIN_EPS_LARGE
+ assert abs(v0 - v2) / v0 < DOLFIN_EPS_LARGE
+
+ return v0
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_volume_2d():
+ "Integrate volume of union of 2D meshes"
+
+ # Number of meshes on top of background mesh
+ num_meshes = 8
+
+ # Create background mesh so we can easily compute volume
+ mesh_0 = UnitSquareMesh(1, 1)
+ mesh_0.scale(10.0)
+ mesh_0.translate(Point(-5, -5))
+ exact_volume = 100.0
+
+ # Create meshes with centres distributed around the unit circle.
+ # Meshes are scaled by a factor 2 so that they overlap.
+ meshes = []
+ for i in range(num_meshes):
+ mesh = UnitSquareMesh(1, 1)
+ angle = 2.*pi*float(i) / float(num_meshes)
+ print("i, angle", i, angle)
+ mesh.translate(Point(-0.5, -0.5))
+ mesh.scale(2.0)
+ mesh.rotate(180.0*angle / pi)
+ mesh.translate(Point(cos(angle), sin(angle)))
+ meshes.append(mesh)
+
+ # Save meshes to file so we can examine them
+ #File('output/background_mesh.pvd') << mesh_0
+ #vtkfile = File('output/meshes.pvd')
+ #for mesh in meshes:
+ # vtkfile << mesh
+
+ # Create multimesh
+ multimesh = MultiMesh()
+ for mesh in [mesh_0] + meshes:
+ multimesh.add(mesh)
+ multimesh.build()
+
+ # Compute approximate volume
+ approximative_volume = compute_volume(multimesh)
+
+ print("exact volume ", exact_volume)
+ print("approximative volume ", approximative_volume)
+ print("relative approximate volume error %1.16e" % ((exact_volume - approximative_volume) / exact_volume))
+ assert abs(exact_volume - approximative_volume) / exact_volume < DOLFIN_EPS_LARGE
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_volume_2d_4_meshes():
+ "Test with four meshes that previously failed"
+
+ # Create multimesh
+ multimesh = MultiMesh()
+
+ # Mesh size
+ h = 0.25
+ Nx = int(round(1 / h))
+
+ # Background mesh
+ mesh_0 = UnitSquareMesh(Nx, Nx)
+ multimesh.add(mesh_0)
+
+ # Mesh 1
+ x0 = 0.35404867974764142602
+ y0 = 0.16597416632155614913
+ x1 = 0.63997881656511634851
+ y1 = 0.68786139026650294781
+ mesh_1 = RectangleMesh(Point(x0, y0), Point(x1, y1),
+ max(int(round((x1-x0)/h)), 1), max(int(round((y1-y0)/h)), 1))
+ mesh_1.rotate(39.609407484349517858)
+ multimesh.add(mesh_1)
+
+ # Mesh 2
+ x0 = 0.33033712968711609337
+ y0 = 0.22896817104377231722
+ x1 = 0.82920109332967595339
+ y1 = 0.89337241458397931293
+ mesh_2 = RectangleMesh(Point(x0, y0), Point(x1, y1),
+ max(int(round((x1-x0)/h)), 1), max(int(round((y1-y0)/h)), 1))
+ mesh_2.rotate(31.532416069662392744)
+ multimesh.add(mesh_2)
+
+ # Mesh 3
+ x0 = 0.28105941241656401397
+ y0 = 0.30745787374091237965
+ x1 = 0.61959648394007071914
+ y1 = 0.78600209801737319637
+ mesh_3 = RectangleMesh(Point(x0, y0), Point(x1, y1),
+ max(int(round((x1-x0)/h)), 1), max(int(round((y1-y0)/h)), 1))
+ mesh_3.rotate(40.233022128340330426)
+ multimesh.add(mesh_3)
+
+ multimesh.build()
+
+ exact_volume = 1
+ approximate_volume = compute_volume(multimesh)
+
+ print("exact volume ", exact_volume)
+ print("approximative volume ", approximate_volume)
+ print("approximate volume error %1.16e" % (exact_volume - approximate_volume))
+ assert abs(exact_volume - approximate_volume) < DOLFIN_EPS_LARGE
+
+ at skip_in_parallel
+ at skip_if_pybind11
+def test_volume_2d_six_meshes():
+ "Integrate volume of six 2D meshes"
+
+ # Number of elements
+ Nx = 8
+ h = 1. / Nx
+
+ # Background mesh
+ mesh_0 = UnitSquareMesh(Nx, Nx)
+
+ # 5 meshes plus background mesh
+ num_meshes = 5
+
+ # List of points for generating the meshes on top
+ points = [[ Point(0.747427, 0.186781), Point(0.849659, 0.417130) ],
+ [ Point(0.152716, 0.471681), Point(0.455943, 0.741585) ],
+ [ Point(0.464473, 0.251876), Point(0.585051, 0.533569) ],
+ [ Point(0.230112, 0.511897), Point(0.646974, 0.892193) ],
+ [ Point(0.080362, 0.422675), Point(0.580151, 0.454286) ]]
+
+ angles = [ 88.339755, 94.547259, 144.366564, 172.579922, 95.439692 ]
+
+ # Create multimesh
+ multimesh = MultiMesh()
+ multimesh.add(mesh_0)
+
+ # Add the 5 background meshes
+ for i in range(num_meshes):
+ nx = max(int(round(abs(points[i][0].x()-points[i][1].x()) / h)), 1)
+ ny = max(int(round(abs(points[i][0].y()-points[i][1].y()) / h)), 1)
+ mesh = RectangleMesh(points[i][0], points[i][1], nx, ny)
+ mesh.rotate(angles[i])
+ multimesh.add(mesh)
+ multimesh.build()
+
+ # Save meshes to file
+ #vtkfile = File('output/test_six_meshes.pvd')
+ #for i in range(multimesh.num_parts()):
+ # vtkfile << multimesh.part(i)
+
+ exact_volume = 1.0
+ approximate_volume = compute_volume(multimesh)
+
+ print("exact volume ", exact_volume)
+ print("approximative volume ", approximate_volume)
+ print("approximate volume error %1.16e" % (exact_volume - approximate_volume))
+ assert abs(exact_volume - approximate_volume) < DOLFIN_EPS_LARGE
diff --git a/test/unit/python/multistage/test_RK_solver.py b/test/unit/python/multistage/test_RK_solver.py
index 3ac1b4e..0a00530 100755
--- a/test/unit/python/multistage/test_RK_solver.py
+++ b/test/unit/python/multistage/test_RK_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the RKSolver interface"""
# Copyright (C) 2013 Johan Hake
@@ -64,7 +62,7 @@ def test_butcher_schemes_scalar():
solver.step_interval(0., tstop, dt)
u_errors.append(u_true(0.0, 0.0) - u(0.0, 0.0))
- assert scheme.order()-min(convergence_order(u_errors))<0.1
+ assert scheme.order() - min(convergence_order(u_errors)) < 0.1
@pytest.mark.slow
@@ -93,5 +91,5 @@ def test_butcher_schemes_vector():
u_errors_0.append(u_true(0.0, 0.0)[0] - u(0.0, 0.0)[0])
u_errors_1.append(u_true(0.0, 0.0)[1] - u(0.0, 0.0)[1])
- assert scheme.order()-min(convergence_order(u_errors_0))<0.1
- assert scheme.order()-min(convergence_order(u_errors_1))<0.1
+ assert scheme.order() - min(convergence_order(u_errors_0)) < 0.1
+ assert scheme.order() - min(convergence_order(u_errors_1)) < 0.1
diff --git a/test/unit/python/multistage/test_point_integral_solver.py b/test/unit/python/multistage/test_point_integral_solver.py
index 320e0c0..4a84ce1 100755
--- a/test/unit/python/multistage/test_point_integral_solver.py
+++ b/test/unit/python/multistage/test_point_integral_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the RKSolver interface"""
# Copyright (C) 2013 Johan Hake
diff --git a/test/unit/python/nls/test_PETScSNES_solver.py b/test/unit/python/nls/test_PETScSNES_solver.py
index 07e87fa..8f896c0 100755
--- a/test/unit/python/nls/test_PETScSNES_solver.py
+++ b/test/unit/python/nls/test_PETScSNES_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit test for the SNES nonlinear solver"""
# Copyright (C) 2012 Patrick E. Farrell
diff --git a/test/unit/python/nls/test_PETScTAOSolver.py b/test/unit/python/nls/test_PETScTAOSolver.py
index 665bacd..d82035b 100755
--- a/test/unit/python/nls/test_PETScTAOSolver.py
+++ b/test/unit/python/nls/test_PETScTAOSolver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit test for the PETSc TAO solver"""
# Copyright (C) 2014 Tianyi Li
diff --git a/test/unit/python/nls/test_TAO_linear_bound_solver.py b/test/unit/python/nls/test_TAO_linear_bound_solver.py
index eb516a5..6d7ad20 100755
--- a/test/unit/python/nls/test_TAO_linear_bound_solver.py
+++ b/test/unit/python/nls/test_TAO_linear_bound_solver.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for the TAOLinearBoundSolver interface"""
# Copyright (C) 2013 Corrado Maurini
diff --git a/test/unit/python/parallel-assembly-solve/test_solve_result_against_reference.py b/test/unit/python/parallel-assembly-solve/test_solve_result_against_reference.py
index d8bb460..1663642 100755
--- a/test/unit/python/parallel-assembly-solve/test_solve_result_against_reference.py
+++ b/test/unit/python/parallel-assembly-solve/test_solve_result_against_reference.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env py.test
"""This file solves a simple reaction-diffusion problem and compares
the norm of the solution vector with a known solution (obtained when
running in serial). It is used for validating mesh partitioning and
@@ -118,8 +117,8 @@ def test_computed_norms_against_references():
# Mesh files and degrees to check
meshes = [(UnitSquareMesh(16, 16), "16x16 unit tri square"),
(UnitCubeMesh(4, 4, 4), "4x4x4 unit tet cube"),
- (UnitQuadMesh.create(16, 16), "16x16 unit quad square"),
- (UnitHexMesh.create(4, 4, 4), "4x4x4 unit hex cube")]
+ (UnitSquareMesh.create(16, 16, CellType.Type_quadrilateral), "16x16 unit quad square"),
+ (UnitCubeMesh.create(4, 4, 4, CellType.Type_hexahedron), "4x4x4 unit hex cube")]
degrees = [1, 2, 3, 4]
# For MUMPS, increase estimated require memory increase. Typically
diff --git a/test/unit/python/parameter/test_parameters.py b/test/unit/python/parameter/test_parameters.py
index df8b0d0..c2bc54c 100755
--- a/test/unit/python/parameter/test_parameters.py
+++ b/test/unit/python/parameter/test_parameters.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env py.test
-
"""Unit tests for parameter library"""
# Copyright (C) 2011 Anders Logg
@@ -22,8 +20,8 @@
from __future__ import print_function
import pytest
import os
-from dolfin import *
+from dolfin import *
from dolfin_utils.test import *
@skip_in_parallel
diff --git a/test/unit/python/ufl-jit-assemble-chain/test_assembly_derivatives.py b/test/unit/python/ufl-jit-assemble-chain/test_assembly_derivatives.py
index 873aae3..004c3e3 100755
--- a/test/unit/python/ufl-jit-assemble-chain/test_assembly_derivatives.py
+++ b/test/unit/python/ufl-jit-assemble-chain/test_assembly_derivatives.py
@@ -1,5 +1,3 @@
-#!usr/bin/env py.test
-
"""System integration tests for ufl-derivative-jit-assembly chain."""
# Copyright (C) 2011 Martin S. Alnaes
diff --git a/test/unit/python/ufl-jit-assemble-chain/test_form_operations.py b/test/unit/python/ufl-jit-assemble-chain/test_form_operations.py
index 6dff3db..8a02eb9 100755
--- a/test/unit/python/ufl-jit-assemble-chain/test_form_operations.py
+++ b/test/unit/python/ufl-jit-assemble-chain/test_form_operations.py
@@ -1,5 +1,3 @@
-#!usr/bin/env py.test
-
"""Tests for DOLFIN integration of various form operations"""
# Copyright (C) 2011 Marie E. Rognes
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/fenics/dolfin.git
More information about the debian-science-commits
mailing list