├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── README.md ├── doc └── maxent │ ├── README.md │ ├── adapt.jl │ ├── comparison.jl │ ├── lme.jl │ ├── main.jl │ ├── matlab_comparison.jl │ ├── move_node.jl │ ├── patterns │ ├── circle.jl │ ├── patterns.jl │ └── threeDStar.jl │ ├── pmesh.jl │ ├── solvers.jl │ ├── testplots.jl │ ├── tests.jl │ └── utils │ ├── colors.jl │ ├── colorutils.jl │ ├── geometry.jl │ └── utils.jl ├── hpc_algorithm.hpp ├── hpc_array.hpp ├── hpc_array_traits.hpp ├── hpc_array_vector.hpp ├── hpc_atomic.hpp ├── hpc_dimensional.hpp ├── hpc_execution.hpp ├── hpc_functional.hpp ├── hpc_index.hpp ├── hpc_iterator.hpp ├── hpc_limits.hpp ├── hpc_macros.hpp ├── hpc_math.hpp ├── hpc_matrix.hpp ├── hpc_matrix3x3.hpp ├── hpc_memory.hpp ├── hpc_numeric.hpp ├── hpc_quaternion.hpp ├── hpc_range.hpp ├── hpc_range_sum.hpp ├── hpc_symmetric3x3.hpp ├── hpc_tensor_detail.hpp ├── hpc_transform_reduce.hpp ├── hpc_vector.hpp ├── hpc_vector3.hpp ├── j2 └── hardening.hpp ├── lgr.cpp ├── lgr_adapt.cpp ├── lgr_adapt.hpp ├── lgr_adapt_util.hpp ├── lgr_bar.cpp ├── lgr_bar.hpp ├── lgr_composite_gradient.cpp ├── lgr_composite_h_min.cpp ├── lgr_composite_inline.hpp ├── lgr_composite_nodal_mass.cpp ├── lgr_composite_tetrahedron.cpp ├── lgr_composite_tetrahedron.hpp ├── lgr_domain.cpp ├── lgr_domain.hpp ├── lgr_element_specific.cpp ├── lgr_element_specific.hpp ├── lgr_element_specific_inline.hpp ├── lgr_exodus.cpp ├── lgr_exodus.hpp ├── lgr_input.hpp ├── lgr_material_set.hpp ├── lgr_matrix4x4.hpp ├── lgr_mesh_indices.hpp ├── lgr_meshing.cpp ├── lgr_meshing.hpp ├── lgr_physics.cpp ├── lgr_physics.hpp ├── lgr_physics_util.hpp ├── lgr_print.cpp ├── lgr_print.hpp ├── lgr_reduce.hpp ├── lgr_stabilized.cpp ├── lgr_stabilized.hpp ├── lgr_state.cpp ├── lgr_state.hpp ├── lgr_tetrahedron.cpp ├── lgr_tetrahedron.hpp ├── lgr_triangle.cpp ├── lgr_triangle.hpp ├── lgr_vector4.hpp ├── lgr_vtk.cpp ├── lgr_vtk.hpp ├── lgr_vtk_util.hpp ├── otm.cpp ├── otm_adapt.cpp ├── otm_adapt.hpp ├── otm_adapt_util.hpp ├── otm_apps.cpp ├── otm_apps.hpp ├── otm_arborx_search_impl.cpp ├── otm_arborx_search_impl.hpp ├── otm_distance.cpp ├── otm_distance.hpp ├── otm_distance_util.cpp ├── otm_distance_util.hpp ├── otm_host_pinned_state.hpp ├── otm_materials.hpp ├── otm_meshing.cpp ├── otm_meshing.hpp ├── otm_meshing_sort.hpp ├── otm_meshless.cpp ├── otm_meshless.hpp ├── otm_search.cpp ├── otm_search.hpp ├── otm_search_util.hpp ├── otm_tet2meshless.cpp ├── otm_tet2meshless.hpp ├── otm_tetrahedron_util.hpp ├── otm_util.hpp ├── otm_vtk.cpp ├── otm_vtk.hpp └── unit_tests ├── CMakeLists.txt ├── adapt.cpp ├── cube.g ├── distances.cpp ├── exodus.cpp ├── map.cpp ├── materials.cpp ├── maxent.cpp ├── mechanics.cpp ├── otm_unit_mesh.hpp ├── quaternion.cpp ├── search.cpp ├── tensor.cpp ├── tets.g ├── unit_arborx_testing_util.hpp ├── unit_device_util.hpp ├── unit_otm_distance_util.hpp ├── ut_main.cpp └── vtk.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: AlwaysBreak 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveDeclarations: true 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: All 18 | AlwaysBreakAfterReturnType: All 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: true 25 | AfterControlStatement: false 26 | AfterEnum: true 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: true 31 | AfterUnion: true 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Custom 37 | BreakBeforeTernaryOperators: false 38 | BreakConstructorInitializersBeforeComma: false 39 | BreakStringLiterals: true 40 | ColumnLimit: 120 41 | CommentPragmas: '^ IWYU pragma:' 42 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 43 | ConstructorInitializerIndentWidth: 4 44 | ContinuationIndentWidth: 4 45 | Cpp11BracedListStyle: true 46 | DerivePointerAlignment: false 47 | DisableFormat: false 48 | ExperimentalAutoDetectBinPacking: false 49 | FixNamespaceComments: true 50 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 51 | IncludeCategories: 52 | - Regex: '^<.*\.h>' 53 | Priority: 1 54 | - Regex: '^<.*' 55 | Priority: 2 56 | - Regex: '.*' 57 | Priority: 3 58 | IncludeIsMainRegex: '([-_](test|unittest))?$' 59 | IndentCaseLabels: true 60 | IndentWidth: 2 61 | IndentWrappedFunctionNames: false 62 | KeepEmptyLinesAtTheStartOfBlocks: false 63 | MacroBlockBegin: '' 64 | MacroBlockEnd: '' 65 | MaxEmptyLinesToKeep: 1 66 | NamespaceIndentation: None 67 | ObjCBlockIndentWidth: 2 68 | ObjCSpaceAfterProperty: false 69 | ObjCSpaceBeforeProtocolList: false 70 | PenaltyBreakBeforeFirstCallParameter: 1 71 | PenaltyBreakComment: 300 72 | PenaltyBreakFirstLessLess: 120 73 | PenaltyBreakString: 1000 74 | PenaltyExcessCharacter: 1000000 75 | PenaltyReturnTypeOnItsOwnLine: 200 76 | PointerAlignment: Left 77 | ReflowComments: true 78 | SortIncludes: true 79 | SortUsingDeclarations: true 80 | SpaceAfterCStyleCast: false 81 | SpaceAfterTemplateKeyword: true 82 | SpaceBeforeAssignmentOperators: true 83 | SpaceBeforeParens: ControlStatements 84 | SpaceInEmptyParentheses: false 85 | SpacesBeforeTrailingComments: 2 86 | SpacesInAngles: false 87 | SpacesInContainerLiterals: true 88 | SpacesInCStyleCastParentheses: false 89 | SpacesInParentheses: false 90 | SpacesInSquareBrackets: false 91 | TabWidth: 8 92 | UseTab: Never 93 | ... 94 | 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.vtk 2 | *.a 3 | lgr 4 | CMakeCache.txt 5 | CMakeFiles/ 6 | Makefile 7 | cmake_install.cmake 8 | .project 9 | .cproject 10 | .settings 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.0...${CMAKE_VERSION}) 2 | 3 | project(LGR VERSION 3.0.0 LANGUAGES CXX) 4 | 5 | option(LGR_ENABLE_CUDA "Build GPU support" OFF) 6 | option(LGR_ENABLE_UNIT_TESTS "Enable unit tests" ON) 7 | option(LGR_ENABLE_EFENCE "Build with ElectricFence support" OFF) 8 | 9 | set(LGR_USE_NVCC_WRAPPER OFF) 10 | set(LGR_EXTRA_NVCC_WRAPPER_FLAGS "") 11 | 12 | if (LGR_ENABLE_CUDA) 13 | enable_language(CUDA) 14 | set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --compiler-options -Wall,-Wextra,-Werror,-Wno-noexcept-type --Werror cross-execution-space-call,deprecated-declarations --expt-extended-lambda") 15 | if (CMAKE_CXX_COMPILER MATCHES ".*nvcc_wrapper") 16 | message(STATUS "Detected nvcc_wrapper as C++/CUDA compiler") 17 | set(LGR_EXTRA_NVCC_WRAPPER_FLAGS "-Wall -Wextra -Werror -Wno-noexcept-type -Wno-deprecated-declarations") 18 | set(LGR_USE_NVCC_WRAPPER ON) 19 | if (NOT LGR_ENABLE_SEARCH) 20 | find_package(Kokkos REQUIRED) 21 | set (CMAKE_CXX_FLAGS "${Kokkos_CXX_FLAGS}") 22 | message(STATUS "Using Kokkos_CXX_FLAGS: ${Kokkos_CXX_FLAGS}") 23 | endif() 24 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 25 | message(STATUS "Detected clang++ as C++/CUDA compiler") 26 | set(LGR_EXTRA_NVCC_WRAPPER_FLAGS "-Wall -Wextra -Werr -Wno-noexcept-type -Wno-deprecated-declarations") 27 | set(LGR_USE_NVCC_WRAPPER ON) 28 | endif() 29 | else() 30 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-float-equal") 32 | endif() 33 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") 34 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") 35 | endif() 36 | endif() 37 | 38 | set(LGR_SOURCES 39 | lgr_adapt.cpp 40 | lgr_bar.cpp 41 | lgr_composite_gradient.cpp 42 | lgr_composite_h_min.cpp 43 | lgr_composite_nodal_mass.cpp 44 | lgr_composite_tetrahedron.cpp 45 | lgr_domain.cpp 46 | lgr_element_specific.cpp 47 | lgr_exodus.cpp 48 | lgr_meshing.cpp 49 | lgr_physics.cpp 50 | lgr_stabilized.cpp 51 | lgr_state.cpp 52 | lgr_tetrahedron.cpp 53 | lgr_triangle.cpp 54 | lgr_vtk.cpp 55 | ) 56 | 57 | set(OTM_SOURCES 58 | otm_adapt.cpp 59 | otm_apps.cpp 60 | otm_distance.cpp 61 | otm_distance_util.cpp 62 | otm_meshing.cpp 63 | otm_meshless.cpp 64 | otm_search.cpp 65 | otm_tet2meshless.cpp 66 | otm_vtk.cpp 67 | ) 68 | 69 | option(LGR_ENABLE_EXODUS "Build the Exodus reader" OFF) 70 | 71 | if (LGR_ENABLE_EXODUS) 72 | option(EXODUS_HAS_MPI "Whether Exodus was built with MPI support" OFF) 73 | find_package(SEACASExodus REQUIRED) 74 | if (EXODUS_HAS_MPI) 75 | find_package(MPI REQUIRED) 76 | endif() 77 | endif() 78 | 79 | option(LGR_ENABLE_SEARCH "Build support for meshfree search via ArborX" OFF) 80 | 81 | if (LGR_ENABLE_SEARCH) 82 | find_package(ArborX REQUIRED) 83 | set(OTM_SOURCES ${OTM_SOURCES} otm_arborx_search_impl.cpp) 84 | endif() 85 | 86 | if (LGR_ENABLE_CUDA AND NOT LGR_USE_NVCC_WRAPPER) 87 | set_source_files_properties(${LGR_SOURCES} PROPERTIES LANGUAGE CUDA) 88 | set_source_files_properties(${OTM_SOURCES} PROPERTIES LANGUAGE CUDA) 89 | endif() 90 | 91 | if (LGR_ENABLE_CUDA AND LGR_USE_NVCC_WRAPPER) 92 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LGR_EXTRA_NVCC_WRAPPER_FLAGS}") 93 | endif() 94 | 95 | add_library(lgrlib ${LGR_SOURCES} ${OTM_SOURCES}) 96 | set_property(TARGET lgrlib PROPERTY CXX_STANDARD "14") 97 | set_property(TARGET lgrlib PROPERTY CXX_STANDARD_REQUIRED ON) 98 | set_property(TARGET lgrlib PROPERTY CXX_EXTENSIONS OFF) 99 | set_property(TARGET lgrlib PROPERTY OUTPUT_NAME lgr) 100 | target_include_directories(lgrlib PUBLIC $) 101 | 102 | if (LGR_ENABLE_EXODUS) 103 | target_compile_definitions(lgrlib PUBLIC -DLGR_ENABLE_EXODUS) 104 | target_link_libraries(lgrlib PUBLIC exodus) 105 | target_include_directories(lgrlib PUBLIC "${SEACASExodus_INCLUDE_DIRS}") 106 | target_include_directories(lgrlib PUBLIC "${SEACASExodus_TPL_INCLUDE_DIRS}") 107 | target_link_libraries(lgrlib PUBLIC "${SEACASExodus_TPL_LIBRARIES}") 108 | if (EXODUS_HAS_MPI) 109 | target_link_libraries(lgrlib PRIVATE MPI::MPI_CXX) 110 | endif() 111 | endif() 112 | 113 | if (LGR_ENABLE_SEARCH) 114 | message(STATUS "Inherited C++/CUDA compiler options from ArborX: ${Kokkos_CXX_FLAGS}") 115 | # target_include_directories(lgrlib PUBLIC "${Kokkos_INCLUDE_DIRS}") 116 | # target_include_directories(lgrlib PUBLIC "${Kokkos_TPL_INCLUDE_DIRS}") 117 | target_link_libraries(lgrlib PUBLIC ArborX::ArborX) 118 | target_compile_definitions(lgrlib PUBLIC -DLGR_ENABLE_SEARCH) 119 | endif() 120 | 121 | if (LGR_ENABLE_CUDA AND NOT LGR_USE_NVCC_WRAPPER) 122 | set_source_files_properties(lgr.cpp PROPERTIES LANGUAGE CUDA) 123 | set_source_files_properties(otm.cpp PROPERTIES LANGUAGE CUDA) 124 | endif() 125 | 126 | set(LGR_LIBRARIES lgrlib) 127 | if (LGR_ENABLE_EFENCE) 128 | set(LGR_LIBRARIES ${LGR_LIBRARIES} efence) 129 | endif() 130 | 131 | 132 | add_executable(lgr lgr.cpp) 133 | set_property(TARGET lgr PROPERTY CXX_STANDARD "14") 134 | set_property(TARGET lgr PROPERTY CXX_STANDARD_REQUIRED ON) 135 | set_property(TARGET lgr PROPERTY CXX_EXTENSIONS OFF) 136 | target_link_libraries(lgr ${LGR_LIBRARIES}) 137 | 138 | if (LGR_ENABLE_SEARCH) 139 | add_executable(otm otm.cpp) 140 | set_property(TARGET otm PROPERTY CXX_STANDARD "14") 141 | set_property(TARGET otm PROPERTY CXX_STANDARD_REQUIRED ON) 142 | set_property(TARGET otm PROPERTY CXX_EXTENSIONS OFF) 143 | target_link_libraries(otm ${LGR_LIBRARIES}) 144 | endif() 145 | 146 | if (LGR_ENABLE_UNIT_TESTS) 147 | add_subdirectory(unit_tests) 148 | endif() 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lgrtk 2 | Tool Kit for Lagrangian Grid Reconnection 3 | 4 | This code explores algorithms for stabilized shock hydrodynamics on tetrahedral meshes. 5 | 6 | At Sandia, LGRTK is SCR# 2300.0 7 | -------------------------------------------------------------------------------- /doc/maxent/README.md: -------------------------------------------------------------------------------- 1 | # Maxent 2 | 3 | Experimental code for Optimal Transportation Method (OTM) Meshless Framework 4 | code with Local Max. Entropy (LME) approximation functions. This code focuses on 5 | the interaction between the nonlinear optimization scheme used to enforce 6 | first-order consistency and the methods for insertion and regulation of node locations. 7 | 8 | ## Getting Started 9 | 10 | ``` 11 | main.jl 12 | ``` 13 | Contains example script used to make visuals for August 2020 presentations. 14 | Start by trying to run this script as-is, all the key modules are called by this script. 15 | 16 | ``` 17 | matlab_comparison.jl 18 | ``` 19 | Visualizations and test of mesh used in Matlab prototype script to confirm functionality. 20 | 21 | The most computationally expensive part of this script, evaluating the shape functions at all 22 | requested points, is trivially parallel at the material point level. The `Threads.@threads` macro has been placed in front of the most expensive loops. 23 | 24 | 25 | The number of active threads can be verified using: 26 | 27 | ``` 28 | julia> Threads.nthreads() 29 | ``` 30 | 31 | To change the number of available threads, you can use e.g.: 32 | 33 | ``` 34 | export JULIA_NUM_THREADS=8 35 | ``` 36 | 37 | ### Prerequisites 38 | 39 | Required Julia packages: 40 | 41 | ``` 42 | PyPlot / Plots 43 | PyCall 44 | LinearAlgebra 45 | Threads 46 | ``` 47 | 48 | Start the Julia REPL by typing "Julia" at the command line or using the REPL panel in Atom. 49 | 50 | If the code crashes while exporting animations or figures, ensure that `Plots` has been added and built correctly. 51 | Add and build Plots using Pkg 52 | 53 | ``` 54 | >> julia 55 | 56 | julia> using Pkg; Pkg.add("Plots"); Pkg.build("Plots") 57 | 58 | ``` 59 | -------------------------------------------------------------------------------- /doc/maxent/adapt.jl: -------------------------------------------------------------------------------- 1 | # Node injection methods. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | module adapt 7 | using LinearAlgebra 8 | function adapt1!(x; tol = 1e-1) 9 | for i ∈ 1:size(x, 1) 10 | dmin = Inf 11 | d = 0 12 | for j ∈ i + 1:size(x, 1) 13 | if norm(x[i, :] - x[j, :]) < dmin 14 | dmin = norm(x[i, :] - x[j, :]) 15 | d = j 16 | end 17 | end 18 | if dmin < tol 19 | x = [x; (x[i, :] .+ x[j, :])./2] 20 | end 21 | end 22 | end 23 | 24 | function adapt2!(x; tol = 1e-1, kmax = 1e6) 25 | error("unimplemented") 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /doc/maxent/main.jl: -------------------------------------------------------------------------------- 1 | # Example script using key functions for visuals used in August 2020 presentation. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | # Default parameters are low resolution to limit execution time. See comparison.jl and testplots.jl 7 | 8 | Pkg.add("Plots") 9 | Pkg.build("Plots") 10 | 11 | include("testplots.jl") 12 | include("comparison.jl") 13 | 14 | # function visuals 15 | oneDSweep() 16 | twoDSweep() 17 | threeDSweep() 18 | 19 | # linear solver success rates 20 | comparison3r(points = 1e3, reps = 5) 21 | -------------------------------------------------------------------------------- /doc/maxent/matlab_comparison.jl: -------------------------------------------------------------------------------- 1 | include("pmesh.jl") 2 | include("patterns/circle.jl") 3 | include("lme.jl") 4 | include("utils/utils.jl") 5 | include("solvers.jl") 6 | 7 | using PyPlot, PyCall, Printf, LinearAlgebra 8 | 9 | function matlab_comparison(; γ = 8.0, a = 5) 10 | p = hm.p 11 | t = hm.t 12 | 13 | _, c, _ = Mesh.tristats(p, t) 14 | xp, _ = Mesh.triquad(p, t, c) 15 | xp = vcat(xp, p) 16 | tp = Mesh.delaunay(xp) 17 | 18 | xp, tp = Mesh.trefine(xp, tp, reps = 2) 19 | 20 | h = 1/2.2 21 | β = γ/(h^2) 22 | N, DN = LME.shape_functions(p, xp, β) 23 | mask = LME.validate2(N, DN, p, xp, ϵ = 1e-10)[1] 24 | 25 | N[:, .!mask] .= NaN 26 | DN[: , :, .!mask] .= NaN 27 | 28 | figure() 29 | suptitle(@sprintf("LME Shape Functions, γ = %.2f", γ)) 30 | PyPlot.subplot(2, 2, 1) 31 | Mesh.tplot(p, t) 32 | PyPlot.plot(p[:, 1], p[:, 2], "ko") 33 | PyPlot.plot(xp[:, 1], xp[:, 2], "k.") 34 | title("Original Mesh") 35 | 36 | subplot(2, 2, 2) 37 | Mesh.tplot(xp, tp, u = N[a, :], limits = (0, 1)) 38 | colorbar(ticks = range(0, step = .1, stop = 1)) 39 | # plot(xp[:, 1], xp[:, 2], "k.", label = "material points", ms = 1) 40 | PyPlot.plot(p[:, 1], p[:, 2], "mo", label = "nodes") 41 | title("N") 42 | legend() 43 | 44 | subplot(2, 2, 3) 45 | u = DN[a, 1, :] 46 | Mesh.tplot(xp, tp, u = u, shownan = false) 47 | colorbar(ticks = range(floor(nanmin(u)), stop = ceil(nanmax(u)), step = 1)) 48 | # plot(xp[:, 1], xp[:, 2], "k.", label = "material points", ms = 1) 49 | PyPlot.plot(p[:, 1], p[:, 2], "mo", label = "nodes") 50 | title("δN/δx") 51 | legend() 52 | 53 | subplot(2, 2, 4) 54 | u = DN[a, 2, :] 55 | Mesh.tplot(xp, tp, u = u, shownan = false) 56 | colorbar(ticks = range(floor(nanmin(u)), stop = ceil(nanmax(u)), step = 1)) 57 | PyPlot.plot(xp[:, 1], xp[:, 2], "k.", label = "material points", ms = 1) 58 | PyPlot.plot(p[:, 1], p[:, 2], "mo", label = "nodes") 59 | title("δN/δy") 60 | legend() 61 | 62 | show() 63 | end 64 | -------------------------------------------------------------------------------- /doc/maxent/move_node.jl: -------------------------------------------------------------------------------- 1 | # Figures to visualize changing node location in 2D. 2 | 3 | include("pmesh.jl") 4 | include("lme.jl") 5 | include("utils/utils.jl") 6 | 7 | using PyPlot, PyCall, Printf, LinearAlgebra 8 | 9 | function movenode(s, a) 10 | p = hm.p 11 | t = hm.t 12 | d = [cos(s); sin(s)] 13 | p[a, :] += d 14 | 15 | _, c, _ = Mesh.tristats(p, t) 16 | xp, _ = Mesh.triquad(p, t, c) 17 | xp = vcat(xp, p) 18 | tp = Mesh.delaunay(xp) 19 | 20 | xp, tp = Mesh.trefine(xp, tp, reps = 5) 21 | 22 | γ = 1.0 23 | h = 1/2.2 24 | β = γ/(h^2) 25 | N, DN = lme.shape_functions(p, xp, β) 26 | 27 | a = 5 28 | tp = Mesh.delaunay(xp) 29 | 30 | figure() 31 | Mesh.tplot(p, t) 32 | plot(p[:, 1], p[:, 2], "ko") 33 | plot(xp[:, 1], xp[:, 2], "k.") 34 | title("Original Mesh") 35 | 36 | figure() 37 | Mesh.tplot(xp, tp, u = N[a, :]) 38 | colorbar(ticks = range(0, step = .1, stop = 1)) 39 | # plot(xp[:, 1], xp[:, 2], "k.", label = "material points", ms = 1) 40 | plot(p[:, 1], p[:, 2], "mo", label = "nodes") 41 | title(@sprintf("LME Shape Functions, γ = %.2f", γ)) 42 | legend() 43 | 44 | 45 | figure() 46 | subplot(1, 2, 1) 47 | Mesh.tplot(xp, tp, u = DN[a, 1, :], shownan = false) 48 | colorbar() 49 | # plot(xp[:, 1], xp[:, 2], "k.", label = "material points", ms = 1) 50 | plot(p[:, 1], p[:, 2], "mo", label = "nodes") 51 | title(@sprintf("δN/δx, γ = %.2f", γ)) 52 | legend() 53 | 54 | subplot(1, 2, 2) 55 | Mesh.tplot(xp, tp, u = DN[a, 2, :], shownan = false) 56 | colorbar() 57 | # plot(xp[:, 1], xp[:, 2], "k.", label = "material points", ms = 1) 58 | plot(p[:, 1], p[:, 2], "mo", label = "nodes") 59 | title(@sprintf("δN/δy, γ = %.2f", γ)) 60 | legend() 61 | 62 | tests = lme.validate(N, DN, p, xp) 63 | println(tests) 64 | end 65 | 66 | for t in range(0, stop = 2*pi, length = 10) 67 | movenode(t, 5) 68 | end 69 | -------------------------------------------------------------------------------- /doc/maxent/patterns/circle.jl: -------------------------------------------------------------------------------- 1 | module hm 2 | 3 | p = [-9.859697476671114e-01 -1.669241057205156e-01; 4 | -9.215446143068161e-01 3.882724855541718e-01; 5 | -7.427230139738629e-01 -6.695987765417645e-01; 6 | -5.681757119451035e-01 8.229072624260151e-01; 7 | -3.814593828867704e-01 4.489997184400797e-02; 8 | -3.480999351875999e-01 -4.143112362365443e-01; 9 | -2.440738566912074e-01 4.846402955972834e-01; 10 | -2.293132993643275e-01 -9.733526649351270e-01; 11 | 4.876770231131387e-03 9.999881079861374e-01; 12 | 1.480284416104837e-01 -3.982349080347914e-01; 13 | 2.360196096563175e-01 3.539012291570796e-01; 14 | 3.145053416815400e-01 -9.492557037485549e-01; 15 | 4.966869641503759e-01 -5.800047883737772e-02; 16 | 5.312183483267620e-01 8.472349548967432e-01; 17 | 7.681321917494182e-01 -6.402912887733220e-01; 18 | 8.992384743913577e-01 4.374587583084430e-01; 19 | 9.925203543498434e-01 -1.220792515233584e-01] 20 | 21 | t = [1 5 2; 22 | 10 11 5; 23 | 7 2 5; 24 | 7 11 9; 25 | 5 11 7; 26 | 4 7 9; 27 | 2 7 4; 28 | 6 10 5; 29 | 6 5 1; 30 | 1 3 6; 31 | 11 10 13; 32 | 17 16 13; 33 | 13 16 11; 34 | 13 15 17; 35 | 10 15 13; 36 | 12 15 10; 37 | 9 11 14; 38 | 11 16 14; 39 | 8 6 3; 40 | 10 6 8; 41 | 8 12 10] 42 | end 43 | -------------------------------------------------------------------------------- /doc/maxent/patterns/patterns.jl: -------------------------------------------------------------------------------- 1 | # Example node patterns for LME test cases. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | struct Pattern 7 | nodes::Array{Float64} 8 | points::Array{Float64} 9 | h::Float64 10 | end 11 | 12 | xp2 = [-15.0 15; 0 7; 15 15; 7 0; 15 -15; 0 -7; -15 -15; -7 0] 13 | 14 | limits = (-1, 1) 15 | xp3 = zeros(6, 3) 16 | 17 | for i in 1:3 18 | global xp3[2*i - 1, i] = 1 19 | global xp3[2i, i] = -1 20 | end 21 | 22 | for x in limits, y in limits, z in limits 23 | global xp3 = vcat(xp3, [x y z]) 24 | end 25 | 26 | xp3[1:6, :] *= 7 27 | xp3[7:end, :] *= 10 28 | 29 | star2d = Pattern(xp2, brick(minimum(xp2, dims = 1), maximum(xp2, dims = 1), (10, 10)), 20) 30 | star3d = Pattern(xp3, brick(minimum(xp3, dims = 1), maximum(xp3, dims = 1), (10, 10, 10)), 20) 31 | -------------------------------------------------------------------------------- /doc/maxent/patterns/threeDStar.jl: -------------------------------------------------------------------------------- 1 | limits = (-1, 1) 2 | 3 | 4 | xp = zeros(6, 3) 5 | 6 | for i in 1:3 7 | global xp[2*i - 1, i] = 1 8 | global xp[2i, i] = -1 9 | 10 | end 11 | 12 | for x in limits, y in limits, z in limits 13 | global xp = vcat(xp, [x y z]) 14 | end 15 | 16 | xp[1:6, :] *= 10 17 | xp[7:end, :] *= 14 18 | 19 | # close("all") 20 | # 21 | # plot3D(xp[:, 1], xp[:, 2], xp[:, 3], "ro") 22 | -------------------------------------------------------------------------------- /doc/maxent/solvers.jl: -------------------------------------------------------------------------------- 1 | # Linear system solvers for LME lagrange multiplier step. 2 | 3 | using LinearAlgebra 4 | 5 | """ 6 | x = babuska(A, b; xo = nothing, ϵ = 1e-6, maxiters = 20, tol = 1e5eps()) 7 | Iteratively solve poorly scaled Ax = b using babuska iterative method. 8 | """ 9 | function babuska(A, b; xo = nothing, ϵ = 1e-2, maxiters = 10, tol = 1e4eps()) 10 | try 11 | x = zeros(length(b), maxiters) 12 | xt = zeros(length(b), maxiters) 13 | r = zeros(length(b), maxiters + 1) 14 | 15 | A_ϵ = A + ϵ*eigen(A).values[1]*eye(length(b)) 16 | 17 | if xo == nothing 18 | xo = zeros(size(b)) 19 | end 20 | 21 | r[:, 1] = b - A*xo 22 | 23 | for i = 1:maxiters 24 | x[:, i] = pinv(A_ϵ)*(b + sum(r, dims = 2)) 25 | r[:, i + 1] = b - A*x[:, i] 26 | 27 | rh = r[:, i + 1] / norm(r[:, i + 1]) 28 | xt[:, i] = x[:, i] - rh * dot(x[:, i], rh) 29 | 30 | if i > 1 && norm(xt[:, i] - xt[:, i - 1]) / norm(xt[:, i - 1]) < tol 31 | break 32 | end 33 | end 34 | return x[:, end] 35 | catch 36 | return 0*b 37 | end 38 | end 39 | 40 | """ 41 | x = pinvs(A, b) 42 | Use Moore-Penrose pseudoinverse to solve Ax = b 43 | """ 44 | function pinvs(A, b) 45 | return pinv(A) * b 46 | end 47 | 48 | """ 49 | x = direct(A, b) 50 | Use backslash to solve Ax = b. Return 0 if system is singular 51 | """ 52 | function direct(A, b) 53 | try 54 | return A \ b 55 | catch SingularException 56 | return 0*b 57 | end 58 | end 59 | 60 | """ 61 | dx = linesearch(f, df, x, dx; maxit = 20, τ = .618, c = .5) 62 | Backtracking line search to minimize function f(x). 63 | """ 64 | function linesearch(f, df, x, dx; maxit = 10, τ = .618, c = .5) 65 | fo = f(x) 66 | m = dot(df, dx) # projected gradient onto search direction 67 | t = -c*m 68 | j = 0 # line search iterations 69 | α = 1.0 70 | converged = false 71 | for j ∈ 1:maxit 72 | Δf = fo - f(x + α * dx) 73 | Δx = α 74 | if Δf/Δx >= t 75 | converged = true 76 | break 77 | end 78 | α *= τ 79 | end 80 | converged ? (return α*dx) : (return dx) 81 | end 82 | 83 | """ 84 | x = gd(A, b) 85 | perform gradient descent by returning vector b and ignoring Hessian A. 86 | """ 87 | function gd(A, b; α = .1) 88 | return α*b 89 | end 90 | 91 | """ 92 | x = guess(A, b; α = 0.1) 93 | Return random vector in n-space 94 | """ 95 | function guess(A, b; α = 10.0) 96 | return α*(rand(length(b)) .- .5) 97 | end 98 | 99 | """ 100 | tests = test_solver(solver) 101 | Performs sanity tests for linear system solver. 102 | """ 103 | function test_solver(solver) 104 | tests = [] 105 | A = eye(3) 106 | b = ones(3) 107 | x = solver(A, b) 108 | r = A*x - b 109 | push!(tests, norm(r) < 1e2eps()) 110 | 111 | A = [1 2 3; 1 3 2; 1 1 1] 112 | b = [0; 0; 1] 113 | x = solver(A, b) 114 | r = A*x - b 115 | push!(tests, norm(r) < 1e2eps()) 116 | 117 | A = [1 1 1; 1 1 0; 1 1 1 + 1e1eps()] 118 | b = [1; 1; 1] 119 | x = solver(A, b) 120 | r = A*x - b 121 | push!(tests, norm(r) < 1e2eps()) 122 | 123 | return tests 124 | end 125 | -------------------------------------------------------------------------------- /doc/maxent/testplots.jl: -------------------------------------------------------------------------------- 1 | # Example graphical outputs and tests for LME shape functions. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | using Plots, PyPlot, PyCall, Printf, LinearAlgebra 7 | 8 | include("pmesh.jl") 9 | include("lme.jl") 10 | include("utils/utils.jl") 11 | include("utils/colorutils.jl") 12 | include("solvers.jl") 13 | 14 | @userplot Cloud # point cloud structure for failure visuals 15 | @recipe function f(c::Cloud) 16 | x, i = c.args 17 | n = length(x) 18 | ticks --> false 19 | grid --> false 20 | dpi --> 80 21 | aspect_ratio --> 1 22 | label --> false 23 | x[:, 1], x[:, 2], x[:, 3] 24 | end 25 | 26 | """ 27 | oneDSweep() 28 | Show LME shape functions with example node placement in 1D for 29 | a range of γ values. Figure used in August 2020 presentation. 30 | Shows values and derivatives. 31 | """ 32 | function oneDSweep() 33 | p = range(0, stop = 1, length = 4) 34 | xp = range(0, stop = 1, length = Int(2e5)) 35 | h = norm(p[1, :] - p[2, :]) 36 | γ = 16*.5 .^(5:-1:0) 37 | β = γ./(h^2) 38 | 39 | cs = colorinterp((.2, .2, 1), (1, .4, .2), length(β)) # colormap 40 | 41 | PyPlot.figure() 42 | suptitle("LME Functions with Varied Locality γ") 43 | for i ∈ 1:length(β) 44 | N, DN, its = LME.shape_functions(p, xp, β[i]) 45 | mask = LME.validate2(N, DN, p, xp, ϵ = 1e-6)[1] 46 | 47 | N[:, .!mask] .= NaN 48 | DN[: , :, .!mask] .= NaN 49 | subplot(length(β), 2, 2*i - 1) 50 | 51 | if i == 1 52 | title("N") 53 | end 54 | a = 2 55 | PyPlot.plot(p, 0*p, "k.") 56 | PyPlot.plot(xp, N[a, :]', color = cs[i]) 57 | PyPlot.plot(xp, N[:, :]', color = cs[i], alpha = .25) 58 | ylabel("γ = "*string(γ[i]), rotation = "vertical") 59 | subplot(length(β), 2, 2*i) 60 | if i == 1 61 | title("dN/dx") 62 | end 63 | PyPlot.plot(p, 0*p, "k.") 64 | PyPlot.plot(xp, DN[a, 1, :]', color = cs[i]) 65 | PyPlot.plot(xp, DN[:, 1, :]', color = cs[i], alpha = .25) 66 | end 67 | end 68 | 69 | """ 70 | twoDSweep() 71 | Show LME shape functions with example node placement in 2D for 72 | a range of γ values. 73 | Shows values, both gradient components, and iterations used. 74 | """ 75 | function twoDSweep() 76 | p = [-20.0 0; -5 5; 0 20; 5 5; 20 0; 5 -5; 0 -20; -5 -5] # Wriggers star pattern 77 | pv = [-20.0 0; 0 20; 20 0; 0 -20; -20 0] 78 | 79 | ns = 3 # number of scales 80 | nγ = 3 81 | γ = range(0, stop = 32.0, length = nγ) 82 | sample = zeros(ns) 83 | 84 | h = 20 # Wriggers spacing for star pattern. 85 | a = 2 # shape function to plot 86 | 87 | xp, xt, _, _, _ = Mesh.mesh(pv, 1, 1) 88 | 89 | for i ∈ 1:nγ 90 | 91 | β = γ[i]/(h^2) 92 | N, DN, its = LME.shape_functions(p, xp, β) 93 | mask = LME.validate2(N, DN, p, xp, ϵ = 1e-10)[1] 94 | N[:, .!mask] .= NaN 95 | DN[: , :, .!mask] .= NaN 96 | 97 | PyPlot.figure() 98 | PyPlot.suptitle(@sprintf("LME Shape Functions, γ = %.2f", γ[i])) 99 | 100 | PyPlot.subplot(2, 2, 1) 101 | Mesh.tplot(xp, xt, u = N[a, :]) 102 | PyPlot.colorbar(ticks = range(0, step = .1, stop = 1)) 103 | PyPlot.plot([p[:, 1]; p[1, 1]], [p[:, 2]; p[1, 2]], "mo--", ms = 10, label = "nodes") 104 | PyPlot.title("N") 105 | PyPlot.legend() 106 | 107 | PyPlot.subplot(2, 2, 2) 108 | Mesh.tplot(xp, xt, u = its, shownan = false) 109 | PyPlot.colorbar(ticks = range(minimum(its), stop = maximum(its), step = 1)) 110 | PyPlot.title("Iterations") 111 | 112 | PyPlot.subplot(2, 2, 3) 113 | Mesh.tplot(xp, xt, u = DN[a, 1, :], shownan = false) 114 | PyPlot.colorbar() 115 | PyPlot.plot([p[:, 1]; p[1, 1]], [p[:, 2]; p[1, 2]], "mo--", ms = 10, label = "nodes") 116 | PyPlot.title("δN/δx") 117 | 118 | PyPlot.subplot(2, 2, 4) 119 | Mesh.tplot(xp, xt, u = DN[a, 2, :], shownan = false) 120 | PyPlot.colorbar() 121 | PyPlot.plot([p[:, 1]; p[1, 1]], [p[:, 2]; p[1, 2]], "mo--", ms = 10, label = "nodes") 122 | PyPlot.title("δN/δy") 123 | end 124 | end 125 | """ 126 | threeDSweep() 127 | Produce animations of point clouds of failure locations. Used for presentation Aug. 2020 128 | """ 129 | function threeDSweep(; n = 90, points = Int(1e3)) 130 | for γ ∈ range(12, stop = 18, length = 3) 131 | x, p = failurestar3(n = points, γ = γ) 132 | anim = @animate for i ∈ range(0, stop = 90 - 90/n, length = n) 133 | cloud(p, i, camera = (i, 22.5), markershape = :circle, markersize = 5, 134 | line = nothing, markercolor = :cyan, markerstrokecolor = :black, 135 | legend = false, cbar = false, framestyle = :none) 136 | cloud!(tetreflect(x), i, camera = (i, 22.5), markeralpha = .5, 137 | line = nothing, markershape = :circle, markersize = 1, 138 | markercolor = :red, markerstrokecolor = :red, 139 | legend = false, cbar = false, framestyle = :none) 140 | end 141 | gif(anim, @sprintf("%d_%d_rotate.gif", γ, points), fps = n÷10) 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /doc/maxent/tests.jl: -------------------------------------------------------------------------------- 1 | # Test LME implementation for numerical correctness. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | using PyPlot, PyCall, Printf, LinearAlgebra 7 | 8 | include("pmesh.jl") 9 | include("lme.jl") 10 | include("utils/utils.jl") 11 | 12 | tests = [] 13 | 14 | ## 1D tests ## 15 | 16 | nf = 5 17 | p = range(0, stop = 1, length = nf) 18 | xp = range(0, stop = 1, length = 1000) 19 | 20 | h = norm(p[1, :] - p[2, :]) 21 | γ = 1.0 22 | β = γ/(h^2) 23 | 24 | N, DN = LME.shape_functions(p, xp, β) 25 | push!(tests, LME.validate(N, DN, p, xp)) 26 | 27 | h = norm(p[1, :] - p[2, :]) 28 | γ = 8*.5 .^[0:4; 3:-1:0] 29 | β = γ./(h^2) 30 | p = range(0, stop = 1, length = length(β)) 31 | 32 | for i in 1:length(β) 33 | N, DN = LME.shape_functions(p, xp, β[i]) 34 | push!(tests, LME.validate(N, DN, p, xp)) 35 | end 36 | 37 | ## 2D tests ## 38 | 39 | p = [-20.0 0; -5 5; 0 20; 5 5; 20 0; 5 -5; 0 -20; -5 -5] # Wriggers star pattern 40 | # pv = [-20.0 20; 20 20; 20 -20; -20 -20; -20 20] # outer hull 41 | pv = copy(p) 42 | # pv = [-30.0 30; 30 30; 30 -30; -30 30] # outer hull, use symmetry and move beyond support domain 43 | 44 | xp, xt, _, _, _ = Mesh.mesh(pv, 2.0, 3) 45 | 46 | γ = range(1, step = 1, stop = 10) 47 | h = 20 # Wriggers spacing 48 | a = 2 # shape function to plot 49 | 50 | for i = 1:length(γ) 51 | β = γ[i]/(h^2) 52 | N, DN = LME.shape_functions(p, xp, β) 53 | push!(tests, LME.validate(N, DN, p, xp)) 54 | tests[end] || println(@sprintf("Failed for γ = %.2f", γ[i])) 55 | end 56 | 57 | ## 3D 58 | 59 | γ = range(0, step = 1, stop = 6) 60 | β = γ./(h^2) 61 | p = [0 0 0 ; 0 0 1.0; 0 1 0; 0 1 1; 1 0 0; 1 0 1; 1 1 0; 1 1 1] 62 | # p = [0 0 0 ; 0 0 1; 0 1.0 0; 1 1 1] 63 | h = norm(p[1, :] - p[2, :]) 64 | a = range(0, stop = 1, length = 10) 65 | 66 | xp = zeros(length(a)^3, 3) 67 | i = 0 68 | for xx = a, yy = a, zz = a 69 | global i += 1 70 | xp[i, :] = [xx, yy, zz] 71 | end 72 | 73 | for b in β 74 | N, DN = LME.shape_functions(p, xp, b) 75 | push!(tests, LME.validate(N, DN, p, xp)) 76 | end 77 | 78 | ## 4D (for generality) 79 | 80 | a = 0:1 81 | h = 1 82 | γ = range(0, step = 1, stop = 6) 83 | β = γ./(h^2) 84 | p = zeros(length(a)^4, 4) 85 | i = 0 86 | for xx = a, yy = a, zz = a, qq = a 87 | global i += 1 88 | p[i, :] = [xx, yy, zz, qq] 89 | end 90 | 91 | a = range(0, stop = 1, length = 10) 92 | xp = zeros(length(a)^4, 4) 93 | 94 | i = 0 95 | for xx = a, yy = a, zz = a, qq = a 96 | global i += 1 97 | xp[i, :] = [xx, yy, zz, qq] 98 | end 99 | 100 | for b in β 101 | N, DN = LME.shape_functions(p, xp, b) 102 | push!(tests, LME.validate(N, DN, p, xp)) 103 | end 104 | 105 | ## Postprocessing 106 | println() 107 | println(@sprintf("Passed %d of %d tests.", count(tests), length(tests))) 108 | -------------------------------------------------------------------------------- /doc/maxent/utils/colors.jl: -------------------------------------------------------------------------------- 1 | """ 2 | c = randcolor(;a = 0.5) 3 | Make a random RGB color triplet with a given average value 4 | """ 5 | function randcolor(; c = nothing, a = 0.5, tol = 1e-3) 6 | 0.0 < a < 1.0 || error("a must be between 0 and 1 exclusive") 7 | if c == nothing 8 | c = rand(3) 9 | else 10 | c = min.(c, 1.0) 11 | c = max.(c, 0.0) 12 | end 13 | 14 | while abs(sum(c) - 3a) > tol 15 | if sum(c) > 3a 16 | c = c.^1.1 17 | else 18 | c = c.^.95 19 | end 20 | end 21 | @assert all( 0.0 .<= c .<= 1.0) "One or more value of c ∉ [0, 1]" 22 | return tuple(c...) 23 | end 24 | 25 | """ 26 | c = colorinterp(s, f, n) 27 | Make interpolated colors between arbitrary values. 28 | `s::RGB triplet`, first color 29 | `f::RGB triplet`, last color 30 | `n::Int8`, number of total values 31 | """ 32 | function colorinterp(s, f, n) 33 | δ = f .- s 34 | a = range(0.0, stop = 1.0, length = n) 35 | c = [] 36 | for i = 1:n 37 | color = s .+ a[i] .* δ 38 | push!(c, color) 39 | end 40 | return c 41 | end 42 | 43 | """ 44 | cs = colorarray() 45 | Produce array of visually distinct random colors suitable for plots with related series. 46 | """ 47 | function colorarray(nr, ns; ci = (.05, .03, .53), cf = (.94, .98, .13)) 48 | cs = fill(tuple(zeros(Float16, 3)...), (nr, ns)) 49 | ca = colorinterp(ci, cf, ns) 50 | for j ∈ 1:ns 51 | c1 = ca[j] 52 | c2 = c1 .* .5 53 | cs[:, j] = colorinterp(c1, c2, nr) 54 | end 55 | return cs 56 | end 57 | -------------------------------------------------------------------------------- /doc/maxent/utils/colorutils.jl: -------------------------------------------------------------------------------- 1 | """ 2 | c = randcolor(;a = 0.5) 3 | Make a random RGB color triplet with a given average value 4 | """ 5 | function randcolor(; c = nothing, a = 0.5, tol = 1e-3) 6 | 0.0 < a < 1.0 || error("a must be between 0 and 1 exclusive") 7 | if c == nothing 8 | c = rand(3) 9 | else 10 | c = min.(c, 1.0) 11 | c = max.(c, 0.0) 12 | end 13 | 14 | while abs(sum(c) - 3a) > tol 15 | if sum(c) > 3a 16 | c = c.^1.1 17 | else 18 | c = c.^.95 19 | end 20 | end 21 | @assert all( 0.0 .<= c .<= 1.0) "One or more value of c ∉ [0, 1]" 22 | return tuple(c...) 23 | end 24 | 25 | """ 26 | c = colorinterp(s, f, n) 27 | Make interpolated colors between arbitrary values. 28 | `s::RGB triplet`, first color 29 | `f::RGB triplet`, last color 30 | `n::Int8`, number of total values 31 | """ 32 | function colorinterp(s, f, n) 33 | δ = f .- s 34 | a = range(0.0, stop = 1.0, length = n) 35 | c = [] 36 | for i = 1:n 37 | color = s .+ a[i] .* δ 38 | push!(c, color) 39 | end 40 | return c 41 | end 42 | 43 | """ 44 | cs = colorarray() 45 | Produce array of visually distinct random colors suitable for plots with related series. 46 | """ 47 | function colorarray(nr, ns; ci = (1, .7, 0), cf = (.6, 0, 1)) 48 | cs = fill(tuple(zeros(Float16, 3)...), (nr, ns)) 49 | ca = colorinterp(ci, cf, ns) 50 | for j ∈ 1:ns 51 | c1 = ca[j] 52 | c2 = c1 .* .5 53 | cs[:, j] = colorinterp(c1, c2, nr) 54 | end 55 | return cs 56 | end 57 | -------------------------------------------------------------------------------- /doc/maxent/utils/geometry.jl: -------------------------------------------------------------------------------- 1 | # Node placement, sampling, and manipulation functions. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | using Plots, PyCall, Random, Combinatorics 7 | pyplot() 8 | 9 | """ 10 | A = brick(min, max, res) 11 | returns n-dimensional grid of points with lower bound min, upper bound max, and number of points res 12 | """ 13 | function brick(lower, upper, res) 14 | b = zeros(prod(res), length(res)) 15 | for (l, u, r, i) in zip(lower, upper, res, 1:length(res)) 16 | b[:, i] = repeat(range(l, stop = u, length = r), inner = prod(res[1:i-1]), outer = prod(res[i+1:end])) 17 | end 18 | return b 19 | end 20 | 21 | """ 22 | X = tetgrid(n) 23 | Return coordinates X = [x y z] such that 1 ≧ x, y, z ≥ 0, x + y + z ≦ 1 24 | x, y, z ∈ range(0, stop = 1, length = n), 25 | where n is the next-lowest tetrahedral number to p 26 | """ 27 | function tetgrid(p) 28 | X = zeros(tetnumber(n), 3) 29 | h = 1/(n - 1) 30 | i = 0 31 | for x ∈ range(0, stop = 1, step = h) 32 | for y ∈ range(0, stop = 1 - x, step = h) 33 | for z ∈ range(0, stop = 1 - x - y, step = h) 34 | i += 1 35 | X[i, :] = [x, y, z] 36 | end 37 | end 38 | end 39 | return X 40 | end 41 | 42 | """ 43 | X = tetrand(n) 44 | Return coordinates X = [x y z] such that 1 ≧ x ≧ y ≧ z ≥ 0, 45 | x, y, z ∈ range(0, stop = 1, length = n) 46 | Represents points in a tetrahendron comprising 47 | 1/48th of a cube with -1 ≦ x, y, z ≦ 1 with p total points 48 | Using method from: http://vcg.isti.cnr.it/jgt/tetra.htm 49 | """ 50 | function tetrand(p) 51 | p = Int(p) 52 | X = zeros(p, 3) 53 | Threads.@threads for d ∈ 1:p 54 | s, t, u = rand(3) 55 | if s + t + u > 1 56 | if s + t > 1 57 | s, t, u = 1 - s, 1 - t, u 58 | end 59 | if s + t + u > 1 60 | if t + u > 1 61 | s, t, u = s, 1 - u, 1 - s - t 62 | else 63 | s, t, u = 1 - t - u, t, s + t + u - 1 64 | end 65 | end 66 | end 67 | X[d, :] = [s, t, u] 68 | end 69 | return X 70 | end 71 | 72 | """ 73 | Tn = tetnumber(n) 74 | Return the n'th tetrahedral number 75 | """ 76 | function tetnumber(n) 77 | return Int((n * (n + 1) * (n + 2)) / 6) 78 | end 79 | 80 | """ 81 | X = tetreflect(x) 82 | undo symmetry reduction from threeDsym to make a full cube 83 | will duplicate points on the iterior surfaces of the subtet 84 | """ 85 | function tetreflect(x) 86 | n = size(x, 1) 87 | X = zeros(48*n, 3) # preallocate 88 | for (i, p) ∈ zip(1:6, permutations(1:3) |> collect) 89 | X[(i - 1) * n + 1: i*n, :] = x[:, p] 90 | end 91 | for (j, m) ∈ zip(2:8, corners(3, limits = [1, -1])[2:8]) 92 | X[6(j-1)*n + 1 : 6j*n , :] = m' .*X[1:6n, :] 93 | end 94 | return X 95 | end 96 | 97 | """ 98 | c = corners(d) 99 | returns the corners of a d-space cube with vertices 100 | at positions defined by limits. Assumed to occupy same interval in all dimensions. 101 | """ 102 | function corners(d; limits = [0 1]) 103 | limits = reshape(limits, (2,)) 104 | c = zeros(2^d, d) 105 | res = tuple(repeat([2], d)...) 106 | Threads.@threads for i ∈ 1:d 107 | inner = prod(res[1:i - 1]) 108 | outer = prod(res[i + 1:end]) 109 | c[:, i] = repeat(limits, inner = inner, outer = outer) 110 | end 111 | return [c[i, :] for i in 1:2^d] 112 | end 113 | 114 | """ 115 | Map 'reference tetrahedron' to have corners at origin and points defined by "corners" 116 | """ 117 | function tetmap(X; corners = [1 0 0; 1 1 0; 1 1 1], origin = [0 0 0]) 118 | return X * corners .+ origin 119 | end 120 | 121 | """ 122 | Plot discrete points in 3d space to show regions where LME solution scheme struggles 123 | """ 124 | function lmeplot(x; camera = (30, 30)) 125 | Plots.plot!(x[:, 1], x[:, 2], x[:, 3], markeralpha = .4, 126 | line = nothing, markershape = :x, markersize = 2, 127 | markercolor = :red, markestrokecolor = :red, camera = camera, 128 | legend = false) 129 | end 130 | 131 | """ 132 | Create random convex combinations of pairs of points from array x. 133 | Returns as many points as are in x. 134 | """ 135 | function convexcombination(x) 136 | ϕ = rand(size(x, 1)) 137 | return ( ϕ .* x + (1 .- ϕ) .* x[Random.randperm(size(x, 1)), :]) 138 | end 139 | 140 | """ 141 | Return, at most, nmax rows of array x 142 | """ 143 | function subsample(x, nmax = 1e5) 144 | nmax = Int(nmax) 145 | if nmax < size(x, 1) 146 | return x[randperm(size(x, 1))[1:nmax], :] 147 | else 148 | return x 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /doc/maxent/utils/utils.jl: -------------------------------------------------------------------------------- 1 | # Support functions for Matlab-like syntax and NaN-handling. 2 | 3 | # Written by Avery Rock, 4 | # Written for Julia 1.4.2, Summer 2020 5 | 6 | """ 7 | s = nansum(x) 8 | Sum entries of array x, ignoring NaN 9 | """ 10 | function nansum(x) 11 | sum = 0 12 | for i in x 13 | if !isnan(i) 14 | sum += i 15 | end 16 | end 17 | return sum 18 | end 19 | 20 | """ 21 | a = nanabs(x) 22 | Absolute value of x if x is not NaN. 23 | Return 0 if x is NaN. 24 | """ 25 | function nanabs(x) 26 | if isnan(x) 27 | return 0 28 | else 29 | return abs(x) 30 | end 31 | end 32 | 33 | """ 34 | a = nanmax(x) 35 | Return max of x, excluding nan. -Inf by default. 36 | """ 37 | function nanmax(x) 38 | m = -Inf 39 | for i in x 40 | if i >= m 41 | m = i 42 | end 43 | end 44 | return m 45 | end 46 | 47 | """ 48 | a = nanmin(x) 49 | Return max of x, excluding nan. Inf by default. 50 | """ 51 | function nanmin(x) 52 | m = Inf 53 | for i in x 54 | if i <= m 55 | m = i 56 | end 57 | end 58 | return m 59 | end 60 | 61 | """ 62 | A = eye(n) 63 | n by n identity matrix. Matlab-like syntax. 64 | """ 65 | function eye(n) 66 | Matrix{Float64}(I, n, n) 67 | end 68 | 69 | """ 70 | timestamp(msg = "") 71 | Print a message (default empty string) with the current time. 72 | """ 73 | function timestamp( msg = "" ) 74 | println() 75 | println(msg, Dates.format(Dates.now(), "HH:MM:SS")) 76 | end 77 | -------------------------------------------------------------------------------- /hpc_array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace hpc { 8 | 9 | template 10 | class array 11 | { 12 | protected: 13 | T m_data[N]; 14 | 15 | public: 16 | using value_type = T; 17 | using size_type = Index; 18 | using difference_type = decltype(std::declval() - std::declval()); 19 | using reference = value_type&; 20 | using const_reference = value_type const&; 21 | using pointer = T*; 22 | using const_pointer = T const*; 23 | using iterator = pointer_iterator; 24 | using const_iterator = pointer_iterator; 25 | array(std::initializer_list l) noexcept 26 | { 27 | auto const b = l.begin(); 28 | for (std::ptrdiff_t i = 0; i < N; ++i) { 29 | new (m_data + i) T(b[i]); 30 | } 31 | } 32 | HPC_ALWAYS_INLINE 33 | array() noexcept = default; 34 | HPC_ALWAYS_INLINE 35 | array(array&& other) noexcept = default; 36 | HPC_ALWAYS_INLINE 37 | array(array const&) noexcept = default; 38 | HPC_ALWAYS_INLINE array& 39 | operator=(array const&) noexcept = default; 40 | HPC_ALWAYS_INLINE array& 41 | operator=(array&& other) noexcept = default; 42 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr pointer 43 | data() noexcept 44 | { 45 | return m_data; 46 | } 47 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr const_pointer 48 | data() const noexcept 49 | { 50 | return m_data; 51 | } 52 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr iterator 53 | begin() noexcept 54 | { 55 | return iterator(m_data, m_data, m_data + N); 56 | } 57 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr const_iterator 58 | begin() const noexcept 59 | { 60 | return const_iterator(m_data, m_data, m_data + N); 61 | } 62 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr const_iterator 63 | cbegin() const noexcept 64 | { 65 | return const_iterator(m_data, m_data, m_data + N); 66 | } 67 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr iterator 68 | end() noexcept 69 | { 70 | return iterator(m_data + N, m_data, m_data + N); 71 | } 72 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr const_iterator 73 | end() const noexcept 74 | { 75 | return const_iterator(m_data + N, m_data, m_data + N); 76 | } 77 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr const_iterator 78 | cend() const noexcept 79 | { 80 | return const_iterator(m_data + N, m_data, m_data + N); 81 | } 82 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 83 | empty() const noexcept 84 | { 85 | return N == 0; 86 | } 87 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr size_type 88 | size() const noexcept 89 | { 90 | return size_type(N); 91 | } 92 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr reference 93 | operator[](size_type const i) noexcept 94 | { 95 | return begin()[i]; 96 | } 97 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr const_reference 98 | operator[](size_type const i) const noexcept 99 | { 100 | return begin()[i]; 101 | } 102 | }; 103 | 104 | template 105 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 106 | operator==(array const& a, array const& b) 107 | { 108 | for (I i(0); i != a.size(); ++i) { 109 | if (!(a[i] == b[i])) return false; 110 | } 111 | return true; 112 | } 113 | 114 | template 115 | class array_traits<::hpc::array> 116 | { 117 | public: 118 | using value_type = T; 119 | using size_type = I; 120 | HPC_HOST_DEVICE static constexpr size_type 121 | size() noexcept 122 | { 123 | return N; 124 | } 125 | template 126 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE static ::hpc::array 127 | load(Iterator it) noexcept 128 | { 129 | ::hpc::array result; 130 | auto it2 = result.begin(); 131 | for (size_type i = 0; i < N; ++i, ++it, ++it2) { 132 | *it2 = *it; 133 | } 134 | return result; 135 | } 136 | template 137 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE static void 138 | store(Iterator it, ::hpc::array const& value) noexcept 139 | { 140 | auto it2 = value.begin(); 141 | for (size_type i = 0; i < N; ++i, ++it, ++it2) { 142 | *it = *it2; 143 | } 144 | } 145 | }; 146 | 147 | } // namespace hpc 148 | -------------------------------------------------------------------------------- /hpc_array_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace hpc { 9 | 10 | // specialize this to describe how T is convertible to an array 11 | template 12 | class array_traits 13 | { 14 | public: 15 | using value_type = T; 16 | using size_type = std::ptrdiff_t; 17 | HPC_HOST_DEVICE static constexpr size_type 18 | size() noexcept 19 | { 20 | return 1; 21 | } 22 | template 23 | HPC_HOST_DEVICE static T 24 | load(Iterator it) noexcept 25 | { 26 | return *it; 27 | } 28 | template 29 | HPC_HOST_DEVICE static void 30 | store(Iterator it, T const& value) noexcept 31 | { 32 | *it = value; 33 | } 34 | }; 35 | 36 | // machine epsilon 37 | template 38 | HPC_HOST_DEVICE constexpr auto 39 | machine_epsilon() 40 | { 41 | return std::numeric_limits::epsilon(); 42 | } 43 | 44 | template <> 45 | HPC_HOST_DEVICE constexpr auto 46 | machine_epsilon() 47 | { 48 | return DBL_EPSILON; 49 | } 50 | 51 | } // namespace hpc 52 | -------------------------------------------------------------------------------- /hpc_atomic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace hpc { 6 | 7 | template 8 | class atomic_ref; 9 | 10 | template <> 11 | class atomic_ref 12 | { 13 | public: 14 | using value_type = int; 15 | 16 | private: 17 | value_type& m_ref; 18 | 19 | public: 20 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE explicit atomic_ref(value_type& ref_in) noexcept : m_ref(ref_in) 21 | { 22 | } 23 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE value_type 24 | operator++(int) const noexcept 25 | { 26 | #ifdef __CUDA_ARCH__ 27 | return atomicAdd(&m_ref, 1); 28 | #else 29 | return m_ref++; 30 | #endif 31 | } 32 | }; 33 | 34 | } // namespace hpc 35 | -------------------------------------------------------------------------------- /hpc_execution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace hpc { 6 | 7 | class local_policy 8 | { 9 | }; 10 | class serial_policy 11 | { 12 | }; 13 | class cuda_policy 14 | { 15 | }; 16 | 17 | using host_policy = serial_policy; 18 | #ifdef HPC_CUDA 19 | using device_policy = cuda_policy; 20 | #else 21 | using device_policy = serial_policy; 22 | #endif 23 | 24 | } // namespace hpc 25 | -------------------------------------------------------------------------------- /hpc_functional.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace hpc { 6 | 7 | template 8 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T 9 | max(T const& a, T const& b) noexcept 10 | { 11 | return (a < b) ? b : a; 12 | } 13 | 14 | template 15 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T 16 | min(T const& a, T const& b) noexcept 17 | { 18 | return (b < a) ? b : a; 19 | } 20 | 21 | template 22 | struct minimum 23 | { 24 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T 25 | operator()(T const& a, T const& b) noexcept 26 | { 27 | return ::hpc::min(a, b); 28 | } 29 | }; 30 | 31 | template 32 | struct maximum 33 | { 34 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T 35 | operator()(T const& a, T const& b) noexcept 36 | { 37 | return ::hpc::max(a, b); 38 | } 39 | }; 40 | 41 | template 42 | struct plus 43 | { 44 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T 45 | operator()(T const& a, T const& b) noexcept 46 | { 47 | return a + b; 48 | } 49 | }; 50 | 51 | template 52 | struct identity 53 | { 54 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T 55 | operator()(T const& a) noexcept 56 | { 57 | return a; 58 | } 59 | }; 60 | 61 | struct logical_or 62 | { 63 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 64 | operator()(bool const a, bool const b) const noexcept 65 | { 66 | return a || b; 67 | } 68 | }; 69 | 70 | struct logical_and 71 | { 72 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 73 | operator()(bool const a, bool const b) const noexcept 74 | { 75 | return a && b; 76 | } 77 | }; 78 | 79 | template 80 | struct cast 81 | { 82 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr To 83 | operator()(From const& a) noexcept 84 | { 85 | return a; 86 | } 87 | }; 88 | 89 | } // namespace hpc 90 | -------------------------------------------------------------------------------- /hpc_index.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace hpc { 8 | 9 | template 10 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr std::enable_if_t::value, Integral> 11 | weaken(Integral i) noexcept 12 | { 13 | return i; 14 | } 15 | 16 | #ifdef HPC_ENABLE_STRONG_INDICES 17 | 18 | template 19 | class index 20 | { 21 | Integral i; 22 | 23 | public: 24 | using integral_type = Integral; 25 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index(std::ptrdiff_t i_in) noexcept : i(integral_type(i_in)) 26 | { 27 | } 28 | HPC_ALWAYS_INLINE 29 | index() noexcept = default; 30 | HPC_ALWAYS_INLINE 31 | index(index const&) noexcept = default; 32 | HPC_ALWAYS_INLINE 33 | index(index&&) noexcept = default; 34 | HPC_ALWAYS_INLINE index& 35 | operator=(index const&) noexcept = default; 36 | HPC_ALWAYS_INLINE index& 37 | operator=(index&&) noexcept = default; 38 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index 39 | operator+(index const& other) const noexcept 40 | { 41 | return index(i + other.i); 42 | } 43 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index 44 | operator-(index const& other) const noexcept 45 | { 46 | return index(i - other.i); 47 | } 48 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE index& 49 | operator++() noexcept 50 | { 51 | ++i; 52 | return *this; 53 | } 54 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE index 55 | operator++(int) const noexcept 56 | { 57 | auto const old = *this; 58 | ++i; 59 | return old; 60 | } 61 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE index& 62 | operator--() noexcept 63 | { 64 | --i; 65 | return *this; 66 | } 67 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE index 68 | operator--(int) const noexcept 69 | { 70 | auto const old = *this; 71 | --i; 72 | return old; 73 | } 74 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 75 | operator==(index const& other) const noexcept 76 | { 77 | return i == other.i; 78 | } 79 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 80 | operator!=(index const& other) const noexcept 81 | { 82 | return i != other.i; 83 | } 84 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 85 | operator>(index const& other) const noexcept 86 | { 87 | return i > other.i; 88 | } 89 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 90 | operator<(index const& other) const noexcept 91 | { 92 | return i < other.i; 93 | } 94 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 95 | operator>=(index const& other) const noexcept 96 | { 97 | return i >= other.i; 98 | } 99 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 100 | operator<=(index const& other) const noexcept 101 | { 102 | return i <= other.i; 103 | } 104 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE index& 105 | operator+=(index const& other) noexcept 106 | { 107 | i += other.i; 108 | return *this; 109 | } 110 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE index& 111 | operator-=(index const& other) noexcept 112 | { 113 | i -= other.i; 114 | return *this; 115 | } 116 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr integral_type 117 | get() const noexcept 118 | { 119 | return i; 120 | } 121 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE explicit constexpr operator integral_type() const noexcept 122 | { 123 | return i; 124 | } 125 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index 126 | operator*(integral_type const n) const noexcept 127 | { 128 | return index(i * n); 129 | } 130 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index 131 | operator/(integral_type const n) const noexcept 132 | { 133 | return index(i / n); 134 | } 135 | template 136 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE explicit constexpr operator ::hpc::index() const noexcept 137 | { 138 | return i; 139 | } 140 | }; 141 | 142 | template 143 | class product_tag 144 | { 145 | }; 146 | 147 | template 148 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index, decltype(LI() * RI())> 149 | operator*(index left, index right) noexcept 150 | { 151 | return left.get() * right.get(); 152 | } 153 | 154 | template 155 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr index 156 | operator/(index, LI> left, index right) noexcept 157 | { 158 | return left.get() / right.get(); 159 | } 160 | 161 | template 162 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr Integral 163 | weaken(index i) noexcept 164 | { 165 | return i.get(); 166 | } 167 | 168 | template 169 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr T* 170 | operator+(T* p, index i) noexcept 171 | { 172 | return p + weaken(i); 173 | } 174 | 175 | #else 176 | 177 | template 178 | using index = Integral; 179 | 180 | #endif 181 | 182 | } // namespace hpc 183 | -------------------------------------------------------------------------------- /hpc_macros.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __CUDACC__ 4 | #define HPC_CUDA 5 | #define HPC_HOST __host__ 6 | #define HPC_DEVICE __device__ 7 | #else 8 | #define HPC_HOST 9 | #define HPC_DEVICE 10 | #endif 11 | 12 | #define HPC_HOST_DEVICE HPC_HOST HPC_DEVICE 13 | 14 | #if defined(DEBUG) 15 | #define HPC_NOINLINE __attribute__((noinline)) 16 | #else 17 | #define HPC_NOINLINE 18 | #endif 19 | #define HPC_ALWAYS_INLINE __attribute__((always_inline)) inline 20 | 21 | // Macros for debugging 22 | #ifdef __CUDACC__ 23 | #else 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #endif 31 | 32 | #ifdef __CUDACC__ 33 | #define HPC_TRACE_IMPL(msg) \ 34 | do { \ 35 | printf("%s ********** HPC_TRACE at "); \ 36 | printf("%s +%d\n%s\n", __FILE__, __LINE__, msg); \ 37 | } while (0) 38 | #else 39 | #define HPC_TRACE_IMPL(msg) \ 40 | do { \ 41 | std::cout << "********** HPC_TRACE at "; \ 42 | std::cout << __FILE__ << " +" << __LINE__ << "\n" << msg << '\n'; \ 43 | } while (0) 44 | #endif 45 | 46 | #ifdef __CUDACC__ 47 | #define HPC_DUMP_IMPL(msg) \ 48 | do { \ 49 | printf("%s", msg); \ 50 | } while (0) 51 | #else 52 | #define HPC_DUMP_IMPL(msg) \ 53 | do { \ 54 | std::cout << msg; \ 55 | } while (0) 56 | #endif 57 | 58 | #ifdef __CUDACC__ 59 | #define HPC_ERROR_EXIT_IMPL(msg) \ 60 | do { \ 61 | printf("********** HPC_ERROR at "); \ 62 | printf("%s:%d\n %s", __FILE__, __LINE__, msg); \ 63 | assert(0); \ 64 | } while (0) 65 | #else 66 | #define HPC_ERROR_EXIT_IMPL(msg) \ 67 | do { \ 68 | std::cout << "********** HPC ERROR at "; \ 69 | std::cout << __FILE__ << " +" << __LINE__ << "\n" << msg << '\n'; \ 70 | exit(1); \ 71 | } while (0) 72 | #endif 73 | 74 | #ifdef __CUDACC__ 75 | #define HPC_TRAP_FPE_IMPL(...) \ 76 | do { \ 77 | } while (0) 78 | #else 79 | #define HPC_TRAP_FPE_IMPL(...) \ 80 | do { \ 81 | _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); \ 82 | _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); \ 83 | _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~(_MM_MASK_INVALID | _MM_MASK_DIV_ZERO | _MM_MASK_OVERFLOW)); \ 84 | } while (0) 85 | #endif 86 | 87 | #ifdef __CUDACC__ 88 | #define HPC_ASSERT_IMPL(cond, msg) \ 89 | do { \ 90 | if (!(cond)) { \ 91 | printf("%s ********** HPC_ASSERT failed at ", #cond); \ 92 | printf("%s +%d\n%s\n", __FILE__, __LINE__, msg); \ 93 | assert(0); \ 94 | } \ 95 | } while (0) 96 | #else 97 | #define HPC_ASSERT_IMPL(cond, msg, ...) \ 98 | do { \ 99 | if (!(cond)) { \ 100 | std::cout << #cond " ********** HPC_ASSERT failed at "; \ 101 | std::cout << __FILE__ << " +" << __LINE__ << "\n" << msg << '\n'; \ 102 | abort(); \ 103 | } \ 104 | } while (0) 105 | #endif 106 | 107 | #define HPC_TRACE(...) HPC_TRACE_IMPL(__VA_ARGS__) 108 | #define HPC_DUMP(...) HPC_DUMP_IMPL(__VA_ARGS__) 109 | #define HPC_ERROR_EXIT(...) HPC_ERROR_EXIT_IMPL(__VA_ARGS__) 110 | #define HPC_TRAP_FPE(...) HPC_TRAP_FPE_IMPL(__VA_ARGS__) 111 | #define HPC_ASSERT(...) HPC_ASSERT_IMPL(__VA_ARGS__) 112 | -------------------------------------------------------------------------------- /hpc_math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __CUDACC__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | -------------------------------------------------------------------------------- /hpc_matrix.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace hpc { 8 | 9 | template < 10 | class T, 11 | layout L = layout::right, 12 | class Allocator = std::allocator, 13 | class ExecutionPolicy = ::hpc::serial_policy, 14 | class RowIndex = std::ptrdiff_t, 15 | class ColumnIndex = std::ptrdiff_t> 16 | class matrix 17 | { 18 | public: 19 | using row_type = RowIndex; 20 | using column_type = ColumnIndex; 21 | using nonzero_type = decltype(std::declval() * std::declval()); 22 | using value_type = T; 23 | 24 | private: 25 | using data_type = ::hpc::vector; 26 | using data_iterator = typename data_type::iterator; 27 | using const_data_iterator = typename data_type::const_iterator; 28 | using range_type = range_product; 29 | using const_range_type = range_product; 30 | vector m_data; 31 | row_type m_rows; 32 | column_type m_columns; 33 | range_type 34 | get_range() noexcept 35 | { 36 | return range_type(m_data.begin(), rows(), columns()); 37 | } 38 | const_range_type 39 | get_const_range() const noexcept 40 | { 41 | return const_range_type(m_data.begin(), rows(), columns()); 42 | } 43 | 44 | public: 45 | using allocator_type = Allocator; 46 | using execution_policy = ExecutionPolicy; 47 | using size_type = row_type; 48 | using difference_type = decltype(m_rows - m_rows); 49 | using reference = value_type&; 50 | using const_reference = value_type const&; 51 | using pointer = T*; 52 | using const_pointer = T const*; 53 | using iterator = typename range_type::iterator; 54 | using const_iterator = typename const_range_type::const_iterator; 55 | constexpr matrix() noexcept : m_rows(0), m_columns(0) 56 | { 57 | } 58 | matrix(row_type row_count, column_type column_count) 59 | : m_data(row_count * column_count), m_rows(row_count), m_columns(column_count) 60 | { 61 | } 62 | constexpr matrix(allocator_type const& allocator_in, execution_policy const& exec_in) noexcept 63 | : m_data(allocator_in, exec_in), m_rows(0), m_columns(0) 64 | { 65 | } 66 | matrix( 67 | row_type row_count, 68 | column_type column_count, 69 | allocator_type const& allocator_in, 70 | execution_policy const& exec_in) 71 | : m_data(row_count * column_count, allocator_in, exec_in), m_rows(row_count), m_columns(column_count) 72 | { 73 | } 74 | matrix(matrix&& other) noexcept = default; 75 | matrix(matrix const&) = delete; 76 | matrix& 77 | operator=(matrix const&) = delete; 78 | matrix& 79 | operator=(matrix&& other) = default; 80 | ~matrix() = default; 81 | T* 82 | data() noexcept 83 | { 84 | return m_data.data(); 85 | } 86 | T const* 87 | data() const noexcept 88 | { 89 | return m_data.data(); 90 | } 91 | iterator 92 | begin() noexcept 93 | { 94 | return get_range().begin(); 95 | } 96 | const_iterator 97 | begin() const noexcept 98 | { 99 | return get_const_range().begin(); 100 | } 101 | const_iterator 102 | cbegin() const noexcept 103 | { 104 | return get_const_range().begin(); 105 | } 106 | iterator 107 | end() noexcept 108 | { 109 | return get_range().end(); 110 | } 111 | const_iterator 112 | end() const noexcept 113 | { 114 | return get_const_range().end(); 115 | } 116 | const_iterator 117 | cend() const noexcept 118 | { 119 | return get_const_range().end(); 120 | } 121 | bool 122 | empty() const noexcept 123 | { 124 | return m_data.empty(); 125 | } 126 | size_type 127 | size() const noexcept 128 | { 129 | return m_rows; 130 | } 131 | row_type 132 | rows() const noexcept 133 | { 134 | return m_rows; 135 | } 136 | column_type 137 | columns() const noexcept 138 | { 139 | return m_columns; 140 | } 141 | void 142 | clear() 143 | { 144 | m_data.clear(); 145 | } 146 | void 147 | resize(row_type row_count, column_type column_count) 148 | { 149 | if (row_count == rows() && column_count == columns()) return; 150 | m_data.clear(); 151 | m_data.resize(row_count * column_count); 152 | m_rows = row_count; 153 | m_columns = column_count; 154 | } 155 | constexpr allocator_type 156 | get_allocator() const noexcept 157 | { 158 | return m_data.get_allocator(); 159 | } 160 | constexpr execution_policy 161 | get_execution_policy() const noexcept 162 | { 163 | return m_data.get_execution_policy(); 164 | } 165 | constexpr typename range_type::reference 166 | operator[](size_type i) noexcept 167 | { 168 | return begin()[i]; 169 | } 170 | constexpr typename range_type::const_reference 171 | operator[](size_type i) const noexcept 172 | { 173 | return begin()[i]; 174 | } 175 | constexpr reference 176 | operator()(row_type i, column_type j) noexcept 177 | { 178 | return begin()[i][j]; 179 | } 180 | constexpr const_reference 181 | operator()(row_type i, column_type j) const noexcept 182 | { 183 | return begin()[i][j]; 184 | } 185 | }; 186 | 187 | } // namespace hpc 188 | -------------------------------------------------------------------------------- /hpc_memory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace hpc { 10 | 11 | template 12 | using host_allocator = std::allocator; 13 | 14 | #ifdef HPC_CUDA 15 | 16 | template 17 | class device_allocator 18 | { 19 | public: 20 | using value_type = T; 21 | using pointer = T*; 22 | using const_pointer = T const*; 23 | using reference = T&; 24 | using const_reference = T const&; 25 | using size_type = std::size_t; 26 | using difference_type = std::ptrdiff_t; 27 | template 28 | struct rebind 29 | { 30 | typedef ::hpc::device_allocator other; 31 | }; 32 | using is_always_equal = std::true_type; 33 | constexpr bool 34 | operator==(device_allocator const&) const noexcept 35 | { 36 | return true; 37 | } 38 | constexpr bool 39 | operator!=(device_allocator const&) const noexcept 40 | { 41 | return false; 42 | } 43 | T* 44 | allocate(std::size_t n) 45 | { 46 | auto err = cudaDeviceSynchronize(); 47 | assert(err == cudaSuccess); 48 | void* ptr; 49 | err = cudaMalloc(&ptr, n * sizeof(T)); 50 | if (err != cudaSuccess) { 51 | throw std::bad_alloc(); 52 | } 53 | return static_cast(ptr); 54 | } 55 | void 56 | deallocate(T* p, std::size_t) 57 | { 58 | auto err = cudaDeviceSynchronize(); 59 | (void)err; 60 | assert(err == cudaSuccess); 61 | err = cudaFree(p); 62 | (void)err; 63 | assert(cudaSuccess == err); 64 | } 65 | }; 66 | 67 | template 68 | class pinned_allocator 69 | { 70 | public: 71 | using value_type = T; 72 | using pointer = T*; 73 | using const_pointer = T const*; 74 | using reference = T&; 75 | using const_reference = T const&; 76 | using size_type = std::size_t; 77 | using difference_type = std::ptrdiff_t; 78 | template 79 | struct rebind 80 | { 81 | typedef ::hpc::pinned_allocator other; 82 | }; 83 | using is_always_equal = std::true_type; 84 | constexpr bool 85 | operator==(pinned_allocator const&) const noexcept 86 | { 87 | return true; 88 | } 89 | constexpr bool 90 | operator!=(pinned_allocator const&) const noexcept 91 | { 92 | return false; 93 | } 94 | T* 95 | allocate(std::size_t n) 96 | { 97 | auto err = cudaDeviceSynchronize(); 98 | assert(err == cudaSuccess); 99 | void* ptr; 100 | err = cudaMallocHost(&ptr, n * sizeof(T)); 101 | if (err != cudaSuccess) { 102 | throw std::bad_alloc(); 103 | } 104 | return static_cast(ptr); 105 | } 106 | void 107 | deallocate(T* p, std::size_t) 108 | { 109 | auto err = cudaDeviceSynchronize(); 110 | (void)err; 111 | assert(err == cudaSuccess); 112 | err = cudaFreeHost(p); 113 | (void)err; 114 | assert(cudaSuccess == err); 115 | } 116 | }; 117 | 118 | #else 119 | 120 | template 121 | using pinned_allocator = std::allocator; 122 | template 123 | using device_allocator = std::allocator; 124 | 125 | #endif 126 | 127 | template 128 | HPC_NOINLINE void 129 | uninitialized_default_construct(serial_policy, Range&& range) 130 | { 131 | using range_type = std::decay_t; 132 | auto first = range.begin(); 133 | auto const last = range.end(); 134 | for (; first != last; ++first) { 135 | ::new (static_cast(std::addressof(*first))) typename range_type::value_type; 136 | } 137 | } 138 | 139 | #ifdef HPC_CUDA 140 | 141 | template 142 | HPC_NOINLINE void 143 | uninitialized_default_construct(cuda_policy policy, Range&& range) 144 | { 145 | using range_type = std::decay_t; 146 | using reference_type = typename range_type::reference; 147 | auto functor = [=] HPC_DEVICE(reference_type ref) { 148 | ::new (static_cast(&ref)) typename range_type::value_type; 149 | }; 150 | for_each(policy, range, functor); 151 | } 152 | 153 | #endif 154 | 155 | template 156 | HPC_ALWAYS_INLINE HPC_DEVICE void 157 | device_destroy_at(T* p) 158 | { 159 | p->~T(); 160 | } 161 | 162 | template 163 | HPC_ALWAYS_INLINE void 164 | host_destroy_at(T* p) 165 | { 166 | p->~T(); 167 | } 168 | 169 | template 170 | HPC_NOINLINE void 171 | destroy(serial_policy, Range&& range) 172 | { 173 | auto first = range.begin(); 174 | auto const last = range.end(); 175 | for (; first != last; ++first) { 176 | ::hpc::host_destroy_at(std::addressof(*first)); 177 | } 178 | } 179 | 180 | #ifdef HPC_CUDA 181 | 182 | template 183 | HPC_NOINLINE void 184 | destroy(cuda_policy policy, Range&& range) 185 | { 186 | using range_type = std::decay_t; 187 | using reference_type = typename range_type::reference; 188 | auto functor = [=] HPC_DEVICE(reference_type ref) { ::hpc::device_destroy_at(&ref); }; 189 | ::hpc::for_each(policy, range, functor); 190 | } 191 | 192 | #endif 193 | 194 | } // namespace hpc 195 | -------------------------------------------------------------------------------- /hpc_transform_reduce.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef HPC_CUDA 6 | 7 | #include 8 | #include 9 | 10 | #endif 11 | 12 | namespace hpc { 13 | 14 | template 15 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE T 16 | transform_reduce(local_policy, Range const& range, T init, BinaryOp binary_op, UnaryOp unary_op) noexcept 17 | { 18 | auto first = range.begin(); 19 | auto const last = range.end(); 20 | for (; first != last; ++first) { 21 | init = binary_op(std::move(init), unary_op(*first)); 22 | } 23 | return init; 24 | } 25 | 26 | template 27 | HPC_NOINLINE T 28 | transform_reduce(serial_policy, Range const& range, T init, BinaryOp binary_op, UnaryOp unary_op) 29 | { 30 | auto first = range.begin(); 31 | auto const last = range.end(); 32 | for (; first != last; ++first) { 33 | init = binary_op(std::move(init), unary_op(*first)); 34 | } 35 | return init; 36 | } 37 | 38 | #ifdef HPC_CUDA 39 | 40 | namespace impl { 41 | 42 | template 43 | T 44 | transform_reduce(cuda_policy, Iterator first, Iterator last, T init, BinaryOp binary_op, UnaryOp unary_op) 45 | { 46 | return ::thrust::transform_reduce(::thrust::device, first, last, unary_op, init, binary_op); 47 | } 48 | 49 | template 50 | T 51 | transform_reduce( 52 | cuda_policy, 53 | ::hpc::counting_iterator first, 54 | ::hpc::counting_iterator last, 55 | T init, 56 | BinaryOp binary_op, 57 | UnaryOp unary_op) 58 | { 59 | int const n = int(last - first); 60 | thrust::counting_iterator new_first(0); 61 | thrust::counting_iterator new_last(n); 62 | return ::thrust::transform_reduce(::thrust::device, new_first, new_last, unary_op, init, binary_op); 63 | } 64 | 65 | template 66 | TResult 67 | transform_reduce( 68 | cuda_policy, 69 | ::hpc::pointer_iterator first, 70 | ::hpc::pointer_iterator last, 71 | TResult init, 72 | BinaryOp binary_op, 73 | UnaryOp unary_op) 74 | { 75 | auto const size = std::size_t(last - first); 76 | TStored* const new_first = &(*first); 77 | TStored* const new_last = new_first + size; 78 | return ::thrust::transform_reduce(::thrust::device, new_first, new_last, unary_op, init, binary_op); 79 | } 80 | 81 | } // namespace impl 82 | 83 | template 84 | HPC_NOINLINE T 85 | transform_reduce(cuda_policy policy, Range const& range, T init, BinaryOp binary_op, UnaryOp unary_op) 86 | { 87 | return ::hpc::impl::transform_reduce(policy, range.begin(), range.end(), init, binary_op, unary_op); 88 | } 89 | 90 | #endif 91 | 92 | } // namespace hpc 93 | -------------------------------------------------------------------------------- /j2/hardening.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lgr { 8 | 9 | namespace j2 { 10 | 11 | struct Properties 12 | { 13 | double K; 14 | double G; 15 | 16 | double Y0; 17 | double n; 18 | double eps0; 19 | 20 | double Svis0; 21 | double m; 22 | double eps_dot0; 23 | }; 24 | 25 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 26 | HardeningPotential(Properties const props, double const eqps) 27 | { 28 | double const& Y0 = props.Y0; 29 | double const& n = props.n; 30 | double const& eps0 = props.eps0; 31 | 32 | if (n == hpc::numeric_limits::infinity()) return Y0 * eqps; 33 | 34 | double const exponent = (1.0 + n) / n; 35 | 36 | return Y0 * eps0 / exponent * (std::pow(1.0 + eqps / eps0, exponent) - 1.0); 37 | } 38 | 39 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 40 | FlowStrength(Properties const props, double const eqps) 41 | { 42 | double const& Y0 = props.Y0; 43 | double const& n = props.n; 44 | double const& eps0 = props.eps0; 45 | 46 | if (n == hpc::numeric_limits::infinity()) return Y0; 47 | 48 | return Y0 * std::pow(1.0 + eqps / eps0, 1.0 / n); 49 | } 50 | 51 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 52 | HardeningRate(Properties const props, double const eqps) 53 | { 54 | double const& Y0 = props.Y0; 55 | double const& n = props.n; 56 | double const& eps0 = props.eps0; 57 | 58 | if (n == hpc::numeric_limits::infinity()) return 0.0; 59 | 60 | return Y0 / (eps0 * n) * std::pow(1.0 + eqps / eps0, (1.0 - n) / n); 61 | } 62 | 63 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 64 | ViscoplasticDualKineticPotential(Properties const props, double const delta_eqps, double const dt) 65 | { 66 | double const& Svis0 = props.Svis0; 67 | double const& m = props.m; 68 | double const& eps_dot0 = props.eps_dot0; 69 | 70 | if (Svis0 == 0.0) return 0.0; 71 | 72 | double const exponent = (1.0 + m) / m; 73 | double psi_star = 0.0; 74 | if (delta_eqps > 0) { 75 | psi_star = dt * Svis0 * eps_dot0 / exponent * std::pow(delta_eqps / dt / eps_dot0, exponent); 76 | } 77 | return psi_star; 78 | } 79 | 80 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 81 | ViscoplasticStress(Properties const props, double const delta_eqps, double const dt) 82 | { 83 | double const& Svis0 = props.Svis0; 84 | double const& m = props.m; 85 | double const& eps_dot0 = props.eps_dot0; 86 | 87 | if (Svis0 == 0.0) return 0.0; 88 | 89 | double Svis = 0; 90 | if (delta_eqps > 0) { 91 | Svis = Svis0 * std::pow(delta_eqps / dt / eps_dot0, 1.0 / m); 92 | } 93 | 94 | return Svis; 95 | } 96 | 97 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 98 | ViscoplasticHardeningRate(Properties const props, double const delta_eqps, double const dt) 99 | { 100 | double const& Svis0 = props.Svis0; 101 | double const& m = props.m; 102 | double const& eps_dot0 = props.eps_dot0; 103 | 104 | if (Svis0 == 0.0) return 0.0; 105 | 106 | double Hvis = 0; 107 | if (delta_eqps > 0) { 108 | Hvis = Svis0 / (eps_dot0 * m * dt) * std::pow(delta_eqps / dt / eps_dot0, (1.0 - m) / m); 109 | } 110 | 111 | return Hvis; 112 | } 113 | 114 | } // namespace j2 115 | } // namespace lgr 116 | -------------------------------------------------------------------------------- /lgr_adapt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class input; 6 | class state; 7 | 8 | void 9 | update_quality(input const& in, state& s); 10 | void 11 | update_min_quality(state& s); 12 | bool 13 | adapt(input const& in, state& s); 14 | void 15 | initialize_h_adapt(state& s); 16 | 17 | } // namespace lgr 18 | -------------------------------------------------------------------------------- /lgr_bar.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace lgr { 5 | 6 | void 7 | initialize_bar_V(state& s) 8 | { 9 | auto const elems_to_nodes_iterator = s.elements_to_nodes.cbegin(); 10 | auto const nodes_to_x = s.x.cbegin(); 11 | auto const points_to_V = s.V.begin(); 12 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 13 | auto const elements_to_points = s.elements * s.points_in_element; 14 | auto functor = [=] HPC_DEVICE(element_index const element) { 15 | constexpr point_in_element_index fp(0); 16 | auto const element_nodes = elements_to_element_nodes[element]; 17 | using l_t = node_in_element_index; 18 | auto const node0 = elems_to_nodes_iterator[element_nodes[l_t(0)]]; 19 | auto const node1 = elems_to_nodes_iterator[element_nodes[l_t(1)]]; 20 | auto const x0 = nodes_to_x[node0].load(); 21 | auto const x1 = nodes_to_x[node1].load(); 22 | auto const V = (x1(0) - x0(0)) * hpc::area(1.0); 23 | assert(V > 0.0); 24 | points_to_V[elements_to_points[element][fp]] = V; 25 | }; 26 | hpc::for_each(hpc::device_policy(), s.elements, functor); 27 | } 28 | 29 | void 30 | initialize_bar_grad_N(state& s) 31 | { 32 | auto const points_to_V = s.V.cbegin(); 33 | auto const point_nodes_to_grad_N = s.grad_N.begin(); 34 | auto const points_to_point_nodes = s.points * s.nodes_in_element; 35 | auto functor = [=] HPC_DEVICE(point_index const point) { 36 | auto const volume = points_to_V[point]; 37 | auto const length = volume / hpc::area(1.0); 38 | auto const inv_length = 1.0 / length; 39 | auto const grad_N0 = hpc::basis_gradient(-inv_length, 0.0, 0.0); 40 | auto const grad_N1 = hpc::basis_gradient(inv_length, 0.0, 0.0); 41 | auto const point_nodes = points_to_point_nodes[point]; 42 | using l_t = node_in_element_index; 43 | point_nodes_to_grad_N[point_nodes[l_t(0)]] = grad_N0; 44 | point_nodes_to_grad_N[point_nodes[l_t(1)]] = grad_N1; 45 | }; 46 | hpc::for_each(hpc::device_policy(), s.points, functor); 47 | } 48 | 49 | void 50 | update_bar_h_min(input const&, state& s) 51 | { 52 | auto const elements_to_points = s.elements * s.points_in_element; 53 | auto const points_to_V = s.V.cbegin(); 54 | auto const elements_to_h_min = s.h_min.begin(); 55 | auto functor = [=] HPC_DEVICE(element_index const element) { 56 | constexpr point_in_element_index fp(0); 57 | auto const point = elements_to_points[element][fp]; 58 | elements_to_h_min[element] = double(points_to_V[point]); 59 | }; 60 | hpc::for_each(hpc::device_policy(), s.elements, functor); 61 | } 62 | 63 | void 64 | update_bar_h_art(state& s) 65 | { 66 | auto const elements_to_points = s.elements * s.points_in_element; 67 | auto const points_to_V = s.V.cbegin(); 68 | auto const elements_to_h_art = s.h_art.begin(); 69 | auto functor = [=] HPC_DEVICE(element_index const element) { 70 | constexpr point_in_element_index fp(0); 71 | auto const point = elements_to_points[element][fp]; 72 | elements_to_h_art[element] = double(points_to_V[point]); 73 | }; 74 | hpc::for_each(hpc::device_policy(), s.elements, functor); 75 | } 76 | 77 | } // namespace lgr 78 | -------------------------------------------------------------------------------- /lgr_bar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class state; 6 | class input; 7 | 8 | void 9 | initialize_bar_V(state& s); 10 | void 11 | initialize_bar_grad_N(state& s); 12 | void 13 | update_bar_h_min(input const&, state& s); 14 | void 15 | update_bar_h_art(state& s); 16 | 17 | } // namespace lgr 18 | -------------------------------------------------------------------------------- /lgr_composite_h_min.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace lgr { 7 | 8 | namespace composite_tetrahedron { 9 | 10 | HPC_HOST_DEVICE inline hpc::array, 4> 11 | get_subtet_coords(hpc::array, 11> in, int subtet) noexcept 12 | { 13 | hpc::array, 4> out; 14 | switch (subtet) { 15 | case 0: 16 | out[0] = in[0]; 17 | out[1] = in[4]; 18 | out[2] = in[6]; 19 | out[3] = in[7]; 20 | break; 21 | case 1: 22 | out[0] = in[1]; 23 | out[1] = in[5]; 24 | out[2] = in[4]; 25 | out[3] = in[8]; 26 | break; 27 | case 2: 28 | out[0] = in[2]; 29 | out[1] = in[6]; 30 | out[2] = in[5]; 31 | out[3] = in[9]; 32 | break; 33 | case 3: 34 | out[0] = in[3]; 35 | out[1] = in[8]; 36 | out[2] = in[7]; 37 | out[3] = in[9]; 38 | break; 39 | case 4: 40 | out[0] = in[4]; 41 | out[1] = in[8]; 42 | out[2] = in[5]; 43 | out[3] = in[10]; 44 | break; 45 | case 5: 46 | out[0] = in[5]; 47 | out[1] = in[8]; 48 | out[2] = in[9]; 49 | out[3] = in[10]; 50 | break; 51 | case 6: 52 | out[0] = in[9]; 53 | out[1] = in[8]; 54 | out[2] = in[7]; 55 | out[3] = in[10]; 56 | break; 57 | case 7: 58 | out[0] = in[7]; 59 | out[1] = in[8]; 60 | out[2] = in[4]; 61 | out[3] = in[10]; 62 | break; 63 | case 8: 64 | out[0] = in[4]; 65 | out[1] = in[5]; 66 | out[2] = in[6]; 67 | out[3] = in[10]; 68 | break; 69 | case 9: 70 | out[0] = in[5]; 71 | out[1] = in[9]; 72 | out[2] = in[6]; 73 | out[3] = in[10]; 74 | break; 75 | case 10: 76 | out[0] = in[9]; 77 | out[1] = in[7]; 78 | out[2] = in[6]; 79 | out[3] = in[10]; 80 | break; 81 | case 11: 82 | out[0] = in[7]; 83 | out[1] = in[4]; 84 | out[2] = in[6]; 85 | out[3] = in[10]; 86 | break; 87 | } 88 | return out; 89 | } 90 | 91 | HPC_HOST_DEVICE inline double 92 | get_tet_diameter(hpc::array, 4> const x) noexcept 93 | { 94 | auto const e10 = x[1] - x[0]; 95 | auto const e20 = x[2] - x[0]; 96 | auto const e30 = x[3] - x[0]; 97 | auto const e21 = x[2] - x[1]; 98 | auto const e31 = x[3] - x[1]; 99 | auto const vol = e30 * cross(e10, e20); 100 | auto const a0 = norm(cross(e10, e20)); 101 | auto const a1 = norm(cross(e10, e30)); 102 | auto const a2 = norm(cross(e20, e30)); 103 | auto const a3 = norm(cross(e21, e31)); 104 | auto const sa = 0.5 * (a0 + a1 + a2 + a3); 105 | return (sa > 0.0) ? (vol / sa) : 0.0; 106 | } 107 | 108 | HPC_HOST_DEVICE inline double 109 | get_length(hpc::array, 10> in) noexcept 110 | { 111 | hpc::array, 11> node_coords_with_center; 112 | for (int i = 0; i < 10; ++i) node_coords_with_center[i] = in[i]; 113 | node_coords_with_center[10] = (in[4] + in[5] + in[6] + in[7] + in[8] + in[9]) / 6.0; 114 | double min_length = hpc::numeric_limits::max(); 115 | for (int tet = 0; tet < 12; ++tet) { 116 | auto const x = get_subtet_coords(node_coords_with_center, tet); 117 | auto const length = get_tet_diameter(x); 118 | min_length = hpc::min(min_length, length); 119 | } 120 | constexpr double magic_number = 2.3; 121 | return min_length * magic_number; 122 | } 123 | 124 | } // namespace composite_tetrahedron 125 | 126 | void 127 | update_composite_tetrahedron_h_min(state& s) 128 | { 129 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 130 | auto const nodes_to_x = s.x.cbegin(); 131 | auto const elements_to_h_min = s.h_min.begin(); 132 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 133 | auto const nodes_in_element = s.nodes_in_element; 134 | auto functor = [=] HPC_DEVICE(element_index const element) { 135 | auto const element_nodes = elements_to_element_nodes[element]; 136 | hpc::array, 10> node_coords; 137 | for (auto const node_in_element : nodes_in_element) { 138 | auto const node = element_nodes_to_nodes[element_nodes[node_in_element]]; 139 | node_coords[hpc::weaken(node_in_element)] = hpc::vector3(nodes_to_x[node].load()); 140 | } 141 | auto const h_min = composite_tetrahedron::get_length(node_coords); 142 | elements_to_h_min[element] = h_min; 143 | }; 144 | hpc::for_each(hpc::device_policy(), s.elements, functor); 145 | } 146 | 147 | } // namespace lgr 148 | -------------------------------------------------------------------------------- /lgr_composite_nodal_mass.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace lgr { 6 | 7 | namespace composite_tetrahedron { 8 | 9 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE double 10 | get_q(double const x) noexcept 11 | { 12 | return 0.25 * (1.0 - std::sqrt(5.0) + 4.0 * std::sqrt(5.0) * x); 13 | } 14 | 15 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE vector4 16 | get_Q(hpc::vector3 const xi) noexcept 17 | { 18 | return vector4(get_q(1.0 - xi(0) - xi(1) - xi(2)), get_q(xi(0)), get_q(xi(1)), get_q(xi(2))); 19 | } 20 | 21 | HPC_NOINLINE HPC_HOST_DEVICE inline void 22 | get_centroids(hpc::array, 12>& xi) noexcept 23 | { 24 | xi[0] = hpc::vector3(0.125, 0.125, 0.125); 25 | xi[1] = hpc::vector3(0.625, 0.125, 0.125); 26 | xi[2] = hpc::vector3(0.125, 0.625, 0.125); 27 | xi[3] = hpc::vector3(0.125, 0.125, 0.625); 28 | xi[4] = hpc::vector3(0.4375, 0.1875, 0.1875); 29 | xi[5] = hpc::vector3(0.3125, 0.3125, 0.3125); 30 | xi[6] = hpc::vector3(0.1875, 0.1875, 0.4375); 31 | xi[7] = hpc::vector3(0.3125, 0.0625, 0.3125); 32 | xi[8] = hpc::vector3(0.3125, 0.3125, 0.0625); 33 | xi[9] = hpc::vector3(0.1875, 0.4375, 0.1875); 34 | xi[10] = hpc::vector3(0.0625, 0.3125, 0.3125); 35 | xi[11] = hpc::vector3(0.1875, 0.1875, 0.1875); 36 | } 37 | 38 | HPC_NOINLINE HPC_HOST_DEVICE inline void 39 | get_consistent_mass_matrix( 40 | hpc::array, 10> const& node_coords, 41 | vector4 const& point_densities, 42 | hpc::array, 10>& mass) noexcept 43 | { 44 | S_t S; 45 | get_S(S); 46 | O_t O; 47 | get_O(node_coords, S, O); 48 | hpc::array O_det; 49 | get_O_det(O, O_det); 50 | hpc::array, 12> C; 51 | get_centroids(C); 52 | gamma_t gamma; 53 | get_gamma(gamma); 54 | for (int i = 0; i < 10; ++i) { 55 | for (int j = 0; j < 10; ++j) { 56 | mass[i][j] = 0.0; 57 | } 58 | } 59 | for (int tet = 0; tet < 12; ++tet) { 60 | auto const c_s = C[tet]; 61 | auto const Q_s = get_Q(c_s); 62 | auto const rho_s = Q_s * point_densities; 63 | auto const J_s = O_det[tet]; 64 | auto const gamma_s = gamma[tet]; 65 | for (int i = 0; i < 10; ++i) { 66 | for (int j = 0; j < 10; ++j) { 67 | mass[i][j] += (J_s * rho_s) * gamma_s[i][j]; 68 | } 69 | } 70 | } 71 | } 72 | 73 | HPC_NOINLINE HPC_HOST_DEVICE inline void 74 | lump_mass_matrix(hpc::array, 10> const& mass, hpc::array& lumped) noexcept 75 | { 76 | for (int i = 0; i < 10; ++i) { 77 | lumped[i] = 0.0; 78 | for (int j = 0; j < 10; ++j) { 79 | lumped[i] += mass[i][j]; 80 | } 81 | } 82 | } 83 | 84 | } // namespace composite_tetrahedron 85 | 86 | void 87 | update_nodal_mass_composite_tetrahedron(state& s, material_index const material) 88 | { 89 | auto const nodes_to_node_elements = s.nodes_to_node_elements.cbegin(); 90 | auto const node_elements_to_elements = s.node_elements_to_elements.cbegin(); 91 | auto const points_to_rho = s.rho.cbegin(); 92 | auto const nodes_to_x = s.x.cbegin(); 93 | auto const nodes_to_m = s.material_mass[material].begin(); 94 | auto const elements_to_points = s.elements * s.points_in_element; 95 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 96 | auto const nodes_in_element = s.nodes_in_element; 97 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 98 | auto const points_in_element = s.points_in_element; 99 | auto const node_elements_to_nodes_in_element = s.node_elements_to_nodes_in_element.cbegin(); 100 | auto const elements_to_material = s.material.cbegin(); 101 | auto functor = [=] HPC_DEVICE(node_index const node) { 102 | double m(0.0); 103 | auto const node_elements = nodes_to_node_elements[node]; 104 | for (auto const node_element : node_elements) { 105 | element_index const element = node_elements_to_elements[node_element]; 106 | material_index const element_material = elements_to_material[element]; 107 | if (element_material != material) continue; 108 | node_in_element_index const node_in_element = node_elements_to_nodes_in_element[node_element]; 109 | auto const element_nodes = elements_to_element_nodes[element]; 110 | hpc::array, 10> node_coords; 111 | for (auto const node_in_element2 : nodes_in_element) { 112 | auto const node2 = element_nodes_to_nodes[element_nodes[node_in_element2]]; 113 | node_coords[hpc::weaken(node_in_element2)] = hpc::vector3(nodes_to_x[node2].load()); 114 | } 115 | vector4 point_densities; 116 | auto const element_points = elements_to_points[element]; 117 | for (auto const point_in_element : points_in_element) { 118 | auto const point = element_points[point_in_element]; 119 | point_densities(hpc::weaken(point_in_element)) = double(points_to_rho[point]); 120 | } 121 | hpc::array, 10> consistent_mass_matrix; 122 | composite_tetrahedron::get_consistent_mass_matrix(node_coords, point_densities, consistent_mass_matrix); 123 | hpc::array coef; 124 | composite_tetrahedron::lump_mass_matrix(consistent_mass_matrix, coef); 125 | m = m + coef[hpc::weaken(node_in_element)]; 126 | } 127 | nodes_to_m[node] = m; 128 | }; 129 | hpc::for_each(hpc::device_policy(), s.node_sets[material], functor); 130 | } 131 | 132 | } // namespace lgr 133 | -------------------------------------------------------------------------------- /lgr_composite_tetrahedron.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace lgr { 7 | 8 | namespace composite_tetrahedron { 9 | 10 | HPC_HOST_DEVICE constexpr inline matrix4x4 11 | get_parent_M_inv() noexcept 12 | { 13 | return matrix4x4( 14 | 96.0, -24.0, -24.0, -24.0, -24.0, 96.0, -24.0, -24.0, -24.0, -24.0, 96.0, -24.0, -24.0, -24.0, -24.0, 96.0); 15 | } 16 | 17 | HPC_HOST_DEVICE inline hpc::array 18 | get_DOL(hpc::array O_det, subtet_int_t subtet_int) noexcept 19 | { 20 | hpc::array DOL; 21 | for (auto& a : DOL) a = 0.0; 22 | for (int tet = 0; tet < 12; ++tet) { 23 | for (int pt = 0; pt < 4; ++pt) { 24 | DOL[pt] += O_det[tet] * subtet_int[tet][pt]; 25 | } 26 | } 27 | return DOL; 28 | } 29 | 30 | HPC_HOST_DEVICE inline hpc::array 31 | get_volumes(hpc::array, 10> const node_coords) noexcept 32 | { 33 | // compute the projected |J| times integration weights 34 | constexpr double ip_weight = 1.0 / 24.0; 35 | hpc::array volumes; 36 | for (auto& a : volumes) a = 0.0; 37 | hpc::array, 4> ref_points; 38 | get_ref_points(ref_points); 39 | subtet_int_t sub_tet_int; 40 | get_subtet_int(sub_tet_int); 41 | S_t S; 42 | get_S(S); 43 | O_t O; 44 | get_O(node_coords, S, O); 45 | hpc::array O_det; 46 | get_O_det(O, O_det); 47 | auto const DOL = get_DOL(O_det, sub_tet_int); 48 | auto const parent_M_inv = get_parent_M_inv(); 49 | for (int pt = 0; pt < 4; ++pt) { 50 | auto const lambda = get_barycentric(ref_points[pt]); 51 | for (int l1 = 0; l1 < 4; ++l1) { 52 | for (int l2 = 0; l2 < 4; ++l2) { 53 | volumes[pt] += lambda[l1] * parent_M_inv(l1, l2) * DOL[l2]; 54 | } 55 | } 56 | volumes[pt] *= ip_weight; 57 | } 58 | return volumes; 59 | } 60 | 61 | } // namespace composite_tetrahedron 62 | 63 | void 64 | initialize_composite_tetrahedron_V(state& s) 65 | { 66 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 67 | auto const nodes_to_x = s.x.cbegin(); 68 | auto const points_to_V = s.V.begin(); 69 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 70 | auto const elements_to_points = s.elements * s.points_in_element; 71 | auto const nodes_in_element = s.nodes_in_element; 72 | auto const points_in_element = s.points_in_element; 73 | auto functor = [=] HPC_DEVICE(element_index const element) { 74 | auto const element_nodes = elements_to_element_nodes[element]; 75 | hpc::array, 10> node_coords; 76 | for (auto const node_in_element : nodes_in_element) { 77 | auto const node = element_nodes_to_nodes[element_nodes[node_in_element]]; 78 | node_coords[hpc::weaken(node_in_element)] = hpc::vector3(nodes_to_x[node].load()); 79 | } 80 | auto const volumes = composite_tetrahedron::get_volumes(node_coords); 81 | #ifndef NDEBUG 82 | for (auto const volume : volumes) { 83 | assert(volume > 0.0); 84 | } 85 | #endif 86 | auto const element_points = elements_to_points[element]; 87 | for (auto const qp : points_in_element) { 88 | points_to_V[element_points[qp]] = volumes[hpc::weaken(qp)]; 89 | } 90 | }; 91 | hpc::for_each(hpc::device_policy(), s.elements, functor); 92 | } 93 | 94 | } // namespace lgr 95 | -------------------------------------------------------------------------------- /lgr_composite_tetrahedron.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr { 6 | 7 | class state; 8 | 9 | void 10 | initialize_composite_tetrahedron_V(state& s); 11 | void 12 | initialize_composite_tetrahedron_grad_N(state& s); 13 | void 14 | update_composite_tetrahedron_h_min(state& s); 15 | void 16 | update_nodal_mass_composite_tetrahedron(state& s, material_index const material); 17 | 18 | } // namespace lgr 19 | -------------------------------------------------------------------------------- /lgr_element_specific.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lgr { 10 | 11 | void 12 | initialize_V(input const& in, state& s) 13 | { 14 | switch (in.element) { 15 | case BAR: initialize_bar_V(s); break; 16 | case TRIANGLE: initialize_triangle_V(s); break; 17 | case TETRAHEDRON: initialize_tetrahedron_V(s); break; 18 | case COMPOSITE_TETRAHEDRON: initialize_composite_tetrahedron_V(s); break; 19 | } 20 | } 21 | 22 | void 23 | initialize_grad_N(input const& in, state& s) 24 | { 25 | switch (in.element) { 26 | case BAR: initialize_bar_grad_N(s); break; 27 | case TRIANGLE: initialize_triangle_grad_N(s); break; 28 | case TETRAHEDRON: initialize_tetrahedron_grad_N(s); break; 29 | case COMPOSITE_TETRAHEDRON: initialize_composite_tetrahedron_grad_N(s); break; 30 | } 31 | } 32 | 33 | HPC_NOINLINE inline void 34 | update_h_min_height(input const&, state& s) 35 | { 36 | auto const point_nodes_to_grad_N = s.grad_N.cbegin(); 37 | auto const elements_to_h_min = s.h_min.begin(); 38 | auto const points_to_point_nodes = s.points * s.nodes_in_element; 39 | auto const elements_to_points = s.elements * s.points_in_element; 40 | auto functor = [=] HPC_DEVICE(element_index const element) { 41 | constexpr point_in_element_index fp(0); 42 | auto const point = elements_to_points[element][fp]; 43 | hpc::length min_height = hpc::numeric_limits::max(); 44 | auto const point_nodes = points_to_point_nodes[point]; 45 | for (auto const point_node : point_nodes) { 46 | auto const grad_N = point_nodes_to_grad_N[point_node].load(); 47 | auto const height = 1.0 / norm(grad_N); 48 | min_height = hpc::min(min_height, height); 49 | } 50 | elements_to_h_min[element] = min_height; 51 | }; 52 | hpc::for_each(hpc::device_policy(), s.elements, functor); 53 | } 54 | 55 | HPC_NOINLINE inline void 56 | update_triangle_h_min(input const& in, state& s) 57 | { 58 | switch (in.h_min) { 59 | case MINIMUM_HEIGHT: update_h_min_height(in, s); break; 60 | case INBALL_DIAMETER: update_triangle_h_min_inball(in, s); break; 61 | } 62 | } 63 | 64 | HPC_NOINLINE inline void 65 | update_tetrahedron_h_min(input const& in, state& s) 66 | { 67 | switch (in.h_min) { 68 | case MINIMUM_HEIGHT: update_h_min_height(in, s); break; 69 | case INBALL_DIAMETER: update_tetrahedron_h_min_inball(in, s); break; 70 | } 71 | } 72 | 73 | HPC_NOINLINE inline void 74 | update_meshless_h_min(input const&, state&) 75 | { 76 | } 77 | 78 | void 79 | update_h_min(input const& in, state& s) 80 | { 81 | switch (in.element) { 82 | case BAR: update_bar_h_min(in, s); break; 83 | case TRIANGLE: update_triangle_h_min(in, s); break; 84 | case TETRAHEDRON: update_tetrahedron_h_min(in, s); break; 85 | case COMPOSITE_TETRAHEDRON: update_composite_tetrahedron_h_min(s); break; 86 | } 87 | } 88 | 89 | void 90 | update_h_art(input const& in, state& s) 91 | { 92 | switch (in.element) { 93 | case BAR: update_bar_h_art(s); break; 94 | case TRIANGLE: update_triangle_h_art(s); break; 95 | case TETRAHEDRON: update_tetrahedron_h_art(s); break; 96 | case COMPOSITE_TETRAHEDRON: update_tetrahedron_h_art(s); break; 97 | } 98 | } 99 | 100 | HPC_NOINLINE inline void 101 | update_nodal_mass_uniform(state& s, material_index const material) 102 | { 103 | auto const nodes_to_node_elements = s.nodes_to_node_elements.cbegin(); 104 | auto const node_elements_to_elements = s.node_elements_to_elements.cbegin(); 105 | auto const points_to_rho = s.rho.cbegin(); 106 | auto const points_to_V = s.V.cbegin(); 107 | assert(s.material_mass[material].size() == s.nodes.size()); 108 | auto const nodes_to_m = s.material_mass[material].begin(); 109 | auto const N = 1.0 / double(hpc::weaken(s.nodes_in_element.size())); 110 | auto const elements_to_points = s.elements * s.points_in_element; 111 | auto const elements_to_material = s.material.cbegin(); 112 | auto functor = [=] HPC_DEVICE(node_index const node) { 113 | hpc::mass m(0.0); 114 | auto const node_elements = nodes_to_node_elements[node]; 115 | for (auto const node_element : node_elements) { 116 | element_index const element = node_elements_to_elements[node_element]; 117 | material_index const element_material = elements_to_material[element]; 118 | if (element_material != material) continue; 119 | for (auto const point : elements_to_points[element]) { 120 | auto const rho = points_to_rho[point]; 121 | auto const V = points_to_V[point]; 122 | m = m + (rho * V) * N; 123 | } 124 | } 125 | nodes_to_m[node] = m; 126 | }; 127 | hpc::for_each(hpc::device_policy(), s.node_sets[material], functor); 128 | } 129 | 130 | void 131 | update_nodal_mass(input const& in, state& s) 132 | { 133 | for (auto const material : in.materials) { 134 | switch (in.element) { 135 | case BAR: 136 | case TRIANGLE: 137 | case TETRAHEDRON: update_nodal_mass_uniform(s, material); break; 138 | case COMPOSITE_TETRAHEDRON: update_nodal_mass_composite_tetrahedron(s, material); break; 139 | } 140 | } 141 | hpc::fill(hpc::device_policy(), s.mass, hpc::mass(0.0)); 142 | for (auto const material : in.materials) { 143 | auto const nodes_to_total = s.mass.begin(); 144 | auto const nodes_to_partial = s.material_mass[material].cbegin(); 145 | auto functor = [=] HPC_DEVICE(node_index const node) { 146 | auto m_total = nodes_to_total[node]; 147 | auto const m_partial = nodes_to_partial[node]; 148 | m_total = m_total + m_partial; 149 | nodes_to_total[node] = m_total; 150 | }; 151 | hpc::for_each(hpc::device_policy(), s.node_sets[material], functor); 152 | } 153 | } 154 | 155 | } // namespace lgr 156 | -------------------------------------------------------------------------------- /lgr_element_specific.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class input; 6 | class state; 7 | 8 | void 9 | initialize_V(input const& in, state& s); 10 | void 11 | initialize_grad_N(input const& in, state& s); 12 | void 13 | update_h_min(input const& in, state& s); 14 | void 15 | update_h_art(input const& in, state& s); 16 | void 17 | update_nodal_mass(input const& in, state& s); 18 | 19 | } // namespace lgr 20 | -------------------------------------------------------------------------------- /lgr_element_specific_inline.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lgr { 8 | 9 | HPC_HOST_DEVICE inline hpc::area 10 | triangle_area(hpc::array, 3> const x) noexcept 11 | { 12 | return 0.5 * (cross((x[1] - x[0]), (x[2] - x[0]))(2)); 13 | } 14 | 15 | HPC_HOST_DEVICE inline hpc::volume 16 | tetrahedron_volume(hpc::array, 4> const x) 17 | { 18 | return (1.0 / 6.0) * (cross((x[1] - x[0]), (x[2] - x[0])) * (x[3] - x[0])); 19 | } 20 | 21 | HPC_HOST_DEVICE inline auto 22 | triangle_basis_gradients(hpc::array, 3> const x, hpc::area const area) noexcept 23 | { 24 | hpc::displacement edge_vectors[3]; 25 | edge_vectors[0] = x[1] - x[0]; 26 | edge_vectors[1] = x[2] - x[0]; 27 | edge_vectors[2] = x[2] - x[1]; 28 | constexpr hpc::vector3 z_axis(0.0, 0.0, 1.0); 29 | auto const factor = 0.5 * (1.0 / area); 30 | hpc::array, 3> grad_N; 31 | grad_N[0] = cross(z_axis, edge_vectors[2]) * factor; 32 | grad_N[1] = -cross(z_axis, edge_vectors[1]) * factor; 33 | grad_N[2] = cross(z_axis, edge_vectors[0]) * factor; 34 | return grad_N; 35 | } 36 | 37 | HPC_HOST_DEVICE inline auto 38 | tetrahedron_basis_gradients(hpc::array, 4> const x, hpc::volume const volume) noexcept 39 | { 40 | hpc::displacement ev[5]; 41 | ev[0] = x[1] - x[0]; 42 | ev[1] = x[2] - x[0]; 43 | ev[2] = x[3] - x[0]; 44 | ev[3] = x[2] - x[1]; 45 | ev[4] = x[3] - x[1]; 46 | auto const factor = (1.0 / 6.0) * (1.0 / volume); 47 | hpc::array, 4> grad_N; 48 | grad_N[0] = cross(ev[4], ev[3]) * factor; 49 | grad_N[1] = cross(ev[1], ev[2]) * factor; 50 | grad_N[2] = cross(ev[2], ev[0]) * factor; 51 | grad_N[3] = cross(ev[0], ev[1]) * factor; 52 | return grad_N; 53 | } 54 | 55 | } // namespace lgr 56 | -------------------------------------------------------------------------------- /lgr_exodus.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef LGR_ENABLE_EXODUS 7 | #ifdef __clang__ 8 | #pragma clang diagnostic push 9 | #pragma clang diagnostic ignored "-Wreserved-id-macro" 10 | #endif 11 | #include 12 | #ifdef __clang__ 13 | #pragma clang diagnostic pop 14 | #endif 15 | #endif 16 | 17 | namespace lgr { 18 | 19 | #ifdef LGR_ENABLE_EXODUS 20 | 21 | int 22 | read_exodus_file(std::string const& filepath, input const& in, state& s) 23 | { 24 | int comp_ws = int(sizeof(double)); 25 | int io_ws = 0; 26 | float version; 27 | auto mode = EX_READ; 28 | int exodus_file = ex_open(filepath.c_str(), mode, &comp_ws, &io_ws, &version); 29 | assert(exodus_file >= 0); 30 | ex_init_params init_params; 31 | int exodus_error_code; 32 | exodus_error_code = ex_get_init_ext(exodus_file, &init_params); 33 | assert(exodus_error_code == 0); 34 | hpc::host_vector block_ids(init_params.num_elem_blk); 35 | exodus_error_code = ex_get_ids(exodus_file, EX_ELEM_BLOCK, block_ids.data()); 36 | assert(exodus_error_code == 0); 37 | switch (in.element) { 38 | case BAR: 39 | s.nodes_in_element.resize(node_in_element_index(2)); 40 | s.points_in_element.resize(point_in_element_index(1)); 41 | break; 42 | case TRIANGLE: 43 | s.nodes_in_element.resize(node_in_element_index(3)); 44 | s.points_in_element.resize(point_in_element_index(1)); 45 | break; 46 | case TETRAHEDRON: 47 | s.nodes_in_element.resize(node_in_element_index(4)); 48 | s.points_in_element.resize(point_in_element_index(1)); 49 | break; 50 | case COMPOSITE_TETRAHEDRON: 51 | s.nodes_in_element.resize(node_in_element_index(10)); 52 | s.points_in_element.resize(point_in_element_index(4)); 53 | break; 54 | } 55 | s.nodes.resize(int(init_params.num_nodes)); 56 | s.elements.resize(int(init_params.num_elem)); 57 | s.material.resize(s.elements.size()); 58 | hpc::host_vector host_conn(int(s.elements.size() * s.nodes_in_element.size())); 59 | int offset = 0; 60 | for (int i = 0; i < init_params.num_elem_blk; ++i) { 61 | char elem_type[MAX_STR_LENGTH + 1]; 62 | int nentries; 63 | int nnodes_per_entry; 64 | int nedges_per_entry; 65 | int nfaces_per_entry; 66 | int nattr_per_entry; 67 | exodus_error_code = ex_get_block( 68 | exodus_file, 69 | EX_ELEM_BLOCK, 70 | block_ids[i], 71 | elem_type, 72 | &nentries, 73 | &nnodes_per_entry, 74 | &nedges_per_entry, 75 | &nfaces_per_entry, 76 | &nattr_per_entry); 77 | assert(exodus_error_code == 0); 78 | if (nentries == 0) continue; 79 | assert(nnodes_per_entry == int(s.nodes_in_element.size())); 80 | if (nedges_per_entry < 0) nedges_per_entry = 0; 81 | if (nfaces_per_entry < 0) nfaces_per_entry = 0; 82 | hpc::host_vector edge_conn(nentries * nedges_per_entry); 83 | hpc::host_vector face_conn(nentries * nfaces_per_entry); 84 | exodus_error_code = ex_get_conn( 85 | exodus_file, 86 | EX_ELEM_BLOCK, 87 | block_ids[i], 88 | host_conn.data() + offset * int(s.nodes_in_element.size()), 89 | edge_conn.data(), 90 | face_conn.data()); 91 | assert(exodus_error_code == 0); 92 | auto material_begin = s.material.begin() + element_index(offset); 93 | auto material_end = material_begin + element_index(nentries); 94 | auto material_range = hpc::make_iterator_range(material_begin, material_end); 95 | material_index const material(block_ids[i]); 96 | hpc::fill(hpc::device_policy(), material_range, material); 97 | offset += nentries; 98 | } 99 | assert(offset == init_params.num_elem); 100 | hpc::pinned_vector pinned_conn(s.elements.size() * s.nodes_in_element.size()); 101 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 102 | for (auto const element : s.elements) { 103 | auto const element_nodes = elements_to_element_nodes[element]; 104 | for (auto const node_in_element : s.nodes_in_element) { 105 | auto const host_index = int(element * s.nodes_in_element.size()) + int(node_in_element); 106 | auto const element_node = element_nodes[node_in_element]; 107 | pinned_conn[element_node] = node_index(host_conn[host_index] - 1); 108 | } 109 | } 110 | s.elements_to_nodes.resize(pinned_conn.size()); 111 | hpc::copy(pinned_conn, s.elements_to_nodes); 112 | pinned_conn.clear(); 113 | hpc::host_vector host_coords[3]; 114 | for (int i = 0; i < 3; ++i) host_coords[i].resize(s.nodes.size()); 115 | exodus_error_code = ex_get_coord(exodus_file, host_coords[0].data(), host_coords[1].data(), host_coords[2].data()); 116 | assert(exodus_error_code == 0); 117 | hpc::pinned_array_vector, node_index> pinned_coords(s.nodes.size()); 118 | for (auto const node : s.nodes) { 119 | pinned_coords[node] = hpc::position(host_coords[0][node], host_coords[1][node], host_coords[2][node]); 120 | } 121 | s.x.resize(s.nodes.size()); 122 | hpc::copy(pinned_coords, s.x); 123 | propagate_connectivity(s); 124 | return exodus_error_code; 125 | } 126 | 127 | #else 128 | 129 | int 130 | read_exodus_file(std::string const&, input const&, state&) 131 | { 132 | throw std::runtime_error("Exodus not enabled! Rebuild with LGR_ENABLE_EXODUS=ON"); 133 | return -1; 134 | } 135 | 136 | #endif 137 | 138 | } // namespace lgr 139 | -------------------------------------------------------------------------------- /lgr_exodus.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr { 6 | 7 | class input; 8 | class state; 9 | 10 | int 11 | read_exodus_file(std::string const& filepath, input const& in, state& s); 12 | 13 | } // namespace lgr 14 | -------------------------------------------------------------------------------- /lgr_material_set.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr { 6 | 7 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE int 8 | popcount(unsigned x) noexcept 9 | { 10 | #ifdef __CUDA_ARCH__ 11 | return __popc(x); 12 | #else 13 | return __builtin_popcount(x); 14 | #endif 15 | } 16 | 17 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE int 18 | popcount(unsigned long x) noexcept 19 | { 20 | #ifdef __CUDA_ARCH__ 21 | return __popcll(x); 22 | #else 23 | return __builtin_popcountl(x); 24 | #endif 25 | } 26 | 27 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE int 28 | popcount(unsigned long long x) noexcept 29 | { 30 | #ifdef __CUDA_ARCH__ 31 | return __popcll(x); 32 | #else 33 | return __builtin_popcountll(x); 34 | #endif 35 | } 36 | 37 | class material_set 38 | { 39 | std::uint64_t bits{0}; 40 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE explicit constexpr material_set(std::uint64_t const bits_in) noexcept 41 | : bits(bits_in) 42 | { 43 | } 44 | 45 | public: 46 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE explicit constexpr material_set(material_index const material) noexcept 47 | : bits(std::uint64_t(1) << hpc::weaken(material)) 48 | { 49 | } 50 | HPC_ALWAYS_INLINE 51 | material_set() noexcept = default; 52 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr material_set 53 | operator|(material_set const other) const noexcept 54 | { 55 | return material_set(bits | other.bits); 56 | } 57 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 58 | contains(material_set const other) const noexcept 59 | { 60 | return (bits | other.bits) == bits; 61 | } 62 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr explicit operator std::uint64_t() const noexcept 63 | { 64 | return bits; 65 | } 66 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE int 67 | size() const noexcept 68 | { 69 | return popcount(bits); 70 | } 71 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr material_set 72 | operator-(material_set const other) const noexcept 73 | { 74 | return material_set(bits & (~other.bits)); 75 | } 76 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr static material_set 77 | none() noexcept 78 | { 79 | return material_set(std::uint64_t(0)); 80 | } 81 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr static material_set 82 | all(material_index const size) noexcept 83 | { 84 | return material_set((std::uint64_t(1) << hpc::weaken(size)) - 1); 85 | } 86 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr bool 87 | operator==(material_set const other) const noexcept 88 | { 89 | return bits == other.bits; 90 | } 91 | }; 92 | 93 | } // namespace lgr 94 | -------------------------------------------------------------------------------- /lgr_mesh_indices.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace lgr { 7 | 8 | struct node_tag 9 | { 10 | }; 11 | using node_index = hpc::index; 12 | struct node_in_element_tag 13 | { 14 | }; 15 | using node_in_element_index = hpc::index; 16 | struct element_tag 17 | { 18 | }; 19 | using element_index = hpc::index; 20 | struct node_element_tag 21 | { 22 | }; 23 | using node_element_index = hpc::index; 24 | struct point_in_element_tag 25 | { 26 | }; 27 | using point_in_element_index = hpc::index; 28 | using point_index = decltype(element_index() * point_in_element_index()); 29 | using element_node_index = decltype(element_index() * node_in_element_index()); 30 | struct point_node_tag 31 | { 32 | }; 33 | using point_node_index = hpc::index; 34 | struct node_point_tag 35 | { 36 | }; 37 | using node_point_index = hpc::index; 38 | struct material_tag 39 | { 40 | }; 41 | using material_index = hpc::index; 42 | 43 | } // namespace lgr 44 | -------------------------------------------------------------------------------- /lgr_meshing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class input; 6 | class state; 7 | 8 | void 9 | build_mesh(input const& in, state& s); 10 | void 11 | propagate_connectivity(state& s); 12 | 13 | } // namespace lgr 14 | -------------------------------------------------------------------------------- /lgr_physics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace lgr { 5 | 6 | class input; 7 | class state; 8 | 9 | void 10 | run(input const& in, std::string const& filename = ""); 11 | 12 | } // namespace lgr 13 | -------------------------------------------------------------------------------- /lgr_physics_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace lgr { 7 | 8 | HPC_NOINLINE inline void 9 | update_c(state& s) 10 | { 11 | auto const points_to_rho = s.rho.cbegin(); 12 | auto const points_to_K = s.K.cbegin(); 13 | auto const points_to_G = s.G.cbegin(); 14 | auto const points_to_c = s.c.begin(); 15 | auto functor = [=] HPC_DEVICE(point_index const point) { 16 | auto const rho = points_to_rho[point]; 17 | auto const K = points_to_K[point]; 18 | auto const G = points_to_G[point]; 19 | auto const M = K + (4.0 / 3.0) * G; 20 | auto const c = sqrt(M / rho); 21 | points_to_c[point] = c; 22 | }; 23 | hpc::for_each(hpc::device_policy(), s.points, functor); 24 | } 25 | 26 | HPC_NOINLINE inline void 27 | find_max_stable_dt(state& s) 28 | { 29 | hpc::time const init(std::numeric_limits::max()); 30 | s.max_stable_dt = hpc::transform_reduce( 31 | hpc::device_policy(), s.element_dt, init, hpc::minimum>(), hpc::identity>()); 32 | assert(s.max_stable_dt < 1.0); 33 | } 34 | 35 | } // namespace lgr 36 | -------------------------------------------------------------------------------- /lgr_print.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace lgr { 5 | 6 | std::ostream& 7 | operator<<(std::ostream& stream, hpc::vector3 v) 8 | { 9 | stream << v(0) << " " << v(1) << " " << v(2); 10 | return stream; 11 | } 12 | 13 | std::ostream& 14 | operator<<(std::ostream& stream, hpc::matrix3x3 m) 15 | { 16 | stream << m(0, 0) << " " << m(0, 1) << " " << m(0, 2) << "\n"; 17 | stream << m(1, 0) << " " << m(1, 1) << " " << m(1, 2) << "\n"; 18 | stream << m(2, 0) << " " << m(2, 1) << " " << m(2, 2) << "\n"; 19 | return stream; 20 | } 21 | 22 | std::ostream& 23 | operator<<(std::ostream& stream, hpc::symmetric3x3 m) 24 | { 25 | stream << m(0, 0) << " " << m(0, 1) << " " << m(0, 2) << "\n"; 26 | stream << m(1, 0) << " " << m(1, 1) << " " << m(1, 2) << "\n"; 27 | stream << m(2, 0) << " " << m(2, 1) << " " << m(2, 2) << "\n"; 28 | return stream; 29 | } 30 | 31 | } // namespace lgr 32 | -------------------------------------------------------------------------------- /lgr_print.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace lgr { 11 | 12 | #ifndef HPC_DISABLE_STRONG_INDICES 13 | template 14 | std::ostream& 15 | operator<<(std::ostream& stream, hpc::index v) 16 | { 17 | stream << weaken(v); 18 | return stream; 19 | } 20 | #endif 21 | 22 | #ifndef HPC_DISABLE_DIMENSIONAL_ANALYSIS 23 | template 24 | std::ostream& 25 | operator<<(std::ostream& stream, hpc::quantity v) 26 | { 27 | stream << weaken(v); 28 | return stream; 29 | } 30 | #endif 31 | 32 | template 33 | std::ostream& 34 | operator<<(std::ostream& stream, hpc::vector3 v) 35 | { 36 | stream << v(0) << " " << v(1) << " " << v(2); 37 | return stream; 38 | } 39 | 40 | template 41 | std::ostream& 42 | operator<<(std::ostream& stream, hpc::matrix3x3 m) 43 | { 44 | stream << m(0, 0) << " " << m(0, 1) << " " << m(0, 2) << "\n"; 45 | stream << m(1, 0) << " " << m(1, 1) << " " << m(1, 2) << "\n"; 46 | stream << m(2, 0) << " " << m(2, 1) << " " << m(2, 2) << "\n"; 47 | return stream; 48 | } 49 | 50 | template 51 | std::ostream& 52 | operator<<(std::ostream& stream, hpc::symmetric3x3 m) 53 | { 54 | stream << m(0, 0) << " " << m(0, 1) << " " << m(0, 2) << "\n"; 55 | stream << m(1, 0) << " " << m(1, 1) << " " << m(1, 2) << "\n"; 56 | stream << m(2, 0) << " " << m(2, 1) << " " << m(2, 2) << "\n"; 57 | return stream; 58 | } 59 | 60 | } // namespace lgr 61 | -------------------------------------------------------------------------------- /lgr_reduce.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr { 6 | 7 | template 8 | T 9 | transform_reduce(Range const& range, T init, BinaryOp binary_op, UnaryOp unary_op) 10 | { 11 | auto first = range.begin(); 12 | auto const last = range.end(); 13 | for (; first != last; ++first) { 14 | init = binary_op(std::move(init), unary_op(*first)); 15 | } 16 | return init; 17 | } 18 | 19 | template 20 | T 21 | reduce(Range const& range, T init) 22 | { 23 | using input_value_type = typename Range::value_type; 24 | auto const unop = [](input_value_type const i) { return T(i); }; 25 | return transform_reduce(range, init, plus(), unop); 26 | } 27 | 28 | template 29 | bool 30 | any_of(Range const& range, UnaryPredicate p) 31 | { 32 | return transform_reduce(range, false, logical_or(), p); 33 | } 34 | 35 | template 36 | bool 37 | all_of(Range const& range, UnaryPredicate p) 38 | { 39 | return transform_reduce(range, true, logical_and(), p); 40 | } 41 | 42 | template 43 | bool 44 | all_of(Range const& range) 45 | { 46 | return all_of(range, identity()); 47 | } 48 | 49 | template 50 | bool 51 | any_of(Range const& range) 52 | { 53 | return any_of(range, identity()); 54 | } 55 | 56 | } // namespace lgr 57 | -------------------------------------------------------------------------------- /lgr_stabilized.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lgr { 8 | 9 | class input; 10 | class state; 11 | 12 | void 13 | update_sigma_with_p_h(state& s, material_index const material); 14 | void 15 | update_sigma_with_p_h_p_prime( 16 | input const& in, 17 | state& s, 18 | material_index const material, 19 | hpc::time const dt, 20 | hpc::device_vector, node_index> const& old_p_h_vector); 21 | void 22 | update_p_h( 23 | state& s, 24 | hpc::time const dt, 25 | material_index const material, 26 | hpc::device_vector, node_index> const& old_p_h_vector); 27 | void 28 | update_e_h( 29 | state& s, 30 | hpc::time const dt, 31 | material_index const material, 32 | hpc::device_vector, node_index> const& old_e_h_vector); 33 | void 34 | nodal_ideal_gas(input const& in, state& s, material_index const); 35 | void 36 | update_nodal_density(state& s, material_index const); 37 | void 38 | interpolate_K(state& s, material_index const); 39 | void 40 | interpolate_rho(state& s, material_index const); 41 | void 42 | interpolate_e(state& s, material_index const); 43 | void 44 | update_p_h_dot_from_a(input const& in, state& s, material_index const material); 45 | void 46 | update_e_h_dot_from_a(input const& in, state& s, material_index const material); 47 | 48 | } // namespace lgr 49 | -------------------------------------------------------------------------------- /lgr_state.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lgr { 8 | 9 | void 10 | resize_state(input const& in, state& s) 11 | { 12 | s.u.resize(s.nodes.size()); 13 | s.v.resize(s.nodes.size()); 14 | s.b.resize(s.nodes.size()); 15 | s.V.resize(s.points.size()); 16 | s.grad_N.resize(s.points.size() * s.nodes_in_element.size()); 17 | s.F_total.resize(s.points.size()); 18 | s.use_comptet_stabilization = in.enable_comptet_stabilization; 19 | if (s.use_comptet_stabilization == true) { 20 | s.JavgJ.resize(s.points.size()); 21 | } 22 | s.sigma.resize(s.points.size()); 23 | s.symm_grad_v.resize(s.points.size()); 24 | auto have_nodal_pressure_or_energy = [&](material_index const material) { 25 | return in.enable_nodal_pressure[material] || in.enable_nodal_energy[material]; 26 | }; 27 | if (!hpc::all_of(hpc::serial_policy(), in.materials, have_nodal_pressure_or_energy)) { 28 | s.p.resize(s.points.size()); 29 | } 30 | s.K.resize(s.points.size()); 31 | s.G.resize(s.points.size()); 32 | s.c.resize(s.points.size()); 33 | s.element_f.resize(s.points.size() * s.nodes_in_element.size()); 34 | s.f.resize(s.nodes.size()); 35 | s.rho.resize(s.points.size()); 36 | if (!hpc::all_of(hpc::serial_policy(), in.enable_nodal_energy)) { 37 | s.e.resize(s.points.size()); 38 | } 39 | if (hpc::any_of(hpc::serial_policy(), in.enable_Mie_Gruneisen_eos)) { 40 | s.e.resize(s.points.size()); 41 | s.dp_de.resize(s.points.size()); 42 | } 43 | s.rho_e_dot.resize(s.points.size()); 44 | { 45 | // Plasticity 46 | s.Fp_total.resize(s.points.size()); 47 | s.ep.resize(s.points.size()); 48 | } 49 | s.material_mass.resize(in.materials.size()); 50 | for (auto& mm : s.material_mass) mm.resize(s.nodes.size()); 51 | s.mass.resize(s.nodes.size()); 52 | s.a.resize(s.nodes.size()); 53 | s.h_min.resize(s.elements.size()); 54 | if (in.enable_viscosity) { 55 | s.h_art.resize(s.elements.size()); 56 | } 57 | s.nu_art.resize(s.points.size()); 58 | s.element_dt.resize(s.points.size()); 59 | s.p_h.resize(in.materials.size()); 60 | s.p_h_dot.resize(in.materials.size()); 61 | s.e_h.resize(in.materials.size()); 62 | s.e_h_dot.resize(in.materials.size()); 63 | s.rho_h.resize(in.materials.size()); 64 | s.K_h.resize(in.materials.size()); 65 | s.dp_de_h.resize(in.materials.size()); 66 | s.temp.resize(in.materials.size()); 67 | for (auto const material : in.materials) { 68 | if (in.enable_nodal_pressure[material]) { 69 | s.p_h[material].resize(s.nodes.size()); 70 | s.p_h_dot[material].resize(s.nodes.size()); 71 | s.v_prime.resize(s.points.size()); 72 | s.W.resize(s.points.size() * s.nodes_in_element.size()); 73 | } 74 | if (in.enable_p_prime[material]) { 75 | s.p_prime.resize(s.points.size()); 76 | } 77 | if (in.enable_nodal_energy[material]) { 78 | s.p_h[material].resize(s.nodes.size()); 79 | s.e_h[material].resize(s.nodes.size()); 80 | s.e_h_dot[material].resize(s.nodes.size()); 81 | s.rho_h[material].resize(s.nodes.size()); 82 | s.K_h[material].resize(s.nodes.size()); 83 | s.q.resize(s.points.size()); 84 | s.W.resize(s.points.size() * s.nodes_in_element.size()); 85 | s.dp_de_h[material].resize(s.nodes.size()); 86 | } 87 | } 88 | s.material.resize(s.elements.size()); 89 | if (in.enable_adapt) { 90 | s.quality.resize(s.elements.size()); 91 | s.h_adapt.resize(s.nodes.size()); 92 | } 93 | } 94 | 95 | } // namespace lgr 96 | -------------------------------------------------------------------------------- /lgr_tetrahedron.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace lgr { 7 | 8 | void 9 | initialize_tetrahedron_V(state& s) 10 | { 11 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 12 | auto const nodes_to_x = s.x.cbegin(); 13 | auto const points_to_V = s.V.begin(); 14 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 15 | auto const elements_to_points = s.elements * s.points_in_element; 16 | auto functor = [=] HPC_DEVICE(element_index const element) { 17 | constexpr point_in_element_index fp(0); 18 | auto const element_nodes = elements_to_element_nodes[element]; 19 | using l_t = node_in_element_index; 20 | hpc::array, 4> x; 21 | for (int i = 0; i < 4; ++i) { 22 | auto const node = element_nodes_to_nodes[element_nodes[l_t(i)]]; 23 | x[i] = nodes_to_x[node].load(); 24 | } 25 | auto const volume = tetrahedron_volume(x); 26 | assert(volume > 0.0); 27 | points_to_V[elements_to_points[element][fp]] = volume; 28 | }; 29 | hpc::for_each(hpc::device_policy(), s.elements, functor); 30 | } 31 | 32 | void 33 | initialize_tetrahedron_grad_N(state& s) 34 | { 35 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 36 | auto const nodes_to_x = s.x.cbegin(); 37 | auto const points_to_V = s.V.cbegin(); 38 | auto const point_nodes_to_grad_N = s.grad_N.begin(); 39 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 40 | auto const elements_to_points = s.elements * s.points_in_element; 41 | auto const points_to_point_nodes = s.points * s.nodes_in_element; 42 | auto functor = [=] HPC_DEVICE(element_index const element) { 43 | constexpr point_in_element_index fp(0); 44 | auto const element_nodes = elements_to_element_nodes[element]; 45 | auto const point = elements_to_points[element][fp]; 46 | auto const point_nodes = points_to_point_nodes[point]; 47 | using l_t = node_in_element_index; 48 | hpc::array, 4> x; 49 | for (int i = 0; i < 4; ++i) { 50 | auto const node = element_nodes_to_nodes[element_nodes[l_t(i)]]; 51 | x[i] = nodes_to_x[node].load(); 52 | } 53 | auto const volume = points_to_V[point]; 54 | auto const grad_N = tetrahedron_basis_gradients(x, volume); 55 | for (int i = 0; i < 4; ++i) { 56 | point_nodes_to_grad_N[point_nodes[l_t(i)]] = grad_N[i]; 57 | } 58 | }; 59 | hpc::for_each(hpc::device_policy(), s.elements, functor); 60 | } 61 | 62 | void 63 | update_tetrahedron_h_min_inball(input const&, state& s) 64 | { 65 | auto const point_nodes_to_grad_N = s.grad_N.cbegin(); 66 | auto const elements_to_h_min = s.h_min.begin(); 67 | auto const points_to_point_nodes = s.points * s.nodes_in_element; 68 | auto const nodes_in_element = s.nodes_in_element; 69 | auto const elements_to_points = s.elements * s.points_in_element; 70 | auto functor = [=] HPC_DEVICE(element_index const element) { 71 | /* find the radius of the inscribed sphere. 72 | first fun fact: the volume of a tetrahedron equals one third 73 | times the radius of the inscribed sphere times the surface area 74 | of the tetrahedron, where the surface area is the sum of its 75 | face areas. 76 | second fun fact: the magnitude of the gradient of the basis function 77 | of a tetrahedron's node is equal to the area of the opposite face 78 | divided by thrice the tetrahedron volume 79 | third fun fact: when solving for the radius, volume cancels out 80 | of the top and bottom of the division. 81 | */ 82 | constexpr point_in_element_index fp(0); 83 | auto const point = elements_to_points[element][fp]; 84 | auto const point_nodes = points_to_point_nodes[point]; 85 | decltype(hpc::area() / hpc::volume()) surface_area_over_thrice_volume = 0.0; 86 | for (auto const i : nodes_in_element) { 87 | auto const grad_N = point_nodes_to_grad_N[point_nodes[i]].load(); 88 | auto const face_area_over_thrice_volume = norm(grad_N); 89 | surface_area_over_thrice_volume += face_area_over_thrice_volume; 90 | } 91 | auto const radius = 1.0 / surface_area_over_thrice_volume; 92 | elements_to_h_min[element] = 2.0 * radius; 93 | }; 94 | hpc::for_each(hpc::device_policy(), s.elements, functor); 95 | } 96 | 97 | void 98 | update_tetrahedron_h_art(state& s) 99 | { 100 | double const C_geom = std::cbrt(12.0 / std::sqrt(2.0)); 101 | auto const points_to_V = s.V.cbegin(); 102 | auto const elements_to_h_art = s.h_art.begin(); 103 | auto const elements_to_points = s.elements * s.points_in_element; 104 | auto functor = [=] HPC_DEVICE(element_index const element) { 105 | hpc::volume volume = 0.0; 106 | for (auto const point : elements_to_points[element]) { 107 | volume += points_to_V[point]; 108 | } 109 | auto const h_art = C_geom * cbrt(volume); 110 | elements_to_h_art[element] = h_art; 111 | }; 112 | hpc::for_each(hpc::device_policy(), s.elements, functor); 113 | } 114 | 115 | } // namespace lgr 116 | -------------------------------------------------------------------------------- /lgr_tetrahedron.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class state; 6 | class input; 7 | 8 | void 9 | initialize_tetrahedron_V(state& s); 10 | void 11 | initialize_tetrahedron_grad_N(state& s); 12 | void 13 | update_tetrahedron_h_min_inball(input const&, state& s); 14 | void 15 | update_tetrahedron_h_art(state& s); 16 | 17 | } // namespace lgr 18 | -------------------------------------------------------------------------------- /lgr_triangle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace lgr { 7 | 8 | void 9 | initialize_triangle_V(state& s) 10 | { 11 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 12 | auto const nodes_to_x = s.x.cbegin(); 13 | auto const points_to_V = s.V.begin(); 14 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 15 | auto const elements_to_points = s.elements * s.points_in_element; 16 | auto functor = [=] HPC_DEVICE(element_index const element) { 17 | constexpr point_in_element_index fp(0); 18 | auto const element_nodes = elements_to_element_nodes[element]; 19 | using l_t = node_in_element_index; 20 | hpc::array nodes; 21 | hpc::array, 3> x; 22 | for (int i = 0; i < 3; ++i) { 23 | auto const node = element_nodes_to_nodes[element_nodes[l_t(i)]]; 24 | nodes[i] = node; 25 | x[i] = nodes_to_x[node].load(); 26 | } 27 | auto const area = triangle_area(x); 28 | assert(area > 0.0); 29 | auto const volume = area * hpc::length(1.0); 30 | points_to_V[elements_to_points[element][fp]] = volume; 31 | }; 32 | hpc::for_each(hpc::device_policy(), s.elements, functor); 33 | } 34 | 35 | void 36 | initialize_triangle_grad_N(state& s) 37 | { 38 | auto const element_nodes_to_nodes = s.elements_to_nodes.cbegin(); 39 | auto const nodes_to_x = s.x.cbegin(); 40 | auto const points_to_V = s.V.cbegin(); 41 | auto const point_nodes_to_grad_N = s.grad_N.begin(); 42 | auto const elements_to_element_nodes = s.elements * s.nodes_in_element; 43 | auto const points_to_point_nodes = s.points * s.nodes_in_element; 44 | auto const elements_to_points = s.elements * s.points_in_element; 45 | auto functor = [=] HPC_DEVICE(element_index const element) { 46 | constexpr point_in_element_index fp(0); 47 | auto const element_nodes = elements_to_element_nodes[element]; 48 | auto const point = elements_to_points[element][fp]; 49 | auto const point_nodes = points_to_point_nodes[point]; 50 | using l_t = node_in_element_index; 51 | hpc::array, 3> x; 52 | for (int i = 0; i < 3; ++i) { 53 | auto const node = element_nodes_to_nodes[element_nodes[l_t(i)]]; 54 | x[i] = nodes_to_x[node].load(); 55 | } 56 | auto const volume = points_to_V[point]; 57 | auto const area = volume / hpc::length(1.0); 58 | auto const grad_N = triangle_basis_gradients(x, area); 59 | for (int i = 0; i < 3; ++i) { 60 | point_nodes_to_grad_N[point_nodes[l_t(i)]] = grad_N[i]; 61 | } 62 | }; 63 | hpc::for_each(hpc::device_policy(), s.elements, functor); 64 | } 65 | 66 | void 67 | update_triangle_h_min_inball(input const&, state& s) 68 | { 69 | auto const point_nodes_to_grad_N = s.grad_N.cbegin(); 70 | auto const elements_to_h_min = s.h_min.begin(); 71 | auto const points_to_point_nodes = s.points * s.nodes_in_element; 72 | auto const nodes_in_element = s.nodes_in_element; 73 | auto const elements_to_points = s.elements * s.points_in_element; 74 | auto functor = [=] HPC_DEVICE(element_index const element) { 75 | /* find the radius of the inscribed circle. 76 | first fun fact: the area of a triangle equals one half 77 | times the radius of the inscribed circle times the perimeter 78 | of the triangle, where the perimeter is the sum of its 79 | edge lengths. 80 | second fun fact: the magnitude of the gradient of the basis function 81 | of a triangle's node is equal to the length of the opposite edge 82 | divided by twice the triangle area 83 | third fun fact: when solving for the radius, area cancels out 84 | of the top and bottom of the division. 85 | */ 86 | constexpr point_in_element_index fp(0); 87 | auto const point = elements_to_points[element][fp]; 88 | auto const point_nodes = points_to_point_nodes[point]; 89 | decltype(hpc::length() / hpc::area()) perimeter_over_twice_area = 0.0; 90 | for (auto const i : nodes_in_element) { 91 | auto const grad_N = point_nodes_to_grad_N[point_nodes[i]].load(); 92 | auto const edge_length_over_twice_area = norm(grad_N); 93 | perimeter_over_twice_area += edge_length_over_twice_area; 94 | } 95 | auto const radius = 1.0 / perimeter_over_twice_area; 96 | elements_to_h_min[element] = 2.0 * radius; 97 | }; 98 | hpc::for_each(hpc::device_policy(), s.elements, functor); 99 | } 100 | 101 | void 102 | update_triangle_h_art(state& s) 103 | { 104 | double const C_geom = std::sqrt(4.0 / std::sqrt(3.0)); 105 | auto const points_to_V = s.V.cbegin(); 106 | auto const elements_to_h_art = s.h_art.begin(); 107 | auto const elements_to_points = s.elements * s.points_in_element; 108 | auto functor = [=] HPC_DEVICE(element_index const element) { 109 | hpc::area area = 0.0; 110 | for (auto const point : elements_to_points[element]) { 111 | area += (points_to_V[point] / hpc::length(1.0)); 112 | } 113 | auto const h_art = C_geom * sqrt(area); 114 | elements_to_h_art[element] = h_art; 115 | }; 116 | hpc::for_each(hpc::device_policy(), s.elements, functor); 117 | } 118 | 119 | } // namespace lgr 120 | -------------------------------------------------------------------------------- /lgr_triangle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class state; 6 | class input; 7 | 8 | void 9 | initialize_triangle_V(state& s); 10 | void 11 | initialize_triangle_grad_N(state& s); 12 | void 13 | update_triangle_h_min_inball(input const&, state& s); 14 | void 15 | update_triangle_h_art(state& s); 16 | 17 | } // namespace lgr 18 | -------------------------------------------------------------------------------- /lgr_vector4.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr { 6 | 7 | template 8 | class vector4 9 | { 10 | public: 11 | using scalar_type = Scalar; 12 | 13 | private: 14 | scalar_type raw[4]; 15 | 16 | public: 17 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE explicit constexpr vector4( 18 | Scalar const x, 19 | Scalar const y, 20 | Scalar const z, 21 | Scalar const w) noexcept 22 | : raw{x, y, z, w} 23 | { 24 | } 25 | HPC_ALWAYS_INLINE 26 | vector4() noexcept = default; 27 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr scalar_type 28 | operator()(int const i) const noexcept 29 | { 30 | return raw[i]; 31 | } 32 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE scalar_type& 33 | operator()(int const i) noexcept 34 | { 35 | return raw[i]; 36 | } 37 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE static constexpr vector4 38 | zero() noexcept 39 | { 40 | return vector4(0.0, 0.0, 0.0, 0.0); 41 | } 42 | }; 43 | 44 | template 45 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr Scalar 46 | inner_product(vector4 const left, vector4 const right) noexcept 47 | { 48 | return left(0) * right(0) + left(1) * right(1) + left(2) * right(2) + left(3) * right(3); 49 | } 50 | 51 | template 52 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE constexpr Scalar 53 | operator*(vector4 const left, vector4 const right) noexcept 54 | { 55 | return inner_product(left, right); 56 | } 57 | 58 | } // namespace lgr 59 | -------------------------------------------------------------------------------- /lgr_vtk.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lgr { 9 | 10 | class input; 11 | class state; 12 | 13 | class captured_state 14 | { 15 | public: 16 | hpc::counting_range elements{0}; 17 | hpc::counting_range nodes{0}; 18 | hpc::counting_range nodes_in_element{0}; 19 | hpc::counting_range points_in_element{0}; 20 | hpc::pinned_vector element_nodes_to_nodes; 21 | hpc::pinned_array_vector, node_index> x; 22 | hpc::pinned_array_vector, node_index> v; 23 | hpc::pinned_array_vector, node_index> sigma; 24 | hpc::pinned_array_vector, node_index> sigma_full; 25 | hpc::pinned_array_vector, node_index> F_total; 26 | hpc::pinned_array_vector, node_index> Fp_total; 27 | hpc::host_vector, node_index>, material_index> p_h; 28 | hpc::host_vector, node_index>, material_index> e_h; 29 | hpc::host_vector, node_index>, material_index> rho_h; 30 | hpc::pinned_vector, node_index> h_adapt; 31 | hpc::pinned_vector, point_index> p; 32 | hpc::pinned_vector, point_index> K; 33 | hpc::pinned_vector, point_index> c; 34 | hpc::pinned_vector, point_index> e; 35 | hpc::pinned_vector, point_index> rho; 36 | hpc::pinned_vector, point_index> ep; 37 | hpc::pinned_array_vector, point_index> q; 38 | hpc::pinned_vector, point_index> p_prime; 39 | hpc::pinned_vector, point_index> element_dt; 40 | hpc::pinned_vector, element_index> quality; 41 | hpc::pinned_vector material; 42 | }; 43 | 44 | class file_writer 45 | { 46 | std::string prefix; 47 | 48 | public: 49 | file_writer(std::string const& prefix_in) : prefix(prefix_in) 50 | { 51 | } 52 | void 53 | capture(input const& in, state const& s); 54 | void 55 | write(input const& in, int const file_output_index); 56 | captured_state captured; 57 | }; 58 | 59 | } // namespace lgr 60 | -------------------------------------------------------------------------------- /lgr_vtk_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace lgr { 16 | 17 | inline std::ofstream 18 | make_vtk_output_stream(const std::string& prefix, int const file_output_index) 19 | { 20 | std::stringstream filename_stream; 21 | filename_stream << prefix << "_" << file_output_index << ".vtk"; 22 | std::ofstream stream(filename_stream.str().c_str()); 23 | stream << std::scientific << std::setprecision(17); 24 | return stream; 25 | } 26 | 27 | inline void 28 | start_vtk_unstructured_grid_file(std::ostream& stream) 29 | { 30 | stream << "# vtk DataFile Version 3.0\n"; 31 | stream << "vtk output\n"; 32 | stream << "ASCII\n"; 33 | stream << "DATASET UNSTRUCTURED_GRID\n"; 34 | } 35 | 36 | template 37 | inline void 38 | write_vtk_point_data(std::ostream& stream, hpc::counting_range const& point_range) 39 | { 40 | stream << "POINT_DATA " << point_range.size() << "\n"; 41 | } 42 | 43 | template 44 | inline void 45 | write_vtk_points(std::ostream& stream, hpc::pinned_array_vector, Index> const& x) 46 | { 47 | stream << "POINTS " << x.size() << " double\n"; 48 | for (auto ref : x) { 49 | stream << hpc::vector3(ref.load()) << "\n"; 50 | } 51 | } 52 | 53 | template 54 | inline void 55 | write_vtk_full_tensors( 56 | std::ostream& stream, 57 | std::string const& name, 58 | hpc::pinned_array_vector, Index> const& tensor) 59 | { 60 | stream << "SCALARS " << name << " double 9\n"; 61 | stream << "LOOKUP_TABLE default\n"; 62 | for (auto const ref : tensor) { 63 | stream << hpc::matrix3x3(ref.load()) << "\n"; 64 | } 65 | } 66 | 67 | template 68 | inline void 69 | write_vtk_sym_tensors( 70 | std::ostream& stream, 71 | char const* name, 72 | hpc::pinned_array_vector, Index> const& tensor) 73 | { 74 | stream << "TENSORS " << name << " double\n"; 75 | for (auto const ref : tensor) { 76 | stream << hpc::symmetric3x3(ref.load()) << "\n"; 77 | } 78 | } 79 | 80 | template 81 | inline void 82 | write_vtk_vectors( 83 | std::ostream& stream, 84 | char const* name, 85 | hpc::pinned_array_vector, Index> const& vec) 86 | { 87 | stream << "VECTORS " << name << " double\n"; 88 | for (auto const ref : vec) { 89 | stream << hpc::vector3(ref.load()) << "\n"; 90 | } 91 | } 92 | 93 | template 94 | inline void 95 | write_vtk_scalars(std::ostream& stream, std::string const& name, hpc::pinned_vector const& vec) 96 | { 97 | stream << "SCALARS " << name << " double 1\n"; 98 | stream << "LOOKUP_TABLE default\n"; 99 | for (Quantity const val : vec) { 100 | stream << double(val) << "\n"; 101 | } 102 | } 103 | 104 | } // namespace lgr 105 | -------------------------------------------------------------------------------- /otm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int 7 | main() 8 | { 9 | HPC_TRAP_FPE(); 10 | lgr::otm_scope scope; 11 | if ((0)) lgr::otm_j2_nu_zero_patch_test(); 12 | if ((0)) lgr::otm_j2_uniaxial_patch_test(); 13 | if ((1)) lgr::otm_taylor(); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /otm_apps.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | struct otm_scope 6 | { 7 | HPC_NOINLINE HPC_HOST 8 | otm_scope(); 9 | HPC_NOINLINE HPC_HOST ~otm_scope(); 10 | }; 11 | 12 | bool 13 | otm_j2_uniaxial_patch_test(); 14 | bool 15 | otm_j2_nu_zero_patch_test(); 16 | bool 17 | otm_taylor(); 18 | bool 19 | otm_rmi(); 20 | 21 | } // namespace lgr 22 | -------------------------------------------------------------------------------- /otm_arborx_search_impl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace lgr { 16 | namespace search { 17 | namespace arborx { 18 | 19 | using device_range = Kokkos::RangePolicy; 20 | using device_bvh = ArborX::BoundingVolumeHierarchy; 21 | 22 | using Kokkos::fence; 23 | using Kokkos::parallel_for; 24 | 25 | template 26 | HPC_NOINLINE void 27 | do_search( 28 | const device_point_view& nodes, 29 | const query_view_type& queries, 30 | device_int_view& indices, 31 | device_int_view& offsets) 32 | { 33 | device_bvh bvh(nodes); 34 | bvh.query(queries, indices, offsets); 35 | } 36 | 37 | HPC_NOINLINE device_nearest_query_view 38 | make_nearest_node_queries(const device_point_view& points, const int num_nodes_to_find) 39 | { 40 | const int numQueries = points.extent(0); 41 | device_nearest_query_view queries(Kokkos::ViewAllocateWithoutInitializing("queries"), numQueries); 42 | 43 | parallel_for( 44 | "setup_queries", device_range(0, numQueries), KOKKOS_LAMBDA(int i) { 45 | queries(i) = ArborX::nearest(points(i), num_nodes_to_find); 46 | }); 47 | fence(); 48 | return queries; 49 | } 50 | 51 | HPC_NOINLINE device_intersects_query_view 52 | make_intersect_sphere_queries(const device_sphere_view& point_spheres) 53 | { 54 | const int numQueries = point_spheres.extent(0); 55 | device_intersects_query_view queries(Kokkos::ViewAllocateWithoutInitializing("queries"), numQueries); 56 | 57 | parallel_for( 58 | "setup_queries", device_range(0, numQueries), KOKKOS_LAMBDA(int i) { 59 | queries(i) = ArborX::intersects(point_spheres(i)); 60 | }); 61 | fence(); 62 | return queries; 63 | } 64 | 65 | template 66 | HPC_NOINLINE device_point_view 67 | make_point_view( 68 | const std::string& view_name, 69 | const hpc::counting_range& lgr_points, 70 | const hpc::device_array_vector, idx_type>& coords) 71 | { 72 | device_point_view search_points(view_name, lgr_points.size()); 73 | auto points_to_x = coords.cbegin(); 74 | device_range point_range(0, search_points.extent(0)); 75 | parallel_for( 76 | point_range, KOKKOS_LAMBDA(int i) { 77 | auto&& search_node = search_points(i); 78 | auto&& lgr_node_coord = points_to_x[idx_type(i)].load(); 79 | search_node[0] = lgr_node_coord(0); 80 | search_node[1] = lgr_node_coord(1); 81 | search_node[2] = lgr_node_coord(2); 82 | }); 83 | fence(); 84 | return search_points; 85 | } 86 | 87 | template 88 | HPC_NOINLINE device_sphere_view 89 | make_sphere_view( 90 | const std::string& view_name, 91 | const hpc::counting_range& lgr_points, 92 | const hpc::device_array_vector, idx_type>& coords, 93 | const hpc::device_vector, idx_type>& radii) 94 | { 95 | using sphere = ArborX::Sphere; 96 | device_sphere_view search_spheres(view_name, lgr_points.size()); 97 | auto points_to_x = coords.cbegin(); 98 | auto points_to_r = radii.cbegin(); 99 | device_range point_range(0, search_spheres.extent(0)); 100 | parallel_for( 101 | point_range, KOKKOS_LAMBDA(int i) { 102 | auto&& search_sphere = search_spheres(i); 103 | auto&& coords = points_to_x[idx_type(i)].load(); 104 | auto&& radius = points_to_r[idx_type(i)]; 105 | search_sphere = sphere({{coords(0), coords(1), coords(2)}}, radius); 106 | }); 107 | fence(); 108 | return search_spheres; 109 | } 110 | 111 | HPC_NOINLINE device_point_view 112 | create_arborx_nodes(const lgr::state& s) 113 | { 114 | return make_point_view("nodes", s.nodes, s.x); 115 | } 116 | 117 | HPC_NOINLINE device_point_view 118 | create_arborx_points(const lgr::state& s) 119 | { 120 | return make_point_view("points", s.points, s.xp); 121 | } 122 | 123 | HPC_NOINLINE device_sphere_view 124 | create_arborx_point_spheres(const lgr::state& s) 125 | { 126 | return make_sphere_view("point_spheres", s.points, s.xp, s.h_otm); 127 | } 128 | 129 | void 130 | inflate_sphere_query_radii(device_intersects_query_view queries, double factor) 131 | { 132 | device_range r(0, queries.extent(0)); 133 | parallel_for( 134 | r, KOKKOS_LAMBDA(int i) { 135 | auto&& query = queries(i); 136 | auto&& sphere = query._geometry; 137 | sphere._radius *= factor; 138 | }); 139 | fence(); 140 | } 141 | 142 | void 143 | initialize() 144 | { 145 | if (!Kokkos::is_initialized()) Kokkos::initialize(); 146 | } 147 | 148 | void 149 | finalize() 150 | { 151 | if (Kokkos::is_initialized()) Kokkos::finalize(); 152 | } 153 | 154 | template void 155 | do_search( 156 | const device_point_view& nodes, 157 | const device_nearest_query_view& queries, 158 | device_int_view& indices, 159 | device_int_view& offsets); 160 | template void 161 | do_search( 162 | const device_point_view& nodes, 163 | const device_intersects_query_view& queries, 164 | device_int_view& indices, 165 | device_int_view& offsets); 166 | 167 | } // namespace arborx 168 | } // namespace search 169 | } // namespace lgr 170 | -------------------------------------------------------------------------------- /otm_arborx_search_impl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __clang__ 4 | #pragma clang diagnostic push 5 | #pragma clang diagnostic ignored "-Wunused-parameter" 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __clang__ 12 | #pragma clang diagnostic pop 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | namespace lgr { 19 | class state; 20 | } 21 | 22 | namespace ArborX { 23 | struct Box; 24 | class Point; 25 | struct Sphere; 26 | template 27 | struct Nearest; 28 | template 29 | struct Intersects; 30 | } // namespace ArborX 31 | 32 | namespace lgr { 33 | namespace search { 34 | namespace arborx { 35 | 36 | using device_exec_space = Kokkos::DefaultExecutionSpace; 37 | #ifdef KOKKOS_ENABLE_CUDA 38 | using device_mem_space = Kokkos::CudaSpace; 39 | #else 40 | using device_mem_space = device_exec_space::memory_space; 41 | #endif 42 | using device_type = Kokkos::Device; 43 | using device_point_view = Kokkos::View; 44 | using device_sphere_view = Kokkos::View; 45 | using device_nearest_query_view = Kokkos::View*, device_type>; 46 | using device_intersects_query_view = Kokkos::View*, device_type>; 47 | using device_int_view = Kokkos::View; 48 | 49 | void 50 | initialize(); 51 | 52 | void 53 | finalize(); 54 | 55 | device_point_view 56 | create_arborx_nodes(const lgr::state& s); 57 | 58 | device_point_view 59 | create_arborx_points(const lgr::state& s); 60 | 61 | device_sphere_view 62 | create_arborx_point_spheres(const lgr::state& s); 63 | 64 | device_nearest_query_view 65 | make_nearest_node_queries(const device_point_view& points, const int num_nodes_to_find); 66 | 67 | device_intersects_query_view 68 | make_intersect_sphere_queries(const device_sphere_view& point_spheres); 69 | 70 | void 71 | inflate_sphere_query_radii(device_intersects_query_view queries, double factor); 72 | 73 | template 74 | void 75 | do_search( 76 | const device_point_view& nodes, 77 | const query_view_type& queries, 78 | device_int_view& indices, 79 | device_int_view& offsets); 80 | 81 | } // namespace arborx 82 | } // namespace search 83 | } // namespace lgr 84 | -------------------------------------------------------------------------------- /otm_distance.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace lgr { 8 | namespace search_util { 9 | 10 | template 11 | void 12 | compute_connected_neighbor_squared_distances( 13 | const hpc::counting_range& indices, 14 | const hpc::device_array_vector, Index>& positions, 15 | const nearest_neighbors& n, 16 | hpc::device_vector, Index>& squared_distances) 17 | { 18 | auto x = positions.cbegin(); 19 | auto total_neighbors = n.entities_to_neighbors.size(); 20 | squared_distances.resize(total_neighbors); 21 | auto neighbor_ranges = n.entities_to_neighbor_ordinals.cbegin(); 22 | auto neighbors = n.entities_to_neighbors.cbegin(); 23 | auto distances = squared_distances.begin(); 24 | auto distance_func = [=] HPC_DEVICE(const Index i) { 25 | auto x_i = x[i].load(); 26 | for (auto neighbor_ord : neighbor_ranges[i]) { 27 | auto neighbor = neighbors[neighbor_ord]; 28 | auto x_neighbor = x[neighbor].load(); 29 | distances[neighbor_ord] = hpc::norm_squared(x_i - x_neighbor); 30 | } 31 | }; 32 | hpc::for_each(hpc::device_policy(), indices, distance_func); 33 | } 34 | 35 | template void 36 | compute_connected_neighbor_squared_distances( 37 | const hpc::counting_range& indices, 38 | const hpc::device_array_vector, node_index>& positions, 39 | const search_util::nearest_neighbors& n, 40 | hpc::device_vector, node_index>& squared_distances); 41 | 42 | #ifdef HPC_ENABLE_STRONG_INDICES 43 | template void 44 | compute_connected_neighbor_squared_distances( 45 | const hpc::counting_range& indices, 46 | const hpc::device_array_vector, point_index>& positions, 47 | const search_util::nearest_neighbors& n, 48 | hpc::device_vector, point_index>& squared_distances); 49 | #endif 50 | 51 | } // namespace search_util 52 | } // namespace lgr 53 | -------------------------------------------------------------------------------- /otm_distance.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace lgr { 12 | namespace search_util { 13 | 14 | template 15 | void 16 | compute_connected_neighbor_squared_distances( 17 | const hpc::counting_range& indices, 18 | const hpc::device_array_vector, Index>& positions, 19 | const search_util::nearest_neighbors& n, 20 | hpc::device_vector, Index>& squared_distances); 21 | 22 | inline void 23 | compute_node_neighbor_squared_distances( 24 | const state& s, 25 | const search_util::node_neighbors& n, 26 | hpc::device_vector, node_index>& nodes_to_neighbor_squared_distances) 27 | { 28 | compute_connected_neighbor_squared_distances(s.nodes, s.x, n, nodes_to_neighbor_squared_distances); 29 | } 30 | 31 | inline void 32 | compute_point_neighbor_squared_distances( 33 | const state& s, 34 | const search_util::point_neighbors& n, 35 | hpc::device_vector, point_index>& points_to_neighbor_squared_distances) 36 | { 37 | compute_connected_neighbor_squared_distances(s.points, s.xp, n, points_to_neighbor_squared_distances); 38 | } 39 | 40 | } // namespace search_util 41 | } // namespace lgr 42 | -------------------------------------------------------------------------------- /otm_distance_util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace lgr { 18 | 19 | template 20 | HPC_NOINLINE inline void 21 | compute_sqrt_nearest_neighbor_distances( 22 | const hpc::counting_range& range, 23 | hpc::device_vector, Index>& dists) 24 | { 25 | assert(dists.size() == range.size()); 26 | auto const neighbor_distances = dists.begin(); 27 | auto dist_func = [=] HPC_DEVICE(Index const i) { 28 | auto const dist = std::sqrt(neighbor_distances[i]); 29 | neighbor_distances[i] = dist; 30 | }; 31 | hpc::for_each(hpc::device_policy(), range, dist_func); 32 | } 33 | 34 | template 35 | HPC_NOINLINE inline void 36 | fill_nearest_neighbors( 37 | const hpc::counting_range& range, 38 | const search_util::nearest_neighbors& neighbors, 39 | hpc::device_vector& nearest_neighbor) 40 | { 41 | assert(nearest_neighbor.size() == range.size()); 42 | assert(neighbors.entities_to_neighbors.size() == range.size()); 43 | auto const result = nearest_neighbor.begin(); 44 | auto const nearest_ordinal = neighbors.entities_to_neighbor_ordinals.cbegin(); 45 | auto const nearest = neighbors.entities_to_neighbors.cbegin(); 46 | auto dist_func = [=] HPC_DEVICE(Index const i) { 47 | auto const nearest_ord = nearest_ordinal[i]; 48 | assert(nearest_ord.size() == 1); 49 | result[i] = nearest[nearest_ord[0]]; 50 | }; 51 | hpc::for_each(hpc::device_policy(), range, dist_func); 52 | } 53 | 54 | void 55 | otm_update_nearest_point_neighbor_distances(state& s) 56 | { 57 | hpc::fill(hpc::device_policy(), s.nearest_point_neighbor_dist, -1.0); 58 | hpc::fill(hpc::device_policy(), s.nearest_point_neighbor, point_index(-1)); 59 | search_util::point_neighbors n; 60 | search::do_otm_point_nearest_point_search(s, n, 1); 61 | 62 | if (n.entities_to_neighbors.empty()) return; 63 | 64 | search_util::compute_point_neighbor_squared_distances(s, n, s.nearest_point_neighbor_dist); 65 | compute_sqrt_nearest_neighbor_distances(s.points, s.nearest_point_neighbor_dist); 66 | fill_nearest_neighbors(s.points, n, s.nearest_point_neighbor); 67 | } 68 | 69 | void 70 | otm_update_nearest_node_neighbor_distances(state& s) 71 | { 72 | hpc::fill(hpc::device_policy(), s.nearest_node_neighbor_dist, -1.0); 73 | hpc::fill(hpc::device_policy(), s.nearest_node_neighbor, node_index(-1)); 74 | search_util::node_neighbors n; 75 | search::do_otm_node_nearest_node_search(s, n, 1); 76 | 77 | if (n.entities_to_neighbors.empty()) return; 78 | 79 | search_util::compute_node_neighbor_squared_distances(s, n, s.nearest_node_neighbor_dist); 80 | compute_sqrt_nearest_neighbor_distances(s.nodes, s.nearest_node_neighbor_dist); 81 | fill_nearest_neighbors(s.nodes, n, s.nearest_node_neighbor); 82 | } 83 | 84 | void 85 | otm_update_min_nearest_neighbor_distances(state& s) 86 | { 87 | // TODO compute a relative change in distance as a metric...average? 88 | hpc::length const init = std::numeric_limits::max(); 89 | s.min_node_neighbor_dist = hpc::transform_reduce( 90 | hpc::device_policy(), 91 | s.nearest_node_neighbor_dist, 92 | init, 93 | hpc::minimum>(), 94 | hpc::identity>()); 95 | s.min_point_neighbor_dist = hpc::transform_reduce( 96 | hpc::device_policy(), 97 | s.nearest_point_neighbor_dist, 98 | init, 99 | hpc::minimum>(), 100 | hpc::identity>()); 101 | } 102 | 103 | } // namespace lgr 104 | -------------------------------------------------------------------------------- /otm_distance_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | class state; 5 | } 6 | 7 | namespace lgr { 8 | 9 | void 10 | otm_update_nearest_point_neighbor_distances(state& s); 11 | void 12 | otm_update_nearest_node_neighbor_distances(state& s); 13 | void 14 | otm_update_min_nearest_neighbor_distances(state& s); 15 | 16 | } // namespace lgr 17 | -------------------------------------------------------------------------------- /otm_host_pinned_state.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace lgr { 10 | 11 | template 12 | inline void 13 | resize_state_arrays(state_type& state, node_index const num_nodes, point_index const num_points) 14 | { 15 | state.x.resize(num_nodes); 16 | state.xp.resize(num_points); 17 | 18 | state.point_nodes_to_nodes.resize(num_points * num_nodes); 19 | state.node_points_to_points.resize(num_nodes * num_points); 20 | state.node_points_to_point_nodes.resize(num_nodes * num_points); 21 | 22 | state.V.resize(num_points); 23 | state.rho.resize(num_points); 24 | } 25 | 26 | struct otm_host_pinned_state 27 | { 28 | hpc::pinned_array_vector, node_index> x; 29 | hpc::pinned_array_vector, point_index> xp; 30 | 31 | hpc::pinned_vector point_nodes_to_nodes; 32 | hpc::pinned_vector node_points_to_points; 33 | hpc::pinned_vector node_points_to_point_nodes; 34 | 35 | hpc::pinned_vector, point_index> V; 36 | hpc::pinned_vector, point_index> rho; 37 | }; 38 | 39 | } // namespace lgr 40 | -------------------------------------------------------------------------------- /otm_meshing.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace lgr { 15 | 16 | void 17 | invert_otm_point_node_relations(lgr::state& s) 18 | { 19 | auto points_to_point_nodes = s.points_to_point_nodes.cbegin(); 20 | auto point_nodes_to_nodes = s.point_nodes_to_nodes.cbegin(); 21 | hpc::device_vector node_point_counts(s.nodes.size(), 0); 22 | auto counts = node_point_counts.begin(); 23 | auto point_count_functor = [=] HPC_DEVICE(point_index const point) { 24 | auto point_node_range = points_to_point_nodes[point]; 25 | for (auto point_node : point_node_range) { 26 | auto node = point_nodes_to_nodes[point_node]; 27 | hpc::atomic_ref count(counts[node]); 28 | count++; 29 | } 30 | }; 31 | hpc::for_each(hpc::device_policy(), s.points, point_count_functor); 32 | auto total_node_points = hpc::reduce(hpc::device_policy(), node_point_counts, 0); 33 | 34 | s.node_points_to_points.resize(node_point_index(total_node_points)); 35 | s.node_points_to_point_nodes.resize(node_point_index(total_node_points)); 36 | s.nodes_to_node_points.assign_sizes(node_point_counts); 37 | hpc::fill(hpc::device_policy(), node_point_counts, 0); 38 | auto node_points_to_points = s.node_points_to_points.begin(); 39 | auto nodes_to_node_points = s.nodes_to_node_points.cbegin(); 40 | auto node_points_to_point_nodes = s.node_points_to_point_nodes.begin(); 41 | auto node_point_fill_functor = [=] HPC_DEVICE(point_index const point) { 42 | auto const point_nodes = points_to_point_nodes[point]; 43 | for (auto ni = 0; ni < point_nodes.size(); ++ni) { 44 | const auto point_node = point_nodes[ni]; 45 | node_index const node = point_nodes_to_nodes[point_node]; 46 | hpc::atomic_ref count(counts[node]); 47 | int const offset = count++; 48 | auto const node_points_range = nodes_to_node_points[node]; 49 | auto const node_point = node_points_range[point_node_index(offset)]; 50 | node_points_to_points[node_point] = point; 51 | node_points_to_point_nodes[node_point] = ni; 52 | } 53 | }; 54 | 55 | hpc::for_each(hpc::device_policy(), s.points, node_point_fill_functor); 56 | 57 | otm_sort_node_relations(s, s.nodes_to_node_points, s.node_points_to_points, s.node_points_to_point_nodes); 58 | } 59 | 60 | } // namespace lgr 61 | -------------------------------------------------------------------------------- /otm_meshing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | class state; 5 | } 6 | 7 | namespace lgr { 8 | 9 | void 10 | invert_otm_point_node_relations(lgr::state& s); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /otm_meshing_sort.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace lgr { 11 | 12 | template 13 | void 14 | otm_sort_node_relations( 15 | lgr::state& s, 16 | NodeRelRangeType& nodes_to_node_relations, 17 | NodeRelToRelRangeType& node_relations_to_relation_indices, 18 | NodeRelToNodesOfRelRangeType& node_relations_to_nodes_of_relation) 19 | { 20 | auto node_rels = nodes_to_node_relations.cbegin(); 21 | auto node_rel_to_rel = node_relations_to_relation_indices.begin(); 22 | auto node_rel_to_nodes_of_node_rel = node_relations_to_nodes_of_relation.begin(); 23 | auto sort_functor = [=] HPC_DEVICE(node_index const node) { 24 | auto const this_node_rel = node_rels[node]; 25 | typename NodeRelRangeType::value_type const except_last(this_node_rel.begin(), this_node_rel.end() - 1); 26 | for (auto const nr : except_last) { 27 | typename NodeRelRangeType::value_type const remaining(nr + 1, this_node_rel.end()); 28 | auto min_rel(node_rel_to_rel[nr]); 29 | auto min_node_rel = nr; 30 | for (auto const nr2 : remaining) { 31 | auto const relation = node_rel_to_rel[nr2]; 32 | if (relation < min_rel) { 33 | min_rel = relation; 34 | min_node_rel = nr2; 35 | } 36 | } 37 | hpc::swap(node_rel_to_rel[nr], node_rel_to_rel[min_node_rel]); 38 | hpc::swap(node_rel_to_nodes_of_node_rel[nr], node_rel_to_nodes_of_node_rel[min_node_rel]); 39 | } 40 | #ifndef NDEBUG 41 | for (auto i(*(this_node_rel.begin())); i < (*(this_node_rel.end())) - 1; ++i) { 42 | assert(node_rel_to_rel[i] < node_rel_to_rel[i + 1]); 43 | } 44 | #endif 45 | }; 46 | hpc::for_each(hpc::device_policy(), s.nodes, sort_functor); 47 | } 48 | 49 | } // namespace lgr 50 | -------------------------------------------------------------------------------- /otm_meshless.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr { 6 | 7 | class state; 8 | class input; 9 | 10 | void 11 | otm_allocate_state(input const& in, state& s); 12 | void 13 | otm_initialize_displacement(state& s); 14 | void 15 | otm_initialize_point_volume(state& s); 16 | void 17 | otm_initialize(input& in, state& s); 18 | void 19 | otm_mark_boundary_domains(state& s); 20 | void 21 | otm_run(input const& in, state& s); 22 | void 23 | otm_set_beta(double gamma, state& s); 24 | void 25 | otm_time_integrator_step(input const& in, state& s); 26 | void 27 | otm_update_material_state(input const& in, state& s, material_index const material); 28 | void 29 | otm_update_neighbor_distances(state& s); 30 | void 31 | otm_update_nodal_external_force(state& s); 32 | void 33 | otm_update_nodal_force(state& s); 34 | void 35 | otm_update_nodal_internal_force(state& s); 36 | void 37 | otm_update_nodal_mass(state& s); 38 | void 39 | otm_update_nodal_momentum(state& s); 40 | void 41 | otm_update_nodal_position(state& s); 42 | void 43 | otm_update_point_position(state& s); 44 | void 45 | otm_update_reference(state& s); 46 | void 47 | otm_update_shape_functions(state& s); 48 | void 49 | otm_update_time(input const& in, state& s); 50 | } // namespace lgr 51 | -------------------------------------------------------------------------------- /otm_search.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | namespace search_util { 5 | template 6 | struct nearest_neighbors; 7 | } 8 | } // namespace lgr 9 | 10 | namespace lgr { 11 | namespace search { 12 | 13 | void 14 | initialize_otm_search(); 15 | 16 | void 17 | finalize_otm_search(); 18 | 19 | void 20 | do_otm_point_nearest_node_search(state& s, int max_support_nodes_per_point); 21 | 22 | void 23 | do_otm_iterative_point_support_search(state& s, int min_support_nodes_per_point); 24 | 25 | void 26 | do_otm_node_nearest_node_search(const state& s, search_util::nearest_neighbors& n, int max_nodes_per_node); 27 | 28 | void 29 | do_otm_point_nearest_point_search( 30 | const state& s, 31 | search_util::nearest_neighbors& n, 32 | int max_nodes_per_node); 33 | 34 | } // namespace search 35 | } // namespace lgr 36 | -------------------------------------------------------------------------------- /otm_search_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace lgr { 9 | namespace search_util { 10 | 11 | template 12 | struct nearest_neighbors 13 | { 14 | hpc::device_range_sum entities_to_neighbor_ordinals; 15 | hpc::device_vector entities_to_neighbors; 16 | 17 | void 18 | resize(const hpc::device_vector& counts) 19 | { 20 | auto const total_neighbors = hpc::reduce(hpc::device_policy(), counts, 0); 21 | entities_to_neighbor_ordinals.assign_sizes(counts); 22 | entities_to_neighbors.resize(total_neighbors); 23 | } 24 | }; 25 | 26 | using node_neighbors = nearest_neighbors; 27 | using point_neighbors = nearest_neighbors; 28 | 29 | } // namespace search_util 30 | } // namespace lgr 31 | -------------------------------------------------------------------------------- /otm_tet2meshless.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace lgr { 20 | 21 | void 22 | convert_tet_mesh_to_meshless(const input& in, state& st) 23 | { 24 | auto const num_points = st.elements.size() * in.otm_material_points_to_add_per_element; 25 | st.points.resize(num_points); 26 | auto const num_nodes_in_support = st.nodes_in_element.size(); 27 | st.point_nodes_to_nodes.resize(num_points * num_nodes_in_support); 28 | st.xp.resize(num_points); 29 | st.h_otm.resize(num_points); 30 | 31 | auto const nodes_in_element = st.nodes_in_element; 32 | auto const elements_to_element_nodes = st.elements * st.nodes_in_element; 33 | auto const element_nodes_to_nodes = st.elements_to_nodes.cbegin(); 34 | auto const points_in_support = hpc::make_counting_range(in.otm_material_points_to_add_per_element); 35 | auto const elements_to_points = st.elements * points_in_support; 36 | 37 | hpc::device_vector nodes_in_support_counts(st.points.size(), st.nodes_in_element.size()); 38 | st.points_to_point_nodes.assign_sizes(nodes_in_support_counts); 39 | 40 | auto const support_nodes_to_nodes = st.point_nodes_to_nodes.begin(); 41 | auto const nodes_in_support = st.points_to_point_nodes.cbegin(); 42 | auto func = [=] HPC_DEVICE(element_index const element) { 43 | auto const cur_elem_points = elements_to_points[element]; 44 | auto const element_nodes = elements_to_element_nodes[element]; 45 | 46 | for (auto element_point : points_in_support) { 47 | auto const point = cur_elem_points[element_point]; 48 | auto&& point_support_nodes = nodes_in_support[point]; 49 | for (auto n : nodes_in_element) { 50 | auto const cur_elem_node_offset = element_nodes[n]; 51 | auto const node = element_nodes_to_nodes[cur_elem_node_offset]; 52 | support_nodes_to_nodes[point_support_nodes[n]] = node; 53 | } 54 | } 55 | }; 56 | hpc::for_each(hpc::device_policy(), st.elements, func); 57 | 58 | invert_otm_point_node_relations(st); 59 | 60 | if (in.xp_transform) { 61 | in.xp_transform(st.points, st.points_to_point_nodes, st.point_nodes_to_nodes, st.x, st.xp); 62 | } 63 | 64 | otm_update_h(st); 65 | } 66 | 67 | void 68 | otm_update_h(state& st) 69 | { 70 | auto const mat_pts_to_x = st.xp.begin(); 71 | auto const mat_pts_to_h = st.h_otm.begin(); 72 | auto const nodes_to_x = st.x.cbegin(); 73 | auto const nodes_in_support = st.points_to_point_nodes.cbegin(); 74 | auto const support_nodes_to_nodes = st.point_nodes_to_nodes.cbegin(); 75 | auto h_min_func = [=] HPC_DEVICE(point_index const point) { 76 | auto min_node_pt_dist = 1.0 / hpc::machine_epsilon(); 77 | for (auto n : nodes_in_support[point]) { 78 | auto node = support_nodes_to_nodes[n]; 79 | auto node_pt_dist = hpc::norm(nodes_to_x[node].load() - mat_pts_to_x[point].load()); 80 | min_node_pt_dist = hpc::min(min_node_pt_dist, node_pt_dist); 81 | } 82 | mat_pts_to_h[point] = min_node_pt_dist; 83 | }; 84 | hpc::for_each(hpc::device_policy(), st.points, h_min_func); 85 | } 86 | 87 | } // namespace lgr 88 | -------------------------------------------------------------------------------- /otm_tet2meshless.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace lgr { 4 | 5 | class input; 6 | class state; 7 | 8 | void 9 | convert_tet_mesh_to_meshless(const input& in, state& st); 10 | void 11 | otm_update_h(state& st); 12 | 13 | } // namespace lgr 14 | -------------------------------------------------------------------------------- /otm_tetrahedron_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace lgr { 19 | 20 | HPC_ALWAYS_INLINE HPC_HOST_DEVICE hpc::position 21 | tet_parametric_to_physical( 22 | hpc::array, 4> const& xn, 23 | point_index const point, 24 | int const points_per_element) 25 | { 26 | auto const ip = hpc::weaken(point) % points_per_element; 27 | hpc::position xp(0.0, 0.0, 0.0); 28 | constexpr auto wa = 0.5854101966249685; 29 | constexpr auto wb = 0.1381966011250105; 30 | switch (points_per_element) { 31 | default: HPC_ERROR_EXIT("Invalid number of integration points"); break; 32 | case 1: xp = 0.25 * (xn[0] + xn[1] + xn[2] + xn[3]); break; 33 | case 4: 34 | switch (ip) { 35 | default: HPC_ERROR_EXIT("Invalid integration point index"); break; 36 | case 0: xp = wa * xn[0] + wb * (xn[1] + xn[2] + xn[3]); break; 37 | case 1: xp = wa * xn[1] + wb * (xn[0] + xn[2] + xn[3]); break; 38 | case 2: xp = wa * xn[2] + wb * (xn[0] + xn[1] + xn[3]); break; 39 | case 3: xp = wa * xn[3] + wb * (xn[0] + xn[1] + xn[2]); break; 40 | } 41 | break; 42 | } 43 | return xp; 44 | } 45 | 46 | struct tet_nodes_to_points 47 | { 48 | tet_nodes_to_points(int const pts_per_elem) : points_per_element(pts_per_elem) 49 | { 50 | } 51 | 52 | int const points_per_element{1}; 53 | 54 | void 55 | operator()( 56 | hpc::counting_range const points, 57 | hpc::device_range_sum const& points_to_point_nodes, 58 | hpc::device_vector const& point_nodes_to_nodes, 59 | hpc::device_array_vector, node_index> const& x, 60 | hpc::device_array_vector, point_index>& xp) const 61 | { 62 | auto pt_to_pt_nodes = points_to_point_nodes.cbegin(); 63 | auto pt_nodes_to_nodes = point_nodes_to_nodes.cbegin(); 64 | auto x_nodes = x.cbegin(); 65 | auto x_points = xp.begin(); 66 | auto const ppe = points_per_element; 67 | auto point_func = [=] HPC_DEVICE(point_index const point) { 68 | auto const point_nodes = pt_to_pt_nodes[point]; 69 | hpc::array, 4> x; 70 | assert(point_nodes.size() == 4); 71 | for (auto i = 0; i < 4; ++i) { 72 | auto const node = pt_nodes_to_nodes[point_nodes[i]]; 73 | x[i] = x_nodes[node].load(); 74 | } 75 | x_points[point] = tet_parametric_to_physical(x, point, ppe); 76 | }; 77 | hpc::for_each(hpc::device_policy(), points, point_func); 78 | } 79 | }; 80 | 81 | } // namespace lgr 82 | -------------------------------------------------------------------------------- /otm_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | std::ostream& 12 | operator<<(std::ostream& os, hpc::vector3 const& v) 13 | { 14 | os << std::scientific << std::setprecision(15); 15 | os << std::setw(24) << v(0) << "," << std::setw(24) << v(1) << "," << std::setw(24) << v(2); 16 | return os; 17 | } 18 | 19 | template 20 | std::ostream& 21 | operator<<(std::ostream& os, hpc::quaternion const& q) 22 | { 23 | os << std::scientific << std::setprecision(15); 24 | os << std::setw(24) << q(0) << "," << std::setw(24) << q(1) << "," << std::setw(24) << q(2) << "," << std::setw(24) 25 | << q(3); 26 | return os; 27 | } 28 | 29 | template 30 | std::ostream& 31 | operator<<(std::ostream& os, hpc::matrix3x3 const& A) 32 | { 33 | os << std::scientific << std::setprecision(15); 34 | os << std::setw(24) << A(0, 0) << "," << std::setw(24) << A(0, 1) << "," << std::setw(24) << A(0, 2) << std::endl; 35 | os << std::setw(24) << A(1, 0) << "," << std::setw(24) << A(1, 1) << "," << std::setw(24) << A(1, 2) << std::endl; 36 | os << std::setw(24) << A(2, 0) << "," << std::setw(24) << A(2, 1) << "," << std::setw(24) << A(2, 2); 37 | return os; 38 | } 39 | -------------------------------------------------------------------------------- /otm_vtk.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace lgr { 13 | 14 | void 15 | otm_file_writer::capture(state const& s) 16 | { 17 | host_s.nodes = s.nodes; 18 | host_s.points = s.points; 19 | host_s.time = s.time; 20 | 21 | auto const num_points = s.points.size(); 22 | auto const num_nodes = s.nodes.size(); 23 | 24 | host_s.x.resize(num_nodes); 25 | host_s.xp.resize(num_points); 26 | host_s.V.resize(num_points); 27 | host_s.rho.resize(num_points); 28 | 29 | hpc::copy(s.x, host_s.x); 30 | hpc::copy(s.xp, host_s.xp); 31 | hpc::copy(s.rho, host_s.rho); 32 | hpc::copy(s.V, host_s.V); 33 | 34 | host_s.v.resize(num_nodes); 35 | hpc::copy(s.v, host_s.v); 36 | host_s.u.resize(num_nodes); 37 | hpc::copy(s.u, host_s.u); 38 | host_s.mass.resize(num_nodes); 39 | hpc::copy(s.mass, host_s.mass); 40 | 41 | host_s.F_total.resize(num_points); 42 | hpc::copy(s.F_total, host_s.F_total); 43 | host_s.sigma.resize(num_points); 44 | hpc::copy(s.sigma_full, host_s.sigma); 45 | host_s.G.resize(num_points); 46 | hpc::copy(s.G, host_s.G); 47 | host_s.K.resize(num_points); 48 | hpc::copy(s.K, host_s.K); 49 | host_s.potential_density.resize(num_points); 50 | hpc::copy(s.potential_density, host_s.potential_density); 51 | 52 | auto const Fp_size = s.Fp_total.size(); 53 | if (Fp_size > 0) { 54 | host_s.Fp_total.resize(Fp_size); 55 | hpc::copy(s.Fp_total, host_s.Fp_total); 56 | } 57 | auto const ep_size = s.ep.size(); 58 | if (ep_size > 0) { 59 | host_s.ep.resize(ep_size); 60 | hpc::copy(s.ep, host_s.ep); 61 | } 62 | } 63 | 64 | void 65 | otm_file_writer::write(int const file_output_index) 66 | { 67 | auto node_stream = make_vtk_output_stream(prefix + "_nodes", file_output_index); 68 | auto point_stream = make_vtk_output_stream(prefix + "_points", file_output_index); 69 | 70 | start_vtk_unstructured_grid_file(node_stream); 71 | // POINTS (nodes) 72 | write_vtk_points(node_stream, host_s.x); 73 | write_vtk_point_data(node_stream, host_s.nodes); 74 | assert(host_s.x.size() == host_s.nodes.size()); 75 | write_vtk_vectors(node_stream, "node_position", host_s.x); 76 | write_vtk_vectors(node_stream, "node_displacement", host_s.u); 77 | write_vtk_vectors(node_stream, "node_velocity", host_s.v); 78 | write_vtk_scalars(node_stream, "node_mass", host_s.mass); 79 | node_stream.close(); 80 | 81 | start_vtk_unstructured_grid_file(point_stream); 82 | // POINTS (points) 83 | write_vtk_points(point_stream, host_s.xp); 84 | write_vtk_point_data(point_stream, host_s.points); 85 | assert(host_s.xp.size() == host_s.points.size()); 86 | write_vtk_vectors(point_stream, "point_position", host_s.xp); 87 | write_vtk_scalars(point_stream, "density", host_s.rho); 88 | write_vtk_scalars(point_stream, "volume", host_s.V); 89 | write_vtk_full_tensors(point_stream, "sigma", host_s.sigma); 90 | write_vtk_full_tensors(point_stream, "deformation_gradient", host_s.F_total); 91 | write_vtk_scalars(point_stream, "G", host_s.G); 92 | write_vtk_scalars(point_stream, "K", host_s.K); 93 | write_vtk_scalars(point_stream, "potential_density", host_s.potential_density); 94 | if (host_s.Fp_total.size() > 0) { 95 | write_vtk_full_tensors(point_stream, "plastic_deformation_gradient", host_s.Fp_total); 96 | } 97 | if (host_s.ep.size() > 0) { 98 | write_vtk_scalars(point_stream, "ep", host_s.ep); 99 | } 100 | point_stream.close(); 101 | } 102 | 103 | void 104 | otm_file_writer::to_console() 105 | { 106 | std::cout << "TIME : " << host_s.time << '\n'; 107 | auto const nodes_to_x = host_s.x.cbegin(); 108 | auto print_x = [=] HPC_HOST(lgr::node_index const node) { 109 | auto const x = nodes_to_x[node].load(); 110 | std::cout << "node: " << node << ", x:" << x << '\n'; 111 | }; 112 | hpc::for_each(hpc::host_policy(), host_s.nodes, print_x); 113 | 114 | auto const nodes_to_v = host_s.v.cbegin(); 115 | auto print_v = [=] HPC_HOST(lgr::node_index const node) { 116 | auto const v = nodes_to_v[node].load(); 117 | std::cout << "node: " << node << ", v:" << v << '\n'; 118 | }; 119 | hpc::for_each(hpc::host_policy(), host_s.nodes, print_v); 120 | 121 | auto const nodes_to_u = host_s.u.cbegin(); 122 | auto print_u = [=] HPC_HOST(lgr::node_index const node) { 123 | auto const u = nodes_to_u[node].load(); 124 | std::cout << "node: " << node << ", u:" << u << '\n'; 125 | }; 126 | hpc::for_each(hpc::host_policy(), host_s.nodes, print_u); 127 | 128 | auto const points_to_xp = host_s.xp.cbegin(); 129 | auto print_xp = [=] HPC_HOST(lgr::point_index const point) { 130 | auto const xp = points_to_xp[point].load(); 131 | std::cout << "point: " << point << ", xp:" << xp << '\n'; 132 | }; 133 | hpc::for_each(hpc::host_policy(), host_s.points, print_xp); 134 | 135 | auto const points_to_F = host_s.F_total.cbegin(); 136 | auto print_F = [=] HPC_HOST(lgr::point_index const point) { 137 | auto const F = points_to_F[point].load(); 138 | std::cout << "point: " << point << ", F:\n" << F; 139 | }; 140 | hpc::for_each(hpc::host_policy(), host_s.points, print_F); 141 | 142 | auto const points_to_sigma = host_s.sigma.cbegin(); 143 | auto const points_to_K = host_s.K.cbegin(); 144 | auto const points_to_G = host_s.G.cbegin(); 145 | auto print_sigma = [=] HPC_HOST(lgr::point_index const point) { 146 | auto const sigma = points_to_sigma[point].load(); 147 | auto const K = points_to_K[point]; 148 | auto const G = points_to_G[point]; 149 | std::cout << "point: " << point << ", K: " << K << ", G: " << G << ", sigma:\n" << sigma; 150 | }; 151 | hpc::for_each(hpc::host_policy(), host_s.points, print_sigma); 152 | } 153 | 154 | } // namespace lgr 155 | -------------------------------------------------------------------------------- /otm_vtk.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace lgr { 11 | 12 | class state; 13 | 14 | struct otm_host_pinned_output_state : public otm_host_pinned_state 15 | { 16 | hpc::counting_range nodes{node_index(0)}; 17 | hpc::counting_range points{point_index(0)}; 18 | 19 | hpc::time time; 20 | 21 | hpc::pinned_array_vector, node_index> u; 22 | hpc::pinned_array_vector, node_index> v; 23 | hpc::pinned_vector, node_index> mass; 24 | 25 | hpc::pinned_array_vector, point_index> F_total; 26 | hpc::pinned_array_vector, point_index> Fp_total; 27 | hpc::pinned_array_vector, point_index> sigma; 28 | hpc::pinned_vector, point_index> K; 29 | hpc::pinned_vector, point_index> G; 30 | hpc::pinned_vector, point_index> ep; 31 | hpc::pinned_vector, point_index> potential_density; 32 | }; 33 | 34 | class otm_file_writer 35 | { 36 | std::string prefix; 37 | 38 | public: 39 | otm_file_writer(std::string const& prefix_in) : prefix(prefix_in) 40 | { 41 | } 42 | void 43 | capture(state const& s); 44 | void 45 | write(int const file_output_index); 46 | void 47 | to_console(); 48 | 49 | otm_host_pinned_output_state host_s; 50 | }; 51 | 52 | } // namespace lgr 53 | -------------------------------------------------------------------------------- /unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | option(LGR_ENABLE_UNIT_TESTS "Build support for unit tests" ON) 3 | 4 | if (LGR_ENABLE_UNIT_TESTS) 5 | set(LGR_UNIT_SOURCES 6 | adapt.cpp 7 | distances.cpp 8 | map.cpp 9 | materials.cpp 10 | maxent.cpp 11 | mechanics.cpp 12 | quaternion.cpp 13 | tensor.cpp 14 | vtk.cpp 15 | ut_main.cpp) 16 | 17 | if (LGR_ENABLE_EXODUS) 18 | set(LGR_UNIT_SOURCES ${LGR_UNIT_SOURCES} exodus.cpp) 19 | endif() 20 | 21 | if (LGR_ENABLE_SEARCH) 22 | set(LGR_UNIT_SOURCES ${LGR_UNIT_SOURCES} search.cpp) 23 | endif() 24 | 25 | if (LGR_ENABLE_CUDA AND NOT LGR_USE_NVCC_WRAPPER) 26 | set_source_files_properties(${LGR_UNIT_SOURCES} PROPERTIES LANGUAGE CUDA) 27 | endif() 28 | 29 | include(CTest) 30 | 31 | find_package(GTest REQUIRED) 32 | include(GoogleTest) 33 | 34 | add_executable(ut_lgr ${LGR_UNIT_SOURCES}) 35 | if (LGR_ENABLE_CUDA) 36 | target_compile_definitions(ut_lgr PUBLIC -DLGR_ENABLE_CUDA) 37 | endif() 38 | set_property(TARGET ut_lgr PROPERTY CXX_STANDARD "14") 39 | set_property(TARGET ut_lgr PROPERTY CXX_STANDARD_REQUIRED ON) 40 | set_property(TARGET ut_lgr PROPERTY CXX_EXTENSIONS OFF) 41 | 42 | set(UT_LIBRARIES lgrlib ${GTEST_LIBRARIES} pthread) 43 | if (LGR_ENABLE_EFENCE) 44 | set(LGR_LIBRARIES ${UT_LIBRARIES} efence) 45 | endif() 46 | 47 | gtest_discover_tests(ut_lgr) 48 | target_include_directories(ut_lgr PUBLIC ${GTEST_INCLUDE_DIRS}) 49 | target_link_libraries(ut_lgr ${UT_LIBRARIES}) 50 | 51 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tets.g ${CMAKE_CURRENT_BINARY_DIR}/tets.g COPYONLY) 52 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cube.g ${CMAKE_CURRENT_BINARY_DIR}/cube.g COPYONLY) 53 | endif() 54 | -------------------------------------------------------------------------------- /unit_tests/cube.g: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandialabs/lgrtk/546c849608c99b55f325d7cf8eec347f875783b4/unit_tests/cube.g -------------------------------------------------------------------------------- /unit_tests/maxent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | TEST(maxent, partition_unity_value_1) 19 | { 20 | lgr::state s; 21 | 22 | tetrahedron_single_point(s); 23 | 24 | auto num_points = s.points.size(); 25 | double const init = -num_points; 26 | auto const error = hpc::reduce(hpc::device_policy(), s.N, init) / num_points; 27 | auto const eps = hpc::machine_epsilon(); 28 | 29 | ASSERT_LE(error, eps); 30 | } 31 | 32 | TEST(maxent, partition_unity_value_2) 33 | { 34 | lgr::state s; 35 | 36 | two_tetrahedra_two_points(s); 37 | 38 | auto num_points = s.points.size(); 39 | double const init = -num_points; 40 | auto const error = hpc::reduce(hpc::device_policy(), s.N, init) / num_points; 41 | auto const eps = hpc::machine_epsilon(); 42 | 43 | ASSERT_LE(error, eps); 44 | } 45 | 46 | TEST(maxent, partition_unity_value_3) 47 | { 48 | lgr::state s; 49 | 50 | hexahedron_eight_points(s); 51 | 52 | auto num_points = s.points.size(); 53 | double const init = -num_points; 54 | auto const error = hpc::reduce(hpc::device_policy(), s.N, init) / num_points; 55 | auto const eps = 3 * hpc::machine_epsilon(); 56 | 57 | ASSERT_LE(error, eps); 58 | } 59 | 60 | namespace lgr_unit { 61 | 62 | double 63 | compute_basis_gradient_error(lgr::state const& s) 64 | { 65 | auto num_points = s.points.size(); 66 | auto const nodes_to_grad_N = s.grad_N.begin(); 67 | auto const supports = s.points_to_point_nodes.cbegin(); 68 | auto functor = [=] HPC_DEVICE(lgr::point_index const point) { 69 | auto const support = supports[point]; 70 | hpc::basis_gradient s(0, 0, 0); 71 | for (auto point_node : support) { 72 | auto const grad_N = nodes_to_grad_N[point_node].load(); 73 | s += grad_N; 74 | } 75 | return hpc::abs(s); 76 | }; 77 | hpc::basis_gradient init(0, 0, 0); 78 | auto const errors = 79 | hpc::transform_reduce(hpc::device_policy(), s.points, init, hpc::plus >(), functor); 80 | return hpc::norm(errors / num_points) / 3.; 81 | } 82 | 83 | } // namespace lgr_unit 84 | 85 | TEST(maxent, partition_unity_gradient_1) 86 | { 87 | lgr::state s; 88 | 89 | tetrahedron_single_point(s); 90 | 91 | auto const error = lgr_unit::compute_basis_gradient_error(s); 92 | auto const eps = 2 * hpc::machine_epsilon(); 93 | 94 | ASSERT_LE(error, eps); 95 | } 96 | 97 | TEST(maxent, partition_unity_gradient_2) 98 | { 99 | lgr::state s; 100 | 101 | two_tetrahedra_two_points(s); 102 | 103 | auto const error = lgr_unit::compute_basis_gradient_error(s); 104 | auto const eps = 4 * hpc::machine_epsilon(); 105 | 106 | ASSERT_LE(error, eps); 107 | } 108 | 109 | TEST(maxent, partition_unity_gradient_3) 110 | { 111 | lgr::state s; 112 | 113 | hexahedron_eight_points(s); 114 | 115 | auto const error = lgr_unit::compute_basis_gradient_error(s); 116 | auto const eps = 256 * hpc::machine_epsilon(); 117 | 118 | ASSERT_LE(error, eps); 119 | } 120 | 121 | namespace lgr_unit { 122 | 123 | double 124 | compute_linear_reproducibility_error(const lgr::state& s) 125 | { 126 | auto num_points = s.points.size(); 127 | auto const points_to_xp = s.xp.begin(); 128 | auto const point_nodes_to_N = s.N.begin(); 129 | auto const nodes_to_x = s.x.begin(); 130 | auto const supports = s.points_to_point_nodes.cbegin(); 131 | auto const points_to_point_nodes = s.point_nodes_to_nodes.cbegin(); 132 | auto functor = [=] HPC_DEVICE(lgr::point_index const point) { 133 | auto const support = supports[point]; 134 | auto const xp = points_to_xp[point].load(); 135 | hpc::position x(0, 0, 0); 136 | for (auto point_node : support) { 137 | auto const node = points_to_point_nodes[point_node]; 138 | auto const xn = nodes_to_x[node].load(); 139 | auto const N = point_nodes_to_N[point_node]; 140 | x += (N * xn); 141 | } 142 | return hpc::abs(x - xp); 143 | }; 144 | hpc::position init(0, 0, 0); 145 | auto const errors = 146 | hpc::transform_reduce(hpc::device_policy(), s.points, init, hpc::plus >(), functor); 147 | return hpc::norm(errors / (3 * num_points)); 148 | } 149 | 150 | } // namespace lgr_unit 151 | 152 | TEST(maxent, linear_reproducibility_1) 153 | { 154 | lgr::state s; 155 | 156 | tetrahedron_single_point(s); 157 | 158 | auto const error = lgr_unit::compute_linear_reproducibility_error(s); 159 | auto const eps = hpc::machine_epsilon(); 160 | 161 | ASSERT_LE(error, eps); 162 | } 163 | 164 | TEST(maxent, linear_reproducibility_2) 165 | { 166 | lgr::state s; 167 | 168 | two_tetrahedra_two_points(s); 169 | 170 | auto const error = lgr_unit::compute_linear_reproducibility_error(s); 171 | auto const eps = hpc::machine_epsilon(); 172 | 173 | ASSERT_LE(error, eps); 174 | } 175 | 176 | TEST(maxent, linear_reproducibility_3) 177 | { 178 | lgr::state s; 179 | 180 | hexahedron_eight_points(s); 181 | 182 | auto const error = lgr_unit::compute_linear_reproducibility_error(s); 183 | auto const eps = 256 * hpc::machine_epsilon(); 184 | 185 | ASSERT_LE(error, eps); 186 | } 187 | -------------------------------------------------------------------------------- /unit_tests/mechanics.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | class mechanics : public ::testing::Test 25 | { 26 | void 27 | SetUp() override 28 | { 29 | lgr_unit::arborx_testing_singleton::instance(); 30 | } 31 | }; 32 | 33 | namespace lgr_unit { 34 | 35 | double 36 | compute_total_mass(const lgr::state& s) 37 | { 38 | auto const rho = s.rho.cbegin(); 39 | auto const V = s.V.cbegin(); 40 | auto mass_func = [=] HPC_DEVICE(lgr::point_index p) { return rho[p] * V[p]; }; 41 | double init_mass = 0.0; 42 | return hpc::transform_reduce(hpc::device_policy(), s.points, init_mass, hpc::plus(), mass_func); 43 | } 44 | 45 | } // namespace lgr_unit 46 | 47 | TEST_F(mechanics, lumped_mass_1) 48 | { 49 | lgr::state s; 50 | 51 | tetrahedron_single_point(s); 52 | lgr::otm_update_nodal_mass(s); 53 | 54 | auto const mass = hpc::reduce(hpc::device_policy(), s.mass, 0.0); 55 | auto const expected_mass = lgr_unit::compute_total_mass(s); 56 | auto const error = std::abs(mass / expected_mass - 1.0); 57 | auto const eps = hpc::machine_epsilon(); 58 | 59 | ASSERT_LE(error, eps); 60 | } 61 | 62 | TEST_F(mechanics, lumped_mass_2) 63 | { 64 | lgr::state s; 65 | 66 | two_tetrahedra_two_points(s); 67 | lgr::otm_update_nodal_mass(s); 68 | 69 | auto const mass = hpc::reduce(hpc::device_policy(), s.mass, 0.0); 70 | auto const expected_mass = lgr_unit::compute_total_mass(s); 71 | auto const error = std::abs(mass / expected_mass - 1.0); 72 | auto const eps = hpc::machine_epsilon(); 73 | 74 | ASSERT_LE(error, eps); 75 | } 76 | 77 | TEST_F(mechanics, lumped_mass_3) 78 | { 79 | lgr::state s; 80 | 81 | hexahedron_eight_points(s); 82 | lgr::otm_update_nodal_mass(s); 83 | 84 | auto const mass = hpc::reduce(hpc::device_policy(), s.mass, 0.0); 85 | auto const expected_mass = lgr_unit::compute_total_mass(s); 86 | auto const error = std::abs(mass / expected_mass - 1.0); 87 | auto const eps = hpc::machine_epsilon(); 88 | 89 | ASSERT_LE(error, eps); 90 | } 91 | 92 | namespace lgr_unit { 93 | 94 | double 95 | compute_sigma_error(const lgr::state& s) 96 | { 97 | auto const points_to_sigma = s.sigma_full.cbegin(); 98 | auto get_error = [=] HPC_DEVICE(lgr::point_index const point) { 99 | auto const s = points_to_sigma[point].load(); 100 | return s(0, 0) + s(0, 1) + s(0, 2) + s(1, 0) + s(1, 1) + s(1, 2) + s(2, 0) + s(2, 1) + s(2, 2); 101 | }; 102 | auto error = hpc::transform_reduce(hpc::device_policy(), s.points, 0.0, hpc::plus(), get_error); 103 | error /= (9 * s.points.size()); 104 | return error; 105 | } 106 | 107 | } // namespace lgr_unit 108 | 109 | TEST_F(mechanics, hex_translation) 110 | { 111 | lgr::state s; 112 | 113 | lgr::material_index const num_materials = 1; 114 | lgr::material_index const num_boundaries = 0; 115 | 116 | lgr::input in(num_materials, num_boundaries); 117 | 118 | in.enable_neo_Hookean.resize(num_materials); 119 | in.enable_variational_J2.resize(num_materials); 120 | in.K0.resize(num_materials); 121 | in.G0.resize(num_materials); 122 | 123 | in.enable_neo_Hookean[0] = true; 124 | in.enable_variational_J2[0] = false; 125 | in.K0[0] = hpc::pressure(1.0e+09); 126 | in.G0[0] = hpc::pressure(1.0e+09); 127 | 128 | hexahedron_eight_points(s); 129 | lgr::otm_update_nodal_mass(s); 130 | 131 | auto const num_points = s.points.size(); 132 | auto const num_nodes = s.nodes.size(); 133 | 134 | s.u.resize(num_nodes); 135 | s.v.resize(num_nodes); 136 | s.lm.resize(num_nodes); 137 | s.f.resize(num_nodes); 138 | s.nodal_materials.resize(num_nodes); 139 | 140 | s.b.resize(num_points); 141 | 142 | s.F_total.resize(num_points); 143 | s.sigma_full.resize(num_points); 144 | s.K.resize(num_points); 145 | s.G.resize(num_points); 146 | s.potential_density.resize(num_points); 147 | 148 | auto const I = hpc::deformation_gradient::identity(); 149 | hpc::fill(hpc::device_policy(), s.F_total, I); 150 | 151 | lgr::otm_initialize_displacement(s); 152 | lgr::otm_update_reference(s); 153 | lgr::otm_update_material_state(in, s, 0); 154 | 155 | auto const error = lgr_unit::compute_sigma_error(s); 156 | auto const tol = 1.0e-06; 157 | ASSERT_LE(error, tol); 158 | } 159 | 160 | TEST_F(mechanics, zero_nu_tension) 161 | { 162 | auto const pass = lgr::otm_j2_nu_zero_patch_test(); 163 | ASSERT_EQ(pass, true); 164 | } 165 | 166 | TEST_F(mechanics, uniaxial_tension) 167 | { 168 | auto const pass = lgr::otm_j2_uniaxial_patch_test(); 169 | ASSERT_EQ(pass, true); 170 | } 171 | -------------------------------------------------------------------------------- /unit_tests/quaternion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | TEST(quaternion, zero_rotation) 9 | { 10 | auto const eps = hpc::machine_epsilon(); 11 | auto const z = hpc::vector3::zero(); 12 | auto const q = hpc::quaternion(1, 0, 0, 0); 13 | auto const I = hpc::matrix3x3::identity(); 14 | auto const R = hpc::rotation_tensor_from_quaternion(q); 15 | auto const e1 = hpc::norm(R - I); 16 | ASSERT_LE(e1, eps); 17 | auto const w = hpc::rotation_vector_from_quaternion(q); 18 | auto const e2 = hpc::norm(w - z); 19 | ASSERT_LE(e2, eps); 20 | auto const p = hpc::quaternion_from_rotation_tensor(I); 21 | auto const e3 = hpc::norm(p - q); 22 | ASSERT_LE(e3, eps); 23 | auto const r = hpc::quaternion_from_rotation_vector(z); 24 | auto const e4 = hpc::norm(r - q); 25 | ASSERT_LE(e4, eps); 26 | auto const i = hpc::rotation_vector_from_rotation_tensor(I); 27 | auto const e5 = hpc::norm(i - z); 28 | ASSERT_LE(e5, eps); 29 | auto const Z = hpc::rotation_tensor_from_rotation_vector(z); 30 | auto const e6 = hpc::norm(Z - I); 31 | ASSERT_LE(e6, eps); 32 | } 33 | 34 | TEST(quaternion, pi_rotation) 35 | { 36 | auto const eps = hpc::machine_epsilon(); 37 | auto const pi = 3.14159265358979323846; 38 | auto const z = hpc::vector3(pi, 0, 0); 39 | auto const q = hpc::quaternion(0, 1, 0, 0); 40 | auto const I = hpc::matrix3x3(1, 0, 0, 0, -1, 0, 0, 0, -1); 41 | auto const R = hpc::rotation_tensor_from_quaternion(q); 42 | auto const e1 = hpc::norm(R - I); 43 | ASSERT_LE(e1, eps); 44 | auto const w = hpc::rotation_vector_from_quaternion(q); 45 | auto const e2 = hpc::norm(w - z); 46 | ASSERT_LE(e2, eps); 47 | auto const p = hpc::quaternion_from_rotation_tensor(I); 48 | auto const e3 = hpc::norm(p - q); 49 | ASSERT_LE(e3, eps); 50 | auto const r = hpc::quaternion_from_rotation_vector(z); 51 | auto const e4 = hpc::norm(r - q); 52 | ASSERT_LE(e4, eps); 53 | auto const i = hpc::rotation_vector_from_rotation_tensor(I); 54 | auto const e5 = hpc::norm(i - z); 55 | ASSERT_LE(e5, eps); 56 | auto const Z = hpc::rotation_tensor_from_rotation_vector(z); 57 | auto const e6 = hpc::norm(Z - I); 58 | ASSERT_LE(e6, eps); 59 | } 60 | 61 | TEST(quaternion, minus_pi_rotation) 62 | { 63 | auto const eps = hpc::machine_epsilon(); 64 | auto const pi = 3.14159265358979323846; 65 | auto const z = hpc::vector3(0, -pi, 0); 66 | auto const q = hpc::quaternion(0, 0, -1, 0); 67 | auto const I = hpc::matrix3x3(-1, 0, 0, 0, 1, 0, 0, 0, -1); 68 | auto const R = hpc::rotation_tensor_from_quaternion(q); 69 | auto const e1 = hpc::norm(R - I); 70 | ASSERT_LE(e1, eps); 71 | auto const w = hpc::rotation_vector_from_quaternion(q); 72 | auto const e2 = hpc::norm(w - z); 73 | ASSERT_LE(e2, eps); 74 | auto const p = hpc::quaternion_from_rotation_tensor(I); 75 | auto const e3 = hpc::norm(p + q); 76 | ASSERT_LE(e3, eps); 77 | auto const r = hpc::quaternion_from_rotation_vector(z); 78 | auto const e4 = hpc::norm(r - q); 79 | ASSERT_LE(e4, eps); 80 | auto const i = hpc::rotation_vector_from_rotation_tensor(I); 81 | auto const e5 = hpc::norm(i + z); 82 | ASSERT_LE(e5, eps); 83 | auto const Z = hpc::rotation_tensor_from_rotation_vector(z); 84 | auto const e6 = hpc::norm(Z - I); 85 | ASSERT_LE(e6, eps); 86 | } 87 | 88 | TEST(quaternion, tau_rotation) 89 | { 90 | auto const eps = hpc::machine_epsilon(); 91 | auto const tau = 6.28318530717958647693; 92 | auto const z = hpc::vector3(0, 0, tau); 93 | auto const q = hpc::quaternion(-1, 0, 0, 0); 94 | auto const I = hpc::matrix3x3::identity(); 95 | auto const R = hpc::rotation_tensor_from_quaternion(q); 96 | auto const e1 = hpc::norm(R - I); 97 | ASSERT_LE(e1, eps); 98 | auto const w = hpc::rotation_vector_from_quaternion(q); 99 | auto const e2 = hpc::norm(w); 100 | ASSERT_LE(e2, eps); 101 | auto const p = hpc::quaternion_from_rotation_tensor(I); 102 | auto const e3 = hpc::norm(p + q); 103 | ASSERT_LE(e3, eps); 104 | auto const r = hpc::quaternion_from_rotation_vector(z); 105 | auto const e4 = hpc::norm(r - q); 106 | ASSERT_LE(e4, eps); 107 | auto const i = hpc::rotation_vector_from_rotation_tensor(I); 108 | auto const e5 = hpc::norm(i); 109 | ASSERT_LE(e5, eps); 110 | auto const Z = hpc::rotation_tensor_from_rotation_vector(z); 111 | auto const e6 = hpc::norm(Z - I); 112 | ASSERT_LE(e6, 2 * eps); 113 | } 114 | 115 | TEST(quaternion, x_to_111_rotation) 116 | { 117 | auto const eps = hpc::machine_epsilon(); 118 | auto const cosine = std::sqrt(3.0) / 3.0; 119 | auto const theta = std::acos(cosine); 120 | auto const a = std::sqrt(2.0) / 2.0; 121 | auto const v = theta * hpc::vector3(0, -a, a); 122 | auto const R = hpc::rotation_tensor_from_rotation_vector(v); 123 | auto const e1 = std::abs(R(0, 0) - cosine); 124 | auto const e2 = std::abs(R(1, 0) - cosine); 125 | auto const e3 = std::abs(R(2, 0) - cosine); 126 | ASSERT_LE(e1, eps); 127 | ASSERT_LE(e2, eps); 128 | ASSERT_LE(e3, eps); 129 | auto const e4 = std::abs(R(0, 1) + cosine); 130 | auto const e5 = std::abs(R(0, 2) + cosine); 131 | ASSERT_LE(e4, eps); 132 | ASSERT_LE(e5, eps); 133 | auto const e6 = std::abs(R(1, 1) * R(1, 1) + R(1, 2) * R(1, 2) - 2.0 / 3.0); 134 | auto const e7 = std::abs(R(2, 1) * R(2, 1) + R(2, 2) * R(2, 2) - 2.0 / 3.0); 135 | ASSERT_LE(e6, eps); 136 | ASSERT_LE(e7, eps); 137 | } 138 | -------------------------------------------------------------------------------- /unit_tests/tensor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using Real = double; 7 | using Tensor = hpc::matrix3x3; 8 | using Vector = hpc::vector3; 9 | 10 | TEST(tensor, exp) 11 | { 12 | auto const eps = hpc::machine_epsilon(); 13 | auto const A = Tensor(2.5, 0.5, 1, 0.5, 2.5, 1, 1, 1, 2); 14 | auto const a = sqrt(2.0) / 2.0; 15 | auto const b = sqrt(3.0) / 3.0; 16 | auto const c = sqrt(6.0) / 6.0; 17 | auto const V = Tensor(c, a, b, c, -a, b, -2 * c, 0, b); 18 | auto const p = std::exp(1.0); 19 | auto const q = std::exp(2.0); 20 | auto const r = std::exp(4.0); 21 | auto const D = Tensor(p, 0, 0, 0, q, 0, 0, 0, r); 22 | auto const B = hpc::exp(A); 23 | auto const C = V * D * hpc::transpose(V); 24 | auto const F = hpc::exp_taylor(A); 25 | auto const error_pade = hpc::norm(B - C) / hpc::norm(A); 26 | ASSERT_LE(error_pade, 9 * eps); 27 | auto const error_taylor = hpc::norm(F - C) / hpc::norm(A); 28 | ASSERT_LE(error_taylor, 20 * eps); 29 | } 30 | 31 | TEST(tensor, log) 32 | { 33 | // Identity 34 | auto const eps = hpc::machine_epsilon(); 35 | auto const I = Tensor::identity(); 36 | auto const i = hpc::log(I); 37 | auto const error_I = hpc::norm(i) / hpc::norm(I); 38 | ASSERT_LE(error_I, eps); 39 | // 1/8 of a rotation 40 | auto const tau = 2.0 * std::acos(-1.0); 41 | auto const c = std::sqrt(2.0) / 2.0; 42 | auto const R = Tensor(c, -c, 0.0, c, c, 0.0, 0.0, 0.0, 1.0); 43 | auto const r = log(R); 44 | auto const error_R = std::abs(r(0, 1) + tau / 8.0); 45 | ASSERT_LE(error_R, eps); 46 | auto const error_r = std::abs(r(0, 1) + r(1, 0)); 47 | ASSERT_LE(error_r, eps); 48 | auto const A = Tensor(7, 1, 2, 3, 8, 4, 5, 6, 9); 49 | auto const a = hpc::log(A); 50 | auto const b = hpc::log_gregory(A); 51 | auto const error_a = norm(b - a); 52 | auto const tol = 32 * eps; 53 | ASSERT_LE(error_a, tol); 54 | } 55 | 56 | TEST(tensor, inverse) 57 | { 58 | auto const eps = hpc::machine_epsilon(); 59 | // Tolerance: see Golub & Van Loan, Matrix Computations 4th Ed., pp 122-123 60 | auto const dim = 3; 61 | auto const tol = 2 * (dim - 1) * eps; 62 | auto const A = Tensor(7, 1, 2, 3, 8, 4, 5, 6, 9); 63 | auto const B = hpc::inverse_full_pivot(A); 64 | auto const I = Tensor::identity(); 65 | auto const C = A * B; 66 | auto const error_1 = hpc::norm(C - I) / hpc::norm(A); 67 | ASSERT_LE(error_1, tol); 68 | auto const D = B * A; 69 | auto const error_2 = hpc::norm(D - I) / hpc::norm(A); 70 | ASSERT_LE(error_2, tol); 71 | } 72 | 73 | TEST(tensor, solve) 74 | { 75 | auto const eps = hpc::machine_epsilon(); 76 | // Tolerance: see Golub & Van Loan, Matrix Computations 4th Ed., pp 122-123 77 | auto const dim = 3; 78 | auto const tol = 2 * (dim - 1) * eps; 79 | auto const A = Tensor(7, 1, 2, 3, 8, 4, 5, 6, 9); 80 | auto const b = Vector(1, 2, 4); 81 | auto const c = hpc::solve_full_pivot(A, b); 82 | auto const error = hpc::norm(A * c - b) / hpc::norm(A); 83 | ASSERT_LE(error, tol); 84 | } 85 | 86 | TEST(tensor, sqrt) 87 | { 88 | auto const eps = hpc::machine_epsilon(); 89 | // Tolerance: see Golub & Van Loan, Matrix Computations 4th Ed., pp 122-123 90 | auto const dim = 3; 91 | auto const tol = 2 * (dim - 1) * eps; 92 | auto const A = Tensor(7, 1, 2, 3, 8, 4, 5, 6, 9); 93 | auto const B = hpc::sqrt(A); 94 | auto const C = B * B; 95 | auto const error = hpc::norm(C - A) / hpc::norm(A); 96 | ASSERT_LE(error, tol); 97 | } 98 | 99 | TEST(tensor, polar) 100 | { 101 | auto const eps = hpc::machine_epsilon(); 102 | auto const c = std::sqrt(2.0) / 2.0; 103 | auto const A = Tensor(c, -c, 0.0, c, c, 0.0, 0.0, 0.0, 1.0); 104 | auto const B = Tensor(2, 1, 0, 1, 2, 1, 0, 1, 2); 105 | auto const C = A * B; 106 | auto R = 0.0 * A; 107 | auto U = 0.0 * B; 108 | std::tie(R, U) = hpc::polar_right(C); 109 | auto const error = (hpc::norm(R - A) + hpc::norm(B - U)) / hpc::norm(C); 110 | ASSERT_LE(error, eps); 111 | } 112 | -------------------------------------------------------------------------------- /unit_tests/tets.g: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandialabs/lgrtk/546c849608c99b55f325d7cf8eec347f875783b4/unit_tests/tets.g -------------------------------------------------------------------------------- /unit_tests/unit_arborx_testing_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace lgr_unit { 6 | 7 | using namespace lgr; 8 | 9 | struct arborx_testing_singleton 10 | { 11 | public: 12 | static arborx_testing_singleton& 13 | instance() 14 | { 15 | static arborx_testing_singleton s; 16 | return s; 17 | } 18 | 19 | ~arborx_testing_singleton() 20 | { 21 | search::finalize_otm_search(); 22 | } 23 | 24 | private: 25 | arborx_testing_singleton() 26 | { 27 | search::initialize_otm_search(); 28 | } 29 | }; 30 | 31 | } // namespace lgr_unit 32 | -------------------------------------------------------------------------------- /unit_tests/unit_device_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #ifdef LGR_ENABLE_CUDA 9 | #define DEVICE_TEST(a) [=] HPC_DEVICE(a, int& num_fails) 10 | #define DEVICE_ASSERT_EQ(a, b) \ 11 | if (a != b) { \ 12 | ++num_fails; \ 13 | return; \ 14 | } 15 | #define DEVICE_EXPECT_EQ(a, b) \ 16 | if (a != b) { \ 17 | ++num_fails; \ 18 | } 19 | #define DEVICE_ASSERT_NE(a, b) \ 20 | if (a == b) { \ 21 | ++num_fails; \ 22 | return; \ 23 | } 24 | #define DEVICE_EXPECT_NE(a, b) \ 25 | if (a == b) { \ 26 | ++num_fails; \ 27 | } 28 | #define DEVICE_ASSERT_GT(a, b) \ 29 | if (a <= b) { \ 30 | ++num_fails; \ 31 | return; \ 32 | } 33 | #define DEVICE_EXPECT_GT(a, b) \ 34 | if (a <= b) { \ 35 | ++num_fails; \ 36 | } 37 | #define DEVICE_ASSERT_LT(a, b) \ 38 | if (a >= b) { \ 39 | ++num_fails; \ 40 | return; \ 41 | } 42 | #define DEVICE_EXPECT_LT(a, b) \ 43 | if (a >= b) { \ 44 | ++num_fails; \ 45 | } 46 | #define DEVICE_EXPECT_TRUE(a) \ 47 | if (!a) { \ 48 | ++num_fails; \ 49 | } 50 | #define DEVICE_EXPECT_FALSE(a) \ 51 | if (a) { \ 52 | ++num_fails; \ 53 | } 54 | #else 55 | #define DEVICE_TEST(a) [=] HPC_DEVICE(a) 56 | #define DEVICE_ASSERT_EQ(a, b) ASSERT_EQ(a, b) 57 | #define DEVICE_EXPECT_EQ(a, b) EXPECT_EQ(a, b) 58 | #define DEVICE_ASSERT_NE(a, b) ASSERT_NE(a, b) 59 | #define DEVICE_EXPECT_NE(a, b) EXPECT_NE(a, b) 60 | #define DEVICE_ASSERT_GT(a, b) ASSERT_GT(a, b) 61 | #define DEVICE_EXPECT_GT(a, b) EXPECT_GT(a, b) 62 | #define DEVICE_ASSERT_LT(a, b) ASSERT_LT(a, b) 63 | #define DEVICE_EXPECT_LT(a, b) EXPECT_LT(a, b) 64 | #define DEVICE_EXPECT_TRUE(a) EXPECT_TRUE(a) 65 | #define DEVICE_EXPECT_FALSE(a) EXPECT_FALSE(a) 66 | #endif 67 | 68 | namespace unit { 69 | 70 | namespace impl { 71 | 72 | template 73 | class device_test 74 | { 75 | public: 76 | device_test(const FuncType& func) : func(func) 77 | { 78 | } 79 | 80 | HPC_ALWAYS_INLINE HPC_DEVICE int 81 | operator()(typename Range::value_type i) const 82 | { 83 | #ifdef LGR_ENABLE_CUDA 84 | int num_fails = 0; 85 | func(i, num_fails); 86 | return num_fails; 87 | #else 88 | func(i); 89 | return 0; 90 | #endif 91 | } 92 | 93 | private: 94 | const FuncType func; 95 | }; 96 | 97 | } // namespace impl 98 | 99 | template 100 | HPC_NOINLINE void 101 | test_for_each(PolicyType policy, Range const& range, const FuncType func) 102 | { 103 | int init(0); 104 | auto num_test_failures = 105 | hpc::transform_reduce(policy, range, init, hpc::plus(), impl::device_test(func)); 106 | EXPECT_EQ(num_test_failures, 0); 107 | } 108 | 109 | } // namespace unit 110 | -------------------------------------------------------------------------------- /unit_tests/unit_otm_distance_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | inline void 17 | check_single_tetrahedron_node_neighbor_squared_distances( 18 | const lgr::state& s, 19 | const lgr::search_util::node_neighbors& n, 20 | hpc::device_vector, lgr::node_index>& nodes_to_neighbor_squared_distances) 21 | { 22 | ASSERT_EQ(nodes_to_neighbor_squared_distances.size(), 12); 23 | 24 | auto n2n_distances = nodes_to_neighbor_squared_distances.cbegin(); 25 | auto n2n_neighbors = n.entities_to_neighbor_ordinals.cbegin(); 26 | auto check_func = DEVICE_TEST(lgr::node_index node) 27 | { 28 | constexpr hpc::length expected[4][3]{{1.0, 1.0, 1.0}, {1.0, 2.0, 2.0}, {1.0, 2.0, 2.0}, {1.0, 2.0, 2.0}}; 29 | auto node_neighbors = n2n_neighbors[node]; 30 | DEVICE_ASSERT_EQ(node_neighbors.size(), 3); 31 | for (int i = 0; i < 3; ++i) { 32 | auto const expected_dist = expected[hpc::weaken(node)][i]; 33 | auto const node_to_node_dist = n2n_distances[node_neighbors[i]]; 34 | DEVICE_EXPECT_EQ(expected_dist, node_to_node_dist); 35 | } 36 | }; 37 | unit::test_for_each(hpc::device_policy(), s.nodes, check_func); 38 | } 39 | 40 | inline void 41 | check_single_tetrahedron_nearest_node_squared_distance( 42 | const lgr::state& s, 43 | const lgr::search_util::node_neighbors& n, 44 | hpc::device_vector, lgr::node_index>& nodes_to_neighbor_squared_distances) 45 | { 46 | ASSERT_EQ(nodes_to_neighbor_squared_distances.size(), 4); 47 | 48 | auto n2n_distances = nodes_to_neighbor_squared_distances.cbegin(); 49 | auto n2n_neighbors = n.entities_to_neighbor_ordinals.cbegin(); 50 | auto check_func = DEVICE_TEST(lgr::node_index node) 51 | { 52 | constexpr hpc::length expected = 1.0; 53 | auto closest_neighbor = n2n_neighbors[node]; 54 | DEVICE_ASSERT_EQ(closest_neighbor.size(), 1); 55 | auto const node_to_node_dist = n2n_distances[closest_neighbor[0]]; 56 | DEVICE_EXPECT_EQ(expected, node_to_node_dist); 57 | }; 58 | unit::test_for_each(hpc::device_policy(), s.nodes, check_func); 59 | } 60 | 61 | inline void 62 | check_two_tetrahedron_point_neighbor_squared_distances( 63 | const lgr::state& s, 64 | const lgr::search_util::point_neighbors& n, 65 | hpc::device_vector, lgr::point_index>& pts_to_neighbor_squared_distances) 66 | { 67 | ASSERT_EQ(pts_to_neighbor_squared_distances.size(), 2); 68 | 69 | auto p2p_distances = pts_to_neighbor_squared_distances.cbegin(); 70 | auto p2p_neighbors = n.entities_to_neighbor_ordinals.cbegin(); 71 | auto check_func = DEVICE_TEST(lgr::point_index pt) 72 | { 73 | constexpr hpc::length expected[2][1]{{0.1875}, {0.1875}}; 74 | auto pt_neighbors = p2p_neighbors[pt]; 75 | DEVICE_ASSERT_EQ(pt_neighbors.size(), 1); 76 | auto const expected_dist = expected[hpc::weaken(pt)][0]; 77 | auto const node_to_node_dist = p2p_distances[pt_neighbors[0]]; 78 | DEVICE_EXPECT_EQ(expected_dist, node_to_node_dist); 79 | }; 80 | unit::test_for_each(hpc::device_policy(), s.points, check_func); 81 | } 82 | -------------------------------------------------------------------------------- /unit_tests/ut_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main(int argc, char* argv[]) 5 | { 6 | ::testing::GTEST_FLAG(print_time) = true; 7 | ::testing::InitGoogleTest(&argc, argv); 8 | auto const retval = RUN_ALL_TESTS(); 9 | return retval; 10 | } 11 | -------------------------------------------------------------------------------- /unit_tests/vtk.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | TEST(vtk, canPrintOtmStateToFile) 10 | { 11 | lgr::state s; 12 | using MI = lgr::material_index; 13 | lgr::input in(MI(0), MI(0)); 14 | 15 | tetrahedron_single_point(s); 16 | lgr::otm_update_nodal_mass(s); 17 | lgr::otm_allocate_state(in, s); 18 | 19 | lgr::otm_file_writer writer("tetrahedron_single_point"); 20 | 21 | writer.capture(s); 22 | 23 | ASSERT_EQ(writer.host_s.nodes.size(), s.nodes.size()); 24 | ASSERT_EQ(writer.host_s.points.size(), s.points.size()); 25 | 26 | ASSERT_EQ(writer.host_s.x.size(), s.x.size()); 27 | ASSERT_EQ(writer.host_s.u.size(), s.u.size()); 28 | ASSERT_EQ(writer.host_s.v.size(), s.v.size()); 29 | ASSERT_EQ(writer.host_s.mass.size(), s.mass.size()); 30 | 31 | ASSERT_EQ(writer.host_s.xp.size(), s.xp.size()); 32 | ASSERT_EQ(writer.host_s.rho.size(), s.V.size()); 33 | ASSERT_EQ(writer.host_s.sigma.size(), s.sigma_full.size()); 34 | ASSERT_EQ(writer.host_s.F_total.size(), s.F_total.size()); 35 | ASSERT_EQ(writer.host_s.G.size(), s.G.size()); 36 | ASSERT_EQ(writer.host_s.K.size(), s.K.size()); 37 | 38 | writer.write(0); 39 | 40 | unlink("tetrahedron_single_point_nodes_0.vtk"); 41 | unlink("tetrahedron_single_point_points_0.vtk"); 42 | } 43 | --------------------------------------------------------------------------------