diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index c78e6b2cfd..2213e52bfb 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,2 +1,4 @@ # `pre-commit run --all-files` (https://github.com/Ferrite-FEM/Ferrite.jl/pull/928) 68e1ab15bea4618f76b9ed1d850e2ce33375e266 +# Apply Runic.jl formatting (https://github.com/Ferrite-FEM/Ferrite.jl/pull/1096) +c50ce06f2261398ecfe6da4578e50baff88cf563 diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml index 08dc6b83e0..8f0a93a2dc 100644 --- a/.github/workflows/Check.yml +++ b/.github/workflows/Check.yml @@ -12,6 +12,22 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + env: + # Skip runic-pre-commit since we use runic-action below instead + SKIP: runic + + runic: + name: "Runic" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: '1.11' + - uses: julia-actions/cache@v2 + - uses: fredrikekre/runic-action@v1 + with: + version: "d9a27b9d6279a103fedc7202258207a7b7afe18c" explicit-imports: runs-on: ubuntu-latest diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index f854b3fecc..7833a0d136 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -26,6 +26,7 @@ jobs: # - {user: 'Ferrite-FEM', repo: 'FerriteDistributed.jl'} # Requires more efforts to be updated to 1.0 - {user: 'Ferrite-FEM', repo: 'FerriteGmsh.jl'} - {user: 'Ferrite-FEM', repo: 'FerriteMeshParser.jl'} + - {user: 'Ferrite-FEM', repo: 'FerriteInterfaceElements.jl'} # - {user: 'Ferrite-FEM', repo: 'FerriteViz.jl'} # Requires release steps: - uses: actions/checkout@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c219d29c1..4c0016add1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,15 @@ repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + - repo: 'https://github.com/pre-commit/pre-commit-hooks' + rev: v5.0.0 hooks: - - id: check-added-large-files - - id: check-case-conflict - - id: check-toml - - id: check-yaml - - id: end-of-file-fixer - - id: mixed-line-ending - - id: trailing-whitespace + - id: check-added-large-files + - id: check-case-conflict + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + - repo: 'https://github.com/fredrikekre/runic-pre-commit' + rev: v1.0.0 + hooks: + - id: runic diff --git a/CHANGELOG.md b/CHANGELOG.md index a2be9f3a67..66bf5b7020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ``` ([#1083]) +### Other + - Ferrite now uses [Runic.jl](https://github.com/fredrikekre/Runic.jl) for code formatting. + ([#1096]) + ## [v1.0.0] - 2024-09-30 Ferrite version 1.0 is a relatively large release, with a lot of new features, improvements, @@ -1027,3 +1031,4 @@ poking into Ferrite internals: [#1058]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1058 [#1059]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1059 [#1083]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1083 +[#1096]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1096 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fcaa50c0e..f515933987 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,6 +70,38 @@ issues][open-issues]. Remember to always include (when applicable): i) unit tests which exercises the new code, ii) documentation, iii) a note in the [CHANGELOG.md](CHANGELOG.md) file. +### Code formatting + +Ferrite uses [Runic.jl][runic] for code formatting. You can install Runic with the following +command: + +```shell +julia --project=@runic -e 'using Pkg; Pkg.add(url = "https://github.com/fredrikekre/Runic.jl")' +``` + +and you can then run the formatter from the root of the repository with: + +```shell +julia --project=@runic -e 'using Runic; exit(Runic.main(ARGS))' -- --inplace . +``` + +Refer to the Runic.jl [README][runic-readme] for more details (such as editor integration). + + +### pre-commit hooks + +Ferrite uses [pre-commit] to run various checks (including Runic formatting). After +installing pre-commit (refer to the [pre-commit installation +instructions][pre-commit-install]) run the following from the root of the repository to +configure pre-commit to run before each commit: + +```shell +pre-commit install +``` + +Note that this currently requires Runic.jl to be installed in the shared `@runic` +environment according to the instructions above. + [documenter]: https://juliadocs.github.io/Documenter.jl/ [first-contributions]: https://github.com/firstcontributions/first-contributions @@ -80,6 +112,10 @@ ii) documentation, iii) a note in the [CHANGELOG.md](CHANGELOG.md) file. [new-discussion]: https://github.com/Ferrite-FEM/Ferrite.jl/discussions/new [new-issue]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/new [open-issues]: https://github.com/Ferrite-FEM/Ferrite.jl/issues +[pre-commit-install]: https://pre-commit.com/#install +[pre-commit]: https://pre-commit.com/ +[runic-readme]: https://github.com/fredrikekre/Runic.jl/blob/master/README.md +[runic]: https://github.com/fredrikekre/Runic.jl [so-mre]: https://stackoverflow.com/help/minimal-reproducible-example [tim-doc]: https://youtu.be/ZpH1ry8qqfw [tim-git]: https://youtu.be/cquJ9kPkwR8 diff --git a/benchmark/benchmarks-assembly.jl b/benchmark/benchmarks-assembly.jl index 619185f602..6273a7374e 100644 --- a/benchmark/benchmarks-assembly.jl +++ b/benchmark/benchmarks-assembly.jl @@ -6,19 +6,19 @@ SUITE["assembly"] = BenchmarkGroup() # Permute over common combinations for some commonly required local matrices. SUITE["assembly"]["common-local"] = BenchmarkGroup() COMMON_LOCAL_ASSEMBLY = SUITE["assembly"]["common-local"] -for spatial_dim ∈ 1:3 - ξ_dummy = Vec{spatial_dim}(ntuple(x->0.0, spatial_dim)) - COMMON_LOCAL_ASSEMBLY["spatial-dim",spatial_dim] = BenchmarkGroup() - for geo_type ∈ FerriteBenchmarkHelper.geo_types_for_spatial_dim(spatial_dim) - COMMON_LOCAL_ASSEMBLY["spatial-dim",spatial_dim][string(geo_type)] = BenchmarkGroup() +for spatial_dim in 1:3 + ξ_dummy = Vec{spatial_dim}(ntuple(x -> 0.0, spatial_dim)) + COMMON_LOCAL_ASSEMBLY["spatial-dim", spatial_dim] = BenchmarkGroup() + for geo_type in FerriteBenchmarkHelper.geo_types_for_spatial_dim(spatial_dim) + COMMON_LOCAL_ASSEMBLY["spatial-dim", spatial_dim][string(geo_type)] = BenchmarkGroup() - grid = generate_grid(geo_type, tuple(repeat([2], spatial_dim)...)); + grid = generate_grid(geo_type, tuple(repeat([2], spatial_dim)...)) topology = ExclusiveTopology(grid) ip_geo = Ferrite.geometric_interpolation(geo_type) ref_type = FerriteBenchmarkHelper.getrefshape(geo_type) # Nodal interpolation tests - for order ∈ 1:2, ip_type ∈ [Lagrange, Serendipity] + for order in 1:2, ip_type in [Lagrange, Serendipity] ip_type == Serendipity && (order < 2 || ref_type ∉ (RefQuadrilateral, RefHexahedron)) && continue ip = ip_type{ref_type, order}() ip_vectorized = ip^spatial_dim @@ -26,24 +26,24 @@ for spatial_dim ∈ 1:3 # Skip over elements which are not implemented !applicable(Ferrite.shape_value, ip, ξ_dummy, 1) && continue - qr = QuadratureRule{ref_type}(2*order-1) + qr = QuadratureRule{ref_type}(2 * order - 1) # Currently we just benchmark nodal Lagrange bases. - COMMON_LOCAL_ASSEMBLY["spatial-dim",spatial_dim][string(geo_type)][string(ip_type),string(order)] = BenchmarkGroup() - LAGRANGE_SUITE = COMMON_LOCAL_ASSEMBLY["spatial-dim",spatial_dim][string(geo_type)][string(ip_type),string(order)] + COMMON_LOCAL_ASSEMBLY["spatial-dim", spatial_dim][string(geo_type)][string(ip_type), string(order)] = BenchmarkGroup() + LAGRANGE_SUITE = COMMON_LOCAL_ASSEMBLY["spatial-dim", spatial_dim][string(geo_type)][string(ip_type), string(order)] LAGRANGE_SUITE["fe-values"] = BenchmarkGroup() LAGRANGE_SUITE["ritz-galerkin"] = BenchmarkGroup() LAGRANGE_SUITE["petrov-galerkin"] = BenchmarkGroup() # Note: at the time of writing this PR the ctor makes the heavy lifting and caches important values. - LAGRANGE_SUITE["fe-values"]["scalar"] = @benchmarkable CellValues($qr, $ip, $ip_geo); - LAGRANGE_SUITE["fe-values"]["vector"] = @benchmarkable CellValues($qr, $ip_vectorized, $ip_geo); + LAGRANGE_SUITE["fe-values"]["scalar"] = @benchmarkable CellValues($qr, $ip, $ip_geo) + LAGRANGE_SUITE["fe-values"]["vector"] = @benchmarkable CellValues($qr, $ip_vectorized, $ip_geo) - csv = CellValues(qr, ip, ip_geo); - csv2 = CellValues(qr, ip, ip_geo); + csv = CellValues(qr, ip, ip_geo) + csv2 = CellValues(qr, ip, ip_geo) - cvv = CellValues(qr, ip_vectorized, ip_geo); - cvv2 = CellValues(qr, ip_vectorized, ip_geo); + cvv = CellValues(qr, ip_vectorized, ip_geo) + cvv2 = CellValues(qr, ip_vectorized, ip_geo) # Scalar shape φ and test ψ: ∫ φ ψ LAGRANGE_SUITE["ritz-galerkin"]["mass"] = @benchmarkable FerriteAssemblyHelper._generalized_ritz_galerkin_assemble_local_matrix($grid, $csv, shape_value, shape_value, *) @@ -61,16 +61,16 @@ for spatial_dim ∈ 1:3 LAGRANGE_SUITE["petrov-galerkin"]["pressure-velocity"] = @benchmarkable FerriteAssemblyHelper._generalized_petrov_galerkin_assemble_local_matrix($grid, $cvv, shape_divergence, $csv, shape_value, *) if spatial_dim > 1 - qr_facet = FacetQuadratureRule{ref_type}(2*order-1) - fsv = FacetValues(qr_facet, ip, ip_geo); - fsv2 = FacetValues(qr_facet, ip, ip_geo); + qr_facet = FacetQuadratureRule{ref_type}(2 * order - 1) + fsv = FacetValues(qr_facet, ip, ip_geo) + fsv2 = FacetValues(qr_facet, ip, ip_geo) LAGRANGE_SUITE["ritz-galerkin"]["face-flux"] = @benchmarkable FerriteAssemblyHelper._generalized_ritz_galerkin_assemble_local_matrix($grid, $fsv, shape_gradient, shape_value, *) LAGRANGE_SUITE["petrov-galerkin"]["face-flux"] = @benchmarkable FerriteAssemblyHelper._generalized_petrov_galerkin_assemble_local_matrix($grid, $fsv, shape_gradient, $fsv2, shape_value, *) ip = DiscontinuousLagrange{ref_type, order}() - isv = InterfaceValues(qr_facet, ip, ip_geo); - isv2 = InterfaceValues(qr_facet, ip, ip_geo); + isv = InterfaceValues(qr_facet, ip, ip_geo) + isv2 = InterfaceValues(qr_facet, ip, ip_geo) dh = DofHandler(grid) add!(dh, :u, ip) close!(dh) diff --git a/benchmark/benchmarks-boundary-conditions.jl b/benchmark/benchmarks-boundary-conditions.jl index 6964196f70..43624193ff 100644 --- a/benchmark/benchmarks-boundary-conditions.jl +++ b/benchmark/benchmarks-boundary-conditions.jl @@ -6,37 +6,37 @@ SUITE["boundary-conditions"] = BenchmarkGroup() SUITE["boundary-conditions"]["Dirichlet"] = BenchmarkGroup() DIRICHLET_SUITE = SUITE["boundary-conditions"]["Dirichlet"] # span artificial scope... -for spatial_dim ∈ [2] +for spatial_dim in [2] # Benchmark application on global system DIRICHLET_SUITE["global"] = BenchmarkGroup() geo_type = Quadrilateral - grid = generate_grid(geo_type, ntuple(x->2, spatial_dim)); + grid = generate_grid(geo_type, ntuple(x -> 2, spatial_dim)) ref_type = FerriteBenchmarkHelper.getrefshape(geo_type) ip_geo = Ferrite.geometric_interpolation(geo_type) order = 2 # assemble a mass matrix to apply BCs on (because its cheap) ip = Lagrange{ref_type, order}() - qr = QuadratureRule{ref_type}(2*order-1) - cellvalues = CellValues(qr, ip, ip_geo); + qr = QuadratureRule{ref_type}(2 * order - 1) + cellvalues = CellValues(qr, ip, ip_geo) dh = DofHandler(grid) push!(dh, :u, 1, ip) - close!(dh); + close!(dh) - ch = ConstraintHandler(dh); - ∂Ω = union(getfacetset.((grid, ), ["left"])...); + ch = ConstraintHandler(dh) + ∂Ω = union(getfacetset.((grid,), ["left"])...) dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) - add!(ch, dbc); - close!(ch); + add!(ch, dbc) + close!(ch) # Non-symmetric application - M, f = FerriteAssemblyHelper._assemble_mass(dh, cellvalues, false); - DIRICHLET_SUITE["global"]["apply!(M,f)"] = @benchmarkable apply!($M, $f, $ch); + M, f = FerriteAssemblyHelper._assemble_mass(dh, cellvalues, false) + DIRICHLET_SUITE["global"]["apply!(M,f)"] = @benchmarkable apply!($M, $f, $ch) # Symmetric application - M, f = FerriteAssemblyHelper._assemble_mass(dh, cellvalues, true); - DIRICHLET_SUITE["global"]["apply!(M_sym,f)"] = @benchmarkable apply!($M, $f, $ch); + M, f = FerriteAssemblyHelper._assemble_mass(dh, cellvalues, true) + DIRICHLET_SUITE["global"]["apply!(M_sym,f)"] = @benchmarkable apply!($M, $f, $ch) - DIRICHLET_SUITE["global"]["apply!(f)"] = @benchmarkable apply!($f, $ch); - DIRICHLET_SUITE["global"]["apply_zero!(f)"] = @benchmarkable apply!($f, $ch); + DIRICHLET_SUITE["global"]["apply!(f)"] = @benchmarkable apply!($f, $ch) + DIRICHLET_SUITE["global"]["apply_zero!(f)"] = @benchmarkable apply!($f, $ch) end diff --git a/benchmark/benchmarks-dofs.jl b/benchmark/benchmarks-dofs.jl index c3f611bbbe..b83490b56e 100644 --- a/benchmark/benchmarks-dofs.jl +++ b/benchmark/benchmarks-dofs.jl @@ -1,4 +1,3 @@ - #----------------------------------------------------------------------# # Benchmarks around the dof management #----------------------------------------------------------------------# @@ -6,33 +5,33 @@ SUITE["dof-management"] = BenchmarkGroup() SUITE["dof-management"]["numbering"] = BenchmarkGroup() # !!! NOTE close! must wrapped into a custom function, because consecutive calls to close!, since the dofs are already distributed. NUMBERING_SUITE = SUITE["dof-management"]["numbering"] -for spatial_dim ∈ [3]# 1:3 - NUMBERING_SUITE["spatial-dim",spatial_dim] = BenchmarkGroup() - for geo_type ∈ FerriteBenchmarkHelper.geo_types_for_spatial_dim(spatial_dim) - NUMBERING_SUITE["spatial-dim",spatial_dim][string(geo_type)] = BenchmarkGroup() +for spatial_dim in [3] # 1:3 + NUMBERING_SUITE["spatial-dim", spatial_dim] = BenchmarkGroup() + for geo_type in FerriteBenchmarkHelper.geo_types_for_spatial_dim(spatial_dim) + NUMBERING_SUITE["spatial-dim", spatial_dim][string(geo_type)] = BenchmarkGroup() ref_type = FerriteBenchmarkHelper.getrefshape(geo_type) - for grid_size ∈ [2]#[3, 6, 9] #multiple grid sized to estimate computational complexity... - NUMBERING_SUITE["spatial-dim",spatial_dim][string(geo_type)]["grid-size-",grid_size] = BenchmarkGroup() - NUMBERING_SUITE["spatial-dim",spatial_dim][string(geo_type)]["grid-size-",grid_size] = BenchmarkGroup() + for grid_size in [2] #[3, 6, 9] #multiple grid sized to estimate computational complexity... + NUMBERING_SUITE["spatial-dim", spatial_dim][string(geo_type)]["grid-size-", grid_size] = BenchmarkGroup() + NUMBERING_SUITE["spatial-dim", spatial_dim][string(geo_type)]["grid-size-", grid_size] = BenchmarkGroup() - grid = generate_grid(geo_type, ntuple(x->grid_size, spatial_dim)); + grid = generate_grid(geo_type, ntuple(x -> grid_size, spatial_dim)) - for field_dim ∈ [3]#1:3 - NUMBERING_SUITE["spatial-dim",spatial_dim][string(geo_type)]["grid-size-",grid_size]["field-dim-", field_dim] = BenchmarkGroup() - NUMBERING_FIELD_DIM_SUITE = NUMBERING_SUITE["spatial-dim",spatial_dim][string(geo_type)]["grid-size-",grid_size]["field-dim-", field_dim] + for field_dim in [3] #1:3 + NUMBERING_SUITE["spatial-dim", spatial_dim][string(geo_type)]["grid-size-", grid_size]["field-dim-", field_dim] = BenchmarkGroup() + NUMBERING_FIELD_DIM_SUITE = NUMBERING_SUITE["spatial-dim", spatial_dim][string(geo_type)]["grid-size-", grid_size]["field-dim-", field_dim] # Lagrange tests - for order ∈ 1:2 + for order in 1:2 ip = Lagrange{ref_type, order}() # Skip over elements which are not implemented - ξ_dummy = Vec{spatial_dim}(ntuple(x->0.0, spatial_dim)) + ξ_dummy = Vec{spatial_dim}(ntuple(x -> 0.0, spatial_dim)) !applicable(Ferrite.shape_value, ip, ξ_dummy, 1) && continue - NUMBERING_FIELD_DIM_SUITE["Lagrange",order] = BenchmarkGroup() - LAGRANGE_SUITE = NUMBERING_FIELD_DIM_SUITE["Lagrange",order] - order2 = max(order-1, 1) + NUMBERING_FIELD_DIM_SUITE["Lagrange", order] = BenchmarkGroup() + LAGRANGE_SUITE = NUMBERING_FIELD_DIM_SUITE["Lagrange", order] + order2 = max(order - 1, 1) ip2 = Lagrange{ref_type, order2}() LAGRANGE_SUITE["DofHandler"] = BenchmarkGroup() @@ -40,7 +39,7 @@ for spatial_dim ∈ [3]# 1:3 close_helper = function(grid, ip) dh = DofHandler(grid) push!(dh, :u, field_dim, ip) - close!(dh) + return close!(dh) end LAGRANGE_SUITE["DofHandler"]["one-field"] = @benchmarkable $close_helper($grid, $ip) @@ -48,24 +47,24 @@ for spatial_dim ∈ [3]# 1:3 dh = DofHandler(grid) push!(dh, :u, field_dim, ip) push!(dh, :p, 1, ip2) - close!(dh) + return close!(dh) end LAGRANGE_SUITE["DofHandler"]["two-fields"] = @benchmarkable $close_helper($grid, $ip, $ip2) close_helper = function(grid) dh = DofHandler(grid) - sdh = SubDofHandler(dh, Set(1:Int(round(getncells(grid)/2)))) + sdh = SubDofHandler(dh, Set(1:Int(round(getncells(grid) / 2)))) add!(sdh, :u, ip^field_dim) - close!(dh) + return close!(dh) end LAGRANGE_SUITE["DofHandler"]["one-field-subdomain"] = @benchmarkable $close_helper($grid) close_helper = function(grid) dh = DofHandler(grid) - sdh = SubDofHandler(dh, Set(1:Int(round(getncells(grid)/2)))) + sdh = SubDofHandler(dh, Set(1:Int(round(getncells(grid) / 2)))) add!(sdh, :u, ip^field_dim) add!(sdh, :p, ip2) - close!(dh) + return close!(dh) end LAGRANGE_SUITE["DofHandler"]["two-fields-subdomain"] = @benchmarkable $close_helper($grid) end diff --git a/benchmark/benchmarks-mesh.jl b/benchmark/benchmarks-mesh.jl index 6513bdc7b4..063c4c7dd2 100644 --- a/benchmark/benchmarks-mesh.jl +++ b/benchmark/benchmarks-mesh.jl @@ -1,4 +1,3 @@ - #----------------------------------------------------------------------# # Benchmarks for mesh functionality within Ferrite #----------------------------------------------------------------------# @@ -10,10 +9,10 @@ SUITE["mesh"]["generator"] = BenchmarkGroup() # Structured hyperrectangle generators SUITE["mesh"]["generator"]["hyperrectangle"] = BenchmarkGroup() HYPERRECTANGLE_GENERATOR = SUITE["mesh"]["generator"]["hyperrectangle"] -for spatial_dim ∈ 1:3 - HYPERRECTANGLE_GENERATOR["spatial-dim",spatial_dim] = BenchmarkGroup() - for geo_type ∈ FerriteBenchmarkHelper.geo_types_for_spatial_dim(spatial_dim) - HYPERRECTANGLE_GENERATOR["spatial-dim",spatial_dim][string(geo_type)] = @benchmarkable generate_grid($geo_type, $(ntuple(x->4, spatial_dim))); +for spatial_dim in 1:3 + HYPERRECTANGLE_GENERATOR["spatial-dim", spatial_dim] = BenchmarkGroup() + for geo_type in FerriteBenchmarkHelper.geo_types_for_spatial_dim(spatial_dim) + HYPERRECTANGLE_GENERATOR["spatial-dim", spatial_dim][string(geo_type)] = @benchmarkable generate_grid($geo_type, $(ntuple(x -> 4, spatial_dim))) end end diff --git a/benchmark/helper.jl b/benchmark/helper.jl index 369681b1db..3ffadc14a4 100644 --- a/benchmark/helper.jl +++ b/benchmark/helper.jl @@ -1,209 +1,209 @@ module FerriteBenchmarkHelper -using Ferrite -using LinearAlgebra: Symmetric + using Ferrite + using LinearAlgebra: Symmetric -function geo_types_for_spatial_dim(spatial_dim) - spatial_dim == 1 && return [Line, QuadraticLine] - spatial_dim == 2 && return [Triangle, QuadraticTriangle, Quadrilateral, QuadraticQuadrilateral] - spatial_dim == 3 && return [Tetrahedron, Hexahedron] # Quadratic* not yet functional in 3D. 3D triangle missing. Embedded also missing. -end + function geo_types_for_spatial_dim(spatial_dim) + spatial_dim == 1 && return [Line, QuadraticLine] + spatial_dim == 2 && return [Triangle, QuadraticTriangle, Quadrilateral, QuadraticQuadrilateral] + return spatial_dim == 3 && return [Tetrahedron, Hexahedron] # Quadratic* not yet functional in 3D. 3D triangle missing. Embedded also missing. + end -getrefshape(::Type{T}) where {refshape, T <: Ferrite.AbstractCell{refshape}} = refshape + getrefshape(::Type{T}) where {refshape, T <: Ferrite.AbstractCell{refshape}} = refshape end module FerriteAssemblyHelper -using Ferrite - -# Minimal Ritz-Galerkin type local assembly loop. -function _generalized_ritz_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, cellvalues::CellValues, f_shape, f_test, op) - n_basefuncs = getnbasefunctions(cellvalues) - - Ke = zeros(n_basefuncs, n_basefuncs) - - X = getcoordinates(grid, 1) - reinit!(cellvalues, X) - - for q_point in 1:getnquadpoints(cellvalues) - dΩ = getdetJdV(cellvalues, q_point) - for i in 1:n_basefuncs - test = f_test(cellvalues, q_point, i) - for j in 1:n_basefuncs - shape = f_shape(cellvalues, q_point, j) - Ke[i, j] += op(test, shape) * dΩ - end - end - end - - Ke -end + using Ferrite -function _generalized_ritz_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, facetvalues::FacetValues, f_shape, f_test, op) - n_basefuncs = getnbasefunctions(facetvalues) + # Minimal Ritz-Galerkin type local assembly loop. + function _generalized_ritz_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, cellvalues::CellValues, f_shape, f_test, op) + n_basefuncs = getnbasefunctions(cellvalues) - f = zeros(n_basefuncs) + Ke = zeros(n_basefuncs, n_basefuncs) - X = getcoordinates(grid, 1) - for facet in 1:nfacets(getcells(grid)[1]) - reinit!(facetvalues, X, facet) + X = getcoordinates(grid, 1) + reinit!(cellvalues, X) - for q_point in 1:getnquadpoints(facetvalues) - n = getnormal(facetvalues, q_point) - dΓ = getdetJdV(facetvalues, q_point) + for q_point in 1:getnquadpoints(cellvalues) + dΩ = getdetJdV(cellvalues, q_point) for i in 1:n_basefuncs - test = f_test(facetvalues, q_point, i) + test = f_test(cellvalues, q_point, i) for j in 1:n_basefuncs - shape = f_shape(facetvalues, q_point, j) - f[i] += op(test, shape) ⋅ n * dΓ + shape = f_shape(cellvalues, q_point, j) + Ke[i, j] += op(test, shape) * dΩ end end end + + return Ke end - f -end + function _generalized_ritz_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, facetvalues::FacetValues, f_shape, f_test, op) + n_basefuncs = getnbasefunctions(facetvalues) -function _generalized_ritz_galerkin_assemble_interfaces(dh::Ferrite.AbstractDofHandler, interfacevalues::InterfaceValues, f_shape, f_test, op) - n_basefuncs = getnbasefunctions(interfacevalues) + f = zeros(n_basefuncs) - K = zeros(ndofs(dh), ndofs(dh)) + X = getcoordinates(grid, 1) + for facet in 1:nfacets(getcells(grid)[1]) + reinit!(facetvalues, X, facet) - for ic in InterfaceIterator(dh) - reinit!(interfacevalues, ic) - for q_point in 1:getnquadpoints(interfacevalues) - dΓ = getdetJdV(interfacevalues, q_point) - for i in 1:n_basefuncs - test = f_test(interfacevalues, q_point, i) - f_test == shape_value_jump && (test *= getnormal(interfacevalues, q_point)) - for j in 1:n_basefuncs - shape = f_shape(interfacevalues, q_point, j) - f_shape == shape_value_jump && (shape *= getnormal(interfacevalues, q_point)) - K[interfacedofs(ic)[i], interfacedofs(ic)[j]] += op(test, shape) * dΓ + for q_point in 1:getnquadpoints(facetvalues) + n = getnormal(facetvalues, q_point) + dΓ = getdetJdV(facetvalues, q_point) + for i in 1:n_basefuncs + test = f_test(facetvalues, q_point, i) + for j in 1:n_basefuncs + shape = f_shape(facetvalues, q_point, j) + f[i] += op(test, shape) ⋅ n * dΓ + end end end end - end - K -end + return f + end -# Minimal Petrov-Galerkin type local assembly loop. We assume that both function spaces share the same integration rule. Test is applied from the left. -function _generalized_petrov_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, cellvalues_shape::CellValues, f_shape, cellvalues_test::CellValues, f_test, op) - n_basefuncs_shape = getnbasefunctions(cellvalues_shape) - n_basefuncs_test = getnbasefunctions(cellvalues_test) - Ke = zeros(n_basefuncs_test, n_basefuncs_shape) - - #implicit assumption: Same geometry! - X_shape = zeros(Ferrite.get_coordinate_type(grid), Ferrite.getngeobasefunctions(cellvalues_shape)) - getcoordinates!(X_shape, grid, 1) - reinit!(cellvalues_shape, X_shape) - - X_test = zeros(Ferrite.get_coordinate_type(grid), Ferrite.getngeobasefunctions(cellvalues_test)) - getcoordinates!(X_test, grid, 1) - reinit!(cellvalues_test, X_test) - - for q_point in 1:getnquadpoints(cellvalues_test) #assume same quadrature rule - dΩ = getdetJdV(cellvalues_test, q_point) - for i in 1:n_basefuncs_test - test = f_test(cellvalues_test, q_point, i) - for j in 1:n_basefuncs_shape - shape = f_shape(cellvalues_shape, q_point, j) - Ke[i, j] += op(test, shape) * dΩ + function _generalized_ritz_galerkin_assemble_interfaces(dh::Ferrite.AbstractDofHandler, interfacevalues::InterfaceValues, f_shape, f_test, op) + n_basefuncs = getnbasefunctions(interfacevalues) + + K = zeros(ndofs(dh), ndofs(dh)) + + for ic in InterfaceIterator(dh) + reinit!(interfacevalues, ic) + for q_point in 1:getnquadpoints(interfacevalues) + dΓ = getdetJdV(interfacevalues, q_point) + for i in 1:n_basefuncs + test = f_test(interfacevalues, q_point, i) + f_test == shape_value_jump && (test *= getnormal(interfacevalues, q_point)) + for j in 1:n_basefuncs + shape = f_shape(interfacevalues, q_point, j) + f_shape == shape_value_jump && (shape *= getnormal(interfacevalues, q_point)) + K[interfacedofs(ic)[i], interfacedofs(ic)[j]] += op(test, shape) * dΓ + end + end end end - end - Ke -end + return K + end -function _generalized_petrov_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, facetvalues_shape::FacetValues, f_shape, facetvalues_test::FacetValues, f_test, op) - n_basefuncs_shape = getnbasefunctions(facetvalues_shape) - n_basefuncs_test = getnbasefunctions(facetvalues_test) + # Minimal Petrov-Galerkin type local assembly loop. We assume that both function spaces share the same integration rule. Test is applied from the left. + function _generalized_petrov_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, cellvalues_shape::CellValues, f_shape, cellvalues_test::CellValues, f_test, op) + n_basefuncs_shape = getnbasefunctions(cellvalues_shape) + n_basefuncs_test = getnbasefunctions(cellvalues_test) + Ke = zeros(n_basefuncs_test, n_basefuncs_shape) - f = zeros(n_basefuncs_test) + #implicit assumption: Same geometry! + X_shape = zeros(Ferrite.get_coordinate_type(grid), Ferrite.getngeobasefunctions(cellvalues_shape)) + getcoordinates!(X_shape, grid, 1) + reinit!(cellvalues_shape, X_shape) - X_shape = getcoordinates(grid, 1) - X_test = getcoordinates(grid, 1) - for facet in 1:nfacets(getcells(grid)[1]) - reinit!(facetvalues_shape, X_shape, facet) - reinit!(facetvalues_test, X_test, facet) + X_test = zeros(Ferrite.get_coordinate_type(grid), Ferrite.getngeobasefunctions(cellvalues_test)) + getcoordinates!(X_test, grid, 1) + reinit!(cellvalues_test, X_test) - for q_point in 1:getnquadpoints(facetvalues_shape) - n = getnormal(facetvalues_test, q_point) - dΓ = getdetJdV(facetvalues_test, q_point) + for q_point in 1:getnquadpoints(cellvalues_test) #assume same quadrature rule + dΩ = getdetJdV(cellvalues_test, q_point) for i in 1:n_basefuncs_test - test = f_test(facetvalues_test, q_point, i) + test = f_test(cellvalues_test, q_point, i) for j in 1:n_basefuncs_shape - shape = f_shape(facetvalues_shape, q_point, j) - f[i] += op(test, shape) ⋅ n * dΓ + shape = f_shape(cellvalues_shape, q_point, j) + Ke[i, j] += op(test, shape) * dΩ end end end - end - f -end + return Ke + end -function _generalized_petrov_galerkin_assemble_interfaces(dh::Ferrite.AbstractDofHandler, interfacevalues_shape::InterfaceValues, f_shape, interfacevalues_test::InterfaceValues, f_test, op) - n_basefuncs_shape = getnbasefunctions(interfacevalues_shape) - n_basefuncs_test = getnbasefunctions(interfacevalues_test) + function _generalized_petrov_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, facetvalues_shape::FacetValues, f_shape, facetvalues_test::FacetValues, f_test, op) + n_basefuncs_shape = getnbasefunctions(facetvalues_shape) + n_basefuncs_test = getnbasefunctions(facetvalues_test) + + f = zeros(n_basefuncs_test) + + X_shape = getcoordinates(grid, 1) + X_test = getcoordinates(grid, 1) + for facet in 1:nfacets(getcells(grid)[1]) + reinit!(facetvalues_shape, X_shape, facet) + reinit!(facetvalues_test, X_test, facet) + + for q_point in 1:getnquadpoints(facetvalues_shape) + n = getnormal(facetvalues_test, q_point) + dΓ = getdetJdV(facetvalues_test, q_point) + for i in 1:n_basefuncs_test + test = f_test(facetvalues_test, q_point, i) + for j in 1:n_basefuncs_shape + shape = f_shape(facetvalues_shape, q_point, j) + f[i] += op(test, shape) ⋅ n * dΓ + end + end + end + end - K = zeros(ndofs(dh), ndofs(dh)) + return f + end - for ic in InterfaceIterator(dh) - reinit!(interfacevalues_shape, ic) - reinit!(interfacevalues_test, ic) - for q_point in 1:getnquadpoints(interfacevalues_shape) - dΓ = getdetJdV(interfacevalues_test, q_point) - for i in 1:n_basefuncs_test - test = f_test(interfacevalues_test, q_point, i) - f_test == shape_value_jump && (test *= getnormal(interfacevalues_test, q_point)) - for j in 1:n_basefuncs_shape - shape = f_shape(interfacevalues_shape, q_point, j) - f_shape == shape_value_jump && (shape *= getnormal(interfacevalues_shape, q_point)) - K[interfacedofs(ic)[i], interfacedofs(ic)[j]] += op(test, shape) * dΓ + function _generalized_petrov_galerkin_assemble_interfaces(dh::Ferrite.AbstractDofHandler, interfacevalues_shape::InterfaceValues, f_shape, interfacevalues_test::InterfaceValues, f_test, op) + n_basefuncs_shape = getnbasefunctions(interfacevalues_shape) + n_basefuncs_test = getnbasefunctions(interfacevalues_test) + + K = zeros(ndofs(dh), ndofs(dh)) + + for ic in InterfaceIterator(dh) + reinit!(interfacevalues_shape, ic) + reinit!(interfacevalues_test, ic) + for q_point in 1:getnquadpoints(interfacevalues_shape) + dΓ = getdetJdV(interfacevalues_test, q_point) + for i in 1:n_basefuncs_test + test = f_test(interfacevalues_test, q_point, i) + f_test == shape_value_jump && (test *= getnormal(interfacevalues_test, q_point)) + for j in 1:n_basefuncs_shape + shape = f_shape(interfacevalues_shape, q_point, j) + f_shape == shape_value_jump && (shape *= getnormal(interfacevalues_shape, q_point)) + K[interfacedofs(ic)[i], interfacedofs(ic)[j]] += op(test, shape) * dΓ + end end end end - end - K -end + return K + end -function _assemble_mass(dh, cellvalues, sym) - n_basefuncs = getnbasefunctions(cellvalues) - Me = zeros(n_basefuncs, n_basefuncs) - fe = zeros(n_basefuncs) + function _assemble_mass(dh, cellvalues, sym) + n_basefuncs = getnbasefunctions(cellvalues) + Me = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) - M = sym ? Symmetric(allocate_matrix(dh)) : allocate_matrix(dh); - f = zeros(ndofs(dh)) + M = sym ? Symmetric(allocate_matrix(dh)) : allocate_matrix(dh) + f = zeros(ndofs(dh)) - assembler = start_assemble(M, f); - @inbounds for cell in CellIterator(dh) - fill!(Me, 0) + assembler = start_assemble(M, f) + @inbounds for cell in CellIterator(dh) + fill!(Me, 0) - reinit!(cellvalues, cell) + reinit!(cellvalues, cell) - for q_point in 1:getnquadpoints(cellvalues) - dΩ = getdetJdV(cellvalues, q_point) + for q_point in 1:getnquadpoints(cellvalues) + dΩ = getdetJdV(cellvalues, q_point) - for i in 1:n_basefuncs - φ = shape_value(cellvalues, q_point, i) - fe[i] += φ * dΩ - for j in 1:n_basefuncs - ψ = shape_value(cellvalues, q_point, j) - Me[i, j] += (φ * ψ) * dΩ + for i in 1:n_basefuncs + φ = shape_value(cellvalues, q_point, i) + fe[i] += φ * dΩ + for j in 1:n_basefuncs + ψ = shape_value(cellvalues, q_point, j) + Me[i, j] += (φ * ψ) * dΩ + end end end + + assemble!(assembler, celldofs(cell), Me, fe) end - assemble!(assembler, celldofs(cell), Me, fe) + return M, f end - return M, f -end - end diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 4f58f32190..4e4c0c74c8 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.0" +julia_version = "1.11.1" manifest_format = "2.0" project_hash = "f5e9904007b2fd3f4bda9fc8dd33eb629bc31d18" @@ -1325,9 +1325,9 @@ version = "2.35.0" [[deps.Literate]] deps = ["Base64", "IOCapture", "JSON", "REPL"] -git-tree-sha1 = "b9b38448af801760a608b7a7f895f7dcf166f4a5" +git-tree-sha1 = "da046be6d63304f7ba9c1bb04820fb306ba1ab12" uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306" -version = "2.19.1" +version = "2.20.1" [[deps.LiveServer]] deps = ["HTTP", "LoggingExtras", "MIMEs", "Pkg", "Sockets", "Test"] diff --git a/docs/clean.jl b/docs/clean.jl index ea0f052fef..89b781f477 100644 --- a/docs/clean.jl +++ b/docs/clean.jl @@ -5,7 +5,8 @@ const DIR = @__DIR__ const ARTIFACTS = String[] -append!(ARTIFACTS, +append!( + ARTIFACTS, # Untracked files in build directory eachline(`git ls-files --other --directory $(joinpath(DIR, "build"))`), # Untracked files in examples/tutorials/howto/gallery generated by Literate.jl @@ -16,5 +17,5 @@ append!(ARTIFACTS, for artifact in ARTIFACTS @info "Removing $artifact" - rm(artifact; recursive=true, force=true) + rm(artifact; recursive = true, force = true) end diff --git a/docs/generate.jl b/docs/generate.jl index 8bcb08dc78..8d8ce830bc 100644 --- a/docs/generate.jl +++ b/docs/generate.jl @@ -20,7 +20,7 @@ mkpath(GALLERY_OUT) include("download_resources.jl") # Run Literate on all examples -@timeit dto "Literate." for (IN, OUT) in [(TUTORIALS_IN, TUTORIALS_OUT), (HOWTO_IN, HOWTO_OUT), (GALLERY_IN, GALLERY_OUT)], program in readdir(IN; join=true) +@timeit dto "Literate." for (IN, OUT) in [(TUTORIALS_IN, TUTORIALS_OUT), (HOWTO_IN, HOWTO_OUT), (GALLERY_IN, GALLERY_OUT)], program in readdir(IN; join = true) name = basename(program) if endswith(program, ".jl") if !liveserver @@ -32,7 +32,7 @@ include("download_resources.jl") # remove "hidden" lines which are not shown in the markdown line_ending_symbol = occursin(code, "\r\n") ? "\r\n" : "\n" - code_clean = join(filter(x->!endswith(x,"#hide"),split(code, r"\n|\r\n")), line_ending_symbol) + code_clean = join(filter(x -> !endswith(x, "#hide"), split(code, r"\n|\r\n")), line_ending_symbol) code_clean = replace(code_clean, r"^# This file was generated .*$"m => "") code_clean = strip(code_clean) @@ -52,7 +52,7 @@ include("download_resources.jl") end end elseif any(endswith.(program, [".png", ".jpg", ".gif"])) - cp(program, joinpath(OUT, name); force=true) + cp(program, joinpath(OUT, name); force = true) else @warn "ignoring $program" end diff --git a/docs/logo.jl b/docs/logo.jl index bd8605f156..2269019348 100755 --- a/docs/logo.jl +++ b/docs/logo.jl @@ -3,7 +3,8 @@ using PGFPlotsX, Ferrite, FerriteGmsh # Add Julia colors -push!(empty!(PGFPlotsX.CUSTOM_PREAMBLE), +push!( + empty!(PGFPlotsX.CUSTOM_PREAMBLE), raw"\definecolor{julia-blue}{rgb}{0.251, 0.388, 0.847}", raw"\definecolor{julia-green}{rgb}{0.22, 0.596, 0.149}", raw"\definecolor{julia-purple}{rgb}{0.584, 0.345, 0.698}", @@ -13,7 +14,7 @@ push!(empty!(PGFPlotsX.CUSTOM_PREAMBLE), raw"\setmainfont{JuliaMono}", ) -function ferrite_logo(; bounding_box=true, mesh=true) +function ferrite_logo(; bounding_box = true, mesh = true) # Run neper ## Tessalation success(`neper -T -dim 2 -n 6 -id 4 -reg 1`) @@ -51,7 +52,7 @@ function ferrite_logo(; bounding_box=true, mesh=true) # Create the plot logo = @pgf TikzPicture( { - scale = "5" + scale = "5", }, """ % Bounding box @@ -76,10 +77,10 @@ PGFPlotsX.save("logo.png", logo) ## Horizontal function ferrite_logo_horizontal(p) horizontal = @pgf TikzPicture( - raw"\node at (2.5, 0) {\pgftext{\includegraphics{" * abspath(p) * "}}};", - raw"\node[anchor=west] at (5.5, -0.2) {{\fontsize{80}{105}\selectfont Ferrite.jl}};", - raw"\node at (0, 2.5) {};", - raw"\node at (0, -2.5) {};", + raw"\node at (2.5, 0) {\pgftext{\includegraphics{" * abspath(p) * "}}};", + raw"\node[anchor=west] at (5.5, -0.2) {{\fontsize{80}{105}\selectfont Ferrite.jl}};", + raw"\node at (0, 2.5) {};", + raw"\node at (0, -2.5) {};", ) return horizontal end @@ -91,15 +92,15 @@ PGFPlotsX.save("logo-horizontal.png", p) ## Square with the name below function ferrite_logo_named(p) logo = @pgf TikzPicture( - raw"\node at (3, 0) {\pgftext{\includegraphics{" * abspath(p) * "}}};", - # x = 3.0 would center the text, but that looks a bit off. x = 3.11 puts the middle - # point between the vertical bar of the "F" and the vertical bar of the "l" in the middle. - raw"\node[] at (3.11, -3.7) {{\fontsize{35}{35}\selectfont {\color{black}Ferrite.jl}}};", - # raw"\node[] at (3.0, -3.4) {{\fontsize{25}{25}\selectfont {\color{black}Ferrite.jl}}};", - raw"\node at (0, 2.7) {};", - # raw"\draw (3, -2) -- (3, -5);", - # raw"\draw (6.5, -2) -- (6.5, -5);", - # raw"\draw (-0.5, -2) -- (-0.5, -5);", + raw"\node at (3, 0) {\pgftext{\includegraphics{" * abspath(p) * "}}};", + # x = 3.0 would center the text, but that looks a bit off. x = 3.11 puts the middle + # point between the vertical bar of the "F" and the vertical bar of the "l" in the middle. + raw"\node[] at (3.11, -3.7) {{\fontsize{35}{35}\selectfont {\color{black}Ferrite.jl}}};", + # raw"\node[] at (3.0, -3.4) {{\fontsize{25}{25}\selectfont {\color{black}Ferrite.jl}}};", + raw"\node at (0, 2.7) {};", + # raw"\draw (3, -2) -- (3, -5);", + # raw"\draw (6.5, -2) -- (6.5, -5);", + # raw"\draw (-0.5, -2) -- (-0.5, -5);", ) return logo end diff --git a/docs/make.jl b/docs/make.jl index 8f85d58842..001312ffaf 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -31,7 +31,7 @@ Changelog.generate( bibtex_plugin = CitationBibliography( joinpath(@__DIR__, "src", "assets", "references.bib"), - style=:numeric + style = :numeric ) # Build documentation. @@ -40,7 +40,7 @@ bibtex_plugin = CitationBibliography( assets = [ "assets/custom.css", "assets/citations.css", - "assets/favicon.ico" + "assets/favicon.ico", ], canonical = "https://ferrite-fem.github.io/Ferrite.jl/stable", collapselevel = 1, @@ -79,7 +79,7 @@ bibtex_plugin = CitationBibliography( "topics/boundary_conditions.md", "topics/constraints.md", "topics/grid.md", - "topics/export.md" + "topics/export.md", ], "API reference" => [ "Reference overview" => "reference/index.md", @@ -109,7 +109,7 @@ bibtex_plugin = CitationBibliography( # ], "devdocs/index.md", "cited-literature.md", - ], + ], plugins = [ bibtex_plugin, ] @@ -134,7 +134,7 @@ end if !liveserver @timeit dto "deploydocs" deploydocs( repo = "github.com/Ferrite-FEM/Ferrite.jl.git", - push_preview=true, + push_preview = true, versions = [ "stable" => "v^", "v#.#", @@ -147,7 +147,7 @@ if !liveserver "v0.3.7", "v0.3.6", "v0.3.5", - "dev" => "dev" + "dev" => "dev", ] ) end diff --git a/docs/src/literate-gallery/helmholtz.jl b/docs/src/literate-gallery/helmholtz.jl index 1bd8ea47c2..8729c55426 100644 --- a/docs/src/literate-gallery/helmholtz.jl +++ b/docs/src/literate-gallery/helmholtz.jl @@ -67,28 +67,31 @@ close!(dh) # This is a good testing strategy for PDE codes and known as the method of manufactured solutions. function u_ana(x::Vec{2, T}) where {T} - xs = (Vec{2}((-0.5, 0.5)), - Vec{2}((-0.5, -0.5)), - Vec{2}(( 0.5, -0.5))) - σ = 1/8 + xs = ( + Vec{2}((-0.5, 0.5)), + Vec{2}((-0.5, -0.5)), + Vec{2}((0.5, -0.5)), + ) + σ = 1 / 8 s = zero(eltype(x)) for i in 1:3 s += exp(- norm(x - xs[i])^2 / σ^2) end - return max(1e-15 * one(T), s) # Denormals, be gone + return max(1.0e-15 * one(T), s) # Denormals, be gone end; dbcs = ConstraintHandler(dh) # The (strong) Dirichlet boundary condition can be handled automatically by the Ferrite library. -dbc = Dirichlet(:u, union(getfacetset(grid, "top"), getfacetset(grid, "right")), (x,t) -> u_ana(x)) +dbc = Dirichlet(:u, union(getfacetset(grid, "top"), getfacetset(grid, "right")), (x, t) -> u_ana(x)) add!(dbcs, dbc) close!(dbcs) update!(dbcs, 0.0) K = allocate_matrix(dh); -function doassemble(cellvalues::CellValues, facetvalues::FacetValues, - K::SparseMatrixCSC, dh::DofHandler) +function doassemble( + cellvalues::CellValues, facetvalues::FacetValues, K::SparseMatrixCSC, dh::DofHandler + ) b = 1.0 f = zeros(ndofs(dh)) assembler = start_assemble(K, f) @@ -135,7 +138,7 @@ function doassemble(cellvalues::CellValues, facetvalues::FacetValues, #+ for facet in 1:nfacets(cell) if (cellcount, facet) ∈ getfacetset(grid, "left") || - (cellcount, facet) ∈ getfacetset(grid, "bottom") + (cellcount, facet) ∈ getfacetset(grid, "bottom") reinit!(facetvalues, cell, facet) for q_point in 1:getnquadpoints(facetvalues) coords_qp = spatial_coordinate(facetvalues, q_point, coords) @@ -167,6 +170,6 @@ using Test #src #src the true maximum is slightly bigger then 1.0 @test maximum(u) ≈ 0.9952772469054607 #src @test u_ana(Vec{2}((-0.5, -0.5))) ≈ 1 #src -@test u_ana(Vec{2}((0.5, -0.5))) ≈ 1 #src -@test u_ana(Vec{2}((-0.5, 0.5))) ≈ 1 #src +@test u_ana(Vec{2}((0.5, -0.5))) ≈ 1 #src +@test u_ana(Vec{2}((-0.5, 0.5))) ≈ 1 #src println("Helmholtz successful") diff --git a/docs/src/literate-gallery/landau.jl b/docs/src/literate-gallery/landau.jl index cb0919e420..e16ead420e 100644 --- a/docs/src/literate-gallery/landau.jl +++ b/docs/src/literate-gallery/landau.jl @@ -30,16 +30,16 @@ using Tensors using Base.Threads # ## Energy terms # ### 4th order Landau free energy -function Fl(P::Vec{3, T}, α::Vec{3}) where T +function Fl(P::Vec{3, T}, α::Vec{3}) where {T} P2 = Vec{3, T}((P[1]^2, P[2]^2, P[3]^2)) - return (α[1] * sum(P2) + - α[2] * (P[1]^4 + P[2]^4 + P[3]^4)) + - α[3] * ((P2[1] * P2[2] + P2[2]*P2[3]) + P2[1]*P2[3]) + return α[1] * sum(P2) + + α[2] * (P[1]^4 + P[2]^4 + P[3]^4) + + α[3] * ((P2[1] * P2[2] + P2[2] * P2[3]) + P2[1] * P2[3]) end # ### Ginzburg free energy @inline Fg(∇P, G) = 0.5(∇P ⊡ G) ⊡ ∇P # ### GL free energy -F(P, ∇P, params) = Fl(P, params.α) + Fg(∇P, params.G) +F(P, ∇P, params) = Fl(P, params.α) + Fg(∇P, params.G) # ### Parameters that characterize the model struct ModelParams{V, T} @@ -50,43 +50,43 @@ end # ### ThreadCache # This holds the values that each thread will use during the assembly. struct ThreadCache{CV, T, DIM, F <: Function, GC <: GradientConfig, HC <: HessianConfig} - cvP ::CV - element_indices ::Vector{Int} - element_dofs ::Vector{T} - element_gradient ::Vector{T} - element_hessian ::Matrix{T} - element_coords ::Vector{Vec{DIM, T}} + cvP::CV + element_indices::Vector{Int} + element_dofs::Vector{T} + element_gradient::Vector{T} + element_hessian::Matrix{T} + element_coords::Vector{Vec{DIM, T}} element_potential::F - gradconf ::GC - hessconf ::HC + gradconf::GC + hessconf::HC end function ThreadCache(dpc::Int, nodespercell, cvP::CellValues, modelparams, elpotential) - element_indices = zeros(Int, dpc) - element_dofs = zeros(dpc) + element_indices = zeros(Int, dpc) + element_dofs = zeros(dpc) element_gradient = zeros(dpc) - element_hessian = zeros(dpc, dpc) - element_coords = zeros(Vec{3, Float64}, nodespercell) - potfunc = x -> elpotential(x, cvP, modelparams) - gradconf = GradientConfig(potfunc, zeros(dpc), Chunk{12}()) - hessconf = HessianConfig(potfunc, zeros(dpc), Chunk{4}()) + element_hessian = zeros(dpc, dpc) + element_coords = zeros(Vec{3, Float64}, nodespercell) + potfunc = x -> elpotential(x, cvP, modelparams) + gradconf = GradientConfig(potfunc, zeros(dpc), Chunk{12}()) + hessconf = HessianConfig(potfunc, zeros(dpc), Chunk{4}()) return ThreadCache(cvP, element_indices, element_dofs, element_gradient, element_hessian, element_coords, potfunc, gradconf, hessconf) end # ## The Model # everything is combined into a model. mutable struct LandauModel{T, DH <: DofHandler, CH <: ConstraintHandler, TC <: ThreadCache} - dofs ::Vector{T} - dofhandler ::DH - boundaryconds ::CH - threadindices ::Vector{Vector{Int}} - threadcaches ::Vector{TC} + dofs::Vector{T} + dofhandler::DH + boundaryconds::CH + threadindices::Vector{Vector{Int}} + threadcaches::Vector{TC} end function LandauModel(α, G, gridsize, left::Vec{DIM, T}, right::Vec{DIM, T}, elpotential) where {DIM, T} grid = generate_grid(Tetrahedron, gridsize, left, right) threadindices = Ferrite.create_coloring(grid) - qr = QuadratureRule{RefTetrahedron}(2) + qr = QuadratureRule{RefTetrahedron}(2) ipP = Lagrange{RefTetrahedron, 1}()^3 cvP = CellValues(qr, ipP) @@ -108,45 +108,48 @@ function LandauModel(α, G, gridsize, left::Vec{DIM, T}, right::Vec{DIM, T}, elp hessian = allocate_matrix(dofhandler) dpc = ndofs_per_cell(dofhandler) cpc = length(grid.cells[1].nodes) - caches = [ThreadCache(dpc, cpc, copy(cvP), ModelParams(α, G), elpotential) for t=1:nthreads()] + caches = [ThreadCache(dpc, cpc, copy(cvP), ModelParams(α, G), elpotential) for t in 1:nthreads()] return LandauModel(dofvector, dofhandler, boundaryconds, threadindices, caches) end # utility to quickly save a model -function save_landau(path, model, dofs=model.dofs) +function save_landau(path, model, dofs = model.dofs) VTKGridFile(path, model.dofhandler) do vtk write_solution(vtk, model.dofhandler, dofs) end + return end # ## Assembly # This macro defines most of the assembly step, since the structure is the same for # the energy, gradient and Hessian calculations. macro assemble!(innerbody) - esc(quote - dofhandler = model.dofhandler - for indices in model.threadindices - @threads for i in indices - cache = model.threadcaches[threadid()] - eldofs = cache.element_dofs - nodeids = dofhandler.grid.cells[i].nodes - for j=1:length(cache.element_coords) - cache.element_coords[j] = dofhandler.grid.nodes[nodeids[j]].x + return esc( + quote + dofhandler = model.dofhandler + for indices in model.threadindices + @threads for i in indices + cache = model.threadcaches[threadid()] + eldofs = cache.element_dofs + nodeids = dofhandler.grid.cells[i].nodes + for j in 1:length(cache.element_coords) + cache.element_coords[j] = dofhandler.grid.nodes[nodeids[j]].x + end + reinit!(cache.cvP, cache.element_coords) + + celldofs!(cache.element_indices, dofhandler, i) + for j in 1:length(cache.element_dofs) + eldofs[j] = dofvector[cache.element_indices[j]] + end + $innerbody end - reinit!(cache.cvP, cache.element_coords) - - celldofs!(cache.element_indices, dofhandler, i) - for j=1:length(cache.element_dofs) - eldofs[j] = dofvector[cache.element_indices[j]] - end - $innerbody end end - end) + ) end # This calculates the total energy calculation of the grid -function F(dofvector::Vector{T}, model) where T +function F(dofvector::Vector{T}, model) where {T} outs = fill(zero(T), nthreads()) @assemble! begin outs[threadid()] += cache.element_potential(eldofs) @@ -155,28 +158,30 @@ function F(dofvector::Vector{T}, model) where T end # The gradient calculation for each dof -function ∇F!(∇f::Vector{T}, dofvector::Vector{T}, model::LandauModel{T}) where T +function ∇F!(∇f::Vector{T}, dofvector::Vector{T}, model::LandauModel{T}) where {T} fill!(∇f, zero(T)) @assemble! begin ForwardDiff.gradient!(cache.element_gradient, cache.element_potential, eldofs, cache.gradconf) @inbounds assemble!(∇f, cache.element_indices, cache.element_gradient) end + return end # The Hessian calculation for the whole grid -function ∇²F!(∇²f::SparseMatrixCSC, dofvector::Vector{T}, model::LandauModel{T}) where T - assemblers = [start_assemble(∇²f) for t=1:nthreads()] +function ∇²F!(∇²f::SparseMatrixCSC, dofvector::Vector{T}, model::LandauModel{T}) where {T} + assemblers = [start_assemble(∇²f) for t in 1:nthreads()] @assemble! begin ForwardDiff.hessian!(cache.element_hessian, cache.element_potential, eldofs, cache.hessconf) @inbounds assemble!(assemblers[threadid()], cache.element_indices, cache.element_hessian) end + return end # We can also calculate all things in one go! -function calcall(∇²f::SparseMatrixCSC, ∇f::Vector{T}, dofvector::Vector{T}, model::LandauModel{T}) where T +function calcall(∇²f::SparseMatrixCSC, ∇f::Vector{T}, dofvector::Vector{T}, model::LandauModel{T}) where {T} outs = fill(zero(T), nthreads()) fill!(∇f, zero(T)) - assemblers = [start_assemble(∇²f, ∇f) for t=1:nthreads()] + assemblers = [start_assemble(∇²f, ∇f) for t in 1:nthreads()] @assemble! begin outs[threadid()] += cache.element_potential(eldofs) ForwardDiff.hessian!(cache.element_hessian, cache.element_potential, eldofs, cache.hessconf) @@ -196,11 +201,11 @@ function minimize!(model; kwargs...) ∇²f = allocate_matrix(dh) function g!(storage, x) ∇F!(storage, x, model) - apply_zero!(storage, model.boundaryconds) + return apply_zero!(storage, model.boundaryconds) end function h!(storage, x) - ∇²F!(storage, x, model) - #apply!(storage, model.boundaryconds) + return ∇²F!(storage, x, model) + ## apply!(storage, model.boundaryconds) end f(x) = F(x, model) @@ -212,7 +217,7 @@ function minimize!(model; kwargs...) ## model.dofs .= res.minimizer ## to get the final convergence, Newton's method is more ideal since the energy landscape should be almost parabolic ##+ - res = optimize(od, model.dofs, Newton(linesearch=BackTracking()), Optim.Options(show_trace=true, show_every=1, g_tol=1e-20)) + res = optimize(od, model.dofs, Newton(linesearch = BackTracking()), Optim.Options(show_trace = true, show_every = 1, g_tol = 1.0e-20)) model.dofs .= res.minimizer return res end @@ -220,10 +225,10 @@ end # ## Testing it # This calculates the contribution of each element to the total energy, # it is also the function that will be put through ForwardDiff for the gradient and Hessian. -function element_potential(eldofs::AbstractVector{T}, cvP, params) where T +function element_potential(eldofs::AbstractVector{T}, cvP, params) where {T} energy = zero(T) - for qp=1:getnquadpoints(cvP) - P = function_value(cvP, qp, eldofs) + for qp in 1:getnquadpoints(cvP) + P = function_value(cvP, qp, eldofs) ∇P = function_gradient(cvP, qp, eldofs) energy += F(P, ∇P, params) * getdetJdV(cvP, qp) end @@ -235,22 +240,23 @@ function startingconditions!(dofvector, dofhandler) for cell in CellIterator(dofhandler) globaldofs = celldofs(cell) it = 1 - for i=1:3:length(globaldofs) - dofvector[globaldofs[i]] = -2.0 - dofvector[globaldofs[i+1]] = 2.0 - dofvector[globaldofs[i+2]] = -2.0tanh(cell.coords[it][1]/20) + for i in 1:3:length(globaldofs) + dofvector[globaldofs[i]] = -2.0 + dofvector[globaldofs[i + 1]] = 2.0 + dofvector[globaldofs[i + 2]] = -2.0tanh(cell.coords[it][1] / 20) it += 1 end end + return end δ(i, j) = i == j ? one(i) : zero(i) -V2T(p11, p12, p44) = Tensor{4, 3}((i,j,k,l) -> p11 * δ(i,j)*δ(k,l)*δ(i,k) + p12*δ(i,j)*δ(k,l)*(1 - δ(i,k)) + p44*δ(i,k)*δ(j,l)*(1 - δ(i,j))) +V2T(p11, p12, p44) = Tensor{4, 3}((i, j, k, l) -> p11 * δ(i, j) * δ(k, l) * δ(i, k) + p12 * δ(i, j) * δ(k, l) * (1 - δ(i, k)) + p44 * δ(i, k) * δ(j, l) * (1 - δ(i, j))) G = V2T(1.0e2, 0.0, 1.0e2) α = Vec{3}((-1.0, 1.0, 1.0)) -left = Vec{3}((-75.,-25.,-2.)) -right = Vec{3}((75.,25.,2.)) +left = Vec{3}((-75.0, -25.0, -2.0)) +right = Vec{3}((75.0, 25.0, 2.0)) model = LandauModel(α, G, (50, 50, 2), left, right, element_potential) save_landau("landauorig", model) diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index 030c36f0d0..2f19d4ada9 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -85,11 +85,11 @@ end # We then create a function to generate a simple test mesh on which to compute FE solution. We also mark the boundaries # to later assign Dirichlet boundary conditions function importTestGrid() - grid = generate_grid(Tetrahedron, (5, 5, 5), zero(Vec{3}), ones(Vec{3})); - addfacetset!(grid, "myBottom", x -> norm(x[2]) ≈ 0.0); - addfacetset!(grid, "myBack", x -> norm(x[3]) ≈ 0.0); - addfacetset!(grid, "myRight", x -> norm(x[1]) ≈ 1.0); - addfacetset!(grid, "myLeft", x -> norm(x[1]) ≈ 0.0); + grid = generate_grid(Tetrahedron, (5, 5, 5), zero(Vec{3}), ones(Vec{3})) + addfacetset!(grid, "myBottom", x -> norm(x[2]) ≈ 0.0) + addfacetset!(grid, "myBack", x -> norm(x[3]) ≈ 0.0) + addfacetset!(grid, "myRight", x -> norm(x[1]) ≈ 1.0) + addfacetset!(grid, "myLeft", x -> norm(x[1]) ≈ 0.0) return grid end; @@ -97,7 +97,7 @@ end; # follows in a similar fashion from the `incompressible_elasticity` example function create_values(interpolation_u, interpolation_p) ## quadrature rules - qr = QuadratureRule{RefTetrahedron}(4) + qr = QuadratureRule{RefTetrahedron}(4) facet_qr = FacetQuadratureRule{RefTetrahedron}(4) ## cell and facetvalues for u @@ -116,7 +116,7 @@ function Ψ(F, p, mp::NeoHooke) λ = mp.λ Ic = tr(tdot(F)) J = det(F) - Js = (λ + p + sqrt((λ + p)^2. + 4. * λ * μ ))/(2. * λ) + Js = (λ + p + sqrt((λ + p)^2.0 + 4.0 * λ * μ)) / (2.0 * λ) return p * (Js - J) + μ / 2 * (Ic - 3) - μ * log(Js) + λ / 2 * (Js - 1)^2 end; @@ -145,10 +145,10 @@ end; # of the loading. function create_bc(dh) dbc = ConstraintHandler(dh) - add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myLeft"), (x,t) -> zero(Vec{1}), [1])) - add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myBottom"), (x,t) -> zero(Vec{1}), [2])) - add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myBack"), (x,t) -> zero(Vec{1}), [3])) - add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myRight"), (x,t) -> t*ones(Vec{1}), [1])) + add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myLeft"), (x, t) -> zero(Vec{1}), [1])) + add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myBottom"), (x, t) -> zero(Vec{1}), [2])) + add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myBack"), (x, t) -> zero(Vec{1}), [3])) + add!(dbc, Dirichlet(:u, getfacetset(dh.grid, "myRight"), (x, t) -> t * ones(Vec{1}), [1])) close!(dbc) Ferrite.update!(dbc, 0.0) return dbc @@ -158,7 +158,7 @@ end; # It is easy to show that this is equal to ∫J*dΩ where J=det(F). This can be done at the level of each element (cell) function calculate_element_volume(cell, cellvalues_u, ue) reinit!(cellvalues_u, cell) - evol::Float64=0.0; + evol::Float64 = 0.0 for qp in 1:getnquadpoints(cellvalues_u) dΩ = getdetJdV(cellvalues_u, qp) ∇u = function_gradient(cellvalues_u, qp, ue) @@ -171,14 +171,14 @@ end; # and then assembled over all the cells (elements) function calculate_volume_deformed_mesh(w, dh::DofHandler, cellvalues_u) - evol::Float64 = 0.0; + evol::Float64 = 0.0 for cell in CellIterator(dh) global_dofs = celldofs(cell) nu = getnbasefunctions(cellvalues_u) global_dofs_u = global_dofs[1:nu] ue = w[global_dofs_u] δevol = calculate_element_volume(cell, cellvalues_u, ue) - evol += δevol; + evol += δevol end return evol end; @@ -211,26 +211,26 @@ function assemble_element!(Ke, fe, cell, cellvalues_u, cellvalues_p, mp, ue, pe) ## gradient of the test function ∇δui = shape_gradient(cellvalues_u, qp, i) ## Add contribution to the residual from this test function - fe[BlockIndex((ublock), (i))] += ( ∇δui ⊡ ∂Ψ∂F) * dΩ + fe[BlockIndex((ublock), (i))] += (∇δui ⊡ ∂Ψ∂F) * dΩ ∇δui∂S∂F = ∇δui ⊡ ∂²Ψ∂F² for j in 1:n_basefuncs_u ∇δuj = shape_gradient(cellvalues_u, qp, j) ## Add contribution to the tangent - Ke[BlockIndex((ublock, ublock), (i, j))] += ( ∇δui∂S∂F ⊡ ∇δuj ) * dΩ + Ke[BlockIndex((ublock, ublock), (i, j))] += (∇δui∂S∂F ⊡ ∇δuj) * dΩ end ## Loop over the `p`-test functions for j in 1:n_basefuncs_p δp = shape_value(cellvalues_p, qp, j) ## Add contribution to the tangent - Ke[BlockIndex((ublock, pblock), (i, j))] += ( ∂²Ψ∂F∂p ⊡ ∇δui ) * δp * dΩ + Ke[BlockIndex((ublock, pblock), (i, j))] += (∂²Ψ∂F∂p ⊡ ∇δui) * δp * dΩ end end ## Loop over the `p`-test functions to calculate the `p-`u` and `p`-`p` blocks for i in 1:n_basefuncs_p δp = shape_value(cellvalues_p, qp, i) - fe[BlockIndex((pblock), (i))] += ( δp * ∂Ψ∂p) * dΩ + fe[BlockIndex((pblock), (i))] += (δp * ∂Ψ∂p) * dΩ for j in 1:n_basefuncs_u ∇δuj = shape_gradient(cellvalues_u, qp, j) @@ -242,12 +242,15 @@ function assemble_element!(Ke, fe, cell, cellvalues_u, cellvalues_p, mp, ue, pe) end end end + return end; # The only thing that changes in the assembly of the global stiffness matrix is slicing the corresponding element # dofs for the displacement (see `global_dofsu`) and pressure (`global_dofsp`). -function assemble_global!(K::SparseMatrixCSC, f, cellvalues_u::CellValues, - cellvalues_p::CellValues, dh::DofHandler, mp::NeoHooke, w) +function assemble_global!( + K::SparseMatrixCSC, f, cellvalues_u::CellValues, + cellvalues_p::CellValues, dh::DofHandler, mp::NeoHooke, w + ) nu = getnbasefunctions(cellvalues_u) np = getnbasefunctions(cellvalues_p) @@ -259,14 +262,15 @@ function assemble_global!(K::SparseMatrixCSC, f, cellvalues_u::CellValues, ## Loop over all cells in the grid for cell in CellIterator(dh) global_dofs = celldofs(cell) - global_dofsu = global_dofs[1:nu]; # first nu dofs are displacement - global_dofsp = global_dofs[nu + 1:end]; # last np dofs are pressure + global_dofsu = global_dofs[1:nu] # first nu dofs are displacement + global_dofsp = global_dofs[(nu + 1):end] # last np dofs are pressure @assert size(global_dofs, 1) == nu + np # sanity check ue = w[global_dofsu] # displacement dofs for the current cell pe = w[global_dofsp] # pressure dofs for the current cell assemble_element!(ke, fe, cell, cellvalues_u, cellvalues_p, mp, ue, pe) assemble!(assembler, global_dofs, ke, fe) end + return end; # We now define a main function `solve`. For nonlinear quasistatic problems we often like to parameterize the @@ -278,8 +282,8 @@ function solve(interpolation_u, interpolation_p) grid = importTestGrid() ## Material parameters - μ = 1. - λ = 1.E4 * μ + μ = 1.0 + λ = 1.0e4 * μ mp = NeoHooke(μ, λ) ## Create the DofHandler and CellValues @@ -291,7 +295,7 @@ function solve(interpolation_u, interpolation_p) ## Pre-allocation of vectors for the solution and Newton increments _ndofs = ndofs(dh) - w = zeros(_ndofs) + w = zeros(_ndofs) ΔΔw = zeros(_ndofs) apply!(w, dbc) @@ -301,24 +305,25 @@ function solve(interpolation_u, interpolation_p) ## We run the simulation parameterized by a time like parameter. `Tf` denotes the final value ## of this parameter, and Δt denotes its increment in each step - Tf = 2.0; - Δt = 0.1; - NEWTON_TOL = 1e-8 + Tf = 2.0 + Δt = 0.1 + NEWTON_TOL = 1.0e-8 - pvd = paraview_collection("hyperelasticity_incomp_mixed"); - for (step, t) ∈ enumerate(0.0:Δt:Tf) + pvd = paraview_collection("hyperelasticity_incomp_mixed") + for (step, t) in enumerate(0.0:Δt:Tf) ## Perform Newton iterations Ferrite.update!(dbc, t) apply!(w, dbc) newton_itr = -1 prog = ProgressMeter.ProgressThresh(NEWTON_TOL; desc = "Solving @ time $t of $Tf;") - fill!(ΔΔw, 0.0); - while true; newton_itr += 1 + fill!(ΔΔw, 0.0) + while true + newton_itr += 1 assemble_global!(K, f, cellvalues_u, cellvalues_p, dh, mp, w) norm_res = norm(f[Ferrite.free_dofs(dbc)]) apply_zero!(K, f, dbc) ## Only display output at specific load steps - if t%(5*Δt) == 0 + if t % (5 * Δt) == 0 ProgressMeter.update!(prog, norm_res; showvalues = [(:iter, newton_itr)]) end if norm_res < NEWTON_TOL @@ -327,22 +332,22 @@ function solve(interpolation_u, interpolation_p) error("Reached maximum Newton iterations, aborting") end ## Compute the incremental `dof`-vector (both displacement and pressure) - ΔΔw .= K\f; + ΔΔw .= K \ f apply_zero!(ΔΔw, dbc) w .-= ΔΔw - end; + end ## Save the solution fields VTKGridFile("hyperelasticity_incomp_mixed_$step", grid) do vtk write_solution(vtk, dh, w) pvd[t] = vtk end - end; - vtk_save(pvd); - vol_def = calculate_volume_deformed_mesh(w, dh, cellvalues_u); + end + vtk_save(pvd) + vol_def = calculate_volume_deformed_mesh(w, dh, cellvalues_u) print("Deformed volume is $vol_def") - return vol_def; + return vol_def end; # We can now test the solution using the Taylor-Hood approximation @@ -353,7 +358,7 @@ vol_def = solve(quadratic_u, linear_p) # The deformed volume is indeed close to 1 (as should be for a nearly incompressible material). using Test #src -@test isapprox(vol_def, 1.0, atol=1E-3) #src +@test isapprox(vol_def, 1.0, atol = 1.0e-3) #src #md # ## Plain program #md # diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index e01e7d1931..37e99e00d7 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -71,15 +71,17 @@ using Ferrite, SparseArrays, LinearAlgebra, Tensors, Printf # will later apply a force in negative y-direction. function create_grid(n) - corners = [Vec{2}((0.0, 0.0)), - Vec{2}((2.0, 0.0)), - Vec{2}((2.0, 1.0)), - Vec{2}((0.0, 1.0))] - grid = generate_grid(Quadrilateral, (2*n, n), corners); + corners = [ + Vec{2}((0.0, 0.0)), + Vec{2}((2.0, 0.0)), + Vec{2}((2.0, 1.0)), + Vec{2}((0.0, 1.0)), + ] + grid = generate_grid(Quadrilateral, (2 * n, n), corners) ## node-/facesets for boundary conditions addnodeset!(grid, "clamped", x -> x[1] ≈ 0.0) - addfacetset!(grid, "traction", x -> x[1] ≈ 2.0 && norm(x[2]-0.5) <= 0.05); + addfacetset!(grid, "traction", x -> x[1] ≈ 2.0 && norm(x[2] - 0.5) <= 0.05) return grid end #md nothing # hide @@ -88,11 +90,11 @@ end function create_values() ## quadrature rules - qr = QuadratureRule{RefQuadrilateral}(2) + qr = QuadratureRule{RefQuadrilateral}(2) facet_qr = FacetQuadratureRule{RefQuadrilateral}(2) ## cell and facetvalues for u - ip = Lagrange{RefQuadrilateral,1}()^2 + ip = Lagrange{RefQuadrilateral, 1}()^2 cellvalues = CellValues(qr, ip) facetvalues = FacetValues(facet_qr, ip) @@ -101,14 +103,14 @@ end function create_dofhandler(grid) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefQuadrilateral,1}()^2) # displacement + add!(dh, :u, Lagrange{RefQuadrilateral, 1}()^2) # displacement close!(dh) return dh end function create_bc(dh) dbc = ConstraintHandler(dh) - add!(dbc, Dirichlet(:u, getnodeset(dh.grid, "clamped"), (x,t) -> zero(Vec{2}), [1,2])) + add!(dbc, Dirichlet(:u, getnodeset(dh.grid, "clamped"), (x, t) -> zero(Vec{2}), [1, 2])) close!(dbc) t = 0.0 update!(dbc, t) @@ -130,12 +132,12 @@ end #md nothing # hide function MaterialParameters(E, ν, χ_min, p, β, η) - δ(i,j) = i == j ? 1.0 : 0.0 # helper function + δ(i, j) = i == j ? 1.0 : 0.0 # helper function G = E / 2(1 + ν) # =μ - λ = E*ν/(1-ν^2) # correction for plane stress included + λ = E * ν / (1 - ν^2) # correction for plane stress included - C = SymmetricTensor{4, 2}((i,j,k,l) -> λ * δ(i,j)*δ(k,l) + G* (δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k))) + C = SymmetricTensor{4, 2}((i, j, k, l) -> λ * δ(i, j) * δ(k, l) + G * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k))) return MaterialParameters(C, χ_min, p, β, η) end #md nothing # hide @@ -150,13 +152,14 @@ mutable struct MaterialState{T, S <: AbstractArray{SymmetricTensor{2, 2, T, 3}, end function MaterialState(ρ, n_qp) - return MaterialState(ρ, Array{SymmetricTensor{2,2,Float64,3},1}(undef, n_qp)) + return MaterialState(ρ, Array{SymmetricTensor{2, 2, Float64, 3}, 1}(undef, n_qp)) end function update_material_states!(χn1, states, dh) - for (element, state) in zip(CellIterator(dh),states) + for (element, state) in zip(CellIterator(dh), states) state.χ = χn1[cellid(element)] end + return end #md nothing # hide @@ -169,8 +172,8 @@ function compute_driving_forces(states, mp, dh, χn) pΨ = zeros(length(states)) for (element, state) in zip(CellIterator(dh), states) i = cellid(element) - ε = sum(state.ε)/length(state.ε) # average element strain - pΨ[i] = 1/2 * mp.p * χn[i]^(mp.p-1) * (ε ⊡ mp.C ⊡ ε) + ε = sum(state.ε) / length(state.ε) # average element strain + pΨ[i] = 1 / 2 * mp.p * χn[i]^(mp.p - 1) * (ε ⊡ mp.C ⊡ ε) end return pΨ end @@ -194,17 +197,17 @@ end function cache_neighborhood(dh, topology) nbgs = Vector{Vector{Int}}(undef, getncells(dh.grid)) _nfacets = nfacets(dh.grid.cells[1]) - opp = Dict(1=>3, 2=>4, 3=>1, 4=>2) + opp = Dict(1 => 3, 2 => 4, 3 => 1, 4 => 2) for element in CellIterator(dh) - nbg = zeros(Int,_nfacets) + nbg = zeros(Int, _nfacets) i = cellid(element) for j in 1:_nfacets - nbg_cellid = getneighborhood(topology, dh.grid, FacetIndex(i,j)) - if(!isempty(nbg_cellid)) + nbg_cellid = getneighborhood(topology, dh.grid, FacetIndex(i, j)) + if !isempty(nbg_cellid) nbg[j] = first(nbg_cellid)[1] # assuming only one facet neighbor per cell else # boundary facet - nbg[j] = first(getneighborhood(topology, dh.grid, FacetIndex(i,opp[j])))[1] + nbg[j] = first(getneighborhood(topology, dh.grid, FacetIndex(i, opp[j])))[1] end end @@ -220,7 +223,7 @@ function approximate_laplacian(nbgs, χn, Δh) ∇²χ = zeros(length(nbgs)) for i in 1:length(nbgs) nbg = nbgs[i] - ∇²χ[i] = (χn[nbg[1]]+χn[nbg[2]]+χn[nbg[3]]+χn[nbg[4]]-4*χn[i])/(Δh^2) + ∇²χ[i] = (χn[nbg[1]] + χn[nbg[2]] + χn[nbg[3]] + χn[nbg[4]] - 4 * χn[i]) / (Δh^2) end return ∇²χ @@ -243,23 +246,23 @@ function compute_χn1(χn, Δχ, ρ, ηs, χ_min) λ_upper = maximum(Δχ) + ηs λ_trial = 0.0 - while(abs(ρ-ρ_trial)>1e-7) + while abs(ρ - ρ_trial) > 1.0e-7 for i in 1:n_el - Δχt = 1/ηs * (Δχ[i] - λ_trial) - χ_trial[i] = max(χ_min, min(1.0, χn[i]+Δχt)) + Δχt = 1 / ηs * (Δχ[i] - λ_trial) + χ_trial[i] = max(χ_min, min(1.0, χn[i] + Δχt)) end ρ_trial = 0.0 for i in 1:n_el - ρ_trial += χ_trial[i]/n_el + ρ_trial += χ_trial[i] / n_el end - if(ρ_trial > ρ) + if ρ_trial > ρ λ_lower = λ_trial - elseif(ρ_trial < ρ) + elseif ρ_trial < ρ λ_upper = λ_trial end - λ_trial = 1/2*(λ_upper+λ_lower) + λ_trial = 1 / 2 * (λ_upper + λ_lower) end return χ_trial @@ -275,10 +278,10 @@ function compute_average_driving_force(mp, pΨ, χn) w = zeros(n) for i in 1:n - w[i] = (χn[i]-mp.χ_min)*(1-χn[i]) + w[i] = (χn[i] - mp.χ_min) * (1 - χn[i]) end - p_Ω = sum(w.*pΨ)/sum(w) # average driving force + p_Ω = sum(w .* pΨ) / sum(w) # average driving force return p_Ω end @@ -287,8 +290,8 @@ end # Finally, we put everything together to update the density. The loop ensures the stability of the # updated solution. -function update_density(dh, states, mp, ρ, neighboorhoods, Δh) - n_j = Int(ceil(6*mp.β/(mp.η*Δh^2))) # iterations needed for stability +function update_density(dh, states, mp, ρ, neighboorhoods, Δh) + n_j = Int(ceil(6 * mp.β / (mp.η * Δh^2))) # iterations needed for stability χn = compute_densities(states, dh) # old density field χn1 = zeros(length(χn)) @@ -297,11 +300,11 @@ function update_density(dh, states, mp, ρ, neighboorhoods, Δh) pΨ = compute_driving_forces(states, mp, dh, χn) # driving forces p_Ω = compute_average_driving_force(mp, pΨ, χn) # average driving force - Δχ = pΨ/p_Ω + mp.β*∇²χ + Δχ = pΨ / p_Ω + mp.β * ∇²χ χn1 = compute_χn1(χn, Δχ, ρ, mp.η, mp.χ_min) - if(j 10 error("Reached maximum Newton iterations, aborting") @@ -457,7 +462,7 @@ function topopt(ra,ρ,n,filename; output=:false) ## current guess u .= un .+ Δu - K, r = doassemble!(cellvalues, facetvalues, K, grid, dh, mp, u, states); + K, r = doassemble!(cellvalues, facetvalues, K, grid, dh, mp, u, states) norm_r = norm(r[Ferrite.free_dofs(dbc)]) if (norm_r) < NEWTON_TOL @@ -472,15 +477,15 @@ function topopt(ra,ρ,n,filename; output=:false) end # of loop while NR-Iteration ## calculate compliance - compliance = 1/2 * u' * K * u + compliance = 1 / 2 * u' * K * u - if(it==1) + if it == 1 compliance_0 = compliance end ## check convergence criterium (twice!) - if(abs(compliance-compliance_n)/compliance < tol) - if(conv) + if abs(compliance - compliance_n) / compliance < tol + if conv println("Converged at iteration number: ", it) break else @@ -500,7 +505,7 @@ function topopt(ra,ρ,n,filename; output=:false) compliance_n = compliance ## output during calculation - if(output) + if output i = @sprintf("%3.3i", it) filename_it = string(filename, "_", i) @@ -511,12 +516,12 @@ function topopt(ra,ρ,n,filename; output=:false) end ## export converged results - if(!output) + if !output VTKGridFile(filename, grid) do vtk write_cell_data(vtk, χ, "density") end end - @printf "Rel. stiffness: %.4f \n" compliance^(-1)/compliance_0^(-1) + @printf "Rel. stiffness: %.4f \n" compliance^(-1) / compliance_0^(-1) return end @@ -527,7 +532,7 @@ end # parameter to `true`. # grid, χ =topopt(0.02, 0.5, 60, "small_radius"; output=:false); -@time topopt(0.03, 0.5, 60, "large_radius"; output=:false); +@time topopt(0.03, 0.5, 60, "large_radius"; output = :false); #topopt(0.02, 0.5, 60, "topopt_animation"; output=:true); # can be used to create animations # We observe, that the stiffness for the lower value of $ra$ is higher, diff --git a/docs/src/literate-howto/postprocessing.jl b/docs/src/literate-howto/postprocessing.jl index f2afd77c47..2eeb09b3c3 100644 --- a/docs/src/literate-howto/postprocessing.jl +++ b/docs/src/literate-howto/postprocessing.jl @@ -48,14 +48,14 @@ include("../tutorials/heat_equation.jl"); # Next we define a function that computes the heat flux for each integration point in the domain. # Fourier's law is adopted, where the conductivity tensor is assumed to be isotropic with unit # conductivity ``\lambda = 1 ⇒ q = - \nabla u``, where ``u`` is the temperature. -function compute_heat_fluxes(cellvalues::CellValues, dh::DofHandler, a::AbstractVector{T}) where T +function compute_heat_fluxes(cellvalues::CellValues, dh::DofHandler, a::AbstractVector{T}) where {T} n = getnbasefunctions(cellvalues) cell_dofs = zeros(Int, n) nqp = getnquadpoints(cellvalues) ## Allocate storage for the fluxes to store - q = [Vec{2,T}[] for _ in 1:getncells(dh.grid)] + q = [Vec{2, T}[] for _ in 1:getncells(dh.grid)] for (cell_num, cell) in enumerate(CellIterator(dh)) q_cell = q[cell_num] @@ -101,7 +101,7 @@ end; # Consider a cut-line through the domain like the black line in *Figure 2* above. # We will evaluate the temperature and the heat flux distribution along a horizontal line. -points = [Vec((x, 0.75)) for x in range(-1.0, 1.0, length=101)]; +points = [Vec((x, 0.75)) for x in range(-1.0, 1.0, length = 101)]; # First, we need to generate a `PointEvalHandler`. This will find and store the cells # containing the input points. @@ -124,11 +124,11 @@ u_points = evaluate_at_points(ph, dh, u, :u); import Plots # Firstly, we are going to plot the temperature values along the given line. -Plots.plot(getindex.(points,1), u_points, xlabel="x (coordinate)", ylabel="u (temperature)", label=nothing) +Plots.plot(getindex.(points, 1), u_points, xlabel = "x (coordinate)", ylabel = "u (temperature)", label = nothing) # *Figure 3*: Temperature along the cut line from *Figure 2*. # Secondly, the horizontal heat flux (i.e. the first component of the heat flux vector) is plotted. -Plots.plot(getindex.(points,1), getindex.(q_points,1), xlabel="x (coordinate)", ylabel="q_x (flux in x-direction)", label=nothing) +Plots.plot(getindex.(points, 1), getindex.(q_points, 1), xlabel = "x (coordinate)", ylabel = "q_x (flux in x-direction)", label = nothing) # *Figure 4*: ``x``-component of the flux along the cut line from *Figure 2*. #md # ## [Plain program](@id postprocessing-plain-program) diff --git a/docs/src/literate-howto/threaded_assembly.jl b/docs/src/literate-howto/threaded_assembly.jl index 8f47cccc75..47cbd32297 100644 --- a/docs/src/literate-howto/threaded_assembly.jl +++ b/docs/src/literate-howto/threaded_assembly.jl @@ -72,6 +72,7 @@ function create_example_2d_grid() Ferrite.write_cell_colors(vtk, grid, colors_workstream, "workstream-coloring") Ferrite.write_cell_colors(vtk, grid, colors_greedy, "greedy-coloring") end + return end create_example_2d_grid() diff --git a/docs/src/literate-tutorials/computational_homogenization.jl b/docs/src/literate-tutorials/computational_homogenization.jl index 4abf225720..ee7b6486e4 100644 --- a/docs/src/literate-tutorials/computational_homogenization.jl +++ b/docs/src/literate-tutorials/computational_homogenization.jl @@ -233,7 +233,7 @@ ch_dirichlet = ConstraintHandler(dh) dirichlet = Dirichlet( :u, union(getfacetset.(Ref(grid), ["left", "right", "top", "bottom"])...), - (x, t) -> [0, 0], + (x, t) -> [0, 0], [1, 2] ) add!(ch_dirichlet, dirichlet) @@ -278,16 +278,16 @@ ch = (dirichlet = ch_dirichlet, periodic = ch_periodic); K = ( dirichlet = allocate_matrix(dh), - periodic = allocate_matrix(dh, ch.periodic), + periodic = allocate_matrix(dh, ch.periodic), ); # We define the fourth order elasticity tensor for the matrix material, and define the # inclusions to have 10 times higher stiffness -λ, μ = 1e10, 7e9 # Lamé parameters -δ(i,j) = i == j ? 1.0 : 0.0 +λ, μ = 1.0e10, 7.0e9 # Lamé parameters +δ(i, j) = i == j ? 1.0 : 0.0 Em = SymmetricTensor{4, 2}( - (i,j,k,l) -> λ * δ(i,j) * δ(k,l) + μ * (δ(i,k) * δ(j,l) + δ(i,l) * δ(j,k)) + (i, j, k, l) -> λ * δ(i, j) * δ(k, l) + μ * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)) ) Ei = 10 * Em; @@ -298,9 +298,9 @@ Ei = 10 * Em; # and will result in three different right-hand-sides: εᴹ = [ - SymmetricTensor{2,2}([1.0 0.0; 0.0 0.0]), # ε_11 loading - SymmetricTensor{2,2}([0.0 0.0; 0.0 1.0]), # ε_22 loading - SymmetricTensor{2,2}([0.0 0.5; 0.5 0.0]), # ε_12/ε_21 loading + SymmetricTensor{2, 2}([1.0 0.0; 0.0 0.0]), # ε_11 loading + SymmetricTensor{2, 2}([0.0 0.0; 0.0 1.0]), # ε_22 loading + SymmetricTensor{2, 2}([0.0 0.5; 0.5 0.0]), # ε_12/ε_21 loading ]; # The assembly function is nothing strange, and in particular there is no impact from the @@ -334,8 +334,8 @@ function doassemble!(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler, end for (rhs, ε) in enumerate(εᴹ) σᴹ = E ⊡ ε - fe[i, rhs] += ( - δεi ⊡ σᴹ) * dΩ - end + fe[i, rhs] += (- δεi ⊡ σᴹ) * dΩ + end end end @@ -351,7 +351,7 @@ end; rhs = ( dirichlet = doassemble!(cellvalues, K.dirichlet, dh, εᴹ), - periodic = doassemble!(cellvalues, K.periodic, dh, εᴹ), + periodic = doassemble!(cellvalues, K.periodic, dh, εᴹ), ); # The next step is to solve the systems. Since application of boundary conditions, using @@ -365,11 +365,11 @@ rhs = ( rhsdata = ( dirichlet = get_rhs_data(ch.dirichlet, K.dirichlet), - periodic = get_rhs_data(ch.periodic, K.periodic), + periodic = get_rhs_data(ch.periodic, K.periodic), ) apply!(K.dirichlet, ch.dirichlet) -apply!(K.periodic, ch.periodic) +apply!(K.periodic, ch.periodic) # We can now solve the problem(s). Note that we only use `apply_rhs!` in the loops below. # The boundary conditions are already applied to the matrix above, so we only need to @@ -377,7 +377,7 @@ apply!(K.periodic, ch.periodic) u = ( dirichlet = Vector{Float64}[], - periodic = Vector{Float64}[], + periodic = Vector{Float64}[], ) for i in 1:size(rhs.dirichlet, 2) @@ -402,7 +402,7 @@ end function compute_stress(cellvalues::CellValues, dh::DofHandler, u, εᴹ) σvM_qpdata = zeros(getnquadpoints(cellvalues), getncells(dh.grid)) - σ̄Ω = zero(SymmetricTensor{2,2}) + σ̄Ω = zero(SymmetricTensor{2, 2}) Ω = 0.0 # Total volume for cell in CellIterator(dh) E = cellid(cell) in getcellset(dh.grid, "inclusions") ? Ei : Em @@ -411,7 +411,7 @@ function compute_stress(cellvalues::CellValues, dh::DofHandler, u, εᴹ) dΩ = getdetJdV(cellvalues, q_point) εμ = function_symmetric_gradient(cellvalues, q_point, u[celldofs(cell)]) σ = E ⊡ (εᴹ + εμ) - σvM_qpdata[q_point, cellid(cell)] = sqrt(3/2 * dev(σ) ⊡ dev(σ)) + σvM_qpdata[q_point, cellid(cell)] = sqrt(3 / 2 * dev(σ) ⊡ dev(σ)) Ω += dΩ # Update total volume σ̄Ω += σ * dΩ # Update integrated stress end @@ -423,12 +423,12 @@ end; # We now compute the homogenized stress and von Mise stress for all cases σ̄ = ( - dirichlet = SymmetricTensor{2,2}[], - periodic = SymmetricTensor{2,2}[], + dirichlet = SymmetricTensor{2, 2}[], + periodic = SymmetricTensor{2, 2}[], ) σ = ( - dirichlet = Vector{Float64}[], - periodic = Vector{Float64}[], + dirichlet = Vector{Float64}[], + periodic = Vector{Float64}[], ) projector = L2Projector(ip, grid) @@ -458,7 +458,7 @@ end # So we have now already computed all the components, and just need to gather the data in # a fourth order tensor: -E_dirichlet = SymmetricTensor{4,2}((i, j, k, l) -> begin +E_dirichlet = SymmetricTensor{4, 2}() do i, j, k, l if k == l == 1 σ̄.dirichlet[1][i, j] # ∂σ∂ε_**11 elseif k == l == 2 @@ -466,9 +466,9 @@ E_dirichlet = SymmetricTensor{4,2}((i, j, k, l) -> begin else σ̄.dirichlet[3][i, j] # ∂σ∂ε_**12 and ∂σ∂ε_**21 end -end) +end -E_periodic = SymmetricTensor{4,2}((i, j, k, l) -> begin +E_periodic = SymmetricTensor{4, 2}() do i, j, k, l if k == l == 1 σ̄.periodic[1][i, j] elseif k == l == 2 @@ -476,7 +476,7 @@ E_periodic = SymmetricTensor{4,2}((i, j, k, l) -> begin else σ̄.periodic[3][i, j] end -end); +end # We can check that the result are what we expect, namely that the stiffness with Dirichlet # boundary conditions is higher than when using periodic boundary conditions, and that @@ -484,7 +484,7 @@ end); # compute the volume fraction of the matrix, and then the Voigt and Reuss bounds: function matrix_volume_fraction(grid, cellvalues) - V = 0.0 # Total volume + V = 0.0 # Total volume Vm = 0.0 # Volume of the matrix for c in CellIterator(grid) reinit!(cellvalues, c) @@ -502,8 +502,8 @@ end vm = matrix_volume_fraction(grid, cellvalues) #- -E_voigt = vm * Em + (1-vm) * Ei -E_reuss = inv(vm * inv(Em) + (1-vm) * inv(Ei)); +E_voigt = vm * Em + (1 - vm) * Ei +E_reuss = inv(vm * inv(Em) + (1 - vm) * inv(Ei)); # We can now compare the different computed stiffness tensors. We expect # ``E_\mathrm{Reuss} \leq E_\mathrm{PeriodicBC} \leq E_\mathrm{DirichletBC} \leq @@ -512,7 +512,7 @@ E_reuss = inv(vm * inv(Em) + (1-vm) * inv(Ei)); ev = (first ∘ eigvals).((E_reuss, E_periodic, E_dirichlet, E_voigt)) @test issorted(ev) #src -round.(ev; digits=-8) +round.(ev; digits = -8) # Finally, we export the solution and the stress field to a VTK file. For the export we # also compute the macroscopic part of the displacement. @@ -534,7 +534,7 @@ end; # Just another way to compute the stiffness for testing purposes #src function homogenize_test(u::Matrix, dh, cv, E_incl, E_mat) #src - ĒΩ = zero(SymmetricTensor{4,2}) #src + ĒΩ = zero(SymmetricTensor{4, 2}) #src Ω = 0.0 #src ue = zeros(ndofs_per_cell(dh), 3) #src for cell in CellIterator(dh) #src @@ -549,15 +549,15 @@ function homogenize_test(u::Matrix, dh, cv, E_incl, E_mat) # dΩ = getdetJdV(cv, qp) #src Ω += dΩ #src ## compute u^ij and u^kl #src - Ē′ = SymmetricTensor{4,2}((i, j, k, l) -> begin #src + Ē′ = SymmetricTensor{4, 2}() do i, j, k, l #src ij = i == j == 1 ? 1 : i == j == 2 ? 2 : 3 #src kl = k == l == 1 ? 1 : k == l == 2 ? 2 : 3 #src εij = function_symmetric_gradient(cv, qp, view(ue, :, ij)) + #src - symmetric((basevec(Vec{2}, i) ⊗ basevec(Vec{2}, j))) #src + symmetric((basevec(Vec{2}, i) ⊗ basevec(Vec{2}, j))) #src εkl = function_symmetric_gradient(cv, qp, view(ue, :, kl)) + #src - symmetric((basevec(Vec{2}, k) ⊗ basevec(Vec{2}, l))) #src + symmetric((basevec(Vec{2}, k) ⊗ basevec(Vec{2}, l))) #src return (εij ⊡ E ⊡ εkl) * dΩ #src - end) #src + end #src ĒΩ += Ē′ #src end #src end #src diff --git a/docs/src/literate-tutorials/dg_heat_equation.jl b/docs/src/literate-tutorials/dg_heat_equation.jl index d0ba910868..407dc7ad5f 100644 --- a/docs/src/literate-tutorials/dg_heat_equation.jl +++ b/docs/src/literate-tutorials/dg_heat_equation.jl @@ -155,8 +155,8 @@ facetvalues = FacetValues(facet_qr, ip); interfacevalues = InterfaceValues(facet_qr, ip); # ### Penalty term parameters # We define functions to calculate the diameter of a set of points, used to calculate the characteristic size $h_e$ in the assembly routine. -getdistance(p1::Vec{N, T},p2::Vec{N, T}) where {N, T} = norm(p1-p2); -getdiameter(cell_coords::Vector{Vec{N, T}}) where {N, T} = maximum(getdistance.(cell_coords, reshape(cell_coords, (1,:)))); +getdistance(p1::Vec{N, T}, p2::Vec{N, T}) where {N, T} = norm(p1 - p2); +getdiameter(cell_coords::Vector{Vec{N, T}}) where {N, T} = maximum(getdistance.(cell_coords, reshape(cell_coords, (1, :)))); # ### Degrees of freedom # Degrees of freedom distribution is handled using `DofHandler` as usual @@ -167,7 +167,7 @@ close!(dh); # However, when generating the sparsity pattern we need to pass the topology and the cross-element coupling matrix when we're using # discontinuous interpolations. The cross-element coupling matrix is of size [1,1] in this case as # we have only one field and one DofHandler. -K = allocate_matrix(dh, topology = topology, interface_coupling = trues(1,1)); +K = allocate_matrix(dh, topology = topology, interface_coupling = trues(1, 1)); # ### Boundary conditions # The Dirichlet boundary conditions are treated @@ -213,7 +213,7 @@ function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues) dΩ = getdetJdV(cellvalues, q_point) ## Loop over test shape functions for i in 1:n_basefuncs - δu = shape_value(cellvalues, q_point, i) + δu = shape_value(cellvalues, q_point, i) ∇δu = shape_gradient(cellvalues, q_point, i) ## Add contribution to fe fe[i] += δu * dΩ @@ -248,7 +248,7 @@ function assemble_interface!(Ki::Matrix, iv::InterfaceValues, μ::Float64) u_jump = shape_value_jump(iv, q_point, j) * (-normal) ∇u_avg = shape_gradient_average(iv, q_point, j) ## Add contribution to Ki - Ki[i, j] += -(δu_jump ⋅ ∇u_avg + ∇δu_avg ⋅ u_jump)*dΓ + μ * (δu_jump ⋅ u_jump) * dΓ + Ki[i, j] += -(δu_jump ⋅ ∇u_avg + ∇δu_avg ⋅ u_jump) * dΓ + μ * (δu_jump ⋅ u_jump) * dΓ end end end @@ -304,7 +304,7 @@ function assemble_global(cellvalues::CellValues, facetvalues::FacetValues, inter ## Reinitialize interfacevalues for this interface reinit!(interfacevalues, ic) ## Calculate the characteristic size hₑ as the face diameter - interfacecoords = ∩(getcoordinates(ic)...) + interfacecoords = ∩(getcoordinates(ic)...) hₑ = getdiameter(interfacecoords) ## Calculate μ μ = (1 + order)^dim / hₑ diff --git a/docs/src/literate-tutorials/heat_equation.jl b/docs/src/literate-tutorials/heat_equation.jl index 21c2963a75..a81f8f75eb 100644 --- a/docs/src/literate-tutorials/heat_equation.jl +++ b/docs/src/literate-tutorials/heat_equation.jl @@ -144,7 +144,7 @@ function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues) dΩ = getdetJdV(cellvalues, q_point) ## Loop over test shape functions for i in 1:n_basefuncs - δu = shape_value(cellvalues, q_point, i) + δu = shape_value(cellvalues, q_point, i) ∇δu = shape_gradient(cellvalues, q_point, i) ## Add contribution to fe fe[i] += δu * dΩ diff --git a/docs/src/literate-tutorials/hyperelasticity.jl b/docs/src/literate-tutorials/hyperelasticity.jl index 271efd2ebc..6ab05b3138 100644 --- a/docs/src/literate-tutorials/hyperelasticity.jl +++ b/docs/src/literate-tutorials/hyperelasticity.jl @@ -69,6 +69,7 @@ using Ferrite, Tensors, TimerOutputs, ProgressMeter, IterativeSolvers # # where ``I_1 = \mathrm{tr}(\mathbf{C})`` is the first invariant, ``J = \sqrt{\det(\mathbf{C})}`` # and ``\mu`` and ``\lambda`` material parameters. + # !!! details "Extra details on compressible neo-Hookean formulations" # The Neo-Hooke model is only a well defined terminology in the incompressible case. # Thus, only $W(\mathbf{C})$ specifies the neo-Hookean behavior, the volume penalty $U(J)$ can vary in different formulations. @@ -80,6 +81,7 @@ using Ferrite, Tensors, TimerOutputs, ProgressMeter, IterativeSolvers # where [SimMie:1992:act; Eq. (2.37)](@cite) published a non-generalized version with $\beta=-2$. # This shows the possible variety of $U(J)$ while all of them refer to compressible neo-Hookean models. # Sometimes the modified first invariant $\overline{I}_1=\frac{I_1}{I_3^{1/3}}$ is used in $W(\mathbf{C})$ instead of $I_1$. + # From the potential we obtain the second Piola-Kirchoff stress ``\mathbf{S}`` as # # ```math @@ -99,60 +101,53 @@ using Ferrite, Tensors, TimerOutputs, ProgressMeter, IterativeSolvers # ```math # \begin{align*} # \mathbf{P} &= \mathbf{F} \cdot \mathbf{S},\\ -# \frac{\partial \mathbf{P}}{\partial \mathbf{F}} &= \mathbf{I} \bar{\otimes} \mathbf{S} + 2\, \mathbf{F} \bar{\otimes} \mathbf{I} : +# \frac{\partial \mathbf{P}}{\partial \mathbf{F}} &= \mathbf{I} \bar{\otimes} \mathbf{S} + 2\, \mathbf{F} \cdot # \frac{\partial \mathbf{S}}{\partial \mathbf{C}} : \mathbf{F}^\mathrm{T} \bar{\otimes} \mathbf{I}. # \end{align*} # ``` -#md # ```@raw html -#md #
-#md # -#md # Derivation of $\partial \mathbf{P} / \partial \mathbf{F}$ -#md # -#md #
-#md # ``` -#nb # ### Derivation of ``\partial \mathbf{P} / \partial \mathbf{F}`` -# Using the product rule, the chain rule, and the relations ``\mathbf{P} = \mathbf{F} \cdot -# \mathbf{S}`` and ``\mathbf{C} = \mathbf{F}^\mathrm{T} \cdot \mathbf{F}``, we obtain the -# following: -# ```math -# \begin{aligned} -# \frac{\partial \mathbf{P}}{\partial \mathbf{F}} &= -# \frac{\partial P_{ij}}{\partial F_{kl}} \\ &= -# \frac{\partial (F_{im}S_{mj})}{\partial F_{kl}} \\ &= -# \frac{\partial F_{im}}{\partial F_{kl}}S_{mj} + -# F_{im}\frac{\partial S_{mj}}{\partial F_{kl}} \\ &= -# \delta_{ik}\delta_{ml} S_{mj} + -# F_{im}\frac{\partial S_{mj}}{\partial C_{no}}\frac{\partial C_{no}}{\partial F_{kl}} \\ &= -# \delta_{ik}S_{lj} + -# F_{im}\frac{\partial S_{mj}}{\partial C_{no}} -# \frac{\partial (F^\mathrm{T}_{np}F_{po})}{\partial F_{kl}} \\ &= -# \delta_{ik}S^\mathrm{T}_{jl} + -# F_{im}\delta_{jq}\frac{\partial S_{mq}}{\partial C_{no}} -# \left( -# \frac{\partial F^\mathrm{T}_{np}}{\partial F_{kl}}F_{po} + -# F^\mathrm{T}_{np}\frac{\partial F_{po}}{\partial F_{kl}} -# \right) \\ &= -# \delta_{ik}S_{jl} + -# F_{im}\delta_{jq}\frac{\partial S_{mq}}{\partial C_{no}} -# (\delta_{nl} \delta_{pk} F_{po} + F^\mathrm{T}_{np}\delta_{pk} \delta_{ol}) \\ &= -# \delta_{ik}S_{lj} + -# F_{im}\delta_{jq}\frac{\partial S_{mq}}{\partial C_{no}} -# (F^\mathrm{T}_{ok} \delta_{nl} + F^\mathrm{T}_{nk} \delta_{ol}) \\ &= -# \delta_{ik}S_{jl} + -# 2\, F_{im}\delta_{jq} \frac{\partial S_{mq}}{\partial C_{no}} -# F^\mathrm{T}_{nk} \delta_{ol} \\ &= -# \mathbf{I}\bar{\otimes}\mathbf{S} + -# 2\, \mathbf{F}\bar{\otimes}\mathbf{I} : \frac{\partial \mathbf{S}}{\partial \mathbf{C}} -# : \mathbf{F}^\mathrm{T} \bar{\otimes} \mathbf{I}, -# \end{aligned} -# ``` -# where we used the fact that ``\mathbf{S}`` is symmetric (``S_{lj} = S_{jl}``) and that -# ``\frac{\partial \mathbf{S}}{\partial \mathbf{C}}`` is *minor* symmetric (``\frac{\partial -# S_{mq}}{\partial C_{no}} = \frac{\partial S_{mq}}{\partial C_{on}}``). -#md # ```@raw html -#md #
-#md # ``` +# !!! details "Derivation of $\partial \mathbf{P} / \partial \mathbf{F}$" +# *Tip:* See [knutam.github.io/tensors](https://knutam.github.io/tensors/Theory/IndexNotation/) for +# an explanation of the index notation used in this derivation. +#md # +# Using the product rule, the chain rule, and the relations ``\mathbf{P} = \mathbf{F} \cdot +# \mathbf{S}`` and ``\mathbf{C} = \mathbf{F}^\mathrm{T} \cdot \mathbf{F}``, we obtain the +# following: +# ```math +# \begin{aligned} +# \frac{\partial P_{ij}}{\partial F_{kl}} &= +# \frac{\partial (F_{im}S_{mj})}{\partial F_{kl}} \\ &= +# \frac{\partial F_{im}}{\partial F_{kl}}S_{mj} + +# F_{im}\frac{\partial S_{mj}}{\partial F_{kl}} \\ &= +# \delta_{ik}\delta_{ml} S_{mj} + +# F_{im}\frac{\partial S_{mj}}{\partial C_{no}}\frac{\partial C_{no}}{\partial F_{kl}} \\ &= +# \delta_{ik}S_{lj} + +# F_{im}\frac{\partial S_{mj}}{\partial C_{no}} +# \frac{\partial (F^\mathrm{T}_{np}F_{po})}{\partial F_{kl}} \\ &= +# \delta_{ik}S^\mathrm{T}_{jl} + +# F_{im}\frac{\partial S_{mj}}{\partial C_{no}} +# \left( +# \frac{\partial F^\mathrm{T}_{np}}{\partial F_{kl}}F_{po} + +# F^\mathrm{T}_{np}\frac{\partial F_{po}}{\partial F_{kl}} +# \right) \\ &= +# \delta_{ik}S_{jl} + +# F_{im}\frac{\partial S_{mj}}{\partial C_{no}} +# (\delta_{nl} \delta_{pk} F_{po} + F^\mathrm{T}_{np}\delta_{pk} \delta_{ol}) \\ &= +# \delta_{ik}S_{lj} + +# F_{im}\frac{\partial S_{mj}}{\partial C_{no}} +# (F^\mathrm{T}_{ok} \delta_{nl} + F^\mathrm{T}_{nk} \delta_{ol}) \\ &= +# \delta_{ik}S_{jl} + +# 2\, F_{im} \frac{\partial S_{mj}}{\partial C_{no}} +# F^\mathrm{T}_{nk} \delta_{ol} \\ +# \frac{\partial \mathbf{P}}{\partial \mathbf{F}} &= +# \mathbf{I}\bar{\otimes}\mathbf{S} + +# 2\, \mathbf{F} \cdot \frac{\partial \mathbf{S}}{\partial \mathbf{C}} +# : \mathbf{F}^\mathrm{T} \bar{\otimes} \mathbf{I}, +# \end{aligned} +# ``` +# where we used the fact that ``\mathbf{S}`` is symmetric (``S_{lj} = S_{jl}``) and that +# ``\frac{\partial \mathbf{S}}{\partial \mathbf{C}}`` is *minor* symmetric (``\frac{\partial +# S_{mj}}{\partial C_{no}} = \frac{\partial S_{mj}}{\partial C_{on}}``). # ### Implementation of material model using automatic differentiation # We can implement the material model as follows, where we utilize automatic differentiation @@ -179,6 +174,19 @@ function constitutive_driver(C, mp::NeoHooke) return S, ∂S∂C end; +## Test the derivation #src +using Test #src +F = rand(Tensor{2, 3}) #src +mp = NeoHooke(rand(2)...) #src +S, ∂S∂C = constitutive_driver(tdot(F), mp) #src +P = F ⋅ S #src +I = one(S) #src +∂P∂F = otimesu(I, S) + 2 * F ⋅ ∂S∂C ⊡ otimesu(F', I) #src +∂P∂F_ad, P_ad = Tensors.hessian(x -> Ψ(tdot(x), mp), F, :all) #src +@test P ≈ P_ad #src +@test ∂P∂F ≈ ∂P∂F_ad #src +nothing #src + # ## Newton's method # # As mentioned above, to deal with the non-linear weak form we first linearize @@ -249,7 +257,7 @@ function assemble_element!(ke, ge, cell, cv, fv, mp, ue, ΓN) S, ∂S∂C = constitutive_driver(C, mp) P = F ⋅ S I = one(S) - ∂P∂F = otimesu(I, S) + 2 * otimesu(F, I) ⊡ ∂S∂C ⊡ otimesu(F', I) + ∂P∂F = otimesu(I, S) + 2 * F ⋅ ∂S∂C ⊡ otimesu(F', I) ## Loop over test functions for i in 1:ndofs @@ -257,13 +265,13 @@ function assemble_element!(ke, ge, cell, cv, fv, mp, ue, ΓN) δui = shape_value(cv, qp, i) ∇δui = shape_gradient(cv, qp, i) ## Add contribution to the residual from this test function - ge[i] += ( ∇δui ⊡ P - δui ⋅ b ) * dΩ + ge[i] += (∇δui ⊡ P - δui ⋅ b) * dΩ ∇δui∂P∂F = ∇δui ⊡ ∂P∂F # Hoisted computation for j in 1:ndofs ∇δuj = shape_gradient(cv, qp, j) ## Add contribution to the tangent - ke[i, j] += ( ∇δui∂P∂F ⊡ ∇δuj ) * dΩ + ke[i, j] += (∇δui∂P∂F ⊡ ∇δuj) * dΩ end end end @@ -282,6 +290,7 @@ function assemble_element!(ke, ge, cell, cv, fv, mp, ue, ΓN) end end end + return end; # Assembling global residual and tangent is also done in the usual way, just looping over @@ -303,6 +312,7 @@ function assemble_global!(K, g, dh, cv, fv, mp, u, ΓN) @timeit "element assemble" assemble_element!(ke, ge, cell, cv, fv, mp, ue, ΓN) assemble!(assembler, global_dofs, ke, ge) end + return end; # Finally, we define a main function which sets up everything and then performs Newton @@ -340,18 +350,20 @@ function solve() function rotation(X, t) θ = pi / 3 # 60° x, y, z = X - return t * Vec{3}(( - 0.0, - L/2 - y + (y-L/2)*cos(θ) - (z-L/2)*sin(θ), - L/2 - z + (y-L/2)*sin(θ) + (z-L/2)*cos(θ) - )) + return t * Vec{3}( + ( + 0.0, + L / 2 - y + (y - L / 2) * cos(θ) - (z - L / 2) * sin(θ), + L / 2 - z + (y - L / 2) * sin(θ) + (z - L / 2) * cos(θ), + ) + ) end dbcs = ConstraintHandler(dh) ## Add a homogeneous boundary condition on the "clamped" edge - dbc = Dirichlet(:u, getfacetset(grid, "right"), (x,t) -> [0.0, 0.0, 0.0], [1, 2, 3]) + dbc = Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> [0.0, 0.0, 0.0], [1, 2, 3]) add!(dbcs, dbc) - dbc = Dirichlet(:u, getfacetset(grid, "left"), (x,t) -> rotation(x, t), [1, 2, 3]) + dbc = Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> rotation(x, t), [1, 2, 3]) add!(dbcs, dbc) close!(dbcs) t = 0.5 @@ -368,7 +380,7 @@ function solve() ## Pre-allocation of vectors for the solution and Newton increments _ndofs = ndofs(dh) un = zeros(_ndofs) # previous solution vector - u = zeros(_ndofs) + u = zeros(_ndofs) Δu = zeros(_ndofs) ΔΔu = zeros(_ndofs) apply!(un, dbcs) @@ -379,11 +391,12 @@ function solve() ## Perform Newton iterations newton_itr = -1 - NEWTON_TOL = 1e-8 + NEWTON_TOL = 1.0e-8 NEWTON_MAXITER = 30 prog = ProgressMeter.ProgressThresh(NEWTON_TOL; desc = "Solving:") - while true; newton_itr += 1 + while true + newton_itr += 1 ## Construct the current guess u .= un .+ Δu ## Compute residual and tangent for current guess @@ -400,7 +413,7 @@ function solve() end ## Compute increment using conjugate gradients - @timeit "linear solve" IterativeSolvers.cg!(ΔΔu, K, g; maxiter=1000) + @timeit "linear solve" IterativeSolvers.cg!(ΔΔu, K, g; maxiter = 1000) apply_zero!(ΔΔu, dbcs) Δu .-= ΔΔu diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index d5c177bd7c..eda174b964 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -27,10 +27,12 @@ using BlockArrays, SparseArrays, LinearAlgebra # First we generate a simple grid, specifying the 4 corners of Cooks membrane. function create_cook_grid(nx, ny) - corners = [Vec{2}(( 0.0, 0.0)), - Vec{2}((48.0, 44.0)), - Vec{2}((48.0, 60.0)), - Vec{2}(( 0.0, 44.0))] + corners = [ + Vec{2}((0.0, 0.0)), + Vec{2}((48.0, 44.0)), + Vec{2}((48.0, 60.0)), + Vec{2}((0.0, 44.0)), + ] grid = generate_grid(Triangle, (nx, ny), corners) ## facesets for boundary conditions addfacetset!(grid, "clamped", x -> norm(x[1]) ≈ 0.0) @@ -41,7 +43,7 @@ end; # Next we define a function to set up our cell- and FacetValues. function create_values(interpolation_u, interpolation_p) ## quadrature rules - qr = QuadratureRule{RefTriangle}(3) + qr = QuadratureRule{RefTriangle}(3) facet_qr = FacetQuadratureRule{RefTriangle}(3) ## cell and FacetValues for u @@ -85,11 +87,11 @@ end # use a `BlockedArray` from `BlockArrays.jl`. function doassemble( - cellvalues_u::CellValues, - cellvalues_p::CellValues, - facetvalues_u::FacetValues, - K::SparseMatrixCSC, grid::Grid, dh::DofHandler, mp::LinearElasticity -) + cellvalues_u::CellValues, + cellvalues_p::CellValues, + facetvalues_u::FacetValues, + K::SparseMatrixCSC, grid::Grid, dh::DofHandler, mp::LinearElasticity + ) f = zeros(ndofs(dh)) assembler = start_assemble(K, f) nu = getnbasefunctions(cellvalues_u) @@ -169,14 +171,16 @@ function assemble_up!(Ke, fe, cell, cellvalues_u, cellvalues_p, facetvalues_u, g end end end + return end function symmetrize_lower!(Ke) for i in 1:size(Ke, 1) - for j in i+1:size(Ke, 1) + for j in (i + 1):size(Ke, 1) Ke[i, j] = Ke[j, i] end end + return end; # To evaluate the stresses after solving the problem we once again loop over the cells in @@ -201,8 +205,10 @@ end; # this formulation. Therefore we expand the strain to a 3D tensor, and then compute the (3D) # stress tensor. -function compute_stresses(cellvalues_u::CellValues, cellvalues_p::CellValues, - dh::DofHandler, mp::LinearElasticity, a::Vector) +function compute_stresses( + cellvalues_u::CellValues, cellvalues_p::CellValues, + dh::DofHandler, mp::LinearElasticity, a::Vector + ) ae = zeros(ndofs_per_cell(dh)) # local solution vector u_range = dof_range(dh, :u) # local range of dofs corresponding to u p_range = dof_range(dh, :p) # local range of dofs corresponding to p @@ -228,9 +234,9 @@ function compute_stresses(cellvalues_u::CellValues, cellvalues_p::CellValues, ## Expand strain to 3D ε3D = SymmetricTensor{2, 3}((i, j) -> i < 3 && j < 3 ? ε[i, j] : 0.0) ## Compute the stress in this quadrature point - σqp = 2 * mp.G * dev(ε3D) - one(ε3D) * p + σqp = 2 * mp.G * dev(ε3D) - one(ε3D) * p σΩi += σqp * dΩ - Ωi += dΩ + Ωi += dΩ end ## Store the value σ[cellid(cc)] = σΩi / Ωi @@ -265,11 +271,12 @@ function solve(ν, interpolation_u, interpolation_p) ## Compute the stress σ = compute_stresses(cellvalues_u, cellvalues_p, dh, mp, u) - σvM = map(x -> √(3/2 * dev(x) ⊡ dev(x)), σ) # von Mise effective stress + σvM = map(x -> √(3 / 2 * dev(x) ⊡ dev(x)), σ) # von Mise effective stress ## Export the solution and the stress - filename = "cook_" * (interpolation_u == Lagrange{RefTriangle, 1}()^2 ? "linear" : "quadratic") * - "_linear" + filename = "cook_" * + (interpolation_u == Lagrange{RefTriangle, 1}()^2 ? "linear" : "quadratic") * + "_linear" VTKGridFile(filename, grid) do vtk write_solution(vtk, dh, u) @@ -288,9 +295,9 @@ end # vectorize it to 2 dimensions such that we obtain vector shape functions (and 2nd order # tensors for the gradients). -linear_p = Lagrange{RefTriangle,1}() -linear_u = Lagrange{RefTriangle,1}()^2 -quadratic_u = Lagrange{RefTriangle,2}()^2 +linear_p = Lagrange{RefTriangle, 1}() +linear_u = Lagrange{RefTriangle, 1}()^2 +quadratic_u = Lagrange{RefTriangle, 2}()^2 #md nothing # hide # All that is left is to solve the problem. We choose a value of Poissons @@ -298,7 +305,7 @@ quadratic_u = Lagrange{RefTriangle,2}()^2 # linear/linear approximation to return garbage, and the quadratic/linear # approximation to be stable. -u1 = solve(0.5, linear_u, linear_p); +u1 = solve(0.5, linear_u, linear_p); u2 = solve(0.5, quadratic_u, linear_p); ## test the result #src diff --git a/docs/src/literate-tutorials/linear_elasticity.jl b/docs/src/literate-tutorials/linear_elasticity.jl index eead07c6ac..49181ee8b5 100644 --- a/docs/src/literate-tutorials/linear_elasticity.jl +++ b/docs/src/literate-tutorials/linear_elasticity.jl @@ -113,9 +113,9 @@ FerriteGmsh.Gmsh.finalize(); #hide # The generated grid lacks the facetsets for the boundaries, so we add them by using Ferrite's # [`addfacetset!`](@ref). It allows us to add facetsets to the grid based on coordinates. # Note that approximate comparison to 0.0 doesn't work well, so we use a tolerance instead. -addfacetset!(grid, "top", x -> x[2] ≈ 1.0) # facets for which x[2] ≈ 1.0 for all nodes -addfacetset!(grid, "left", x -> abs(x[1]) < 1e-6) -addfacetset!(grid, "bottom", x -> abs(x[2]) < 1e-6); +addfacetset!(grid, "top", x -> x[2] ≈ 1.0) # facets for which x[2] ≈ 1.0 for all nodes +addfacetset!(grid, "left", x -> abs(x[1]) < 1.0e-6) +addfacetset!(grid, "bottom", x -> abs(x[2]) < 1.0e-6); # ### Trial and test functions # In this tutorial, we use the same linear Lagrange shape functions to approximate both the @@ -156,7 +156,7 @@ close!(dh); # constrained. If no argument is given, all components are constrained by default. ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> 0.0, 2)) -add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0.0, 1)) +add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0.0, 1)) close!(ch); # In addition, we will use Neumann boundary conditions on the top surface, where @@ -164,7 +164,7 @@ close!(ch); # ```math # \boldsymbol{t}_\mathrm{N}(\boldsymbol{x}) = (20e3) x_1 \boldsymbol{e}_2\ \mathrm{N}/\mathrm{mm}^2 # ``` -traction(x) = Vec(0.0, 20e3 * x[1]); +traction(x) = Vec(0.0, 20.0e3 * x[1]); # On the right boundary, we don't do anything, resulting in a zero traction Neumann boundary. # In order to assemble the external forces, $f_i^\mathrm{ext}$, we need to iterate over all @@ -222,15 +222,15 @@ end # ```math # G = \frac{E}{2(1 + \nu)}, \quad K = \frac{E}{3(1 - 2\nu)} # ``` -Emod = 200e3 # Young's modulus [MPa] -ν = 0.3 # Poisson's ratio [-] +Emod = 200.0e3 # Young's modulus [MPa] +ν = 0.3 # Poisson's ratio [-] Gmod = Emod / (2(1 + ν)) # Shear modulus Kmod = Emod / (3(1 - 2ν)) # Bulk modulus # Finally, we demonstrate `Tensors.jl`'s automatic differentiation capabilities when # calculating the elastic stiffness tensor -C = gradient(ϵ -> 2 * Gmod * dev(ϵ) + 3 * Kmod * vol(ϵ), zero(SymmetricTensor{2,2})); +C = gradient(ϵ -> 2 * Gmod * dev(ϵ) + 3 * Kmod * vol(ϵ), zero(SymmetricTensor{2, 2})); #md # !!! details "Plane stress instead of plane strain?" #md # In order to change this tutorial to consider plane stress instead of plane strain, @@ -326,8 +326,9 @@ u = K \ f_ext; function calculate_stresses(grid, dh, cv, u, C) qp_stresses = [ - [zero(SymmetricTensor{2,2}) for _ in 1:getnquadpoints(cv)] - for _ in 1:getncells(grid)] + [zero(SymmetricTensor{2, 2}) for _ in 1:getnquadpoints(cv)] + for _ in 1:getncells(grid) + ] avg_cell_stresses = tuple((zeros(getncells(grid)) for _ in 1:3)...) for cell in CellIterator(dh) reinit!(cv, cell) @@ -357,8 +358,8 @@ colors = [ #hide "1" => 1, "5" => 1, # purple #hide "2" => 2, "3" => 2, # red #hide "4" => 3, # blue #hide - "6" => 4 # green #hide - ] #hide + "6" => 4, # green #hide +] #hide for (key, color) in colors #hide for i in getcellset(grid, key) #hide color_data[i] = color #hide diff --git a/docs/src/literate-tutorials/linear_shell.jl b/docs/src/literate-tutorials/linear_shell.jl index 014ce2948a..71a74c79c1 100644 --- a/docs/src/literate-tutorials/linear_shell.jl +++ b/docs/src/literate-tutorials/linear_shell.jl @@ -1,3 +1,4 @@ +# runic: off # # [Linear shell](@id tutorial-linear-shell) # # ![](linear_shell.png) diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index 478314fe27..c5b8a53ef4 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -1,10 +1,10 @@ # We check for a divergence free velocity field in the CI #src - if isdefined(Main, :is_ci) #hide - IS_CI = Main.is_ci #hide - else #hide - IS_CI = false #hide - end #hide - nothing #hide +if isdefined(Main, :is_ci) #hide + IS_CI = Main.is_ci #hide +else #hide + IS_CI = false #hide +end #hide +nothing #hide # # [Incompressible Navier-Stokes equations via DifferentialEquations.jl](@id tutorial-ins-ordinarydiffeq) # # ![nsdiffeq](nsdiffeq.gif) @@ -125,7 +125,7 @@ using Ferrite, SparseArrays, BlockArrays, LinearAlgebra, UnPack, LinearSolve, Wr using OrdinaryDiffEq # We start off by defining our only material parameter. -ν = 1.0/1000.0; #dynamic viscosity +ν = 1.0 / 1000.0; #dynamic viscosity # Next a rectangular grid with a cylinder in it has to be generated. # We use Gmsh.jl for the creation of the mesh and FerriteGmsh.jl to translate it to a `Ferrite.Grid`. @@ -139,13 +139,15 @@ dim = 2; # We specify first the rectangle, the cylinder, the surface spanned by the cylinder # and the boolean difference of rectangle and cylinder. if !IS_CI #hide + # runic: off #src rect_tag = gmsh.model.occ.add_rectangle(0, 0, 0, 1.1, 0.41) circle_tag = gmsh.model.occ.add_circle(0.2, 0.2, 0, 0.05) circle_curve_tag = gmsh.model.occ.add_curve_loop([circle_tag]) circle_surf_tag = gmsh.model.occ.add_plane_surface([circle_curve_tag]) -gmsh.model.occ.cut([(dim,rect_tag)],[(dim,circle_surf_tag)]); +gmsh.model.occ.cut([(dim, rect_tag)], [(dim, circle_surf_tag)]) + # runic: on #src else #hide -rect_tag = gmsh.model.occ.add_rectangle(0, 0, 0, 0.55, 0.41); #hide + rect_tag = gmsh.model.occ.add_rectangle(0, 0, 0, 0.55, 0.41) #hide end #hide nothing #hide # Now, the geometrical entities need to be synchronized in order to be available outside @@ -153,26 +155,28 @@ nothing gmsh.model.occ.synchronize() # In the next lines, we add the physical groups needed to define boundary conditions. if !IS_CI #hide -bottomtag = gmsh.model.model.add_physical_group(dim-1,[6],-1,"bottom") -lefttag = gmsh.model.model.add_physical_group(dim-1,[7],-1,"left") -righttag = gmsh.model.model.add_physical_group(dim-1,[8],-1,"right") -toptag = gmsh.model.model.add_physical_group(dim-1,[9],-1,"top") -holetag = gmsh.model.model.add_physical_group(dim-1,[5],-1,"hole"); + # runic: off #src +bottomtag = gmsh.model.model.add_physical_group(dim - 1, [6], -1, "bottom") +lefttag = gmsh.model.model.add_physical_group(dim - 1, [7], -1, "left") +righttag = gmsh.model.model.add_physical_group(dim - 1, [8], -1, "right") +toptag = gmsh.model.model.add_physical_group(dim - 1, [9], -1, "top") +holetag = gmsh.model.model.add_physical_group(dim - 1, [5], -1, "hole") + # runic: on #src else #hide -gmsh.model.model.add_physical_group(dim-1,[4],7,"left") #hide -gmsh.model.model.add_physical_group(dim-1,[3],8,"top") #hide -gmsh.model.model.add_physical_group(dim-1,[2],9,"right") #hide -gmsh.model.model.add_physical_group(dim-1,[1],10,"bottom"); #hide -end #hide + gmsh.model.model.add_physical_group(dim - 1, [4], 7, "left") #hide + gmsh.model.model.add_physical_group(dim - 1, [3], 8, "top") #hide + gmsh.model.model.add_physical_group(dim - 1, [2], 9, "right") #hide + gmsh.model.model.add_physical_group(dim - 1, [1], 10, "bottom") #hide +end #hide nothing #hide # Since we want a quad mesh, we specify the meshing algorithm to the quasi structured quad one. # For a complete list, [see the Gmsh docs](https://gmsh.info/doc/texinfo/gmsh.html#Mesh-options-list). -gmsh.option.setNumber("Mesh.Algorithm",11) -gmsh.option.setNumber("Mesh.MeshSizeFromCurvature",20) -gmsh.option.setNumber("Mesh.MeshSizeMax",0.05) +gmsh.option.setNumber("Mesh.Algorithm", 11) +gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 20) +gmsh.option.setNumber("Mesh.MeshSizeMax", 0.05) if IS_CI #hide -gmsh.option.setNumber("Mesh.MeshSizeFromCurvature",20) #hide -gmsh.option.setNumber("Mesh.MeshSizeMax",0.15) #hide + gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 20) #hide + gmsh.option.setNumber("Mesh.MeshSizeMax", 0.15) #hide end #hide # In the next step, the mesh is generated and finally translated. gmsh.model.mesh.generate(dim) @@ -204,10 +208,10 @@ ch = ConstraintHandler(dh); nosplip_facet_names = ["top", "bottom", "hole"]; # No hole for the test present #src if IS_CI #hide -nosplip_facet_names = ["top", "bottom"] #hide + nosplip_facet_names = ["top", "bottom"] #hide end #hide -∂Ω_noslip = union(getfacetset.((grid, ), nosplip_facet_names)...); -noslip_bc = Dirichlet(:v, ∂Ω_noslip, (x, t) -> Vec((0.0,0.0)), [1,2]) +∂Ω_noslip = union(getfacetset.((grid,), nosplip_facet_names)...); +noslip_bc = Dirichlet(:v, ∂Ω_noslip, (x, t) -> Vec((0.0, 0.0)), [1, 2]) add!(ch, noslip_bc); # The left boundary has a parabolic inflow with peak velocity of 1.5. This @@ -220,10 +224,10 @@ add!(ch, noslip_bc); # !!! note # The kink in the velocity profile will lead to a discontinuity in the pressure at $t=1$. # This needs to be considered in the DiffEq `init` by providing the keyword argument `d_discontinuities=[1.0]`. -vᵢₙ(t) = min(t*1.5, 1.5) #inflow velocity +vᵢₙ(t) = min(t * 1.5, 1.5) #inflow velocity -parabolic_inflow_profile(x,t) = Vec((4*vᵢₙ(t)*x[2]*(0.41-x[2])/0.41^2, 0.0)) -inflow_bc = Dirichlet(:v, ∂Ω_inflow, parabolic_inflow_profile, [1,2]) +parabolic_inflow_profile(x, t) = Vec((4 * vᵢₙ(t) * x[2] * (0.41 - x[2]) / 0.41^2, 0.0)) +inflow_bc = Dirichlet(:v, ∂Ω_inflow, parabolic_inflow_profile, [1, 2]) add!(ch, inflow_bc); # The outflow boundary condition has been applied on the right side of the @@ -399,7 +403,7 @@ p = RHSparams(K, ch, dh, cellvalues_v, copy(u₀)) function ferrite_limiter!(u, _, p, t) update!(p.ch, t) - apply!(u, p.ch) + return apply!(u, p.ch) end function navierstokes_rhs_element!(dvₑ, vₑ, cellvalues_v) @@ -420,12 +424,13 @@ function navierstokes_rhs_element!(dvₑ, vₑ, cellvalues_v) dvₑ[j] -= v ⋅ ∇v' ⋅ φⱼ * dΩ end end + return end -function navierstokes!(du,u_uc,p::RHSparams,t) +function navierstokes!(du, u_uc, p::RHSparams, t) # Unpack the struct to save some allocations. #+ - @unpack K,ch,dh,cellvalues_v,u = p + @unpack K, ch, dh, cellvalues_v, u = p # We start by applying the time-dependent Dirichlet BCs. Note that we are # not allowed to mutate `u_uc`! Furthermore not that we also can not pre- @@ -455,6 +460,7 @@ function navierstokes!(du,u_uc,p::RHSparams,t) navierstokes_rhs_element!(duₑ, vₑ, cellvalues_v) assemble!(du, v_celldofs, duₑ) end + return end; function navierstokes_jac_element!(Jₑ, vₑ, cellvalues_v) @@ -479,9 +485,10 @@ function navierstokes_jac_element!(Jₑ, vₑ, cellvalues_v) end end end + return end -function navierstokes_jac!(J,u_uc,p,t) +function navierstokes_jac!(J, u_uc, p, t) # Unpack the struct to save some allocations. #+ @unpack K, ch, dh, cellvalues_v, u = p @@ -499,7 +506,7 @@ function navierstokes_jac!(J,u_uc,p,t) ## Here we assume that J has exactly the same structure as K by construction nonzeros(J) .= nonzeros(K) - assembler = start_assemble(J; fillzero=false) + assembler = start_assemble(J; fillzero = false) ## Assemble variation of the nonlinear term n_basefuncs = getnbasefunctions(cellvalues_v) @@ -519,13 +526,13 @@ function navierstokes_jac!(J,u_uc,p,t) # Finally we eliminate the constrained dofs from the Jacobian to # decouple them in the nonlinear solver from the remaining system. #+ - apply!(J, ch) + return apply!(J, ch) end; # Finally, together with our pre-assembled mass matrix, we are now able to # define our problem in mass matrix form. -rhs = ODEFunction(navierstokes!, mass_matrix=M; jac=navierstokes_jac!, jac_prototype=jac_sparsity) -problem = ODEProblem(rhs, u₀, (0.0,T), p); +rhs = ODEFunction(navierstokes!, mass_matrix = M; jac = navierstokes_jac!, jac_prototype = jac_sparsity) +problem = ODEProblem(rhs, u₀, (0.0, T), p); # All norms must not depend on constrained dofs. A problem with the presented implementation # is that we are currently unable to strictly enforce constraint everywhere in the internal @@ -550,7 +557,7 @@ end # To visualize the result we export the grid and our fields # to VTK-files, which can be viewed in [ParaView](https://www.paraview.org/) # by utilizing the corresponding pvd file. -timestepper = Rodas5P(autodiff=false, step_limiter! = ferrite_limiter!); +timestepper = Rodas5P(autodiff = false, step_limiter! = ferrite_limiter!); # timestepper = ImplicitEuler(nlsolve=NonlinearSolveAlg(OrdinaryDiffEq.NonlinearSolve.NewtonRaphson(autodiff=OrdinaryDiffEq.AutoFiniteDiff()); max_iter=50), step_limiter! = ferrite_limiter!) #src #NOTE! This is left for future reference #src # function algebraicmultigrid(W,du,u,p,t,newW,Plprev,Prprev,solverdata) #src @@ -566,10 +573,10 @@ timestepper = Rodas5P(autodiff=false, step_limiter! = ferrite_limiter!); # !!! info "Debugging convergence issues" # We can obtain some debug information from OrdinaryDiffEq by wrapping the following section into a [debug logger](https://docs.julialang.org/en/v1/stdlib/Logging/#Example:-Enable-debug-level-messages). integrator = init( - problem, timestepper; initializealg=NoInit(), dt=Δt₀, - adaptive=true, abstol=1e-4, reltol=1e-5, - progress=true, progress_steps=1, - verbose=true, internalnorm=FreeDofErrorNorm(ch), d_discontinuities=[1.0] + problem, timestepper; initializealg = NoInit(), dt = Δt₀, + adaptive = true, abstol = 1.0e-4, reltol = 1.0e-5, + progress = true, progress_steps = 1, + verbose = true, internalnorm = FreeDofErrorNorm(ch), d_discontinuities = [1.0] ); @@ -577,7 +584,7 @@ integrator = init( # Exporting interpolated solutions of problems containing mass matrices is currently broken. # Thus, the `intervals` iterator is used. Note that `solve` holds all solutions in the memory. pvd = paraview_collection("vortex-street") -for (step, (u,t)) in enumerate(intervals(integrator)) +for (step, (u, t)) in enumerate(intervals(integrator)) VTKGridFile("vortex-street-$step", dh) do vtk write_solution(vtk, dh, u) pvd[t] = vtk @@ -594,11 +601,11 @@ if IS_CI Ferrite.reinit!(cellvalues_v, cell) #hide for q_point in 1:getnquadpoints(cellvalues_v) #hide dΩ = getdetJdV(cellvalues_v, q_point) #hide - #hide + #hide all_celldofs = celldofs(cell) #hide v_celldofs = all_celldofs[dof_range(dh, :v)] #hide v_cell = u[v_celldofs] #hide - #hide + #hide divv += function_divergence(cellvalues_v, q_point, v_cell) * dΩ #hide end #hide end #hide @@ -607,8 +614,8 @@ if IS_CI let #hide u = copy(integrator.u) #hide Δdivv = abs(compute_divergence(dh, u, cellvalues_v)) #hide - @test isapprox(Δdivv, 0.0, atol=1e-12) #hide - #hide + @test isapprox(Δdivv, 0.0, atol = 1.0e-12) #hide + #hide Δv = 0.0 #hide for cell in CellIterator(dh) #hide Ferrite.reinit!(cellvalues_v, cell) #hide @@ -620,11 +627,11 @@ if IS_CI dΩ = getdetJdV(cellvalues_v, q_point) #hide coords_qp = spatial_coordinate(cellvalues_v, q_point, coords) #hide v = function_value(cellvalues_v, q_point, v_cell) #hide - Δv += norm(v - parabolic_inflow_profile(coords_qp, T))^2*dΩ #hide + Δv += norm(v - parabolic_inflow_profile(coords_qp, T))^2 * dΩ #hide end #hide end #hide - @test isapprox(sqrt(Δv), 0.0, atol=1e-3) #hide - end; #hide + @test isapprox(sqrt(Δv), 0.0, atol = 1.0e-3) #hide + end #hide nothing #hide end #hide diff --git a/docs/src/literate-tutorials/plasticity.jl b/docs/src/literate-tutorials/plasticity.jl index 79a35c18c4..b1e4cbf60d 100644 --- a/docs/src/literate-tutorials/plasticity.jl +++ b/docs/src/literate-tutorials/plasticity.jl @@ -47,12 +47,12 @@ end; # Next, we define a constructor for the material instance. function J2Plasticity(E, ν, σ₀, H) - δ(i,j) = i == j ? 1.0 : 0.0 # helper function + δ(i, j) = i == j ? 1.0 : 0.0 # helper function G = E / 2(1 + ν) K = E / 3(1 - 2ν) - Isymdev(i,j,k,l) = 0.5*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k)) - 1.0/3.0*δ(i,j)*δ(k,l) - temp(i,j,k,l) = 2.0G *( 0.5*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k)) + ν/(1.0-2.0ν)*δ(i,j)*δ(k,l)) + Isymdev(i, j, k, l) = 0.5 * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)) - 1.0 / 3.0 * δ(i, j) * δ(k, l) + temp(i, j, k, l) = 2.0G * (0.5 * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)) + ν / (1.0 - 2.0ν) * δ(i, j) * δ(k, l)) Dᵉ = SymmetricTensor{4, 3}(temp) return J2Plasticity(G, K, σ₀, H, Dᵉ) end; @@ -73,16 +73,17 @@ end # Constructor for initializing a material state. Every quantity is set to zero. function MaterialState() return MaterialState( - zero(SymmetricTensor{2, 3}), - zero(SymmetricTensor{2, 3}), - 0.0) + zero(SymmetricTensor{2, 3}), + zero(SymmetricTensor{2, 3}), + 0.0 + ) end # For later use, during the post-processing step, we define a function to # compute the von Mises effective stress. function vonMises(σ) s = dev(σ) - return sqrt(3.0/2.0 * s ⊡ s) + return sqrt(3.0 / 2.0 * s ⊡ s) end; # ## Constitutive driver @@ -99,16 +100,16 @@ function compute_stress_tangent(ϵ::SymmetricTensor{2, 3}, material::J2Plasticit σᵗ = material.Dᵉ ⊡ (ϵ - state.ϵᵖ) # trial-stress sᵗ = dev(σᵗ) # deviatoric part of trial-stress J₂ = 0.5 * sᵗ ⊡ sᵗ # second invariant of sᵗ - σᵗₑ = sqrt(3.0*J₂) # effective trial-stress (von Mises stress) + σᵗₑ = sqrt(3.0 * J₂) # effective trial-stress (von Mises stress) σʸ = material.σ₀ + H * state.k # Previous yield limit - φᵗ = σᵗₑ - σʸ # Trial-value of the yield surface + φᵗ = σᵗₑ - σʸ # Trial-value of the yield surface if φᵗ < 0.0 # elastic loading return σᵗ, material.Dᵉ, MaterialState(state.ϵᵖ, σᵗ, state.k) else # plastic loading h = H + 3G - μ = φᵗ / h # plastic multiplier + μ = φᵗ / h # plastic multiplier c1 = 1 - 3G * μ / σᵗₑ s = c1 * sᵗ # updated deviatoric stress @@ -118,18 +119,18 @@ function compute_stress_tangent(ϵ::SymmetricTensor{2, 3}, material::J2Plasticit κ = H * (state.k + μ) # drag stress σₑ = material.σ₀ + κ # updated yield surface - δ(i,j) = i == j ? 1.0 : 0.0 - Isymdev(i,j,k,l) = 0.5*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k)) - 1.0/3.0*δ(i,j)*δ(k,l) - Q(i,j,k,l) = Isymdev(i,j,k,l) - 3.0 / (2.0*σₑ^2) * s[i,j]*s[k,l] - b = (3G*μ/σₑ) / (1.0 + 3G*μ/σₑ) + δ(i, j) = i == j ? 1.0 : 0.0 + Isymdev(i, j, k, l) = 0.5 * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)) - 1.0 / 3.0 * δ(i, j) * δ(k, l) + Q(i, j, k, l) = Isymdev(i, j, k, l) - 3.0 / (2.0 * σₑ^2) * s[i, j] * s[k, l] + b = (3G * μ / σₑ) / (1.0 + 3G * μ / σₑ) - Dtemp(i,j,k,l) = -2G*b * Q(i,j,k,l) - 9G^2 / (h*σₑ^2) * s[i,j]*s[k,l] + Dtemp(i, j, k, l) = -2G * b * Q(i, j, k, l) - 9G^2 / (h * σₑ^2) * s[i, j] * s[k, l] D = material.Dᵉ + SymmetricTensor{4, 3}(Dtemp) ## Return new state - Δϵᵖ = 3/2 * μ / σₑ * s # plastic strain - ϵᵖ = state.ϵᵖ + Δϵᵖ # plastic strain - k = state.k + μ # hardening variable + Δϵᵖ = 3 / 2 * μ / σₑ * s # plastic strain + ϵᵖ = state.ϵᵖ + Δϵᵖ # plastic strain + k = state.k + μ # hardening variable return σ, D, MaterialState(ϵᵖ, σ, k) end end @@ -138,7 +139,7 @@ end # What follows are methods for assembling and and solving the FE-problem. function create_values(interpolation) ## setup quadrature rules - qr = QuadratureRule{RefTetrahedron}(2) + qr = QuadratureRule{RefTetrahedron}(2) facet_qr = FacetQuadratureRule{RefTetrahedron}(3) ## cell and facetvalues for u @@ -161,7 +162,7 @@ function create_bc(dh, grid) dbcs = ConstraintHandler(dh) ## Clamped on the left side dofs = [1, 2, 3] - dbc = Dirichlet(:u, getfacetset(grid, "left"), (x,t) -> [0.0, 0.0, 0.0], dofs) + dbc = Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> [0.0, 0.0, 0.0], dofs) add!(dbcs, dbc) close!(dbcs) return dbcs @@ -172,8 +173,10 @@ end; # # * Residual vector `r` # * Tangent stiffness `K` -function doassemble!(K::SparseMatrixCSC, r::Vector, cellvalues::CellValues, dh::DofHandler, - material::J2Plasticity, u, states, states_old) +function doassemble!( + K::SparseMatrixCSC, r::Vector, cellvalues::CellValues, dh::DofHandler, + material::J2Plasticity, u, states, states_old + ) assembler = start_assemble(K, r) nu = getnbasefunctions(cellvalues) re = zeros(nu) # element residual vector @@ -197,8 +200,7 @@ end #md # Due to symmetry, we only compute the lower half of the tangent #md # and then symmetrize it. #md # -function assemble_cell!(Ke, re, cell, cellvalues, material, - ue, state, state_old) +function assemble_cell!(Ke, re, cell, cellvalues, material, ue, state, state_old) n_basefuncs = getnbasefunctions(cellvalues) reinit!(cellvalues, cell) @@ -218,15 +220,17 @@ function assemble_cell!(Ke, re, cell, cellvalues, material, end end symmetrize_lower!(Ke) + return end # Helper function to symmetrize the material tangent function symmetrize_lower!(K) - for i in 1:size(K,1) - for j in i+1:size(K,1) - K[i,j] = K[j,i] + for i in 1:size(K, 1) + for j in (i + 1):size(K, 1) + K[i, j] = K[j, i] end end + return end; function doassemble_neumann!(r, dh, facetset, facetvalues, t) @@ -251,10 +255,10 @@ end # Define a function which solves the FE-problem. function solve() ## Define material parameters - E = 200.0e9 # [Pa] - H = E/20 # [Pa] - ν = 0.3 # [-] - σ₀ = 200e6 # [Pa] + E = 200.0e9 # [Pa] + H = E / 20 # [Pa] + ν = 0.3 # [-] + σ₀ = 200.0e6 # [Pa] material = J2Plasticity(E, ν, σ₀, H) L = 10.0 # beam length [m] @@ -262,7 +266,7 @@ function solve() h = 1.0 # beam height[m] n_timesteps = 10 u_max = zeros(n_timesteps) - traction_magnitude = 1.e7 * range(0.5, 1.0, length=n_timesteps) + traction_magnitude = 1.0e7 * range(0.5, 1.0, length = n_timesteps) ## Create geometry, dofs and boundary conditions n = 2 @@ -279,10 +283,10 @@ function solve() ## Pre-allocate solution vectors, etc. n_dofs = ndofs(dh) # total number of dofs - u = zeros(n_dofs) # solution vector + u = zeros(n_dofs) # solution vector Δu = zeros(n_dofs) # displacement correction r = zeros(n_dofs) # residual - K = allocate_matrix(dh); # tangent stiffness matrix + K = allocate_matrix(dh) # tangent stiffness matrix ## Create material states. One array for each cell, where each element is an array of material- ## states - one for each integration point @@ -302,14 +306,14 @@ function solve() update!(dbcs, t) # evaluates the D-bndc at time t apply!(u, dbcs) # set the prescribed values in the solution vector - while true; newton_itr += 1 - + while true + newton_itr += 1 if newton_itr > 8 error("Reached maximum Newton iterations, aborting") break end ## Tangent and residual contribution from the cells (volume integral) - doassemble!(K, r, cellvalues, dh, material, u, states, states_old); + doassemble!(K, r, cellvalues, dh, material, u, states, states_old) ## Residual contribution from the Neumann boundary (surface integral) doassemble_neumann!(r, dh, getfacetset(grid, "right"), facetvalues, traction) norm_r = norm(r[Ferrite.free_dofs(dbcs)]) @@ -339,7 +343,7 @@ function solve() for (el, cell_states) in enumerate(eachcol(states)) for state in cell_states mises_values[el] += vonMises(state.σ) - κ_values[el] += state.k*material.H + κ_values[el] += state.k * material.H end mises_values[el] /= length(cell_states) # average von Mises stress κ_values[el] /= length(cell_states) # average drag stress @@ -362,11 +366,11 @@ using Plots plot( vcat(0.0, u_max), # add the origin as a point vcat(0.0, traction_magnitude), - linewidth=2, - title="Traction-displacement", - label=nothing, - markershape=:auto - ) + linewidth = 2, + title = "Traction-displacement", + label = nothing, + markershape = :auto +) ylabel!("Traction [Pa]") xlabel!("Maximum deflection [m]") diff --git a/docs/src/literate-tutorials/porous_media.jl b/docs/src/literate-tutorials/porous_media.jl index 8af94e8cc4..71c0837d62 100644 --- a/docs/src/literate-tutorials/porous_media.jl +++ b/docs/src/literate-tutorials/porous_media.jl @@ -102,15 +102,15 @@ using Ferrite, FerriteMeshParser, Tensors, WriteVTK # We start by defining the elastic material type, containing the elastic stiffness, # for the linear elastic impermeable solid aggregates. struct Elastic{T} - C::SymmetricTensor{4,2,T,9} + C::SymmetricTensor{4, 2, T, 9} end -function Elastic(;E=20.e3, ν=0.3) +function Elastic(; E = 20.0e3, ν = 0.3) G = E / 2(1 + ν) K = E / 3(1 - 2ν) - I2 = one(SymmetricTensor{2,2}) - I4vol = I2⊗I2 - I4dev = minorsymmetric(otimesu(I2,I2)) - I4vol / 3 - return Elastic(2G*I4dev + K*I4vol) + I2 = one(SymmetricTensor{2, 2}) + I4vol = I2 ⊗ I2 + I4dev = minorsymmetric(otimesu(I2, I2)) - I4vol / 3 + return Elastic(2G * I4dev + K * I4vol) end; # Next, we define the element routine for the solid aggregates, where we dispatch on the @@ -125,13 +125,14 @@ function element_routine!(Ke, re, material::Elastic, cv, cell, a, args...) σ = material.C ⊡ ϵ for i in 1:n_basefuncs δ∇N = shape_symmetric_gradient(cv, q_point, i) - re[i] += (δ∇N ⊡ σ)*dΩ + re[i] += (δ∇N ⊡ σ) * dΩ for j in 1:n_basefuncs ∇N = shape_symmetric_gradient(cv, q_point, j) Ke[i, j] += (δ∇N ⊡ material.C ⊡ ∇N) * dΩ end end end + return end; # ### PoroElasticity @@ -144,7 +145,7 @@ struct PoroElastic{T} α::T ## Biot's coefficient [-] β::T ## Liquid compressibility [1/MPa] end -PoroElastic(;elastic, k, ϕ, α, β) = PoroElastic(elastic, k, ϕ, α, β); +PoroElastic(; elastic, k, ϕ, α, β) = PoroElastic(elastic, k, ϕ, α, β); # The element routine requires a few more inputs since we have two fields, as well # as the dependence on the rates of the displacements and pressure. @@ -163,17 +164,17 @@ function element_routine!(Ke, re, m::PoroElastic, cvs::Tuple, cell, a, a_old, Δ dΩ = getdetJdV(cv_u, q_point) p = function_value(cv_p, q_point, a, dr_p) p_old = function_value(cv_p, q_point, a_old, dr_p) - pdot = (p - p_old)/Δt + pdot = (p - p_old) / Δt ∇p = function_gradient(cv_p, q_point, a, dr_p) ϵ = function_symmetric_gradient(cv_u, q_point, a, dr_u) tr_ϵ_old = function_divergence(cv_u, q_point, a_old, dr_u) - tr_ϵ_dot = (tr(ϵ) - tr_ϵ_old)/Δt + tr_ϵ_dot = (tr(ϵ) - tr_ϵ_old) / Δt σ_eff = C ⊡ ϵ ## Variation of u_i for (iᵤ, Iᵤ) in pairs(dr_u) ∇δNu = shape_symmetric_gradient(cv_u, q_point, iᵤ) div_δNu = shape_divergence(cv_u, q_point, iᵤ) - re[Iᵤ] += (∇δNu ⊡ σ_eff - div_δNu*p*m.α) * dΩ + re[Iᵤ] += (∇δNu ⊡ σ_eff - div_δNu * p * m.α) * dΩ for (jᵤ, Jᵤ) in pairs(dr_u) ∇Nu = shape_symmetric_gradient(cv_u, q_point, jᵤ) Ke[Iᵤ, Jᵤ] += (∇δNu ⊡ C ⊡ ∇Nu) * dΩ @@ -187,23 +188,24 @@ function element_routine!(Ke, re, m::PoroElastic, cvs::Tuple, cell, a, a_old, Δ for (iₚ, Iₚ) in pairs(dr_p) δNp = shape_value(cv_p, q_point, iₚ) ∇δNp = shape_gradient(cv_p, q_point, iₚ) - re[Iₚ] += (δNp * (m.α * tr_ϵ_dot + m.β*pdot) + m.k * (∇δNp ⋅ ∇p) ) * dΩ + re[Iₚ] += (δNp * (m.α * tr_ϵ_dot + m.β * pdot) + m.k * (∇δNp ⋅ ∇p)) * dΩ for (jᵤ, Jᵤ) in pairs(dr_u) div_Nu = shape_divergence(cv_u, q_point, jᵤ) - Ke[Iₚ,Jᵤ] += δNp*(m.α/Δt)*div_Nu*dΩ + Ke[Iₚ, Jᵤ] += δNp * (m.α / Δt) * div_Nu * dΩ end for (jₚ, Jₚ) in pairs(dr_p) ∇Np = shape_gradient(cv_p, q_point, jₚ) Np = shape_value(cv_p, q_point, jₚ) - Ke[Iₚ,Jₚ] += (δNp * m.β*Np/Δt + m.k * (∇δNp ⋅ ∇Np) ) * dΩ + Ke[Iₚ, Jₚ] += (δNp * m.β * Np / Δt + m.k * (∇δNp ⋅ ∇Np)) * dΩ end end end + return end; # ### Assembly # To organize the different domains, we'll first define a container type -struct FEDomain{M,CV,SDH<:SubDofHandler} +struct FEDomain{M, CV, SDH <: SubDofHandler} material::M cellvalues::CV sdh::SDH @@ -217,6 +219,7 @@ function doassemble!(K, r, domains::Vector{<:FEDomain}, a, a_old, Δt) for domain in domains doassemble!(assembler, domain, a, a_old, Δt) end + return end; # For one domain (corresponding to a specific SubDofHandler), @@ -229,19 +232,20 @@ function doassemble!(assembler, domain::FEDomain, a, a_old, Δt) cv = domain.cellvalues sdh = domain.sdh n = ndofs_per_cell(sdh) - Ke = zeros(n,n) + Ke = zeros(n, n) re = zeros(n) ae_old = zeros(n) ae = zeros(n) for cell in CellIterator(sdh) ## copy values from a to ae - map!(i->a[i], ae, celldofs(cell)) - map!(i->a_old[i], ae_old, celldofs(cell)) + map!(i -> a[i], ae, celldofs(cell)) + map!(i -> a_old[i], ae_old, celldofs(cell)) fill!(Ke, 0) fill!(re, 0) element_routine!(Ke, re, material, cv, cell, ae, ae_old, Δt, sdh) assemble!(assembler, celldofs(cell), Ke, re) end + return end; # ### Mesh import @@ -262,23 +266,23 @@ end; # ### Problem setup # Define the finite element interpolation, integration, and boundary conditions. -function setup_problem(;t_rise=0.1, u_max=-0.1) +function setup_problem(; t_rise = 0.1, u_max = -0.1) grid = get_grid() ## Define materials - m_solid = Elastic(;E=20.e3, ν=0.3) - m_porous = PoroElastic(;elastic=Elastic(;E=10e3, ν=0.3), β=1/15e3, α=0.9, k=5.0e-3, ϕ=0.8) + m_solid = Elastic(; E = 20.0e3, ν = 0.3) + m_porous = PoroElastic(; elastic = Elastic(; E = 10.0e3, ν = 0.3), β = 1 / 15.0e3, α = 0.9, k = 5.0e-3, ϕ = 0.8) ## Define interpolations - ipu_quad = Lagrange{RefQuadrilateral,2}()^2 - ipu_tri = Lagrange{RefTriangle,2}()^2 - ipp_quad = Lagrange{RefQuadrilateral,1}() - ipp_tri = Lagrange{RefTriangle,1}() + ipu_quad = Lagrange{RefQuadrilateral, 2}()^2 + ipu_tri = Lagrange{RefTriangle, 2}()^2 + ipp_quad = Lagrange{RefQuadrilateral, 1}() + ipp_tri = Lagrange{RefTriangle, 1}() ## Quadrature rules qr_quad = QuadratureRule{RefQuadrilateral}(2) - qr_tri = QuadratureRule{RefTriangle}(2) + qr_tri = QuadratureRule{RefTriangle}(2) ## CellValues cvu_quad = CellValues(qr_quad, ipu_quad) @@ -289,10 +293,10 @@ function setup_problem(;t_rise=0.1, u_max=-0.1) ## Setup the DofHandler dh = DofHandler(grid) ## Solid quads - sdh_solid_quad = SubDofHandler(dh, getcellset(grid,"solid4")) + sdh_solid_quad = SubDofHandler(dh, getcellset(grid, "solid4")) add!(sdh_solid_quad, :u, ipu_quad) ## Solid triangles - sdh_solid_tri = SubDofHandler(dh, getcellset(grid,"solid3")) + sdh_solid_tri = SubDofHandler(dh, getcellset(grid, "solid3")) add!(sdh_solid_tri, :u, ipu_tri) ## Porous quads sdh_porous_quad = SubDofHandler(dh, getcellset(grid, "porous4")) @@ -306,21 +310,22 @@ function setup_problem(;t_rise=0.1, u_max=-0.1) close!(dh) ## Setup the domains - domains = [FEDomain(m_solid, cvu_quad, sdh_solid_quad), - FEDomain(m_solid, cvu_tri, sdh_solid_tri), - FEDomain(m_porous, (cvu_quad, cvp_quad), sdh_porous_quad), - FEDomain(m_porous, (cvu_tri, cvp_tri), sdh_porous_tri) - ] + domains = [ + FEDomain(m_solid, cvu_quad, sdh_solid_quad), + FEDomain(m_solid, cvu_tri, sdh_solid_tri), + FEDomain(m_porous, (cvu_quad, cvp_quad), sdh_porous_quad), + FEDomain(m_porous, (cvu_tri, cvp_tri), sdh_porous_tri), + ] ## Boundary conditions ## Sliding for u, except top which is compressed ## Sealed for p, except top with prescribed zero pressure - addfacetset!(dh.grid, "sides", x -> x[1] < 1e-6 || x[1] ≈ 5.0) - addfacetset!(dh.grid, "top", x -> x[2]≈10.0) - ch = ConstraintHandler(dh); + addfacetset!(dh.grid, "sides", x -> x[1] < 1.0e-6 || x[1] ≈ 5.0) + addfacetset!(dh.grid, "top", x -> x[2] ≈ 10.0) + ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> zero(Vec{1}), [2])) add!(ch, Dirichlet(:u, getfacetset(grid, "sides"), (x, t) -> zero(Vec{1}), [1])) - add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> u_max*clamp(t/t_rise, 0, 1), [2])) + add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> u_max * clamp(t / t_rise, 0, 1), [2])) add!(ch, Dirichlet(:p, getfacetset(grid, "top_p"), (x, t) -> 0.0)) close!(ch) @@ -330,7 +335,7 @@ end; # ### Solving # Given the `DofHandler`, `ConstraintHandler`, and `CellValues`, # we can solve the problem by stepping through the time history -function solve(dh, ch, domains; Δt=0.025, t_total=1.0) +function solve(dh, ch, domains; Δt = 0.025, t_total = 1.0) K = allocate_matrix(dh) r = zeros(ndofs(dh)) a = zeros(ndofs(dh)) @@ -338,12 +343,12 @@ function solve(dh, ch, domains; Δt=0.025, t_total=1.0) pvd = paraview_collection("porous_media") step = 0 for t in 0:Δt:t_total - if t>0 + if t > 0 update!(ch, t) apply!(a, ch) doassemble!(K, r, domains, a, a_old, Δt) apply_zero!(K, r, ch) - Δa = -K\r + Δa = -K \ r apply_zero!(Δa, ch) a .+= Δa copyto!(a_old, a) @@ -354,7 +359,8 @@ function solve(dh, ch, domains; Δt=0.025, t_total=1.0) pvd[t] = vtk end end - vtk_save(pvd); + vtk_save(pvd) + return end; # Finally we call the functions to actually run the code diff --git a/docs/src/literate-tutorials/reactive_surface.jl b/docs/src/literate-tutorials/reactive_surface.jl index e0900e1b81..723c50a756 100644 --- a/docs/src/literate-tutorials/reactive_surface.jl +++ b/docs/src/literate-tutorials/reactive_surface.jl @@ -93,8 +93,8 @@ function assemble_element_mass!(Me::Matrix, cellvalues::CellValues) n_basefuncs = getnbasefunctions(cellvalues) ## The mass matrices between the reactions are not coupled, so we get a blocked-strided matrix. num_reactants = 2 - r₁range = 1:num_reactants:num_reactants*n_basefuncs - r₂range = 2:num_reactants:num_reactants*n_basefuncs + r₁range = 1:num_reactants:(num_reactants * n_basefuncs) + r₂range = 2:num_reactants:(num_reactants * n_basefuncs) Me₁ = @view Me[r₁range, r₁range] Me₂ = @view Me[r₂range, r₂range] ## Reset to 0 @@ -110,8 +110,8 @@ function assemble_element_mass!(Me::Matrix, cellvalues::CellValues) for j in 1:n_basefuncs δuⱼ = shape_value(cellvalues, q_point, j) ## Add contribution to Ke - Me₁[i,j] += (δuᵢ * δuⱼ) * dΩ - Me₂[i,j] += (δuᵢ * δuⱼ) * dΩ + Me₁[i, j] += (δuᵢ * δuⱼ) * dΩ + Me₂[i, j] += (δuᵢ * δuⱼ) * dΩ end end end @@ -124,8 +124,8 @@ function assemble_element_diffusion!(De::Matrix, cellvalues::CellValues, materia D₂ = material.D₂ ## The diffusion between the reactions is not coupled, so we get a blocked-strided matrix. num_reactants = 2 - r₁range = 1:num_reactants:num_reactants*n_basefuncs - r₂range = 2:num_reactants:num_reactants*n_basefuncs + r₁range = 1:num_reactants:(num_reactants * n_basefuncs) + r₂range = 2:num_reactants:(num_reactants * n_basefuncs) De₁ = @view De[r₁range, r₁range] De₂ = @view De[r₂range, r₂range] ## Reset to 0 @@ -141,8 +141,8 @@ function assemble_element_diffusion!(De::Matrix, cellvalues::CellValues, materia for j in 1:n_basefuncs ∇δuⱼ = shape_gradient(cellvalues, q_point, j) ## Add contribution to Ke - De₁[i,j] += D₁ * (∇δuᵢ ⋅ ∇δuⱼ) * dΩ - De₂[i,j] += D₂ * (∇δuᵢ ⋅ ∇δuⱼ) * dΩ + De₁[i, j] += D₁ * (∇δuᵢ ⋅ ∇δuⱼ) * dΩ + De₂[i, j] += D₂ * (∇δuᵢ ⋅ ∇δuⱼ) * dΩ end end end @@ -153,8 +153,8 @@ function assemble_matrices!(M::SparseMatrixCSC, D::SparseMatrixCSC, cellvalues:: n_basefuncs = getnbasefunctions(cellvalues) ## Allocate the element stiffness matrix and element force vector - Me = zeros(2*n_basefuncs, 2*n_basefuncs) - De = zeros(2*n_basefuncs, 2*n_basefuncs) + Me = zeros(2 * n_basefuncs, 2 * n_basefuncs) + De = zeros(2 * n_basefuncs, 2 * n_basefuncs) ## Create an assembler M_assembler = start_assemble(M) @@ -202,7 +202,8 @@ function setup_initial_conditions!(u₀::Vector, cellvalues::CellValues, dh::Dof end end - u₀ .+= 0.01*rand(ndofs(dh)) + u₀ .+= 0.01 * rand(ndofs(dh)) + return end; # ### Mesh generation @@ -211,7 +212,7 @@ function create_embedded_sphere(refinements) gmsh.initialize() ## Add a unit sphere in 3D space - gmsh.model.occ.addSphere(0.0,0.0,0.0,1.0) + gmsh.model.occ.addSphere(0.0, 0.0, 0.0, 1.0) gmsh.model.occ.synchronize() ## Generate nodes and surface elements only, hence we need to pass 2 into generate @@ -227,7 +228,7 @@ function create_embedded_sphere(refinements) nodes = tonodes() elements, _ = toelements(2) gmsh.finalize() - grid = Grid(elements, nodes); + return Grid(elements, nodes) end # ### Simulation routines @@ -243,7 +244,7 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r ## elements are embedded into, which is in this example 3. ip = Lagrange{RefTriangle, 1}() qr = QuadratureRule{RefTriangle}(2) - cellvalues = CellValues(qr, ip, ip^3); + cellvalues = CellValues(qr, ip, ip^3) ## We have two options to add the reactants to the dof handler, which will give us slightly ## different resulting dof distributions: @@ -256,22 +257,22 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r ## we can create simply reshape the solution vector u to a matrix where the inner index ## corresponds to the index of the reactant. Note that we will still use the scalar ## interpolation for the assembly procedure. - dh = DofHandler(grid); - add!(dh, :reactants, ip^2); - close!(dh); + dh = DofHandler(grid) + add!(dh, :reactants, ip^2) + close!(dh) ## We can save some memory by telling the sparsity pattern that the matrices are not coupled. - M = allocate_matrix(dh; coupling=[true false; false true]) - D = allocate_matrix(dh; coupling=[true false; false true]) + M = allocate_matrix(dh; coupling = [true false; false true]) + D = allocate_matrix(dh; coupling = [true false; false true]) ## Since the heat problem is linear and has no time dependent parameters, we precompute the ## decomposition of the system matrix to speed up the linear system solver. - assemble_matrices!(M, D, cellvalues, dh, material); + assemble_matrices!(M, D, cellvalues, dh, material) A = M + Δt .* D cholA = cholesky(A) ## Now we setup buffers for the time dependent solution and fill the initial condition. - uₜ = zeros(ndofs(dh)) + uₜ = zeros(ndofs(dh)) uₜ₋₁ = ones(ndofs(dh)) setup_initial_conditions!(uₜ₋₁, cellvalues, dh) @@ -285,7 +286,7 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r ## This is now the main solve loop. F = material.F k = material.k - for (iₜ, t) ∈ enumerate(Δt:Δt:T) + for (iₜ, t) in enumerate(Δt:Δt:T) ## First we solve the heat problem uₜ .= cholA \ (M * uₜ₋₁) @@ -293,11 +294,11 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r ## the heat problem as initial guess. 2 is the number of reactants. num_individual_reaction_dofs = ndofs(dh) ÷ 2 rvₜ = reshape(uₜ, (2, num_individual_reaction_dofs)) - for i ∈ 1:num_individual_reaction_dofs + for i in 1:num_individual_reaction_dofs r₁ = rvₜ[1, i] r₂ = rvₜ[2, i] - rvₜ[1, i] += Δt*( -r₁*r₂^2 + F *(1 - r₁) ) - rvₜ[2, i] += Δt*( r₁*r₂^2 - r₂*(F + k ) ) + rvₜ[1, i] += Δt * (-r₁ * r₂^2 + F * (1 - r₁)) + rvₜ[2, i] += Δt * (r₁ * r₂^2 - r₂ * (F + k)) end ## The solution is then stored every 10th step to vtk files for @@ -312,16 +313,16 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r ## Finally we totate the solution to initialize the next timestep. uₜ₋₁ .= uₜ end - - vtk_save(pvd); + vtk_save(pvd) + return end ## This parametrization gives the spot pattern shown in the gif above. material = GrayScottMaterial(0.00016, 0.00008, 0.06, 0.062) if !IS_CI #src -gray_scott_on_sphere(material, 10.0, 32000.0, 3) + gray_scott_on_sphere(material, 10.0, 32000.0, 3) else #src -gray_scott_on_sphere(material, 10.0, 20.0, 0) #src + gray_scott_on_sphere(material, 10.0, 20.0, 0) #src end #src nothing #src diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index 6b74a671bf..b88baa3a4a 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -156,7 +156,7 @@ using Test #src # the "inlet" and "outlet" parts using `gmsh.model.set_periodic`. This is necessary to later # on apply a periodicity constraint for the approximated velocity field. -function setup_grid(h=0.05) +function setup_grid(h = 0.05) ## Initialize gmsh Gmsh.initialize() gmsh.option.set_number("General.Verbosity", 2) @@ -321,7 +321,7 @@ function setup_mean_constraint(dh, fvp) V ./= V[constrained_dof_idx] mean_value_constraint = AffineConstraint( constrained_dof, - Pair{Int,Float64}[J[i] => -V[i] for i in 1:length(J) if J[i] != constrained_dof], + Pair{Int, Float64}[J[i] => -V[i] for i in 1:length(J) if J[i] != constrained_dof], 0.0, ) return mean_value_constraint @@ -385,8 +385,8 @@ function assemble_system!(K, f, dh, cvu, cvp) ndofs_u = length(range_u) range_p = dof_range(dh, :p) ndofs_p = length(range_p) - ϕᵤ = Vector{Vec{2,Float64}}(undef, ndofs_u) - ∇ϕᵤ = Vector{Tensor{2,2,Float64,4}}(undef, ndofs_u) + ϕᵤ = Vector{Vec{2, Float64}}(undef, ndofs_u) + ∇ϕᵤ = Vector{Tensor{2, 2, Float64, 4}}(undef, ndofs_u) divϕᵤ = Vector{Float64}(undef, ndofs_u) ϕₚ = Vector{Float64}(undef, ndofs_p) for cell in CellIterator(dh) @@ -406,15 +406,15 @@ function assemble_system!(K, f, dh, cvu, cvp) end ## u-u for (i, I) in pairs(range_u), (j, J) in pairs(range_u) - ke[I, J] += ( ∇ϕᵤ[i] ⊡ ∇ϕᵤ[j] ) * dΩ + ke[I, J] += (∇ϕᵤ[i] ⊡ ∇ϕᵤ[j]) * dΩ end ## u-p for (i, I) in pairs(range_u), (j, J) in pairs(range_p) - ke[I, J] += ( -divϕᵤ[i] * ϕₚ[j] ) * dΩ + ke[I, J] += (-divϕᵤ[i] * ϕₚ[j]) * dΩ end ## p-u for (i, I) in pairs(range_p), (j, J) in pairs(range_u) - ke[I, J] += ( -divϕᵤ[j] * ϕₚ[i] ) * dΩ + ke[I, J] += (-divϕᵤ[j] * ϕₚ[i]) * dΩ end ## rhs for (i, I) in pairs(range_u) @@ -444,7 +444,7 @@ function check_mean_constraint(dh, fvp, u) #src range_p = dof_range(dh, :p) #src cc = CellCache(dh) #src ## Loop over all the boundaries and compute the integrated pressure #src - ∫pdΓ, Γ= 0.0, 0.0 #src + ∫pdΓ, Γ = 0.0, 0.0 #src for (ci, fi) in set #src reinit!(cc, ci) #src reinit!(fvp, cc, fi) #src @@ -452,10 +452,11 @@ function check_mean_constraint(dh, fvp, u) #src for qp in 1:getnquadpoints(fvp) #src dΓ = getdetJdV(fvp, qp) #src ∫pdΓ += function_value(fvp, qp, ue, range_p) * dΓ #src - Γ += dΓ #src + Γ += dΓ #src end #src end #src - @test ∫pdΓ / Γ ≈ 0.0 atol=1e-16 #src + @test ∫pdΓ / Γ ≈ 0.0 atol = 1.0e-16 #src + return #src end #src function check_L2(dh, cvu, cvp, u) #src @@ -473,11 +474,12 @@ function check_L2(dh, cvu, cvp, u) #src ph = function_value(cvp, qp, ue, range_p) #src ∫uudΩ += (uh ⋅ uh) * dΩ #src ∫ppdΩ += (ph * ph) * dΩ #src - Ω += dΩ #src + Ω += dΩ #src end #src end #src - @test √(∫uudΩ) / Ω ≈ 0.0007255988117907926 atol=1e-7 #src - @test √(∫ppdΩ) / Ω ≈ 0.02169683180923709 atol=1e-5 #src + @test √(∫uudΩ) / Ω ≈ 0.0007255988117907926 atol = 1.0e-7 #src + @test √(∫ppdΩ) / Ω ≈ 0.02169683180923709 atol = 1.0e-5 #src + return #src end #src function main() @@ -485,18 +487,18 @@ function main() h = 0.05 # approximate element size grid = setup_grid(h) ## Interpolations - ipu = Lagrange{RefTriangle,2}() ^ 2 # quadratic - ipp = Lagrange{RefTriangle,1}() # linear + ipu = Lagrange{RefTriangle, 2}()^2 # quadratic + ipp = Lagrange{RefTriangle, 1}() # linear ## Dofs dh = setup_dofs(grid, ipu, ipp) ## FE values - ipg = Lagrange{RefTriangle,1}() # linear geometric interpolation + ipg = Lagrange{RefTriangle, 1}() # linear geometric interpolation cvu, cvp, fvp = setup_fevalues(ipu, ipp, ipg) ## Boundary conditions ch = setup_constraints(dh, fvp) ## Global tangent matrix and rhs coupling = [true true; true false] # no coupling between pressure test/trial functions - K = allocate_matrix(dh, ch; coupling=coupling) + K = allocate_matrix(dh, ch; coupling = coupling) f = zeros(ndofs(dh)) ## Assemble system assemble_system!(K, f, dh, cvu, cvp) diff --git a/docs/src/literate-tutorials/transient_heat_equation.jl b/docs/src/literate-tutorials/transient_heat_equation.jl index 5895978adc..03c48aa696 100644 --- a/docs/src/literate-tutorials/transient_heat_equation.jl +++ b/docs/src/literate-tutorials/transient_heat_equation.jl @@ -132,7 +132,7 @@ function doassemble_K!(K::SparseMatrixCSC, f::Vector, cellvalues::CellValues, dh fe[i] += 0.1 * v * dΩ for j in 1:n_basefuncs ∇u = shape_gradient(cellvalues, q_point, j) - Ke[i, j] += 1e-3 * (∇v ⋅ ∇u) * dΩ + Ke[i, j] += 1.0e-3 * (∇v ⋅ ∇u) * dΩ end end end diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index b3b107274d..1395e6aadf 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -7,17 +7,24 @@ using Ferrite, Test # * The cell shape has the same dimension as the physical space (excludes so-called embedded cells). struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues - N::Matrix{T} # Precalculated shape values, N[i, q_point] where i is the - ## shape function number and q_point the integration point - dNdξ::Matrix{Vec{dim,T}} # Precalculated shape gradients in the reference domain, dNdξ[i, q_point] - dNdx::Matrix{Vec{dim,T}} # Cache for shape gradients in the physical domain, dNdx[i, q_point] - M::Matrix{T} # Precalculated geometric shape values, M[j, q_point] where j is the - ## geometric shape function number - dMdξ::Matrix{Vec{dim,T}} # Precalculated geometric shape gradients, dMdξ[j, q_point] - weights::Vector{T} # Given quadrature weights in the reference domain, weights[q_point] - detJdV::Vector{T} # Cache for quadrature weights in the physical domain, detJdV[q_point], i.e. - ## det(J)*weight[q_point], where J is the jacobian of the geometric mapping - ## at the quadrature point, q_point. + ## Precalculated shape values, N[i, q_point] where i is the + ## shape function number and q_point the integration point + N::Matrix{T} + ## Precalculated shape gradients in the reference domain, dNdξ[i, q_point] + dNdξ::Matrix{Vec{dim, T}} + ## Cache for shape gradients in the physical domain, dNdx[i, q_point] + dNdx::Matrix{Vec{dim, T}} + ## Precalculated geometric shape values, M[j, q_point] where j is the + ## geometric shape function number + M::Matrix{T} + ## Precalculated geometric shape gradients, dMdξ[j, q_point] + dMdξ::Matrix{Vec{dim, T}} + ## Given quadrature weights in the reference domain, weights[q_point] + weights::Vector{T} + ## Cache for quadrature weights in the physical domain, detJdV[q_point], i.e. + ## det(J)*weight[q_point], where J is the jacobian of the geometric mapping + ## at the quadrature point, q_point. + detJdV::Vector{T} end; # Next, we create a constructor with the same input as `CellValues` @@ -30,14 +37,14 @@ function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Int ## Function interpolation n_func_basefuncs = getnbasefunctions(ip_fun) - N = zeros(T, n_func_basefuncs, n_qpoints) - dNdx = zeros(Vec{dim,T}, n_func_basefuncs, n_qpoints) - dNdξ = zeros(Vec{dim,T}, n_func_basefuncs, n_qpoints) + N = zeros(T, n_func_basefuncs, n_qpoints) + dNdx = zeros(Vec{dim, T}, n_func_basefuncs, n_qpoints) + dNdξ = zeros(Vec{dim, T}, n_func_basefuncs, n_qpoints) ## Geometry interpolation n_geom_basefuncs = getnbasefunctions(ip_geo) - M = zeros(T, n_geom_basefuncs, n_qpoints) - dMdξ = zeros(Vec{dim,T}, n_geom_basefuncs, n_qpoints) + M = zeros(T, n_geom_basefuncs, n_qpoints) + dMdξ = zeros(Vec{dim, T}, n_geom_basefuncs, n_qpoints) ## Precalculate function and geometric shape values and gradients for (qp, ξ) in pairs(Ferrite.getpoints(qr)) @@ -50,7 +57,7 @@ function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Int end detJdV = zeros(T, n_qpoints) - SimpleCellValues(N, dNdξ, dNdx, M, dMdξ, weights, detJdV) + return SimpleCellValues(N, dNdξ, dNdx, M, dMdξ, weights, detJdV) end; # To make our `SimpleCellValues` work in standard Ferrite code, @@ -63,31 +70,32 @@ Ferrite.shape_gradient(cv::SimpleCellValues, q_point::Int, i::Int) = cv.dNdx[i, # The last step is then to dispatch `reinit!` for our `SimpleCellValues` to calculate # the cached values `dNdx` and `detJdV` for the current cell according to the # theory for `IdentityMapping` above. -function Ferrite.reinit!(cv::SimpleCellValues, x::Vector{Vec{dim,T}}) where {dim,T} +function Ferrite.reinit!(cv::SimpleCellValues, x::Vector{Vec{dim, T}}) where {dim, T} for (q_point, w) in pairs(cv.weights) # Loop over each quadrature point ## Calculate the jacobian, J - J = zero(Tensor{2,dim,T}) + J = zero(Tensor{2, dim, T}) for i in eachindex(x) J += x[i] ⊗ cv.dMdξ[i, q_point] end ## Calculate the correct integration weight for the current q_point - cv.detJdV[q_point] = det(J)*w + cv.detJdV[q_point] = det(J) * w ## map the shape gradients to the current geometry Jinv = inv(J) for i in 1:getnbasefunctions(cv) cv.dNdx[i, q_point] = cv.dNdξ[i, q_point] ⋅ Jinv end end + return end; # To test our implementation, we create instances of our `SimpleCellValues` and the standard `CellValues`: qr = QuadratureRule{RefQuadrilateral}(2) -ip = Lagrange{RefQuadrilateral,1}() +ip = Lagrange{RefQuadrilateral, 1}() simple_cv = SimpleCellValues(qr, ip, ip) cv = CellValues(qr, ip, ip); # The first thing to try is to reinitialize the cell values to a given cell, in this case cell nr. 2 -grid = generate_grid(Quadrilateral, (2,2)) +grid = generate_grid(Quadrilateral, (2, 2)) x = getcoordinates(grid, 2) reinit!(simple_cv, x) reinit!(cv, x); diff --git a/ext/FerriteBlockArrays.jl b/ext/FerriteBlockArrays.jl index 8f4b73fbde..c708f72958 100644 --- a/ext/FerriteBlockArrays.jl +++ b/ext/FerriteBlockArrays.jl @@ -70,7 +70,7 @@ end Ferrite.matrix_handle(ba::BlockAssembler) = ba.K Ferrite.vector_handle(ba::BlockAssembler) = ba.f -function Ferrite.start_assemble(K::BlockMatrix, f; fillzero::Bool=true) +function Ferrite.start_assemble(K::BlockMatrix, f; fillzero::Bool = true) fillzero && (fillzero!(K); fillzero!(f)) return BlockAssembler(K, f, BlockIndex{1}[]) end @@ -122,7 +122,7 @@ end function Ferrite.apply!(::BlockMatrix, ::AbstractVector, ::ConstraintHandler) error( "Condensation of constraints with `apply!` after assembling not supported yet " * - "for BlockMatrix, use local condensation with `apply_assemble!` instead." + "for BlockMatrix, use local condensation with `apply_assemble!` instead." ) end @@ -131,7 +131,7 @@ end ## Overloaded assembly pieces from src/arrayutils.jl ## ####################################################### -function Ferrite.addindex!(B::BlockMatrix{Tv}, v::Tv, i::Int, j::Int) where Tv +function Ferrite.addindex!(B::BlockMatrix{Tv}, v::Tv, i::Int, j::Int) where {Tv} @boundscheck checkbounds(B, i, j) Bi, li = splindex(findblockindex(axes(B, 1), i)) Bj, lj = splindex(findblockindex(axes(B, 2), j)) @@ -140,7 +140,7 @@ function Ferrite.addindex!(B::BlockMatrix{Tv}, v::Tv, i::Int, j::Int) where Tv return B end -function Ferrite.fillzero!(B::Union{BlockVector,BlockMatrix}) +function Ferrite.fillzero!(B::Union{BlockVector, BlockMatrix}) for blk in blocks(B) fillzero!(blk) end diff --git a/ext/FerriteMetis.jl b/ext/FerriteMetis.jl index a8a5bbb1e7..71697adff2 100644 --- a/ext/FerriteMetis.jl +++ b/ext/FerriteMetis.jl @@ -8,7 +8,7 @@ using Metis.LibMetis: idx_t using Metis: Metis struct MetisOrder <: DofOrder.Ext{Metis} - coupling::Union{Matrix{Bool},Nothing} + coupling::Union{Matrix{Bool}, Nothing} end """ @@ -21,16 +21,16 @@ DoFs the field/component coupling can be provided; see [`allocate_matrix`](@ref) details. """ function DofOrder.Ext{Metis}(; - coupling::Union{AbstractMatrix{Bool},Nothing}=nothing, -) + coupling::Union{AbstractMatrix{Bool}, Nothing} = nothing, + ) return MetisOrder(coupling) end function Ferrite.compute_renumber_permutation( - dh::DofHandler, - ch::Union{ConstraintHandler,Nothing}, - order::DofOrder.Ext{Metis} -) + dh::DofHandler, + ch::Union{ConstraintHandler, Nothing}, + order::DofOrder.Ext{Metis} + ) # Expand the coupling matrix to size ndofs_per_cell × ndofs_per_cell coupling = order.coupling diff --git a/src/CollectionsOfViews.jl b/src/CollectionsOfViews.jl index a85691af06..bafe5c528d 100644 --- a/src/CollectionsOfViews.jl +++ b/src/CollectionsOfViews.jl @@ -43,7 +43,7 @@ function push_at_index!(b::ConstructionBuffer, val, indices::Vararg{Int, N}) whe if r.start == 0 # `indices...` not previously added, allocate new space for it at the end of `b.data` resize!(b.data, n + b.sizehint) - b.data[n+1] = val + b.data[n + 1] = val setindex!(b.indices, AdaptiveRange(n + 1, 1, b.sizehint), indices...) elseif r.ncurrent == r.nmax # We have used up our space, move data associated with `indices...` to the end of `b.data` @@ -73,13 +73,13 @@ end Base.size(cv::ArrayOfVectorViews) = size(cv.lin_idx) @inline function Base.getindex(cv::ArrayOfVectorViews, linear_index::Int) @boundscheck checkbounds(cv.lin_idx, linear_index) - return @inbounds view(cv.data, cv.indices[linear_index]:(cv.indices[linear_index+1]-1)) + return @inbounds view(cv.data, cv.indices[linear_index]:(cv.indices[linear_index + 1] - 1)) end @inline function Base.getindex(cv::ArrayOfVectorViews, idx...) linear_index = getindex(cv.lin_idx, idx...) return @inbounds getindex(cv, linear_index) end -Base.IndexStyle(::Type{<:ArrayOfVectorViews{<:Any, N}}) where N = Base.IndexStyle(Array{Int, N}) +Base.IndexStyle(::Type{<:ArrayOfVectorViews{<:Any, N}}) where {N} = Base.IndexStyle(Array{Int, N}) # Constructors """ @@ -114,10 +114,10 @@ end Creates the `ArrayOfVectorViews` directly from the `ConstructionBuffer` that was manually created and filled. """ -function ArrayOfVectorViews(b::ConstructionBuffer{T}) where T +function ArrayOfVectorViews(b::ConstructionBuffer{T}) where {T} indices = Vector{Int}(undef, length(b.indices) + 1) lin_idx = LinearIndices(b.indices) - data_length = sum(ar.ncurrent for ar in b.indices; init=0) + data_length = sum(ar.ncurrent for ar in b.indices; init = 0) data = Vector{T}(undef, data_length) data_index = 1 for (idx, ar) in pairs(b.indices) diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index 89a4e1a55f..a48cb3940f 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -40,13 +40,13 @@ which applies the condition via [`apply!`](@ref) and/or [`apply_zero!`](@ref). """ struct Dirichlet # <: Constraint f::Function # f(x) or f(x,t) -> value(s) - facets::OrderedSet{T} where T <: Union{Int, FacetIndex, FaceIndex, EdgeIndex, VertexIndex} + facets::OrderedSet{T} where {T <: Union{Int, FacetIndex, FaceIndex, EdgeIndex, VertexIndex}} field_name::Symbol components::Vector{Int} # components of the field local_facet_dofs::Vector{Int} local_facet_dofs_offset::Vector{Int} end -function Dirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function, components=nothing) +function Dirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function, components = nothing) return Dirichlet(f, convert_to_orderedset(facets), field_name, __to_components(components), Int[], Int[]) end @@ -61,7 +61,7 @@ function __to_components(c) return components end -const DofCoefficients{T} = Vector{Pair{Int,T}} +const DofCoefficients{T} = Vector{Pair{Int, T}} """ AffineConstraint(constrained_dof::Int, entries::Vector{Pair{Int,T}}, b::T) where T @@ -81,18 +81,18 @@ end A collection of constraints associated with the dof handler `dh`. `T` is the numeric type for stored values. """ -mutable struct ConstraintHandler{DH<:AbstractDofHandler,T} +mutable struct ConstraintHandler{DH <: AbstractDofHandler, T} const dbcs::Vector{Dirichlet} const prescribed_dofs::Vector{Int} const free_dofs::Vector{Int} const inhomogeneities::Vector{T} # Store the original constant inhomogeneities for affine constraints used to compute # "effective" inhomogeneities in `update!` and then stored in .inhomogeneities. - const affine_inhomogeneities::Vector{Union{Nothing,T}} + const affine_inhomogeneities::Vector{Union{Nothing, T}} # `nothing` for pure DBC constraint, otherwise affine constraint const dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}} # global dof -> index into dofs and inhomogeneities and dofcoefficients - const dofmapping::Dict{Int,Int} + const dofmapping::Dict{Int, Int} const bcvalues::Vector{BCValues{T}} const dh::DH closed::Bool @@ -100,11 +100,11 @@ end ConstraintHandler(dh::AbstractDofHandler) = ConstraintHandler(Float64, dh) -function ConstraintHandler(::Type{T}, dh::AbstractDofHandler) where T <: Number +function ConstraintHandler(::Type{T}, dh::AbstractDofHandler) where {T <: Number} @assert isclosed(dh) - ConstraintHandler( - Dirichlet[], Int[], Int[], T[], Union{Nothing,T}[], Union{Nothing,DofCoefficients{T}}[], - Dict{Int,Int}(), BCValues{T}[], dh, false, + return ConstraintHandler( + Dirichlet[], Int[], Int[], T[], Union{Nothing, T}[], Union{Nothing, DofCoefficients{T}}[], + Dict{Int, Int}(), BCValues{T}[], dh, false, ) end @@ -140,7 +140,7 @@ Applies the boundary condition to the right-hand-side vector without modifying t See also: [`get_rhs_data`](@ref). """ -function apply_rhs!(data::RHSData, f::AbstractVector, ch::ConstraintHandler, applyzero::Bool=false) +function apply_rhs!(data::RHSData, f::AbstractVector, ch::ConstraintHandler, applyzero::Bool = false) K = data.constrained_columns @assert length(f) == size(K, 1) @boundscheck checkbounds(f, ch.prescribed_dofs) @@ -166,6 +166,7 @@ function apply_rhs!(data::RHSData, f::AbstractVector, ch::ConstraintHandler, app bz = applyzero ? zero(eltype(f)) : b f[pdof] = bz * m end + return end function Base.show(io::IO, ::MIME"text/plain", ch::ConstraintHandler) @@ -178,6 +179,7 @@ function Base.show(io::IO, ::MIME"text/plain", ch::ConstraintHandler) print(io, "\n ", "Field: ", dbc.field_name, ", ", "Components: ", dbc.components) end end + return end isclosed(ch::ConstraintHandler) = ch.closed @@ -208,7 +210,7 @@ Close and finalize the `ConstraintHandler`. """ function close!(ch::ConstraintHandler) @assert(!isclosed(ch)) - @assert( allunique(ch.prescribed_dofs) ) + @assert(allunique(ch.prescribed_dofs)) I = sortperm(ch.prescribed_dofs) ch.prescribed_dofs .= ch.prescribed_dofs[I] @@ -270,6 +272,7 @@ function add!(ch::ConstraintHandler, ac::AffineConstraint) # that this constraint is an AffineConstraint which is currently needed in update! # in order to not update inhomogeneities for affine constraints add_prescribed_dof!(ch, ac.constrained_dof, ac.b, #=isempty(ac.entries) ? nothing : =# ac.entries) + return ch end """ @@ -279,7 +282,7 @@ Add a constrained dof directly to the `ConstraintHandler`. This function checks if the `constrained_dof` is already constrained, and overrides the old constraint if true. """ -function add_prescribed_dof!(ch::ConstraintHandler, constrained_dof::Int, inhomogeneity, dofcoefficients=nothing) +function add_prescribed_dof!(ch::ConstraintHandler, constrained_dof::Int, inhomogeneity, dofcoefficients = nothing) @assert(!isclosed(ch)) i = get(ch.dofmapping, constrained_dof, 0) if i != 0 @@ -300,7 +303,7 @@ function add_prescribed_dof!(ch::ConstraintHandler, constrained_dof::Int, inhomo end # Dirichlet on (facet|face|edge|vertex)set -function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcfacets::AbstractVecOrSet{Index}, interpolation::Interpolation, field_dim::Int, offset::Int, bcvalue::BCValues, _) where {Index<:BoundaryIndex} +function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcfacets::AbstractVecOrSet{Index}, interpolation::Interpolation, field_dim::Int, offset::Int, bcvalue::BCValues, _) where {Index <: BoundaryIndex} local_facet_dofs, local_facet_dofs_offset = _local_facet_dofs_for_bc(interpolation, field_dim, dbc.components, offset, dirichlet_boundarydof_indices(eltype(bcfacets))) copy!(dbc.local_facet_dofs, local_facet_dofs) @@ -308,10 +311,10 @@ function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcfacets::AbstractVecOrSet # loop over all the faces in the set and add the global dofs to `constrained_dofs` constrained_dofs = Int[] - cc = CellCache(ch.dh, UpdateFlags(; nodes=false, coords=false, dofs=true)) + cc = CellCache(ch.dh, UpdateFlags(; nodes = false, coords = false, dofs = true)) for (cellidx, facetidx) in bcfacets reinit!(cc, cellidx) - r = local_facet_dofs_offset[facetidx]:(local_facet_dofs_offset[facetidx+1]-1) + r = local_facet_dofs_offset[facetidx]:(local_facet_dofs_offset[facetidx + 1] - 1) append!(constrained_dofs, cc.dofs[local_facet_dofs[r]]) # TODO: for-loop over r and simply push! to ch.prescribed_dofs @debug println("adding dofs $(cc.dofs[local_facet_dofs[r]]) to dbc") end @@ -327,14 +330,14 @@ end # Calculate which local dof index live on each facet: # facet `i` have dofs `local_facet_dofs[local_facet_dofs_offset[i]:local_facet_dofs_offset[i+1]-1] -function _local_facet_dofs_for_bc(interpolation, field_dim, components, offset, boundaryfunc::F=dirichlet_facetdof_indices) where F +function _local_facet_dofs_for_bc(interpolation, field_dim, components, offset, boundaryfunc::F = dirichlet_facetdof_indices) where {F} @assert issorted(components) local_facet_dofs = Int[] local_facet_dofs_offset = Int[1] for (_, facet) in enumerate(boundaryfunc(interpolation)) for fdof in facet, d in 1:field_dim if d in components - push!(local_facet_dofs, (fdof-1)*field_dim + d + offset) + push!(local_facet_dofs, (fdof - 1) * field_dim + d + offset) end end push!(local_facet_dofs_offset, length(local_facet_dofs) + 1) @@ -342,7 +345,7 @@ function _local_facet_dofs_for_bc(interpolation, field_dim, components, offset, return local_facet_dofs, local_facet_dofs_offset end -function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcnodes::AbstractVecOrSet{Int}, interpolation::Interpolation, field_dim::Int, offset::Int, bcvalue::BCValues, cellset::AbstractVecOrSet{Int}=OrderedSet{Int}(1:getncells(get_grid(ch.dh)))) +function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcnodes::AbstractVecOrSet{Int}, interpolation::Interpolation, field_dim::Int, offset::Int, bcvalue::BCValues, cellset::AbstractVecOrSet{Int} = OrderedSet{Int}(1:getncells(get_grid(ch.dh)))) grid = get_grid(ch.dh) if interpolation !== geometric_interpolation(getcelltype(grid, first(cellset))) @warn("adding constraint to nodeset is not recommended for sub/super-parametric approximations.") @@ -357,9 +360,9 @@ function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcnodes::AbstractVecOrSet{ for idx in 1:min(interpol_points, length(cell.nodes)) node = cell.nodes[idx] if !visited[node] - noderange = (offset + (idx-1)*field_dim + 1):(offset + idx*field_dim) # the dofs in this node - for (i,c) in enumerate(dbc.components) - node_dofs[i,node] = cell.dofs[noderange[c]] + noderange = (offset + (idx - 1) * field_dim + 1):(offset + idx * field_dim) # the dofs in this node + for (i, c) in enumerate(dbc.components) + node_dofs[i, node] = cell.dofs[noderange[c]] @debug println("adding dof $(cell.dofs[noderange[c]]) to node_dofs") end visited[node] = true @@ -368,7 +371,7 @@ function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcnodes::AbstractVecOrSet{ end constrained_dofs = Int[] - sizehint!(constrained_dofs, ncomps*length(bcnodes)) + sizehint!(constrained_dofs, ncomps * length(bcnodes)) sizehint!(dbc.local_facet_dofs, length(bcnodes)) for node in bcnodes if !visited[node] @@ -376,7 +379,7 @@ function _add!(ch::ConstraintHandler, dbc::Dirichlet, bcnodes::AbstractVecOrSet{ continue end for i in 1:ncomps - push!(constrained_dofs, node_dofs[i,node]) + push!(constrained_dofs, node_dofs[i, node]) end push!(dbc.local_facet_dofs, node) # use this field to store the node idx for each node end @@ -400,7 +403,7 @@ compute the inhomogeneities. Note that this is called implicitly in `close!(::ConstraintHandler)`. """ -function update!(ch::ConstraintHandler, time::Real=0.0) +function update!(ch::ConstraintHandler, time::Real = 0.0) @assert ch.closed for (i, dbc) in pairs(ch.dbcs) # If the BC function only accept one argument, i.e. f(x), we create a wrapper @@ -408,8 +411,10 @@ function update!(ch::ConstraintHandler, time::Real=0.0) # the function with two arguments internally. wrapper_f = hasmethod(dbc.f, Tuple{get_coordinate_type(get_grid(ch.dh)), typeof(time)}) ? dbc.f : (x, _) -> dbc.f(x) # Function barrier - _update!(ch.inhomogeneities, wrapper_f, dbc.facets, dbc.field_name, dbc.local_facet_dofs, dbc.local_facet_dofs_offset, - dbc.components, ch.dh, ch.bcvalues[i], ch.dofmapping, ch.dofcoefficients, time) + _update!( + ch.inhomogeneities, wrapper_f, dbc.facets, dbc.field_name, dbc.local_facet_dofs, dbc.local_facet_dofs_offset, + dbc.components, ch.dh, ch.bcvalues[i], ch.dofmapping, ch.dofcoefficients, time + ) end # Compute effective inhomogeneity for affine constraints with prescribed dofs in the # RHS. For example, in u2 = w3 * u3 + w4 * u4 + b2 we allow e.g. u3 to be prescribed by @@ -434,11 +439,13 @@ function update!(ch::ConstraintHandler, time::Real=0.0) end # for facets, vertices, faces and edges -function _update!(inhomogeneities::Vector{T}, f::Function, boundary_entities::AbstractVecOrSet{<:BoundaryIndex}, field::Symbol, local_facet_dofs::Vector{Int}, local_facet_dofs_offset::Vector{Int}, - components::Vector{Int}, dh::AbstractDofHandler, boundaryvalues::BCValues, - dofmapping::Dict{Int,Int}, dofcoefficients::Vector{Union{Nothing,DofCoefficients{T}}}, time::Real) where {T} +function _update!( + inhomogeneities::Vector{T}, f::Function, boundary_entities::AbstractVecOrSet{<:BoundaryIndex}, field::Symbol, local_facet_dofs::Vector{Int}, local_facet_dofs_offset::Vector{Int}, + components::Vector{Int}, dh::AbstractDofHandler, boundaryvalues::BCValues, + dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real + ) where {T} - cc = CellCache(dh, UpdateFlags(; nodes=false, coords=true, dofs=true)) + cc = CellCache(dh, UpdateFlags(; nodes = false, coords = true, dofs = true)) for (cellidx, entityidx) in boundary_entities reinit!(cc, cellidx) @@ -446,7 +453,7 @@ function _update!(inhomogeneities::Vector{T}, f::Function, boundary_entities::Ab boundaryvalues.current_entity = entityidx # local dof-range for this facet - r = local_facet_dofs_offset[entityidx]:(local_facet_dofs_offset[entityidx+1]-1) + r = local_facet_dofs_offset[entityidx]:(local_facet_dofs_offset[entityidx + 1] - 1) counter = 1 for location in 1:getnquadpoints(boundaryvalues) x = spatial_coordinate(boundaryvalues, location, cc.coords) @@ -468,12 +475,15 @@ function _update!(inhomogeneities::Vector{T}, f::Function, boundary_entities::Ab end end end + return end # for nodes -function _update!(inhomogeneities::Vector{T}, f::Function, ::AbstractVecOrSet{Int}, field::Symbol, nodeidxs::Vector{Int}, globaldofs::Vector{Int}, - components::Vector{Int}, dh::AbstractDofHandler, facetvalues::BCValues, - dofmapping::Dict{Int,Int}, dofcoefficients::Vector{Union{Nothing,DofCoefficients{T}}}, time::Real) where T +function _update!( + inhomogeneities::Vector{T}, f::Function, ::AbstractVecOrSet{Int}, field::Symbol, nodeidxs::Vector{Int}, globaldofs::Vector{Int}, + components::Vector{Int}, dh::AbstractDofHandler, facetvalues::BCValues, + dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real + ) where {T} counter = 1 for nodenumber in nodeidxs x = get_node_coordinate(get_grid(dh), nodenumber) @@ -491,6 +501,7 @@ function _update!(inhomogeneities::Vector{T}, f::Function, ::AbstractVecOrSet{In end end end + return end """ @@ -569,7 +580,7 @@ apply_zero!(ΔΔu, ch) # Make sure values are exactly zero apply_zero! apply_zero!(v::AbstractVector, ch::ConstraintHandler) = _apply_v(v, ch, true) -apply!( v::AbstractVector, ch::ConstraintHandler) = _apply_v(v, ch, false) +apply!(v::AbstractVector, ch::ConstraintHandler) = _apply_v(v, ch, false) function _apply_v(v::AbstractVector, ch::ConstraintHandler, apply_zero::Bool) @assert isclosed(ch) @@ -587,15 +598,15 @@ function _apply_v(v::AbstractVector, ch::ConstraintHandler, apply_zero::Bool) return v end -function apply!(K::Union{SparseMatrixCSC,Symmetric}, ch::ConstraintHandler) - apply!(K, eltype(K)[], ch, true) +function apply!(K::Union{SparseMatrixCSC, Symmetric}, ch::ConstraintHandler) + return apply!(K, eltype(K)[], ch, true) end -function apply_zero!(K::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, ch::ConstraintHandler) - apply!(K, f, ch, true) +function apply_zero!(K::Union{SparseMatrixCSC, Symmetric}, f::AbstractVector, ch::ConstraintHandler) + return apply!(K, f, ch, true) end -function apply!(KK::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, ch::ConstraintHandler, applyzero::Bool=false) +function apply!(KK::Union{SparseMatrixCSC, Symmetric}, f::AbstractVector, ch::ConstraintHandler, applyzero::Bool = false) @assert isclosed(ch) sym = isa(KK, Symmetric) K = sym ? KK.data : KK @@ -653,6 +664,7 @@ function apply!(KK::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, ch::Con f[d] = vz * m end end + return end # Fetch dof coefficients for a dof prescribed by an affine constraint. Return nothing if the @@ -664,11 +676,11 @@ end end # Condenses K and f: C'*K*C, C'*f, in-place assuming the sparsity pattern is correct -function _condense!(K::SparseMatrixCSC, f::AbstractVector, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, dofmapping::Dict{Int,Int}, sym::Bool=false) where T +function _condense!(K::SparseMatrixCSC, f::AbstractVector, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, dofmapping::Dict{Int, Int}, sym::Bool = false) where {T} ndofs = size(K, 1) condense_f = !(length(f) == 0) - condense_f && @assert( length(f) == ndofs ) + condense_f && @assert(length(f) == ndofs) # Return early if there are no non-trivial affine constraints any(i -> !(i === nothing || isempty(i)), dofcoefficients) || return @@ -721,6 +733,7 @@ function _condense!(K::SparseMatrixCSC, f::AbstractVector, dofcoefficients::Vect end end end + return end function _add_or_grow(cnt::Int, I::Vector{Int}, J::Vector{Int}, dofi::Int, dofj::Int) @@ -730,6 +743,7 @@ function _add_or_grow(cnt::Int, I::Vector{Int}, J::Vector{Int}, dofi::Int, dofj: end I[cnt] = dofi J[cnt] = dofj + return end """ @@ -742,19 +756,19 @@ The constraint matrix relates constrained, `a_c`, and free, `a_f`, degrees of fr `a_c = C * a_f + g`. The condensed system of linear equations is obtained as `C' * K * C = C' * (f - K * g)`. """ -function create_constraint_matrix(ch::ConstraintHandler{dh,T}) where {dh,T} +function create_constraint_matrix(ch::ConstraintHandler{dh, T}) where {dh, T} @assert(isclosed(ch)) - I = Int[]; J = Int[]; V = T[]; + I = Int[]; J = Int[]; V = T[] g = zeros(T, ndofs(ch.dh)) # inhomogeneities for (j, d) in enumerate(ch.free_dofs) - push!(I, d) - push!(J, j) - push!(V, 1.0) + push!(I, d) + push!(J, j) + push!(V, 1.0) end - for (i,pdof) in enumerate(ch.prescribed_dofs) + for (i, pdof) in enumerate(ch.prescribed_dofs) dofcoef = ch.dofcoefficients[i] if dofcoef !== nothing #if affine constraint for (d, v) in dofcoef @@ -779,6 +793,7 @@ function zero_out_columns!(K, dofs::Vector{Int}) # can be removed in 0.7 with #2 r = nzrange(K, col) K.nzval[r] .= 0.0 end + return end function zero_out_rows!(K, dofmapping) @@ -789,6 +804,7 @@ function zero_out_rows!(K, dofmapping) nzval[i] = 0 end end + return end function meandiag(K::AbstractMatrix) @@ -861,7 +877,7 @@ end function filter_dbc_set(grid::AbstractGrid, fhset::AbstractSet{Int}, dbcset::AbstractSet{Int}) ret = empty(dbcset) nodes_in_fhset = OrderedSet{Int}() - for cc in CellIterator(grid, fhset, UpdateFlags(; nodes=true, coords=false)) + for cc in CellIterator(grid, fhset, UpdateFlags(; nodes = true, coords = false)) union!(nodes_in_fhset, cc.nodes) end for nodeid in dbcset @@ -901,28 +917,28 @@ See the manual section on [Periodic boundary conditions](@ref) for more informat struct PeriodicDirichlet field_name::Symbol components::Vector{Int} # components of the field - facet_pairs::Vector{Pair{String,String}} # legacy that will populate facet_map on add! + facet_pairs::Vector{Pair{String, String}} # legacy that will populate facet_map on add! facet_map::Vector{PeriodicFacetPair} - func::Union{Function,Nothing} - rotation_matrix::Union{Matrix{Float64},Nothing} + func::Union{Function, Nothing} + rotation_matrix::Union{Matrix{Float64}, Nothing} end # Default to no inhomogeneity function/rotation -PeriodicDirichlet(fn::Symbol, fp::Union{Vector{<:Pair},Vector{PeriodicFacetPair}}, c=nothing) = +PeriodicDirichlet(fn::Symbol, fp::Union{Vector{<:Pair}, Vector{PeriodicFacetPair}}, c = nothing) = PeriodicDirichlet(fn, fp, nothing, c) # Basic constructor for the simple case where face_map will be populated in # add!(::ConstraintHandler, ...) instead -function PeriodicDirichlet(fn::Symbol, fp::Vector{<:Pair}, f::Union{Function,Nothing}, c=nothing) +function PeriodicDirichlet(fn::Symbol, fp::Vector{<:Pair}, f::Union{Function, Nothing}, c = nothing) facet_map = PeriodicFacetPair[] # This will be populated in add!(::ConstraintHandler, ...) instead return PeriodicDirichlet(fn, __to_components(c), fp, facet_map, f, nothing) end -function PeriodicDirichlet(fn::Symbol, fm::Vector{PeriodicFacetPair}, f_or_r::Union{AbstractMatrix,Function,Nothing}, c=nothing) +function PeriodicDirichlet(fn::Symbol, fm::Vector{PeriodicFacetPair}, f_or_r::Union{AbstractMatrix, Function, Nothing}, c = nothing) f = f_or_r isa Function ? f_or_r : nothing rotation_matrix = f_or_r isa AbstractMatrix ? f_or_r : nothing components = __to_components(c) - return PeriodicDirichlet(fn, components, Pair{String,String}[], fm, f, rotation_matrix) + return PeriodicDirichlet(fn, components, Pair{String, String}[], fm, f, rotation_matrix) end function add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet) @@ -969,8 +985,10 @@ function add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet) return ch end -function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::Interpolation, - field_dim::Int, offset::Int, is_legacy::Bool, rotation_matrix::Union{Matrix{T},Nothing}, ::Type{dof_map_t}, iterator_f::F) where {T, dof_map_t, F <: Function} +function _add!( + ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::Interpolation, + field_dim::Int, offset::Int, is_legacy::Bool, rotation_matrix::Union{Matrix{T}, Nothing}, ::Type{dof_map_t}, iterator_f::F + ) where {T, dof_map_t, F <: Function} grid = get_grid(ch.dh) facet_map = pdbc.facet_map @@ -982,19 +1000,19 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In rotated_indices = rotate_local_dofs(local_facet_dofs, local_facet_dofs_offset, interpolation, length(pdbc.components)) # Dof map for mirror dof => image dof - dof_map = Dict{dof_map_t,dof_map_t}() + dof_map = Dict{dof_map_t, dof_map_t}() n = ndofs_per_cell(ch.dh, first(facet_map).mirror[1]) mirror_dofs = zeros(Int, n) - image_dofs = zeros(Int, n) + image_dofs = zeros(Int, n) for facet_pair in facet_map m = facet_pair.mirror i = facet_pair.image celldofs!(mirror_dofs, ch.dh, m[1]) - celldofs!( image_dofs, ch.dh, i[1]) + celldofs!(image_dofs, ch.dh, i[1]) - mdof_range = local_facet_dofs_offset[m[2]] : (local_facet_dofs_offset[m[2] + 1] - 1) - idof_range = local_facet_dofs_offset[i[2]] : (local_facet_dofs_offset[i[2] + 1] - 1) + mdof_range = local_facet_dofs_offset[m[2]]:(local_facet_dofs_offset[m[2] + 1] - 1) + idof_range = local_facet_dofs_offset[i[2]]:(local_facet_dofs_offset[i[2] + 1] - 1) for (md, id) in zip(iterator_f(mdof_range), iterator_f(idof_range)) mdof = image_dofs[local_facet_dofs[id]] @@ -1008,7 +1026,7 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In # @info "$cdof => $mdof, but $mdof => $mdof′, remapping $cdof => $mdof′." # TODO: Is this needed now when untangling below? push!(dof_map, cdof => mdof′) - # elseif haskey(dof_map, cdof) && dof_map[cdof] == mdof + # elseif haskey(dof_map, cdof) && dof_map[cdof] == mdof # @info "$cdof => $mdof already in the set, skipping." elseif haskey(dof_map, cdof) # @info "$cdof => $mdof, but $cdof => $(dof_map[cdof]) already, skipping." @@ -1038,11 +1056,13 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In # For legacy code add Dirichlet conditions in the corners if is_legacy - Base.depwarn("It looks like you are using legacy code for PeriodicDirichlet " * - "meaning that the solution is automatically locked in the \"corners\"." * - "This will not be done automatically in the future. Instead add a " * - "Dirichlet boundary condition on the relevant nodeset.", - :PeriodicDirichlet) + Base.depwarn( + "It looks like you are using legacy code for PeriodicDirichlet " * + "meaning that the solution is automatically locked in the \"corners\"." * + "This will not be done automatically in the future. Instead add a " * + "Dirichlet boundary condition on the relevant nodeset.", + :PeriodicDirichlet + ) all_node_idxs = Set{Int}() Tx = get_coordinate_type(grid) min_x = Tx(i -> typemax(eltype(Tx))) @@ -1063,7 +1083,8 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In idxs, _ = NearestNeighbors.nn(tree, points) corner_set = OrderedSet{Int}(all_node_idxs_v[i] for i in idxs) - dbc = Dirichlet(pdbc.field_name, corner_set, + dbc = Dirichlet( + pdbc.field_name, corner_set, pdbc.func === nothing ? (x, _) -> pdbc.components * eltype(x)(0) : pdbc.func, pdbc.components ) @@ -1099,8 +1120,9 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In inhomogeneity_map = Dict{Int, Float64}() for (k, v) in dof_map g = chtmp2.inhomogeneities - push!(inhomogeneity_map, - k => - g[chtmp2.dofmapping[v]] + g[chtmp2.dofmapping[k]] + push!( + inhomogeneity_map, + k => - g[chtmp2.dofmapping[v]] + g[chtmp2.dofmapping[k]] ) end end @@ -1115,7 +1137,7 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In @assert rotation_matrix !== nothing for (i, ki) in pairs(k) # u_mirror = R ⋅ u_image - vs = Pair{Int,eltype(T)}[v[j] => rotation_matrix[i, j] for j in 1:length(v)] + vs = Pair{Int, eltype(T)}[v[j] => rotation_matrix[i, j] for j in 1:length(v)] ac = AffineConstraint(ki, vs, 0.0) add!(ch, ac) end @@ -1125,43 +1147,43 @@ function _add!(ch::ConstraintHandler, pdbc::PeriodicDirichlet, interpolation::In return ch end -function construct_cornerish(min_x::V, max_x::V) where {T, V <: Vec{1,T}} +function construct_cornerish(min_x::V, max_x::V) where {T, V <: Vec{1, T}} lx = max_x - min_x max_x += lx min_x -= lx return V[min_x, max_x] end -function construct_cornerish(min_x::V, max_x::V) where {T, V <: Vec{2,T}} +function construct_cornerish(min_x::V, max_x::V) where {T, V <: Vec{2, T}} lx = max_x - min_x max_x += lx min_x -= lx return V[ - max_x, - min_x, - Vec{2,T}((max_x[1], min_x[2])), - Vec{2,T}((min_x[1], max_x[2])), + max_x, + min_x, + Vec{2, T}((max_x[1], min_x[2])), + Vec{2, T}((min_x[1], max_x[2])), ] end -function construct_cornerish(min_x::V, max_x::V) where {T, V <: Vec{3,T}} +function construct_cornerish(min_x::V, max_x::V) where {T, V <: Vec{3, T}} lx = max_x - min_x max_x += lx min_x -= lx return V[ min_x, max_x, - Vec{3,T}((max_x[1], min_x[2] , min_x[3])), - Vec{3,T}((max_x[1], max_x[2] , min_x[3])), - Vec{3,T}((min_x[1], max_x[2] , min_x[3])), - Vec{3,T}((min_x[1], min_x[2] , max_x[3])), - Vec{3,T}((max_x[1], min_x[2] , max_x[3])), - Vec{3,T}((min_x[1], max_x[2] , max_x[3])), + Vec{3, T}((max_x[1], min_x[2], min_x[3])), + Vec{3, T}((max_x[1], max_x[2], min_x[3])), + Vec{3, T}((min_x[1], max_x[2], min_x[3])), + Vec{3, T}((min_x[1], min_x[2], max_x[3])), + Vec{3, T}((max_x[1], min_x[2], max_x[3])), + Vec{3, T}((min_x[1], max_x[2], max_x[3])), ] end function mirror_local_dofs(_, _, ::Lagrange{RefLine}, ::Int) # For 1D there is nothing to do end -function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefQuadrilateral,RefTriangle}}, n::Int) +function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefQuadrilateral, RefTriangle}}, n::Int) # For 2D we always permute since Ferrite defines dofs counter-clockwise ret = collect(1:length(local_facet_dofs)) for (i, f) in enumerate(dirichlet_facetdof_indices(ip)) @@ -1179,16 +1201,16 @@ function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagran end # TODO: Can probably be combined with the method above. -function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefHexahedron,RefTetrahedron},O}, n::Int) where O +function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefHexahedron, RefTetrahedron}, O}, n::Int) where {O} @assert 1 <= O <= 2 N = ip isa Lagrange{RefHexahedron} ? 4 : 3 ret = collect(1:length(local_facet_dofs)) # Mirror by changing from counter-clockwise to clockwise for (i, f) in enumerate(dirichlet_facetdof_indices(ip)) - r = local_facet_dofs_offset[i]:(local_facet_dofs_offset[i+1] - 1) + r = local_facet_dofs_offset[i]:(local_facet_dofs_offset[i + 1] - 1) # 1. Rotate the corners - vertex_range = r[1:(N*n)] + vertex_range = r[1:(N * n)] vlr = @view ret[vertex_range] for i in 1:N reverse!(vlr, (i - 1) * n + 1, i * n) @@ -1197,7 +1219,7 @@ function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagran circshift!(vlr, n) # 2. Rotate the edge dofs for quadratic interpolation if O > 1 - edge_range = r[(N*n+1):(2N*n)] + edge_range = r[(N * n + 1):(2N * n)] elr = @view ret[edge_range] for i in 1:N reverse!(elr, (i - 1) * n + 1, i * n) @@ -1209,24 +1231,24 @@ function mirror_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagran return ret end -function rotate_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefQuadrilateral,RefTriangle}}, ncomponents) +function rotate_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefQuadrilateral, RefTriangle}}, ncomponents) return collect(1:length(local_facet_dofs)) # TODO: Return range? end -function rotate_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefHexahedron,RefTetrahedron}, O}, ncomponents) where O +function rotate_local_dofs(local_facet_dofs, local_facet_dofs_offset, ip::Lagrange{<:Union{RefHexahedron, RefTetrahedron}, O}, ncomponents) where {O} @assert 1 <= O <= 2 N = ip isa Lagrange{RefHexahedron} ? 4 : 3 ret = similar(local_facet_dofs, length(local_facet_dofs), N) ret[:, :] .= 1:length(local_facet_dofs) - for f in 1:length(local_facet_dofs_offset)-1 - facet_range = local_facet_dofs_offset[f]:(local_facet_dofs_offset[f+1]-1) - for i in 1:(N-1) + for f in 1:(length(local_facet_dofs_offset) - 1) + facet_range = local_facet_dofs_offset[f]:(local_facet_dofs_offset[f + 1] - 1) + for i in 1:(N - 1) # 1. Rotate the vertex dofs - vertex_range = facet_range[1:(N*ncomponents)] - circshift!(@view(ret[vertex_range, i+1]), @view(ret[vertex_range, i]), -ncomponents) + vertex_range = facet_range[1:(N * ncomponents)] + circshift!(@view(ret[vertex_range, i + 1]), @view(ret[vertex_range, i]), -ncomponents) # 2. Rotate the edge dofs if O > 1 - edge_range = facet_range[(N*ncomponents+1):(2N*ncomponents)] - circshift!(@view(ret[edge_range, i+1]), @view(ret[edge_range, i]), -ncomponents) + edge_range = facet_range[(N * ncomponents + 1):(2N * ncomponents)] + circshift!(@view(ret[edge_range, i + 1]), @view(ret[edge_range, i]), -ncomponents) end end end @@ -1253,7 +1275,7 @@ between a image-facet and mirror-facet, for them to be considered matched. See also: [`collect_periodic_facets!`](@ref), [`PeriodicDirichlet`](@ref). """ -function collect_periodic_facets(grid::Grid, mset::Union{AbstractSet{FacetIndex},String}, iset::Union{AbstractSet{FacetIndex},String}, transform::Union{Function,Nothing}=nothing; tol::Float64=1e-12) +function collect_periodic_facets(grid::Grid, mset::Union{AbstractSet{FacetIndex}, String}, iset::Union{AbstractSet{FacetIndex}, String}, transform::Union{Function, Nothing} = nothing; tol::Float64 = 1.0e-12) return collect_periodic_facets!(PeriodicFacetPair[], grid, mset, iset, transform; tol) end @@ -1268,7 +1290,7 @@ have a neighbor) is used. See also: [`collect_periodic_facets!`](@ref), [`PeriodicDirichlet`](@ref). """ -function collect_periodic_facets(grid::Grid, all_facets::Union{AbstractSet{FacetIndex},String,Nothing}=nothing; tol::Float64=1e-12) +function collect_periodic_facets(grid::Grid, all_facets::Union{AbstractSet{FacetIndex}, String, Nothing} = nothing; tol::Float64 = 1.0e-12) return collect_periodic_facets!(PeriodicFacetPair[], grid, all_facets; tol) end @@ -1278,12 +1300,12 @@ end Same as [`collect_periodic_facets`](@ref) but adds all matches to the existing `facet_map`. """ -function collect_periodic_facets!(facet_map::Vector{PeriodicFacetPair}, grid::Grid, mset::Union{AbstractSet{FacetIndex},String}, iset::Union{AbstractSet{FacetIndex},String}, transform::Union{Function,Nothing}=nothing; tol::Float64=1e-12) +function collect_periodic_facets!(facet_map::Vector{PeriodicFacetPair}, grid::Grid, mset::Union{AbstractSet{FacetIndex}, String}, iset::Union{AbstractSet{FacetIndex}, String}, transform::Union{Function, Nothing} = nothing; tol::Float64 = 1.0e-12) mset = __to_facetset(grid, mset) iset = __to_facetset(grid, iset) if transform === nothing # This method is destructive, hence the copy - __collect_periodic_facets_bruteforce!(facet_map, grid, copy(mset), copy(iset), #=known_order=#true, tol) + __collect_periodic_facets_bruteforce!(facet_map, grid, copy(mset), copy(iset), #=known_order=# true, tol) else # This method relies on ordering, hence the collect __collect_periodic_facets_tree!(facet_map, grid, collect(mset), collect(iset), transform, tol) @@ -1291,12 +1313,12 @@ function collect_periodic_facets!(facet_map::Vector{PeriodicFacetPair}, grid::Gr return facet_map end -function collect_periodic_facets!(facet_map::Vector{PeriodicFacetPair}, grid::Grid, facetset::Union{AbstractSet{FacetIndex},String,Nothing}; tol::Float64=1e-12) +function collect_periodic_facets!(facet_map::Vector{PeriodicFacetPair}, grid::Grid, facetset::Union{AbstractSet{FacetIndex}, String, Nothing}; tol::Float64 = 1.0e-12) facetset = facetset === nothing ? __collect_boundary_facets(grid) : copy(__to_facetset(grid, facetset)) if mod(length(facetset), 2) != 0 error("uneven number of facets") end - return __collect_periodic_facets_bruteforce!(facet_map, grid, facetset, facetset, #=known_order=#false, tol) + return __collect_periodic_facets_bruteforce!(facet_map, grid, facetset, facetset, #=known_order=# false, tol) end __to_facetset(_, set::AbstractSet{FacetIndex}) = set @@ -1316,7 +1338,7 @@ function __collect_boundary_facets(grid::Grid) return OrderedSet{FacetIndex}(values(candidates)) end -function __collect_periodic_facets_tree!(facet_map::Vector{PeriodicFacetPair}, grid::Grid, mset::Vector{FacetIndex}, iset::Vector{FacetIndex}, transformation::F, tol::Float64) where F <: Function +function __collect_periodic_facets_tree!(facet_map::Vector{PeriodicFacetPair}, grid::Grid, mset::Vector{FacetIndex}, iset::Vector{FacetIndex}, transformation::F, tol::Float64) where {F <: Function} if length(mset) != length(mset) error("different number of facets in mirror and image set") end @@ -1325,7 +1347,7 @@ function __collect_periodic_facets_tree!(facet_map::Vector{PeriodicFacetPair}, g mirror_mean_x = Tx[] for (c, f) in mset fn = facets(grid.cells[c])[f] - push!(mirror_mean_x, sum(get_node_coordinate(grid,i) for i in fn) / length(fn)) + push!(mirror_mean_x, sum(get_node_coordinate(grid, i) for i in fn) / length(fn)) end # Same dance for the image @@ -1333,7 +1355,7 @@ function __collect_periodic_facets_tree!(facet_map::Vector{PeriodicFacetPair}, g for (c, f) in iset fn = facets(grid.cells[c])[f] # Apply transformation to all coordinates - push!(image_mean_x, sum(transformation(get_node_coordinate(grid,i))::Tx for i in fn) / length(fn)) + push!(image_mean_x, sum(transformation(get_node_coordinate(grid, i))::Tx for i in fn) / length(fn)) end # Use KDTree to find closest facet @@ -1381,42 +1403,42 @@ function __collect_periodic_facets_bruteforce!(facet_map::Vector{PeriodicFacetPa return facet_map end -function __periodic_options(::T) where T <: Vec{2} +function __periodic_options(::T) where {T <: Vec{2}} # (3^2 - 1) / 2 options return ( - Vec{2}((1.0, 0.0)), - Vec{2}((0.0, 1.0)), - Vec{2}((1.0, 1.0)) / sqrt(2), - Vec{2}((1.0, -1.0)) / sqrt(2), + Vec{2}((1.0, 0.0)), + Vec{2}((0.0, 1.0)), + Vec{2}((1.0, 1.0)) / sqrt(2), + Vec{2}((1.0, -1.0)) / sqrt(2), ) end -function __periodic_options(::T) where T <: Vec{3} +function __periodic_options(::T) where {T <: Vec{3}} # (3^3 - 1) / 2 options return ( - Vec{3}((1.0, 0.0, 0.0)), - Vec{3}((0.0, 1.0, 0.0)), - Vec{3}((0.0, 0.0, 1.0)), - Vec{3}((1.0, 1.0, 0.0)) / sqrt(2), - Vec{3}((0.0, 1.0, 1.0)) / sqrt(2), - Vec{3}((1.0, 0.0, 1.0)) / sqrt(2), - Vec{3}((1.0, 1.0, 1.0)) / sqrt(3), - Vec{3}((1.0, -1.0, 0.0)) / sqrt(2), - Vec{3}((0.0, 1.0, -1.0)) / sqrt(2), - Vec{3}((1.0, 0.0, -1.0)) / sqrt(2), - Vec{3}((1.0, 1.0, -1.0)) / sqrt(3), - Vec{3}((1.0, -1.0, 1.0)) / sqrt(3), - Vec{3}((1.0, -1.0, -1.0)) / sqrt(3), + Vec{3}((1.0, 0.0, 0.0)), + Vec{3}((0.0, 1.0, 0.0)), + Vec{3}((0.0, 0.0, 1.0)), + Vec{3}((1.0, 1.0, 0.0)) / sqrt(2), + Vec{3}((0.0, 1.0, 1.0)) / sqrt(2), + Vec{3}((1.0, 0.0, 1.0)) / sqrt(2), + Vec{3}((1.0, 1.0, 1.0)) / sqrt(3), + Vec{3}((1.0, -1.0, 0.0)) / sqrt(2), + Vec{3}((0.0, 1.0, -1.0)) / sqrt(2), + Vec{3}((1.0, 0.0, -1.0)) / sqrt(2), + Vec{3}((1.0, 1.0, -1.0)) / sqrt(3), + Vec{3}((1.0, -1.0, 1.0)) / sqrt(3), + Vec{3}((1.0, -1.0, -1.0)) / sqrt(3), ) end -function __outward_normal(grid::Grid{2}, nodes, transformation::F=identity) where F <: Function +function __outward_normal(grid::Grid{2}, nodes, transformation::F = identity) where {F <: Function} n1::Vec{2} = transformation(get_node_coordinate(grid, nodes[1])) n2::Vec{2} = transformation(get_node_coordinate(grid, nodes[2])) n = Vec{2}((n2[2] - n1[2], - n2[1] + n1[1])) return n / norm(n) end -function __outward_normal(grid::Grid{3}, nodes, transformation::F=identity) where F <: Function +function __outward_normal(grid::Grid{3}, nodes, transformation::F = identity) where {F <: Function} n1::Vec{3} = transformation(get_node_coordinate(grid, nodes[1])) n2::Vec{3} = transformation(get_node_coordinate(grid, nodes[2])) n3::Vec{3} = transformation(get_node_coordinate(grid, nodes[3])) @@ -1424,8 +1446,8 @@ function __outward_normal(grid::Grid{3}, nodes, transformation::F=identity) wher return n / norm(n) end -function circshift_tuple(x::T, n) where T - Tuple(circshift!(collect(x), n))::T +function circshift_tuple(x::T, n) where {T} + return Tuple(circshift!(collect(x), n))::T end # Check if two facets are periodic. This method assumes that the facets are mirrored and thus @@ -1506,7 +1528,7 @@ end # a transformation function and we have then used the KDTree to find the matching pair of # facets. This function only need to i) check whether facets have aligned or opposite normal # vectors, and ii) compute the relative rotation. -function __check_periodic_facets_f(grid::Grid, fi::FacetIndex, fj::FacetIndex, xmi, xmj, transformation::F, tol::Float64) where F +function __check_periodic_facets_f(grid::Grid, fi::FacetIndex, fj::FacetIndex, xmi, xmj, transformation::F, tol::Float64) where {F} cii, fii = fi nodes_i = facets(grid.cells[cii])[fii] cij, fij = fj @@ -1579,18 +1601,24 @@ requires writing to entries in the global matrix/vector. For such a case, Note that this method is destructive since it, by definition, modifies `local_matrix` and `local_vector`. """ -function apply_local!(local_matrix::AbstractMatrix, local_vector::AbstractVector, - global_dofs::AbstractVector, ch::ConstraintHandler; - apply_zero::Bool = false) - return _apply_local!(local_matrix, local_vector, global_dofs, ch, apply_zero, - #=global_matrix=# nothing, #=global_vector=# nothing) +function apply_local!( + local_matrix::AbstractMatrix, local_vector::AbstractVector, + global_dofs::AbstractVector, ch::ConstraintHandler; + apply_zero::Bool = false + ) + return _apply_local!( + local_matrix, local_vector, global_dofs, ch, apply_zero, + #=global_matrix=# nothing, #=global_vector=# nothing + ) end # Element local application of boundary conditions. Global matrix and vectors are necessary # if there are affine constraints that connect dofs from different elements. -function _apply_local!(local_matrix::AbstractMatrix, local_vector::AbstractVector, - global_dofs::AbstractVector, ch::ConstraintHandler, apply_zero::Bool, - global_matrix, global_vector) +function _apply_local!( + local_matrix::AbstractMatrix, local_vector::AbstractVector, + global_dofs::AbstractVector, ch::ConstraintHandler, apply_zero::Bool, + global_matrix, global_vector + ) @assert isclosed(ch) # TODO: With apply_zero it shouldn't be required to pass the vector. length(global_dofs) == size(local_matrix, 1) == size(local_matrix, 2) == length(local_vector) || error("?") @@ -1612,8 +1640,8 @@ function _apply_local!(local_matrix::AbstractMatrix, local_vector::AbstractVecto end # Check if this is an affine constraint has_nontrivial_affine_constraints = has_nontrivial_affine_constraints || ( - coeffs = ch.dofcoefficients[pdofs_index]; - !(coeffs === nothing || isempty(coeffs)) + coeffs = ch.dofcoefficients[pdofs_index]; + !(coeffs === nothing || isempty(coeffs)) ) end # 2. Compute mean of diagonal before modifying local matrix @@ -1648,11 +1676,13 @@ end # Condensation of affine constraints on element level. If possible this function only # modifies the local arrays. @noinline missing_global() = error("can not condense constraint without the global matrix and vector") -function _condense_local!(local_matrix::AbstractMatrix, local_vector::AbstractVector, - global_matrix#=::SparseMatrixCSC=#, global_vector#=::Vector=#, - global_dofs::AbstractVector, dofmapping::Dict, dofcoefficients::Vector) +function _condense_local!( + local_matrix::AbstractMatrix, local_vector::AbstractVector, + global_matrix #=::SparseMatrixCSC=#, global_vector #=::Vector=#, + global_dofs::AbstractVector, dofmapping::Dict, dofcoefficients::Vector + ) @assert axes(local_matrix, 1) == axes(local_matrix, 2) == - axes(local_vector, 1) == axes(global_dofs, 1) + axes(local_vector, 1) == axes(global_dofs, 1) has_global_arrays = global_matrix !== nothing && global_vector !== nothing for (local_col, global_col) in pairs(global_dofs) col_coeffs = coefficients_for_dof(dofmapping, dofcoefficients, global_col) @@ -1730,4 +1760,5 @@ function _condense_local!(local_matrix::AbstractMatrix, local_vector::AbstractVe local_vector[local_col] = 0 end end + return end diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index 4db8f9eb65..a89ffef1cb 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -79,6 +79,7 @@ function Base.show(io::IO, mime::MIME"text/plain", sdh::SubDofHandler) println(io, typeof(sdh)) println(io, " Cell type: ", getcelltype(sdh)) _print_field_information(io, mime, sdh) + return end function _print_field_information(io::IO, mime::MIME"text/plain", sdh::SubDofHandler) @@ -91,9 +92,10 @@ function _print_field_information(io::IO, mime::MIME"text/plain", sdh::SubDofHan else println(io, " Dofs per cell: ", ndofs_per_cell(sdh)) end + return end -mutable struct DofHandler{dim,G<:AbstractGrid{dim}} <: AbstractDofHandler +mutable struct DofHandler{dim, G <: AbstractGrid{dim}} <: AbstractDofHandler const subdofhandlers::Vector{SubDofHandler{DofHandler{dim, G}}} const field_names::Vector{Symbol} # Dofs for cell i are stored in cell_dofs at the range: @@ -131,7 +133,7 @@ close!(dh) function DofHandler(grid::G) where {dim, G <: AbstractGrid{dim}} ncells = getncells(grid) sdhs = SubDofHandler{DofHandler{dim, G}}[] - DofHandler{dim, G}(sdhs, Symbol[], Int[], zeros(Int, ncells), zeros(Int, ncells), false, grid, -1) + return DofHandler{dim, G}(sdhs, Symbol[], Int[], zeros(Int, ncells), zeros(Int, ncells), false, grid, -1) end function Base.show(io::IO, mime::MIME"text/plain", dh::DofHandler) @@ -145,7 +147,7 @@ function Base.show(io::IO, mime::MIME"text/plain", dh::DofHandler) if ip isa ScalarInterpolation field_type = "scalar" elseif ip isa VectorInterpolation - _getvdim(::VectorInterpolation{vdim}) where vdim = vdim + _getvdim(::VectorInterpolation{vdim}) where {vdim} = vdim field_type = "Vec{$(_getvdim(ip))}" end println(io, " ", repr(fieldname), ", ", field_type) @@ -156,6 +158,7 @@ function Base.show(io::IO, mime::MIME"text/plain", dh::DofHandler) else print(io, " Total dofs: ", ndofs(dh)) end + return end isclosed(dh::AbstractDofHandler) = dh.closed @@ -220,7 +223,7 @@ function celldofs(dh::AbstractDofHandler, i::Int) end function cellnodes!(global_nodes::Vector{Int}, dh::DofHandler, i::Union{Int, <:AbstractCell}) - cellnodes!(global_nodes, get_grid(dh), i) + return cellnodes!(global_nodes, get_grid(dh), i) end """ @@ -388,7 +391,7 @@ function __close!(dh::DofHandler{dim}) where {dim} facedicts, ) end - dh.ndofs = maximum(dh.cell_dofs; init=0) + dh.ndofs = maximum(dh.cell_dofs; init = 0) dh.closed = true return dh, vertexdicts, edgedicts, facedicts @@ -407,25 +410,25 @@ function _close_subdofhandler!(dh::DofHandler{sdim}, sdh::SubDofHandler, sdh_ind base_ip = get_base_interpolation(interpolation) begin next_dof_index = 1 - for vdofs ∈ vertexdof_indices(base_ip) - for dof_index ∈ vdofs + for vdofs in vertexdof_indices(base_ip) + for dof_index in vdofs @assert dof_index == next_dof_index "Vertex dof ordering not supported. Please consult the dev docs." next_dof_index += 1 end end - for vdofs ∈ edgedof_interior_indices(base_ip) - for dof_index ∈ vdofs + for vdofs in edgedof_interior_indices(base_ip) + for dof_index in vdofs @assert dof_index == next_dof_index "Edge dof ordering not supported. Please consult the dev docs." next_dof_index += 1 end end - for vdofs ∈ facedof_interior_indices(base_ip) - for dof_index ∈ vdofs + for vdofs in facedof_interior_indices(base_ip) + for dof_index in vdofs @assert dof_index == next_dof_index "Face dof ordering not supported. Please consult the dev docs." next_dof_index += 1 end end - for dof_index ∈ volumedof_interior_indices(base_ip) + for dof_index in volumedof_interior_indices(base_ip) @assert next_dof_index <= dof_index <= getnbasefunctions(base_ip) "Cell dof ordering not supported. Please consult the dev docs." end end @@ -479,7 +482,7 @@ function _close_subdofhandler!(dh::DofHandler{sdim}, sdh::SubDofHandler, sdh_ind else @assert ndofs_per_cell == length(dh.cell_dofs) - len_cell_dofs_start end - @debug println("\tDofs for cell #$ci:\t$(dh.cell_dofs[(end-ndofs_per_cell+1):end])") + @debug println("\tDofs for cell #$ci:\t$(dh.cell_dofs[(end - ndofs_per_cell + 1):end])") end # cell loop return nextdof end @@ -529,7 +532,7 @@ function add_vertex_dofs(cell_dofs::Vector{Int}, cell::AbstractCell, vertexdict, # (Re)compute the next dof from first_dof by adding n_copies dofs from the # (lvi-1) previous vertex dofs and the (d-1) dofs already distributed for # the current vertex dof - dof = first_dof + (lvi-1)*n_copies + (d-1) + dof = first_dof + (lvi - 1) * n_copies + (d - 1) push!(cell_dofs, dof) end else # create dofs @@ -539,7 +542,7 @@ function add_vertex_dofs(cell_dofs::Vector{Int}, cell::AbstractCell, vertexdict, nextdof += 1 end end - @debug println("\t\t\tdofs: $(cell_dofs[(end-nvertexdofs[vi]*n_copies+1):end])") + @debug println("\t\t\tdofs: $(cell_dofs[(end - nvertexdofs[vi] * n_copies + 1):end])") end return nextdof end @@ -554,25 +557,25 @@ for the object (vertex, face) then simply return those, otherwise create new dof token = Base.ht_keyindex2!(dict, key) if token > 0 # vertex, face etc. visited before first_dof = dict.vals[token] - dofs = first_dof : n_copies : (first_dof + n_copies * ndofs - 1) + dofs = first_dof:n_copies:(first_dof + n_copies * ndofs - 1) @debug println("\t\t\tkey: $key dofs: $(dofs) (reused dofs)") else # create new dofs - dofs = nextdof : n_copies : (nextdof + n_copies*ndofs-1) + dofs = nextdof:n_copies:(nextdof + n_copies * ndofs - 1) @debug println("\t\t\tkey: $key dofs: $dofs") Base._setindex!(dict, nextdof, key, -token) - nextdof += ndofs*n_copies + nextdof += ndofs * n_copies end return nextdof, dofs end function add_face_dofs(cell_dofs::Vector{Int}, cell::AbstractCell, facedict::Dict, nfacedofs::Vector{Int}, nextdof::Int, adjust_during_distribution::Bool, n_copies::Int) - for (fi,face) in pairs(faces(cell)) + for (fi, face) in pairs(faces(cell)) nfacedofs[fi] > 0 || continue # skip if no dof on this vertex sface, orientation = sortface(face) @debug println("\t\tface #$sface, $orientation") nextdof, dofs = get_or_create_dofs!(nextdof, nfacedofs[fi], n_copies, facedict, sface) permute_and_push!(cell_dofs, dofs, orientation, adjust_during_distribution, getrefdim(cell)) # TODO: passing rdim of cell is temporary, simply to check if facedofs are internal to cell - @debug println("\t\t\tadjusted dofs: $(cell_dofs[(end - nfacedofs[fi]*n_copies + 1):end])") + @debug println("\t\t\tadjusted dofs: $(cell_dofs[(end - nfacedofs[fi] * n_copies + 1):end])") end return nextdof end @@ -584,14 +587,14 @@ function add_edge_dofs(cell_dofs::Vector{Int}, cell::AbstractCell, edgedict::Dic @debug println("\t\tedge #$sedge, $orientation") nextdof, dofs = get_or_create_dofs!(nextdof, nedgedofs[ei], n_copies, edgedict, sedge) permute_and_push!(cell_dofs, dofs, orientation, adjust_during_distribution) - @debug println("\t\t\tadjusted dofs: $(cell_dofs[(end - nedgedofs[ei]*n_copies + 1):end])") + @debug println("\t\t\tadjusted dofs: $(cell_dofs[(end - nedgedofs[ei] * n_copies + 1):end])") end end return nextdof end function add_volume_dofs(cell_dofs::CD, nvolumedofs::Int, nextdof::Int, n_copies::Int) where {CD} - @debug println("\t\tvolumedofs #$nextdof:$(nvolumedofs*n_copies-1)") + @debug println("\t\tvolumedofs #$nextdof:$(nvolumedofs * n_copies - 1)") for _ in 1:nvolumedofs, _ in 1:n_copies push!(cell_dofs, nextdof) nextdof += 1 @@ -632,7 +635,7 @@ described therein. # References - [Scroggs2022](@cite) Scroggs et al. ACM Trans. Math. Softw. 48 (2022). """ -@inline function permute_and_push!(cell_dofs::Vector{Int}, dofs::StepRange{Int,Int}, orientation::PathOrientationInfo, adjust_during_distribution::Bool) +@inline function permute_and_push!(cell_dofs::Vector{Int}, dofs::StepRange{Int, Int}, orientation::PathOrientationInfo, adjust_during_distribution::Bool) # TODO Investigate if we can somehow pass the interpolation into this function in a # typestable way. n_copies = step(dofs) @@ -643,7 +646,7 @@ described therein. end for dof in dofs for i in 1:n_copies - push!(cell_dofs, dof+(i-1)) + push!(cell_dofs, dof + (i - 1)) end end return nothing @@ -657,9 +660,9 @@ Here the unique representation is the sorted node index tuple. The orientation is `true` if the edge is not flipped, where it is `false` if the edge is flipped. """ -function sortedge(edge::Tuple{Int,Int}) +function sortedge(edge::Tuple{Int, Int}) a, b = edge - a < b ? (return (edge, PathOrientationInfo(true))) : (return ((b, a), PathOrientationInfo(false))) + return a < b ? (edge, PathOrientationInfo(true)) : ((b, a), PathOrientationInfo(false)) end """ @@ -668,9 +671,9 @@ sortedge_fast(edge::Tuple{Int,Int}) Returns the unique representation of an edge. Here the unique representation is the sorted node index tuple. """ -function sortedge_fast(edge::Tuple{Int,Int}) +function sortedge_fast(edge::Tuple{Int, Int}) a, b = edge - a < b ? (return edge) : (return (b, a)) + return a < b ? edge : (b, a) end """ @@ -713,21 +716,21 @@ For more details we refer to [1] as we follow the methodology described therein. !!!TODO Investigate if we can somehow pass the interpolation into this function in a typestable way. """ -@inline function permute_and_push!(cell_dofs::Vector{Int}, dofs::StepRange{Int,Int}, ::SurfaceOrientationInfo, adjust_during_distribution::Bool, rdim::Int) - if rdim==3 && adjust_during_distribution && length(dofs) > 1 +@inline function permute_and_push!(cell_dofs::Vector{Int}, dofs::StepRange{Int, Int}, ::SurfaceOrientationInfo, adjust_during_distribution::Bool, rdim::Int) + if rdim == 3 && adjust_during_distribution && length(dofs) > 1 error("Dof distribution for interpolations with multiple dofs per face not implemented yet.") end n_copies = step(dofs) @assert n_copies > 0 for dof in dofs for i in 1:n_copies - push!(cell_dofs, dof+(i-1)) + push!(cell_dofs, dof + (i - 1)) end end return nothing end -function sortface(face::Tuple{Int,Int,Int}) +function sortface(face::Tuple{Int, Int, Int}) a, b, c = face b, c = minmax(b, c) a, c = minmax(a, c) @@ -736,7 +739,7 @@ function sortface(face::Tuple{Int,Int,Int}) end -function sortface_fast(face::Tuple{Int,Int,Int}) +function sortface_fast(face::Tuple{Int, Int, Int}) a, b, c = face b, c = minmax(b, c) a, c = minmax(a, c) @@ -745,7 +748,7 @@ function sortface_fast(face::Tuple{Int,Int,Int}) end -function sortface(face::Tuple{Int,Int,Int,Int}) +function sortface(face::Tuple{Int, Int, Int, Int}) a, b, c, d = face c, d = minmax(c, d) b, d = minmax(b, d) @@ -757,7 +760,7 @@ function sortface(face::Tuple{Int,Int,Int,Int}) end -function sortface_fast(face::Tuple{Int,Int,Int,Int}) +function sortface_fast(face::Tuple{Int, Int, Int, Int}) a, b, c, d = face c, d = minmax(c, d) b, d = minmax(b, d) @@ -838,7 +841,7 @@ end # Calculate the offset to the first local dof of a field function field_offset(sdh::SubDofHandler, field_idx::Int) offset = 0 - for i in 1:(field_idx-1) + for i in 1:(field_idx - 1) offset += getnbasefunctions(sdh.field_interpolations[i])::Int end return offset @@ -884,7 +887,7 @@ function dof_range(sdh::SubDofHandler, field_idx::Int) offset = field_offset(sdh, field_idx) field_interpolation = sdh.field_interpolations[field_idx] n_field_dofs = getnbasefunctions(field_interpolation)::Int - return (offset+1):(offset+n_field_dofs) + return (offset + 1):(offset + n_field_dofs) end dof_range(sdh::SubDofHandler, field_name::Symbol) = dof_range(sdh, find_field(sdh, field_name)) @@ -904,7 +907,7 @@ end Return the interpolation of a given field. The field can be specified by its index (see [`find_field`](@ref) or its name. """ -function getfieldinterpolation(dh::DofHandler, field_idxs::NTuple{2,Int}) +function getfieldinterpolation(dh::DofHandler, field_idxs::NTuple{2, Int}) sdh_idx, field_idx = field_idxs ip = dh.subdofhandlers[sdh_idx].field_interpolations[field_idx] return ip @@ -927,13 +930,13 @@ function evaluate_at_grid_nodes(dh::DofHandler, u::AbstractVector, fieldname::Sy end # Internal method that have the vtk option to allocate the output differently -function _evaluate_at_grid_nodes(dh::DofHandler, u::AbstractVector{T}, fieldname::Symbol, ::Val{vtk}=Val(false)) where {T, vtk} +function _evaluate_at_grid_nodes(dh::DofHandler, u::AbstractVector{T}, fieldname::Symbol, ::Val{vtk} = Val(false)) where {T, vtk} # Make sure the field exists fieldname ∈ getfieldnames(dh) || error("Field $fieldname not found.") # Figure out the return type (scalar or vector) field_idx = find_field(dh, fieldname) ip = getfieldinterpolation(dh, field_idx) - RT = ip isa ScalarInterpolation ? T : Vec{n_components(ip),T} + RT = ip isa ScalarInterpolation ? T : Vec{n_components(ip), T} if vtk # VTK output of solution field (or L2 projected scalar data) n_c = n_components(ip) @@ -968,8 +971,10 @@ function _evaluate_at_grid_nodes(dh::DofHandler, u::AbstractVector{T}, fieldname end # Loop over the cells and use shape functions to compute the value -function _evaluate_at_grid_nodes!(data::Union{Vector,Matrix}, sdh::SubDofHandler, - u::AbstractVector{T}, cv::CellValues, drange::UnitRange, ::Type{RT}) where {T, RT} +function _evaluate_at_grid_nodes!( + data::Union{Vector, Matrix}, sdh::SubDofHandler, + u::AbstractVector{T}, cv::CellValues, drange::UnitRange, ::Type{RT} + ) where {T, RT} ue = zeros(T, length(drange)) # TODO: Remove this hack when embedding works... if RT <: Vec && function_interpolation(cv) isa ScalarInterpolation @@ -987,7 +992,7 @@ function _evaluate_at_grid_nodes!(data::Union{Vector,Matrix}, sdh::SubDofHandler val = function_value(cv, qp, uer) if data isa Matrix # VTK data[1:length(val), nodeid] .= val - data[(length(val)+1):end, nodeid] .= 0 # purge the NaN + data[(length(val) + 1):end, nodeid] .= 0 # purge the NaN else data[nodeid] = val end diff --git a/src/Dofs/DofRenumbering.jl b/src/Dofs/DofRenumbering.jl index 43bb28daa1..0d1a2f70b0 100644 --- a/src/Dofs/DofRenumbering.jl +++ b/src/Dofs/DofRenumbering.jl @@ -17,12 +17,12 @@ module DofOrder struct FieldWise # <: DofOrdering target_blocks::Vector{Int} - FieldWise(x=Int[]) = new(_check_target_blocks(x)) + FieldWise(x = Int[]) = new(_check_target_blocks(x)) end struct ComponentWise # <: DofOrdering target_blocks::Vector{Int} - ComponentWise(x=Int[]) = new(_check_target_blocks(x)) + ComponentWise(x = Int[]) = new(_check_target_blocks(x)) end """ @@ -33,7 +33,7 @@ module DofOrder [Metis.jl](https://github.com/JuliaSparse/Metis.jl). """ abstract type Ext{T} end - function Ext{T}(args...; kwargs...) where T + function Ext{T}(args...; kwargs...) where {T} throw(ArgumentError("Unknown external order DofOrder.Ext{$T}. See documentation for `DofOrder.Ext` for details.")) end @@ -70,7 +70,7 @@ renumber!(dh::AbstractDofHandler, ch::ConstraintHandler, order) = _renumber!(dh, # `compute_renumber_permutation(::DofHandler, ::ConstraintHandler, ::O)` returning the dof # permutation or `_renumber!(::DofHandler, ::ConstraintHandler, ::O)`. -function _renumber!(dh::AbstractDofHandler, ch::Union{ConstraintHandler,Nothing}, order) +function _renumber!(dh::AbstractDofHandler, ch::Union{ConstraintHandler, Nothing}, order) @assert ch === nothing || ch.dh === dh perm = compute_renumber_permutation(dh, ch, order) @assert isperm(perm) && length(perm) == ndofs(dh) @@ -192,7 +192,7 @@ function compute_renumber_permutation(dh::DofHandler, _, order::DofOrder.Compone nblocks = maximum(target_blocks) dofs_for_blocks = [Set{Int}() for _ in 1:nblocks] component_offsets = pushfirst!(cumsum(field_dims), 0) - flags = UpdateFlags(nodes=false, coords=false, dofs=true) + flags = UpdateFlags(nodes = false, coords = false, dofs = true) for sdh in dh.subdofhandlers dof_ranges = [dof_range(sdh, f) for f in eachindex(sdh.field_names)] global_idxs = [findfirst(x -> x === f, dh.field_names) for f in sdh.field_names] @@ -225,6 +225,6 @@ function compute_renumber_permutation(dh::DofHandler, _, order::DofOrder.Compone return perm end -function compute_renumber_permutation(dh::AbstractDofHandler, ::Union{ConstraintHandler,Nothing}, ::DofOrder.Ext{M}) where M +function compute_renumber_permutation(dh::AbstractDofHandler, ::Union{ConstraintHandler, Nothing}, ::DofOrder.Ext{M}) where {M} error("Renumbering extension based on package $M not available.") end diff --git a/src/Dofs/apply_analytical.jl b/src/Dofs/apply_analytical.jl index 13015a1f77..878e5985ff 100644 --- a/src/Dofs/apply_analytical.jl +++ b/src/Dofs/apply_analytical.jl @@ -1,7 +1,7 @@ function _geometric_interpolations(dh::DofHandler) sdhs = dh.subdofhandlers getcelltype(i) = typeof(getcells(get_grid(dh), first(sdhs[i].cellset))) - ntuple(i -> geometric_interpolation(getcelltype(i)), length(sdhs)) + return ntuple(i -> geometric_interpolation(getcelltype(i)), length(sdhs)) end """ @@ -26,8 +26,9 @@ This function can be used to apply initial conditions for time dependent problem sub- and superparametric elements. """ function apply_analytical!( - a::AbstractVector, dh::DofHandler, fieldname::Symbol, f::Function, - cellset = 1:getncells(get_grid(dh))) + a::AbstractVector, dh::DofHandler, fieldname::Symbol, f::Function, + cellset = 1:getncells(get_grid(dh)) + ) fieldname ∉ getfieldnames(dh) && error("The fieldname $fieldname was not found in the dof handler") ip_geos = _geometric_interpolations(dh) @@ -50,8 +51,9 @@ function apply_analytical!( end function _apply_analytical!( - a::AbstractVector, dh::AbstractDofHandler, celldofinds, field_dim, - ip_fun::Interpolation{RefShape}, ip_geo::Interpolation, f::Function, cellset) where {dim, RefShape<:AbstractRefShape{dim}} + a::AbstractVector, dh::AbstractDofHandler, celldofinds, field_dim, + ip_fun::Interpolation{RefShape}, ip_geo::Interpolation, f::Function, cellset + ) where {dim, RefShape <: AbstractRefShape{dim}} coords = getcoordinates(get_grid(dh), first(cellset)) ref_points = reference_coordinates(ip_fun) @@ -80,7 +82,7 @@ function _apply_analytical!(a::AbstractVector, dofs::Vector{Int}, coords::Vector for i_dof in 1:getnquadpoints(cv) x_dof = spatial_coordinate(cv, i_dof, coords) for (idim, icval) in enumerate(f(x_dof)) - a[dofs[field_dim*(i_dof-1)+idim]] = icval + a[dofs[field_dim * (i_dof - 1) + idim]] = icval end end return a diff --git a/src/Dofs/block_sparsity_pattern.jl b/src/Dofs/block_sparsity_pattern.jl index 465f5f5223..d94bd286cd 100644 --- a/src/Dofs/block_sparsity_pattern.jl +++ b/src/Dofs/block_sparsity_pattern.jl @@ -121,7 +121,7 @@ function Base.iterate(it::BSPRowIterator, state = (1, 1)) else # Compute global col idx and advance idx col_local = colidxs[idx] - offset = sum((bsp.block_sizes[i] for i in 1:col_block-1); init = 0) + offset = sum((bsp.block_sizes[i] for i in 1:(col_block - 1)); init = 0) return offset + col_local, (col_block, idx + 1) end end diff --git a/src/Dofs/sparsity_pattern.jl b/src/Dofs/sparsity_pattern.jl index def97b55e4..95f80841cc 100644 --- a/src/Dofs/sparsity_pattern.jl +++ b/src/Dofs/sparsity_pattern.jl @@ -147,9 +147,9 @@ function Base.show(io::IO, ::MIME"text/plain", sp::SparsityPattern) println(iob, " - Entries per row (min, max, avg): $(min_entries), $(max_entries), $(avg_entries)") # Compute memory estimate @assert getnrows(sp) * sizeof(eltype(sp.rows)) == sizeof(sp.rows) - bytes_used = sizeof(sp.rows) + stored_entries * sizeof(Int) + bytes_used = sizeof(sp.rows) + stored_entries * sizeof(Int) bytes_allocated = sizeof(sp.rows) + PoolAllocator.mempool_stats(sp.mempool)[2] - print(iob, " - Memory estimate: $(Base.format_bytes(bytes_used)) used, $(Base.format_bytes(bytes_allocated)) allocated") + print(iob, " - Memory estimate: $(Base.format_bytes(bytes_used)) used, $(Base.format_bytes(bytes_allocated)) allocated") write(io, seekstart(iob)) return end @@ -173,7 +173,7 @@ end return x end -eachrow(sp::SparsityPattern) = sp.rows +eachrow(sp::SparsityPattern) = sp.rows eachrow(sp::SparsityPattern, row::Int) = sp.rows[row] @@ -329,7 +329,7 @@ function must be called as the *last* step when creating the sparsity pattern. function add_constraint_entries!( sp::AbstractSparsityPattern, ch::ConstraintHandler; keep_constrained::Bool = true, -) + ) return _add_constraint_entries!(sp, ch.dofcoefficients, ch.dofmapping, keep_constrained) end @@ -512,7 +512,7 @@ end function _add_constraint_entries!( sp::AbstractSparsityPattern, dofcoefficients::Vector{Union{DofCoefficients{T}, Nothing}}, - dofmapping::Dict{Int,Int}, keep_constrained::Bool, + dofmapping::Dict{Int, Int}, keep_constrained::Bool, ) where {T} # Return early if there are no non-trivial affine constraints @@ -587,9 +587,11 @@ function _add_constraint_entries!( return sp end -function _add_interface_entry(sp::SparsityPattern, +function _add_interface_entry( + sp::SparsityPattern, cell_field_dofs::Union{Vector{Int}, SubArray}, neighbor_field_dofs::Union{Vector{Int}, SubArray}, - i::Int, j::Int, keep_constrained::Bool, ch::Union{ConstraintHandler, Nothing}) + i::Int, j::Int, keep_constrained::Bool, ch::Union{ConstraintHandler, Nothing} + ) dofi = cell_field_dofs[i] dofj = neighbor_field_dofs[j] # sym && (dofj > dofi && return cnt) @@ -656,7 +658,7 @@ function _allocate_matrix(::Type{SparseMatrixCSC{Tv, Ti}}, sp::AbstractSparsityP for (row, colidxs) in enumerate(eachrow(sp)) for col in colidxs sym && row > col && continue - colptr[col+1] += 1 + colptr[col + 1] += 1 end end cumsum!(colptr, colptr) diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index db28841103..840817fd4b 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -1,4 +1,3 @@ - """ VTKGridFile(filename::AbstractString, grid::AbstractGrid; kwargs...) VTKGridFile(filename::AbstractString, dh::DofHandler; kwargs...) @@ -26,7 +25,7 @@ VTKGridFile(filename, grid) do vtk end ``` """ -struct VTKGridFile{VTK<:WriteVTK.DatasetFile} +struct VTKGridFile{VTK <: WriteVTK.DatasetFile} vtk::VTK end function VTKGridFile(filename::String, dh::DofHandler; kwargs...) @@ -56,6 +55,7 @@ function Base.show(io::IO, ::MIME"text/plain", vtk::VTKGridFile) open_str = isopen(vtk.vtk) ? "open" : "closed" filename = vtk.vtk.path print(io, "VTKGridFile for the $open_str file \"$(filename)\".") + return end function WriteVTK.collection_add_timestep(pvd::WriteVTK.CollectionFile, datfile::VTKGridFile, time::Real) @@ -83,7 +83,7 @@ cell_to_vtkcell(::Type{Wedge}) = VTKCellTypes.VTK_WEDGE cell_to_vtkcell(::Type{Pyramid}) = VTKCellTypes.VTK_PYRAMID nodes_to_vtkorder(cell::AbstractCell) = collect(cell.nodes) -nodes_to_vtkorder(cell::Pyramid) = cell.nodes[[1,2,4,3,5]] +nodes_to_vtkorder(cell::Pyramid) = cell.nodes[[1, 2, 4, 3, 5]] nodes_to_vtkorder(cell::QuadraticHexahedron) = [ cell.nodes[1], # faces cell.nodes[2], @@ -114,7 +114,7 @@ nodes_to_vtkorder(cell::QuadraticHexahedron) = [ cell.nodes[27], # interior ] -function create_vtk_griddata(grid::Grid{dim,C,T}) where {dim,C,T} +function create_vtk_griddata(grid::Grid{dim, C, T}) where {dim, C, T} cls = WriteVTK.MeshCell[] for cell in getcells(grid) celltype = cell_to_vtkcell(typeof(cell)) @@ -124,22 +124,24 @@ function create_vtk_griddata(grid::Grid{dim,C,T}) where {dim,C,T} return coords, cls end -function create_vtk_grid(filename::AbstractString, grid::Grid{dim,C,T}; kwargs...) where {dim,C,T} +function create_vtk_grid(filename::AbstractString, grid::Grid{dim, C, T}; kwargs...) where {dim, C, T} coords, cls = create_vtk_griddata(grid) return WriteVTK.vtk_grid(filename, coords, cls; kwargs...) end -function toparaview!(v, x::Vec{D}) where D +function toparaview!(v, x::Vec{D}) where {D} v[1:D] .= x + return v end function toparaview!(v, x::SecondOrderTensor) tovoigt!(v, x) + return v end function _vtk_write_node_data( - vtk::WriteVTK.DatasetFile, - nodedata::Vector{S}, - name::AbstractString + vtk::WriteVTK.DatasetFile, + nodedata::Vector{S}, + name::AbstractString ) where {O, D, T, M, S <: Union{Tensor{O, D, T, M}, SymmetricTensor{O, D, T, M}}} noutputs = S <: Vec{2} ? 3 : M # Pad 2D Vec to 3D npoints = length(nodedata) @@ -147,26 +149,26 @@ function _vtk_write_node_data( for i in 1:npoints toparaview!(@view(out[:, i]), nodedata[i]) end - return WriteVTK.vtk_point_data(vtk, out, name; component_names=component_names(S)) + return WriteVTK.vtk_point_data(vtk, out, name; component_names = component_names(S)) end function _vtk_write_node_data(vtk::WriteVTK.DatasetFile, nodedata::Vector{<:Real}, name::AbstractString) return WriteVTK.vtk_point_data(vtk, nodedata, name) end -function _vtk_write_node_data(vtk::WriteVTK.DatasetFile, nodedata::Matrix{<:Real}, name::AbstractString; component_names=nothing) - return WriteVTK.vtk_point_data(vtk, nodedata, name; component_names=component_names) +function _vtk_write_node_data(vtk::WriteVTK.DatasetFile, nodedata::Matrix{<:Real}, name::AbstractString; component_names = nothing) + return WriteVTK.vtk_point_data(vtk, nodedata, name; component_names = component_names) end -function component_names(::Type{S}) where S +function component_names(::Type{S}) where {S} names = - S <: Vec{1} ? ["x"] : - S <: Vec ? ["x", "y", "z"] : # Pad 2D Vec to 3D - S <: Tensor{2,1} ? ["xx"] : - S <: SymmetricTensor{2,1} ? ["xx"] : - S <: Tensor{2,2} ? ["xx", "yy", "xy", "yx"] : - S <: SymmetricTensor{2,2} ? ["xx", "yy", "xy"] : - S <: Tensor{2,3} ? ["xx", "yy", "zz", "yz", "xz", "xy", "zy", "zx", "yx"] : - S <: SymmetricTensor{2,3} ? ["xx", "yy", "zz", "yz", "xz", "xy"] : - nothing + S <: Vec{1} ? ["x"] : + S <: Vec ? ["x", "y", "z"] : # Pad 2D Vec to 3D + S <: Tensor{2, 1} ? ["xx"] : + S <: SymmetricTensor{2, 1} ? ["xx"] : + S <: Tensor{2, 2} ? ["xx", "yy", "xy", "yx"] : + S <: SymmetricTensor{2, 2} ? ["xx", "yy", "xy"] : + S <: Tensor{2, 3} ? ["xx", "yy", "zz", "yz", "xz", "xy", "zy", "zx", "yx"] : + S <: SymmetricTensor{2, 3} ? ["xx", "yy", "zz", "yz", "xz", "xy"] : + nothing return names end @@ -182,7 +184,7 @@ degree of freedom in `dh`, see [`write_node_data`](@ref write_node_data) for det Use `write_node_data` directly when exporting values that are already sorted by the nodes in the grid. """ -function write_solution(vtk::VTKGridFile, dh::AbstractDofHandler, u::Vector, suffix="") +function write_solution(vtk::VTKGridFile, dh::AbstractDofHandler, u::Vector, suffix = "") fieldnames = getfieldnames(dh) # all primary fields for name in fieldnames data = _evaluate_at_grid_nodes(dh, u, name, #=vtk=# Val(true)) @@ -199,7 +201,7 @@ Project `vals` to the grid nodes with `proj` and save to `vtk`. function write_projection(vtk::VTKGridFile, proj::L2Projector, vals, name) data = _evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix @assert size(data, 2) == getnnodes(get_grid(proj.dh)) - _vtk_write_node_data(vtk.vtk, data, name; component_names=component_names(eltype(vals))) + _vtk_write_node_data(vtk.vtk, data, name; component_names = component_names(eltype(vals))) return vtk end @@ -252,7 +254,7 @@ Write all cell sets in the grid with name according to their keys and celldata 1 if the cell is in the set, and 0 otherwise. It is also possible to only export a single `cellset`, or multiple `cellsets`. """ -function write_cellset(vtk, grid::AbstractGrid, cellsets=keys(getcellsets(grid))) +function write_cellset(vtk, grid::AbstractGrid, cellsets = keys(getcellsets(grid))) z = zeros(getncells(grid)) for cellset in cellsets fill!(z, 0) @@ -310,7 +312,7 @@ Write cell colors (see [`create_coloring`](@ref)) to a VTK file for visualizatio In case of coloring a subset, the cells which are not part of the subset are represented as color 0. """ -function write_cell_colors(vtk, grid::AbstractGrid, cell_colors::AbstractVector{<:AbstractVector{<:Integer}}, name="coloring") +function write_cell_colors(vtk, grid::AbstractGrid, cell_colors::AbstractVector{<:AbstractVector{<:Integer}}, name = "coloring") color_vector = zeros(Int, getncells(grid)) for (i, cells_color) in enumerate(cell_colors) for cell in cells_color diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 0403814b94..9f7583c1ab 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -46,8 +46,10 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} or Nothing end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation, - ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder, DetJdV}) where {T, FunDiffOrder, GeoDiffOrder, DetJdV} +function CellValues( + ::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation, + ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder, DetJdV} + ) where {T, FunDiffOrder, GeoDiffOrder, DetJdV} geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) @@ -56,10 +58,10 @@ function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo end CellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = CellValues(Float64, qr, ip, args...; kwargs...) -function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where T +function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where {T} return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo); kwargs...) end -function CellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation, ip_geo::VectorizedInterpolation = default_geometric_interpolation(ip); kwargs...) where T +function CellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation, ip_geo::VectorizedInterpolation = default_geometric_interpolation(ip); kwargs...) where {T} return CellValues(T, qr, ip, ip_geo, ValuesUpdateFlags(ip; kwargs...)) end @@ -95,6 +97,7 @@ getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds detJvec[q_point] = detJ * w + return end @inline _update_detJdV!(::Nothing, q_point, w, mapping) = nothing @@ -129,10 +132,11 @@ function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 GradT = shape_gradient_type(cv) sdim = GradT === nothing ? nothing : sdim_from_gradtype(GradT) - vstr = vdim==0 ? "scalar" : "vdim=$vdim" + vstr = vdim == 0 ? "scalar" : "vdim=$vdim" print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ") print(io, getnquadpoints(cv), " quadrature points") print(io, "\n Function interpolation: "); show(io, d, ip_fun) - print(io, "\nGeometric interpolation: "); + print(io, "\nGeometric interpolation: ") sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim) + return end diff --git a/src/FEValues/FacetValues.jl b/src/FEValues/FacetValues.jl index 84781ff469..43c9860cc3 100644 --- a/src/FEValues/FacetValues.jl +++ b/src/FEValues/FacetValues.jl @@ -36,7 +36,7 @@ values of nodal functions, gradients and divergences of nodal functions etc. on """ FacetValues -mutable struct FacetValues{FV, GM, FQR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: AbstractFacetValues +mutable struct FacetValues{FV, GM, FQR, detT, nT, V_FV <: AbstractVector{FV}, V_GM <: AbstractVector{GM}} <: AbstractFacetValues const fun_values::V_FV # AbstractVector{FunctionValues} const geo_mapping::V_GM # AbstractVector{GeometryMapping} const fqr::FQR # FacetQuadratureRule @@ -45,24 +45,26 @@ mutable struct FacetValues{FV, GM, FQR, detT, nT, V_FV<:AbstractVector{FV}, V_GM current_facet::Int end -function FacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}, - ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder}) where {T, sdim, FunDiffOrder, GeoDiffOrder} +function FacetValues( + ::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}, + ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder} + ) where {T, sdim, FunDiffOrder, GeoDiffOrder} # max(GeoDiffOrder, 1) ensures that we get the jacobian needed to calculate the normal. geo_mapping = map(qr -> GeometryMapping{max(GeoDiffOrder, 1)}(T, ip_geo.ip, qr), fqr.face_rules) fun_values = map(qr -> FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo), fqr.face_rules) max_nquadpoints = maximum(qr -> length(getweights(qr)), fqr.face_rules) # detJdV always calculated, since we needed to calculate the jacobian anyways for the normal. - detJdV = fill(T(NaN), max_nquadpoints) + detJdV = fill(T(NaN), max_nquadpoints) normals = fill(zero(Vec{sdim, T}) * T(NaN), max_nquadpoints) return FacetValues(fun_values, geo_mapping, fqr, detJdV, normals, 1) end FacetValues(qr::FacetQuadratureRule, ip::Interpolation, args...; kwargs...) = FacetValues(Float64, qr, ip, args...; kwargs...) -function FacetValues(::Type{T}, qr::FacetQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where T +function FacetValues(::Type{T}, qr::FacetQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where {T} return FacetValues(T, qr, ip, VectorizedInterpolation(ip_geo); kwargs...) end -function FacetValues(::Type{T}, qr::FacetQuadratureRule, ip::Interpolation, ip_geo::VectorizedInterpolation = default_geometric_interpolation(ip); kwargs...) where T +function FacetValues(::Type{T}, qr::FacetQuadratureRule, ip::Interpolation, ip_geo::VectorizedInterpolation = default_geometric_interpolation(ip); kwargs...) where {T} return FacetValues(T, qr, ip, ip_geo, ValuesUpdateFlags(ip; kwargs...)) end @@ -115,6 +117,7 @@ function set_current_facet!(fv::FacetValues, face_nr::Int) # when indexing by getcurrentfacet(fv) in other places! checkbounds(Bool, 1:nfacets(fv), face_nr) || throw(ArgumentError("Face index out of range.")) fv.current_facet = face_nr + return end @inline function reinit!(fv::FacetValues, x::AbstractVector, face_nr::Int) @@ -147,6 +150,7 @@ function reinit!(fv::FacetValues, cell::Union{AbstractCell, Nothing}, x::Abstrac @inbounds fv.normals[q_point] = weight_norm / norm(weight_norm) apply_mapping!(fun_values, q_point, mapping, cell) end + return end function Base.show(io::IO, d::MIME"text/plain", fv::FacetValues) @@ -155,17 +159,19 @@ function Base.show(io::IO, d::MIME"text/plain", fv::FacetValues) vdim = isa(shape_value(fv, 1, 1), Vec) ? length(shape_value(fv, 1, 1)) : 0 GradT = shape_gradient_type(fv) sdim = GradT === nothing ? nothing : sdim_from_gradtype(GradT) - vstr = vdim==0 ? "scalar" : "vdim=$vdim" + vstr = vdim == 0 ? "scalar" : "vdim=$vdim" print(io, "FacetValues(", vstr, ", rdim=$rdim, sdim=$sdim): ") nqp = getnquadpoints.(fv.fqr.face_rules) - if all(n==first(nqp) for n in nqp) + if all(n == first(nqp) for n in nqp) println(io, first(nqp), " quadrature points per face") else println(io, tuple(nqp...), " quadrature points on each face") end print(io, " Function interpolation: "); show(io, d, function_interpolation(fv)) - print(io, "\nGeometric interpolation: "); + print(io, "\nGeometric interpolation: ") + return sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim) sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim) + return end """ @@ -175,7 +181,7 @@ end for each dof-position determined by the `func_interpol`. Used mainly by the `ConstraintHandler`. """ mutable struct BCValues{T} - const M::Array{T,3} + const M::Array{T, 3} const nqp::Array{Int} current_entity::Int end @@ -183,14 +189,14 @@ end BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Type{<:BoundaryIndex} = FaceIndex) = BCValues(Float64, func_interpol, geom_interpol, boundary_type) -function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interpol::Interpolation{refshape}, boundary_type::Type{<:BoundaryIndex} = FaceIndex) where {T,dim,refshape <: AbstractRefShape{dim}} +function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interpol::Interpolation{refshape}, boundary_type::Type{<:BoundaryIndex} = FaceIndex) where {T, dim, refshape <: AbstractRefShape{dim}} # set up quadrature rules for each boundary entity with dof-positions # (determined by func_interpol) as the quadrature points interpolation_coords = reference_coordinates(func_interpol) - qrs = QuadratureRule{refshape,Vector{T},Vector{Vec{dim,T}}}[] + qrs = QuadratureRule{refshape, Vector{T}, Vector{Vec{dim, T}}}[] for boundarydofs in dirichlet_boundarydof_indices(boundary_type)(func_interpol) - dofcoords = Vec{dim,T}[] + dofcoords = Vec{dim, T}[] for boundarydof in boundarydofs push!(dofcoords, interpolation_coords[boundarydof]) end @@ -199,10 +205,10 @@ function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interp end n_boundary_entities = length(qrs) - n_qpoints = n_boundary_entities == 0 ? 0 : maximum(qr->length(getweights(qr)), qrs) # Bound number of qps correctly. + n_qpoints = n_boundary_entities == 0 ? 0 : maximum(qr -> length(getweights(qr)), qrs) # Bound number of qps correctly. n_geom_basefuncs = getnbasefunctions(geom_interpol) - M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints, n_boundary_entities) - nqp = zeros(Int,n_boundary_entities) + M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints, n_boundary_entities) + nqp = zeros(Int, n_boundary_entities) for n_boundary_entity in 1:n_boundary_entities for (qp, ξ) in pairs(qrs[n_boundary_entity].points) @@ -211,17 +217,17 @@ function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interp nqp[n_boundary_entity] = length(qrs[n_boundary_entity].points) end - BCValues{T}(M, nqp, 0) + return BCValues{T}(M, nqp, 0) end getnquadpoints(bcv::BCValues) = bcv.nqp[bcv.current_entity] -function spatial_coordinate(bcv::BCValues, q_point::Int, xh::AbstractVector{Vec{dim,T}}) where {dim,T} +function spatial_coordinate(bcv::BCValues, q_point::Int, xh::AbstractVector{Vec{dim, T}}) where {dim, T} n_base_funcs = size(bcv.M, 1) length(xh) == n_base_funcs || throw_incompatible_coord_length(length(xh), n_base_funcs) - x = zero(Vec{dim,T}) + x = zero(Vec{dim, T}) face = bcv.current_entity[] @inbounds for i in 1:n_base_funcs - x += bcv.M[i,q_point,face] * xh[i] # geometric_value(fe_v, q_point, i) * xh[i] + x += bcv.M[i, q_point, face] * xh[i] # geometric_value(fe_v, q_point, i) * xh[i] end return x end diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 67cdf5ad44..006b365789 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -5,34 +5,34 @@ # vdim = vector dimension (dimension of the field) # ################################################################# -# Scalar, sdim == rdim sdim rdim -typeof_N( ::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = T -typeof_dNdx( ::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Vec{dim, T} -typeof_dNdξ( ::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Vec{dim, T} -typeof_d2Ndx2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} -typeof_d2Ndξ2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} - -# Vector, vdim == sdim == rdim vdim sdim rdim -typeof_N( ::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Vec{dim, T} -typeof_dNdx( ::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} -typeof_dNdξ( ::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} -typeof_d2Ndx2(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Tensor{3, dim, T} -typeof_d2Ndξ2(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <: AbstractRefShape{dim}}) where {T, dim} = Tensor{3, dim, T} +# Scalar, sdim == rdim sdim rdim +typeof_N(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = T +typeof_dNdx(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Vec{dim, T} +typeof_dNdξ(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Vec{dim, T} +typeof_d2Ndx2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} +typeof_d2Ndξ2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} + +# Vector, vdim == sdim == rdim vdim sdim rdim +typeof_N(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Vec{dim, T} +typeof_dNdx(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} +typeof_dNdξ(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Tensor{2, dim, T} +typeof_d2Ndx2(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Tensor{3, dim, T} +typeof_d2Ndξ2(::Type{T}, ::VectorInterpolation{dim}, ::VectorizedInterpolation{dim, <:AbstractRefShape{dim}}) where {T, dim} = Tensor{3, dim, T} # Scalar, sdim != rdim (TODO: Use Vec if (s|r)dim <= 3?) -typeof_N( ::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, sdim, rdim} = T -typeof_dNdx(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, sdim, rdim} = SVector{sdim, T} -typeof_dNdξ(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, sdim, rdim} = SVector{rdim, T} -typeof_d2Ndx2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, sdim, rdim} = SMatrix{sdim, sdim, T, sdim*sdim} -typeof_d2Ndξ2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, sdim, rdim} = SMatrix{rdim, rdim, T, rdim*rdim} +typeof_N(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, sdim, rdim} = T +typeof_dNdx(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, sdim, rdim} = SVector{sdim, T} +typeof_dNdξ(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, sdim, rdim} = SVector{rdim, T} +typeof_d2Ndx2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, sdim, rdim} = SMatrix{sdim, sdim, T, sdim * sdim} +typeof_d2Ndξ2(::Type{T}, ::ScalarInterpolation, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, sdim, rdim} = SMatrix{rdim, rdim, T, rdim * rdim} # Vector, vdim != sdim != rdim (TODO: Use Vec/Tensor if (s|r)dim <= 3?) -typeof_N( ::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SVector{vdim, T} -typeof_dNdx(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SMatrix{vdim, sdim, T, vdim*sdim} -typeof_dNdξ(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SMatrix{vdim, rdim, T, vdim*rdim} -typeof_d2Ndx2(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SArray{Tuple{vdim, sdim, sdim}, T, 3, vdim*sdim*sdim} -typeof_d2Ndξ2(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <: AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SArray{Tuple{vdim, rdim, rdim}, T, 3, vdim*rdim*rdim} +typeof_N(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SVector{vdim, T} +typeof_dNdx(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SMatrix{vdim, sdim, T, vdim * sdim} +typeof_dNdξ(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SMatrix{vdim, rdim, T, vdim * rdim} +typeof_d2Ndx2(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SArray{Tuple{vdim, sdim, sdim}, T, 3, vdim * sdim * sdim} +typeof_d2Ndξ2(::Type{T}, ::VectorInterpolation{vdim}, ::VectorizedInterpolation{sdim, <:AbstractRefShape{rdim}}) where {T, vdim, sdim, rdim} = SArray{Tuple{vdim, rdim, rdim}, T, 3, vdim * rdim * rdim} """ @@ -51,13 +51,13 @@ struct FunctionValues{DiffOrder, IP, N_t, dNdx_t, dNdξ_t, d2Ndx2_t, d2Ndξ2_t} dNdξ::dNdξ_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} or Nothing d2Ndx2::d2Ndx2_t # ::AbstractMatrix{<:Tensor{2}} Hessians of geometric shape functions in ref-domain d2Ndξ2::d2Ndξ2_t # ::AbstractMatrix{<:Tensor{2}} Hessians of geometric shape functions in ref-domain - function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, ::Nothing, ::Nothing, ::Nothing, ::Nothing) where {N_t<:AbstractMatrix} + function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, ::Nothing, ::Nothing, ::Nothing, ::Nothing) where {N_t <: AbstractMatrix} return new{0, typeof(ip), N_t, Nothing, Nothing, Nothing, Nothing}(ip, Nx, Nξ, nothing, nothing, nothing, nothing) end - function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix, ::Nothing, ::Nothing) where {N_t<:AbstractMatrix} + function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix, ::Nothing, ::Nothing) where {N_t <: AbstractMatrix} return new{1, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ), Nothing, Nothing}(ip, Nx, Nξ, dNdx, dNdξ, nothing, nothing) end - function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix, d2Ndx2::AbstractMatrix, d2Ndξ2::AbstractMatrix) where {N_t<:AbstractMatrix} + function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix, d2Ndx2::AbstractMatrix, d2Ndξ2::AbstractMatrix) where {N_t <: AbstractMatrix} return new{2, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ), typeof(d2Ndx2), typeof(d2Ndξ2)}(ip, Nx, Nξ, dNdx, dNdξ, d2Ndx2, d2Ndξ2) end end @@ -70,12 +70,12 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR dNdξ = dNdx = d2Ndξ2 = d2Ndx2 = nothing if DiffOrder >= 1 - dNdξ = zeros(typeof_dNdξ(T, ip, ip_geo), n_shape, n_qpoints) + dNdξ = zeros(typeof_dNdξ(T, ip, ip_geo), n_shape, n_qpoints) dNdx = fill(zero(typeof_dNdx(T, ip, ip_geo)) * T(NaN), n_shape, n_qpoints) end if DiffOrder >= 2 - d2Ndξ2 = zeros(typeof_d2Ndξ2(T, ip, ip_geo), n_shape, n_qpoints) + d2Ndξ2 = zeros(typeof_d2Ndξ2(T, ip, ip_geo), n_shape, n_qpoints) d2Ndx2 = fill(zero(typeof_d2Ndx2(T, ip, ip_geo)) * T(NaN), n_shape, n_qpoints) end @@ -89,13 +89,13 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR end function precompute_values!(fv::FunctionValues{0}, qr_points::AbstractVector{<:Vec}) - reference_shape_values!(fv.Nξ, fv.ip, qr_points) + return reference_shape_values!(fv.Nξ, fv.ip, qr_points) end function precompute_values!(fv::FunctionValues{1}, qr_points::AbstractVector{<:Vec}) - reference_shape_gradients_and_values!(fv.dNdξ, fv.Nξ, fv.ip, qr_points) + return reference_shape_gradients_and_values!(fv.dNdξ, fv.Nξ, fv.ip, qr_points) end function precompute_values!(fv::FunctionValues{2}, qr_points::AbstractVector{<:Vec}) - reference_shape_hessians_gradients_and_values!(fv.d2Ndξ2, fv.dNdξ, fv.Nξ, fv.ip, qr_points) + return reference_shape_hessians_gradients_and_values!(fv.d2Ndξ2, fv.dNdξ, fv.Nξ, fv.ip, qr_points) end function Base.copy(v::FunctionValues) @@ -115,7 +115,7 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.Nx, 1) @propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) function_interpolation(funvals::FunctionValues) = funvals.ip -function_difforder(::FunctionValues{DiffOrder}) where DiffOrder = DiffOrder +function_difforder(::FunctionValues{DiffOrder}) where {DiffOrder} = DiffOrder shape_value_type(funvals::FunctionValues) = eltype(funvals.Nx) shape_gradient_type(funvals::FunctionValues) = eltype(funvals.dNdx) shape_gradient_type(::FunctionValues{0}) = nothing @@ -125,17 +125,18 @@ shape_hessian_type(::FunctionValues{1}) = nothing # Checks that the user provides the right dimension of coordinates to reinit! methods to ensure good error messages if not -sdim_from_gradtype(::Type{<:AbstractTensor{<:Any,sdim}}) where sdim = sdim -sdim_from_gradtype(::Type{<:SVector{sdim}}) where sdim = sdim -sdim_from_gradtype(::Type{<:SMatrix{<:Any,sdim}}) where sdim = sdim +sdim_from_gradtype(::Type{<:AbstractTensor{<:Any, sdim}}) where {sdim} = sdim +sdim_from_gradtype(::Type{<:SVector{sdim}}) where {sdim} = sdim +sdim_from_gradtype(::Type{<:SMatrix{<:Any, sdim}}) where {sdim} = sdim # For performance, these must be fully inferable for the compiler. # args: valname (:CellValues or :FacetValues), shape_gradient_type, eltype(x) function check_reinit_sdim_consistency(valname, gradtype::Type, ::Type{<:Vec{sdim}}) where {sdim} check_reinit_sdim_consistency(valname, Val(sdim_from_gradtype(gradtype)), Val(sdim)) + return end check_reinit_sdim_consistency(_, ::Nothing, ::Type{<:Vec}) = nothing # gradient not stored, cannot check -check_reinit_sdim_consistency(_, ::Val{sdim}, ::Val{sdim}) where sdim = nothing +check_reinit_sdim_consistency(_, ::Val{sdim}, ::Val{sdim}) where {sdim} = nothing function check_reinit_sdim_consistency(valname, ::Val{sdim_val}, ::Val{sdim_x}) where {sdim_val, sdim_x} throw(ArgumentError("The $valname (sdim=$sdim_val) and coordinates (sdim=$sdim_x) have different spatial dimensions.")) end @@ -157,7 +158,7 @@ Return the required order of geometric derivatives to map the function values and gradients from the reference cell to the physical cell geometry. """ -required_geo_diff_order(::IdentityMapping, fun_diff_order::Int) = fun_diff_order +required_geo_diff_order(::IdentityMapping, fun_diff_order::Int) = fun_diff_order #required_geo_diff_order(::ContravariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order # PR798 #required_geo_diff_order(::CovariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order # PR798 @@ -209,12 +210,12 @@ end @inbounds for j in 1:getnbasefunctions(funvals) dNdx = dothelper(funvals.dNdξ[j, q_point], Jinv) if is_vector_valued - d2Ndx2 = (funvals.d2Ndξ2[j, q_point] - dNdx⋅H) ⊡ Jinv_otimesu_Jinv + d2Ndx2 = (funvals.d2Ndξ2[j, q_point] - dNdx ⋅ H) ⊡ Jinv_otimesu_Jinv else - d2Ndx2 = Jinv'⋅(funvals.d2Ndξ2[j, q_point] - dNdx⋅H)⋅Jinv + d2Ndx2 = Jinv' ⋅ (funvals.d2Ndξ2[j, q_point] - dNdx ⋅ H) ⋅ Jinv end - funvals.dNdx[j, q_point] = dNdx + funvals.dNdx[j, q_point] = dNdx funvals.d2Ndx2[j, q_point] = d2Ndx2 end return nothing diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index b5cede1a58..85aed2d45a 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -14,7 +14,7 @@ struct MappingValues{JT, HT} end @inline getjacobian(mv::MappingValues{<:Union{AbstractTensor, SMatrix}}) = mv.J -@inline gethessian(mv::MappingValues{<:Any,<:AbstractTensor}) = mv.H +@inline gethessian(mv::MappingValues{<:Any, <:AbstractTensor}) = mv.H """ @@ -34,50 +34,51 @@ struct GeometryMapping{DiffOrder, IP, M_t, dMdξ_t, d2Mdξ2_t} ip::IP # ::Interpolation Geometric interpolation M::M_t # ::AbstractMatrix{<:Number} Values of geometric shape functions dMdξ::dMdξ_t # ::AbstractMatrix{<:Vec} Gradients of geometric shape functions in ref-domain - d2Mdξ2::d2Mdξ2_t # ::AbstractMatrix{<:Tensor{2}} Hessians of geometric shape functions in ref-domain - # ::Nothing (dMdξ or d2Mdξ2 if not required) + d2Mdξ2::d2Mdξ2_t # ::Union{AbstractMatrix{<:Tensor{2}}, Nothing} Hessians of geometric shape functions in ref-domain function GeometryMapping( - ip::IP, M::M_t, ::Nothing, ::Nothing - ) where {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}} + ip::IP, M::M_t, ::Nothing, ::Nothing + ) where {IP <: ScalarInterpolation, M_t <: AbstractMatrix{<:Number}} return new{0, IP, M_t, Nothing, Nothing}(ip, M, nothing, nothing) end function GeometryMapping( - ip::IP, M::M_t, dMdξ::dMdξ_t, ::Nothing - ) where {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}, dMdξ_t <: AbstractMatrix{<:Vec}} + ip::IP, M::M_t, dMdξ::dMdξ_t, ::Nothing + ) where {IP <: ScalarInterpolation, M_t <: AbstractMatrix{<:Number}, dMdξ_t <: AbstractMatrix{<:Vec}} return new{1, IP, M_t, dMdξ_t, Nothing}(ip, M, dMdξ, nothing) end function GeometryMapping( - ip::IP, M::M_t, dMdξ::dMdξ_t, d2Mdξ2::d2Mdξ2_t) where - {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}, - dMdξ_t <: AbstractMatrix{<:Vec}, d2Mdξ2_t <: AbstractMatrix{<:Tensor{2}}} + ip::IP, M::M_t, dMdξ::dMdξ_t, d2Mdξ2::d2Mdξ2_t + ) where { + IP <: ScalarInterpolation, M_t <: AbstractMatrix{<:Number}, + dMdξ_t <: AbstractMatrix{<:Vec}, d2Mdξ2_t <: AbstractMatrix{<:Tensor{2}}, + } return new{2, IP, M_t, dMdξ_t, d2Mdξ2_t}(ip, M, dMdξ, d2Mdξ2) end end -function GeometryMapping{0}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T +function GeometryMapping{0}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where {T} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) gm = GeometryMapping(ip, zeros(T, n_shape, n_qpoints), nothing, nothing) precompute_values!(gm, getpoints(qr)) return gm end -function GeometryMapping{1}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T +function GeometryMapping{1}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where {T} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - M = zeros(T, n_shape, n_qpoints) - dMdξ = zeros(Vec{getrefdim(ip),T}, n_shape, n_qpoints) + M = zeros(T, n_shape, n_qpoints) + dMdξ = zeros(Vec{getrefdim(ip), T}, n_shape, n_qpoints) gm = GeometryMapping(ip, M, dMdξ, nothing) precompute_values!(gm, getpoints(qr)) return gm end -function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T +function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where {T} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - M = zeros(T, n_shape, n_qpoints) - dMdξ = zeros(Vec{getrefdim(ip),T}, n_shape, n_qpoints) - d2Mdξ2 = zeros(Tensor{2,getrefdim(ip),T}, n_shape, n_qpoints) + M = zeros(T, n_shape, n_qpoints) + dMdξ = zeros(Vec{getrefdim(ip), T}, n_shape, n_qpoints) + d2Mdξ2 = zeros(Tensor{2, getrefdim(ip), T}, n_shape, n_qpoints) gm = GeometryMapping(ip, M, dMdξ, d2Mdξ2) precompute_values!(gm, getpoints(qr)) @@ -85,13 +86,13 @@ function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRu end function precompute_values!(gm::GeometryMapping{0}, qr_points::AbstractVector{<:Vec}) - reference_shape_values!(gm.M, gm.ip, qr_points) + return reference_shape_values!(gm.M, gm.ip, qr_points) end function precompute_values!(gm::GeometryMapping{1}, qr_points::AbstractVector{<:Vec}) - reference_shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr_points) + return reference_shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr_points) end function precompute_values!(gm::GeometryMapping{2}, qr_points::AbstractVector{<:Vec}) - reference_shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr_points) + return reference_shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr_points) end function Base.copy(v::GeometryMapping) @@ -104,21 +105,21 @@ geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip # Hot-fixes to support embedded elements before MixedTensors are available # See https://github.com/Ferrite-FEM/Tensors.jl/pull/188 -@inline otimes_helper(x::Vec{dim}, dMdξ::Vec{dim}) where dim = x ⊗ dMdξ +@inline otimes_helper(x::Vec{dim}, dMdξ::Vec{dim}) where {dim} = x ⊗ dMdξ @inline function otimes_helper(x::Vec{sdim}, dMdξ::Vec{rdim}) where {sdim, rdim} - SMatrix{sdim,rdim}((x[i]*dMdξ[j] for i in 1:sdim, j in 1:rdim)...) + return SMatrix{sdim, rdim}((x[i] * dMdξ[j] for i in 1:sdim, j in 1:rdim)...) end # End of embedded hot-fixes # For creating initial value -function otimes_returntype(#=typeof(x)=#::Type{<:Vec{sdim,Tx}}, #=typeof(dMdξ)=#::Type{<:Vec{rdim,TM}}) where {sdim,rdim,Tx,TM} - return SMatrix{sdim,rdim,promote_type(Tx,TM)} +function otimes_returntype(#=typeof(x)=# ::Type{<:Vec{sdim, Tx}}, #=typeof(dMdξ)=# ::Type{<:Vec{rdim, TM}}) where {sdim, rdim, Tx, TM} + return SMatrix{sdim, rdim, promote_type(Tx, TM)} end -function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(dMdξ)=#::Type{<:Vec{dim,TM}}) where {dim, Tx, TM} - return Tensor{2,dim,promote_type(Tx,TM)} +function otimes_returntype(#=typeof(x)=# ::Type{<:Vec{dim, Tx}}, #=typeof(dMdξ)=# ::Type{<:Vec{dim, TM}}) where {dim, Tx, TM} + return Tensor{2, dim, promote_type(Tx, TM)} end -function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2)=#::Type{<:Tensor{2,dim,TM}}) where {dim, Tx, TM} - return Tensor{3,dim,promote_type(Tx,TM)} +function otimes_returntype(#=typeof(x)=# ::Type{<:Vec{dim, Tx}}, #=typeof(d2Mdξ2)=# ::Type{<:Tensor{2, dim, TM}}) where {dim, Tx, TM} + return Tensor{3, dim, promote_type(Tx, TM)} end @inline function calculate_mapping(::GeometryMapping{0}, q_point::Int, x::AbstractVector{<:Vec}) @@ -150,11 +151,11 @@ end return MappingValues(nothing, nothing) end -@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim,T}, x::AbstractVector{<:Vec{sdim}}, ::Val{1}) where {T,rdim, sdim} +@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim, T}, x::AbstractVector{<:Vec{sdim}}, ::Val{1}) where {T, rdim, sdim} n_basefuncs = getnbasefunctions(gip) @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) - J = zero(otimes_returntype(Vec{sdim,T}, Vec{rdim,T})) + J = zero(otimes_returntype(Vec{sdim, T}, Vec{rdim, T})) @inbounds for j in 1:n_basefuncs dMdξ = reference_shape_gradient(gip, ξ, j) # J += x[j] ⊗ dMdξ # https://github.com/Ferrite-FEM/Tensors.jl/pull/188 @@ -163,11 +164,11 @@ end return MappingValues(J, nothing) end -@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim,T}, x::AbstractVector{<:Vec{sdim}}, ::Val{2}) where {T,rdim, sdim} +@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim, T}, x::AbstractVector{<:Vec{sdim}}, ::Val{2}) where {T, rdim, sdim} n_basefuncs = getnbasefunctions(gip) @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) (rdim != sdim) && error("hessian for embedded elements not implemented (rdim=$rdim, sdim=$sdim)") - J = zero(otimes_returntype(Vec{sdim,T}, Vec{rdim,T})) + J = zero(otimes_returntype(Vec{sdim, T}, Vec{rdim, T})) H = zero(otimes_returntype(eltype(x), typeof(J))) @inbounds for j in 1:n_basefuncs d2Mdξ2, dMdξ, _ = reference_shape_hessian_gradient_and_value(gip, ξ, j) @@ -180,11 +181,11 @@ end calculate_detJ(J::Tensor{2}) = det(J) calculate_detJ(J::SMatrix) = embedding_det(J) -function calculate_jacobian_and_spatial_coordinate(gip::ScalarInterpolation, ξ::Vec{rdim,Tξ}, x::AbstractVector{<:Vec{sdim, Tx}}) where {Tξ, Tx, rdim, sdim} +function calculate_jacobian_and_spatial_coordinate(gip::ScalarInterpolation, ξ::Vec{rdim, Tξ}, x::AbstractVector{<:Vec{sdim, Tx}}) where {Tξ, Tx, rdim, sdim} n_basefuncs = getnbasefunctions(gip) @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) - fecv_J = zero(otimes_returntype(Vec{sdim,Tx}, Vec{rdim,Tξ})) + fecv_J = zero(otimes_returntype(Vec{sdim, Tx}, Vec{rdim, Tξ})) sx = zero(Vec{sdim, Tx}) @inbounds for j in 1:n_basefuncs dMdξ, M = reference_shape_gradient_and_value(gip, ξ, j) @@ -210,7 +211,7 @@ where ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ is "detJ" and n is the unit normal. See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. For more details see e.g. the doctoral thesis by Mirza Cenanovic **Tangential Calculus** [Cenanovic2017](@cite). """ -embedding_det(J::SMatrix{3,2}) = norm(J[:,1] × J[:,2]) +embedding_det(J::SMatrix{3, 2}) = norm(J[:, 1] × J[:, 2]) """ embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) diff --git a/src/FEValues/InterfaceValues.jl b/src/FEValues/InterfaceValues.jl index 53c5ecb33a..8866811aa4 100644 --- a/src/FEValues/InterfaceValues.jl +++ b/src/FEValues/InterfaceValues.jl @@ -59,7 +59,7 @@ end function InterfaceValues( qr_here::FacetQuadratureRule, ip_here::Interpolation, ipg_here::Interpolation, qr_there::FacetQuadratureRule, ip_there::Interpolation, ipg_there::Interpolation - ) + ) # FacetValues constructor enforces that refshape matches for all arguments here = FacetValues(qr_here, ip_here, ipg_here) there = FacetValues(qr_there, ip_there, ipg_there) @@ -84,7 +84,7 @@ function InterfaceValues( end # From FacetValue(s) InterfaceValues(facetvalues_here::FVA, facetvalues_there::FVB = deepcopy(FacetValues_here)) where {FVA <: FacetValues, FVB <: FacetValues} = - InterfaceValues{FVA,FVB}(facetvalues_here, facetvalues_there) + InterfaceValues{FVA, FVB}(facetvalues_here, facetvalues_there) function Base.copy(iv::InterfaceValues) return InterfaceValues(copy(iv.here), copy(iv.there)) @@ -136,7 +136,7 @@ function reinit!( @assert length(quad_points_a) <= length(quad_points_b) # Re-evaluate shape functions in the transformed quadrature points - precompute_values!(get_fun_values(iv.there), quad_points_b) + precompute_values!(get_fun_values(iv.there), quad_points_b) precompute_values!(get_geo_mapping(iv.there), quad_points_b) # reinit! the "there" side @@ -151,7 +151,7 @@ Return the normal vector in the quadrature point `qp` on the interface. If `here (default) the outward normal to the "here" element is returned, otherwise the outward normal to the "there" element. """ -function getnormal(iv::InterfaceValues, qp::Int; here::Bool=true) +function getnormal(iv::InterfaceValues, qp::Int; here::Bool = true) # TODO: Remove the `here` kwarg and let user use `- getnormal(iv, qp)` instead? return getnormal(here ? iv.here : iv.there, qp) end @@ -238,10 +238,10 @@ multiply by minus the outward facing normal to the first element's side of the i """ function shape_gradient_jump end -for (func, f_, f_type) in ( - (:shape_value, :shape_value, :shape_value_type), - (:shape_gradient, :shape_gradient, :shape_gradient_type), -) +for (func, f_, f_type) in ( + (:shape_value, :shape_value, :shape_value_type), + (:shape_gradient, :shape_gradient, :shape_gradient_type), + ) @eval begin function $(func)(iv::InterfaceValues, qp::Int, i::Int; here::Bool) nbf = getnbasefunctions(iv) @@ -262,12 +262,12 @@ for (func, f_, f_type) in ( end end -for (func, f_, is_avg) in ( - (:shape_value_average, :shape_value, true), - (:shape_gradient_average, :shape_gradient, true), - (:shape_value_jump, :shape_value, false), - (:shape_gradient_jump, :shape_gradient, false), -) +for (func, f_, is_avg) in ( + (:shape_value_average, :shape_value, true), + (:shape_gradient_average, :shape_gradient, true), + (:shape_value_jump, :shape_value, false), + (:shape_gradient_jump, :shape_gradient, false), + ) @eval begin function $(func)(iv::InterfaceValues, qp::Int, i::Int) f_here = $(f_)(iv, qp, i; here = true) @@ -317,10 +317,7 @@ multiply by minus the outward facing normal to the first element's side of the i """ function function_gradient_jump end -for (func, ) in ( - (:function_value, ), - (:function_gradient, ), -) +for func in (:function_value, :function_gradient) @eval begin function $(func)( iv::InterfaceValues, q_point::Int, u::AbstractVector; @@ -352,12 +349,12 @@ for (func, ) in ( end end -for (func, f_, is_avg) in ( - (:function_value_average, :function_value, true ), - (:function_gradient_average, :function_gradient, true ), - (:function_value_jump, :function_value, false), - (:function_gradient_jump, :function_gradient, false), -) +for (func, f_, is_avg) in ( + (:function_value_average, :function_value, true), + (:function_gradient_average, :function_gradient, true), + (:function_value_jump, :function_value, false), + (:function_gradient_jump, :function_gradient, false), + ) @eval begin function $(func)(iv::InterfaceValues, qp::Int, u::AbstractVector) @boundscheck checkbounds(u, getnbasefunctions(iv)) @@ -430,7 +427,7 @@ function InterfaceOrientationInfo(cell_a::AbstractCell{RefShapeA}, cell_b::Abstr end function InterfaceOrientationInfo(_::AbstractCell{RefShapeA}, _::AbstractCell{RefShapeB}, _::Int, _::Int) where {RefShapeA <: AbstractRefShape{1}, RefShapeB <: AbstractRefShape{1}} - (error("1D elements don't use transformations for interfaces.")) + error("1D elements don't use transformations for interfaces.") end """ @@ -443,45 +440,45 @@ If the face is not flipped then the transformation is a function of relative ori """ get_transformation_matrix -function get_transformation_matrix(interface_transformation::InterfaceOrientationInfo{RefShapeA}) where RefShapeA <: AbstractRefShape{3} +function get_transformation_matrix(interface_transformation::InterfaceOrientationInfo{RefShapeA}) where {RefShapeA <: AbstractRefShape{3}} facet_a = interface_transformation.facet_a facenodes = reference_facets(RefShapeA)[facet_a] - _get_transformation_matrix(facenodes, interface_transformation) + return _get_transformation_matrix(facenodes, interface_transformation) end -@inline function _get_transformation_matrix(::NTuple{3,Int}, interface_transformation::InterfaceOrientationInfo) +@inline function _get_transformation_matrix(::NTuple{3, Int}, interface_transformation::InterfaceOrientationInfo) flipped = interface_transformation.flipped shift_index = interface_transformation.shift_index lowest_node_shift_index = interface_transformation.lowest_node_shift_index - θ = 2*shift_index/3 - θpre = 2*lowest_node_shift_index/3 + θ = 2 * shift_index / 3 + θpre = 2 * lowest_node_shift_index / 3 - flipping = SMatrix{3,3}(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0) + flipping = SMatrix{3, 3}(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0) - translate_1 = SMatrix{3,3}(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -sinpi(2/3)/3, -0.5, 1.0) - stretch_1 = SMatrix{3,3}(sinpi(2/3), 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) + translate_1 = SMatrix{3, 3}(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -sinpi(2 / 3) / 3, -0.5, 1.0) + stretch_1 = SMatrix{3, 3}(sinpi(2 / 3), 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) - translate_2 = SMatrix{3,3}(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, sinpi(2/3)/3, 0.5, 1.0) - stretch_2 = SMatrix{3,3}(1/sinpi(2/3), -1/2/sinpi(2/3), 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) + translate_2 = SMatrix{3, 3}(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, sinpi(2 / 3) / 3, 0.5, 1.0) + stretch_2 = SMatrix{3, 3}(1 / sinpi(2 / 3), -1 / 2 / sinpi(2 / 3), 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) - return flipped ? stretch_2 * translate_2 * rotation_tensor(0,0,θpre*pi) * flipping * rotation_tensor(0,0,(θ - θpre)*pi) * translate_1 * stretch_1 : - stretch_2 * translate_2 * rotation_tensor(0,0,θ*pi) * translate_1 * stretch_1 + return flipped ? stretch_2 * translate_2 * rotation_tensor(0, 0, θpre * pi) * flipping * rotation_tensor(0, 0, (θ - θpre) * pi) * translate_1 * stretch_1 : + stretch_2 * translate_2 * rotation_tensor(0, 0, θ * pi) * translate_1 * stretch_1 end -@inline function _get_transformation_matrix(::NTuple{4,Int}, interface_transformation::InterfaceOrientationInfo) +@inline function _get_transformation_matrix(::NTuple{4, Int}, interface_transformation::InterfaceOrientationInfo) flipped = interface_transformation.flipped shift_index = interface_transformation.shift_index lowest_node_shift_index = interface_transformation.lowest_node_shift_index - θ = 2*shift_index/4 - θpre = 2*lowest_node_shift_index/4 + θ = 2 * shift_index / 4 + θpre = 2 * lowest_node_shift_index / 4 - flipping = SMatrix{3,3}(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0) - return flipped ? rotation_tensor(0,0,θpre*pi) * flipping * rotation_tensor(0,0,(θ - θpre)*pi) : rotation_tensor(0,0,θ*pi) + flipping = SMatrix{3, 3}(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0) + return flipped ? rotation_tensor(0, 0, θpre * pi) * flipping * rotation_tensor(0, 0, (θ - θpre) * pi) : rotation_tensor(0, 0, θ * pi) end -@inline function _get_transformation_matrix(::NTuple{N,Int}, ::InterfaceOrientationInfo) where N +@inline function _get_transformation_matrix(::NTuple{N, Int}, ::InterfaceOrientationInfo) where {N} throw(ArgumentError("transformation is not implemented")) end @@ -556,8 +553,8 @@ function transform_interface_points!(dst::AbstractVector{Vec{3, Float64}}, point M = get_transformation_matrix(interface_transformation) for (idx, point) in pairs(points) face_point = element_to_facet_transformation(point, RefShapeA, facet_a) - result = M * Vec(face_point[1],face_point[2], 1.0) - dst[idx] = facet_to_element_transformation(Vec(result[1],result[2]), RefShapeB, facet_b) + result = M * Vec(face_point[1], face_point[2], 1.0) + dst[idx] = facet_to_element_transformation(Vec(result[1], result[2]), RefShapeB, facet_b) end return nothing end @@ -578,8 +575,9 @@ end function Base.show(io::IO, m::MIME"text/plain", iv::InterfaceValues) println(io, "InterfaceValues with") print(io, "{Here} ") - show(io,m,iv.here) + show(io, m, iv.here) println(io) print(io, "{There} ") - show(io,m,iv.there) + show(io, m, iv.there) + return end diff --git a/src/FEValues/PointValues.jl b/src/FEValues/PointValues.jl index 8168ff73a6..84645697d8 100644 --- a/src/FEValues/PointValues.jl +++ b/src/FEValues/PointValues.jl @@ -29,17 +29,17 @@ function PointValues(cv::CellValues) ip_fun = function_interpolation(cv) ip_geo = geometric_interpolation(cv) update_gradients = Val(function_difforder(cv) ≥ 1) - update_hessians = Val(function_difforder(cv) ≥ 2) + update_hessians = Val(function_difforder(cv) ≥ 2) return PointValues(T, ip_fun, ip_geo; update_gradients, update_hessians) end function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip); kwargs...) return PointValues(Float64, ip, ipg; kwargs...) end function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolation(ip); kwargs...) where { - T, dim, shape <: AbstractRefShape{dim}, - IP <: Interpolation{shape}, - GIP <: Interpolation{shape} -} + T, dim, shape <: AbstractRefShape{dim}, + IP <: Interpolation{shape}, + GIP <: Interpolation{shape}, + } qr = QuadratureRule{shape}([one(T)], [zero(Vec{dim, T})]) cv = CellValues(T, qr, ip, ipg; update_detJdV = Val(false), kwargs...) return PointValues{typeof(cv)}(cv) @@ -78,4 +78,5 @@ end function Base.show(io::IO, d::MIME"text/plain", cv::PointValues) println(io, "PointValues containing a") show(io, d, cv.cv) + return end diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 3b6ae37cc7..a2cd905bbe 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -10,19 +10,17 @@ function checkquadpoint(fe_v::AbstractValues, qp::Int) end @noinline function throw_incompatible_dof_length(length_ue, n_base_funcs) - throw(ArgumentError( - "the number of base functions ($(n_base_funcs)) does not match the length " * + msg = "the number of base functions ($(n_base_funcs)) does not match the length " * "of the vector ($(length_ue)). Perhaps you passed the global vector, " * "or forgot to pass a dof_range?" - )) + throw(ArgumentError(msg)) end @noinline function throw_incompatible_coord_length(length_x, n_base_funcs) - throw(ArgumentError( - "the number of (geometric) base functions ($(n_base_funcs)) does not match " * + msg = "the number of (geometric) base functions ($(n_base_funcs)) does not match " * "the number of coordinates in the vector ($(length_x)). Perhaps you forgot to " * "use an appropriate geometric interpolation when creating FE values? See " * "https://github.com/Ferrite-FEM/Ferrite.jl/issues/265 for more details." - )) + throw(ArgumentError(msg)) end """ @@ -37,8 +35,9 @@ function ValuesUpdateFlags(ip_fun::Interpolation; update_gradients = Val(true), toval(V::Val) = V return ValuesUpdateFlags(ip_fun, toval(update_gradients), toval(update_hessians), toval(update_detJdV)) end -function ValuesUpdateFlags(ip_fun::Interpolation, ::Val{update_gradients}, ::Val{update_hessians}, ::Val{update_detJdV} - ) where {update_gradients, update_hessians, update_detJdV} +function ValuesUpdateFlags( + ip_fun::Interpolation, ::Val{update_gradients}, ::Val{update_hessians}, ::Val{update_detJdV} + ) where {update_gradients, update_hessians, update_detJdV} FunDiffOrder = update_hessians ? 2 : (update_gradients ? 1 : 0) GeoDiffOrder = max(required_geo_diff_order(mapping_type(ip_fun), FunDiffOrder), update_detJdV) return ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder, update_detJdV}() @@ -133,8 +132,8 @@ quadrature point `q_point`. function shape_curl(cv::AbstractValues, q_point::Int, base_func::Int) return curl_from_gradient(shape_gradient(cv, q_point, base_func)) end -curl_from_gradient(∇v::SecondOrderTensor{3}) = Vec{3}((∇v[3,2] - ∇v[2,3], ∇v[1,3] - ∇v[3,1], ∇v[2,1] - ∇v[1,2])) -curl_from_gradient(∇v::SecondOrderTensor{2}) = Vec{1}((∇v[2,1] - ∇v[1,2],)) # Alternatively define as Vec{3}((0,0,v)) +curl_from_gradient(∇v::SecondOrderTensor{3}) = Vec{3}((∇v[3, 2] - ∇v[2, 3], ∇v[1, 3] - ∇v[3, 1], ∇v[2, 1] - ∇v[1, 2])) +curl_from_gradient(∇v::SecondOrderTensor{2}) = Vec{1}((∇v[2, 1] - ∇v[1, 2],)) # Alternatively define as Vec{3}((0, 0, v)) """ function_value(fe_v::AbstractValues, q_point::Int, u::AbstractVector, [dof_range]) @@ -372,16 +371,19 @@ function reference_shape_values!(values::AbstractMatrix, ip, qr_points::Abstract for (qp, ξ) in pairs(qr_points) reference_shape_values!(@view(values[:, qp]), ip, ξ) end + return end function reference_shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::AbstractVector{<:Vec}) for (qp, ξ) in pairs(qr_points) reference_shape_gradients_and_values!(@view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end + return end function reference_shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::AbstractVector{<:Vec}) for (qp, ξ) in pairs(qr_points) reference_shape_hessians_gradients_and_values!(@view(hessians[:, qp]), @view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end + return end diff --git a/src/FEValues/face_integrals.jl b/src/FEValues/face_integrals.jl index d2d26bc6bc..2fbd0506d9 100644 --- a/src/FEValues/face_integrals.jl +++ b/src/FEValues/face_integrals.jl @@ -37,7 +37,7 @@ cell has facets of different shapes (i.e. quadrilaterals and triangles) then eac facets indices, weights and points are passed separately. """ function create_facet_quad_rule(::Type{RefShape}, w::AbstractVector{T}, p::AbstractVector{Vec{N, T}}) where {N, T, RefShape <: AbstractRefShape} - facet_quad_rule = QuadratureRule{RefShape, Vector{T}, Vector{Vec{N+1, T}}}[] + facet_quad_rule = QuadratureRule{RefShape, Vector{T}, Vector{Vec{N + 1, T}}}[] for facet in 1:nfacets(RefShape) new_points = [facet_to_element_transformation(p[i], RefShape, facet) for i in 1:length(w)] push!(facet_quad_rule, QuadratureRule{RefShape}(copy(w), new_points)) @@ -47,11 +47,11 @@ end # For cells with mixed faces function create_facet_quad_rule( - ::Type{RefShape}, - quad_facets::AbstractVector{Int}, w_quad::AbstractVector{T}, p_quad::AbstractVector{Vec{N, T}}, - tri_facets::AbstractVector{Int}, w_tri::AbstractVector{T}, p_tri::AbstractVector{Vec{N, T}} -) where {N, T, RefShape <: Union{RefPrism, RefPyramid}} - facet_quad_rule = Vector{QuadratureRule{RefShape, Vector{T}, Vector{Vec{N+1, T}}}}(undef, nfacets(RefShape)) + ::Type{RefShape}, + quad_facets::AbstractVector{Int}, w_quad::AbstractVector{T}, p_quad::AbstractVector{Vec{N, T}}, + tri_facets::AbstractVector{Int}, w_tri::AbstractVector{T}, p_tri::AbstractVector{Vec{N, T}} + ) where {N, T, RefShape <: Union{RefPrism, RefPyramid}} + facet_quad_rule = Vector{QuadratureRule{RefShape, Vector{T}, Vector{Vec{N + 1, T}}}}(undef, nfacets(RefShape)) for facet in quad_facets new_points = [facet_to_element_transformation(p_quad[i], RefShape, facet) for i in 1:length(w_quad)] facet_quad_rule[facet] = QuadratureRule{RefShape}(copy(w_quad), new_points) @@ -68,23 +68,23 @@ end ################## # Mapping from to 0D node to 1D line vertex. -function facet_to_element_transformation(::Union{Vec{0, T},Vec{1, T}}, ::Type{RefLine}, face::Int) where {T} - face == 1 && return Vec{1, T}(( -one(T),)) - face == 2 && return Vec{1, T}(( one(T),)) +function facet_to_element_transformation(::Union{Vec{0, T}, Vec{1, T}}, ::Type{RefLine}, face::Int) where {T} + face == 1 && return Vec{1, T}((-one(T),)) + face == 2 && return Vec{1, T}((one(T),)) throw(ArgumentError("unknown facet number")) end # Mapping from 1D line to point. -function element_to_facet_transformation(point::Vec{1, T}, ::Type{RefLine}, face::Int) where T +function element_to_facet_transformation(point::Vec{1, T}, ::Type{RefLine}, face::Int) where {T} x = point[] face == 1 && return Vec(-x) - face == 2 && return Vec( x) + face == 2 && return Vec(x) throw(ArgumentError("unknown facet number")) end -function weighted_normal(::Tensor{2,1,T}, ::Type{RefLine}, face::Int) where {T} - face == 1 && return Vec{1,T}((-one(T),)) - face == 2 && return Vec{1,T}(( one(T),)) +function weighted_normal(::Tensor{2, 1, T}, ::Type{RefLine}, face::Int) where {T} + face == 1 && return Vec{1, T}((-one(T),)) + face == 2 && return Vec{1, T}((one(T),)) throw(ArgumentError("unknown facet number")) end @@ -93,31 +93,31 @@ end ########################### # Mapping from 1D line to 2D face of a quadrilateral. -function facet_to_element_transformation(point::Vec{1, T}, ::Type{RefQuadrilateral}, face::Int) where T +function facet_to_element_transformation(point::Vec{1, T}, ::Type{RefQuadrilateral}, face::Int) where {T} x = point[1] - face == 1 && return Vec{2, T}(( x, -one(T))) - face == 2 && return Vec{2, T}(( one(T), x)) - face == 3 && return Vec{2, T}(( -x, one(T))) - face == 4 && return Vec{2, T}(( -one(T), -x)) + face == 1 && return Vec{2, T}((x, -one(T))) + face == 2 && return Vec{2, T}((one(T), x)) + face == 3 && return Vec{2, T}((-x, one(T))) + face == 4 && return Vec{2, T}((-one(T), -x)) throw(ArgumentError("unknown facet number")) end # Mapping from 2D face of a quadrilateral to 1D line. -function element_to_facet_transformation(point::Vec{2, T}, ::Type{RefQuadrilateral}, face::Int) where T +function element_to_facet_transformation(point::Vec{2, T}, ::Type{RefQuadrilateral}, face::Int) where {T} x, y = point - face == 1 && return Vec( x) - face == 2 && return Vec( y) - face == 3 && return Vec( -x) - face == 4 && return Vec( -y) + face == 1 && return Vec(x) + face == 2 && return Vec(y) + face == 3 && return Vec(-x) + face == 4 && return Vec(-y) throw(ArgumentError("unknown facet number")) end -function weighted_normal(J::Tensor{2,2}, ::Type{RefQuadrilateral}, face::Int) +function weighted_normal(J::Tensor{2, 2}, ::Type{RefQuadrilateral}, face::Int) @inbounds begin - face == 1 && return Vec{2}(( J[2,1], -J[1,1])) - face == 2 && return Vec{2}(( J[2,2], -J[1,2])) - face == 3 && return Vec{2}((-J[2,1], J[1,1])) - face == 4 && return Vec{2}((-J[2,2], J[1,2])) + face == 1 && return Vec{2}((J[2, 1], -J[1, 1])) + face == 2 && return Vec{2}((J[2, 2], -J[1, 2])) + face == 3 && return Vec{2}((-J[2, 1], J[1, 1])) + face == 4 && return Vec{2}((-J[2, 2], J[1, 2])) end throw(ArgumentError("unknown facet number")) end @@ -127,28 +127,28 @@ end ###################### # Mapping from 1D line to 2D face of a triangle. -function facet_to_element_transformation(point::Vec{1, T}, ::Type{RefTriangle}, face::Int) where T +function facet_to_element_transformation(point::Vec{1, T}, ::Type{RefTriangle}, face::Int) where {T} x = (point[1] + one(T)) / 2 - face == 1 && return Vec{2, T}(( one(T) - x, x )) - face == 2 && return Vec{2, T}(( zero(T), one(T) -x)) - face == 3 && return Vec{2, T}(( x, zero(T))) + face == 1 && return Vec{2, T}((one(T) - x, x)) + face == 2 && return Vec{2, T}((zero(T), one(T) - x)) + face == 3 && return Vec{2, T}((x, zero(T))) throw(ArgumentError("unknown facet number")) end # Mapping from 2D face of a triangle to 1D line. -function element_to_facet_transformation(point::Vec{2, T}, ::Type{RefTriangle}, face::Int) where T +function element_to_facet_transformation(point::Vec{2, T}, ::Type{RefTriangle}, face::Int) where {T} x, y = point - face == 1 && return Vec( one(T) - x * 2) - face == 2 && return Vec( one(T) - y * 2 ) - face == 3 && return Vec( x * 2 - one(T)) + face == 1 && return Vec(one(T) - x * 2) + face == 2 && return Vec(one(T) - y * 2) + face == 3 && return Vec(x * 2 - one(T)) throw(ArgumentError("unknown facet number")) end -function weighted_normal(J::Tensor{2,2}, ::Type{RefTriangle}, face::Int) +function weighted_normal(J::Tensor{2, 2}, ::Type{RefTriangle}, face::Int) @inbounds begin - face == 1 && return Vec{2}((-(J[2,1] - J[2,2]), J[1,1] - J[1,2])) - face == 2 && return Vec{2}((-J[2,2], J[1,2])) - face == 3 && return Vec{2}((J[2,1], -J[1,1])) + face == 1 && return Vec{2}((-(J[2, 1] - J[2, 2]), J[1, 1] - J[1, 2])) + face == 2 && return Vec{2}((-J[2, 2], J[1, 2])) + face == 3 && return Vec{2}((J[2, 1], -J[1, 1])) end throw(ArgumentError("unknown facet number")) end @@ -158,37 +158,37 @@ end ######################## # Mapping from 2D quadrilateral to 3D face of a hexahedron. -function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefHexahedron}, face::Int) where T +function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefHexahedron}, face::Int) where {T} x, y = point - face == 1 && return Vec{3, T}(( y, x, -one(T))) - face == 2 && return Vec{3, T}(( x, -one(T), y)) - face == 3 && return Vec{3, T}(( one(T), x, y)) - face == 4 && return Vec{3, T}(( -x, one(T), y)) - face == 5 && return Vec{3, T}((-one(T), y, x)) - face == 6 && return Vec{3, T}(( x, y, one(T))) + face == 1 && return Vec{3, T}((y, x, -one(T))) + face == 2 && return Vec{3, T}((x, -one(T), y)) + face == 3 && return Vec{3, T}((one(T), x, y)) + face == 4 && return Vec{3, T}((-x, one(T), y)) + face == 5 && return Vec{3, T}((-one(T), y, x)) + face == 6 && return Vec{3, T}((x, y, one(T))) throw(ArgumentError("unknown facet number")) end # Mapping from 3D face of a hexahedron to 2D quadrilateral. -function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefHexahedron}, face::Int) where T +function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefHexahedron}, face::Int) where {T} x, y, z = point - face == 1 && return Vec( y, x) - face == 2 && return Vec( x, z) - face == 3 && return Vec( y, z) - face == 4 && return Vec( -x, z) - face == 5 && return Vec( z, y) - face == 6 && return Vec( x, y) + face == 1 && return Vec(y, x) + face == 2 && return Vec(x, z) + face == 3 && return Vec(y, z) + face == 4 && return Vec(-x, z) + face == 5 && return Vec(z, y) + face == 6 && return Vec(x, y) throw(ArgumentError("unknown facet number")) end -function weighted_normal(J::Tensor{2,3}, ::Type{RefHexahedron}, face::Int) +function weighted_normal(J::Tensor{2, 3}, ::Type{RefHexahedron}, face::Int) @inbounds begin - face == 1 && return J[:,2] × J[:,1] - face == 2 && return J[:,1] × J[:,3] - face == 3 && return J[:,2] × J[:,3] - face == 4 && return J[:,3] × J[:,1] - face == 5 && return J[:,3] × J[:,2] - face == 6 && return J[:,1] × J[:,2] + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return J[:, 2] × J[:, 3] + face == 4 && return J[:, 3] × J[:, 1] + face == 5 && return J[:, 3] × J[:, 2] + face == 6 && return J[:, 1] × J[:, 2] end throw(ArgumentError("unknown facet number")) end @@ -198,31 +198,31 @@ end ######################### # Mapping from 2D triangle to 3D face of a tetrahedon. -function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefTetrahedron}, face::Int) where T +function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefTetrahedron}, face::Int) where {T} x, y = point - face == 1 && return Vec{3, T}( (one(T)-x-y, y, zero(T))) - face == 2 && return Vec{3, T}( (y, zero(T), one(T)-x-y)) - face == 3 && return Vec{3, T}( (x, y, one(T)-x-y)) - face == 4 && return Vec{3, T}( (zero(T), one(T)-x-y, y)) + face == 1 && return Vec{3, T}((one(T) - x - y, y, zero(T))) + face == 2 && return Vec{3, T}((y, zero(T), one(T) - x - y)) + face == 3 && return Vec{3, T}((x, y, one(T) - x - y)) + face == 4 && return Vec{3, T}((zero(T), one(T) - x - y, y)) throw(ArgumentError("unknown facet number")) end # Mapping from 3D face of a tetrahedon to 2D triangle. -function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefTetrahedron}, face::Int) where T +function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefTetrahedron}, face::Int) where {T} x, y, z = point - face == 1 && return Vec( one(T)-x-y, y) - face == 2 && return Vec( one(T)-z-x, x) - face == 3 && return Vec( x, y) - face == 4 && return Vec( one(T)-y-z, z) + face == 1 && return Vec(one(T) - x - y, y) + face == 2 && return Vec(one(T) - z - x, x) + face == 3 && return Vec(x, y) + face == 4 && return Vec(one(T) - y - z, z) throw(ArgumentError("unknown facet number")) end -function weighted_normal(J::Tensor{2,3}, ::Type{RefTetrahedron}, face::Int) +function weighted_normal(J::Tensor{2, 3}, ::Type{RefTetrahedron}, face::Int) @inbounds begin - face == 1 && return J[:,2] × J[:,1] - face == 2 && return J[:,1] × J[:,3] - face == 3 && return (J[:,1]-J[:,3]) × (J[:,2]-J[:,3]) - face == 4 && return J[:,3] × J[:,2] + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return (J[:, 1] - J[:, 3]) × (J[:, 2] - J[:, 3]) + face == 4 && return J[:, 3] × J[:, 2] end throw(ArgumentError("unknown facet number")) end @@ -232,35 +232,35 @@ end ################### # Mapping from 2D quadrilateral/triangle to 3D face of a wedge. -function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefPrism}, face::Int) where T +function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefPrism}, face::Int) where {T} # Note that for quadrilaterals the domain is [-1, 1]² but for triangles it is [0, 1]² x, y = point - face == 1 && return Vec{3, T}(( one(T)-x-y, y, zero(T))) - face == 2 && return Vec{3, T}(( (one(T)+x)/2, zero(T), (one(T)+y)/2)) - face == 3 && return Vec{3, T}(( zero(T), one(T)-(one(T)+x)/2, (one(T)+y)/2)) - face == 4 && return Vec{3, T}(( one(T)-(one(T)+x)/2, (one(T)+x)/2, (one(T)+y)/2)) - face == 5 && return Vec{3, T}(( y, one(T)-x-y, one(T))) + face == 1 && return Vec{3, T}((one(T) - x - y, y, zero(T))) + face == 2 && return Vec{3, T}(((one(T) + x) / 2, zero(T), (one(T) + y) / 2)) + face == 3 && return Vec{3, T}((zero(T), one(T) - (one(T) + x) / 2, (one(T) + y) / 2)) + face == 4 && return Vec{3, T}((one(T) - (one(T) + x) / 2, (one(T) + x) / 2, (one(T) + y) / 2)) + face == 5 && return Vec{3, T}((y, one(T) - x - y, one(T))) throw(ArgumentError("unknown facet number")) end # Mapping from 3D face of a wedge to 2D triangle or 2D quadrilateral. -function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefPrism}, face::Int) where T +function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefPrism}, face::Int) where {T} x, y, z = point - face == 1 && return Vec( one(T)-x-y, y) - face == 2 && return Vec( 2*x - one(T), 2*z - one(T) ) - face == 3 && return Vec( 2*(one(T) - y) - one(T), 2*z - one(T) ) - face == 4 && return Vec( 2*y - one(T), 2*z - one(T) ) - face == 5 && return Vec( one(T) - x - y, x) + face == 1 && return Vec(one(T) - x - y, y) + face == 2 && return Vec(2 * x - one(T), 2 * z - one(T)) + face == 3 && return Vec(2 * (one(T) - y) - one(T), 2 * z - one(T)) + face == 4 && return Vec(2 * y - one(T), 2 * z - one(T)) + face == 5 && return Vec(one(T) - x - y, x) throw(ArgumentError("unknown facet number")) end -function weighted_normal(J::Tensor{2,3}, ::Type{RefPrism}, face::Int) +function weighted_normal(J::Tensor{2, 3}, ::Type{RefPrism}, face::Int) @inbounds begin - face == 1 && return J[:,2] × J[:,1] - face == 2 && return J[:,1] × J[:,3] - face == 3 && return J[:,3] × J[:,2] - face == 4 && return (J[:,2]-J[:,1]) × J[:,3] - face == 5 && return J[:,1] × J[:,2] + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return J[:, 3] × J[:, 2] + face == 4 && return (J[:, 2] - J[:, 1]) × J[:, 3] + face == 5 && return J[:, 1] × J[:, 2] end throw(ArgumentError("unknown facet number")) end @@ -270,34 +270,34 @@ end ##################### # Mapping from 2D face to 3D face of a pyramid. -function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefPyramid}, face::Int) where T +function facet_to_element_transformation(point::Vec{2, T}, ::Type{RefPyramid}, face::Int) where {T} x, y = point - face == 1 && return Vec{3, T}(( (y+one(T))/2, (x+one(T))/2, zero(T))) - face == 2 && return Vec{3, T}(( y, zero(T), one(T)-x-y)) - face == 3 && return Vec{3, T}(( zero(T), one(T)-x-y, y)) - face == 4 && return Vec{3, T}(( x+y, y, one(T)-x-y)) - face == 5 && return Vec{3, T}(( one(T)-x-y, one(T)-y, y)) + face == 1 && return Vec{3, T}(((y + one(T)) / 2, (x + one(T)) / 2, zero(T))) + face == 2 && return Vec{3, T}((y, zero(T), one(T) - x - y)) + face == 3 && return Vec{3, T}((zero(T), one(T) - x - y, y)) + face == 4 && return Vec{3, T}((x + y, y, one(T) - x - y)) + face == 5 && return Vec{3, T}((one(T) - x - y, one(T) - y, y)) throw(ArgumentError("unknown facet number")) end # Mapping from 3D face of a pyramid to 2D triangle or 2D quadrilateral. -function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefPyramid}, face::Int) where T +function element_to_facet_transformation(point::Vec{3, T}, ::Type{RefPyramid}, face::Int) where {T} x, y, z = point - face == 1 && return Vec( 2*y - one(T), 2*x - one(T)) - face == 2 && return Vec( one(T) - z - x, x) - face == 3 && return Vec( one(T) - y - z, z) - face == 4 && return Vec( x - y, y) - face == 5 && return Vec( one(T) - x - z, z) + face == 1 && return Vec(2 * y - one(T), 2 * x - one(T)) + face == 2 && return Vec(one(T) - z - x, x) + face == 3 && return Vec(one(T) - y - z, z) + face == 4 && return Vec(x - y, y) + face == 5 && return Vec(one(T) - x - z, z) throw(ArgumentError("unknown facet number")) end -function weighted_normal(J::Tensor{2,3}, ::Type{RefPyramid}, face::Int) +function weighted_normal(J::Tensor{2, 3}, ::Type{RefPyramid}, face::Int) @inbounds begin - face == 1 && return J[:,2] × J[:,1] - face == 2 && return J[:,1] × J[:,3] - face == 3 && return J[:,3] × J[:,2] - face == 4 && return J[:,2] × (J[:,3]-J[:,1]) - face == 5 && return (J[:,3]-J[:,2]) × J[:,1] + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return J[:, 3] × J[:, 2] + face == 4 && return J[:, 2] × (J[:, 3] - J[:, 1]) + face == 5 && return (J[:, 3] - J[:, 2]) × J[:, 1] end throw(ArgumentError("unknown facet number")) end diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 9c0be69311..c3eec0b3ce 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -44,14 +44,14 @@ abstract type AbstractRefShape{refdim} end # See src/docs.jl for detailed documentation struct RefHypercube{refdim} <: AbstractRefShape{refdim} end -struct RefSimplex{refdim} <: AbstractRefShape{refdim} end -const RefLine = RefHypercube{1} +struct RefSimplex{refdim} <: AbstractRefShape{refdim} end +const RefLine = RefHypercube{1} const RefQuadrilateral = RefHypercube{2} -const RefHexahedron = RefHypercube{3} -const RefTriangle = RefSimplex{2} -const RefTetrahedron = RefSimplex{3} -struct RefPrism <: AbstractRefShape{3} end -struct RefPyramid <: AbstractRefShape{3} end +const RefHexahedron = RefHypercube{3} +const RefTriangle = RefSimplex{2} +const RefTetrahedron = RefSimplex{3} +struct RefPrism <: AbstractRefShape{3} end +struct RefPyramid <: AbstractRefShape{3} end """ Ferrite.getrefdim(RefShape::Type{<:AbstractRefShape}) @@ -59,7 +59,7 @@ struct RefPyramid <: AbstractRefShape{3} end Get the dimension of the reference shape """ getrefdim(::Type{<:AbstractRefShape}) # To get correct doc filtering -getrefdim(::Type{<:AbstractRefShape{rdim}}) where rdim = rdim +getrefdim(::Type{<:AbstractRefShape{rdim}}) where {rdim} = rdim abstract type AbstractCell{refshape <: AbstractRefShape} end @@ -83,28 +83,28 @@ end A `FaceIndex` wraps an (Int, Int) and defines a local face by pointing to a (cell, face). """ struct FaceIndex <: BoundaryIndex - idx::Tuple{Int,Int} # cell and side + idx::Tuple{Int, Int} # cell and side end """ A `EdgeIndex` wraps an (Int, Int) and defines a local edge by pointing to a (cell, edge). """ struct EdgeIndex <: BoundaryIndex - idx::Tuple{Int,Int} # cell and side + idx::Tuple{Int, Int} # cell and side end """ A `VertexIndex` wraps an (Int, Int) and defines a local vertex by pointing to a (cell, vert). """ struct VertexIndex <: BoundaryIndex - idx::Tuple{Int,Int} # cell and side + idx::Tuple{Int, Int} # cell and side end """ A `FacetIndex` wraps an (Int, Int) and defines a local facet by pointing to a (cell, facet). """ struct FacetIndex <: BoundaryIndex - idx::Tuple{Int,Int} # cell and side + idx::Tuple{Int, Int} # cell and side end const AbstractVecOrSet{T} = Union{AbstractSet{T}, AbstractVector{T}} diff --git a/src/Grid/coloring.jl b/src/Grid/coloring.jl index 47951786f4..a6a1610073 100644 --- a/src/Grid/coloring.jl +++ b/src/Grid/coloring.jl @@ -1,5 +1,5 @@ # Incidence matrix for element connections in the grid -function create_incidence_matrix(g::AbstractGrid, cellset=1:getncells(g)) +function create_incidence_matrix(g::AbstractGrid, cellset = 1:getncells(g)) cell_containing_node = Dict{Int, Set{Int}}() for cellid in cellset cell = getcells(g, cellid) @@ -29,8 +29,8 @@ end # Greedy algorithm for coloring a grid such that no two cells with the same node # have the same color -function greedy_coloring(incidence_matrix, cells=1:size(incidence_matrix, 1)) - cell_colors = Dict{Int,Int}(i => 0 for i in cells) # Zero represents no color set yet +function greedy_coloring(incidence_matrix, cells = 1:size(incidence_matrix, 1)) + cell_colors = Dict{Int, Int}(i => 0 for i in cells) # Zero represents no color set yet occupied_colors = Set{Int}() final_colors = Vector{Int}[] total_colors = 0 @@ -96,11 +96,11 @@ function workstream_coloring(incidence_matrix, cellset) # Loop over all elements in previous zone and add their neighbouring elements # unless they are in any of the previous 2 zones. empty_zone = true - for c in get(zones, Z-1, Z0) + for c in get(zones, Z - 1, Z0) c ∈ cellset || continue # technically not needed as long as incidence matrix is created with cellset for r in nzrange(incidence_matrix, c) cell_neighbour = incidence_matrix.rowval[r] - if !(cell_neighbour in get(zones, Z-2, Z0) || cell_neighbour in get(zones, Z-1, Z0)) + if !(cell_neighbour in get(zones, Z - 2, Z0) || cell_neighbour in get(zones, Z - 1, Z0)) push!(s, cell_neighbour) empty_zone = false end @@ -124,7 +124,7 @@ function workstream_coloring(incidence_matrix, cellset) ################ # 3. Gathering # ################ - Nodd, Zodd = findmax(x -> isodd(x) ? length(zone_colors[x]) : typemin(Int), 1:length(zone_colors)) + Nodd, Zodd = findmax(x -> isodd(x) ? length(zone_colors[x]) : typemin(Int), 1:length(zone_colors)) Neven, Zeven = findmax(x -> iseven(x) ? length(zone_colors[x]) : typemin(Int), 1:length(zone_colors)) N = Nodd + Neven final_colors = append!(zone_colors[Zodd], zone_colors[Zeven]) # Reuse these for output @@ -137,7 +137,7 @@ function workstream_coloring(incidence_matrix, cellset) empty!(used_for_zone) - for local_color in sortperm(zone_color_vectors; by=length, rev=true) + for local_color in sortperm(zone_color_vectors; by = length, rev = true) cond = odd ? (x -> x <= Nodd) : (x -> x > Nodd) _, global_color = findmin(x -> (cond(x) && x ∉ used_for_zone) ? color_sizes[x] : typemax(Int), 1:N) push!(used_for_zone, global_color) @@ -195,7 +195,7 @@ The resulting colors can be visualized using [`Ferrite.write_cell_colors`](@ref) # References - [Turcksin2016](@cite) Turcksin et al. ACM Trans. Math. Softw. 43 (2016). """ -function create_coloring(g::AbstractGrid, cellset=1:getncells(g); alg::ColoringAlgorithm.T=ColoringAlgorithm.WorkStream) +function create_coloring(g::AbstractGrid, cellset = 1:getncells(g); alg::ColoringAlgorithm.T = ColoringAlgorithm.WorkStream) incidence_matrix = create_incidence_matrix(g, cellset) if alg === ColoringAlgorithm.WorkStream return workstream_coloring(incidence_matrix, cellset) diff --git a/src/Grid/grid.jl b/src/Grid/grid.jl index a1d2113fab..3d27431bd1 100644 --- a/src/Grid/grid.jl +++ b/src/Grid/grid.jl @@ -9,10 +9,10 @@ A `Node` is a point in space. # Fields - `x::Vec{dim,T}`: stores the coordinates """ -struct Node{dim,T} - x::Vec{dim,T} +struct Node{dim, T} + x::Vec{dim, T} end -Node(x::NTuple{dim,T}) where {dim,T} = Node(Vec{dim,T}(x)) +Node(x::NTuple{dim, T}) where {dim, T} = Node(Vec{dim, T}(x)) """ get_node_coordinate(::Node) @@ -26,14 +26,14 @@ get_node_coordinate(n::Node) = n.x Get the data type of the the node coordinate. """ -get_coordinate_type(::Node{dim,T}) where {dim,T} = Vec{dim,T} +get_coordinate_type(::Node{dim, T}) where {dim, T} = Vec{dim, T} """ get_coordinate_eltype(::Node) Get the data type of the components of the nodes coordinate. """ -get_coordinate_eltype(::Node{dim,T}) where {dim,T} = T +get_coordinate_eltype(::Node{dim, T}) where {dim, T} = T ########################## # AbstractCell interface # @@ -42,19 +42,19 @@ get_coordinate_eltype(::Node{dim,T}) where {dim,T} = T # Defined in src/Ferrite.jl # abstract type AbstractCell{refshape <: AbstractRefShape} end -getrefshape(::AbstractCell{refshape}) where refshape = refshape -getrefshape(::Type{<:AbstractCell{refshape}}) where refshape = refshape +getrefshape(::AbstractCell{refshape}) where {refshape} = refshape +getrefshape(::Type{<:AbstractCell{refshape}}) where {refshape} = refshape nvertices(c::AbstractCell) = length(vertices(c)) -nedges( c::AbstractCell) = length(edges(c)) -nfaces( c::AbstractCell) = length(faces(c)) -nfacets( c::AbstractCell) = length(facets(c)) -nnodes( c::AbstractCell) = length(get_node_ids(c)) +nedges(c::AbstractCell) = length(edges(c)) +nfaces(c::AbstractCell) = length(faces(c)) +nfacets(c::AbstractCell) = length(facets(c)) +nnodes(c::AbstractCell) = length(get_node_ids(c)) nvertices(::Type{T}) where {T <: AbstractRefShape} = length(reference_vertices(T)) -nedges( ::Type{T}) where {T <: AbstractRefShape} = length(reference_edges(T)) -nfaces( ::Type{T}) where {T <: AbstractRefShape} = length(reference_faces(T)) -nfacets( ::Type{T}) where {T <: AbstractRefShape} = length(reference_facets(T)) +nedges(::Type{T}) where {T <: AbstractRefShape} = length(reference_edges(T)) +nfaces(::Type{T}) where {T <: AbstractRefShape} = length(reference_faces(T)) +nfacets(::Type{T}) where {T <: AbstractRefShape} = length(reference_facets(T)) """ @@ -148,10 +148,10 @@ reference_facets(::Type{<:AbstractRefShape}) @inline reference_facets(refshape::Type{<:AbstractRefShape{2}}) = reference_edges(refshape) @inline reference_facets(refshape::Type{<:AbstractRefShape{3}}) = reference_faces(refshape) -@inline reference_faces(::AbstractCell{refshape}) where refshape = reference_faces(refshape) -@inline reference_edges(::AbstractCell{refshape}) where refshape = reference_edges(refshape) -@inline reference_vertices(::AbstractCell{refshape}) where refshape = reference_vertices(refshape) -@inline reference_facets(::AbstractCell{refshape}) where refshape = reference_facets(refshape) +@inline reference_faces(::AbstractCell{refshape}) where {refshape} = reference_faces(refshape) +@inline reference_edges(::AbstractCell{refshape}) where {refshape} = reference_edges(refshape) +@inline reference_vertices(::AbstractCell{refshape}) where {refshape} = reference_vertices(refshape) +@inline reference_facets(::AbstractCell{refshape}) where {refshape} = reference_facets(refshape) """ geometric_interpolation(::AbstractCell)::ScalarInterpolation @@ -174,19 +174,19 @@ get_node_ids(c::AbstractCell) = c.nodes # Default implementations of = vertices/edges/faces that work as long as get_node_ids # and `reference_` are correctly implemented for the cell / reference shape. -function vertices(c::AbstractCell{RefShape}) where RefShape +function vertices(c::AbstractCell{RefShape}) where {RefShape} ns = get_node_ids(c) return map(i -> ns[i], reference_vertices(RefShape)) end -function edges(c::AbstractCell{RefShape}) where RefShape +function edges(c::AbstractCell{RefShape}) where {RefShape} ns = get_node_ids(c) return map(reference_edges(RefShape)) do re map(i -> ns[i], re) end end -function faces(c::AbstractCell{RefShape}) where RefShape +function faces(c::AbstractCell{RefShape}) where {RefShape} ns = get_node_ids(c) return map(reference_faces(RefShape)) do rf map(i -> ns[i], rf) @@ -216,34 +216,46 @@ reference_faces(::Type{RefTetrahedron}) = ((1, 3, 2), (1, 2, 4), (2, 3, 4), (1, # RefHexahedron (refdim = 3) reference_vertices(::Type{RefHexahedron}) = (1, 2, 3, 4, 5, 6, 7, 8) function reference_edges(::Type{RefHexahedron}) - return ((1, 2), (2, 3), (3, 4), (4, 1), (5, 6), (6, 7), # e1 ... e6 - (7, 8), (8, 5), (1, 5), (2, 6), (3, 7), (4, 8)) # e7 ... e12 + return ( + (1, 2), (2, 3), (3, 4), (4, 1), (5, 6), (6, 7), # e1 ... e6 + (7, 8), (8, 5), (1, 5), (2, 6), (3, 7), (4, 8), # e7 ... e12 + ) end function reference_faces(::Type{RefHexahedron}) - return ((1, 4, 3, 2), (1, 2, 6, 5), (2, 3, 7, 6), # f1, f2, f3 - (3, 4, 8, 7), (1, 5, 8, 4), (5, 6, 7, 8)) # f4, f5, f6 + return ( + (1, 4, 3, 2), (1, 2, 6, 5), (2, 3, 7, 6), # f1, f2, f3 + (3, 4, 8, 7), (1, 5, 8, 4), (5, 6, 7, 8), # f4, f5, f6 + ) end # RefPrism (refdim = 3) reference_vertices(::Type{RefPrism}) = (1, 2, 3, 4, 5, 6) function reference_edges(::Type{RefPrism}) - return ((2, 1), (1, 3), (1, 4), (3, 2), (2, 5), # e1, e2, e3, e4, e5 - (3, 6), (4, 5), (4, 6), (6, 5)) # e6, e7, e8, e9 + return ( + (2, 1), (1, 3), (1, 4), (3, 2), (2, 5), # e1, e2, e3, e4, e5 + (3, 6), (4, 5), (4, 6), (6, 5), # e6, e7, e8, e9 + ) end function reference_faces(::Type{RefPrism}) - return ((1, 3, 2), (1, 2, 5, 4), (3, 1, 4, 6), # f1, f2, f3 - (2, 3, 6, 5), (4, 5, 6)) # f4, f5 + return ( + (1, 3, 2), (1, 2, 5, 4), (3, 1, 4, 6), # f1, f2, f3 + (2, 3, 6, 5), (4, 5, 6), # f4, f5 + ) end # RefPyramid (refdim = 3) reference_vertices(::Type{RefPyramid}) = (1, 2, 3, 4, 5) function reference_edges(::Type{RefPyramid}) - return ((1, 2), (1, 3), (1, 5), (2, 4), # e1 ... e4 - (2, 5), (4, 3), (3, 5), (4, 5)) # e5 ... e8 + return ( + (1, 2), (1, 3), (1, 5), (2, 4), # e1 ... e4 + (2, 5), (4, 3), (3, 5), (4, 5), # e5 ... e8 + ) end function reference_faces(::Type{RefPyramid}) - return ((1, 3, 4, 2), (1, 2, 5), (1, 5, 3), # f1, f2, f3 - (2, 4, 5), (3, 5, 4)) # f4, f5 + return ( + (1, 3, 4, 2), (1, 2, 5), (1, 5, 3), # f1, f2, f3 + (2, 4, 5), (3, 5, 4), # f4, f5 + ) end ###################################################### @@ -251,38 +263,66 @@ end ###################################################### # Lagrange interpolation based cells -struct Line <: AbstractCell{RefLine} nodes::NTuple{ 2, Int} end -struct QuadraticLine <: AbstractCell{RefLine} nodes::NTuple{ 3, Int} end -struct Triangle <: AbstractCell{RefTriangle} nodes::NTuple{ 3, Int} end -struct QuadraticTriangle <: AbstractCell{RefTriangle} nodes::NTuple{ 6, Int} end -struct Quadrilateral <: AbstractCell{RefQuadrilateral} nodes::NTuple{ 4, Int} end -struct QuadraticQuadrilateral <: AbstractCell{RefQuadrilateral} nodes::NTuple{ 9, Int} end -struct Tetrahedron <: AbstractCell{RefTetrahedron} nodes::NTuple{ 4, Int} end -struct QuadraticTetrahedron <: AbstractCell{RefTetrahedron} nodes::NTuple{10, Int} end -struct Hexahedron <: AbstractCell{RefHexahedron} nodes::NTuple{ 8, Int} end -struct QuadraticHexahedron <: AbstractCell{RefHexahedron} nodes::NTuple{27, Int} end -struct Wedge <: AbstractCell{RefPrism} nodes::NTuple{ 6, Int} end -struct Pyramid <: AbstractCell{RefPyramid} nodes::NTuple{ 5, Int} end - -geometric_interpolation(::Type{Line}) = Lagrange{RefLine, 1}() -geometric_interpolation(::Type{QuadraticLine}) = Lagrange{RefLine, 2}() -geometric_interpolation(::Type{Triangle}) = Lagrange{RefTriangle, 1}() -geometric_interpolation(::Type{QuadraticTriangle}) = Lagrange{RefTriangle, 2}() -geometric_interpolation(::Type{Quadrilateral}) = Lagrange{RefQuadrilateral, 1}() +struct Line <: AbstractCell{RefLine} + nodes::NTuple{2, Int} +end +struct QuadraticLine <: AbstractCell{RefLine} + nodes::NTuple{3, Int} +end +struct Triangle <: AbstractCell{RefTriangle} + nodes::NTuple{3, Int} +end +struct QuadraticTriangle <: AbstractCell{RefTriangle} + nodes::NTuple{6, Int} +end +struct Quadrilateral <: AbstractCell{RefQuadrilateral} + nodes::NTuple{4, Int} +end +struct QuadraticQuadrilateral <: AbstractCell{RefQuadrilateral} + nodes::NTuple{9, Int} +end +struct Tetrahedron <: AbstractCell{RefTetrahedron} + nodes::NTuple{4, Int} +end +struct QuadraticTetrahedron <: AbstractCell{RefTetrahedron} + nodes::NTuple{10, Int} +end +struct Hexahedron <: AbstractCell{RefHexahedron} + nodes::NTuple{8, Int} +end +struct QuadraticHexahedron <: AbstractCell{RefHexahedron} + nodes::NTuple{27, Int} +end +struct Wedge <: AbstractCell{RefPrism} + nodes::NTuple{6, Int} +end +struct Pyramid <: AbstractCell{RefPyramid} + nodes::NTuple{5, Int} +end + +geometric_interpolation(::Type{Line}) = Lagrange{RefLine, 1}() +geometric_interpolation(::Type{QuadraticLine}) = Lagrange{RefLine, 2}() +geometric_interpolation(::Type{Triangle}) = Lagrange{RefTriangle, 1}() +geometric_interpolation(::Type{QuadraticTriangle}) = Lagrange{RefTriangle, 2}() +geometric_interpolation(::Type{Quadrilateral}) = Lagrange{RefQuadrilateral, 1}() geometric_interpolation(::Type{QuadraticQuadrilateral}) = Lagrange{RefQuadrilateral, 2}() -geometric_interpolation(::Type{Tetrahedron}) = Lagrange{RefTetrahedron, 1}() -geometric_interpolation(::Type{QuadraticTetrahedron}) = Lagrange{RefTetrahedron, 2}() -geometric_interpolation(::Type{Hexahedron}) = Lagrange{RefHexahedron, 1}() -geometric_interpolation(::Type{QuadraticHexahedron}) = Lagrange{RefHexahedron, 2}() -geometric_interpolation(::Type{Wedge}) = Lagrange{RefPrism, 1}() -geometric_interpolation(::Type{Pyramid}) = Lagrange{RefPyramid, 1}() +geometric_interpolation(::Type{Tetrahedron}) = Lagrange{RefTetrahedron, 1}() +geometric_interpolation(::Type{QuadraticTetrahedron}) = Lagrange{RefTetrahedron, 2}() +geometric_interpolation(::Type{Hexahedron}) = Lagrange{RefHexahedron, 1}() +geometric_interpolation(::Type{QuadraticHexahedron}) = Lagrange{RefHexahedron, 2}() +geometric_interpolation(::Type{Wedge}) = Lagrange{RefPrism, 1}() +geometric_interpolation(::Type{Pyramid}) = Lagrange{RefPyramid, 1}() # Serendipity interpolation based cells -struct SerendipityQuadraticQuadrilateral <: AbstractCell{RefQuadrilateral} nodes::NTuple{ 8, Int} end -struct SerendipityQuadraticHexahedron <: AbstractCell{RefHexahedron} nodes::NTuple{20, Int} end +struct SerendipityQuadraticQuadrilateral <: AbstractCell{RefQuadrilateral} + nodes::NTuple{8, Int} +end +struct SerendipityQuadraticHexahedron <: AbstractCell{RefHexahedron} + nodes::NTuple{20, Int} +end geometric_interpolation(::Type{SerendipityQuadraticQuadrilateral}) = Serendipity{RefQuadrilateral, 2}() -geometric_interpolation(::Type{SerendipityQuadraticHexahedron}) = Serendipity{RefHexahedron, 2}() +geometric_interpolation(::Type{SerendipityQuadraticHexahedron}) = Serendipity{RefHexahedron, 2}() """ nvertices_on_face(cell::AbstractCell, local_face_index::Int) @@ -303,7 +343,7 @@ Get the reference dimension of the cell, i.e. the dimension of the cell's reference shape. """ getrefdim(c::AbstractCell) = getrefdim(typeof(c)) -getrefdim(::Type{<:AbstractCell{RefShape}}) where RefShape = getrefdim(RefShape) +getrefdim(::Type{<:AbstractCell{RefShape}}) where {RefShape} = getrefdim(RefShape) ###################### @@ -326,27 +366,29 @@ Helper structures for applying boundary conditions or define subdomains are gath - `facetsets::Dict{String, OrderedSet{FacetIndex}}`: maps a `String` to an `OrderedSet` of `FacetIndex` - `vertexsets::Dict{String, OrderedSet{VertexIndex}}`: maps a `String` key to an `OrderedSet` of `VertexIndex` """ -mutable struct Grid{dim,C<:AbstractCell,T<:Real} <: AbstractGrid{dim} +mutable struct Grid{dim, C <: AbstractCell, T <: Real} <: AbstractGrid{dim} cells::Vector{C} - nodes::Vector{Node{dim,T}} + nodes::Vector{Node{dim, T}} # Sets - cellsets::Dict{String,OrderedSet{Int}} - nodesets::Dict{String,OrderedSet{Int}} - facetsets::Dict{String,OrderedSet{FacetIndex}} - vertexsets::Dict{String,OrderedSet{VertexIndex}} -end - -function Grid(cells::Vector{C}, - nodes::Vector{Node{dim,T}}; - cellsets::Dict{String, <:AbstractVecOrSet{Int}}=Dict{String,OrderedSet{Int}}(), - nodesets::Dict{String, <:AbstractVecOrSet{Int}}=Dict{String,OrderedSet{Int}}(), - facetsets::Dict{String, <:AbstractVecOrSet{FacetIndex}}=Dict{String,OrderedSet{FacetIndex}}(), - facesets=nothing, # deprecated - vertexsets::Dict{String, <:AbstractVecOrSet{VertexIndex}}=Dict{String,OrderedSet{VertexIndex}}(), - boundary_matrix = nothing) where {dim,C,T} + cellsets::Dict{String, OrderedSet{Int}} + nodesets::Dict{String, OrderedSet{Int}} + facetsets::Dict{String, OrderedSet{FacetIndex}} + vertexsets::Dict{String, OrderedSet{VertexIndex}} +end + +function Grid( + cells::Vector{C}, + nodes::Vector{Node{dim, T}}; + cellsets::Dict{String, <:AbstractVecOrSet{Int}} = Dict{String, OrderedSet{Int}}(), + nodesets::Dict{String, <:AbstractVecOrSet{Int}} = Dict{String, OrderedSet{Int}}(), + facetsets::Dict{String, <:AbstractVecOrSet{FacetIndex}} = Dict{String, OrderedSet{FacetIndex}}(), + facesets = nothing, # deprecated + vertexsets::Dict{String, <:AbstractVecOrSet{VertexIndex}} = Dict{String, OrderedSet{VertexIndex}}(), + boundary_matrix = nothing + ) where {dim, C, T} if facesets !== nothing if isempty(facetsets) - @warn "facesets in Grid is deprecated, use facetsets instead" maxlog=1 + @warn "facesets in Grid is deprecated, use facetsets instead" maxlog = 1 for (key, set) in facesets facetsets[key] = OrderedSet(FacetIndex(cellnr, facenr) for (cellnr, facenr) in set) end @@ -375,22 +417,22 @@ end Get the datatype for a single point in the grid. """ -get_coordinate_type(::Grid{dim,C,T}) where {dim,C,T} = Vec{dim,T} # Node is baked into the mesh type. +get_coordinate_type(::Grid{dim, C, T}) where {dim, C, T} = Vec{dim, T} # Node is baked into the mesh type. """ toglobal(grid::AbstractGrid, vertexidx::VertexIndex) -> Int toglobal(grid::AbstractGrid, vertexidx::Vector{VertexIndex}) -> Vector{Int} This function takes the local vertex representation (a `VertexIndex`) and looks up the unique global id (an `Int`). """ -toglobal(grid::AbstractGrid,vertexidx::VertexIndex) = vertices(getcells(grid,vertexidx[1]))[vertexidx[2]] -toglobal(grid::AbstractGrid,vertexidx::Vector{VertexIndex}) = unique(toglobal.((grid,),vertexidx)) +toglobal(grid::AbstractGrid, vertexidx::VertexIndex) = vertices(getcells(grid, vertexidx[1]))[vertexidx[2]] +toglobal(grid::AbstractGrid, vertexidx::Vector{VertexIndex}) = unique(toglobal.((grid,), vertexidx)) """ Ferrite.getspatialdim(grid::AbstractGrid) Get the spatial dimension of the grid, corresponding to the vector dimension of the grid's coordinates. """ -getspatialdim(::AbstractGrid{sdim}) where sdim = sdim +getspatialdim(::AbstractGrid{sdim}) where {sdim} = sdim """ get_reference_dimension(grid::AbstractGrid) -> Union{Int, Symbol} @@ -401,7 +443,7 @@ For grids with mixed reference dimensions, `:mixed` is returned. Used internally to dispatch facet-calls to the correct entity when `rdim isa Int`. """ get_reference_dimension(g::AbstractGrid) = _get_reference_dimension(getcells(g)) -_get_reference_dimension(::AbstractVector{C}) where C <: AbstractCell{<:AbstractRefShape{rdim}} where rdim = rdim # Fast path for single rdim inferable from eltype +_get_reference_dimension(::AbstractVector{C}) where {C <: AbstractCell{<:AbstractRefShape{rdim}}} where {rdim} = rdim # Fast path for single rdim inferable from eltype function _get_reference_dimension(cells::AbstractVector{<:AbstractCell}) # Could make fast-path for eltype being union of cells with different rdims, but @KristofferC recommends against that, # https://discourse.julialang.org/t/iterating-through-types-of-a-union-in-a-type-stable-manner/58285/3 @@ -424,7 +466,7 @@ Whereas the last option tries to call a `cellset` of the `grid`. `Collection` ca """ @inline getcells(grid::AbstractGrid) = grid.cells @inline getcells(grid::AbstractGrid, v::Union{Int, Vector{Int}}) = grid.cells[v] -@inline getcells(grid::AbstractGrid, setname::String) = grid.cells[collect(getcellset(grid,setname))] +@inline getcells(grid::AbstractGrid, setname::String) = grid.cells[collect(getcellset(grid, setname))] "Returns the number of cells in the `<:AbstractGrid`." @inline getncells(grid::AbstractGrid) = length(grid.cells) "Returns the celltype of the `<:AbstractGrid`." @@ -442,7 +484,7 @@ to a Node. """ @inline getnodes(grid::AbstractGrid) = grid.nodes @inline getnodes(grid::AbstractGrid, v::Union{Int, Vector{Int}}) = grid.nodes[v] -@inline getnodes(grid::AbstractGrid, setname::String) = grid.nodes[collect(getnodeset(grid,setname))] +@inline getnodes(grid::AbstractGrid, setname::String) = grid.nodes[collect(getnodeset(grid, setname))] "Returns the number of nodes in the grid." @inline getnnodes(grid::AbstractGrid) = length(grid.nodes) "Returns the number of nodes of the `i`-th cell." @@ -532,7 +574,7 @@ Get a vector with the coordinates of the cell corresponding to `idx` or `cache` cell = getcells(grid, idx) N = nnodes(cell) x = Vector{CT}(undef, N) - getcoordinates!(x, grid, cell) + return getcoordinates!(x, grid, cell) end @inline getcoordinates(grid::AbstractGrid, cell::CellIndex) = getcoordinates(grid, cell.idx) @@ -542,18 +584,18 @@ end Mutate `x` to the coordinates of the cell corresponding to `idx` or `cell`. """ -@inline function getcoordinates!(x::Vector{Vec{dim,T}}, grid::AbstractGrid, cell::AbstractCell) where {dim,T} +@inline function getcoordinates!(x::Vector{Vec{dim, T}}, grid::AbstractGrid, cell::AbstractCell) where {dim, T} node_ids = get_node_ids(cell) @inbounds for i in 1:length(x) x[i] = get_node_coordinate(grid, node_ids[i]) end return x end -@inline function getcoordinates!(x::Vector{Vec{dim,T}}, grid::AbstractGrid, cellid::Int) where {dim,T} +@inline function getcoordinates!(x::Vector{Vec{dim, T}}, grid::AbstractGrid, cellid::Int) where {dim, T} cell = getcells(grid, cellid) - getcoordinates!(x, grid, cell) + return getcoordinates!(x, grid, cell) end -@inline getcoordinates!(x::Vector{Vec{dim,T}}, grid::AbstractGrid, cell::CellIndex) where {dim, T} = getcoordinates!(x, grid, cell.idx) +@inline getcoordinates!(x::Vector{Vec{dim, T}}, grid::AbstractGrid, cell::CellIndex) where {dim, T} = getcoordinates!(x, grid, cell.idx) """ get_node_coordinate(grid::AbstractGrid, n::Int) @@ -565,6 +607,7 @@ get_node_coordinate(grid, n) = get_node_coordinate(getnodes(grid, n)) function cellnodes!(global_nodes::Vector{Int}, grid::AbstractGrid, i::Int) cell = getcells(grid, i) _cellnodes!(global_nodes, cell) + return global_nodes end function _cellnodes!(global_nodes::Vector{Int}, cell::AbstractCell) @assert length(global_nodes) == nnodes(cell) @@ -583,6 +626,7 @@ function Base.show(io::IO, ::MIME"text/plain", grid::Grid) end join(io, typestrs, '/') print(io, " cells and $(getnnodes(grid)) nodes") + return end """ @@ -600,12 +644,12 @@ boundaryfunction(::Type{FacetIndex}) = facets for INDEX in (:VertexIndex, :EdgeIndex, :FaceIndex, :FacetIndex) @eval begin #Constructor - ($INDEX)(a::Int, b::Int) = ($INDEX)((a,b)) + ($INDEX)(a::Int, b::Int) = ($INDEX)((a, b)) Base.getindex(I::($INDEX), i::Int) = I.idx[i] #To be able to do a,b = faceidx - Base.iterate(I::($INDEX), state::Int=1) = (state==3) ? nothing : (I[state], state+1) + Base.iterate(I::($INDEX), state::Int = 1) = (state == 3) ? nothing : (I[state], state + 1) # Necessary to check if, e.g. `(cellid, faceidx) in faceset` Base.isequal(x::$INDEX, y::$INDEX) = x.idx == y.idx @@ -721,13 +765,13 @@ function OrientationInfo(path::NTuple{2, Int}) return OrientationInfo(flipped, 0) end -function OrientationInfo(surface::NTuple{N, Int}) where N +function OrientationInfo(surface::NTuple{N, Int}) where {N} min_idx = argmin(surface) shift_index = min_idx - 1 if min_idx == 1 flipped = surface[2] < surface[end] elseif min_idx == length(surface) - flipped = surface[1] < surface[end-1] + flipped = surface[1] < surface[end - 1] else flipped = surface[min_idx + 1] < surface[min_idx - 1] end diff --git a/src/Grid/grid_generators.jl b/src/Grid/grid_generators.jl index 9897d8c252..52b7359b2d 100644 --- a/src/Grid/grid_generators.jl +++ b/src/Grid/grid_generators.jl @@ -8,14 +8,13 @@ e.g. `Triangle` or `Hexahedron`. `nel` is a tuple of the number of elements in e generate_grid # Line -function generate_grid(::Type{Line}, nel::NTuple{1,Int}, left::Vec{1,T}=Vec{1}((-1.0,)), right::Vec{1,T}=Vec{1}((1.0,))) where {T} - +function generate_grid(::Type{Line}, nel::NTuple{1, Int}, left::Vec{1, T} = Vec{1}((-1.0,)), right::Vec{1, T} = Vec{1}((1.0,))) where {T} nel_x = nel[1] n_nodes = nel_x + 1 # Generate nodes - coords_x = collect(range(left[1], stop=right[1], length=n_nodes)) - nodes = Node{1,T}[] + coords_x = collect(range(left[1], stop = right[1], length = n_nodes)) + nodes = Node{1, T}[] for i in 1:n_nodes push!(nodes, Node((coords_x[i],))) end @@ -24,29 +23,33 @@ function generate_grid(::Type{Line}, nel::NTuple{1,Int}, left::Vec{1,T}=Vec{1}(( cells = Line[] for i in 1:nel_x - push!(cells, Line((i, i+1))) + push!(cells, Line((i, i + 1))) end # Cell faces - boundary = Vector([FacetIndex(1, 1), - FacetIndex(nel_x, 2)]) + boundary = [ + FacetIndex(1, 1), + FacetIndex(nel_x, 2), + ] # Cell face sets - facetsets = Dict("left" => OrderedSet{FacetIndex}([boundary[1]]), - "right" => OrderedSet{FacetIndex}([boundary[2]])) + facetsets = Dict( + "left" => OrderedSet{FacetIndex}([boundary[1]]), + "right" => OrderedSet{FacetIndex}([boundary[2]]) + ) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # QuadraticLine -function generate_grid(::Type{QuadraticLine}, nel::NTuple{1,Int}, left::Vec{1,T}=Vec{1}((-1.0,)), right::Vec{1,T}=Vec{1}((1.0,))) where {T} +function generate_grid(::Type{QuadraticLine}, nel::NTuple{1, Int}, left::Vec{1, T} = Vec{1}((-1.0,)), right::Vec{1, T} = Vec{1}((1.0,))) where {T} nel_x = nel[1] - n_nodes = 2*nel_x + 1 + n_nodes = 2 * nel_x + 1 # Generate nodes - coords_x = collect(range(left[1], stop=right[1], length=n_nodes)) - nodes = Node{1,T}[] + coords_x = collect(range(left[1], stop = right[1], length = n_nodes)) + nodes = Node{1, T}[] for i in 1:n_nodes push!(nodes, Node((coords_x[i],))) end @@ -54,23 +57,27 @@ function generate_grid(::Type{QuadraticLine}, nel::NTuple{1,Int}, left::Vec{1,T} # Generate cells cells = QuadraticLine[] for i in 1:nel_x - push!(cells, QuadraticLine((2*i-1, 2*i+1, 2*i))) + push!(cells, QuadraticLine((2 * i - 1, 2 * i + 1, 2 * i))) end # Cell faces - boundary = FacetIndex[FacetIndex(1, 1), - FacetIndex(nel_x, 2)] + boundary = FacetIndex[ + FacetIndex(1, 1), + FacetIndex(nel_x, 2), + ] # Cell face sets - facetsets = Dict("left" => OrderedSet{FacetIndex}([boundary[1]]), - "right" => OrderedSet{FacetIndex}([boundary[2]])) + facetsets = Dict( + "left" => OrderedSet{FacetIndex}([boundary[1]]), + "right" => OrderedSet{FacetIndex}([boundary[2]]) + ) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end -function _generate_2d_nodes!(nodes::Vector{Node{2, T}}, nx, ny, LL, LR, UR, UL) where T - for i in 0:ny-1 - ratio_bounds = convert(T, i) / (ny-1) +function _generate_2d_nodes!(nodes::Vector{Node{2, T}}, nx, ny, LL, LR, UR, UL) where {T} + for i in 0:(ny - 1) + ratio_bounds = convert(T, i) / (ny - 1) x0 = LL[1] * (1 - ratio_bounds) + ratio_bounds * UL[1] x1 = LR[1] * (1 - ratio_bounds) + ratio_bounds * UR[1] @@ -78,115 +85,123 @@ function _generate_2d_nodes!(nodes::Vector{Node{2, T}}, nx, ny, LL, LR, UR, UL) y0 = LL[2] * (1 - ratio_bounds) + ratio_bounds * UL[2] y1 = LR[2] * (1 - ratio_bounds) + ratio_bounds * UR[2] - for j in 0:nx-1 - ratio = convert(T, j) / (nx-1) + for j in 0:(nx - 1) + ratio = convert(T, j) / (nx - 1) x = x0 * (1 - ratio) + ratio * x1 y = y0 * (1 - ratio) + ratio * y1 push!(nodes, Node((x, y))) end end + return end -function generate_grid(C::Type{<:AbstractCell{<:AbstractRefShape{2}}}, nel::NTuple{2,Int}, X::Vector{Vec{2,T}}) where {T} +function generate_grid(C::Type{<:AbstractCell{<:AbstractRefShape{2}}}, nel::NTuple{2, Int}, X::Vector{Vec{2, T}}) where {T} @assert length(X) == 4 - generate_grid(C, nel, X[1], X[2], X[3], X[4]) + return generate_grid(C, nel, X[1], X[2], X[3], X[4]) end -function generate_grid(C::Type{<:AbstractCell{<:AbstractRefShape{2}}}, nel::NTuple{2,Int}, left::Vec{2,T}=Vec{2}((-1.0,-1.0)), right::Vec{2,T}=Vec{2}((1.0,1.0))) where {T} - +function generate_grid(C::Type{<:AbstractCell{<:AbstractRefShape{2}}}, nel::NTuple{2, Int}, left::Vec{2, T} = Vec{2}((-1.0, -1.0)), right::Vec{2, T} = Vec{2}((1.0, 1.0))) where {T} LL = left UR = right LR = Vec{2}((UR[1], LL[2])) UL = Vec{2}((LL[1], UR[2])) - generate_grid(C, nel, LL, LR, UR, UL) + return generate_grid(C, nel, LL, LR, UR, UL) end # Quadrilateral -function generate_grid(C::Type{Quadrilateral}, nel::NTuple{2,Int}, LL::Vec{2,T}, LR::Vec{2,T}, UR::Vec{2,T}, UL::Vec{2,T}) where {T} - - nel_x = nel[1]; nel_y = nel[2]; nel_tot = nel_x*nel_y +function generate_grid(C::Type{Quadrilateral}, nel::NTuple{2, Int}, LL::Vec{2, T}, LR::Vec{2, T}, UR::Vec{2, T}, UL::Vec{2, T}) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_tot = nel_x * nel_y n_nodes_x = nel_x + 1; n_nodes_y = nel_y + 1 n_nodes = n_nodes_x * n_nodes_y # Generate nodes - nodes = Node{2,T}[] + nodes = Node{2, T}[] _generate_2d_nodes!(nodes, n_nodes_x, n_nodes_y, LL, LR, UR, UL) # Generate cells node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y)) cells = Quadrilateral[] for j in 1:nel_y, i in 1:nel_x - push!(cells, Quadrilateral((node_array[i,j], node_array[i+1,j], node_array[i+1,j+1], node_array[i,j+1]))) + push!(cells, Quadrilateral((node_array[i, j], node_array[i + 1, j], node_array[i + 1, j + 1], node_array[i, j + 1]))) end # Cell faces - cell_array = reshape(collect(1:nel_tot),(nel_x, nel_y)) - boundary = FacetIndex[[FacetIndex(cl, 1) for cl in cell_array[:,1]]; - [FacetIndex(cl, 2) for cl in cell_array[end,:]]; - [FacetIndex(cl, 3) for cl in cell_array[:,end]]; - [FacetIndex(cl, 4) for cl in cell_array[1,:]]] + cell_array = reshape(collect(1:nel_tot), (nel_x, nel_y)) + boundary = FacetIndex[ + [FacetIndex(cl, 1) for cl in cell_array[:, 1]]; + [FacetIndex(cl, 2) for cl in cell_array[end, :]]; + [FacetIndex(cl, 3) for cl in cell_array[:, end]]; + [FacetIndex(cl, 4) for cl in cell_array[1, :]] + ] # Cell face sets offset = 0 facetsets = Dict{String, OrderedSet{FacetIndex}}() - facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,1])) .+ offset]); offset += length(cell_array[:,1]) - facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end,:])) .+ offset]); offset += length(cell_array[end,:]) - facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,end])) .+ offset]); offset += length(cell_array[:,end]) - facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,:])) .+ offset]); offset += length(cell_array[1,:]) + facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, 1])) .+ offset]); offset += length(cell_array[:, 1]) + facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end, :])) .+ offset]); offset += length(cell_array[end, :]) + facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, end])) .+ offset]); offset += length(cell_array[:, end]) + facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, :])) .+ offset]); offset += length(cell_array[1, :]) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # QuadraticQuadrilateral -function generate_grid(::Type{QuadraticQuadrilateral}, nel::NTuple{2,Int}, LL::Vec{2,T}, LR::Vec{2,T}, UR::Vec{2,T}, UL::Vec{2,T}) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_tot = nel_x*nel_y - n_nodes_x = 2*nel_x + 1; n_nodes_y = 2*nel_y + 1 +function generate_grid(::Type{QuadraticQuadrilateral}, nel::NTuple{2, Int}, LL::Vec{2, T}, LR::Vec{2, T}, UR::Vec{2, T}, UL::Vec{2, T}) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_tot = nel_x * nel_y + n_nodes_x = 2 * nel_x + 1; n_nodes_y = 2 * nel_y + 1 n_nodes = n_nodes_x * n_nodes_y # Generate nodes - nodes = Node{2,T}[] + nodes = Node{2, T}[] _generate_2d_nodes!(nodes, n_nodes_x, n_nodes_y, LL, LR, UR, UL) # Generate cells node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y)) cells = QuadraticQuadrilateral[] for j in 1:nel_y, i in 1:nel_x - push!(cells, QuadraticQuadrilateral((node_array[2*i-1,2*j-1],node_array[2*i+1,2*j-1],node_array[2*i+1,2*j+1],node_array[2*i-1,2*j+1], - node_array[2*i,2*j-1],node_array[2*i+1,2*j],node_array[2*i,2*j+1],node_array[2*i-1,2*j], - node_array[2*i,2*j]))) + cell = QuadraticQuadrilateral( + ( + node_array[2 * i - 1, 2 * j - 1], node_array[2 * i + 1, 2 * j - 1], node_array[2 * i + 1, 2 * j + 1], node_array[2 * i - 1, 2 * j + 1], + node_array[2 * i, 2 * j - 1], node_array[2 * i + 1, 2 * j], node_array[2 * i, 2 * j + 1], node_array[2 * i - 1, 2 * j], + node_array[2 * i, 2 * j], + ) + ) + push!(cells, cell) end # Cell faces - cell_array = reshape(collect(1:nel_tot),(nel_x, nel_y)) - boundary = FacetIndex[[FacetIndex(cl, 1) for cl in cell_array[:,1]]; - [FacetIndex(cl, 2) for cl in cell_array[end,:]]; - [FacetIndex(cl, 3) for cl in cell_array[:,end]]; - [FacetIndex(cl, 4) for cl in cell_array[1,:]]] + cell_array = reshape(collect(1:nel_tot), (nel_x, nel_y)) + boundary = FacetIndex[ + [FacetIndex(cl, 1) for cl in cell_array[:, 1]]; + [FacetIndex(cl, 2) for cl in cell_array[end, :]]; + [FacetIndex(cl, 3) for cl in cell_array[:, end]]; + [FacetIndex(cl, 4) for cl in cell_array[1, :]] + ] # Cell face sets offset = 0 facetsets = Dict{String, OrderedSet{FacetIndex}}() - facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,1])) .+ offset]); offset += length(cell_array[:,1]) - facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end,:])) .+ offset]); offset += length(cell_array[end,:]) - facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,end])) .+ offset]); offset += length(cell_array[:,end]) - facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,:])) .+ offset]); offset += length(cell_array[1,:]) + facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, 1])) .+ offset]); offset += length(cell_array[:, 1]) + facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end, :])) .+ offset]); offset += length(cell_array[end, :]) + facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, end])) .+ offset]); offset += length(cell_array[:, end]) + facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, :])) .+ offset]); offset += length(cell_array[1, :]) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # Hexahedron -function generate_grid(::Type{Hexahedron}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3}((-1.0,-1.0,-1.0)), right::Vec{3,T}=Vec{3}((1.0,1.0,1.0))) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x*nel_y*nel_z +function generate_grid(::Type{Hexahedron}, nel::NTuple{3, Int}, left::Vec{3, T} = Vec{3}((-1.0, -1.0, -1.0)), right::Vec{3, T} = Vec{3}((1.0, 1.0, 1.0))) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x * nel_y * nel_z n_nodes_x = nel_x + 1; n_nodes_y = nel_y + 1; n_nodes_z = nel_z + 1 n_nodes = n_nodes_x * n_nodes_y * n_nodes_z # Generate nodes - coords_x = range(left[1], stop=right[1], length=n_nodes_x) - coords_y = range(left[2], stop=right[2], length=n_nodes_y) - coords_z = range(left[3], stop=right[3], length=n_nodes_z) - nodes = Node{3,T}[] + coords_x = range(left[1], stop = right[1], length = n_nodes_x) + coords_y = range(left[2], stop = right[2], length = n_nodes_y) + coords_z = range(left[3], stop = right[3], length = n_nodes_z) + nodes = Node{3, T}[] for k in 1:n_nodes_z, j in 1:n_nodes_y, i in 1:n_nodes_x push!(nodes, Node((coords_x[i], coords_y[j], coords_z[k]))) end @@ -195,44 +210,51 @@ function generate_grid(::Type{Hexahedron}, nel::NTuple{3,Int}, left::Vec{3,T}=Ve node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y, n_nodes_z)) cells = Hexahedron[] for k in 1:nel_z, j in 1:nel_y, i in 1:nel_x - push!(cells, Hexahedron((node_array[i,j,k], node_array[i+1,j,k], node_array[i+1,j+1,k], node_array[i,j+1,k], - node_array[i,j,k+1], node_array[i+1,j,k+1], node_array[i+1,j+1,k+1], node_array[i,j+1,k+1]))) + cell = Hexahedron( + ( + node_array[i, j, k], node_array[i + 1, j, k], node_array[i + 1, j + 1, k], node_array[i, j + 1, k], + node_array[i, j, k + 1], node_array[i + 1, j, k + 1], node_array[i + 1, j + 1, k + 1], node_array[i, j + 1, k + 1], + ) + ) + push!(cells, cell) end # Cell faces - cell_array = reshape(collect(1:nel_tot),(nel_x, nel_y, nel_z)) - boundary = FacetIndex[[FacetIndex(cl, 1) for cl in cell_array[:,:,1][:]]; - [FacetIndex(cl, 2) for cl in cell_array[:,1,:][:]]; - [FacetIndex(cl, 3) for cl in cell_array[end,:,:][:]]; - [FacetIndex(cl, 4) for cl in cell_array[:,end,:][:]]; - [FacetIndex(cl, 5) for cl in cell_array[1,:,:][:]]; - [FacetIndex(cl, 6) for cl in cell_array[:,:,end][:]]] + cell_array = reshape(collect(1:nel_tot), (nel_x, nel_y, nel_z)) + boundary = FacetIndex[ + [FacetIndex(cl, 1) for cl in cell_array[:, :, 1][:]]; + [FacetIndex(cl, 2) for cl in cell_array[:, 1, :][:]]; + [FacetIndex(cl, 3) for cl in cell_array[end, :, :][:]]; + [FacetIndex(cl, 4) for cl in cell_array[:, end, :][:]]; + [FacetIndex(cl, 5) for cl in cell_array[1, :, :][:]]; + [FacetIndex(cl, 6) for cl in cell_array[:, :, end][:]] + ] # Cell face sets offset = 0 - facetsets = Dict{String,OrderedSet{FacetIndex}}() - facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,:,1][:])) .+ offset]); offset += length(cell_array[:,:,1][:]) - facetsets["front"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,1,:][:])) .+ offset]); offset += length(cell_array[:,1,:][:]) - facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end,:,:][:])) .+ offset]); offset += length(cell_array[end,:,:][:]) - facetsets["back"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,end,:][:])) .+ offset]); offset += length(cell_array[:,end,:][:]) - facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,:,:][:])) .+ offset]); offset += length(cell_array[1,:,:][:]) - facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,:,end][:])) .+ offset]); offset += length(cell_array[:,:,end][:]) + facetsets = Dict{String, OrderedSet{FacetIndex}}() + facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, :, 1][:])) .+ offset]); offset += length(cell_array[:, :, 1][:]) + facetsets["front"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, 1, :][:])) .+ offset]); offset += length(cell_array[:, 1, :][:]) + facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end, :, :][:])) .+ offset]); offset += length(cell_array[end, :, :][:]) + facetsets["back"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, end, :][:])) .+ offset]); offset += length(cell_array[:, end, :][:]) + facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, :, :][:])) .+ offset]); offset += length(cell_array[1, :, :][:]) + facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, :, end][:])) .+ offset]); offset += length(cell_array[:, :, end][:]) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # Wedge -function generate_grid(::Type{Wedge}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3}((-1.0,-1.0,-1.0)), right::Vec{3,T}=Vec{3}((1.0,1.0,1.0))) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x*nel_y*nel_z +function generate_grid(::Type{Wedge}, nel::NTuple{3, Int}, left::Vec{3, T} = Vec{3}((-1.0, -1.0, -1.0)), right::Vec{3, T} = Vec{3}((1.0, 1.0, 1.0))) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x * nel_y * nel_z n_nodes_x = nel_x + 1; n_nodes_y = nel_y + 1; n_nodes_z = nel_z + 1 n_nodes = n_nodes_x * n_nodes_y * n_nodes_z # Generate nodes - coords_x = range(left[1], stop=right[1], length=n_nodes_x) - coords_y = range(left[2], stop=right[2], length=n_nodes_y) - coords_z = range(left[3], stop=right[3], length=n_nodes_z) - nodes = Node{3,T}[] + coords_x = range(left[1], stop = right[1], length = n_nodes_x) + coords_y = range(left[2], stop = right[2], length = n_nodes_y) + coords_z = range(left[3], stop = right[3], length = n_nodes_z) + nodes = Node{3, T}[] for k in 1:n_nodes_z, j in 1:n_nodes_y, i in 1:n_nodes_x push!(nodes, Node((coords_x[i], coords_y[j], coords_z[k]))) end @@ -241,21 +263,33 @@ function generate_grid(::Type{Wedge}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3}( node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y, n_nodes_z)) cells = Wedge[] for k in 1:nel_z, j in 1:nel_y, i in 1:nel_x - push!(cells, Wedge((node_array[i,j,k], node_array[i+1,j,k], node_array[i,j+1,k], - node_array[i,j,k+1], node_array[i+1,j,k+1], node_array[i,j+1,k+1]))) # ◺ - push!(cells, Wedge((node_array[i+1,j,k], node_array[i+1,j+1,k], node_array[i,j+1,k], - node_array[i+1,j,k+1], node_array[i+1,j+1,k+1], node_array[i,j+1,k+1]))) # ◹ + # ◺ + wedge1 = Wedge( + ( + node_array[i, j, k], node_array[i + 1, j, k], node_array[i, j + 1, k], + node_array[i, j, k + 1], node_array[i + 1, j, k + 1], node_array[i, j + 1, k + 1], + ) + ) + push!(cells, wedge1) + # ◹ + wedge2 = Wedge( + ( + node_array[i + 1, j, k], node_array[i + 1, j + 1, k], node_array[i, j + 1, k], + node_array[i + 1, j, k + 1], node_array[i + 1, j + 1, k + 1], node_array[i, j + 1, k + 1], + ) + ) + push!(cells, wedge2) end # Order the cells as c_nxyz[2, x, y, z] such that we can look up boundary cells c_nxyz = reshape(1:length(cells), (2, nel...)) - @views le = map(x -> FacetIndex(x,3), c_nxyz[1, 1, :, :][:]) - @views ri = map(x -> FacetIndex(x,2), c_nxyz[2, end, :, :][:]) - @views fr = map(x -> FacetIndex(x,2), c_nxyz[1, :, 1, :][:]) - @views ba = map(x -> FacetIndex(x,4), c_nxyz[2, :, end, :][:]) - @views bo = [map(x -> FacetIndex(x,1), c_nxyz[1, :, :, 1][:]) ; map(x -> FacetIndex(x,1), c_nxyz[2, :, :, 1][:])] - @views to = [map(x -> FacetIndex(x,5), c_nxyz[1, :, :, end][:]) ; map(x -> FacetIndex(x,5), c_nxyz[2, :, :, end][:])] + @views le = map(x -> FacetIndex(x, 3), c_nxyz[1, 1, :, :][:]) + @views ri = map(x -> FacetIndex(x, 2), c_nxyz[2, end, :, :][:]) + @views fr = map(x -> FacetIndex(x, 2), c_nxyz[1, :, 1, :][:]) + @views ba = map(x -> FacetIndex(x, 4), c_nxyz[2, :, end, :][:]) + @views bo = [map(x -> FacetIndex(x, 1), c_nxyz[1, :, :, 1][:]) ; map(x -> FacetIndex(x, 1), c_nxyz[2, :, :, 1][:])] + @views to = [map(x -> FacetIndex(x, 5), c_nxyz[1, :, :, end][:]) ; map(x -> FacetIndex(x, 5), c_nxyz[2, :, :, end][:])] facetsets = Dict( "left" => OrderedSet{FacetIndex}(le), @@ -267,57 +301,57 @@ function generate_grid(::Type{Wedge}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3}( ) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end #Pyramid -function generate_grid(::Type{Pyramid}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3}((-1.0,-1.0,-1.0)), right::Vec{3,T}=Vec{3}((1.0,1.0,1.0))) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x*nel_y*nel_z +function generate_grid(::Type{Pyramid}, nel::NTuple{3, Int}, left::Vec{3, T} = Vec{3}((-1.0, -1.0, -1.0)), right::Vec{3, T} = Vec{3}((1.0, 1.0, 1.0))) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x * nel_y * nel_z n_nodes_x = nel_x + 1; n_nodes_y = nel_y + 1; n_nodes_z = nel_z + 1 n_nodes = n_nodes_x * n_nodes_y * n_nodes_z # Generate nodes - coords_x = range(left[1], stop=right[1], length=n_nodes_x) - coords_y = range(left[2], stop=right[2], length=n_nodes_y) - coords_z = range(left[3], stop=right[3], length=n_nodes_z) - nodes = Node{3,T}[] + coords_x = range(left[1], stop = right[1], length = n_nodes_x) + coords_y = range(left[2], stop = right[2], length = n_nodes_y) + coords_z = range(left[3], stop = right[3], length = n_nodes_z) + nodes = Node{3, T}[] for k in 1:n_nodes_z, j in 1:n_nodes_y, i in 1:n_nodes_x push!(nodes, Node((coords_x[i], coords_y[j], coords_z[k]))) end #Center node in each "voxel" for k in 1:nel_z, j in 1:nel_y, i in 1:nel_x - midx = (coords_x[i+1] + coords_x[i]) / 2 - midy = (coords_y[j+1] + coords_y[j]) / 2 - midz = (coords_z[k+1] + coords_z[k]) / 2 + midx = (coords_x[i + 1] + coords_x[i]) / 2 + midy = (coords_y[j + 1] + coords_y[j]) / 2 + midz = (coords_z[k + 1] + coords_z[k]) / 2 push!(nodes, Node((midx, midy, midz))) end # Generate cells node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y, n_nodes_z)) cells = Pyramid[] - midnodecounter = n_nodes_x*n_nodes_y*n_nodes_z + midnodecounter = n_nodes_x * n_nodes_y * n_nodes_z for k in 1:nel_z, j in 1:nel_y, i in 1:nel_x midnodecounter += 1 - pyramid1 = Pyramid((node_array[i,j,k], node_array[i+1,j,k], node_array[i,j+1,k], node_array[i+1,j+1,k], midnodecounter )) # bottom - pyramid2 = Pyramid((node_array[i,j,k], node_array[i,j,k+1], node_array[i+1,j,k], node_array[i+1,j,k+1], midnodecounter )) # front - pyramid3 = Pyramid((node_array[i+1,j,k], node_array[i+1,j,k+1], node_array[i+1,j+1,k], node_array[i+1,j+1,k+1], midnodecounter )) # right - pyramid4 = Pyramid((node_array[i,j+1,k], node_array[i+1,j+1,k], node_array[i,j+1,k+1], node_array[i+1,j+1,k+1], midnodecounter )) # back - pyramid5 = Pyramid((node_array[i,j,k], node_array[i,j+1,k], node_array[i,j,k+1], node_array[i,j+1,k+1], midnodecounter )) # left - pyramid6 = Pyramid((node_array[i,j,k+1], node_array[i,j+1,k+1], node_array[i+1,j,k+1], node_array[i+1,j+1,k+1], midnodecounter )) # top + pyramid1 = Pyramid((node_array[i, j, k], node_array[i + 1, j, k], node_array[i, j + 1, k], node_array[i + 1, j + 1, k], midnodecounter)) # bottom + pyramid2 = Pyramid((node_array[i, j, k], node_array[i, j, k + 1], node_array[i + 1, j, k], node_array[i + 1, j, k + 1], midnodecounter)) # front + pyramid3 = Pyramid((node_array[i + 1, j, k], node_array[i + 1, j, k + 1], node_array[i + 1, j + 1, k], node_array[i + 1, j + 1, k + 1], midnodecounter)) # right + pyramid4 = Pyramid((node_array[i, j + 1, k], node_array[i + 1, j + 1, k], node_array[i, j + 1, k + 1], node_array[i + 1, j + 1, k + 1], midnodecounter)) # back + pyramid5 = Pyramid((node_array[i, j, k], node_array[i, j + 1, k], node_array[i, j, k + 1], node_array[i, j + 1, k + 1], midnodecounter)) # left + pyramid6 = Pyramid((node_array[i, j, k + 1], node_array[i, j + 1, k + 1], node_array[i + 1, j, k + 1], node_array[i + 1, j + 1, k + 1], midnodecounter)) # top push!(cells, pyramid1, pyramid2, pyramid3, pyramid4, pyramid5, pyramid6) end # Order the cells as c_nxyz[2, x, y, z] such that we can look up boundary cells ncells_per_voxel = 6 - c_nxyz = reshape(1:(prod(nel)*ncells_per_voxel), (ncells_per_voxel, nel...)) + c_nxyz = reshape(1:(prod(nel) * ncells_per_voxel), (ncells_per_voxel, nel...)) - @views le = map(x -> FacetIndex(x,1), c_nxyz[5, 1, :, :][:]) - @views ri = map(x -> FacetIndex(x,1), c_nxyz[3, end, :, :][:]) - @views fr = map(x -> FacetIndex(x,1), c_nxyz[2, :, 1, :][:]) - @views ba = map(x -> FacetIndex(x,1), c_nxyz[4, :, end, :][:]) - @views bo = map(x -> FacetIndex(x,1), c_nxyz[1, :, :, 1][:]) - @views to = map(x -> FacetIndex(x,1), c_nxyz[6, :, :, end][:]) + @views le = map(x -> FacetIndex(x, 1), c_nxyz[5, 1, :, :][:]) + @views ri = map(x -> FacetIndex(x, 1), c_nxyz[3, end, :, :][:]) + @views fr = map(x -> FacetIndex(x, 1), c_nxyz[2, :, 1, :][:]) + @views ba = map(x -> FacetIndex(x, 1), c_nxyz[4, :, end, :][:]) + @views bo = map(x -> FacetIndex(x, 1), c_nxyz[1, :, :, 1][:]) + @views to = map(x -> FacetIndex(x, 1), c_nxyz[6, :, :, end][:]) facetsets = Dict( "left" => OrderedSet{FacetIndex}(le), @@ -329,20 +363,20 @@ function generate_grid(::Type{Pyramid}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3 ) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end -function generate_grid(::Type{SerendipityQuadraticHexahedron}, nel::NTuple{3,Int}, left::Vec{3,T}=Vec{3}((-1.0,-1.0,-1.0)), right::Vec{3,T}=Vec{3}((1.0,1.0,1.0))) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x*nel_y*nel_z +function generate_grid(::Type{SerendipityQuadraticHexahedron}, nel::NTuple{3, Int}, left::Vec{3, T} = Vec{3}((-1.0, -1.0, -1.0)), right::Vec{3, T} = Vec{3}((1.0, 1.0, 1.0))) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_z = nel[3]; nel_tot = nel_x * nel_y * nel_z nnode_x = 2nel_x + 1; nnode_y = 2nel_y + 1; nnode_z = 2nel_z + 1 #Note: not the actually number of nodes in x/y/z, just a temporary variables # Generate nodes - coords_x = range(left[1], stop=right[1], length=nnode_x) - coords_y = range(left[2], stop=right[2], length=nnode_y) - coords_z = range(left[3], stop=right[3], length=nnode_z) - nodes = Node{3,T}[] + coords_x = range(left[1], stop = right[1], length = nnode_x) + coords_y = range(left[2], stop = right[2], length = nnode_y) + coords_z = range(left[3], stop = right[3], length = nnode_z) + nodes = Node{3, T}[] - node_array = fill(0, (nnode_x,nnode_y,nnode_z)) + node_array = fill(0, (nnode_x, nnode_y, nnode_z)) nodeid = 0 for k in 1:nnode_z, j in 1:nnode_y, i in 1:nnode_x (iseven(i) && iseven(j)) && continue @@ -350,123 +384,144 @@ function generate_grid(::Type{SerendipityQuadraticHexahedron}, nel::NTuple{3,Int (iseven(k) && iseven(j)) && continue push!(nodes, Node((coords_x[i], coords_y[j], coords_z[k]))) nodeid += 1 - node_array[i,j,k] = nodeid + node_array[i, j, k] = nodeid end # Generate cells cells = SerendipityQuadraticHexahedron[] for k in 1:2:2nel_z, j in 1:2:2nel_y, i in 1:2:2nel_x - push!(cells, SerendipityQuadraticHexahedron(( - node_array[i,j,k], node_array[i+2,j,k], node_array[i+2,j+2,k], node_array[i,j+2,k], # vertices bot - node_array[i,j,k+2], node_array[i+2,j,k+2], node_array[i+2,j+2,k+2], node_array[i,j+2,k+2], # vertices top - node_array[i+1,j,k], node_array[i+2,j+1,k], node_array[i+1,j+2,k], node_array[i,j+1,k], # edges horizontal bottom - node_array[i+1,j,k+2], node_array[i+2,j+1,k+2], node_array[i+1,j+2,k+2], node_array[i,j+1,k+2], # edges horizontal top - node_array[i,j,k+1], node_array[i+2,j,k+1], node_array[i+2,j+2,k+1], node_array[i,j+2,k+1] )) # edges vertical + cell = SerendipityQuadraticHexahedron( + ( + node_array[i, j, k], node_array[i + 2, j, k], node_array[i + 2, j + 2, k], node_array[i, j + 2, k], # vertices bot + node_array[i, j, k + 2], node_array[i + 2, j, k + 2], node_array[i + 2, j + 2, k + 2], node_array[i, j + 2, k + 2], # vertices top + node_array[i + 1, j, k], node_array[i + 2, j + 1, k], node_array[i + 1, j + 2, k], node_array[i, j + 1, k], # edges horizontal bottom + node_array[i + 1, j, k + 2], node_array[i + 2, j + 1, k + 2], node_array[i + 1, j + 2, k + 2], node_array[i, j + 1, k + 2], # edges horizontal top + node_array[i, j, k + 1], node_array[i + 2, j, k + 1], node_array[i + 2, j + 2, k + 1], node_array[i, j + 2, k + 1], # edges vertical ) + ) + push!(cells, cell) end # Cell faces - cell_array = reshape(collect(1:nel_tot),(nel_x, nel_y, nel_z)) - boundary = FacetIndex[[FacetIndex(cl, 1) for cl in cell_array[:,:,1][:]]; - [FacetIndex(cl, 2) for cl in cell_array[:,1,:][:]]; - [FacetIndex(cl, 3) for cl in cell_array[end,:,:][:]]; - [FacetIndex(cl, 4) for cl in cell_array[:,end,:][:]]; - [FacetIndex(cl, 5) for cl in cell_array[1,:,:][:]]; - [FacetIndex(cl, 6) for cl in cell_array[:,:,end][:]]] + cell_array = reshape(collect(1:nel_tot), (nel_x, nel_y, nel_z)) + boundary = FacetIndex[ + [FacetIndex(cl, 1) for cl in cell_array[:, :, 1][:]]; + [FacetIndex(cl, 2) for cl in cell_array[:, 1, :][:]]; + [FacetIndex(cl, 3) for cl in cell_array[end, :, :][:]]; + [FacetIndex(cl, 4) for cl in cell_array[:, end, :][:]]; + [FacetIndex(cl, 5) for cl in cell_array[1, :, :][:]]; + [FacetIndex(cl, 6) for cl in cell_array[:, :, end][:]] + ] # Cell face sets offset = 0 - facetsets = Dict{String,OrderedSet{FacetIndex}}() - facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,:,1][:])) .+ offset]); offset += length(cell_array[:,:,1][:]) - facetsets["front"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,1,:][:])) .+ offset]); offset += length(cell_array[:,1,:][:]) - facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end,:,:][:])) .+ offset]); offset += length(cell_array[end,:,:][:]) - facetsets["back"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,end,:][:])) .+ offset]); offset += length(cell_array[:,end,:][:]) - facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,:,:][:])) .+ offset]); offset += length(cell_array[1,:,:][:]) - facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:,:,end][:])) .+ offset]); offset += length(cell_array[:,:,end][:]) + facetsets = Dict{String, OrderedSet{FacetIndex}}() + facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, :, 1][:])) .+ offset]); offset += length(cell_array[:, :, 1][:]) + facetsets["front"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, 1, :][:])) .+ offset]); offset += length(cell_array[:, 1, :][:]) + facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[end, :, :][:])) .+ offset]); offset += length(cell_array[end, :, :][:]) + facetsets["back"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, end, :][:])) .+ offset]); offset += length(cell_array[:, end, :][:]) + facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, :, :][:])) .+ offset]); offset += length(cell_array[1, :, :][:]) + facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[:, :, end][:])) .+ offset]); offset += length(cell_array[:, :, end][:]) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # Triangle -function generate_grid(::Type{Triangle}, nel::NTuple{2,Int}, LL::Vec{2,T}, LR::Vec{2,T}, UR::Vec{2,T}, UL::Vec{2,T}) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_tot = 2*nel_x*nel_y +function generate_grid(::Type{Triangle}, nel::NTuple{2, Int}, LL::Vec{2, T}, LR::Vec{2, T}, UR::Vec{2, T}, UL::Vec{2, T}) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_tot = 2 * nel_x * nel_y n_nodes_x = nel_x + 1; n_nodes_y = nel_y + 1 n_nodes = n_nodes_x * n_nodes_y # Generate nodes - nodes = Node{2,T}[] + nodes = Node{2, T}[] _generate_2d_nodes!(nodes, n_nodes_x, n_nodes_y, LL, LR, UR, UL) # Generate cells node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y)) cells = Triangle[] for j in 1:nel_y, i in 1:nel_x - push!(cells, Triangle((node_array[i,j], node_array[i+1,j], node_array[i,j+1]))) # ◺ - push!(cells, Triangle((node_array[i+1,j], node_array[i+1,j+1], node_array[i,j+1]))) # ◹ + push!(cells, Triangle((node_array[i, j], node_array[i + 1, j], node_array[i, j + 1]))) # ◺ + push!(cells, Triangle((node_array[i + 1, j], node_array[i + 1, j + 1], node_array[i, j + 1]))) # ◹ end # Cell faces - cell_array = reshape(collect(1:nel_tot),(2, nel_x, nel_y)) - boundary = FacetIndex[[FacetIndex(cl, 1) for cl in cell_array[1,:,1]]; - [FacetIndex(cl, 1) for cl in cell_array[2,end,:]]; - [FacetIndex(cl, 2) for cl in cell_array[2,:,end]]; - [FacetIndex(cl, 3) for cl in cell_array[1,1,:]]] + cell_array = reshape(collect(1:nel_tot), (2, nel_x, nel_y)) + boundary = FacetIndex[ + [FacetIndex(cl, 1) for cl in cell_array[1, :, 1]]; + [FacetIndex(cl, 1) for cl in cell_array[2, end, :]]; + [FacetIndex(cl, 2) for cl in cell_array[2, :, end]]; + [FacetIndex(cl, 3) for cl in cell_array[1, 1, :]] + ] # Cell face sets offset = 0 - facetsets = Dict{String,OrderedSet{FacetIndex}}() - facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,:,1])) .+ offset]); offset += length(cell_array[1,:,1]) - facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2,end,:])) .+ offset]); offset += length(cell_array[2,end,:]) - facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2,:,end])) .+ offset]); offset += length(cell_array[2,:,end]) - facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,1,:])) .+ offset]); offset += length(cell_array[1,1,:]) + facetsets = Dict{String, OrderedSet{FacetIndex}}() + facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, :, 1])) .+ offset]); offset += length(cell_array[1, :, 1]) + facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2, end, :])) .+ offset]); offset += length(cell_array[2, end, :]) + facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2, :, end])) .+ offset]); offset += length(cell_array[2, :, end]) + facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, 1, :])) .+ offset]); offset += length(cell_array[1, 1, :]) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # QuadraticTriangle -function generate_grid(::Type{QuadraticTriangle}, nel::NTuple{2,Int}, LL::Vec{2,T}, LR::Vec{2,T}, UR::Vec{2,T}, UL::Vec{2,T}) where {T} - nel_x = nel[1]; nel_y = nel[2]; nel_tot = 2*nel_x*nel_y - n_nodes_x = 2*nel_x + 1; n_nodes_y = 2*nel_y + 1 +function generate_grid(::Type{QuadraticTriangle}, nel::NTuple{2, Int}, LL::Vec{2, T}, LR::Vec{2, T}, UR::Vec{2, T}, UL::Vec{2, T}) where {T} + nel_x = nel[1]; nel_y = nel[2]; nel_tot = 2 * nel_x * nel_y + n_nodes_x = 2 * nel_x + 1; n_nodes_y = 2 * nel_y + 1 n_nodes = n_nodes_x * n_nodes_y # Generate nodes - nodes = Node{2,T}[] + nodes = Node{2, T}[] _generate_2d_nodes!(nodes, n_nodes_x, n_nodes_y, LL, LR, UR, UL) # Generate cells node_array = reshape(collect(1:n_nodes), (n_nodes_x, n_nodes_y)) cells = QuadraticTriangle[] for j in 1:nel_y, i in 1:nel_x - push!(cells, QuadraticTriangle((node_array[2*i-1,2*j-1], node_array[2*i+1,2*j-1], node_array[2*i-1,2*j+1], - node_array[2*i,2*j-1], node_array[2*i,2*j], node_array[2*i-1,2*j]))) # ◺ - push!(cells, QuadraticTriangle((node_array[2*i+1,2*j-1], node_array[2*i+1,2*j+1], node_array[2*i-1,2*j+1], - node_array[2*i+1,2*j], node_array[2*i,2*j+1], node_array[2*i,2*j]))) # ◹ + # ◺ + triangle1 = QuadraticTriangle( + ( + node_array[2 * i - 1, 2 * j - 1], node_array[2 * i + 1, 2 * j - 1], node_array[2 * i - 1, 2 * j + 1], + node_array[2 * i, 2 * j - 1], node_array[2 * i, 2 * j], node_array[2 * i - 1, 2 * j], + ) + ) + push!(cells, triangle1) + # ◹ + triangle2 = QuadraticTriangle( + ( + node_array[2 * i + 1, 2 * j - 1], node_array[2 * i + 1, 2 * j + 1], node_array[2 * i - 1, 2 * j + 1], + node_array[2 * i + 1, 2 * j], node_array[2 * i, 2 * j + 1], node_array[2 * i, 2 * j], + ) + ) + push!(cells, triangle2) end # Cell faces - cell_array = reshape(collect(1:nel_tot),(2, nel_x, nel_y)) - boundary = FacetIndex[[FacetIndex(cl, 1) for cl in cell_array[1,:,1]]; - [FacetIndex(cl, 1) for cl in cell_array[2,end,:]]; - [FacetIndex(cl, 2) for cl in cell_array[2,:,end]]; - [FacetIndex(cl, 3) for cl in cell_array[1,1,:]]] + cell_array = reshape(collect(1:nel_tot), (2, nel_x, nel_y)) + boundary = FacetIndex[ + [FacetIndex(cl, 1) for cl in cell_array[1, :, 1]]; + [FacetIndex(cl, 1) for cl in cell_array[2, end, :]]; + [FacetIndex(cl, 2) for cl in cell_array[2, :, end]]; + [FacetIndex(cl, 3) for cl in cell_array[1, 1, :]] + ] # Cell face sets offset = 0 - facetsets = Dict{String,OrderedSet{FacetIndex}}() - facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,:,1])) .+ offset]); offset += length(cell_array[1,:,1]) - facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2,end,:])) .+ offset]); offset += length(cell_array[2,end,:]) - facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2,:,end])) .+ offset]); offset += length(cell_array[2,:,end]) - facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1,1,:])) .+ offset]); offset += length(cell_array[1,1,:]) + facetsets = Dict{String, OrderedSet{FacetIndex}}() + facetsets["bottom"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, :, 1])) .+ offset]); offset += length(cell_array[1, :, 1]) + facetsets["right"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2, end, :])) .+ offset]); offset += length(cell_array[2, end, :]) + facetsets["top"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[2, :, end])) .+ offset]); offset += length(cell_array[2, :, end]) + facetsets["left"] = OrderedSet{FacetIndex}(boundary[(1:length(cell_array[1, 1, :])) .+ offset]); offset += length(cell_array[1, 1, :]) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end # Tetrahedron -function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left::Vec{3,T}=Vec{3}((-1.0,-1.0,-1.0)), right::Vec{3,T}=Vec{3}((1.0,1.0,1.0))) where {T} +function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3, Int}, left::Vec{3, T} = Vec{3}((-1.0, -1.0, -1.0)), right::Vec{3, T} = Vec{3}((1.0, 1.0, 1.0))) where {T} nodes_per_dim = cells_per_dim .+ 1 cells_per_cube = 6 @@ -477,13 +532,13 @@ function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left:: n_cells_x, n_cells_y, n_cells_z = cells_per_dim # Generate nodes - coords_x = range(left[1], stop=right[1], length=n_nodes_x) - coords_y = range(left[2], stop=right[2], length=n_nodes_y) - coords_z = range(left[3], stop=right[3], length=n_nodes_z) + coords_x = range(left[1], stop = right[1], length = n_nodes_x) + coords_y = range(left[2], stop = right[2], length = n_nodes_y) + coords_z = range(left[3], stop = right[3], length = n_nodes_z) numbering = reshape(1:total_nodes, nodes_per_dim) # Pre-allocate the nodes & cells - nodes = Vector{Node{3,T}}(undef, total_nodes) + nodes = Vector{Node{3, T}}(undef, total_nodes) cells = Vector{Tetrahedron}(undef, total_elements) # Generate nodes @@ -501,14 +556,14 @@ function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left:: cell_idx = 0 @inbounds for k in 1:n_cells_z, j in 1:n_cells_y, i in 1:n_cells_x cell = ( - numbering[i , j , k], - numbering[i+1, j , k], - numbering[i+1, j+1, k], - numbering[i , j+1, k], - numbering[i , j , k+1], - numbering[i+1, j , k+1], - numbering[i+1, j+1, k+1], - numbering[i , j+1, k+1] + numbering[i, j, k], + numbering[i + 1, j, k], + numbering[i + 1, j + 1, k], + numbering[i, j + 1, k], + numbering[i, j, k + 1], + numbering[i + 1, j, k + 1], + numbering[i + 1, j + 1, k + 1], + numbering[i, j + 1, k + 1], ) cells[cell_idx + 1] = Tetrahedron((cell[1], cell[2], cell[4], cell[8])) @@ -524,12 +579,12 @@ function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left:: # Order the cells as c_nxyz[n, x, y, z] such that we can look up boundary cells c_nxyz = reshape(1:total_elements, (cells_per_cube, cells_per_dim...)) - @views le = [map(x -> FacetIndex(x,4), c_nxyz[1, 1, :, :][:]) ; map(x -> FacetIndex(x,2), c_nxyz[2, 1, :, :][:])] - @views ri = [map(x -> FacetIndex(x,1), c_nxyz[4, end, :, :][:]) ; map(x -> FacetIndex(x,1), c_nxyz[6, end, :, :][:])] - @views fr = [map(x -> FacetIndex(x,1), c_nxyz[2, :, 1, :][:]) ; map(x -> FacetIndex(x,1), c_nxyz[5, :, 1, :][:])] - @views ba = [map(x -> FacetIndex(x,3), c_nxyz[3, :, end, :][:]) ; map(x -> FacetIndex(x,3), c_nxyz[4, :, end, :][:])] - @views bo = [map(x -> FacetIndex(x,1), c_nxyz[1, :, :, 1][:]) ; map(x -> FacetIndex(x,1), c_nxyz[3, :, :, 1][:])] - @views to = [map(x -> FacetIndex(x,3), c_nxyz[5, :, :, end][:]) ; map(x -> FacetIndex(x,3), c_nxyz[6, :, :, end][:])] + @views le = [map(x -> FacetIndex(x, 4), c_nxyz[1, 1, :, :][:]) ; map(x -> FacetIndex(x, 2), c_nxyz[2, 1, :, :][:])] + @views ri = [map(x -> FacetIndex(x, 1), c_nxyz[4, end, :, :][:]) ; map(x -> FacetIndex(x, 1), c_nxyz[6, end, :, :][:])] + @views fr = [map(x -> FacetIndex(x, 1), c_nxyz[2, :, 1, :][:]) ; map(x -> FacetIndex(x, 1), c_nxyz[5, :, 1, :][:])] + @views ba = [map(x -> FacetIndex(x, 3), c_nxyz[3, :, end, :][:]) ; map(x -> FacetIndex(x, 3), c_nxyz[4, :, end, :][:])] + @views bo = [map(x -> FacetIndex(x, 1), c_nxyz[1, :, :, 1][:]) ; map(x -> FacetIndex(x, 1), c_nxyz[3, :, :, 1][:])] + @views to = [map(x -> FacetIndex(x, 3), c_nxyz[5, :, :, end][:]) ; map(x -> FacetIndex(x, 3), c_nxyz[6, :, :, end][:])] facetsets = Dict( "left" => OrderedSet{FacetIndex}(le), @@ -541,5 +596,5 @@ function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left:: ) foreach(s -> sort!(s, by = x -> x.idx), values(facetsets)) - return Grid(cells, nodes, facetsets=facetsets) + return Grid(cells, nodes, facetsets = facetsets) end diff --git a/src/Grid/topology.jl b/src/Grid/topology.jl index c5ace16bce..e70023e2ab 100644 --- a/src/Grid/topology.jl +++ b/src/Grid/topology.jl @@ -51,7 +51,7 @@ mutable struct ExclusiveTopology <: AbstractTopology facet_skeleton::Union{Vector{FacetIndex}, Nothing} end -function ExclusiveTopology(grid::AbstractGrid{sdim}) where sdim +function ExclusiveTopology(grid::AbstractGrid{sdim}) where {sdim} if sdim != get_reference_dimension(grid) error("ExclusiveTopology does not support embedded cells (i.e. reference dimensions different from the spatial dimension)") end @@ -79,19 +79,17 @@ function ExclusiveTopology(grid::AbstractGrid{sdim}) where sdim num_shared_vertices = _num_shared_vertices(cell, neighbor_cell) if num_shared_vertices == 1 _add_single_vertex_neighbor!(vertex_vertex_neighbor_buf, cell, cell_id, neighbor_cell, neighbor_cell_id) - # Shared edge - elseif num_shared_vertices == 2 + elseif num_shared_vertices == 2 # Shared edge _add_single_edge_neighbor!(edge_edge_neighbor_buf, cell, cell_id, neighbor_cell, neighbor_cell_id) - # Shared face - elseif num_shared_vertices >= 3 + elseif num_shared_vertices >= 3 # Shared face _add_single_face_neighbor!(face_face_neighbor_buf, cell, cell_id, neighbor_cell, neighbor_cell_id) else error("Found connected elements without shared vertex... Mesh broken?") end end end - face_face_neighbor = ArrayOfVectorViews(face_face_neighbor_buf) - edge_edge_neighbor = ArrayOfVectorViews(edge_edge_neighbor_buf) + face_face_neighbor = ArrayOfVectorViews(face_face_neighbor_buf) + edge_edge_neighbor = ArrayOfVectorViews(edge_edge_neighbor_buf) vertex_vertex_neighbor = ArrayOfVectorViews(vertex_vertex_neighbor_buf) return ExclusiveTopology(vertex_to_cell, cell_neighbor, face_face_neighbor, edge_edge_neighbor, vertex_vertex_neighbor, nothing) end @@ -99,10 +97,10 @@ end function get_facet_facet_neighborhood(t::ExclusiveTopology, g::AbstractGrid) return _get_facet_facet_neighborhood(t, Val(get_reference_dimension(g))) end -_get_facet_facet_neighborhood(t::ExclusiveTopology, #=rdim=#::Val{1}) = t.vertex_vertex_neighbor -_get_facet_facet_neighborhood(t::ExclusiveTopology, #=rdim=#::Val{2}) = t.edge_edge_neighbor -_get_facet_facet_neighborhood(t::ExclusiveTopology, #=rdim=#::Val{3}) = t.face_face_neighbor -function _get_facet_facet_neighborhood(::ExclusiveTopology, #=rdim=#::Val{:mixed}) +_get_facet_facet_neighborhood(t::ExclusiveTopology, #=rdim=# ::Val{1}) = t.vertex_vertex_neighbor +_get_facet_facet_neighborhood(t::ExclusiveTopology, #=rdim=# ::Val{2}) = t.edge_edge_neighbor +_get_facet_facet_neighborhood(t::ExclusiveTopology, #=rdim=# ::Val{3}) = t.face_face_neighbor +function _get_facet_facet_neighborhood(::ExclusiveTopology, #=rdim=# ::Val{:mixed}) throw(ArgumentError("get_facet_facet_neightborhood is only supported for grids containing cells with the same reference dimension. Access the `vertex_vertex_neighbor`, `edge_edge_neighbor`, or `face_face_neighbor` fields explicitly instead.")) end @@ -110,34 +108,34 @@ end # Guess of how many neighbors depending on grid dimension and index type. # This could be possible to optimize further by studying connectivities of non-uniform # grids, see https://github.com/Ferrite-FEM/Ferrite.jl/pull/974#discussion_r1660838649 -function _getsizehint(g::AbstractGrid, ::Type{IDX}) where IDX +function _getsizehint(g::AbstractGrid, ::Type{IDX}) where {IDX} CT = getcelltype(g) isconcretetype(CT) && return _getsizehint(getrefshape(CT)(), IDX) rdim = get_reference_dimension(g)::Int return _getsizehint(RefSimplex{rdim}(), IDX) # Simplex is "worst case", used as default. end -_getsizehint(::AbstractRefShape, ::Type{FaceIndex}) = 1 # Always 1 or zero if not mixed rdim +_getsizehint(::AbstractRefShape, ::Type{FaceIndex}) = 1 # Always 1 or zero if not mixed rdim _getsizehint(::AbstractRefShape{1}, ::Type{EdgeIndex}) = 1 _getsizehint(::AbstractRefShape{2}, ::Type{EdgeIndex}) = 1 _getsizehint(::AbstractRefShape{3}, ::Type{EdgeIndex}) = 3 # Number for RefTetrahedron -_getsizehint(::RefHexahedron, ::Type{EdgeIndex}) = 1 # Optim for RefHexahedron +_getsizehint(::RefHexahedron, ::Type{EdgeIndex}) = 1 # Optim for RefHexahedron _getsizehint(::AbstractRefShape{1}, ::Type{VertexIndex}) = 1 _getsizehint(::AbstractRefShape{2}, ::Type{VertexIndex}) = 3 _getsizehint(::AbstractRefShape{3}, ::Type{VertexIndex}) = 13 -_getsizehint(::RefHypercube, ::Type{VertexIndex}) = 1 # Optim for RefHypercube +_getsizehint(::RefHypercube, ::Type{VertexIndex}) = 1 # Optim for RefHypercube _getsizehint(::AbstractRefShape{1}, ::Type{CellIndex}) = 2 _getsizehint(::AbstractRefShape{2}, ::Type{CellIndex}) = 12 _getsizehint(::AbstractRefShape{3}, ::Type{CellIndex}) = 70 -_getsizehint(::RefQuadrilateral, ::Type{CellIndex}) = 8 -_getsizehint(::RefHexahedron, ::Type{CellIndex}) = 26 +_getsizehint(::RefQuadrilateral, ::Type{CellIndex}) = 8 +_getsizehint(::RefHexahedron, ::Type{CellIndex}) = 26 function _num_shared_vertices(cell_a::C1, cell_b::C2) where {C1, C2} num_shared_vertices = 0 - for vertex ∈ vertices(cell_a) - for vertex_neighbor ∈ vertices(cell_b) + for vertex in vertices(cell_a) + for vertex_neighbor in vertices(cell_b) if vertex_neighbor == vertex num_shared_vertices += 1 continue @@ -148,7 +146,7 @@ function _num_shared_vertices(cell_a::C1, cell_b::C2) where {C1, C2} end "Return the highest number of vertices, edges, and faces per cell" -function _max_nentities_per_cell(cells::Vector{C}) where C +function _max_nentities_per_cell(cells::Vector{C}) where {C} if isconcretetype(C) cell = first(cells) return nvertices(cell), nedges(cell), nfaces(cell) @@ -168,9 +166,9 @@ function _max_nentities_per_cell(cells::Vector{C}) where C end function _add_single_face_neighbor!(face_table::ConstructionBuffer, cell::AbstractCell, cell_id::Int, cell_neighbor::AbstractCell, cell_neighbor_id::Int) - for (lfi, face) ∈ enumerate(faces(cell)) + for (lfi, face) in enumerate(faces(cell)) uniqueface = sortface_fast(face) - for (lfi2, face_neighbor) ∈ enumerate(faces(cell_neighbor)) + for (lfi2, face_neighbor) in enumerate(faces(cell_neighbor)) uniqueface2 = sortface_fast(face_neighbor) if uniqueface == uniqueface2 push_at_index!(face_table, FaceIndex(cell_neighbor_id, lfi2), cell_id, lfi) @@ -178,12 +176,13 @@ function _add_single_face_neighbor!(face_table::ConstructionBuffer, cell::Abstra end end end + return end function _add_single_edge_neighbor!(edge_table::ConstructionBuffer, cell::AbstractCell, cell_id::Int, cell_neighbor::AbstractCell, cell_neighbor_id::Int) - for (lei, edge) ∈ enumerate(edges(cell)) + for (lei, edge) in enumerate(edges(cell)) uniqueedge = sortedge_fast(edge) - for (lei2, edge_neighbor) ∈ enumerate(edges(cell_neighbor)) + for (lei2, edge_neighbor) in enumerate(edges(cell_neighbor)) uniqueedge2 = sortedge_fast(edge_neighbor) if uniqueedge == uniqueedge2 push_at_index!(edge_table, EdgeIndex(cell_neighbor_id, lei2), cell_id, lei) @@ -191,27 +190,29 @@ function _add_single_edge_neighbor!(edge_table::ConstructionBuffer, cell::Abstra end end end + return end function _add_single_vertex_neighbor!(vertex_table::ConstructionBuffer, cell::AbstractCell, cell_id::Int, cell_neighbor::AbstractCell, cell_neighbor_id::Int) - for (lvi, vertex) ∈ enumerate(vertices(cell)) - for (lvi2, vertex_neighbor) ∈ enumerate(vertices(cell_neighbor)) + for (lvi, vertex) in enumerate(vertices(cell)) + for (lvi2, vertex_neighbor) in enumerate(vertices(cell_neighbor)) if vertex_neighbor == vertex push_at_index!(vertex_table, VertexIndex(cell_neighbor_id, lvi2), cell_id, lvi) break end end end + return end function build_vertex_to_cell(cells; max_vertices, nnodes) vertex_to_cell = ArrayOfVectorViews(sizehint!(Int[], max_vertices * nnodes), (nnodes,); sizehint = max_vertices) do cov - for (cellid, cell) in enumerate(cells) - for vertex in vertices(cell) - push_at_index!(cov, cellid, vertex) - end + for (cellid, cell) in enumerate(cells) + for vertex in vertices(cell) + push_at_index!(cov, cellid, vertex) end end + end return vertex_to_cell end @@ -226,8 +227,8 @@ function build_cell_neighbor(grid, cells, vertex_to_cell; ncells) n = 1 for (cell_id, cell) in enumerate(cells) empty!(cell_neighbor_ids) - for vertex ∈ vertices(cell) - for vertex_cell_id ∈ vertex_to_cell[vertex] + for vertex in vertices(cell) + for vertex_cell_id in vertex_to_cell[vertex] if vertex_cell_id != cell_id vertex_cell_id ∈ cell_neighbor_ids || push!(cell_neighbor_ids, vertex_cell_id) end @@ -242,7 +243,7 @@ function build_cell_neighbor(grid, cells, vertex_to_cell; ncells) return ArrayOfVectorViews(indices, data, LinearIndices(1:ncells)) end -function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, cellidx::CellIndex, include_self=false) +function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, cellidx::CellIndex, include_self = false) patch = top.cell_neighbor[cellidx.idx] if include_self return view(push!(collect(patch), cellidx.idx), 1:(length(patch) + 1)) @@ -251,7 +252,7 @@ function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, cellidx::Ce end end -function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, faceidx::FaceIndex, include_self=false) +function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, faceidx::FaceIndex, include_self = false) neighbors = top.face_face_neighbor[faceidx[1], faceidx[2]] if include_self return view(push!(collect(neighbors), faceidx), 1:(length(neighbors) + 1)) @@ -260,7 +261,7 @@ function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, faceidx::Fa end end -function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{2}, edgeidx::EdgeIndex, include_self=false) +function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{2}, edgeidx::EdgeIndex, include_self = false) neighbors = top.edge_edge_neighbor[edgeidx[1], edgeidx[2]] if include_self return view(push!(collect(neighbors), edgeidx), 1:(length(neighbors) + 1)) @@ -269,22 +270,22 @@ function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{2}, edgeidx: end end -function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, vertexidx::VertexIndex, include_self=false) +function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, vertexidx::VertexIndex, include_self = false) cellid, local_vertexid = vertexidx[1], vertexidx[2] - cell_vertices = vertices(getcells(grid,cellid)) + cell_vertices = vertices(getcells(grid, cellid)) global_vertexid = cell_vertices[local_vertexid] vertex_to_cell = top.vertex_to_cell[global_vertexid] self_reference_local = Vector{VertexIndex}() sizehint!(self_reference_local, length(vertex_to_cell)) - for (i,cellid) in enumerate(vertex_to_cell) - local_vertex = VertexIndex(cellid,findfirst(x->x==global_vertexid,vertices(getcells(grid,cellid)))::Int) + for (i, cellid) in enumerate(vertex_to_cell) + local_vertex = VertexIndex(cellid, findfirst(x -> x == global_vertexid, vertices(getcells(grid, cellid)))::Int) !include_self && local_vertex == vertexidx && continue push!(self_reference_local, local_vertex) end return view(self_reference_local, 1:length(self_reference_local)) end -function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{3}, edgeidx::EdgeIndex, include_self=false) +function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{3}, edgeidx::EdgeIndex, include_self = false) cellid, local_edgeidx = edgeidx[1], edgeidx[2] cell_edges = edges(getcells(grid, cellid)) nonlocal_edgeid = cell_edges[local_edgeidx] @@ -293,7 +294,7 @@ function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{3}, edgeidx: for cellid in cell_neighbors local_neighbor_edgeid = findfirst(x -> issubset(x, nonlocal_edgeid), edges(getcells(grid, cellid))) local_neighbor_edgeid === nothing && continue - local_edge = EdgeIndex(cellid,local_neighbor_edgeid) + local_edge = EdgeIndex(cellid, local_neighbor_edgeid) push!(self_reference_local, local_edge) end if include_self @@ -304,7 +305,7 @@ function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{3}, edgeidx: return view(neighbors, 1:length(neighbors)) end -function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, facetindex::FacetIndex, include_self=false) +function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, facetindex::FacetIndex, include_self = false) rdim = get_reference_dimension(grid) return _getneighborhood(Val(rdim), top, grid, facetindex, include_self) end @@ -324,16 +325,16 @@ function vertex_star_stencils(top::ExclusiveTopology, grid::Grid) cells = grid.cells stencil_table = ArrayOfVectorViews(VertexIndex[], (getnnodes(grid),); sizehint = 10) do buf # Vertex Connectivity - for (global_vertexid,cellset) ∈ enumerate(top.vertex_to_cell) - for cell ∈ cellset + for (global_vertexid, cellset) in enumerate(top.vertex_to_cell) + for cell in cellset neighbor_boundary = edges(cells[cell]) - neighbor_connected_faces = neighbor_boundary[findall(x->global_vertexid ∈ x, neighbor_boundary)] - this_local_vertex = findfirst(i->toglobal(grid, VertexIndex(cell, i)) == global_vertexid, 1:nvertices(cells[cell])) + neighbor_connected_faces = neighbor_boundary[findall(x -> global_vertexid ∈ x, neighbor_boundary)] + this_local_vertex = findfirst(i -> toglobal(grid, VertexIndex(cell, i)) == global_vertexid, 1:nvertices(cells[cell])) push_at_index!(buf, VertexIndex(cell, this_local_vertex), global_vertexid) - other_vertices = findfirst.(x->x!=global_vertexid,neighbor_connected_faces) + other_vertices = findfirst.(x -> x != global_vertexid, neighbor_connected_faces) any(other_vertices .=== nothing) && continue neighbor_vertices_global = getindex.(neighbor_connected_faces, other_vertices) - neighbor_vertices_local = [VertexIndex(cell,local_vertex) for local_vertex ∈ findall(x->x ∈ neighbor_vertices_global, vertices(cells[cell]))] + neighbor_vertices_local = [VertexIndex(cell, local_vertex) for local_vertex in findall(x -> x ∈ neighbor_vertices_global, vertices(cells[cell]))] for vertex_index in neighbor_vertices_local push_at_index!(buf, vertex_index, global_vertexid) end @@ -360,9 +361,9 @@ unique facets in the grid. *Example:* With `BI=EdgeIndex`, and an edge between cells and 1 and 2, with vertices 2 and 5, could be described by either `EdgeIndex(1, 2)` or `EdgeIndex(2, 4)`, but only one of these will be in the vector returned by this function. """ -function _create_facet_skeleton(neighborhood::ArrayOfVectorViews{BI, 2}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} +function _create_facet_skeleton(neighborhood::ArrayOfVectorViews{BI, 2}) where {BI <: Union{FaceIndex, EdgeIndex, VertexIndex}} i = 1 - skeleton = Vector{FacetIndex}(undef, length(neighborhood) - count(neighbors -> !isempty(neighbors) , values(neighborhood)) ÷ 2) + skeleton = Vector{FacetIndex}(undef, length(neighborhood) - count(neighbors -> !isempty(neighbors), values(neighborhood)) ÷ 2) for (idx, entity) in pairs(neighborhood) isempty(entity) || entity[][1] > idx[1] || continue skeleton[i] = FacetIndex(idx[1], idx[2]) diff --git a/src/Grid/utils.jl b/src/Grid/utils.jl index c684409aaf..ce5937010e 100644 --- a/src/Grid/utils.jl +++ b/src/Grid/utils.jl @@ -19,11 +19,11 @@ addcellset!(grid, "right", x -> norm(x[1]) < 2.0 ) #add cell to cellset right, i ``` """ function addcellset!(grid::AbstractGrid, name::String, cellid::AbstractVecOrSet{Int}) - _addset!(grid, name, cellid, getcellsets(grid)) + return _addset!(grid, name, cellid, getcellsets(grid)) end -function addcellset!(grid::AbstractGrid, name::String, f::Function; all::Bool=true) - _addset!(grid, name, create_cellset(grid, f; all), getcellsets(grid)) +function addcellset!(grid::AbstractGrid, name::String, f::Function; all::Bool = true) + return _addset!(grid, name, create_cellset(grid, f; all), getcellsets(grid)) end """ @@ -57,8 +57,8 @@ addfacetset!(grid, "clamped", x -> norm(x[1]) ≈ 0.0) #see incompressible elast addfacetset!(grid::AbstractGrid, name::String, set::AbstractVecOrSet{FacetIndex}) = _addset!(grid, name, set, getfacetsets(grid)) -addfacetset!(grid::AbstractGrid, name::String, f::Function; all::Bool=true) = - _addset!(grid, name, create_facetset(grid, f; all=all), getfacetsets(grid)) +addfacetset!(grid::AbstractGrid, name::String, f::Function; all::Bool = true) = + _addset!(grid, name, create_facetset(grid, f; all = all), getfacetsets(grid)) """ addvertexset!(grid::AbstractGrid, name::String, faceid::AbstractVecOrSet{FaceIndex}) @@ -77,14 +77,14 @@ addvertexset!(grid::AbstractGrid, name::String, set::AbstractVecOrSet{VertexInde _addset!(grid, name, set, getvertexsets(grid)) addvertexset!(grid::AbstractGrid, name::String, f::Function) = - _addset!(grid, name, create_vertexset(grid, f; all=true), getvertexsets(grid)) + _addset!(grid, name, create_vertexset(grid, f; all = true), getvertexsets(grid)) function _addset!(grid::AbstractGrid, name::String, _set::AbstractVecOrSet, dict::Dict) _check_setname(dict, name) set = convert_to_orderedset(_set) _warn_emptyset(set, name) dict[name] = set - grid + return grid end """ @@ -116,7 +116,7 @@ function addboundaryfacetset!(grid::AbstractGrid, top::ExclusiveTopology, name:: return _addset!(grid, name, set, getfacetsets(grid)) end -function _create_set(f::Function, grid::AbstractGrid, ::Type{BI}; all=true) where {BI <: BoundaryIndex} +function _create_set(f::Function, grid::AbstractGrid, ::Type{BI}; all = true) where {BI <: BoundaryIndex} set = OrderedSet{BI}() # Since we loop over the cells in order the resulting set will be sorted # lexicographically based on the (cell_idx, entity_idx) tuple @@ -139,7 +139,7 @@ function push_entity_instances!(set::OrderedSet{BI}, grid::AbstractGrid, top::Ex push!(set, entity) # Add the given entity cell = getcells(grid, entity[1]) verts = boundaryfunction(BI)(cell)[entity[2]] - for cell_idx in top.vertex_to_cell[verts[1]]# Since all vertices should be shared, the first one can be used here + for cell_idx in top.vertex_to_cell[verts[1]] # Since all vertices should be shared, the first one can be used here cell_entities = boundaryfunction(BI)(getcells(grid, cell_idx)) for (entity_idx, cell_entity) in pairs(cell_entities) if all(x -> x in verts, cell_entity) @@ -162,7 +162,7 @@ function _create_boundaryset(f::Function, grid::AbstractGrid, top::ExclusiveTopo for (ff_nh_idx, neighborhood) in pairs(ff_nh) # ff_nh_idx::CartesianIndex into AbstractMatrix{AbstractVector{BI}} isempty(neighborhood) || continue # Skip any facets with neighbors (not on boundary) - cell_idx = ff_nh_idx[1] + cell_idx = ff_nh_idx[1] facet_nr = ff_nh_idx[2] cell = getcells(grid, cell_idx) facet_nodes = facets(cell)[facet_nr] @@ -182,7 +182,7 @@ function _create_boundaryset(f::Function, grid::AbstractGrid, top::ExclusiveTopo return _makeset(get_facet_facet_neighborhood(top, grid))::OrderedSet{BI} end -function create_cellset(grid::AbstractGrid, f::Function; all::Bool=true) +function create_cellset(grid::AbstractGrid, f::Function; all::Bool = true) cells = OrderedSet{Int}() # Since we loop over the cells in order the resulting set will be sorted for (i, cell) in enumerate(getcells(grid)) @@ -203,14 +203,14 @@ function create_nodeset(grid::AbstractGrid, f::Function) return nodes end create_vertexset(grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, VertexIndex; kwargs...) -create_edgeset( grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, EdgeIndex; kwargs...) -create_faceset( grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, FaceIndex; kwargs...) -create_facetset( grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, FacetIndex; kwargs...) +create_edgeset(grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, EdgeIndex; kwargs...) +create_faceset(grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, FaceIndex; kwargs...) +create_facetset(grid::AbstractGrid, f::Function; kwargs...) = _create_set(f, grid, FacetIndex; kwargs...) create_boundaryvertexset(grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, VertexIndex; kwargs...) -create_boundaryedgeset( grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, EdgeIndex; kwargs...) -create_boundaryfaceset( grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, FaceIndex; kwargs...) -create_boundaryfacetset( grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, FacetIndex; kwargs...) +create_boundaryedgeset(grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, EdgeIndex; kwargs...) +create_boundaryfaceset(grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, FaceIndex; kwargs...) +create_boundaryfacetset(grid::AbstractGrid, top::ExclusiveTopology, f::Function; kwargs...) = _create_boundaryset(f, grid, top, FacetIndex; kwargs...) """ bounding_box(grid::AbstractGrid) @@ -220,8 +220,8 @@ Returns the minimum and maximum vertex coordinates of the bounding box. """ function bounding_box(grid::AbstractGrid{dim}) where {dim} T = get_coordinate_eltype(grid) - min_vertex = Vec{dim}(i->typemax(T)) - max_vertex = Vec{dim}(i->typemin(T)) + min_vertex = Vec{dim}(i -> typemax(T)) + max_vertex = Vec{dim}(i -> typemin(T)) for node in getnodes(grid) x = get_node_coordinate(node) _max_tmp = max_vertex # avoid type instability diff --git a/src/L2_projection.jl b/src/L2_projection.jl index 69717fc699..2083a2bce7 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -104,9 +104,10 @@ Add an interpolation `ip` on the cells in `set` to the `L2Projector` `proj`. and defaults to a quadrature rule that integrates the mass-matrix exactly for the given interpolation `ip`. """ -function add!(proj::L2Projector, set::AbstractVecOrSet{Int}, ip::Interpolation; +function add!( + proj::L2Projector, set::AbstractVecOrSet{Int}, ip::Interpolation; qr_rhs::Union{QuadratureRule, Nothing}, qr_lhs::QuadratureRule = _mass_qr(ip) - ) + ) # Validate user input isclosed(proj) && error("The L2Projector is already closed") if qr_rhs !== nothing @@ -162,11 +163,12 @@ function _assemble_L2_matrix!(assembler, cellvalues::CellValues, sdh::SubDofHand Me = zeros(n, n) function symmetrize_to_lower!(K::Matrix) - for i in 1:size(K, 1) - for j in i+1:size(K, 1) - K[j, i] = K[i, j] - end - end + for i in 1:size(K, 1) + for j in (i + 1):size(K, 1) + K[j, i] = K[i, j] + end + end + return end ## Assemble contributions from each cell @@ -175,11 +177,11 @@ function _assemble_L2_matrix!(assembler, cellvalues::CellValues, sdh::SubDofHand reinit!(cellvalues, cell) ## ∭( v ⋅ u )dΩ - for q_point = 1:getnquadpoints(cellvalues) + for q_point in 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) - for j = 1:n + for j in 1:n v = shape_value(cellvalues, q_point, j) - for i = 1:j + for i in 1:j u = shape_value(cellvalues, q_point, i) Me[i, j] += v ⋅ u * dΩ end @@ -251,8 +253,9 @@ function project(p::L2Projector, vars::AbstractMatrix, args...) return project(p, collect(eachcol(vars)), args...) end -function _project(proj::L2Projector, vars::Union{AbstractVector{TC}, AbstractDict{Int, TC}}, qrs_rhs::Vector{<:QuadratureRule}) where - {TC <: AbstractVector{T}} where T <: Union{Number, AbstractTensor} +function _project(proj::L2Projector, vars::Union{AbstractVector{TC}, AbstractDict{Int, TC}}, qrs_rhs::Vector{<:QuadratureRule}) where { + T <: Union{Number, AbstractTensor}, TC <: AbstractVector{T}, + } # Sanity checks for user input isclosed(proj) || error("The L2Projector is not closed") @@ -273,7 +276,7 @@ function _project(proj::L2Projector, vars::Union{AbstractVector{TC}, AbstractDic return _project(proj, qrs_rhs, vars, M, T)::Vector{T} end -function _project(proj::L2Projector, qrs_rhs::Vector{<:QuadratureRule}, vars::Union{AbstractVector, AbstractDict}, M::Integer, ::Type{T}) where T +function _project(proj::L2Projector, qrs_rhs::Vector{<:QuadratureRule}, vars::Union{AbstractVector, AbstractDict}, M::Integer, ::Type{T}) where {T} f = zeros(ndofs(proj.dh), M) for (sdh, qr_rhs) in zip(proj.dh.subdofhandlers, qrs_rhs) ip_fun = only(sdh.field_interpolations) @@ -308,10 +311,10 @@ function assemble_proj_rhs!(f::Matrix, cellvalues::CellValues, sdh::SubDofHandle length(cell_vars) == nqp || error("The number of variables per cell doesn't match the number of quadrature points") reinit!(cellvalues, cell) - for q_point = 1:nqp + for q_point in 1:nqp dΩ = getdetJdV(cellvalues, q_point) qp_vars = cell_vars[q_point] - for i = 1:n + for i in 1:n v = shape_value(cellvalues, q_point, i) for j in 1:M fe[i, j] += v * get_data(qp_vars, j) * dΩ @@ -324,6 +327,7 @@ function assemble_proj_rhs!(f::Matrix, cellvalues::CellValues, sdh::SubDofHandle f[dof, :] += fe[num, :] end end + return end evaluate_at_grid_nodes(proj::L2Projector, vals::AbstractVector) = @@ -335,8 +339,8 @@ _evaluate_at_grid_nodes(proj::L2Projector, vals::AbstractVector{<:Number}, vtk) # Deal with projected tensors function _evaluate_at_grid_nodes( - proj::L2Projector, vals::AbstractVector{S}, ::Val{vtk} -) where {order, dim, T, M, S <: Union{Tensor{order,dim,T,M}, SymmetricTensor{order,dim,T,M}}, vtk} + proj::L2Projector, vals::AbstractVector{S}, ::Val{vtk} + ) where {order, dim, T, M, S <: Union{Tensor{order, dim, T, M}, SymmetricTensor{order, dim, T, M}}, vtk} dh = proj.dh # The internal dofhandler in the projector is a scalar field, but the values in vals # can be any tensor field, however, the number of dofs should always match the length of vals @@ -359,7 +363,7 @@ function _evaluate_at_grid_nodes( return data end -function _evaluate_at_grid_nodes!(data, cv, sdh, u::AbstractVector{S}) where S +function _evaluate_at_grid_nodes!(data, cv, sdh, u::AbstractVector{S}) where {S} ue = zeros(S, getnbasefunctions(cv)) for cell in CellIterator(sdh) reinit!(cv, cell) diff --git a/src/PointEvalHandler.jl b/src/PointEvalHandler.jl index 5388528a33..496cbc4e88 100644 --- a/src/PointEvalHandler.jl +++ b/src/PointEvalHandler.jl @@ -1,7 +1,7 @@ Base.@kwdef struct NewtonLineSearchPointFinder{T} max_iters::Int = 10 max_line_searches::Int = 5 - residual_tolerance::T = 1e-10 + residual_tolerance::T = 1.0e-10 end """ @@ -29,10 +29,10 @@ There are two ways to use the `PointEvalHandler` to evaluate functions: """ PointEvalHandler -struct PointEvalHandler{G,T<:Real} +struct PointEvalHandler{G, T <: Real} grid::G cells::Vector{Union{Nothing, Int}} - local_coords::Vector{Union{Nothing, Vec{1,T},Vec{2,T},Vec{3,T}}} + local_coords::Vector{Union{Nothing, Vec{1, T}, Vec{2, T}, Vec{3, T}}} end function Base.show(io::IO, ::MIME"text/plain", ph::PointEvalHandler) @@ -44,6 +44,7 @@ function Base.show(io::IO, ::MIME"text/plain", ph::PointEvalHandler) else print(io, " Could not find corresponding cell for ", n_missing, " points.") end + return end # Internals: @@ -54,19 +55,19 @@ end # - `newton_max_iters::Int`: Maximum number of inner Newton iterations. Default value: `10`. # - `newton_residual_tolerance`: Tolerance for the residual norm to indicate convergence in the # inner Newton solver. Default value: `1e-10`. -function PointEvalHandler(grid::AbstractGrid{dim}, points::AbstractVector{Vec{dim,T}}; search_nneighbors=3, warn::Bool=true, strategy = NewtonLineSearchPointFinder()) where {dim, T} +function PointEvalHandler(grid::AbstractGrid{dim}, points::AbstractVector{Vec{dim, T}}; search_nneighbors = 3, warn::Bool = true, strategy = NewtonLineSearchPointFinder()) where {dim, T} node_cell_dicts = _get_node_cell_map(grid) cells, local_coords = _get_cellcoords(points, grid, node_cell_dicts, search_nneighbors, warn, strategy) return PointEvalHandler(grid, cells, local_coords) end -function _get_cellcoords(points::AbstractVector{Vec{dim,T}}, grid::AbstractGrid, node_cell_dicts::Dict{C,Dict{Int, Vector{Int}}}, search_nneighbors, warn, strategy::NewtonLineSearchPointFinder) where {dim, T<:Real, C} +function _get_cellcoords(points::AbstractVector{Vec{dim, T}}, grid::AbstractGrid, node_cell_dicts::Dict{C, Dict{Int, Vector{Int}}}, search_nneighbors, warn, strategy::NewtonLineSearchPointFinder) where {dim, T <: Real, C} # set up tree structure for finding nearest nodes to points - kdtree = KDTree(reinterpret(Vec{dim,T}, getnodes(grid))) + kdtree = KDTree(reinterpret(Vec{dim, T}, getnodes(grid))) nearest_nodes, _ = knn(kdtree, points, search_nneighbors, true) cells = Vector{Union{Nothing, Int}}(nothing, length(points)) - local_coords = Vector{Union{Nothing, Vec{1, T},Vec{2, T},Vec{3, T}}}(nothing, length(points)) + local_coords = Vector{Union{Nothing, Vec{1, T}, Vec{2, T}, Vec{3, T}}}(nothing, length(points)) for point_idx in 1:length(points) cell_found = false @@ -110,10 +111,10 @@ function check_isoparametric_boundaries(::Type{RefSimplex{dim}}, x_local::Vec{di end cellcenter(::Type{<:RefHypercube{dim}}, _::Type{T}) where {dim, T} = zero(Vec{dim, T}) -cellcenter(::Type{<:RefSimplex{dim}}, _::Type{T}) where {dim, T} = Vec{dim, T}((ntuple(d->1/3, dim))) +cellcenter(::Type{<:RefSimplex{dim}}, _::Type{T}) where {dim, T} = Vec{dim, T}((ntuple(d -> 1 / 3, dim))) -_solve_helper(A::Tensor{2,dim}, b::Vec{dim}) where {dim} = inv(A) ⋅ b -_solve_helper(A::SMatrix{idim, odim}, b::Vec{idim,T}) where {odim, idim, T} = Vec{odim,T}(pinv(A) * b) +_solve_helper(A::Tensor{2, dim}, b::Vec{dim}) where {dim} = inv(A) ⋅ b +_solve_helper(A::SMatrix{idim, odim}, b::Vec{idim, T}) where {odim, idim, T} = Vec{odim, T}(pinv(A) * b) # See https://discourse.julialang.org/t/finding-the-value-of-a-field-at-a-spatial-location-in-juafem/38975/2 function find_local_coordinate(interpolation::Interpolation{refshape}, cell_coordinates::Vector{<:Vec{sdim}}, global_coordinate::Vec{sdim}, strategy::NewtonLineSearchPointFinder; warn::Bool = false) where {sdim, refshape} @@ -154,8 +155,8 @@ function find_local_coordinate(interpolation::Interpolation{refshape}, cell_coor best_residual_norm = norm(global_guess - global_coordinate) if !check_isoparametric_boundaries(refshape, new_local_guess, boundary_tolerance) # Search for the residual minimizer, which is still inside the element - for next_index ∈ 2:strategy.max_line_searches - new_local_guess = local_guess - Δξ/2^(next_index-1) + for next_index in 2:strategy.max_line_searches + new_local_guess = local_guess - Δξ / 2^(next_index - 1) global_guess = spatial_coordinate(interpolation, new_local_guess, cell_coordinates) residual_norm = norm(global_guess - global_coordinate) if residual_norm < best_residual_norm && check_isoparametric_boundaries(refshape, new_local_guess, boundary_tolerance) @@ -164,7 +165,7 @@ function find_local_coordinate(interpolation::Interpolation{refshape}, cell_coor end end end - local_guess -= Δξ / 2^(best_index-1) + local_guess -= Δξ / 2^(best_index - 1) # Late convergence check if best_residual_norm ≤ strategy.residual_tolerance converged = check_isoparametric_boundaries(refshape, local_guess, boundary_tolerance) @@ -189,7 +190,7 @@ function _get_node_cell_map(grid::AbstractGrid) cell_dicts = Dict{Type{<:C}, Dict{Int, Vector{Int}}}() ctypes = Set{Type{<:C}}(typeof(c) for c in cells) for ctype in ctypes - cell_dict = cell_dicts[ctype] = Dict{Int,Vector{Int}}() + cell_dict = cell_dicts[ctype] = Dict{Int, Vector{Int}}() for (cellidx, cell) in enumerate(cells) cell isa ctype || continue for node in cell.nodes @@ -218,11 +219,13 @@ have `NaN`s for the corresponding entries in the output vector. evaluate_at_points function evaluate_at_points(ph::PointEvalHandler, proj::L2Projector, dof_vals::AbstractVector) - evaluate_at_points(ph, proj.dh, dof_vals) + return evaluate_at_points(ph, proj.dh, dof_vals) end -function evaluate_at_points(ph::PointEvalHandler{<:Any, T1}, dh::AbstractDofHandler, dof_vals::AbstractVector{T2}, - fname::Symbol=find_single_field(dh)) where {T1, T2} +function evaluate_at_points( + ph::PointEvalHandler{<:Any, T1}, dh::AbstractDofHandler, dof_vals::AbstractVector{T2}, + fname::Symbol = find_single_field(dh) + ) where {T1, T2} npoints = length(ph.cells) # Figure out the value type by creating a dummy PointValues ip = getfieldinterpolation(dh, find_field(dh, fname)) @@ -244,12 +247,13 @@ function find_single_field(dh) end # values in dof-order. They must be obtained from the same DofHandler that was used for constructing the PointEvalHandler -function evaluate_at_points!(out_vals::Vector{T2}, - ph::PointEvalHandler{<:Any, T_ph}, - dh::DofHandler, - dof_vals::Vector{T}, - fname::Symbol, - func_interpolations +function evaluate_at_points!( + out_vals::Vector{T2}, + ph::PointEvalHandler{<:Any, T_ph}, + dh::DofHandler, + dof_vals::Vector{T}, + fname::Symbol, + func_interpolations ) where {T2, T_ph, T} # TODO: I don't think this is correct?? @@ -271,14 +275,14 @@ end # function barrier with concrete type of PointValues function _evaluate_at_points!( - out_vals::Vector{T2}, - dof_vals::Vector{T}, - ph::PointEvalHandler, - dh::AbstractDofHandler, - pv::PointValues, - cellset::Union{Nothing, AbstractSet{Int}}, - dofrange::AbstractRange{Int}, - ) where {T2,T} + out_vals::Vector{T2}, + dof_vals::Vector{T}, + ph::PointEvalHandler, + dh::AbstractDofHandler, + pv::PointValues, + cellset::Union{Nothing, AbstractSet{Int}}, + dofrange::AbstractRange{Int}, + ) where {T2, T} # extract variables local_coords = ph.local_coords @@ -310,7 +314,7 @@ function _evaluate_at_points!( end function get_func_interpolations(dh::DofHandler, fieldname) - func_interpolations = Union{Interpolation,Nothing}[] + func_interpolations = Union{Interpolation, Nothing}[] for sdh in dh.subdofhandlers j = _find_field(sdh, fieldname) if j === nothing @@ -346,14 +350,14 @@ end """ PointIterator -struct PointIterator{PH<:PointEvalHandler, V <: Vec} +struct PointIterator{PH <: PointEvalHandler, V <: Vec} ph::PH coords::Vector{V} end -function PointIterator(ph::PointEvalHandler{G}) where {D,C,T,G<:Grid{D,C,T}} +function PointIterator(ph::PointEvalHandler{G}) where {D, C, T, G <: Grid{D, C, T}} n = nnodes_per_cell(ph.grid) - coords = zeros(Vec{D,T}, n) # resize!d later if needed + coords = zeros(Vec{D, T}, n) # resize!d later if needed return PointIterator(ph, coords) end diff --git a/src/PoolAllocator.jl b/src/PoolAllocator.jl index c0fb8b19e2..fba1cdd2e0 100644 --- a/src/PoolAllocator.jl +++ b/src/PoolAllocator.jl @@ -12,7 +12,7 @@ mutable struct Page{T} const blocksize::Int # blocksize for this page const freelist::BitVector # block is free/used n_free::Int # number of free blocks - function Page{T}(blocksize::Int) where T + function Page{T}(blocksize::Int) where {T} @assert isbitstype(T) buf = Vector{T}(undef, PAGE_SIZE ÷ sizeof(T)) n_blocks, r = divrem(length(buf), blocksize) @@ -80,7 +80,7 @@ end struct MemoryPool{T} books::Vector{Book{T}} # blocksizes 2, 4, 6, 8, ... - function MemoryPool{T}() where T + function MemoryPool{T}() where {T} mempool = new(Book{T}[]) return mempool end @@ -96,7 +96,7 @@ function free(mempool::MemoryPool) return end -function mempool_stats(mempool::MemoryPool{T}) where T +function mempool_stats(mempool::MemoryPool{T}) where {T} bytes_used = 0 bytes_allocated = 0 for bookidx in 1:length(mempool.books) @@ -110,7 +110,7 @@ function mempool_stats(mempool::MemoryPool{T}) where T return bytes_used, bytes_allocated end -function Base.show(io::IO, ::MIME"text/plain", mempool::MemoryPool{T}) where T +function Base.show(io::IO, ::MIME"text/plain", mempool::MemoryPool{T}) where {T} n_books = count(i -> isassigned(mempool.books, i), 1:length(mempool.books)) print(io, "PoolAllocator.MemoryPool{$(T)} with $(n_books) fixed size pools") n_books == 0 && return @@ -121,7 +121,7 @@ function Base.show(io::IO, ::MIME"text/plain", mempool::MemoryPool{T}) where T blocksize = h.blocksize # @assert blocksize == 2^idx npages = length(h.pages) - n_free = mapreduce(p -> p.n_free, +, h.pages; init=0) + n_free = mapreduce(p -> p.n_free, +, h.pages; init = 0) n_tot = npages * PAGE_SIZE ÷ blocksize ÷ sizeof(T) println(io, " - blocksize: $(blocksize), npages: $(npages), usage: $(n_tot - n_free) / $(n_tot)") end @@ -164,10 +164,10 @@ const PoolVector{T} = PoolArray{T, 1} # Constructors function malloc(mempool::MemoryPool, dim1::Int) - return malloc(mempool, (dim1, )) + return malloc(mempool, (dim1,)) end function malloc(mempool::MemoryPool, dim1::Int, dim2::Int, dimx::Int...) - dims = (dim1, dim2, map(Int, dimx)..., ) + dims = (dim1, dim2, map(Int, dimx)...) return malloc(mempool, dims) end @@ -176,7 +176,7 @@ function free(x::PoolArray) return end -function realloc(x::PoolArray{T}, newsize::Int) where T +function realloc(x::PoolArray{T}, newsize::Int) where {T} @assert newsize > length(x) # TODO: Allow shrinkage? @assert newsize <= PAGE_SIZE ÷ sizeof(T) # TODO: Might be required # Find the page for the block to make sure it was allocated in this mempool @@ -198,22 +198,22 @@ Base.IndexStyle(::Type{<:PoolArray}) = IndexLinear() @boundscheck checkbounds(mv, i) return @inbounds mv.page.buf[mv.offset + i] end -@propagate_inbounds function Base.setindex!(mv::PoolArray{T}, v::T, i::Int) where T +@propagate_inbounds function Base.setindex!(mv::PoolArray{T}, v::T, i::Int) where {T} @boundscheck checkbounds(mv, i) @inbounds mv.page.buf[mv.offset + i] = v return mv end # Utilities needed for the sparsity pattern -@inline function resize(x::PoolVector{T}, n::Int) where T +@inline function resize(x::PoolVector{T}, n::Int) where {T} if n > allocated_length(x) return realloc(x, n) else - return PoolVector{T}(x.mempool, x.page, x.offset, (n, )) + return PoolVector{T}(x.mempool, x.page, x.offset, (n,)) end end -@inline function insert(x::PoolVector{T}, k::Int, item::T) where T +@inline function insert(x::PoolVector{T}, k::Int, item::T) where {T} lx = length(x) # Make room x = resize(x, lx + 1) diff --git a/src/Quadrature/gaussquad_prism_table.jl b/src/Quadrature/gaussquad_prism_table.jl index 3d7488b0d1..6479cdcf77 100644 --- a/src/Quadrature/gaussquad_prism_table.jl +++ b/src/Quadrature/gaussquad_prism_table.jl @@ -1,3 +1,4 @@ +# runic: off # Symmetric quadrature rules takes from # Witherden, Freddie D., and Peter E. Vincent. "On the identification of # symmetric quadrature rules for finite element methods." Computers & diff --git a/src/Quadrature/gaussquad_pyramid_table.jl b/src/Quadrature/gaussquad_pyramid_table.jl index 77f773d2c0..6717f7fc7d 100644 --- a/src/Quadrature/gaussquad_pyramid_table.jl +++ b/src/Quadrature/gaussquad_pyramid_table.jl @@ -1,3 +1,4 @@ +# runic: off # Symmetric quadrature rules takes from # Witherden, Freddie D., and Peter E. Vincent. "On the identification of # symmetric quadrature rules for finite element methods." Computers & diff --git a/src/Quadrature/gaussquad_tet_table.jl b/src/Quadrature/gaussquad_tet_table.jl index 0694caa432..6729492cea 100644 --- a/src/Quadrature/gaussquad_tet_table.jl +++ b/src/Quadrature/gaussquad_tet_table.jl @@ -1,3 +1,4 @@ +# runic: off # Yu, Jinyun. Symmetric Gaussian Quadrature Formulae for Tetrahedronal Regions. 1984. CMAME. function _get_jinyun_tet_quadrature_data(n::Int) if n == 1 diff --git a/src/Quadrature/gaussquad_tri_table.jl b/src/Quadrature/gaussquad_tri_table.jl index 7be355ce45..d0a1232d5c 100644 --- a/src/Quadrature/gaussquad_tri_table.jl +++ b/src/Quadrature/gaussquad_tri_table.jl @@ -1,3 +1,4 @@ +# runic: off # Order 1 to 8 heights points / wheights have been suggested in # Dunavant, D. A. (1985), High degree efficient symmetrical Gaussian quadrature # rules for the triangle. Int. J. Numer. Meth. Engng., 21: 1129–1148. doi: diff --git a/src/Quadrature/generate_quadrature.jl b/src/Quadrature/generate_quadrature.jl index 1da7a1216a..9c81e59383 100644 --- a/src/Quadrature/generate_quadrature.jl +++ b/src/Quadrature/generate_quadrature.jl @@ -69,20 +69,19 @@ module GaussQuadrature # Hall, Englewood Cliffs, N.J., 1966. - """ Enumeration type used to specify which endpoints of the integration interval should be included amongst the quadrature points: neither, left, right or both. """ struct EndPt - label :: Char + label::Char end const neither = EndPt('N') -const left = EndPt('L') -const right = EndPt('R') -const both = EndPt('B') +const left = EndPt('L') +const right = EndPt('R') +const both = EndPt('B') # Maximum number of QL iterations used by steig!. # You might need to increase this. @@ -95,7 +94,7 @@ for the interval -1 < x < 1 with weight function w(x) = 1. Use endpt=left, right, both for the left Radau, right Radau, Lobatto rules. """ -function legendre(::Type{T}, n::Integer, endpt::EndPt=neither) where {T<:AbstractFloat} +function legendre(::Type{T}, n::Integer, endpt::EndPt = neither) where {T <: AbstractFloat} a, b, muzero = legendre_coeff(T, n, endpt) return custom_gauss_rule(-one(T), one(T), a, b, muzero, endpt) end @@ -104,14 +103,14 @@ end x, w = legendre(n, endpt=neither) Convenience function with type T = Float64. """ -legendre(n, endpt=neither) = legendre(Float64, n, endpt) +legendre(n, endpt = neither) = legendre(Float64, n, endpt) -function legendre_coeff(::Type{T}, n::Integer, endpt::EndPt) where {T<:AbstractFloat} +function legendre_coeff(::Type{T}, n::Integer, endpt::EndPt) where {T <: AbstractFloat} muzero = convert(T, 2.0) a = zeros(T, n) b = zeros(T, n) - for i = 1:n - b[i] = i / sqrt(convert(T, 4*i^2-1)) + for i in 1:n + b[i] = i / sqrt(convert(T, 4 * i^2 - 1)) end return a, b, muzero end @@ -125,7 +124,7 @@ for the interval -1 < x < 1 with weight function Use endpt=left, right, both for the left Radau, right Radau, Lobatto rules. """ -function chebyshev(::Type{T}, n::Integer, kind::Integer=1, endpt::EndPt=neither) where {T<:AbstractFloat} +function chebyshev(::Type{T}, n::Integer, kind::Integer = 1, endpt::EndPt = neither) where {T <: AbstractFloat} a, b, muzero = chebyshev_coeff(T, n, kind, endpt) return custom_gauss_rule(-one(T), one(T), a, b, muzero, endpt) end @@ -134,9 +133,9 @@ end x, w = chebyshev(n, kind=1, endpt=neither) Convenience function with type T = Float64. """ -chebyshev(n, kind=1, endpt=neither) = chebyshev(Float64, n, kind, endpt) +chebyshev(n, kind = 1, endpt = neither) = chebyshev(Float64, n, kind, endpt) -function chebyshev_coeff(::Type{T}, n::Integer, kind::Integer, endpt::EndPt) where {T<:AbstractFloat} +function chebyshev_coeff(::Type{T}, n::Integer, kind::Integer, endpt::EndPt) where {T <: AbstractFloat} muzero = convert(T, pi) half = convert(T, 0.5) a = zeros(T, n) @@ -152,9 +151,11 @@ function chebyshev_coeff(::Type{T}, n::Integer, kind::Integer, endpt::EndPt) whe end -function custom_gauss_rule(lo::T, hi::T, - a::Array{T,1}, b::Array{T,1}, muzero::T, endpt::EndPt, - maxits::Integer=maxiterations[T]) where {T<:AbstractFloat} +function custom_gauss_rule( + lo::T, hi::T, + a::Array{T, 1}, b::Array{T, 1}, muzero::T, endpt::EndPt, + maxits::Integer = maxiterations[T] + ) where {T <: AbstractFloat} # # On entry: # @@ -183,33 +184,33 @@ function custom_gauss_rule(lo::T, hi::T, if n == 1 a[1] = lo else - a[n] = solve(n, lo, a, b) * b[n-1]^2 + lo + a[n] = solve(n, lo, a, b) * b[n - 1]^2 + lo end elseif endpt == right if n == 1 a[1] = hi else - a[n] = solve(n, hi, a, b) * b[n-1]^2 + hi + a[n] = solve(n, hi, a, b) * b[n - 1]^2 + hi end elseif endpt == both if n == 1 error("Must have at least two points for both ends.") end g = solve(n, lo, a, b) - t1 = ( hi - lo ) / ( g - solve(n, hi, a, b) ) - b[n-1] = sqrt(t1) + t1 = (hi - lo) / (g - solve(n, hi, a, b)) + b[n - 1] = sqrt(t1) a[n] = lo + g * t1 end w = zero(a) steig!(a, b, w, maxits) - for i = 1:n + for i in 1:n w[i] = muzero * w[i]^2 end idx = sortperm(a) return a[idx], w[idx] end -function solve(n::Integer, shift::T, a::Array{T,1}, b::Array{T,1}) where {T<:AbstractFloat} +function solve(n::Integer, shift::T, a::Array{T, 1}, b::Array{T, 1}) where {T <: AbstractFloat} # # Perform elimination to find the nth component s = delta[n] # of the solution to the nxn linear system @@ -221,13 +222,13 @@ function solve(n::Integer, shift::T, a::Array{T,1}, b::Array{T,1}) where {T<:Abs # standard basis vector. # t = a[1] - shift - for i = 2:n-1 - t = a[i] - shift - b[i-1]^2 / t + for i in 2:(n - 1) + t = a[i] - shift - b[i - 1]^2 / t end return one(t) / t end -function steig!(d::Array{T,1}, e::Array{T,1}, z::Array{T,1}, maxits::Integer) where {T<:AbstractFloat} +function steig!(d::Array{T, 1}, e::Array{T, 1}, z::Array{T, 1}, maxits::Integer) where {T <: AbstractFloat} # # Finds the eigenvalues and first components of the normalised # eigenvectors of a symmetric tridiagonal matrix by the implicit @@ -260,12 +261,12 @@ function steig!(d::Array{T,1}, e::Array{T,1}, z::Array{T,1}, maxits::Integer) wh if n == 1 # Nothing to do for a 1x1 matrix. return end - for l = 1:n - for j = 1:maxits + for l in 1:n + for j in 1:maxits # Look for small off-diagonal elements. m = n - for i = l:n-1 - if abs(e[i]) <= eps(T) * ( abs(d[i]) + abs(d[i+1]) ) + for i in l:(n - 1) + if abs(e[i]) <= eps(T) * (abs(d[i]) + abs(d[i + 1])) m = i break end @@ -280,63 +281,68 @@ function steig!(d::Array{T,1}, e::Array{T,1}, z::Array{T,1}, maxits::Integer) wh error(msg) end # Form shift - g = ( d[l+1] - p ) / ( 2 * e[l] ) + g = (d[l + 1] - p) / (2 * e[l]) r = hypot(g, one(T)) - g = d[m] - p + e[l] / ( g + copysign(r, g) ) + g = d[m] - p + e[l] / (g + copysign(r, g)) s = one(T) c = one(T) p = zero(T) - for i = m-1:-1:l + for i in (m - 1):-1:l f = s * e[i] b = c * e[i] - if abs(f) < abs(g) + if abs(f) < abs(g) s = f / g r = hypot(s, one(T)) - e[i+1] = g * r + e[i + 1] = g * r c = one(T) / r s *= c else c = g / f r = hypot(c, one(T)) - e[i+1] = f * r + e[i + 1] = f * r s = one(T) / r c *= s end - g = d[i+1] - p - r = ( d[i] - g ) * s + 2 * c * b + g = d[i + 1] - p + r = (d[i] - g) * s + 2 * c * b p = s * r - d[i+1] = g + p + d[i + 1] = g + p g = c * r - b # Form first component of vector. - f = z[i+1] - z[i+1] = s * z[i] + c * f - z[i] = c * z[i] - s * f + f = z[i + 1] + z[i + 1] = s * z[i] + c * f + z[i] = c * z[i] - s * f end # loop over i d[l] -= p e[l] = g e[m] = zero(T) end # loop over j end # loop over l + return end -function orthonormal_poly(x::Array{T,1}, - a::Array{T,1}, b::Array{T,1}, muzero::T) where {T<:AbstractFloat} +function orthonormal_poly( + x::Array{T, 1}, + a::Array{T, 1}, b::Array{T, 1}, muzero::T + ) where {T <: AbstractFloat} # p[i,j] = value at x[i] of orthonormal polynomial of degree j-1. m = length(x) n = length(a) - p = zeros(T, m, n+1) + p = zeros(T, m, n + 1) c = one(T) / sqrt(muzero) rb = one(T) / b[1] - for i = 1:m - p[i,1] = c - p[i,2] = rb * ( x[i] - a[1] ) * c + for i in 1:m + p[i, 1] = c + p[i, 2] = rb * (x[i] - a[1]) * c end - for j = 2:n - rb = one(T) / b[j] - for i = 1:m - p[i,j+1] = rb * ( (x[i]-a[j]) * p[i,j] - - b[j-1] * p[i,j-1] ) - end + for j in 2:n + rb = one(T) / b[j] + for i in 1:m + p[i, j + 1] = rb * ( + (x[i] - a[j]) * p[i, j] + - b[j - 1] * p[i, j - 1] + ) + end end return p end diff --git a/src/Quadrature/quadrature.jl b/src/Quadrature/quadrature.jl index 27379bddfa..dae3cc9795 100644 --- a/src/Quadrature/quadrature.jl +++ b/src/Quadrature/quadrature.jl @@ -56,7 +56,7 @@ struct QuadratureRule{shape, WeightStorageType, PointStorageType} if length(weights) != length(points) throw(ArgumentError("number of weights and number of points do not match (#weights=$(length(weights)) != #points=$(length(points)))")) end - new{shape, typeof(weights), typeof(points)}(weights, points) + return new{shape, typeof(weights), typeof(points)}(weights, points) end end @@ -81,7 +81,7 @@ end # over all dimensions for dim in 1:3 @eval begin - function QuadratureRule{RefHypercube{$dim}}(::Type{T}, quad_type::Symbol, order::Int) where T + function QuadratureRule{RefHypercube{$dim}}(::Type{T}, quad_type::Symbol, order::Int) where {T} if quad_type === :legendre p, w = GaussQuadrature.legendre(T, order) elseif quad_type === :lobatto @@ -90,13 +90,13 @@ for dim in 1:3 throw(ArgumentError("unsupported quadrature rule")) end weights = Vector{T}(undef, order^($dim)) - points = Vector{Vec{$dim,T}}(undef, order^($dim)) + points = Vector{Vec{$dim, T}}(undef, order^($dim)) count = 1 - @nloops $dim i j->(1:order) begin - t = @ntuple $dim q-> p[$(Symbol("i"*"_q"))] - points[count] = Vec{$dim,T}(t) + @nloops $dim i j -> (1:order) begin + t = @ntuple $dim q -> p[$(Symbol("i" * "_q"))] + points[count] = Vec{$dim, T}(t) weight = 1.0 - @nexprs $dim j->(weight *= w[i_{j}]) + @nexprs $dim j -> (weight *= w[i_{j}]) weights[count] = weight count += 1 end @@ -107,7 +107,7 @@ end for dim in 2:3 @eval begin - function QuadratureRule{RefSimplex{$dim}}(::Type{T}, quad_type::Symbol, order::Int) where T + function QuadratureRule{RefSimplex{$dim}}(::Type{T}, quad_type::Symbol, order::Int) where {T} if $dim == 2 && quad_type === :dunavant data = _get_dunavant_gauss_tridata(order) elseif $dim == 2 && quad_type === :gaussjacobi @@ -121,50 +121,50 @@ for dim in 2:3 else throw(ArgumentError("unsupported quadrature rule")) end - n_points = size(data,1) - points = Vector{Vec{$dim,T}}(undef, n_points) + n_points = size(data, 1) + points = Vector{Vec{$dim, T}}(undef, n_points) for p in 1:size(data, 1) - points[p] = Vec{$dim,T}(@ntuple $dim i -> data[p, i]) + points[p] = Vec{$dim, T}(@ntuple $dim i -> data[p, i]) end weights = T.(data[:, $dim + 1]) - QuadratureRule{RefSimplex{$dim}}(weights, points) + return QuadratureRule{RefSimplex{$dim}}(weights, points) end end end # Grab prism quadrature rule from table -function QuadratureRule{RefPrism}(::Type{T}, quad_type::Symbol, order::Int) where T +function QuadratureRule{RefPrism}(::Type{T}, quad_type::Symbol, order::Int) where {T} if quad_type == :polyquad data = _get_gauss_prismdata_polyquad(order) else throw(ArgumentError("unsupported quadrature rule")) end - n_points = size(data,1) - points = Vector{Vec{3,T}}(undef, n_points) + n_points = size(data, 1) + points = Vector{Vec{3, T}}(undef, n_points) for p in 1:size(data, 1) - points[p] = Vec{3,T}(@ntuple 3 i -> data[p, i]) + points[p] = Vec{3, T}(@ntuple 3 i -> data[p, i]) end weights = T.(data[:, 4]) - QuadratureRule{RefPrism}(weights, points) + return QuadratureRule{RefPrism}(weights, points) end # Grab pyramid quadrature rule from table -function QuadratureRule{RefPyramid}(::Type{T}, quad_type::Symbol, order::Int) where T +function QuadratureRule{RefPyramid}(::Type{T}, quad_type::Symbol, order::Int) where {T} if quad_type == :polyquad data = _get_gauss_pyramiddata_polyquad(order) else throw(ArgumentError("unsupported quadrature rule")) end - n_points = size(data,1) - points = Vector{Vec{3,T}}(undef, n_points) + n_points = size(data, 1) + points = Vector{Vec{3, T}}(undef, n_points) for p in 1:size(data, 1) - points[p] = Vec{3,T}(@ntuple 3 i -> data[p, i]) + points[p] = Vec{3, T}(@ntuple 3 i -> data[p, i]) end weights = T.(data[:, 4]) - QuadratureRule{RefPyramid}(weights, points) + return QuadratureRule{RefPyramid}(weights, points) end ###################### @@ -208,46 +208,52 @@ end # For RefShapes with equal face-shapes: generate quad rule for the face shape # and expand to each face -function FacetQuadratureRule{RefLine}(::Type{T}, ::Int) where T +function FacetQuadratureRule{RefLine}(::Type{T}, ::Int) where {T} w, p = T[1], Vec{0, T}[Vec{0, T}(())] return create_facet_quad_rule(RefLine, w, p) end -FacetQuadratureRule{RefQuadrilateral}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefQuadrilateral}(T,_default_quadrature_rule(RefLine),order) -function FacetQuadratureRule{RefQuadrilateral}(::Type{T}, quad_type::Symbol, order::Int) where T +FacetQuadratureRule{RefQuadrilateral}(::Type{T}, order::Int) where {T} = FacetQuadratureRule{RefQuadrilateral}(T, _default_quadrature_rule(RefLine), order) +function FacetQuadratureRule{RefQuadrilateral}(::Type{T}, quad_type::Symbol, order::Int) where {T} qr = QuadratureRule{RefLine}(T, quad_type, order) return create_facet_quad_rule(RefQuadrilateral, qr.weights, qr.points) end -FacetQuadratureRule{RefHexahedron}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefHexahedron}(T,_default_quadrature_rule(RefQuadrilateral),order) -function FacetQuadratureRule{RefHexahedron}(::Type{T}, quad_type::Symbol, order::Int) where T +FacetQuadratureRule{RefHexahedron}(::Type{T}, order::Int) where {T} = FacetQuadratureRule{RefHexahedron}(T, _default_quadrature_rule(RefQuadrilateral), order) +function FacetQuadratureRule{RefHexahedron}(::Type{T}, quad_type::Symbol, order::Int) where {T} qr = QuadratureRule{RefQuadrilateral}(T, quad_type, order) return create_facet_quad_rule(RefHexahedron, qr.weights, qr.points) end -FacetQuadratureRule{RefTriangle}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefTriangle}(T,_default_quadrature_rule(RefLine),order) -function FacetQuadratureRule{RefTriangle}(::Type{T}, quad_type::Symbol, order::Int) where T +FacetQuadratureRule{RefTriangle}(::Type{T}, order::Int) where {T} = FacetQuadratureRule{RefTriangle}(T, _default_quadrature_rule(RefLine), order) +function FacetQuadratureRule{RefTriangle}(::Type{T}, quad_type::Symbol, order::Int) where {T} qr = QuadratureRule{RefLine}(T, quad_type, order) # Interval scaled and shifted in facet_to_element_transformation from (-1,1) to (0,1) -> half the length -> half quadrature weights - return create_facet_quad_rule(RefTriangle, qr.weights/2, qr.points) + return create_facet_quad_rule(RefTriangle, qr.weights / 2, qr.points) end -FacetQuadratureRule{RefTetrahedron}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefTetrahedron}(T,_default_quadrature_rule(RefTriangle),order) -function FacetQuadratureRule{RefTetrahedron}(::Type{T}, quad_type::Symbol, order::Int) where T +FacetQuadratureRule{RefTetrahedron}(::Type{T}, order::Int) where {T} = FacetQuadratureRule{RefTetrahedron}(T, _default_quadrature_rule(RefTriangle), order) +function FacetQuadratureRule{RefTetrahedron}(::Type{T}, quad_type::Symbol, order::Int) where {T} qr = QuadratureRule{RefTriangle}(T, quad_type, order) return create_facet_quad_rule(RefTetrahedron, qr.weights, qr.points) end -FacetQuadratureRule{RefPrism}(::Type{T}, order::Int) where T = _FacetQuadratureRulePrism(T,(_default_quadrature_rule(RefTriangle), _default_quadrature_rule(RefQuadrilateral)),order) -function _FacetQuadratureRulePrism(::Type{T}, quad_types::Tuple{Symbol,Symbol}, order::Int) where T +FacetQuadratureRule{RefPrism}(::Type{T}, order::Int) where {T} = _FacetQuadratureRulePrism(T, (_default_quadrature_rule(RefTriangle), _default_quadrature_rule(RefQuadrilateral)), order) +function _FacetQuadratureRulePrism(::Type{T}, quad_types::Tuple{Symbol, Symbol}, order::Int) where {T} qr_quad = QuadratureRule{RefQuadrilateral}(T, quad_types[2], order) qr_tri = QuadratureRule{RefTriangle}(T, quad_types[1], order) # Interval scaled and shifted in facet_to_element_transformation for quadrilateral faces from (-1,1)² to (0,1)² -> quarter the area -> quarter the quadrature weights - return create_facet_quad_rule(RefPrism, [2,3,4], qr_quad.weights/4, qr_quad.points, - [1,5], qr_tri.weights, qr_tri.points) + return create_facet_quad_rule( + RefPrism, + [2, 3, 4], qr_quad.weights / 4, qr_quad.points, + [1, 5], qr_tri.weights, qr_tri.points + ) end -FacetQuadratureRule{RefPyramid}(::Type{T}, order::Int) where T = _FacetQuadratureRulePyramid(T,(_default_quadrature_rule(RefTriangle), _default_quadrature_rule(RefQuadrilateral)),order) -function _FacetQuadratureRulePyramid(::Type{T}, quad_types::Tuple{Symbol,Symbol}, order::Int) where T +FacetQuadratureRule{RefPyramid}(::Type{T}, order::Int) where {T} = _FacetQuadratureRulePyramid(T, (_default_quadrature_rule(RefTriangle), _default_quadrature_rule(RefQuadrilateral)), order) +function _FacetQuadratureRulePyramid(::Type{T}, quad_types::Tuple{Symbol, Symbol}, order::Int) where {T} qr_quad = QuadratureRule{RefQuadrilateral}(T, quad_types[2], order) qr_tri = QuadratureRule{RefTriangle}(T, quad_types[1], order) # Interval scaled and shifted in facet_to_element_transformation for quadrilateral faces from (-1,1)² to (0,1)² -> quarter the area -> quarter the quadrature weights - return create_facet_quad_rule(RefPyramid, [1], qr_quad.weights/4, qr_quad.points, - [2,3,4,5], qr_tri.weights, qr_tri.points) + return create_facet_quad_rule( + RefPyramid, + [1], qr_quad.weights / 4, qr_quad.points, + [2, 3, 4, 5], qr_tri.weights, qr_tri.points + ) end ################## @@ -309,7 +315,7 @@ julia> getpoints(qr) getpoints(qr::QuadratureRule) = qr.points getpoints(qr::FacetQuadratureRule, face::Int) = getpoints(qr.face_rules[face]) -getrefshape(::QuadratureRule{RefShape}) where RefShape = RefShape +getrefshape(::QuadratureRule{RefShape}) where {RefShape} = RefShape # TODO: This is used in copy(::(Cell|Face)Values), but it it useful to get an actual copy? Base.copy(qr::Union{QuadratureRule, FacetQuadratureRule}) = qr diff --git a/src/arrayutils.jl b/src/arrayutils.jl index c9e28804b3..f5b1bf868c 100644 --- a/src/arrayutils.jl +++ b/src/arrayutils.jl @@ -24,19 +24,19 @@ Fallback: `A[i, j] += v`. """ addindex! -function addindex!(A::AbstractMatrix{T}, v, i::Integer, j::Integer) where T +function addindex!(A::AbstractMatrix{T}, v, i::Integer, j::Integer) where {T} return addindex!(A, T(v), Int(i), Int(j)) end -function addindex!(A::AbstractMatrix{T}, v::T, i::Int, j::Int) where T +function addindex!(A::AbstractMatrix{T}, v::T, i::Int, j::Int) where {T} iszero(v) && return A A[i, j] += v return A end -function addindex!(b::AbstractVector{T}, v, i::Integer) where T +function addindex!(b::AbstractVector{T}, v, i::Integer) where {T} return addindex!(b, T(v), Int(i)) end -function addindex!(b::AbstractVector{T}, v::T, i::Int) where T +function addindex!(b::AbstractVector{T}, v::T, i::Int) where {T} iszero(v) && return b b[i] += v return b @@ -51,7 +51,7 @@ Fallback: `fill!(A, zero(T))`. """ fillzero!(A) -function fillzero!(A::AbstractVecOrMat{T}) where T +function fillzero!(A::AbstractVecOrMat{T}) where {T} return fill!(A, zero(T)) end @@ -59,13 +59,13 @@ end ## SparseArrays.SparseMatrixCSC ## ################################## -function addindex!(A::SparseMatrixCSC{Tv}, v::Tv, i::Int, j::Int) where Tv +function addindex!(A::SparseMatrixCSC{Tv}, v::Tv, i::Int, j::Int) where {Tv} @boundscheck checkbounds(A, i, j) # Return early if v is 0 iszero(v) && return A # Search column j for row i coljfirstk = Int(SparseArrays.getcolptr(A)[j]) - coljlastk = Int(SparseArrays.getcolptr(A)[j+1] - 1) + coljlastk = Int(SparseArrays.getcolptr(A)[j + 1] - 1) searchk = searchsortedfirst(rowvals(A), i, coljfirstk, coljlastk, Base.Order.Forward) if searchk <= coljlastk && rowvals(A)[searchk] == i # Column j contains entry A[i,j]. Update and return. @@ -77,11 +77,11 @@ function addindex!(A::SparseMatrixCSC{Tv}, v::Tv, i::Int, j::Int) where Tv end end -function fillzero!(A::SparseMatrixCSC{T}) where T +function fillzero!(A::SparseMatrixCSC{T}) where {T} fill!(nonzeros(A), zero(T)) return A end -function fillzero!(A::Symmetric{T,<:SparseMatrixCSC}) where T +function fillzero!(A::Symmetric{T, <:SparseMatrixCSC}) where {T} fillzero!(A.data) return A end diff --git a/src/assembler.jl b/src/assembler.jl index 06f4da881f..078f5fcd34 100644 --- a/src/assembler.jl +++ b/src/assembler.jl @@ -105,8 +105,8 @@ function assemble!(a::COOAssembler{T}, rowdofs::AbstractVector{Int}, coldofs::Ab nrows = length(rowdofs) ncols = length(coldofs) - @assert(size(Ke,1) == nrows) - @assert(size(Ke,2) == ncols) + @assert(size(Ke, 1) == nrows) + @assert(size(Ke, 2) == ncols) append!(a.V, Ke) @inbounds for i in 1:ncols @@ -115,6 +115,7 @@ function assemble!(a::COOAssembler{T}, rowdofs::AbstractVector{Int}, coldofs::Ab push!(a.J, coldofs[i]) end end + return end """ @@ -156,6 +157,7 @@ Assembles the element residual `ge` into the global residual vector `g`. @inbounds for (i, dof) in pairs(dofs) addindex!(g, ge[i], dof) end + return end """ @@ -170,7 +172,7 @@ matrix_handle, vector_handle """ Assembler for sparse matrix with CSC storage type. """ -struct CSCAssembler{Tv,Ti,MT<:AbstractSparseMatrixCSC{Tv,Ti}} <: AbstractCSCAssembler +struct CSCAssembler{Tv, Ti, MT <: AbstractSparseMatrixCSC{Tv, Ti}} <: AbstractCSCAssembler K::MT f::Vector{Tv} permutation::Vector{Int} @@ -180,7 +182,7 @@ end """ Assembler for symmetric sparse matrix with CSC storage type. """ -struct SymmetricCSCAssembler{Tv,Ti, MT <: Symmetric{Tv,<:AbstractSparseMatrixCSC{Tv,Ti}}} <: AbstractCSCAssembler +struct SymmetricCSCAssembler{Tv, Ti, MT <: Symmetric{Tv, <:AbstractSparseMatrixCSC{Tv, Ti}}} <: AbstractCSCAssembler K::MT f::Vector{Tv} permutation::Vector{Int} @@ -195,6 +197,7 @@ function Base.show(io::IO, ::MIME"text/plain", a::Union{CSCAssembler, SymmetricC print(io, "\n - ") summary(io, f) end + return end matrix_handle(a::AbstractCSCAssembler) = a.K @@ -219,15 +222,15 @@ necessary for efficient matrix assembly. To assemble the contribution from an el The keyword argument `fillzero` can be set to `false` if `K` and `f` should not be zeroed out, but instead keep their current values. """ -start_assemble(K::Union{AbstractSparseMatrixCSC, Symmetric{<:Any,<:AbstractSparseMatrixCSC}}, f::Vector; fillzero::Bool) +start_assemble(K::Union{AbstractSparseMatrixCSC, Symmetric{<:Any, <:AbstractSparseMatrixCSC}}, f::Vector; fillzero::Bool) -function start_assemble(K::AbstractSparseMatrixCSC{T}, f::Vector=T[]; fillzero::Bool=true, maxcelldofs_hint::Int=0) where {T} +function start_assemble(K::AbstractSparseMatrixCSC{T}, f::Vector = T[]; fillzero::Bool = true, maxcelldofs_hint::Int = 0) where {T} fillzero && (fillzero!(K); fillzero!(f)) - return CSCAssembler(K, f, zeros(Int,maxcelldofs_hint), zeros(Int,maxcelldofs_hint)) + return CSCAssembler(K, f, zeros(Int, maxcelldofs_hint), zeros(Int, maxcelldofs_hint)) end -function start_assemble(K::Symmetric{T,<:SparseMatrixCSC}, f::Vector=T[]; fillzero::Bool=true, maxcelldofs_hint::Int=0) where T +function start_assemble(K::Symmetric{T, <:SparseMatrixCSC}, f::Vector = T[]; fillzero::Bool = true, maxcelldofs_hint::Int = 0) where {T} fillzero && (fillzero!(K); fillzero!(f)) - return SymmetricCSCAssembler(K, f, zeros(Int,maxcelldofs_hint), zeros(Int,maxcelldofs_hint)) + return SymmetricCSCAssembler(K, f, zeros(Int, maxcelldofs_hint), zeros(Int, maxcelldofs_hint)) end function finish_assemble(a::Union{CSCAssembler, SymmetricCSCAssembler}) @@ -247,10 +250,10 @@ stiffness matrix and `f` the global force/residual vector, but more efficient. assemble!(::AbstractAssembler, ::AbstractVector{<:Integer}, ::AbstractMatrix, ::AbstractVector) @propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::Union{AbstractVector, Nothing} = nothing) - _assemble!(A, dofs, Ke, fe, false) + return _assemble!(A, dofs, Ke, fe, false) end @propagate_inbounds function assemble!(A::SymmetricCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::Union{AbstractVector, Nothing} = nothing) - _assemble!(A, dofs, Ke, fe, true) + return _assemble!(A, dofs, Ke, fe, true) end """ @@ -324,17 +327,17 @@ end end current_col += 1 end + return end function _missing_sparsity_pattern_error(Krow::Int, Kcol::Int) - throw(ErrorException( - "You are trying to assemble values in to K[$(Krow), $(Kcol)], but K[$(Krow), " * + msg = "You are trying to assemble values in to K[$(Krow), $(Kcol)], but K[$(Krow), " * "$(Kcol)] is missing in the sparsity pattern. Make sure you have called `K = " * "allocate_matrix(dh)` or `K = allocate_matrix(dh, ch)` if you " * "have affine constraints. This error might also happen if you are using " * "the assembler in a threaded assembly loop (you need to create one " * "`assembler` for each task)." - )) + throw(ErrorException(msg)) end ## assemble! with local condensation ## @@ -382,22 +385,22 @@ end Sort the input vector inplace and compute the corresponding permutation. """ function sortperm2!(B, ii) - @inbounds for i = 1:length(B) - ii[i] = i - end - quicksort!(B, ii) - return + @inbounds for i in 1:length(B) + ii[i] = i + end + quicksort!(B, ii) + return end -function quicksort!(A, order, i=1,j=length(A)) +function quicksort!(A, order, i = 1, j = length(A)) @inbounds if j > i - if j - i <= 12 - # Insertion sort for small groups is faster than Quicksort - InsertionSort!(A, order, i, j) - return A + if j - i <= 12 + # Insertion sort for small groups is faster than Quicksort + InsertionSort!(A, order, i, j) + return A end - pivot = A[div(i+j,2)] + pivot = A[div(i + j, 2)] left, right = i, j while left <= right while A[left] < pivot @@ -415,33 +418,33 @@ function quicksort!(A, order, i=1,j=length(A)) end end # left <= right - quicksort!(A,order, i, right) - quicksort!(A,order, left,j) + quicksort!(A, order, i, right) + quicksort!(A, order, left, j) end # j > i return A end -function InsertionSort!(A, order, ii=1, jj=length(A)) - @inbounds for i = ii+1 : jj +function InsertionSort!(A, order, ii = 1, jj = length(A)) + @inbounds for i in (ii + 1):jj j = i - 1 - temp = A[i] + temp = A[i] itemp = order[i] while true - if j == ii-1 + if j == ii - 1 break end if A[j] <= temp break end - A[j+1] = A[j] - order[j+1] = order[j] + A[j + 1] = A[j] + order[j + 1] = order[j] j -= 1 end - A[j+1] = temp - order[j+1] = itemp + A[j + 1] = temp + order[j + 1] = itemp end # i return end diff --git a/src/deprecations.jl b/src/deprecations.jl index 207fb938fc..06b931f804 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -3,16 +3,17 @@ struct DeprecationError <: Exception end function DeprecationError(msg::Pair) io = _iobuffer() - printstyled(io, "`$(msg.first)`", color=:red) + printstyled(io, "`$(msg.first)`", color = :red) print(io, " is deprecated, use ") - printstyled(io, "`$(msg.second)`", color=:green) + printstyled(io, "`$(msg.second)`", color = :green) print(io, " instead.") - DeprecationError(takestring(io)) + return DeprecationError(takestring(io)) end function Base.showerror(io::IO, err::DeprecationError) print(io, "DeprecationError: ") print(io, err.msg) + return end function _iobuffer() @@ -21,7 +22,7 @@ function _iobuffer() return ioc end function takestring(ioc) - String(take!(ioc.io)) + return String(take!(ioc.io)) end function Base.push!(::AbstractDofHandler, args...) @@ -83,25 +84,25 @@ struct Cell{refdim, nnodes, nfaces} else throw(DeprecationError("Cell{$refdim, $nnodes, $nfaces}(nodes)" => "$replacement(nodes)")) end + return end end export Cell -const Line2D = Cell{2,2,1} -const Line3D = Cell{3,2,0} -const Quadrilateral3D = Cell{3,4,1} +const Line2D = Cell{2, 2, 1} +const Line3D = Cell{3, 2, 0} +const Quadrilateral3D = Cell{3, 4, 1} export Line2D, Line3D, Quadrilateral3D using WriteVTK: vtk_grid export vtk_grid # To give better error -function WriteVTK.vtk_grid(::String, ::Union{AbstractGrid,AbstractDofHandler}; kwargs...) - throw(DeprecationError( - "The vtk interface has been updated in Ferrite v1.0. " * +function WriteVTK.vtk_grid(::String, ::Union{AbstractGrid, AbstractDofHandler}; kwargs...) + msg = "The vtk interface has been updated in Ferrite v1.0. " * "See https://github.com/Ferrite-FEM/Ferrite.jl/pull/692. " * "Use VTKGridFile to open a vtk file, and the functions " * "write_solution, write_cell_data, and write_projection to save data." - )) + throw(DeprecationError(msg)) end # Deprecation of auto-vectorized methods @@ -111,7 +112,7 @@ function add!(dh::DofHandler, name::Symbol, dim::Int) error("If you have more than one celltype in Grid, you must use add!(dh::DofHandler, fh::FieldHandler)") end io = _iobuffer() - printstyled(io, "`add!(dh::DofHandler, name::Symbol, dim::Int)`", color=:red) + printstyled(io, "`add!(dh::DofHandler, name::Symbol, dim::Int)`", color = :red) print(io, " is deprecated. Instead, pass the interpolation explicitly, and vectorize it to `dim` for vector-valued fields.") print(io, " See CHANGELOG for more details.") throw(DeprecationError(takestring(io))) @@ -119,7 +120,7 @@ end function add!(dh::DofHandler, name::Symbol, dim::Int, ip::ScalarInterpolation) io = _iobuffer() - printstyled(io, "`add!(dh::DofHandler, name::Symbol, dim::Int, ip::ScalarInterpolation)`", color=:red) + printstyled(io, "`add!(dh::DofHandler, name::Symbol, dim::Int, ip::ScalarInterpolation)`", color = :red) print(io, " is deprecated. Instead, vectorize the interpolation to the appropriate dimension and then `add!` it.") print(io, " See CHANGELOG for more details.") throw(DeprecationError(takestring(io))) @@ -159,35 +160,38 @@ end # Define dummy types so that loading old code doesn't directly error, and let # us print a descriptive error message in the constructor. for VT in ( - :CellScalarValues, :CellVectorValues, - :FaceScalarValues, :FaceVectorValues, - :PointScalarValues, :PointVectorValues, -) + :CellScalarValues, :CellVectorValues, + :FaceScalarValues, :FaceVectorValues, + :PointScalarValues, :PointVectorValues, + ) str = string(VT) str_scalar = replace(str, "Vector" => "Scalar") str_vector = replace(str, "Scalar" => "Vector") str_new = replace(str_scalar, "Scalar" => "") io = IOBuffer() - print(io, """ - The `$(str)` interface has been reworked for Ferrite.jl 0.4.0: - - - `$(str_scalar)` and `$(str_vector)` have been merged into a single type: `$(str_new)` - - "Vectorization" of (scalar) interpolations should now be done on the interpolation - instead of implicitly in the `CellValues` constructor. - - Upgrade as follows: - - Scalar fields: Replace usage of - $(str_scalar)(quad_rule, interpolation) - with - $(str_new)(quad_rule, interpolation) - - Vector fields: Replace usage of - $(str_vector)(quad_rule, interpolation) - with - $(str_new)(quad_rule, interpolation^dim) - where `dim` is the dimension to vectorize to. - - See CHANGELOG.md (https://github.com/Ferrite-FEM/Ferrite.jl/blob/master/CHANGELOG.md) for more details. - """) + print( + io, + """ + The `$(str)` interface has been reworked for Ferrite.jl 0.4.0: + + - `$(str_scalar)` and `$(str_vector)` have been merged into a single type: `$(str_new)` + - "Vectorization" of (scalar) interpolations should now be done on the interpolation + instead of implicitly in the `CellValues` constructor. + + Upgrade as follows: + - Scalar fields: Replace usage of + $(str_scalar)(quad_rule, interpolation) + with + $(str_new)(quad_rule, interpolation) + - Vector fields: Replace usage of + $(str_vector)(quad_rule, interpolation) + with + $(str_new)(quad_rule, interpolation^dim) + where `dim` is the dimension to vectorize to. + + See CHANGELOG.md (https://github.com/Ferrite-FEM/Ferrite.jl/blob/master/CHANGELOG.md) for more details. + """ + ) message = String(take!(io)) if occursin("Point", str) message = replace(message, "quad_rule, " => "") @@ -213,17 +217,17 @@ end # TODO: Are these needed to be deprecated - harder? with the new parameterization # (Cell|Face)Values with vector dofs -const _VectorValues = Union{CellValues{<:FV}, FacetValues{<:FV}} where {FV <: FunctionValues{<:Any,<:VectorInterpolation}} -function function_value(::_VectorValues, ::Int, ::AbstractVector{Vec{dim,T}}) where {dim,T} +const _VectorValues = Union{CellValues{<:FV}, FacetValues{<:FV}} where {FV <: FunctionValues{<:Any, <:VectorInterpolation}} +function function_value(::_VectorValues, ::Int, ::AbstractVector{Vec{dim, T}}) where {dim, T} throw(DeprecationError("function_value(fe_v::VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}})" => "function_value(fe_v, q_point, reinterpret(T, u))")) end -function function_gradient(::_VectorValues, ::Int, ::AbstractVector{Vec{dim,T}}) where {dim,T} +function function_gradient(::_VectorValues, ::Int, ::AbstractVector{Vec{dim, T}}) where {dim, T} throw(DeprecationError("function_gradient(fe_v::VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}})" => "function_gradient(fe_v, q_point, reinterpret(T, u))")) end -function function_divergence(::_VectorValues, ::Int, ::AbstractVector{Vec{dim,T}}) where {dim,T} +function function_divergence(::_VectorValues, ::Int, ::AbstractVector{Vec{dim, T}}) where {dim, T} throw(DeprecationError("function_divergence(fe_v::VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}})" => "function_divergence(fe_v, q_point, reinterpret(T, u))")) end -function function_curl(::_VectorValues, ::Int, ::AbstractVector{Vec{dim,T}}) where {dim,T} +function function_curl(::_VectorValues, ::Int, ::AbstractVector{Vec{dim, T}}) where {dim, T} throw(DeprecationError("function_curl(fe_v::VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}})" => "function_curl(fe_v, q_point, reinterpret(T, u))")) end @@ -239,20 +243,20 @@ export RefCube # QuadratureRule{3, RefCube}(...) -> QuadratureRule{RefHexahedron}(...) # QuadratureRule{1, RefCube}(...) -> FacetQuadratureRule{RefQuadrilateral}(...) # QuadratureRule{2, RefCube}(...) -> FacetQuadratureRule{RefHexahedron}(...) -function QuadratureRule{D, RefCube}(order::Int) where D +function QuadratureRule{D, RefCube}(order::Int) where {D} shapes = (RefLine, RefQuadrilateral, RefHexahedron) msg = "`QuadratureRule{$D, RefCube}(order::Int)` is deprecated, use `QuadratureRule{$(shapes[D])}(order)` instead" if D == 1 || D == 2 - msg *= " (or `FacetQuadratureRule{$(shapes[D+1])}(order)` if this is a face quadrature rule)" + msg *= " (or `FacetQuadratureRule{$(shapes[D + 1])}(order)` if this is a face quadrature rule)" end msg *= "." throw(DeprecationError(msg)) end -function QuadratureRule{D, RefCube}(quad_type::Symbol, order::Int) where D +function QuadratureRule{D, RefCube}(quad_type::Symbol, order::Int) where {D} shapes = (RefLine, RefQuadrilateral, RefHexahedron) msg = "`QuadratureRule{$D, RefCube}(quad_type::Symbol, order::Int)` is deprecated, use `QuadratureRule{$(shapes[D])}(quad_type, order)` instead" if D == 1 || D == 2 - msg *= " (or `FacetQuadratureRule{$(shapes[D+1])}(quad_type, order)` if this is a face quadrature rule)" + msg *= " (or `FacetQuadratureRule{$(shapes[D + 1])}(quad_type, order)` if this is a face quadrature rule)" end msg *= "." throw(DeprecationError(msg)) @@ -261,7 +265,7 @@ end # QuadratureRule{2, RefTetrahedron}(...) -> QuadratureRule{RefTriangle}(...) # QuadratureRule{3, RefTetrahedron}(...) -> QuadratureRule{RefTetrahedron}(...) # QuadratureRule{2, RefTetrahedron}(...) -> FacetQuadratureRule{RefTetrahedron}(...) -function QuadratureRule{D, RefTetrahedron}(order::Int) where D +function QuadratureRule{D, RefTetrahedron}(order::Int) where {D} shapes = (nothing, RefTriangle, RefTetrahedron) msg = "`QuadratureRule{$D, RefTetrahedron}(order::Int)` is deprecated, use `QuadratureRule{$(shapes[D])}(order)` instead" if D == 2 @@ -270,7 +274,7 @@ function QuadratureRule{D, RefTetrahedron}(order::Int) where D msg *= "." throw(DeprecationError(msg)) end -function QuadratureRule{D, RefTetrahedron}(quad_type::Symbol, order::Int) where D +function QuadratureRule{D, RefTetrahedron}(quad_type::Symbol, order::Int) where {D} shapes = (nothing, RefTriangle, RefTetrahedron) msg = "`QuadratureRule{$D, RefTetrahedron}(quad_type::Symbol, order::Int)` is deprecated, use `QuadratureRule{$(shapes[D])}(quad_type, order)` instead" if D == 2 @@ -302,34 +306,36 @@ end # Catch remaining cases in (Cell|Face)Value constructors function CellValues( - ::Type{T}, qr::QuadratureRule{2, RefTetrahedron, TQ}, ip::Interpolation{RefTriangle}, - gip::Interpolation{RefTriangle} = default_geometric_interpolation(ip), -) where {T, TQ} + ::Type{T}, qr::QuadratureRule{2, RefTetrahedron, TQ}, ip::Interpolation{RefTriangle}, + gip::Interpolation{RefTriangle} = default_geometric_interpolation(ip), + ) where {T, TQ} msg = "The input quadrature rule have the wrong reference shape, likely this comes from a constructor like `QuadratureRule{2, RefTetrahedron}(...)` which have been deprecated in favor of `QuadratureRule{RefTriangle}(...)`." throw(DeprecationError(msg)) end -function FacetValues(qr::QuadratureRule, ip::Interpolation, - gip::Interpolation = default_geometric_interpolation(ip)) +function FacetValues( + qr::QuadratureRule, ip::Interpolation, + gip::Interpolation = default_geometric_interpolation(ip) + ) return FacetValues(Float64, qr, ip, gip) end function FacetValues( - ::Type{T}, qr::QuadratureRule{RefLine, TQ}, ip::Interpolation{RefQuadrilateral}, - gip::Interpolation{RefQuadrilateral} = default_geometric_interpolation(ip), -) where {T, TQ} + ::Type{T}, qr::QuadratureRule{RefLine, TQ}, ip::Interpolation{RefQuadrilateral}, + gip::Interpolation{RefQuadrilateral} = default_geometric_interpolation(ip), + ) where {T, TQ} msg = "The input quadrature rule have the wrong reference shape, likely this comes from a constructor like `QuadratureRule{1, RefCube}(...)` which have been deprecated in favor of `FacetQuadratureRule{RefQuadrilateral}(...)`." throw(DeprecationError(msg)) end function FacetValues( - ::Type{T}, qr::QuadratureRule{RefQuadrilateral, TQ}, ip::Interpolation{RefHexahedron}, - gip::Interpolation{RefHexahedron} = default_geometric_interpolation(ip), -) where {T, TQ} + ::Type{T}, qr::QuadratureRule{RefQuadrilateral, TQ}, ip::Interpolation{RefHexahedron}, + gip::Interpolation{RefHexahedron} = default_geometric_interpolation(ip), + ) where {T, TQ} msg = "The input quadrature rule have the wrong reference shape, likely this comes from a constructor like `QuadratureRule{2, RefCube}(...)` which have been deprecated in favor of `FacetQuadratureRule{RefHexahedron}(...)`." throw(DeprecationError(msg)) end function FacetValues( - ::Type{T}, qr::QuadratureRule{RefTriangle, TQ}, ip::Interpolation{RefTetrahedron}, - gip::Interpolation{RefTetrahedron} = default_geometric_interpolation(ip), -) where {T, TQ} + ::Type{T}, qr::QuadratureRule{RefTriangle, TQ}, ip::Interpolation{RefTetrahedron}, + gip::Interpolation{RefTetrahedron} = default_geometric_interpolation(ip), + ) where {T, TQ} msg = "The input quadrature rule have the wrong reference shape, likely this comes from a constructor like `QuadratureRule{2, RefTetrahedron}(...)` which have been deprecated in favor of `FacetQuadratureRule{RefTetrahedron}(...)`." throw(DeprecationError(msg)) end @@ -346,10 +352,12 @@ end export MixedDofHandler function MixedDofHandler(::AbstractGrid) - throw(DeprecationError( - "MixedDofHandler is the standard DofHandler in Ferrite now and has been renamed " * - "to DofHandler. Use DofHandler even for mixed grids and fields on subdomains.", - )) + throw( + DeprecationError( + "MixedDofHandler is the standard DofHandler in Ferrite now and has been renamed " * + "to DofHandler. Use DofHandler even for mixed grids and fields on subdomains.", + ) + ) end export end_assemble diff --git a/src/exports.jl b/src/exports.jl index 54e6ab04c8..4380dc499f 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -1,5 +1,5 @@ export -# Interpolations + # Interpolations Interpolation, VectorInterpolation, ScalarInterpolation, @@ -20,12 +20,12 @@ export getnbasefunctions, getrefshape, -# Quadrature + # Quadrature QuadratureRule, FacetQuadratureRule, getnquadpoints, -# FEValues + # FEValues AbstractCellValues, AbstractFacetValues, CellValues, @@ -55,7 +55,7 @@ export function_gradient_average, function_gradient_jump, -# Grid + # Grid Grid, Node, Line, @@ -106,11 +106,11 @@ export generate_grid, GPUGrid, -# Grid coloring + # Grid coloring create_coloring, ColoringAlgorithm, -# Dofs + # Dofs DofHandler, SubDofHandler, close!, @@ -125,7 +125,7 @@ export apply_analytical!, get_grid, -# Sparsity pattern + # Sparsity pattern # AbstractSparsityPattern, SparsityPattern, BlockSparsityPattern, @@ -136,7 +136,7 @@ export add_constraint_entries!, allocate_matrix, -# Constraints + # Constraints ConstraintHandler, Dirichlet, PeriodicDirichlet, @@ -154,7 +154,7 @@ export add!, free_dofs, -# iterators + # iterators CellCache, CellIterator, FacetCache, @@ -165,23 +165,23 @@ export cellid, interfacedofs, -# assembly + # assembly start_assemble, assemble!, finish_assemble, -# exporting data + # exporting data VTKGridFile, write_solution, write_cell_data, write_projection, write_node_data, -# L2 Projection + # L2 Projection project, L2Projector, -# Point Evaluation + # Point Evaluation PointEvalHandler, evaluate_at_points, PointIterator, diff --git a/src/interpolations.jl b/src/interpolations.jl index c78621c90e..afc2119340 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -48,11 +48,11 @@ abstract type Interpolation{shape #=<: AbstractRefShape=#, order} end const InterpolationByDim{dim} = Interpolation{<:AbstractRefShape{dim}} -abstract type ScalarInterpolation{ refshape, order} <: Interpolation{refshape, order} end +abstract type ScalarInterpolation{refshape, order} <: Interpolation{refshape, order} end abstract type VectorInterpolation{vdim, refshape, order} <: Interpolation{refshape, order} end # Number of components for the interpolation. -n_components(::ScalarInterpolation) = 1 +n_components(::ScalarInterpolation) = 1 n_components(::VectorInterpolation{vdim}) where {vdim} = vdim # Number of components that are allowed to prescribe in e.g. Dirichlet BC n_dbc_components(ip::Interpolation) = n_components(ip) @@ -86,23 +86,24 @@ struct InterpolationInfo n_copies::Int is_discontinuous::Bool end -function InterpolationInfo(interpolation::Interpolation{shape}, n_copies) where {rdim, shape<:AbstractRefShape{rdim}} - InterpolationInfo( - [length(i) for i ∈ vertexdof_indices(interpolation)], - [length(i) for i ∈ edgedof_interior_indices(interpolation)], - [length(i) for i ∈ facedof_interior_indices(interpolation)], +function InterpolationInfo(interpolation::Interpolation{shape}, n_copies) where {rdim, shape <: AbstractRefShape{rdim}} + info = InterpolationInfo( + [length(i) for i in vertexdof_indices(interpolation)], + [length(i) for i in edgedof_interior_indices(interpolation)], + [length(i) for i in facedof_interior_indices(interpolation)], length(volumedof_interior_indices(interpolation)), rdim, adjust_dofs_during_distribution(interpolation), n_copies, is_discontinuous(interpolation) ) + return info end InterpolationInfo(interpolation::Interpolation) = InterpolationInfo(interpolation, 1) -nvertices(::Interpolation{RefShape}) where RefShape = nvertices(RefShape) -nedges(::Interpolation{RefShape}) where RefShape = nedges(RefShape) -nfaces(::Interpolation{RefShape}) where RefShape = nfaces(RefShape) +nvertices(::Interpolation{RefShape}) where {RefShape} = nvertices(RefShape) +nedges(::Interpolation{RefShape}) where {RefShape} = nedges(RefShape) +nfaces(::Interpolation{RefShape}) where {RefShape} = nfaces(RefShape) Base.copy(ip::Interpolation) = ip @@ -112,7 +113,7 @@ Base.copy(ip::Interpolation) = ip Return the dimension of the reference element for a given interpolation. """ getrefdim(::Interpolation) # To make doc-filtering work -@inline getrefdim(::Interpolation{RefShape}) where RefShape = getrefdim(RefShape) +@inline getrefdim(::Interpolation{RefShape}) where {RefShape} = getrefdim(RefShape) """ Ferrite.getrefshape(::Interpolation)::AbstractRefShape @@ -126,7 +127,7 @@ Return the reference element shape of the interpolation. Return order of the interpolation. """ -@inline getorder(::Interpolation{shape,order}) where {shape,order} = order +@inline getorder(::Interpolation{shape, order}) where {shape, order} = order ##################### @@ -157,6 +158,7 @@ Evaluate all shape functions of `ip` at once at the reference point `ξ` and sto @inbounds for i in 1:getnbasefunctions(ip) values[i] = reference_shape_value(ip, ξ, i) end + return end """ @@ -170,6 +172,7 @@ function reference_shape_gradients!(gradients::AT, ip::IP, ξ::Vec) where {IP <: @inbounds for i in 1:getnbasefunctions(ip) gradients[i] = reference_shape_gradient(ip, ξ, i) end + return end """ @@ -184,6 +187,7 @@ function reference_shape_gradients_and_values!(gradients::GAT, values::SAT, ip:: @inbounds for i in 1:getnbasefunctions(ip) gradients[i], values[i] = reference_shape_gradient_and_value(ip, ξ, i) end + return end """ @@ -199,6 +203,7 @@ and store them in `hessians`, `gradients`, and `values`. @inbounds for i in 1:getnbasefunctions(ip) hessians[i], gradients[i], values[i] = reference_shape_hessian_gradient_and_value(ip, ξ, i) end + return end @@ -372,8 +377,8 @@ volumedof_interior_indices(::Interpolation) = () # Some helpers to skip boilerplate edgedof_indices(ip::Interpolation) = ntuple(_ -> (), nedges(ip)) edgedof_interior_indices(ip::Interpolation) = ntuple(_ -> (), nedges(ip)) -facedof_indices(ip::Interpolation) = ntuple(_ -> (), nfaces(ip)) -facedof_interior_indices(ip::Interpolation) = ntuple(_ -> (), nfaces(ip)) +facedof_indices(ip::Interpolation) = ntuple(_ -> (), nfaces(ip)) +facedof_interior_indices(ip::Interpolation) = ntuple(_ -> (), nfaces(ip)) """ boundarydof_indices(::Type{<:BoundaryIndex}) @@ -431,19 +436,19 @@ Piecewise discontinuous Lagrange basis via Gauss-Lobatto points. """ struct DiscontinuousLagrange{shape, order} <: ScalarInterpolation{shape, order} function DiscontinuousLagrange{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order}() + return new{shape, order}() end end adjust_dofs_during_distribution(::DiscontinuousLagrange) = false -getlowerorder(::DiscontinuousLagrange{shape,order}) where {shape,order} = DiscontinuousLagrange{shape,order-1}() +getlowerorder(::DiscontinuousLagrange{shape, order}) where {shape, order} = DiscontinuousLagrange{shape, order - 1}() -getnbasefunctions(::DiscontinuousLagrange{shape,order}) where {shape,order} = getnbasefunctions(Lagrange{shape,order}()) -getnbasefunctions(::DiscontinuousLagrange{shape,0}) where {shape} = 1 +getnbasefunctions(::DiscontinuousLagrange{shape, order}) where {shape, order} = getnbasefunctions(Lagrange{shape, order}()) +getnbasefunctions(::DiscontinuousLagrange{shape, 0}) where {shape} = 1 # This just moves all dofs into the interior of the element. -volumedof_interior_indices(ip::DiscontinuousLagrange) = ntuple(i->i, getnbasefunctions(ip)) +volumedof_interior_indices(ip::DiscontinuousLagrange) = ntuple(i -> i, getnbasefunctions(ip)) # Mirror the Lagrange element for now to avoid repeating. dirichlet_facedof_indices(ip::DiscontinuousLagrange{shape, order}) where {shape, order} = dirichlet_facedof_indices(Lagrange{shape, order}()) @@ -452,23 +457,23 @@ dirichlet_vertexdof_indices(ip::DiscontinuousLagrange{shape, order}) where {shap # Mirror the Lagrange element for now. function reference_coordinates(ip::DiscontinuousLagrange{shape, order}) where {shape, order} - return reference_coordinates(Lagrange{shape,order}()) + return reference_coordinates(Lagrange{shape, order}()) end function reference_shape_value(::DiscontinuousLagrange{shape, order}, ξ::Vec{dim}, i::Int) where {dim, shape <: AbstractRefShape{dim}, order} return reference_shape_value(Lagrange{shape, order}(), ξ, i) end # Excepting the L0 element. -function reference_coordinates(ip::DiscontinuousLagrange{RefHypercube{dim},0}) where dim - return [Vec{dim, Float64}(ntuple(x->0.0, dim))] +function reference_coordinates(ip::DiscontinuousLagrange{RefHypercube{dim}, 0}) where {dim} + return [Vec{dim, Float64}(ntuple(x -> 0.0, dim))] end -function reference_coordinates(ip::DiscontinuousLagrange{RefTriangle,0}) - return [Vec{2,Float64}((1/3,1/3))] +function reference_coordinates(ip::DiscontinuousLagrange{RefTriangle, 0}) + return [Vec{2, Float64}((1 / 3, 1 / 3))] end -function reference_coordinates(ip::DiscontinuousLagrange{RefTetrahedron,0}) - return [Vec{3,Float64}((1/4,1/4,1/4))] +function reference_coordinates(ip::DiscontinuousLagrange{RefTetrahedron, 0}) + return [Vec{3, Float64}((1 / 4, 1 / 4, 1 / 4))] end function reference_shape_value(ip::DiscontinuousLagrange{shape, 0}, ::Vec{dim, T}, i::Int) where {dim, shape <: AbstractRefShape{dim}, T} @@ -488,7 +493,7 @@ Standard continuous Lagrange polynomials with equidistant node placement. """ struct Lagrange{shape, order} <: ScalarInterpolation{shape, order} function Lagrange{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order}() + return new{shape, order}() end end @@ -497,27 +502,29 @@ adjust_dofs_during_distribution(::Lagrange{<:Any, 2}) = false adjust_dofs_during_distribution(::Lagrange{<:Any, 1}) = false # Vertices for all Lagrange interpolations are the same -vertexdof_indices(::Lagrange{RefLine}) = ((1,),(2,)) -vertexdof_indices(::Lagrange{RefQuadrilateral}) = ((1,),(2,),(3,),(4,)) -vertexdof_indices(::Lagrange{RefHexahedron}) = ((1,),(2,),(3,),(4,),(5,),(6,),(7,),(8,)) -vertexdof_indices(::Lagrange{RefTriangle}) = ((1,),(2,),(3,)) -vertexdof_indices(::Lagrange{RefTetrahedron}) = ((1,),(2,),(3,),(4,)) +vertexdof_indices(::Lagrange{RefLine}) = ((1,), (2,)) +vertexdof_indices(::Lagrange{RefQuadrilateral}) = ((1,), (2,), (3,), (4,)) +vertexdof_indices(::Lagrange{RefHexahedron}) = ((1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,)) +vertexdof_indices(::Lagrange{RefTriangle}) = ((1,), (2,), (3,)) +vertexdof_indices(::Lagrange{RefTetrahedron}) = ((1,), (2,), (3,), (4,)) vertexdof_indices(::Lagrange{RefPrism}) = ((1,), (2,), (3,), (4,), (5,), (6,)) -vertexdof_indices(::Lagrange{RefPyramid}) = ((1,), (2,), (3,), (4,), (5,),) +vertexdof_indices(::Lagrange{RefPyramid}) = ((1,), (2,), (3,), (4,), (5,)) -getlowerorder(::Lagrange{shape,order}) where {shape,order} = Lagrange{shape,order-1}() -getlowerorder(::Lagrange{shape,1}) where {shape} = DiscontinuousLagrange{shape,0}() +getlowerorder(::Lagrange{shape, order}) where {shape, order} = Lagrange{shape, order - 1}() +getlowerorder(::Lagrange{shape, 1}) where {shape} = DiscontinuousLagrange{shape, 0}() ############################ # Lagrange RefLine order 1 # ############################ -getnbasefunctions(::Lagrange{RefLine,1}) = 2 +getnbasefunctions(::Lagrange{RefLine, 1}) = 2 -edgedof_indices(::Lagrange{RefLine,1}) = ((1,2),) +edgedof_indices(::Lagrange{RefLine, 1}) = ((1, 2),) -function reference_coordinates(::Lagrange{RefLine,1}) - return [Vec{1, Float64}((-1.0,)), - Vec{1, Float64}(( 1.0,))] +function reference_coordinates(::Lagrange{RefLine, 1}) + return [ + Vec{1, Float64}((-1.0,)), + Vec{1, Float64}((1.0,)), + ] end function reference_shape_value(ip::Lagrange{RefLine, 1}, ξ::Vec{1}, i::Int) @@ -530,15 +537,17 @@ end ############################ # Lagrange RefLine order 2 # ############################ -getnbasefunctions(::Lagrange{RefLine,2}) = 3 +getnbasefunctions(::Lagrange{RefLine, 2}) = 3 -edgedof_indices(::Lagrange{RefLine,2}) = ((1,2,3),) -edgedof_interior_indices(::Lagrange{RefLine,2}) = (3,) +edgedof_indices(::Lagrange{RefLine, 2}) = ((1, 2, 3),) +edgedof_interior_indices(::Lagrange{RefLine, 2}) = (3,) -function reference_coordinates(::Lagrange{RefLine,2}) - return [Vec{1, Float64}((-1.0,)), - Vec{1, Float64}(( 1.0,)), - Vec{1, Float64}(( 0.0,))] +function reference_coordinates(::Lagrange{RefLine, 2}) + return [ + Vec{1, Float64}((-1.0,)), + Vec{1, Float64}((1.0,)), + Vec{1, Float64}((0.0,)), + ] end function reference_shape_value(ip::Lagrange{RefLine, 2}, ξ::Vec{1}, i::Int) @@ -552,16 +561,18 @@ end ##################################### # Lagrange RefQuadrilateral order 1 # ##################################### -getnbasefunctions(::Lagrange{RefQuadrilateral,1}) = 4 +getnbasefunctions(::Lagrange{RefQuadrilateral, 1}) = 4 -edgedof_indices(::Lagrange{RefQuadrilateral,1}) = ((1,2), (2,3), (3,4), (4,1)) -facedof_indices(ip::Lagrange{RefQuadrilateral,1}) = (ntuple(i->i, getnbasefunctions(ip)),) +edgedof_indices(::Lagrange{RefQuadrilateral, 1}) = ((1, 2), (2, 3), (3, 4), (4, 1)) +facedof_indices(ip::Lagrange{RefQuadrilateral, 1}) = (ntuple(i -> i, getnbasefunctions(ip)),) -function reference_coordinates(::Lagrange{RefQuadrilateral,1}) - return [Vec{2, Float64}((-1.0, -1.0)), - Vec{2, Float64}(( 1.0, -1.0)), - Vec{2, Float64}(( 1.0, 1.0,)), - Vec{2, Float64}((-1.0, 1.0,))] +function reference_coordinates(::Lagrange{RefQuadrilateral, 1}) + return [ + Vec{2, Float64}((-1.0, -1.0)), + Vec{2, Float64}((1.0, -1.0)), + Vec{2, Float64}((1.0, 1.0)), + Vec{2, Float64}((-1.0, 1.0)), + ] end function reference_shape_value(ip::Lagrange{RefQuadrilateral, 1}, ξ::Vec{2}, i::Int) @@ -577,23 +588,25 @@ end ##################################### # Lagrange RefQuadrilateral order 2 # ##################################### -getnbasefunctions(::Lagrange{RefQuadrilateral,2}) = 9 +getnbasefunctions(::Lagrange{RefQuadrilateral, 2}) = 9 -edgedof_indices(::Lagrange{RefQuadrilateral,2}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8)) -edgedof_interior_indices(::Lagrange{RefQuadrilateral,2}) = ((5,), (6,), (7,), (8,)) -facedof_indices(ip::Lagrange{RefQuadrilateral,2}) = (ntuple(i->i, getnbasefunctions(ip)),) -facedof_interior_indices(::Lagrange{RefQuadrilateral,2}) = ((9,)) +edgedof_indices(::Lagrange{RefQuadrilateral, 2}) = ((1, 2, 5), (2, 3, 6), (3, 4, 7), (4, 1, 8)) +edgedof_interior_indices(::Lagrange{RefQuadrilateral, 2}) = ((5,), (6,), (7,), (8,)) +facedof_indices(ip::Lagrange{RefQuadrilateral, 2}) = (ntuple(i -> i, getnbasefunctions(ip)),) +facedof_interior_indices(::Lagrange{RefQuadrilateral, 2}) = ((9,)) -function reference_coordinates(::Lagrange{RefQuadrilateral,2}) - return [Vec{2, Float64}((-1.0, -1.0)), - Vec{2, Float64}(( 1.0, -1.0)), - Vec{2, Float64}(( 1.0, 1.0)), - Vec{2, Float64}((-1.0, 1.0)), - Vec{2, Float64}(( 0.0, -1.0)), - Vec{2, Float64}(( 1.0, 0.0)), - Vec{2, Float64}(( 0.0, 1.0)), - Vec{2, Float64}((-1.0, 0.0)), - Vec{2, Float64}(( 0.0, 0.0))] +function reference_coordinates(::Lagrange{RefQuadrilateral, 2}) + return [ + Vec{2, Float64}((-1.0, -1.0)), + Vec{2, Float64}((1.0, -1.0)), + Vec{2, Float64}((1.0, 1.0)), + Vec{2, Float64}((-1.0, 1.0)), + Vec{2, Float64}((0.0, -1.0)), + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((-1.0, 0.0)), + Vec{2, Float64}((0.0, 0.0)), + ] end function reference_shape_value(ip::Lagrange{RefQuadrilateral, 2}, ξ::Vec{2}, i::Int) @@ -616,66 +629,70 @@ end ##################################### getnbasefunctions(::Lagrange{RefQuadrilateral, 3}) = 16 -edgedof_indices(::Lagrange{RefQuadrilateral, 3}) = ((1,2, 5,6), (2,3, 7,8), (3,4, 9,10), (4,1, 11,12)) -edgedof_interior_indices(::Lagrange{RefQuadrilateral, 3}) = ((5,6), (7,8), (9,10), (11,12)) -facedof_indices(ip::Lagrange{RefQuadrilateral,3}) = (ntuple(i->i, getnbasefunctions(ip)),) -facedof_interior_indices(::Lagrange{RefQuadrilateral, 3}) = ((13,14,15,16,),) +edgedof_indices(::Lagrange{RefQuadrilateral, 3}) = ((1, 2, 5, 6), (2, 3, 7, 8), (3, 4, 9, 10), (4, 1, 11, 12)) +edgedof_interior_indices(::Lagrange{RefQuadrilateral, 3}) = ((5, 6), (7, 8), (9, 10), (11, 12)) +facedof_indices(ip::Lagrange{RefQuadrilateral, 3}) = (ntuple(i -> i, getnbasefunctions(ip)),) +facedof_interior_indices(::Lagrange{RefQuadrilateral, 3}) = ((13, 14, 15, 16),) function reference_coordinates(::Lagrange{RefQuadrilateral, 3}) - return [Vec{2, Float64}((-1.0, -1.0)), - Vec{2, Float64}(( 1.0, -1.0)), - Vec{2, Float64}(( 1.0, 1.0)), - Vec{2, Float64}((-1.0, 1.0)), - Vec{2, Float64}((-1/3, -1.0)), - Vec{2, Float64}(( 1/3, -1.0)), - Vec{2, Float64}(( 1.0, -1/3)), - Vec{2, Float64}(( 1.0, 1/3)), - Vec{2, Float64}(( 1/3, 1.0)), - Vec{2, Float64}((-1/3, 1.0)), - Vec{2, Float64}((-1.0, 1/3)), - Vec{2, Float64}((-1.0, -1/3)), - Vec{2, Float64}((-1/3, -1/3)), - Vec{2, Float64}(( 1/3, -1/3)), - Vec{2, Float64}((-1/3, 1/3)), - Vec{2, Float64}(( 1/3, 1/3))] + return [ + Vec{2, Float64}((-1.0, -1.0)), + Vec{2, Float64}((1.0, -1.0)), + Vec{2, Float64}((1.0, 1.0)), + Vec{2, Float64}((-1.0, 1.0)), + Vec{2, Float64}((-1 / 3, -1.0)), + Vec{2, Float64}((1 / 3, -1.0)), + Vec{2, Float64}((1.0, -1 / 3)), + Vec{2, Float64}((1.0, 1 / 3)), + Vec{2, Float64}((1 / 3, 1.0)), + Vec{2, Float64}((-1 / 3, 1.0)), + Vec{2, Float64}((-1.0, 1 / 3)), + Vec{2, Float64}((-1.0, -1 / 3)), + Vec{2, Float64}((-1 / 3, -1 / 3)), + Vec{2, Float64}((1 / 3, -1 / 3)), + Vec{2, Float64}((-1 / 3, 1 / 3)), + Vec{2, Float64}((1 / 3, 1 / 3)), + ] end function reference_shape_value(ip::Lagrange{RefQuadrilateral, 3}, ξ::Vec{2}, i::Int) # See https://defelement.com/elements/examples/quadrilateral-Q-3.html # Transform domain from [-1, 1] × [-1, 1] to [0, 1] × [0, 1] - ξ_x = (ξ[1]+1)/2 - ξ_y = (ξ[2]+1)/2 - i == 1 && return (81*ξ_x^3*ξ_y^3)/4 - (81*ξ_x^3*ξ_y^2)/2 + (99*ξ_x^3*ξ_y)/4 - (9*ξ_x^3)/2 - (81*ξ_x^2*ξ_y^3)/2 + (81*ξ_x^2*ξ_y^2) - (99*ξ_x^2*ξ_y)/2 + (9*ξ_x^2) + (99*ξ_x*ξ_y^3)/4 - (99*ξ_x*ξ_y^2)/2 + (121*ξ_x*ξ_y)/4 - (11*ξ_x)/2 - (9*ξ_y^3)/2 + 9*ξ_y^2 - (11*ξ_y)/2 + 1 - i == 2 && return (ξ_x*( - 81*ξ_x^2*ξ_y^3 + 162*ξ_x^2*ξ_y^2 - 99*ξ_x^2*ξ_y + 18*ξ_x^2 + 81*ξ_x*ξ_y^3 - 162*ξ_x*ξ_y^2 + 99*ξ_x*ξ_y - 18*ξ_x - 18*ξ_y^3 + 36*ξ_y^2 - 22*ξ_y + 4))/4 - i == 4 && return (ξ_y*( - 81*ξ_x^3*ξ_y^2 + 81*ξ_x^3*ξ_y - 18*ξ_x^3 + 162*ξ_x^2*ξ_y^2 - 162*ξ_x^2*ξ_y + 36*ξ_x^2 - 99*ξ_x*ξ_y^2 + 99*ξ_x*ξ_y - 22*ξ_x + 18*ξ_y^2 - 18*ξ_y + 4))/4 - i == 3 && return (ξ_x*ξ_y*(81*ξ_x^2*ξ_y^2 - 81*ξ_x^2*ξ_y + 18*ξ_x^2 - 81*ξ_x*ξ_y^2 + 81*ξ_x*ξ_y - 18*ξ_x + 18*ξ_y^2 - 18*ξ_y + 4))/4 - i == 5 && return (9*ξ_x*( - 27*ξ_x^2*ξ_y^3 + 54*ξ_x^2*ξ_y^2 - 33*ξ_x^2*ξ_y + 6*ξ_x^2 + 45*ξ_x*ξ_y^3 - 90*ξ_x*ξ_y^2 + 55*ξ_x*ξ_y - 10*ξ_x - 18*ξ_y^3 + 36*ξ_y^2 - 22*ξ_y + 4))/4 - i == 6 && return (9*ξ_x*(27*ξ_x^2*ξ_y^3 - 54*ξ_x^2*ξ_y^2 + 33*ξ_x^2*ξ_y - 6*ξ_x^2 - 36*ξ_x*ξ_y^3 + 72*ξ_x*ξ_y^2 - 44*ξ_x*ξ_y + 8*ξ_x + 9*ξ_y^3 - 18*ξ_y^2 + 11*ξ_y - 2))/4 - i == 12 && return (9*ξ_y*( - 27*ξ_x^3*ξ_y^2 + 45*ξ_x^3*ξ_y - 18*ξ_x^3 + 54*ξ_x^2*ξ_y^2 - 90*ξ_x^2*ξ_y + 36*ξ_x^2 - 33*ξ_x*ξ_y^2 + 55*ξ_x*ξ_y - 22*ξ_x + 6*ξ_y^2 - 10*ξ_y + 4))/4 - i == 11 && return (9*ξ_y*(27*ξ_x^3*ξ_y^2 - 36*ξ_x^3*ξ_y + 9*ξ_x^3 - 54*ξ_x^2*ξ_y^2 + 72*ξ_x^2*ξ_y - 18*ξ_x^2 + 33*ξ_x*ξ_y^2 - 44*ξ_x*ξ_y + 11*ξ_x - 6*ξ_y^2 + 8*ξ_y - 2))/4 - i == 7 && return (9*ξ_x*ξ_y*(27*ξ_x^2*ξ_y^2 - 45*ξ_x^2*ξ_y + 18*ξ_x^2 - 27*ξ_x*ξ_y^2 + 45*ξ_x*ξ_y - 18*ξ_x + 6*ξ_y^2 - 10*ξ_y + 4))/4 - i == 8 && return (9*ξ_x*ξ_y*( - 27*ξ_x^2*ξ_y^2 + 36*ξ_x^2*ξ_y - 9*ξ_x^2 + 27*ξ_x*ξ_y^2 - 36*ξ_x*ξ_y + 9*ξ_x - 6*ξ_y^2 + 8*ξ_y - 2))/4 - i == 10 && return (9*ξ_x*ξ_y*(27*ξ_x^2*ξ_y^2 - 27*ξ_x^2*ξ_y + 6*ξ_x^2 - 45*ξ_x*ξ_y^2 + 45*ξ_x*ξ_y - 10*ξ_x + 18*ξ_y^2 - 18*ξ_y + 4))/4 - i == 9 && return (9*ξ_x*ξ_y*( - 27*ξ_x^2*ξ_y^2 + 27*ξ_x^2*ξ_y - 6*ξ_x^2 + 36*ξ_x*ξ_y^2 - 36*ξ_x*ξ_y + 8*ξ_x - 9*ξ_y^2 + 9*ξ_y - 2))/4 - i == 13 && return (81*ξ_x*ξ_y*(9*ξ_x^2*ξ_y^2 - 15*ξ_x^2*ξ_y + 6*ξ_x^2 - 15*ξ_x*ξ_y^2 + 25*ξ_x*ξ_y - 10*ξ_x + 6*ξ_y^2 - 10*ξ_y + 4))/4 - i == 14 && return (81*ξ_x*ξ_y*( - 9*ξ_x^2*ξ_y^2 + 15*ξ_x^2*ξ_y - 6*ξ_x^2 + 12*ξ_x*ξ_y^2 - 20*ξ_x*ξ_y + 8*ξ_x - 3*ξ_y^2 + 5*ξ_y - 2))/4 - i == 15 && return (81*ξ_x*ξ_y*( - 9*ξ_x^2*ξ_y^2 + 12*ξ_x^2*ξ_y - 3*ξ_x^2 + 15*ξ_x*ξ_y^2 - 20*ξ_x*ξ_y + 5*ξ_x - 6*ξ_y^2 + 8*ξ_y - 2))/4 - i == 16 && return (81*ξ_x*ξ_y*(9*ξ_x^2*ξ_y^2 - 12*ξ_x^2*ξ_y + 3*ξ_x^2 - 12*ξ_x*ξ_y^2 + 16*ξ_x*ξ_y - 4*ξ_x + 3*ξ_y^2 - 4*ξ_y + 1))/4 + ξ_x = (ξ[1] + 1) / 2 + ξ_y = (ξ[2] + 1) / 2 + i == 1 && return (81 * ξ_x^3 * ξ_y^3) / 4 - (81 * ξ_x^3 * ξ_y^2) / 2 + (99 * ξ_x^3 * ξ_y) / 4 - (9 * ξ_x^3) / 2 - (81 * ξ_x^2 * ξ_y^3) / 2 + (81 * ξ_x^2 * ξ_y^2) - (99 * ξ_x^2 * ξ_y) / 2 + (9 * ξ_x^2) + (99 * ξ_x * ξ_y^3) / 4 - (99 * ξ_x * ξ_y^2) / 2 + (121 * ξ_x * ξ_y) / 4 - (11 * ξ_x) / 2 - (9 * ξ_y^3) / 2 + 9 * ξ_y^2 - (11 * ξ_y) / 2 + 1 + i == 2 && return (ξ_x * (- 81 * ξ_x^2 * ξ_y^3 + 162 * ξ_x^2 * ξ_y^2 - 99 * ξ_x^2 * ξ_y + 18 * ξ_x^2 + 81 * ξ_x * ξ_y^3 - 162 * ξ_x * ξ_y^2 + 99 * ξ_x * ξ_y - 18 * ξ_x - 18 * ξ_y^3 + 36 * ξ_y^2 - 22 * ξ_y + 4)) / 4 + i == 4 && return (ξ_y * (- 81 * ξ_x^3 * ξ_y^2 + 81 * ξ_x^3 * ξ_y - 18 * ξ_x^3 + 162 * ξ_x^2 * ξ_y^2 - 162 * ξ_x^2 * ξ_y + 36 * ξ_x^2 - 99 * ξ_x * ξ_y^2 + 99 * ξ_x * ξ_y - 22 * ξ_x + 18 * ξ_y^2 - 18 * ξ_y + 4)) / 4 + i == 3 && return (ξ_x * ξ_y * (81 * ξ_x^2 * ξ_y^2 - 81 * ξ_x^2 * ξ_y + 18 * ξ_x^2 - 81 * ξ_x * ξ_y^2 + 81 * ξ_x * ξ_y - 18 * ξ_x + 18 * ξ_y^2 - 18 * ξ_y + 4)) / 4 + i == 5 && return (9 * ξ_x * (- 27 * ξ_x^2 * ξ_y^3 + 54 * ξ_x^2 * ξ_y^2 - 33 * ξ_x^2 * ξ_y + 6 * ξ_x^2 + 45 * ξ_x * ξ_y^3 - 90 * ξ_x * ξ_y^2 + 55 * ξ_x * ξ_y - 10 * ξ_x - 18 * ξ_y^3 + 36 * ξ_y^2 - 22 * ξ_y + 4)) / 4 + i == 6 && return (9 * ξ_x * (27 * ξ_x^2 * ξ_y^3 - 54 * ξ_x^2 * ξ_y^2 + 33 * ξ_x^2 * ξ_y - 6 * ξ_x^2 - 36 * ξ_x * ξ_y^3 + 72 * ξ_x * ξ_y^2 - 44 * ξ_x * ξ_y + 8 * ξ_x + 9 * ξ_y^3 - 18 * ξ_y^2 + 11 * ξ_y - 2)) / 4 + i == 12 && return (9 * ξ_y * (- 27 * ξ_x^3 * ξ_y^2 + 45 * ξ_x^3 * ξ_y - 18 * ξ_x^3 + 54 * ξ_x^2 * ξ_y^2 - 90 * ξ_x^2 * ξ_y + 36 * ξ_x^2 - 33 * ξ_x * ξ_y^2 + 55 * ξ_x * ξ_y - 22 * ξ_x + 6 * ξ_y^2 - 10 * ξ_y + 4)) / 4 + i == 11 && return (9 * ξ_y * (27 * ξ_x^3 * ξ_y^2 - 36 * ξ_x^3 * ξ_y + 9 * ξ_x^3 - 54 * ξ_x^2 * ξ_y^2 + 72 * ξ_x^2 * ξ_y - 18 * ξ_x^2 + 33 * ξ_x * ξ_y^2 - 44 * ξ_x * ξ_y + 11 * ξ_x - 6 * ξ_y^2 + 8 * ξ_y - 2)) / 4 + i == 7 && return (9 * ξ_x * ξ_y * (27 * ξ_x^2 * ξ_y^2 - 45 * ξ_x^2 * ξ_y + 18 * ξ_x^2 - 27 * ξ_x * ξ_y^2 + 45 * ξ_x * ξ_y - 18 * ξ_x + 6 * ξ_y^2 - 10 * ξ_y + 4)) / 4 + i == 8 && return (9 * ξ_x * ξ_y * (- 27 * ξ_x^2 * ξ_y^2 + 36 * ξ_x^2 * ξ_y - 9 * ξ_x^2 + 27 * ξ_x * ξ_y^2 - 36 * ξ_x * ξ_y + 9 * ξ_x - 6 * ξ_y^2 + 8 * ξ_y - 2)) / 4 + i == 10 && return (9 * ξ_x * ξ_y * (27 * ξ_x^2 * ξ_y^2 - 27 * ξ_x^2 * ξ_y + 6 * ξ_x^2 - 45 * ξ_x * ξ_y^2 + 45 * ξ_x * ξ_y - 10 * ξ_x + 18 * ξ_y^2 - 18 * ξ_y + 4)) / 4 + i == 9 && return (9 * ξ_x * ξ_y * (- 27 * ξ_x^2 * ξ_y^2 + 27 * ξ_x^2 * ξ_y - 6 * ξ_x^2 + 36 * ξ_x * ξ_y^2 - 36 * ξ_x * ξ_y + 8 * ξ_x - 9 * ξ_y^2 + 9 * ξ_y - 2)) / 4 + i == 13 && return (81 * ξ_x * ξ_y * (9 * ξ_x^2 * ξ_y^2 - 15 * ξ_x^2 * ξ_y + 6 * ξ_x^2 - 15 * ξ_x * ξ_y^2 + 25 * ξ_x * ξ_y - 10 * ξ_x + 6 * ξ_y^2 - 10 * ξ_y + 4)) / 4 + i == 14 && return (81 * ξ_x * ξ_y * (- 9 * ξ_x^2 * ξ_y^2 + 15 * ξ_x^2 * ξ_y - 6 * ξ_x^2 + 12 * ξ_x * ξ_y^2 - 20 * ξ_x * ξ_y + 8 * ξ_x - 3 * ξ_y^2 + 5 * ξ_y - 2)) / 4 + i == 15 && return (81 * ξ_x * ξ_y * (- 9 * ξ_x^2 * ξ_y^2 + 12 * ξ_x^2 * ξ_y - 3 * ξ_x^2 + 15 * ξ_x * ξ_y^2 - 20 * ξ_x * ξ_y + 5 * ξ_x - 6 * ξ_y^2 + 8 * ξ_y - 2)) / 4 + i == 16 && return (81 * ξ_x * ξ_y * (9 * ξ_x^2 * ξ_y^2 - 12 * ξ_x^2 * ξ_y + 3 * ξ_x^2 - 12 * ξ_x * ξ_y^2 + 16 * ξ_x * ξ_y - 4 * ξ_x + 3 * ξ_y^2 - 4 * ξ_y + 1)) / 4 throw(ArgumentError("no shape function $i for interpolation $ip")) end ################################ # Lagrange RefTriangle order 1 # ################################ -getnbasefunctions(::Lagrange{RefTriangle,1}) = 3 +getnbasefunctions(::Lagrange{RefTriangle, 1}) = 3 -edgedof_indices(::Lagrange{RefTriangle,1}) = ((1,2), (2,3), (3,1)) -facedof_indices(ip::Lagrange{RefTriangle,1}) = (ntuple(i->i, getnbasefunctions(ip)),) +edgedof_indices(::Lagrange{RefTriangle, 1}) = ((1, 2), (2, 3), (3, 1)) +facedof_indices(ip::Lagrange{RefTriangle, 1}) = (ntuple(i -> i, getnbasefunctions(ip)),) -function reference_coordinates(::Lagrange{RefTriangle,1}) - return [Vec{2, Float64}((1.0, 0.0)), - Vec{2, Float64}((0.0, 1.0)), - Vec{2, Float64}((0.0, 0.0))] +function reference_coordinates(::Lagrange{RefTriangle, 1}) + return [ + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((0.0, 0.0)), + ] end function reference_shape_value(ip::Lagrange{RefTriangle, 1}, ξ::Vec{2}, i::Int) @@ -690,19 +707,21 @@ end ################################ # Lagrange RefTriangle order 2 # ################################ -getnbasefunctions(::Lagrange{RefTriangle,2}) = 6 +getnbasefunctions(::Lagrange{RefTriangle, 2}) = 6 -edgedof_indices(::Lagrange{RefTriangle,2}) = ((1,2,4), (2,3,5), (3,1,6)) -edgedof_interior_indices(::Lagrange{RefTriangle,2}) = ((4,), (5,), (6,)) -facedof_indices(ip::Lagrange{RefTriangle,2}) = (ntuple(i->i, getnbasefunctions(ip)),) +edgedof_indices(::Lagrange{RefTriangle, 2}) = ((1, 2, 4), (2, 3, 5), (3, 1, 6)) +edgedof_interior_indices(::Lagrange{RefTriangle, 2}) = ((4,), (5,), (6,)) +facedof_indices(ip::Lagrange{RefTriangle, 2}) = (ntuple(i -> i, getnbasefunctions(ip)),) -function reference_coordinates(::Lagrange{RefTriangle,2}) - return [Vec{2, Float64}((1.0, 0.0)), - Vec{2, Float64}((0.0, 1.0)), - Vec{2, Float64}((0.0, 0.0)), - Vec{2, Float64}((0.5, 0.5)), - Vec{2, Float64}((0.0, 0.5)), - Vec{2, Float64}((0.5, 0.0))] +function reference_coordinates(::Lagrange{RefTriangle, 2}) + return [ + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((0.0, 0.0)), + Vec{2, Float64}((0.5, 0.5)), + Vec{2, Float64}((0.0, 0.5)), + Vec{2, Float64}((0.5, 0.0)), + ] end function reference_shape_value(ip::Lagrange{RefTriangle, 2}, ξ::Vec{2}, i::Int) @@ -724,9 +743,9 @@ end # see https://getfem.readthedocs.io/en/latest/userdoc/appendixA.html const Lagrange2Tri345 = Union{ - Lagrange{RefTriangle,3}, - Lagrange{RefTriangle,4}, - Lagrange{RefTriangle,5}, + Lagrange{RefTriangle, 3}, + Lagrange{RefTriangle, 4}, + Lagrange{RefTriangle, 5}, } function getnbasefunctions(ip::Lagrange2Tri345) @@ -735,7 +754,7 @@ function getnbasefunctions(ip::Lagrange2Tri345) end # Permutation to switch numbering to Ferrite ordering -const permdof2DLagrange2Tri345 = Dict{Int,Vector{Int}}( +const permdof2DLagrange2Tri345 = Dict{Int, Vector{Int}}( 1 => [1, 2, 3], 2 => [3, 6, 1, 5, 4, 2], 3 => [4, 10, 1, 7, 9, 8, 5, 2, 3, 6], @@ -745,11 +764,11 @@ const permdof2DLagrange2Tri345 = Dict{Int,Vector{Int}}( function edgedof_indices(ip::Lagrange2Tri345) order = getorder(ip) - order == 1 && return ((1,2), (2,3), (3,1)) - order == 2 && return ((1,2,4), (2,3,5), (3,1,6)) - order == 3 && return ((1,2,4,5), (2,3,6,7), (3,1,8,9)) - order == 4 && return ((1,2,4,5,6), (2,3,7,8,9), (3,1,10,11,12)) - order == 5 && return ((1,2,4,5,6,7), (2,3,8,9,10,11), (3,1,12,13,14,15)) + order == 1 && return ((1, 2), (2, 3), (3, 1)) + order == 2 && return ((1, 2, 4), (2, 3, 5), (3, 1, 6)) + order == 3 && return ((1, 2, 4, 5), (2, 3, 6, 7), (3, 1, 8, 9)) + order == 4 && return ((1, 2, 4, 5, 6), (2, 3, 7, 8, 9), (3, 1, 10, 11, 12)) + order == 5 && return ((1, 2, 4, 5, 6, 7), (2, 3, 8, 9, 10, 11), (3, 1, 12, 13, 14, 15)) throw(ArgumentError("Unsupported order $order for Lagrange on triangles.")) end @@ -758,26 +777,26 @@ function edgedof_interior_indices(ip::Lagrange2Tri345) order = getorder(ip) order == 1 && return ((), (), ()) order == 2 && return ((4,), (5,), (6,)) - order == 3 && return ((4,5), (6,7), (8,9)) - order == 4 && return ((4,5,6), (7,8,9), (10,11,12)) - order == 5 && return ((4,5,6,7), (8,9,10,11), (12,13,14,15)) + order == 3 && return ((4, 5), (6, 7), (8, 9)) + order == 4 && return ((4, 5, 6), (7, 8, 9), (10, 11, 12)) + order == 5 && return ((4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14, 15)) throw(ArgumentError("Unsupported order $order for Lagrange on triangles.")) end -facedof_indices(ip::Lagrange2Tri345) = (ntuple(i->i, getnbasefunctions(ip)),) +facedof_indices(ip::Lagrange2Tri345) = (ntuple(i -> i, getnbasefunctions(ip)),) function facedof_interior_indices(ip::Lagrange2Tri345) order = getorder(ip) ncellintdofs = (order + 1) * (order + 2) ÷ 2 - 3 * order totaldofs = getnbasefunctions(ip) - return (ntuple(i->totaldofs-ncellintdofs+i, ncellintdofs),) + return (ntuple(i -> totaldofs - ncellintdofs + i, ncellintdofs),) end function reference_coordinates(ip::Lagrange2Tri345) order = getorder(ip) coordpts = Vector{Vec{2, Float64}}() - for k = 0:order - for l = 0:(order - k) + for k in 0:order + for l in 0:(order - k) push!(coordpts, Vec{2, Float64}((l / order, k / order))) end end @@ -794,15 +813,15 @@ function reference_shape_value(ip::Lagrange2Tri345, ξ::Vec{2}, i::Int) ξ_y = ξ[2] i1, i2, i3 = _numlin_basis2D(i, order) val = one(ξ_y) - i1 ≥ 1 && (val *= prod((order - order * (ξ_x + ξ_y ) - j) / (j + 1) for j = 0:(i1 - 1))) - i2 ≥ 1 && (val *= prod((order * ξ_x - j) / (j + 1) for j = 0:(i2 - 1))) - i3 ≥ 1 && (val *= prod((order * ξ_y - j) / (j + 1) for j = 0:(i3 - 1))) + i1 ≥ 1 && (val *= prod((order - order * (ξ_x + ξ_y) - j) / (j + 1) for j in 0:(i1 - 1))) + i2 ≥ 1 && (val *= prod((order * ξ_x - j) / (j + 1) for j in 0:(i2 - 1))) + i3 ≥ 1 && (val *= prod((order * ξ_y - j) / (j + 1) for j in 0:(i3 - 1))) return val end function _numlin_basis2D(i, order) c, j1, j2, j3 = 0, 0, 0, 0 - for k = 0:order + for k in 0:order if i <= c + (order + 1 - k) j2 = i - c - 1 break @@ -811,23 +830,25 @@ function _numlin_basis2D(i, order) c += order + 1 - k end end - j1 = order - j2 -j3 + j1 = order - j2 - j3 return j1, j2, j3 end ################################### # Lagrange RefTetrahedron order 1 # ################################### -getnbasefunctions(::Lagrange{RefTetrahedron,1}) = 4 +getnbasefunctions(::Lagrange{RefTetrahedron, 1}) = 4 -facedof_indices(::Lagrange{RefTetrahedron,1}) = ((1,3,2), (1,2,4), (2,3,4), (1,4,3)) -edgedof_indices(::Lagrange{RefTetrahedron,1}) = ((1,2), (2,3), (3,1), (1,4), (2,4), (3,4)) +facedof_indices(::Lagrange{RefTetrahedron, 1}) = ((1, 3, 2), (1, 2, 4), (2, 3, 4), (1, 4, 3)) +edgedof_indices(::Lagrange{RefTetrahedron, 1}) = ((1, 2), (2, 3), (3, 1), (1, 4), (2, 4), (3, 4)) -function reference_coordinates(::Lagrange{RefTetrahedron,1}) - return [Vec{3, Float64}((0.0, 0.0, 0.0)), - Vec{3, Float64}((1.0, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1.0))] +function reference_coordinates(::Lagrange{RefTetrahedron, 1}) + return [ + Vec{3, Float64}((0.0, 0.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + ] end function reference_shape_value(ip::Lagrange{RefTetrahedron, 1}, ξ::Vec{3}, i::Int) @@ -844,23 +865,25 @@ end ################################### # Lagrange RefTetrahedron order 2 # ################################### -getnbasefunctions(::Lagrange{RefTetrahedron,2}) = 10 +getnbasefunctions(::Lagrange{RefTetrahedron, 2}) = 10 -facedof_indices(::Lagrange{RefTetrahedron,2}) = ((1,3,2,7,6,5), (1,2,4,5,9,8), (2,3,4,6,10,9), (1,4,3,8,10,7)) -edgedof_indices(::Lagrange{RefTetrahedron,2}) = ((1,2,5), (2,3,6), (3,1,7), (1,4,8), (2,4,9), (3,4,10)) -edgedof_interior_indices(::Lagrange{RefTetrahedron,2}) = ((5,), (6,), (7,), (8,), (9,), (10,)) +facedof_indices(::Lagrange{RefTetrahedron, 2}) = ((1, 3, 2, 7, 6, 5), (1, 2, 4, 5, 9, 8), (2, 3, 4, 6, 10, 9), (1, 4, 3, 8, 10, 7)) +edgedof_indices(::Lagrange{RefTetrahedron, 2}) = ((1, 2, 5), (2, 3, 6), (3, 1, 7), (1, 4, 8), (2, 4, 9), (3, 4, 10)) +edgedof_interior_indices(::Lagrange{RefTetrahedron, 2}) = ((5,), (6,), (7,), (8,), (9,), (10,)) -function reference_coordinates(::Lagrange{RefTetrahedron,2}) - return [Vec{3, Float64}((0.0, 0.0, 0.0)), - Vec{3, Float64}((1.0, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1.0)), - Vec{3, Float64}((0.5, 0.0, 0.0)), - Vec{3, Float64}((0.5, 0.5, 0.0)), - Vec{3, Float64}((0.0, 0.5, 0.0)), - Vec{3, Float64}((0.0, 0.0, 0.5)), - Vec{3, Float64}((0.5, 0.0, 0.5)), - Vec{3, Float64}((0.0, 0.5, 0.5))] +function reference_coordinates(::Lagrange{RefTetrahedron, 2}) + return [ + Vec{3, Float64}((0.0, 0.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + Vec{3, Float64}((0.5, 0.0, 0.0)), + Vec{3, Float64}((0.5, 0.5, 0.0)), + Vec{3, Float64}((0.0, 0.5, 0.0)), + Vec{3, Float64}((0.0, 0.0, 0.5)), + Vec{3, Float64}((0.5, 0.0, 0.5)), + Vec{3, Float64}((0.0, 0.5, 0.5)), + ] end # http://www.colorado.edu/engineering/CAS/courses.d/AFEM.d/AFEM.Ch09.d/AFEM.Ch09.pdf @@ -885,20 +908,22 @@ end ################################## # Lagrange RefHexahedron order 1 # ################################## -getnbasefunctions(::Lagrange{RefHexahedron,1}) = 8 +getnbasefunctions(::Lagrange{RefHexahedron, 1}) = 8 -facedof_indices(::Lagrange{RefHexahedron,1}) = ((1,4,3,2), (1,2,6,5), (2,3,7,6), (3,4,8,7), (1,5,8,4), (5,6,7,8)) -edgedof_indices(::Lagrange{RefHexahedron,1}) = ((1,2), (2,3), (3,4), (4,1), (5,6), (6,7), (7,8), (8,5), (1,5), (2,6), (3,7), (4,8)) +facedof_indices(::Lagrange{RefHexahedron, 1}) = ((1, 4, 3, 2), (1, 2, 6, 5), (2, 3, 7, 6), (3, 4, 8, 7), (1, 5, 8, 4), (5, 6, 7, 8)) +edgedof_indices(::Lagrange{RefHexahedron, 1}) = ((1, 2), (2, 3), (3, 4), (4, 1), (5, 6), (6, 7), (7, 8), (8, 5), (1, 5), (2, 6), (3, 7), (4, 8)) -function reference_coordinates(::Lagrange{RefHexahedron,1}) - return [Vec{3, Float64}((-1.0, -1.0, -1.0)), - Vec{3, Float64}(( 1.0, -1.0, -1.0)), - Vec{3, Float64}(( 1.0, 1.0, -1.0)), - Vec{3, Float64}((-1.0, 1.0, -1.0)), - Vec{3, Float64}((-1.0, -1.0, 1.0)), - Vec{3, Float64}(( 1.0, -1.0, 1.0)), - Vec{3, Float64}(( 1.0, 1.0, 1.0)), - Vec{3, Float64}((-1.0, 1.0, 1.0))] +function reference_coordinates(::Lagrange{RefHexahedron, 1}) + return [ + Vec{3, Float64}((-1.0, -1.0, -1.0)), + Vec{3, Float64}((1.0, -1.0, -1.0)), + Vec{3, Float64}((1.0, 1.0, -1.0)), + Vec{3, Float64}((-1.0, 1.0, -1.0)), + Vec{3, Float64}((-1.0, -1.0, 1.0)), + Vec{3, Float64}((1.0, -1.0, 1.0)), + Vec{3, Float64}((1.0, 1.0, 1.0)), + Vec{3, Float64}((-1.0, 1.0, 1.0)), + ] end function reference_shape_value(ip::Lagrange{RefHexahedron, 1}, ξ::Vec{3}, i::Int) @@ -921,79 +946,80 @@ end # Lagrange RefHexahedron order 2 # ################################## # Based on vtkTriQuadraticHexahedron (see https://kitware.github.io/vtk-examples/site/Cxx/GeometricObjects/IsoparametricCellsDemo/) -getnbasefunctions(::Lagrange{RefHexahedron,2}) = 27 - -facedof_indices(::Lagrange{RefHexahedron,2}) = ( - (1,4,3,2, 12,11,10,9, 21), - (1,2,6,5, 9,18,13,17, 22), - (2,3,7,6, 10,19,14,18, 23), - (3,4,8,7, 11,20,15,19, 24), - (1,5,8,4, 17,16,20,12, 25), - (5,6,7,8, 13,14,15,16, 26), +getnbasefunctions(::Lagrange{RefHexahedron, 2}) = 27 + +facedof_indices(::Lagrange{RefHexahedron, 2}) = ( + (1, 4, 3, 2, 12, 11, 10, 9, 21), + (1, 2, 6, 5, 9, 18, 13, 17, 22), + (2, 3, 7, 6, 10, 19, 14, 18, 23), + (3, 4, 8, 7, 11, 20, 15, 19, 24), + (1, 5, 8, 4, 17, 16, 20, 12, 25), + (5, 6, 7, 8, 13, 14, 15, 16, 26), ) -facedof_interior_indices(::Lagrange{RefHexahedron,2}) = ( +facedof_interior_indices(::Lagrange{RefHexahedron, 2}) = ( (21,), (22,), (23,), (24,), (25,), (26,), ) -edgedof_indices(::Lagrange{RefHexahedron,2}) = ( - (1,2, 9), - (2,3, 10), - (3,4, 11), - (4,1, 12), - (5,6, 13), - (6,7, 14), - (7,8, 15), - (8,5, 16), - (1,5, 17), - (2,6, 18), - (3,7, 19), - (4,8, 20), +edgedof_indices(::Lagrange{RefHexahedron, 2}) = ( + (1, 2, 9), + (2, 3, 10), + (3, 4, 11), + (4, 1, 12), + (5, 6, 13), + (6, 7, 14), + (7, 8, 15), + (8, 5, 16), + (1, 5, 17), + (2, 6, 18), + (3, 7, 19), + (4, 8, 20), ) -edgedof_interior_indices(::Lagrange{RefHexahedron,2}) = ( - (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,) +edgedof_interior_indices(::Lagrange{RefHexahedron, 2}) = ( + (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,), ) -volumedof_interior_indices(::Lagrange{RefHexahedron,2}) = (27,) - -function reference_coordinates(::Lagrange{RefHexahedron,2}) - # vertex - return [Vec{3, Float64}((-1.0, -1.0, -1.0)), # 1 - Vec{3, Float64}(( 1.0, -1.0, -1.0)), # 2 - Vec{3, Float64}(( 1.0, 1.0, -1.0)), # 3 - Vec{3, Float64}((-1.0, 1.0, -1.0)), # 4 - Vec{3, Float64}((-1.0, -1.0, 1.0)), # 5 - Vec{3, Float64}(( 1.0, -1.0, 1.0)), # 6 - Vec{3, Float64}(( 1.0, 1.0, 1.0)), # 7 - Vec{3, Float64}((-1.0, 1.0, 1.0)), # 8 - # edge - Vec{3, Float64}(( 0.0, -1.0, -1.0)), # 9 - Vec{3, Float64}(( 1.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, 1.0, -1.0)), - Vec{3, Float64}((-1.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, -1.0, 1.0)), - Vec{3, Float64}(( 1.0, 0.0, 1.0)), - Vec{3, Float64}(( 0.0, 1.0, 1.0)), - Vec{3, Float64}((-1.0, 0.0, 1.0)), - Vec{3, Float64}((-1.0, -1.0, 0.0)), - Vec{3, Float64}(( 1.0, -1.0, 0.0)), - Vec{3, Float64}(( 1.0, 1.0, 0.0)), - Vec{3, Float64}((-1.0, 1.0, 0.0)), # 20 - Vec{3, Float64}(( 0.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, -1.0, 0.0)), - Vec{3, Float64}(( 1.0, 0.0, 0.0)), - Vec{3, Float64}(( 0.0, 1.0, 0.0)), - Vec{3, Float64}((-1.0, 0.0, 0.0)), - Vec{3, Float64}(( 0.0, 0.0, 1.0)), # 26 - # interior - Vec{3, Float64}((0.0, 0.0, 0.0)), # 27 - ] +volumedof_interior_indices(::Lagrange{RefHexahedron, 2}) = (27,) + +function reference_coordinates(::Lagrange{RefHexahedron, 2}) + return [ + # vertex + Vec{3, Float64}((-1.0, -1.0, -1.0)), # 1 + Vec{3, Float64}((1.0, -1.0, -1.0)), # 2 + Vec{3, Float64}((1.0, 1.0, -1.0)), # 3 + Vec{3, Float64}((-1.0, 1.0, -1.0)), # 4 + Vec{3, Float64}((-1.0, -1.0, 1.0)), # 5 + Vec{3, Float64}((1.0, -1.0, 1.0)), # 6 + Vec{3, Float64}((1.0, 1.0, 1.0)), # 7 + Vec{3, Float64}((-1.0, 1.0, 1.0)), # 8 + # edge + Vec{3, Float64}((0.0, -1.0, -1.0)), # 9 + Vec{3, Float64}((1.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, 1.0, -1.0)), + Vec{3, Float64}((-1.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 1.0)), + Vec{3, Float64}((1.0, 0.0, 1.0)), + Vec{3, Float64}((0.0, 1.0, 1.0)), + Vec{3, Float64}((-1.0, 0.0, 1.0)), + Vec{3, Float64}((-1.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, 1.0, 0.0)), + Vec{3, Float64}((-1.0, 1.0, 0.0)), # 20 + Vec{3, Float64}((0.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((-1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), # 26 + # interior + Vec{3, Float64}((0.0, 0.0, 0.0)), # 27 + ] end function reference_shape_value(ip::Lagrange{RefHexahedron, 2}, ξ::Vec{3, T}, i::Int) where {T} # Some local helpers. - @inline φ₁(x::T) = -x*(1-x)/2 - @inline φ₂(x::T) = (1+x)*(1-x) - @inline φ₃(x::T) = x*(1+x)/2 + @inline φ₁(x::T) = -x * (1 - x) / 2 + @inline φ₂(x::T) = (1 + x) * (1 - x) + @inline φ₃(x::T) = x * (1 + x) / 2 (ξ_x, ξ_y, ξ_z) = ξ # vertices i == 1 && return φ₁(ξ_x) * φ₁(ξ_y) * φ₁(ξ_z) @@ -1005,7 +1031,7 @@ function reference_shape_value(ip::Lagrange{RefHexahedron, 2}, ξ::Vec{3, T}, i: i == 7 && return φ₃(ξ_x) * φ₃(ξ_y) * φ₃(ξ_z) i == 8 && return φ₁(ξ_x) * φ₃(ξ_y) * φ₃(ξ_z) # edges - i == 9 && return φ₂(ξ_x) * φ₁(ξ_y) * φ₁(ξ_z) + i == 9 && return φ₂(ξ_x) * φ₁(ξ_y) * φ₁(ξ_z) i == 10 && return φ₃(ξ_x) * φ₂(ξ_y) * φ₁(ξ_z) i == 11 && return φ₂(ξ_x) * φ₃(ξ_y) * φ₁(ξ_z) i == 12 && return φ₁(ξ_x) * φ₂(ξ_y) * φ₁(ξ_z) @@ -1034,28 +1060,30 @@ end # Lagrange RefPrism order 1 # ############################# # Build on https://defelement.com/elements/examples/prism-Lagrange-1.html -getnbasefunctions(::Lagrange{RefPrism,1}) = 6 - -facedof_indices(::Lagrange{RefPrism,1}) = ((1,3,2), (1,2,5,4), (3,1,4,6), (2,3,6,5), (4,5,6)) -edgedof_indices(::Lagrange{RefPrism,1}) = ((2,1), (1,3), (1,4), (3,2), (2,5), (3,6), (4,5), (4,6), (6,5)) - -function reference_coordinates(::Lagrange{RefPrism,1}) - return [Vec{3, Float64}((0.0, 0.0, 0.0)), - Vec{3, Float64}((1.0, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1.0)), - Vec{3, Float64}((1.0, 0.0, 1.0)), - Vec{3, Float64}((0.0, 1.0, 1.0))] -end - -function reference_shape_value(ip::Lagrange{RefPrism,1}, ξ::Vec{3}, i::Int) - (x,y,z) = ξ - i == 1 && return 1-x-y -z*(1-x-y) - i == 2 && return x*(1-z) - i == 3 && return y*(1-z) - i == 4 && return z*(1-x-y) - i == 5 && return x*z - i == 6 && return y*z +getnbasefunctions(::Lagrange{RefPrism, 1}) = 6 + +facedof_indices(::Lagrange{RefPrism, 1}) = ((1, 3, 2), (1, 2, 5, 4), (3, 1, 4, 6), (2, 3, 6, 5), (4, 5, 6)) +edgedof_indices(::Lagrange{RefPrism, 1}) = ((2, 1), (1, 3), (1, 4), (3, 2), (2, 5), (3, 6), (4, 5), (4, 6), (6, 5)) + +function reference_coordinates(::Lagrange{RefPrism, 1}) + return [ + Vec{3, Float64}((0.0, 0.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + Vec{3, Float64}((1.0, 0.0, 1.0)), + Vec{3, Float64}((0.0, 1.0, 1.0)), + ] +end + +function reference_shape_value(ip::Lagrange{RefPrism, 1}, ξ::Vec{3}, i::Int) + (x, y, z) = ξ + i == 1 && return 1 - x - y - z * (1 - x - y) + i == 2 && return x * (1 - z) + i == 3 && return y * (1 - z) + i == 4 && return z * (1 - x - y) + i == 5 && return x * z + i == 6 && return y * z throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1064,38 +1092,38 @@ end ############################# # Build on https://defelement.com/elements/examples/prism-Lagrange-2.html . # This is simply the tensor-product of a quadratic triangle with a quadratic line. -getnbasefunctions(::Lagrange{RefPrism,2}) = 18 - -facedof_indices(::Lagrange{RefPrism,2}) = ( - #Vertices| Edges | Face - (1,3,2 , 8,10,7 ), - (1,2,5,4, 7,11,13,9, 16), - (3,1,4,6, 8,9,14,12, 17), - (2,3,6,5, 10,12,15,11, 18), - (4,5,6 , 13,15,14 ), +getnbasefunctions(::Lagrange{RefPrism, 2}) = 18 + +facedof_indices(::Lagrange{RefPrism, 2}) = ( + # Vertices, Edges, Face + (1, 3, 2, 8, 10, 7), + (1, 2, 5, 4, 7, 11, 13, 9, 16), + (3, 1, 4, 6, 8, 9, 14, 12, 17), + (2, 3, 6, 5, 10, 12, 15, 11, 18), + (4, 5, 6, 13, 15, 14), ) -facedof_interior_indices(::Lagrange{RefPrism,2}) = ( - #Vertices| Edges | Face +facedof_interior_indices(::Lagrange{RefPrism, 2}) = ( + # Face (), (16,), (17,), (18,), (), ) -edgedof_indices(::Lagrange{RefPrism,2}) = ( - #Vert|Edge - (2,1, 7), - (1,3, 8), - (1,4, 9), - (3,2, 10), - (2,5, 11), - (3,6, 12), - (4,5, 13), - (4,6, 14), - (6,5, 15), +edgedof_indices(::Lagrange{RefPrism, 2}) = ( + # Vertices, Edge + (2, 1, 7), + (1, 3, 8), + (1, 4, 9), + (3, 2, 10), + (2, 5, 11), + (3, 6, 12), + (4, 5, 13), + (4, 6, 14), + (6, 5, 15), ) -edgedof_interior_indices(::Lagrange{RefPrism,2}) = ( - #Vert|Edge +edgedof_interior_indices(::Lagrange{RefPrism, 2}) = ( + # Edge (7,), (8,), (9,), @@ -1107,50 +1135,52 @@ edgedof_interior_indices(::Lagrange{RefPrism,2}) = ( (15,), ) -function reference_coordinates(::Lagrange{RefPrism,2}) - return [Vec{3, Float64}((0.0, 0.0, 0.0)), - Vec{3, Float64}((1.0, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1.0)), - Vec{3, Float64}((1.0, 0.0, 1.0)), - Vec{3, Float64}((0.0, 1.0, 1.0)), - Vec{3, Float64}((1/2, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1/2, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1/2)), - Vec{3, Float64}((1/2, 1/2, 0.0)), - Vec{3, Float64}((1.0, 0.0, 1/2)), - Vec{3, Float64}((0.0, 1.0, 1/2)), - Vec{3, Float64}((1/2, 0.0, 1.0)), - Vec{3, Float64}((0.0, 1/2, 1.0)), - Vec{3, Float64}((1/2, 1/2, 1.0)), - Vec{3, Float64}((1/2, 0.0, 1/2)), - Vec{3, Float64}((0.0, 1/2, 1/2)), - Vec{3, Float64}((1/2, 1/2, 1/2)),] +function reference_coordinates(::Lagrange{RefPrism, 2}) + return [ + Vec{3, Float64}((0.0, 0.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + Vec{3, Float64}((1.0, 0.0, 1.0)), + Vec{3, Float64}((0.0, 1.0, 1.0)), + Vec{3, Float64}((0.5, 0.0, 0.0)), + Vec{3, Float64}((0.0, 0.5, 0.0)), + Vec{3, Float64}((0.0, 0.0, 0.5)), + Vec{3, Float64}((0.5, 0.5, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.5)), + Vec{3, Float64}((0.0, 1.0, 0.5)), + Vec{3, Float64}((0.5, 0.0, 1.0)), + Vec{3, Float64}((0.0, 0.5, 1.0)), + Vec{3, Float64}((0.5, 0.5, 1.0)), + Vec{3, Float64}((0.5, 0.0, 0.5)), + Vec{3, Float64}((0.0, 0.5, 0.5)), + Vec{3, Float64}((0.5, 0.5, 0.5)), + ] end function reference_shape_value(ip::Lagrange{RefPrism, 2}, ξ::Vec{3}, i::Int) - (x,y,z) = ξ - x² = x*x - y² = y*y - z² = z*z - i == 1 && return 4*x²*z² - 6x²*z +2x² +8x*y*z² -12x*y*z +4x*y -6x*z² +9x*z -3x +4y²*z² -6y²*z + 2y² -6y*z² +9y*z -3*y +2z² -3z +1 - i == 2 && return x*(4x*z² -6x*z +2x -2z² +3z -1) - i == 3 && return y*(4y*z² -6y*z +2y -2z² +3z -1) - i == 4 && return z*(4x²*z -2x² + 8x*y*z -4x*y -6x*z +3x +4y²*z -2y² -6y*z +3y +2z -1) - i == 5 && return x*z*(4x*z -2x -2z +1) - i == 6 && return y*z*(4y*z -2y -2z +1) - i == 7 && return 4x*(-2x*z² +3x*z -x -2*y*z² +3y*z -y +2z² -3z +1) - i == 8 && return 4y*(-2x*z² +3x*z -x -2*y*z² +3y*z -y +2z² -3z +1) - i == 9 && return 4z*(-2x²*z +2x² -4x*y*z +4x*y +3x*z -3x -2y²*z +2y² +3y*z -3y -z +1) - i == 10 && return 4x*y*(2z² -3z +1) - i == 11 && return 4x*z*(-2x*z +2x +z -1) - i == 12 && return 4y*z*(-2y*z +2y +z -1) - i == 13 && return 4x*z*(-2x*z +x -2y*z +y +2z -1) - i == 14 && return 4y*z*(-2x*z +x -2y*z +y +2z -1) - i == 15 && return 4x*y*z*(2z -1) - i == 16 && return 16x*z*(x*z -x +y*z -y -z +1) - i == 17 && return 16y*z*(x*z -x +y*z -y -z +1) - i == 18 && return 16x*y*z*(1 -z) + (x, y, z) = ξ + x² = x * x + y² = y * y + z² = z * z + i == 1 && return 4 * x² * z² - 6x² * z + 2x² + 8x * y * z² - 12x * y * z + 4x * y - 6x * z² + 9x * z - 3x + 4y² * z² - 6y² * z + 2y² - 6y * z² + 9y * z - 3 * y + 2z² - 3z + 1 + i == 2 && return x * (4x * z² - 6x * z + 2x - 2z² + 3z - 1) + i == 3 && return y * (4y * z² - 6y * z + 2y - 2z² + 3z - 1) + i == 4 && return z * (4x² * z - 2x² + 8x * y * z - 4x * y - 6x * z + 3x + 4y² * z - 2y² - 6y * z + 3y + 2z - 1) + i == 5 && return x * z * (4x * z - 2x - 2z + 1) + i == 6 && return y * z * (4y * z - 2y - 2z + 1) + i == 7 && return 4x * (-2x * z² + 3x * z - x - 2 * y * z² + 3y * z - y + 2z² - 3z + 1) + i == 8 && return 4y * (-2x * z² + 3x * z - x - 2 * y * z² + 3y * z - y + 2z² - 3z + 1) + i == 9 && return 4z * (-2x² * z + 2x² - 4x * y * z + 4x * y + 3x * z - 3x - 2y² * z + 2y² + 3y * z - 3y - z + 1) + i == 10 && return 4x * y * (2z² - 3z + 1) + i == 11 && return 4x * z * (-2x * z + 2x + z - 1) + i == 12 && return 4y * z * (-2y * z + 2y + z - 1) + i == 13 && return 4x * z * (-2x * z + x - 2y * z + y + 2z - 1) + i == 14 && return 4y * z * (-2x * z + x - 2y * z + y + 2z - 1) + i == 15 && return 4x * y * z * (2z - 1) + i == 16 && return 16x * z * (x * z - x + y * z - y - z + 1) + i == 17 && return 16y * z * (x * z - x + y * z - y - z + 1) + i == 18 && return 16x * y * z * (1 - z) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1158,25 +1188,27 @@ end ##################################### # Lagrange dim 3 RefPyramid order 1 # ##################################### -getnbasefunctions(::Lagrange{RefPyramid,1}) = 5 -facedof_indices(::Lagrange{RefPyramid,1}) = ((1,3,4,2), (1,2,5), (1,5,3), (2,4,5), (3,5,4), ) -edgedof_indices(::Lagrange{RefPyramid,1}) = ((1,2), (1,3), (1,5), (2,4), (2,5), (4,3), (3,5), (4,5)) +getnbasefunctions(::Lagrange{RefPyramid, 1}) = 5 +facedof_indices(::Lagrange{RefPyramid, 1}) = ((1, 3, 4, 2), (1, 2, 5), (1, 5, 3), (2, 4, 5), (3, 5, 4)) +edgedof_indices(::Lagrange{RefPyramid, 1}) = ((1, 2), (1, 3), (1, 5), (2, 4), (2, 5), (4, 3), (3, 5), (4, 5)) -function reference_coordinates(::Lagrange{RefPyramid,1}) - return [Vec{3, Float64}((0.0, 0.0, 0.0)), - Vec{3, Float64}((1.0, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1.0, 0.0)), - Vec{3, Float64}((1.0, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1.0))] +function reference_coordinates(::Lagrange{RefPyramid, 1}) + return [ + Vec{3, Float64}((0.0, 0.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((1.0, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + ] end -function reference_shape_value(ip::Lagrange{RefPyramid,1}, ξ::Vec{3,T}, i::Int) where T - (x,y,z) = ξ +function reference_shape_value(ip::Lagrange{RefPyramid, 1}, ξ::Vec{3, T}, i::Int) where {T} + (x, y, z) = ξ zzero = z ≈ one(T) - i == 1 && return zzero ? zero(T) : (-x*y+(z-1)*(-x-y-z+1))/(z-1) - i == 2 && return zzero ? zero(T) : x*(y+z-1)/(z-1) - i == 3 && return zzero ? zero(T) : y*(x+z-1)/(z-1) - i == 4 && return zzero ? zero(T) : -x*y/(z-1) + i == 1 && return zzero ? zero(T) : (-x * y + (z - 1) * (-x - y - z + 1)) / (z - 1) + i == 2 && return zzero ? zero(T) : x * (y + z - 1) / (z - 1) + i == 3 && return zzero ? zero(T) : y * (x + z - 1) / (z - 1) + i == 4 && return zzero ? zero(T) : -x * y / (z - 1) i == 5 && return z throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1184,34 +1216,34 @@ end ##################################### # Lagrange dim 3 RefPyramid order 2 # ##################################### -getnbasefunctions(::Lagrange{RefPyramid,2}) = 14 - -facedof_indices(::Lagrange{RefPyramid,2}) = ( - #Vertices | Edges | Face - (1,3,4,2, 7,11,9,6, 14), - (1,2,5 , 6,10,8 ), - (1,5,3 , 7,12,8 ), - (2,4,5 , 9,13,10 ), - (3,5,4 , 12,13,11 ), +getnbasefunctions(::Lagrange{RefPyramid, 2}) = 14 + +facedof_indices(::Lagrange{RefPyramid, 2}) = ( + # Vertices, Edges, Face + (1, 3, 4, 2, 7, 11, 9, 6, 14), + (1, 2, 5, 6, 10, 8), + (1, 5, 3, 7, 12, 8), + (2, 4, 5, 9, 13, 10), + (3, 5, 4, 12, 13, 11), ) -facedof_interior_indices(::Lagrange{RefPyramid,2}) = ( +facedof_interior_indices(::Lagrange{RefPyramid, 2}) = ( (14,), (), (), (), (), ) -edgedof_indices(::Lagrange{RefPyramid,2}) = ( - (1,2,6), - (1,3,7), - (1,5,8), - (2,4,9), - (2,5,10), - (4,3,11), - (3,5,12), - (4,5,13) +edgedof_indices(::Lagrange{RefPyramid, 2}) = ( + (1, 2, 6), + (1, 3, 7), + (1, 5, 8), + (2, 4, 9), + (2, 5, 10), + (4, 3, 11), + (3, 5, 12), + (4, 5, 13), ) -edgedof_interior_indices(::Lagrange{RefPyramid,2}) = ( +edgedof_interior_indices(::Lagrange{RefPyramid, 2}) = ( (6,), (7,), (8,), @@ -1221,45 +1253,47 @@ edgedof_interior_indices(::Lagrange{RefPyramid,2}) = ( (12,), (13,), ) -function reference_coordinates(::Lagrange{RefPyramid,2}) - return [Vec{3, Float64}((0.0, 0.0, 0.0)), - Vec{3, Float64}((1.0, 0.0, 0.0)), - Vec{3, Float64}((0.0, 1.0, 0.0)), - Vec{3, Float64}((1.0, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.0, 1.0)), - # edges - Vec{3, Float64}((0.5, 0.0, 0.0)), - Vec{3, Float64}((0.0, 0.5, 0.0)), - Vec{3, Float64}((0.0, 0.0, 0.5)), - Vec{3, Float64}((1.0, 0.5, 0.0)), - Vec{3, Float64}((0.5, 0.0, 0.5)), - Vec{3, Float64}((0.5, 1.0, 0.0)), - Vec{3, Float64}((0.0, 0.5, 0.5)), - Vec{3, Float64}((0.5, 0.5, 0.5)), - # faces - Vec{3, Float64}((0.5, 0.5, 0.0))] -end - -function reference_shape_value(ip::Lagrange{RefPyramid,2}, ξ::Vec{3,T}, i::Int) where T - (x,y,z) = ξ - x² = x*x - y² = y*y - z² = z*z +function reference_coordinates(::Lagrange{RefPyramid, 2}) + return [ + Vec{3, Float64}((0.0, 0.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((1.0, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + # edges + Vec{3, Float64}((0.5, 0.0, 0.0)), + Vec{3, Float64}((0.0, 0.5, 0.0)), + Vec{3, Float64}((0.0, 0.0, 0.5)), + Vec{3, Float64}((1.0, 0.5, 0.0)), + Vec{3, Float64}((0.5, 0.0, 0.5)), + Vec{3, Float64}((0.5, 1.0, 0.0)), + Vec{3, Float64}((0.0, 0.5, 0.5)), + Vec{3, Float64}((0.5, 0.5, 0.5)), + # faces + Vec{3, Float64}((0.5, 0.5, 0.0)), + ] +end + +function reference_shape_value(ip::Lagrange{RefPyramid, 2}, ξ::Vec{3, T}, i::Int) where {T} + (x, y, z) = ξ + x² = x * x + y² = y * y + z² = z * z zzero = z ≈ one(T) - i == 1 && return zzero ? zero(T) : (4*x²*y²*(z-1) + x*y*(6x+6y+z)*(z²-2z+1) + (z-1)*(z² - 2z + 1)*(2x² + 9*x*y + 4*x*z - 3x + 2y² + 4*y*z - 3y + 2z² - 3z + 1)) / ((z-1)*(z²-2z+1)) - i == 2 && return zzero ? zero(T) : x*(4x*y²*(z-1) + y*(6x+2y-z)*(z²-2z+1) + (z-1)*(2x+3y-1)*(z²-2z+1))/((z-1)*(z²-2z+1)) - i == 3 && return zzero ? zero(T) : y*(4x²*y*(z-1) + x*(2x+6y-z)*(z²-2z+1) + (z-1)*(3x+2y-1)*(z²-2z+1))/((z-1)*(z²-2z+1)) - i == 4 && return zzero ? zero(T) : x*y*(4*x*y + 2x*z - 2x + 2y*z - 2y + 2z² - 3z + 1)/(z²-2z+1) - i == 5 && return z*(2z-1) - i == 6 && return zzero ? zero(T) : 4x*(2x*y²*(1-z) - y*(3x+2y)*(z²-2z+1) + (z-1)*(z²-2z+1)*(-x-3y-z+1))/((z-1)*(z²-2z+1)) - i == 7 && return zzero ? zero(T) : 4y*(2x²*y*(1-z) - x*(2x+3y)*(z²-2z+1) + (z-1)*(z²-2z+1)*(-3x-y-z+1))/((z-1)*(z²-2z+1)) - i == 8 && return zzero ? zero(T) : 4z*(-x*y + (z-1)*(-x-y-z+1))/(z-1) - i == 9 && return zzero ? zero(T) : 4*x*y*(-2x*y - 2x*z + 2x - y*z + y - z² + 2*z - 1)/(z²-2z+1) - i == 10 && return zzero ? zero(T) : 4x*z*(y + z - 1)/(z-1) - i == 11 && return zzero ? zero(T) : 4*x*y*(-2x*y - x*z + x - 2y*z + 2y - z² + 2z -1)/(z²-2z+1) - i == 12 && return zzero ? zero(T) : 4y*z*(x + z - 1)/(z-1) - i == 13 && return zzero ? zero(T) : -4x*y*z/(z-1) - i == 14 && return zzero ? zero(T) : 16x*y*(x*y + x*z - x + y*z - y + z² - 2z + 1)/(z²-2z+1) + i == 1 && return zzero ? zero(T) : (4 * x² * y² * (z - 1) + x * y * (6x + 6y + z) * (z² - 2z + 1) + (z - 1) * (z² - 2z + 1) * (2x² + 9 * x * y + 4 * x * z - 3x + 2y² + 4 * y * z - 3y + 2z² - 3z + 1)) / ((z - 1) * (z² - 2z + 1)) + i == 2 && return zzero ? zero(T) : x * (4x * y² * (z - 1) + y * (6x + 2y - z) * (z² - 2z + 1) + (z - 1) * (2x + 3y - 1) * (z² - 2z + 1)) / ((z - 1) * (z² - 2z + 1)) + i == 3 && return zzero ? zero(T) : y * (4x² * y * (z - 1) + x * (2x + 6y - z) * (z² - 2z + 1) + (z - 1) * (3x + 2y - 1) * (z² - 2z + 1)) / ((z - 1) * (z² - 2z + 1)) + i == 4 && return zzero ? zero(T) : x * y * (4 * x * y + 2x * z - 2x + 2y * z - 2y + 2z² - 3z + 1) / (z² - 2z + 1) + i == 5 && return z * (2z - 1) + i == 6 && return zzero ? zero(T) : 4x * (2x * y² * (1 - z) - y * (3x + 2y) * (z² - 2z + 1) + (z - 1) * (z² - 2z + 1) * (-x - 3y - z + 1)) / ((z - 1) * (z² - 2z + 1)) + i == 7 && return zzero ? zero(T) : 4y * (2x² * y * (1 - z) - x * (2x + 3y) * (z² - 2z + 1) + (z - 1) * (z² - 2z + 1) * (-3x - y - z + 1)) / ((z - 1) * (z² - 2z + 1)) + i == 8 && return zzero ? zero(T) : 4z * (-x * y + (z - 1) * (-x - y - z + 1)) / (z - 1) + i == 9 && return zzero ? zero(T) : 4 * x * y * (-2x * y - 2x * z + 2x - y * z + y - z² + 2 * z - 1) / (z² - 2z + 1) + i == 10 && return zzero ? zero(T) : 4x * z * (y + z - 1) / (z - 1) + i == 11 && return zzero ? zero(T) : 4 * x * y * (-2x * y - x * z + x - 2y * z + 2y - z² + 2z - 1) / (z² - 2z + 1) + i == 12 && return zzero ? zero(T) : 4y * z * (x + z - 1) / (z - 1) + i == 13 && return zzero ? zero(T) : -4x * y * z / (z - 1) + i == 14 && return zzero ? zero(T) : 16x * y * (x * y + x * z - x + y * z - y + z² - 2z + 1) / (z² - 2z + 1) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1271,7 +1305,7 @@ Lagrange element with bubble stabilization. """ struct BubbleEnrichedLagrange{shape, order} <: ScalarInterpolation{shape, order} function BubbleEnrichedLagrange{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order}() + return new{shape, order}() end end @@ -1279,28 +1313,30 @@ end # Lagrange-Bubble RefTriangle order 1 # ####################################### # Taken from https://defelement.com/elements/bubble-enriched-lagrange.html -getnbasefunctions(::BubbleEnrichedLagrange{RefTriangle,1}) = 4 -adjust_dofs_during_distribution(::BubbleEnrichedLagrange{RefTriangle,1}) = false +getnbasefunctions(::BubbleEnrichedLagrange{RefTriangle, 1}) = 4 +adjust_dofs_during_distribution(::BubbleEnrichedLagrange{RefTriangle, 1}) = false -vertexdof_indices(::BubbleEnrichedLagrange{RefTriangle,1}) = ((1,), (2,), (3,)) -edgedof_indices(::BubbleEnrichedLagrange{RefTriangle,1}) = ((1,2), (2,3), (3,1)) -facedof_indices(ip::BubbleEnrichedLagrange{RefTriangle,1}) = (ntuple(i->i, getnbasefunctions(ip)),) -facedof_interior_indices(::BubbleEnrichedLagrange{RefTriangle,1}) = ((4,),) +vertexdof_indices(::BubbleEnrichedLagrange{RefTriangle, 1}) = ((1,), (2,), (3,)) +edgedof_indices(::BubbleEnrichedLagrange{RefTriangle, 1}) = ((1, 2), (2, 3), (3, 1)) +facedof_indices(ip::BubbleEnrichedLagrange{RefTriangle, 1}) = (ntuple(i -> i, getnbasefunctions(ip)),) +facedof_interior_indices(::BubbleEnrichedLagrange{RefTriangle, 1}) = ((4,),) -function reference_coordinates(::BubbleEnrichedLagrange{RefTriangle,1}) - return [Vec{2, Float64}((1.0, 0.0)), - Vec{2, Float64}((0.0, 1.0)), - Vec{2, Float64}((0.0, 0.0)), - Vec{2, Float64}((1/3, 1/3)),] +function reference_coordinates(::BubbleEnrichedLagrange{RefTriangle, 1}) + return [ + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((0.0, 0.0)), + Vec{2, Float64}((1 / 3, 1 / 3)), + ] end function reference_shape_value(ip::BubbleEnrichedLagrange{RefTriangle, 1}, ξ::Vec{2}, i::Int) ξ_x = ξ[1] ξ_y = ξ[2] - i == 1 && return ξ_x*(9ξ_y^2 + 9ξ_x*ξ_y - 9ξ_y + 1) - i == 2 && return ξ_y*(9ξ_x^2 + 9ξ_x*ξ_y - 9ξ_x + 1) - i == 3 && return 9ξ_x^2*ξ_y + 9ξ_x*ξ_y^2 - 9ξ_x*ξ_y - ξ_x - ξ_y + 1 - i == 4 && return 27ξ_x*ξ_y*(1 - ξ_x - ξ_y) + i == 1 && return ξ_x * (9ξ_y^2 + 9ξ_x * ξ_y - 9ξ_y + 1) + i == 2 && return ξ_y * (9ξ_x^2 + 9ξ_x * ξ_y - 9ξ_x + 1) + i == 3 && return 9ξ_x^2 * ξ_y + 9ξ_x * ξ_y^2 - 9ξ_x * ξ_y - ξ_x - ξ_y + 1 + i == 4 && return 27ξ_x * ξ_y * (1 - ξ_x - ξ_y) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1312,9 +1348,9 @@ end Serendipity element on hypercubes. Currently only second order variants are implemented. """ -struct Serendipity{shape, order} <: ScalarInterpolation{shape,order} +struct Serendipity{shape, order} <: ScalarInterpolation{shape, order} function Serendipity{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order}() + return new{shape, order}() end end @@ -1325,36 +1361,38 @@ adjust_dofs_during_distribution(::Serendipity{<:Any, 2}) = false adjust_dofs_during_distribution(::Serendipity{<:Any, 1}) = false # Vertices for all Serendipity interpolations are the same -vertexdof_indices(::Serendipity{RefQuadrilateral}) = ((1,),(2,),(3,),(4,)) -vertexdof_indices(::Serendipity{RefHexahedron}) = ((1,),(2,),(3,),(4,),(5,),(6,),(7,),(8,)) +vertexdof_indices(::Serendipity{RefQuadrilateral}) = ((1,), (2,), (3,), (4,)) +vertexdof_indices(::Serendipity{RefHexahedron}) = ((1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,)) ######################################## # Serendipity RefQuadrilateral order 2 # ######################################## -getnbasefunctions(::Serendipity{RefQuadrilateral,2}) = 8 -getlowerorder(::Serendipity{RefQuadrilateral,2}) = Lagrange{RefQuadrilateral,1}() +getnbasefunctions(::Serendipity{RefQuadrilateral, 2}) = 8 +getlowerorder(::Serendipity{RefQuadrilateral, 2}) = Lagrange{RefQuadrilateral, 1}() -edgedof_indices(::Serendipity{RefQuadrilateral,2}) = ((1,2,5), (2,3,6), (3,4,7), (4,1,8)) -edgedof_interior_indices(::Serendipity{RefQuadrilateral,2}) = ((5,), (6,), (7,), (8,)) -facedof_indices(ip::Serendipity{RefQuadrilateral,2}) = (ntuple(i->i, getnbasefunctions(ip)),) +edgedof_indices(::Serendipity{RefQuadrilateral, 2}) = ((1, 2, 5), (2, 3, 6), (3, 4, 7), (4, 1, 8)) +edgedof_interior_indices(::Serendipity{RefQuadrilateral, 2}) = ((5,), (6,), (7,), (8,)) +facedof_indices(ip::Serendipity{RefQuadrilateral, 2}) = (ntuple(i -> i, getnbasefunctions(ip)),) -function reference_coordinates(::Serendipity{RefQuadrilateral,2}) - return [Vec{2, Float64}((-1.0, -1.0)), - Vec{2, Float64}(( 1.0, -1.0)), - Vec{2, Float64}(( 1.0, 1.0)), - Vec{2, Float64}((-1.0, 1.0)), - Vec{2, Float64}(( 0.0, -1.0)), - Vec{2, Float64}(( 1.0, 0.0)), - Vec{2, Float64}(( 0.0, 1.0)), - Vec{2, Float64}((-1.0, 0.0))] -end - -function reference_shape_value(ip::Serendipity{RefQuadrilateral,2}, ξ::Vec{2}, i::Int) +function reference_coordinates(::Serendipity{RefQuadrilateral, 2}) + return [ + Vec{2, Float64}((-1.0, -1.0)), + Vec{2, Float64}((1.0, -1.0)), + Vec{2, Float64}((1.0, 1.0)), + Vec{2, Float64}((-1.0, 1.0)), + Vec{2, Float64}((0.0, -1.0)), + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((-1.0, 0.0)), + ] +end + +function reference_shape_value(ip::Serendipity{RefQuadrilateral, 2}, ξ::Vec{2}, i::Int) ξ_x = ξ[1] ξ_y = ξ[2] i == 1 && return (1 - ξ_x) * (1 - ξ_y) * (-ξ_x - ξ_y - 1) / 4 - i == 2 && return (1 + ξ_x) * (1 - ξ_y) * ( ξ_x - ξ_y - 1) / 4 - i == 3 && return (1 + ξ_x) * (1 + ξ_y) * ( ξ_x + ξ_y - 1) / 4 + i == 2 && return (1 + ξ_x) * (1 - ξ_y) * (ξ_x - ξ_y - 1) / 4 + i == 3 && return (1 + ξ_x) * (1 + ξ_y) * (ξ_x + ξ_y - 1) / 4 i == 4 && return (1 - ξ_x) * (1 + ξ_y) * (-ξ_x + ξ_y - 1) / 4 i == 5 && return (1 - ξ_x * ξ_x) * (1 - ξ_y) / 2 i == 6 && return (1 + ξ_x) * (1 - ξ_y * ξ_y) / 2 @@ -1367,57 +1405,59 @@ end # Serendipity RefHexahedron order 2 # ##################################### # Note that second order serendipity hex has no interior face indices. -getnbasefunctions(::Serendipity{RefHexahedron,2}) = 20 -getlowerorder(::Serendipity{RefHexahedron,2}) = Lagrange{RefHexahedron,1}() - -facedof_indices(::Serendipity{RefHexahedron,2}) = ( - (1,4,3,2, 12,11,10,9), - (1,2,6,5, 9,18,13,17), - (2,3,7,6, 10,19,14,18), - (3,4,8,7, 11,20,15,19), - (1,5,8,4, 17,16,20,12), - (5,6,7,8, 13,14,15,16) +getnbasefunctions(::Serendipity{RefHexahedron, 2}) = 20 +getlowerorder(::Serendipity{RefHexahedron, 2}) = Lagrange{RefHexahedron, 1}() + +facedof_indices(::Serendipity{RefHexahedron, 2}) = ( + (1, 4, 3, 2, 12, 11, 10, 9), + (1, 2, 6, 5, 9, 18, 13, 17), + (2, 3, 7, 6, 10, 19, 14, 18), + (3, 4, 8, 7, 11, 20, 15, 19), + (1, 5, 8, 4, 17, 16, 20, 12), + (5, 6, 7, 8, 13, 14, 15, 16), ) -edgedof_indices(::Serendipity{RefHexahedron,2}) = ( - (1,2, 9), - (2,3, 10), - (3,4, 11), - (4,1, 12), - (5,6, 13), - (6,7, 14), - (7,8, 15), - (8,5, 16), - (1,5, 17), - (2,6, 18), - (3,7, 19), - (4,8, 20), +edgedof_indices(::Serendipity{RefHexahedron, 2}) = ( + (1, 2, 9), + (2, 3, 10), + (3, 4, 11), + (4, 1, 12), + (5, 6, 13), + (6, 7, 14), + (7, 8, 15), + (8, 5, 16), + (1, 5, 17), + (2, 6, 18), + (3, 7, 19), + (4, 8, 20), ) -edgedof_interior_indices(::Serendipity{RefHexahedron,2}) = ( - (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,) +edgedof_interior_indices(::Serendipity{RefHexahedron, 2}) = ( + (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,), ) -function reference_coordinates(::Serendipity{RefHexahedron,2}) - return [Vec{3, Float64}((-1.0, -1.0, -1.0)), - Vec{3, Float64}(( 1.0, -1.0, -1.0)), - Vec{3, Float64}(( 1.0, 1.0, -1.0)), - Vec{3, Float64}((-1.0, 1.0, -1.0)), - Vec{3, Float64}((-1.0, -1.0, 1.0)), - Vec{3, Float64}(( 1.0, -1.0, 1.0)), - Vec{3, Float64}(( 1.0, 1.0, 1.0)), - Vec{3, Float64}((-1.0, 1.0, 1.0)), - Vec{3, Float64}((0.0, -1.0, -1.0)), - Vec{3, Float64}((1.0, 0.0, -1.0)), - Vec{3, Float64}((0.0, 1.0, -1.0)), - Vec{3, Float64}((-1.0, 0.0, -1.0)), - Vec{3, Float64}((0.0, -1.0, 1.0)), - Vec{3, Float64}((1.0, 0.0, 1.0)), - Vec{3, Float64}((0.0, 1.0, 1.0)), - Vec{3, Float64}((-1.0, 0.0, 1.0)), - Vec{3, Float64}((-1.0, -1.0, 0.0)), - Vec{3, Float64}((1.0, -1.0, 0.0)), - Vec{3, Float64}((1.0, 1.0, 0.0)), - Vec{3, Float64}((-1.0, 1.0, 0.0)),] +function reference_coordinates(::Serendipity{RefHexahedron, 2}) + return [ + Vec{3, Float64}((-1.0, -1.0, -1.0)), + Vec{3, Float64}((1.0, -1.0, -1.0)), + Vec{3, Float64}((1.0, 1.0, -1.0)), + Vec{3, Float64}((-1.0, 1.0, -1.0)), + Vec{3, Float64}((-1.0, -1.0, 1.0)), + Vec{3, Float64}((1.0, -1.0, 1.0)), + Vec{3, Float64}((1.0, 1.0, 1.0)), + Vec{3, Float64}((-1.0, 1.0, 1.0)), + Vec{3, Float64}((0.0, -1.0, -1.0)), + Vec{3, Float64}((1.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, 1.0, -1.0)), + Vec{3, Float64}((-1.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 1.0)), + Vec{3, Float64}((1.0, 0.0, 1.0)), + Vec{3, Float64}((0.0, 1.0, 1.0)), + Vec{3, Float64}((-1.0, 0.0, 1.0)), + Vec{3, Float64}((-1.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, 1.0, 0.0)), + Vec{3, Float64}((-1.0, 1.0, 0.0)), + ] end # Inlined to resolve the recursion properly @@ -1433,7 +1473,7 @@ end i == 6 && return (1 + ξ_x) * (1 - ξ_y) * (1 + ξ_z) / 8 - (reference_shape_value(ip, ξ, 13) + reference_shape_value(ip, ξ, 14) + reference_shape_value(ip, ξ, 18)) / 2 i == 7 && return (1 + ξ_x) * (1 + ξ_y) * (1 + ξ_z) / 8 - (reference_shape_value(ip, ξ, 14) + reference_shape_value(ip, ξ, 15) + reference_shape_value(ip, ξ, 19)) / 2 i == 8 && return (1 - ξ_x) * (1 + ξ_y) * (1 + ξ_z) / 8 - (reference_shape_value(ip, ξ, 15) + reference_shape_value(ip, ξ, 16) + reference_shape_value(ip, ξ, 20)) / 2 - i == 9 && return (1 - ξ_x^2) * (1 - ξ_y) * (1 - ξ_z) / 4 + i == 9 && return (1 - ξ_x^2) * (1 - ξ_y) * (1 - ξ_z) / 4 i == 10 && return (1 + ξ_x) * (1 - ξ_y^2) * (1 - ξ_z) / 4 i == 11 && return (1 - ξ_x^2) * (1 + ξ_y) * (1 - ξ_z) / 4 i == 12 && return (1 - ξ_x) * (1 - ξ_y^2) * (1 - ξ_z) / 4 @@ -1465,58 +1505,60 @@ struct CrouzeixRaviart{shape, order} <: ScalarInterpolation{shape, order} end # CR elements are characterized by not having vertex dofs -vertexdof_indices(ip::CrouzeixRaviart) = ntuple(i->(), nvertices(ip)) +vertexdof_indices(ip::CrouzeixRaviart) = ntuple(i -> (), nvertices(ip)) ################################################# # Non-conforming Crouzeix-Raviart dim 2 order 1 # ################################################# -getnbasefunctions(::CrouzeixRaviart{RefTriangle,1}) = 3 +getnbasefunctions(::CrouzeixRaviart{RefTriangle, 1}) = 3 adjust_dofs_during_distribution(::CrouzeixRaviart) = true adjust_dofs_during_distribution(::CrouzeixRaviart{<:Any, 1}) = false -edgedof_indices(::CrouzeixRaviart{RefTriangle,1}) = ((1,), (2,), (3,)) -edgedof_interior_indices(::CrouzeixRaviart{RefTriangle,1}) = ((1,), (2,), (3,)) -facedof_indices(ip::CrouzeixRaviart{RefTriangle,1}) = (ntuple(i->i, getnbasefunctions(ip)),) +edgedof_indices(::CrouzeixRaviart{RefTriangle, 1}) = ((1,), (2,), (3,)) +edgedof_interior_indices(::CrouzeixRaviart{RefTriangle, 1}) = ((1,), (2,), (3,)) +facedof_indices(ip::CrouzeixRaviart{RefTriangle, 1}) = (ntuple(i -> i, getnbasefunctions(ip)),) -function reference_coordinates(::CrouzeixRaviart{RefTriangle,1}) - return [Vec{2, Float64}((0.5, 0.5)), - Vec{2, Float64}((0.0, 0.5)), - Vec{2, Float64}((0.5, 0.0))] +function reference_coordinates(::CrouzeixRaviart{RefTriangle, 1}) + return [ + Vec{2, Float64}((0.5, 0.5)), + Vec{2, Float64}((0.0, 0.5)), + Vec{2, Float64}((0.5, 0.0)), + ] end function reference_shape_value(ip::CrouzeixRaviart{RefTriangle, 1}, ξ::Vec{2}, i::Int) ξ_x = ξ[1] ξ_y = ξ[2] - i == 1 && return 2*ξ_x + 2*ξ_y - 1 - i == 2 && return 1 - 2*ξ_x - i == 3 && return 1 - 2*ξ_y + i == 1 && return 2 * ξ_x + 2 * ξ_y - 1 + i == 2 && return 1 - 2 * ξ_x + i == 3 && return 1 - 2 * ξ_y throw(ArgumentError("no shape function $i for interpolation $ip")) end ################################################# # Non-conforming Crouzeix-Raviart dim 3 order 1 # ################################################# -getnbasefunctions(::CrouzeixRaviart{RefTetrahedron,1}) = 4 +getnbasefunctions(::CrouzeixRaviart{RefTetrahedron, 1}) = 4 -facedof_indices(::CrouzeixRaviart{RefTetrahedron,1}) = ((1,), (2,), (3,), (4,)) -facedof_interior_indices(::CrouzeixRaviart{RefTetrahedron,1}) = ((1,), (2,), (3,), (4,)) +facedof_indices(::CrouzeixRaviart{RefTetrahedron, 1}) = ((1,), (2,), (3,), (4,)) +facedof_interior_indices(::CrouzeixRaviart{RefTetrahedron, 1}) = ((1,), (2,), (3,), (4,)) -function reference_coordinates(::CrouzeixRaviart{RefTetrahedron,1}) +function reference_coordinates(::CrouzeixRaviart{RefTetrahedron, 1}) return [ - Vec{3, Float64}((1/3, 1/3, 0.0)), - Vec{3, Float64}((1/3, 0.0, 1/3)), - Vec{3, Float64}((1/3, 1/3, 1/3)), - Vec{3, Float64}((0.0, 1/3, 1/3)), - ] -end - -function reference_shape_value(ip::CrouzeixRaviart{RefTetrahedron,1}, ξ::Vec{3}, i::Int) - (x,y,z) = ξ - i == 1 && return 1 -3z - i == 2 && return 1 -3y - i == 3 && return 3x +3y +3z -2 - i == 4 && return 1 -3x + Vec{3, Float64}((1 / 3, 1 / 3, 0.0)), + Vec{3, Float64}((1 / 3, 0.0, 1 / 3)), + Vec{3, Float64}((1 / 3, 1 / 3, 1 / 3)), + Vec{3, Float64}((0.0, 1 / 3, 1 / 3)), + ] +end + +function reference_shape_value(ip::CrouzeixRaviart{RefTetrahedron, 1}, ξ::Vec{3}, i::Int) + (x, y, z) = ξ + i == 1 && return 1 - 3z + i == 2 && return 1 - 3y + i == 3 && return 3x + 3y + 3z - 2 + i == 4 && return 1 - 3x return throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1528,10 +1570,10 @@ Classical non-conforming Rannacher-Turek element. This element is basically the idea from Crouzeix and Raviart applied to hypercubes. For details see the original paper [RanTur:1992:snq](@cite). """ -struct RannacherTurek{shape,order} <: ScalarInterpolation{shape,order} end +struct RannacherTurek{shape, order} <: ScalarInterpolation{shape, order} end # CR-type elements are characterized by not having vertex dofs -vertexdof_indices(ip::RannacherTurek) = ntuple(i->(), nvertices(ip)) +vertexdof_indices(ip::RannacherTurek) = ntuple(i -> (), nvertices(ip)) adjust_dofs_during_distribution(::RannacherTurek) = true adjust_dofs_during_distribution(::RannacherTurek{<:Any, 1}) = false @@ -1539,57 +1581,61 @@ adjust_dofs_during_distribution(::RannacherTurek{<:Any, 1}) = false ################################# # Rannacher-Turek dim 2 order 1 # ################################# -getnbasefunctions(::RannacherTurek{RefQuadrilateral,1}) = 4 +getnbasefunctions(::RannacherTurek{RefQuadrilateral, 1}) = 4 -edgedof_indices(::RannacherTurek{RefQuadrilateral,1}) = ((1,), (2,), (3,), (4,)) -edgedof_interior_indices(::RannacherTurek{RefQuadrilateral,1}) = ((1,), (2,), (3,), (4,)) -facedof_indices(ip::RannacherTurek{RefQuadrilateral,1}) = (ntuple(i->i, getnbasefunctions(ip)),) +edgedof_indices(::RannacherTurek{RefQuadrilateral, 1}) = ((1,), (2,), (3,), (4,)) +edgedof_interior_indices(::RannacherTurek{RefQuadrilateral, 1}) = ((1,), (2,), (3,), (4,)) +facedof_indices(ip::RannacherTurek{RefQuadrilateral, 1}) = (ntuple(i -> i, getnbasefunctions(ip)),) -function reference_coordinates(::RannacherTurek{RefQuadrilateral,1}) - return [Vec{2, Float64}(( 0.0, -1.0)), - Vec{2, Float64}(( 1.0, 0.0)), - Vec{2, Float64}(( 0.0, 1.0)), - Vec{2, Float64}((-1.0, 0.0))] +function reference_coordinates(::RannacherTurek{RefQuadrilateral, 1}) + return [ + Vec{2, Float64}((0.0, -1.0)), + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((-1.0, 0.0)), + ] end -function reference_shape_value(ip::RannacherTurek{RefQuadrilateral,1}, ξ::Vec{2,T}, i::Int) where T - (x,y) = ξ +function reference_shape_value(ip::RannacherTurek{RefQuadrilateral, 1}, ξ::Vec{2, T}, i::Int) where {T} + (x, y) = ξ - i == 1 && return -(x+1)^2/4 +(y+1)^2/4 +(x+1)/2 -(y+1) +T(3)/4 - i == 2 && return (x+1)^2/4 -(y+1)^2/4 +(y+1)/2 -T(1)/4 - i == 3 && return -(x+1)^2/4 +(y+1)^2/4 +(x+1)/2 -T(1)/4 - i == 4 && return (x+1)^2/4 -(y+1)^2/4 -(x+1) +(y+1)/2 +T(3)/4 + i == 1 && return -(x + 1)^2 / 4 + (y + 1)^2 / 4 + (x + 1) / 2 - (y + 1) + T(3) / 4 + i == 2 && return (x + 1)^2 / 4 - (y + 1)^2 / 4 + (y + 1) / 2 - T(1) / 4 + i == 3 && return -(x + 1)^2 / 4 + (y + 1)^2 / 4 + (x + 1) / 2 - T(1) / 4 + i == 4 && return (x + 1)^2 / 4 - (y + 1)^2 / 4 - (x + 1) + (y + 1) / 2 + T(3) / 4 throw(ArgumentError("no shape function $i for interpolation $ip")) end ################################# # Rannacher-Turek dim 3 order 1 # ################################# -getnbasefunctions(::RannacherTurek{RefHexahedron,1}) = 6 +getnbasefunctions(::RannacherTurek{RefHexahedron, 1}) = 6 -edgedof_indices(ip::RannacherTurek{RefHexahedron,1}) = ntuple(i->(), nedges(ip)) -edgedof_interior_indices(ip::RannacherTurek{RefHexahedron,1}) = ntuple(i->(), nedges(ip)) -facedof_indices(::RannacherTurek{RefHexahedron,1}) = ((1,), (2,), (3,), (4,), (5,), (6,)) -facedof_interior_indices(::RannacherTurek{RefHexahedron,1}) = ((1,), (2,), (3,), (4,), (5,), (6,)) +edgedof_indices(ip::RannacherTurek{RefHexahedron, 1}) = ntuple(i -> (), nedges(ip)) +edgedof_interior_indices(ip::RannacherTurek{RefHexahedron, 1}) = ntuple(i -> (), nedges(ip)) +facedof_indices(::RannacherTurek{RefHexahedron, 1}) = ((1,), (2,), (3,), (4,), (5,), (6,)) +facedof_interior_indices(::RannacherTurek{RefHexahedron, 1}) = ((1,), (2,), (3,), (4,), (5,), (6,)) -function reference_coordinates(::RannacherTurek{RefHexahedron,1}) - return [Vec{3, Float64}(( 0.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, -1.0, 0.0)), - Vec{3, Float64}(( 1.0, 0.0, 0.0)), - Vec{3, Float64}(( 0.0, 1.0, 0.0)), - Vec{3, Float64}((-1.0, 0.0, 0.0)), - Vec{3, Float64}(( 0.0, 0.0, 1.0)),] +function reference_coordinates(::RannacherTurek{RefHexahedron, 1}) + return [ + Vec{3, Float64}((0.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((-1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + ] end -function reference_shape_value(ip::RannacherTurek{RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where T - (x,y,z) = ξ +function reference_shape_value(ip::RannacherTurek{RefHexahedron, 1}, ξ::Vec{3, T}, i::Int) where {T} + (x, y, z) = ξ - i == 1 && return -2((x+1))^2/12 +1(x+1)/3 -2((y+1))^2/12 +1(y+1)/3 +4((z+1))^2/12 -7(z+1)/6 + T(2)/3 - i == 2 && return -2((x+1))^2/12 +1(x+1)/3 +4((y+1))^2/12 -7(y+1)/6 -2((z+1))^2/12 +1(z+1)/3 + T(2)/3 - i == 3 && return 4((x+1))^2/12 -1(x+1)/6 -2((y+1))^2/12 +1(y+1)/3 -2((z+1))^2/12 +1(z+1)/3 - T(1)/3 - i == 4 && return -2((x+1))^2/12 +1(x+1)/3 +4((y+1))^2/12 -1(y+1)/6 -2((z+1))^2/12 +1(z+1)/3 - T(1)/3 - i == 5 && return 4((x+1))^2/12 -7(x+1)/6 -2((y+1))^2/12 +1(y+1)/3 -2((z+1))^2/12 +1(z+1)/3 + T(2)/3 - i == 6 && return -2((x+1))^2/12 +1(x+1)/3 -2((y+1))^2/12 +1(y+1)/3 +4((z+1))^2/12 -1(z+1)/6 - T(1)/3 + i == 1 && return -2((x + 1))^2 / 12 + 1(x + 1) / 3 - 2((y + 1))^2 / 12 + 1(y + 1) / 3 + 4((z + 1))^2 / 12 - 7(z + 1) / 6 + T(2) / 3 + i == 2 && return -2((x + 1))^2 / 12 + 1(x + 1) / 3 + 4((y + 1))^2 / 12 - 7(y + 1) / 6 - 2((z + 1))^2 / 12 + 1(z + 1) / 3 + T(2) / 3 + i == 3 && return 4((x + 1))^2 / 12 - 1(x + 1) / 6 - 2((y + 1))^2 / 12 + 1(y + 1) / 3 - 2((z + 1))^2 / 12 + 1(z + 1) / 3 - T(1) / 3 + i == 4 && return -2((x + 1))^2 / 12 + 1(x + 1) / 3 + 4((y + 1))^2 / 12 - 1(y + 1) / 6 - 2((z + 1))^2 / 12 + 1(z + 1) / 3 - T(1) / 3 + i == 5 && return 4((x + 1))^2 / 12 - 7(x + 1) / 6 - 2((y + 1))^2 / 12 + 1(y + 1) / 3 - 2((z + 1))^2 / 12 + 1(z + 1) / 3 + T(2) / 3 + i == 6 && return -2((x + 1))^2 / 12 + 1(x + 1) / 3 - 2((y + 1))^2 / 12 + 1(y + 1) / 3 + 4((z + 1))^2 / 12 - 1(z + 1) / 6 - T(1) / 3 throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1597,7 +1643,7 @@ end ################################################## # VectorizedInterpolation{<:ScalarInterpolation} # ################################################## -struct VectorizedInterpolation{vdim, refshape, order, SI <: ScalarInterpolation{refshape, order}} <: VectorInterpolation{vdim, refshape,order} +struct VectorizedInterpolation{vdim, refshape, order, SI <: ScalarInterpolation{refshape, order}} <: VectorInterpolation{vdim, refshape, order} ip::SI function VectorizedInterpolation{vdim}(ip::SI) where {vdim, refshape, order, SI <: ScalarInterpolation{refshape, order}} return new{vdim, refshape, order, SI}(ip) @@ -1612,17 +1658,18 @@ function VectorizedInterpolation(ip::ScalarInterpolation{shape}) where {refdim, end Base.:(^)(ip::ScalarInterpolation, vdim::Int) = VectorizedInterpolation{vdim}(ip) -function Base.literal_pow(::typeof(^), ip::ScalarInterpolation, ::Val{vdim}) where vdim +function Base.literal_pow(::typeof(^), ip::ScalarInterpolation, ::Val{vdim}) where {vdim} return VectorizedInterpolation{vdim}(ip) end -function Base.show(io::IO, mime::MIME"text/plain", ip::VectorizedInterpolation{vdim}) where vdim +function Base.show(io::IO, mime::MIME"text/plain", ip::VectorizedInterpolation{vdim}) where {vdim} show(io, mime, ip.ip) print(io, "^", vdim) + return end # Helper to get number of copies for DoF distribution -get_n_copies(::VectorizedInterpolation{vdim}) where vdim = vdim +get_n_copies(::VectorizedInterpolation{vdim}) where {vdim} = vdim InterpolationInfo(ip::VectorizedInterpolation) = InterpolationInfo(ip.ip, get_n_copies(ip)) # Error when trying to get dof indicies from vectorized interpolations. @@ -1640,7 +1687,7 @@ volumedof_interior_indices(::VectorizedInterpolation) = _entitydof_indices_vecto get_base_interpolation(ip::Interpolation) = ip get_base_interpolation(ip::VectorizedInterpolation) = ip.ip -function getnbasefunctions(ipv::VectorizedInterpolation{vdim}) where vdim +function getnbasefunctions(ipv::VectorizedInterpolation{vdim}) where {vdim} return vdim * getnbasefunctions(ipv.ip) end function reference_shape_value(ipv::VectorizedInterpolation{vdim, shape}, ξ::Vec{refdim, T}, I::Int) where {vdim, refdim, shape <: AbstractRefShape{refdim}, T} @@ -1670,12 +1717,12 @@ function reference_shape_hessian_gradient_and_value(ipv::VectorizedInterpolation end # vdim != refdim function reference_shape_hessian_gradient_and_value(ipv::VectorizedInterpolation{vdim, shape}, ξ::V, I::Int) where {vdim, refdim, shape <: AbstractRefShape{refdim}, T, V <: Vec{refdim, T}} - _reference_shape_hessian_gradient_and_value_static_array(ipv, ξ, I) + return _reference_shape_hessian_gradient_and_value_static_array(ipv, ξ, I) end function _reference_shape_hessian_gradient_and_value_static_array(ipv::VectorizedInterpolation{vdim, shape}, ξ::V, I::Int) where {vdim, refdim, shape <: AbstractRefShape{refdim}, T, V <: Vec{refdim, T}} # Load with dual numbers and compute the value f = x -> reference_shape_value(ipv, x, I) - ξd = Tensors._load(Tensors._load(ξ, ForwardDiff.Tag(f, V)), ForwardDiff.Tag(f, V)) + ξd = Tensors._load(Tensors._load(ξ, ForwardDiff.Tag(f, V)), ForwardDiff.Tag(f, V)) value_hess = f(ξd) # Extract the value and gradient val = Vec{vdim, T}(i -> ForwardDiff.value(ForwardDiff.value(value_hess[i]))) diff --git a/src/iterators.jl b/src/iterators.jl index b97888ae4f..0abf2972b1 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -6,7 +6,7 @@ struct UpdateFlags dofs::Bool end -UpdateFlags(; nodes::Bool=true, coords::Bool=true, dofs::Bool=true) = +UpdateFlags(; nodes::Bool = true, coords::Bool = true, dofs::Bool = true) = UpdateFlags(nodes, coords, dofs) @@ -32,7 +32,7 @@ cell. The cache is updated for a new cell by calling `reinit!(cache, cellid)` wh See also [`CellIterator`](@ref). """ -mutable struct CellCache{X,G<:AbstractGrid,DH<:Union{AbstractDofHandler,Nothing}} +mutable struct CellCache{X, G <: AbstractGrid, DH <: Union{AbstractDofHandler, Nothing}} const flags::UpdateFlags const grid::G # Pretty useless to store this since you have it already for the reinit! call, but @@ -45,14 +45,14 @@ mutable struct CellCache{X,G<:AbstractGrid,DH<:Union{AbstractDofHandler,Nothing} const dofs::Vector{Int} end -function CellCache(grid::Grid{dim,C,T}, flags::UpdateFlags=UpdateFlags()) where {dim,C,T} +function CellCache(grid::Grid{dim, C, T}, flags::UpdateFlags = UpdateFlags()) where {dim, C, T} N = nnodes_per_cell(grid, 1) # nodes and coords will be resized in `reinit!` nodes = zeros(Int, N) - coords = zeros(Vec{dim,T}, N) + coords = zeros(Vec{dim, T}, N) return CellCache(flags, grid, -1, nodes, coords, nothing, Int[]) end -function CellCache(dh::DofHandler{dim}, flags::UpdateFlags=UpdateFlags()) where {dim} +function CellCache(dh::DofHandler{dim}, flags::UpdateFlags = UpdateFlags()) where {dim} n = ndofs_per_cell(dh.subdofhandlers[1]) # dofs and coords will be resized in `reinit!` N = nnodes_per_cell(get_grid(dh), 1) nodes = zeros(Int, N) @@ -61,9 +61,9 @@ function CellCache(dh::DofHandler{dim}, flags::UpdateFlags=UpdateFlags()) where return CellCache(flags, get_grid(dh), -1, nodes, coords, dh, celldofs) end -function CellCache(sdh::SubDofHandler, flags::UpdateFlags=UpdateFlags()) +function CellCache(sdh::SubDofHandler, flags::UpdateFlags = UpdateFlags()) Tv = get_coordinate_type(sdh.dh.grid) - CellCache(flags, sdh.dh.grid, -1, Int[], Tv[], sdh, Int[]) + return CellCache(flags, sdh.dh.grid, -1, Int[], Tv[], sdh, Int[]) end function reinit!(cc::CellCache, i::Int) @@ -115,14 +115,14 @@ calling `reinit!(cache, fi::FacetIndex)`. See also [`FacetIterator`](@ref). """ -mutable struct FacetCache{CC<:CellCache} +mutable struct FacetCache{CC <: CellCache} const cc::CC # const for julia > 1.8 const dofs::Vector{Int} # aliasing cc.dofs current_facet_id::Int end function FacetCache(args...) cc = CellCache(args...) - FacetCache(cc, cc.dofs, 0) + return FacetCache(cc, cc.dofs, 0) end function reinit!(fc::FacetCache, facet::BoundaryIndex) @@ -133,7 +133,7 @@ function reinit!(fc::FacetCache, facet::BoundaryIndex) end # Delegate methods to the cell cache -for op = (:getnodes, :getcoordinates, :cellid, :celldofs) +for op in (:getnodes, :getcoordinates, :cellid, :celldofs) @eval begin function $op(fc::FacetCache, args...) return $op(fc.cc, args...) @@ -142,7 +142,7 @@ for op = (:getnodes, :getcoordinates, :cellid, :celldofs) end @inline function reinit!(fv::FacetValues, fc::FacetCache) - reinit!(fv, fc.cc, fc.current_facet_id) + return reinit!(fv, fc.cc, fc.current_facet_id) end """ @@ -164,7 +164,7 @@ interface. The cache is updated for a new cell by calling `reinit!(cache, facet_ See also [`InterfaceIterator`](@ref). """ -struct InterfaceCache{FC<:FacetCache} +struct InterfaceCache{FC <: FacetCache} a::FC b::FC dofs::Vector{Int} @@ -190,14 +190,15 @@ function reinit!(cache::InterfaceCache, facet_a::BoundaryIndex, facet_b::Boundar end function reinit!(iv::InterfaceValues, ic::InterfaceCache) - return reinit!(iv, + return reinit!( + iv, getcells(ic.a.cc.grid, cellid(ic.a)), getcoordinates(ic.a), ic.a.current_facet_id[], getcells(ic.b.cc.grid, cellid(ic.b)), getcoordinates(ic.b), ic.b.current_facet_id[], - ) + ) end interfacedofs(ic::InterfaceCache) = ic.dofs @@ -235,14 +236,16 @@ end `CellIterator` is stateful and should not be used for things other than `for`-looping (e.g. broadcasting over, or collecting the iterator may yield unexpected results). """ -struct CellIterator{CC<:CellCache, IC<:IntegerCollection} +struct CellIterator{CC <: CellCache, IC <: IntegerCollection} cc::CC set::IC end -function CellIterator(gridordh::Union{Grid,DofHandler}, - set::Union{IntegerCollection,Nothing}=nothing, - flags::UpdateFlags=UpdateFlags()) +function CellIterator( + gridordh::Union{Grid, DofHandler}, + set::Union{IntegerCollection, Nothing} = nothing, + flags::UpdateFlags = UpdateFlags() + ) if set === nothing grid = gridordh isa DofHandler ? get_grid(gridordh) : gridordh set = 1:getncells(grid) @@ -254,11 +257,11 @@ function CellIterator(gridordh::Union{Grid,DofHandler}, end return CellIterator(CellCache(gridordh, flags), set) end -function CellIterator(gridordh::Union{Grid,DofHandler}, flags::UpdateFlags) +function CellIterator(gridordh::Union{Grid, DofHandler}, flags::UpdateFlags) return CellIterator(gridordh, nothing, flags) end -function CellIterator(sdh::SubDofHandler, flags::UpdateFlags=UpdateFlags()) - CellIterator(CellCache(sdh, flags), sdh.cellset) +function CellIterator(sdh::SubDofHandler, flags::UpdateFlags = UpdateFlags()) + return CellIterator(CellCache(sdh, flags), sdh.cellset) end @inline _getset(ci::CellIterator) = ci.set @@ -290,13 +293,15 @@ for faceindex in facetset # ... end """ -struct FacetIterator{FC<:FacetCache} +struct FacetIterator{FC <: FacetCache} fc::FC set::OrderedSet{FacetIndex} end -function FacetIterator(gridordh::Union{Grid,AbstractDofHandler}, - set::AbstractVecOrSet{FacetIndex}, flags::UpdateFlags=UpdateFlags()) +function FacetIterator( + gridordh::Union{Grid, AbstractDofHandler}, + set::AbstractVecOrSet{FacetIndex}, flags::UpdateFlags = UpdateFlags() + ) if gridordh isa DofHandler # Keep here to maintain same settings as for CellIterator _check_same_celltype(get_grid(gridordh), set) @@ -342,14 +347,16 @@ struct InterfaceIterator{IC <: InterfaceCache, G <: Grid} topology::ExclusiveTopology end -function InterfaceIterator(gridordh::Union{Grid,AbstractDofHandler}, - topology::ExclusiveTopology = ExclusiveTopology(gridordh isa Grid ? gridordh : get_grid(gridordh))) +function InterfaceIterator( + gridordh::Union{Grid, AbstractDofHandler}, + topology::ExclusiveTopology = ExclusiveTopology(gridordh isa Grid ? gridordh : get_grid(gridordh)) + ) grid = gridordh isa Grid ? gridordh : get_grid(gridordh) return InterfaceIterator(InterfaceCache(gridordh), grid, topology) end # Iterator interface -function Base.iterate(ii::InterfaceIterator{<:Any, <:Grid{sdim}}, state...) where sdim +function Base.iterate(ii::InterfaceIterator{<:Any, <:Grid{sdim}}, state...) where {sdim} neighborhood = get_facet_facet_neighborhood(ii.topology, ii.grid) # TODO: This could be moved to InterfaceIterator constructor (potentially type-instable for non-union or mixed grids) while true it = iterate(facetskeleton(ii.topology, ii.grid), state...) @@ -364,6 +371,7 @@ function Base.iterate(ii::InterfaceIterator{<:Any, <:Grid{sdim}}, state...) wher reinit!(ii.cache, facet_a, facet_b) return (ii.cache, state) end + return end @@ -378,9 +386,9 @@ function Base.iterate(iterator::GridIterators, state_in...) reinit!(cache, item) return (cache, state_out) end -Base.IteratorSize(::Type{<:GridIterators{C}}) where C = Base.IteratorSize(C) +Base.IteratorSize(::Type{<:GridIterators{C}}) where {C} = Base.IteratorSize(C) Base.IteratorEltype(::Type{<:GridIterators}) = Base.HasEltype() -Base.eltype(::Type{<:GridIterators{C}}) where C = C +Base.eltype(::Type{<:GridIterators{C}}) where {C} = C Base.length(iterator::GridIterators) = length(_getset(iterator)) function _check_same_celltype(grid::AbstractGrid, cellset::IntegerCollection) @@ -389,6 +397,7 @@ function _check_same_celltype(grid::AbstractGrid, cellset::IntegerCollection) if !all(getcelltype(grid, i) == celltype for i in cellset) error("The cells in the cellset are not all of the same celltype.") end + return end function _check_same_celltype(grid::AbstractGrid, facetset::AbstractVecOrSet{<:BoundaryIndex}) @@ -397,4 +406,5 @@ function _check_same_celltype(grid::AbstractGrid, facetset::AbstractVecOrSet{<:B if !all(getcelltype(grid, facet[1]) == celltype for facet in facetset) error("The cells in the set (set of $(eltype(facetset))) are not all of the same celltype.") end + return end diff --git a/src/utils.jl b/src/utils.jl index 84c0f5a908..a53ffe89bb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -19,6 +19,7 @@ function debug_mode(; enable = true) Preferences.@set_preferences!("use_debug" => enable) @info "Debug mode $(enable ? "en" : "dis")abled. Restart the Julia session for this change to take effect!" end + return end @static if DEBUG @@ -28,17 +29,17 @@ end end end else - @eval begin + @eval begin macro debug(ex) return nothing end end end -convert_to_orderedset(set::AbstractVector{T}) where T = OrderedSet{T}(set) -convert_to_orderedset(set::AbstractSet{T}) where T = convert(OrderedSet{T}, set) +convert_to_orderedset(set::AbstractVector{T}) where {T} = OrderedSet{T}(set) +convert_to_orderedset(set::AbstractSet{T}) where {T} = convert(OrderedSet{T}, set) -function convert_to_orderedsets(namedsets::Dict{String, <: AbstractVecOrSet{T}}) where T - return Dict{String,OrderedSet{T}}(k => convert_to_orderedset(v) for (k,v) in namedsets) +function convert_to_orderedsets(namedsets::Dict{String, <:AbstractVecOrSet{T}}) where {T} + return Dict{String, OrderedSet{T}}(k => convert_to_orderedset(v) for (k, v) in namedsets) end -convert_to_orderedsets(namedsets::Dict{String, <: OrderedSet}) = namedsets +convert_to_orderedsets(namedsets::Dict{String, <:OrderedSet}) = namedsets diff --git a/test/blockarrays.jl b/test/blockarrays.jl index 81b1f627c8..6eb0502865 100644 --- a/test/blockarrays.jl +++ b/test/blockarrays.jl @@ -4,7 +4,7 @@ using Ferrite, BlockArrays, SparseArrays, Test grid = generate_grid(Triangle, (10, 10)) dh = DofHandler(grid) - ip = Lagrange{RefTriangle,1}() + ip = Lagrange{RefTriangle, 1}() add!(dh, :u, ip^2) add!(dh, :p, ip) close!(dh) diff --git a/test/integration/test_simple_scalar_convergence.jl b/test/integration/test_simple_scalar_convergence.jl index 4f7543ebc4..f08006d768 100644 --- a/test/integration/test_simple_scalar_convergence.jl +++ b/test/integration/test_simple_scalar_convergence.jl @@ -3,230 +3,230 @@ import Ferrite: getrefdim, geometric_interpolation module ConvergenceTestHelper -using Ferrite, SparseArrays, ForwardDiff, Test -import LinearAlgebra: diag - -get_geometry(::Ferrite.Interpolation{RefLine}) = Line -get_geometry(::Ferrite.Interpolation{RefQuadrilateral}) = Quadrilateral -get_geometry(::Ferrite.Interpolation{RefTriangle}) = Triangle -get_geometry(::Ferrite.Interpolation{RefPrism}) = Wedge -get_geometry(::Ferrite.Interpolation{RefHexahedron}) = Hexahedron -get_geometry(::Ferrite.Interpolation{RefTetrahedron}) = Tetrahedron -get_geometry(::Ferrite.Interpolation{RefPyramid}) = Pyramid - -get_quadrature_order(::Lagrange{shape, order}) where {shape, order} = max(2*order-1,2) -get_quadrature_order(::Lagrange{RefTriangle, 5}) where {shape, order} = 8 -get_quadrature_order(::Lagrange{RefPrism, order}) where order = 2*order # Don't know why -get_quadrature_order(::Serendipity{shape, order}) where {shape, order} = max(2*order-1,2) -get_quadrature_order(::CrouzeixRaviart{shape, order}) where {shape, order} = max(2*order-1,2) -get_quadrature_order(::RannacherTurek{shape, order}) where {shape, order} = max(2*order-1,2) -get_quadrature_order(::BubbleEnrichedLagrange{shape, order}) where {shape, order} = max(2*order-1,2) - -get_num_elements(::Ferrite.Interpolation{shape, 1}) where {shape} = 21 -get_num_elements(::Ferrite.Interpolation{shape, 2}) where {shape} = 7 -get_num_elements(::Ferrite.Interpolation{RefHexahedron, 1}) = 11 -get_num_elements(::Ferrite.RannacherTurek{RefQuadrilateral, 1}) = 15 -get_num_elements(::Ferrite.RannacherTurek{RefHexahedron, 1}) = 13 -get_num_elements(::Ferrite.Interpolation{RefHexahedron, 2}) = 4 -get_num_elements(::Ferrite.Interpolation{shape, 3}) where {shape} = 8 -get_num_elements(::Ferrite.Interpolation{shape, 4}) where {shape} = 5 -get_num_elements(::Ferrite.Interpolation{shape, 5}) where {shape} = 3 - -get_test_tolerance(ip) = 1e-2 -get_test_tolerance(ip::RannacherTurek) = 4e-2 -get_test_tolerance(ip::CrouzeixRaviart) = 4e-2 - -analytical_solution(x) = prod(cos, x*π/2) -analytical_rhs(x) = -Tensors.laplace(analytical_solution,x) - -# Standard assembly copy pasta for Poisson problem -function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues, coords) - n_basefuncs = getnbasefunctions(cellvalues) - ## Reset to 0 - fill!(Ke, 0) - fill!(fe, 0) - ## Loop over quadrature points - for q_point in 1:getnquadpoints(cellvalues) - ## Get the quadrature weight - dΩ = getdetJdV(cellvalues, q_point) - x = spatial_coordinate(cellvalues, q_point, coords) - ## Loop over test shape functions - for i in 1:n_basefuncs - δu = shape_value(cellvalues, q_point, i) - ∇δu = shape_gradient(cellvalues, q_point, i) - ## Add contribution to fe - fe[i] += analytical_rhs(x) * δu * dΩ - ## Loop over trial shape functions - for j in 1:n_basefuncs - ∇u = shape_gradient(cellvalues, q_point, j) - ## Add contribution to Ke - Ke[i, j] += (∇δu ⋅ ∇u) * dΩ - end - end - end - return Ke, fe -end - -# Standard assembly copy pasta for Poisson problem -function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) - ## Allocate the element stiffness matrix and element force vector - n_basefuncs = getnbasefunctions(cellvalues) - Ke = zeros(n_basefuncs, n_basefuncs) - fe = zeros(n_basefuncs) - ## Allocate global force vector f - f = zeros(ndofs(dh)) - ## Create an assembler - assembler = start_assemble(K, f) - ## Loop over all cels - for cell in CellIterator(dh) - ## Reinitialize cellvalues for this cell - reinit!(cellvalues, cell) - coords = getcoordinates(cell) - ## Compute element contribution - assemble_element!(Ke, fe, cellvalues, coords) - ## Assemble Ke and fe into K and f - assemble!(assembler, celldofs(cell), Ke, fe) - end - return K, f -end - -# Compute norms -function check_and_compute_convergence_norms(dh, u, cellvalues, testatol) - L2norm = 0.0 - ∇L2norm = 0.0 - L∞norm = 0.0 - for cell in CellIterator(dh) - reinit!(cellvalues, cell) + using Ferrite, SparseArrays, ForwardDiff, Test + import LinearAlgebra: diag + + get_geometry(::Ferrite.Interpolation{RefLine}) = Line + get_geometry(::Ferrite.Interpolation{RefQuadrilateral}) = Quadrilateral + get_geometry(::Ferrite.Interpolation{RefTriangle}) = Triangle + get_geometry(::Ferrite.Interpolation{RefPrism}) = Wedge + get_geometry(::Ferrite.Interpolation{RefHexahedron}) = Hexahedron + get_geometry(::Ferrite.Interpolation{RefTetrahedron}) = Tetrahedron + get_geometry(::Ferrite.Interpolation{RefPyramid}) = Pyramid + + get_quadrature_order(::Lagrange{shape, order}) where {shape, order} = max(2 * order - 1, 2) + get_quadrature_order(::Lagrange{RefTriangle, 5}) where {shape, order} = 8 + get_quadrature_order(::Lagrange{RefPrism, order}) where {order} = 2 * order # Don't know why + get_quadrature_order(::Serendipity{shape, order}) where {shape, order} = max(2 * order - 1, 2) + get_quadrature_order(::CrouzeixRaviart{shape, order}) where {shape, order} = max(2 * order - 1, 2) + get_quadrature_order(::RannacherTurek{shape, order}) where {shape, order} = max(2 * order - 1, 2) + get_quadrature_order(::BubbleEnrichedLagrange{shape, order}) where {shape, order} = max(2 * order - 1, 2) + + get_num_elements(::Ferrite.Interpolation{shape, 1}) where {shape} = 21 + get_num_elements(::Ferrite.Interpolation{shape, 2}) where {shape} = 7 + get_num_elements(::Ferrite.Interpolation{RefHexahedron, 1}) = 11 + get_num_elements(::Ferrite.RannacherTurek{RefQuadrilateral, 1}) = 15 + get_num_elements(::Ferrite.RannacherTurek{RefHexahedron, 1}) = 13 + get_num_elements(::Ferrite.Interpolation{RefHexahedron, 2}) = 4 + get_num_elements(::Ferrite.Interpolation{shape, 3}) where {shape} = 8 + get_num_elements(::Ferrite.Interpolation{shape, 4}) where {shape} = 5 + get_num_elements(::Ferrite.Interpolation{shape, 5}) where {shape} = 3 + + get_test_tolerance(ip) = 1.0e-2 + get_test_tolerance(ip::RannacherTurek) = 4.0e-2 + get_test_tolerance(ip::CrouzeixRaviart) = 4.0e-2 + + analytical_solution(x) = prod(cos, x * π / 2) + analytical_rhs(x) = -Tensors.laplace(analytical_solution, x) + + # Standard assembly copy pasta for Poisson problem + function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues, coords) n_basefuncs = getnbasefunctions(cellvalues) - coords = getcoordinates(cell) - uₑ = u[celldofs(cell)] + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Loop over quadrature points for q_point in 1:getnquadpoints(cellvalues) + ## Get the quadrature weight dΩ = getdetJdV(cellvalues, q_point) x = spatial_coordinate(cellvalues, q_point, coords) - uₐₙₐ = prod(cos, x*π/2) - uₐₚₚᵣₒₓ = function_value(cellvalues, q_point, uₑ) - L∞norm = max(L∞norm, norm(uₐₙₐ-uₐₚₚᵣₒₓ)) - L2norm += norm(uₐₙₐ-uₐₚₚᵣₒₓ)^2*dΩ + ## Loop over test shape functions + for i in 1:n_basefuncs + δu = shape_value(cellvalues, q_point, i) + ∇δu = shape_gradient(cellvalues, q_point, i) + ## Add contribution to fe + fe[i] += analytical_rhs(x) * δu * dΩ + ## Loop over trial shape functions + for j in 1:n_basefuncs + ∇u = shape_gradient(cellvalues, q_point, j) + ## Add contribution to Ke + Ke[i, j] += (∇δu ⋅ ∇u) * dΩ + end + end + end + return Ke, fe + end - ∇uₐₙₐ = gradient(x-> prod(cos, x*π/2), x) - ∇uₐₚₚᵣₒₓ = function_gradient(cellvalues, q_point, uₑ) - ∇L2norm += norm(∇uₐₙₐ-∇uₐₚₚᵣₒₓ)^2*dΩ + # Standard assembly copy pasta for Poisson problem + function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) + ## Allocate the element stiffness matrix and element force vector + n_basefuncs = getnbasefunctions(cellvalues) + Ke = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) + ## Allocate global force vector f + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + ## Loop over all cels + for cell in CellIterator(dh) + ## Reinitialize cellvalues for this cell + reinit!(cellvalues, cell) + coords = getcoordinates(cell) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, coords) + ## Assemble Ke and fe into K and f + assemble!(assembler, celldofs(cell), Ke, fe) + end + return K, f + end - # Pointwise convergence - @test uₐₙₐ ≈ uₐₚₚᵣₒₓ atol=testatol + # Compute norms + function check_and_compute_convergence_norms(dh, u, cellvalues, testatol) + L2norm = 0.0 + ∇L2norm = 0.0 + L∞norm = 0.0 + for cell in CellIterator(dh) + reinit!(cellvalues, cell) + n_basefuncs = getnbasefunctions(cellvalues) + coords = getcoordinates(cell) + uₑ = u[celldofs(cell)] + for q_point in 1:getnquadpoints(cellvalues) + dΩ = getdetJdV(cellvalues, q_point) + x = spatial_coordinate(cellvalues, q_point, coords) + uₐₙₐ = prod(cos, x * π / 2) + uₐₚₚᵣₒₓ = function_value(cellvalues, q_point, uₑ) + L∞norm = max(L∞norm, norm(uₐₙₐ - uₐₚₚᵣₒₓ)) + L2norm += norm(uₐₙₐ - uₐₚₚᵣₒₓ)^2 * dΩ + + ∇uₐₙₐ = gradient(x -> prod(cos, x * π / 2), x) + ∇uₐₚₚᵣₒₓ = function_gradient(cellvalues, q_point, uₑ) + ∇L2norm += norm(∇uₐₙₐ - ∇uₐₚₚᵣₒₓ)^2 * dΩ + + # Pointwise convergence + @test uₐₙₐ ≈ uₐₚₚᵣₒₓ atol = testatol + end end + return √(L2norm), √(∇L2norm), L∞norm end - √(L2norm), √(∇L2norm), L∞norm -end -# Assemble and solve -function solve(dh, ch, cellvalues) - K, f = assemble_global(cellvalues, allocate_matrix(dh), dh); - apply!(K, f, ch) - u = K \ f; -end + # Assemble and solve + function solve(dh, ch, cellvalues) + K, f = assemble_global(cellvalues, allocate_matrix(dh), dh) + apply!(K, f, ch) + return K \ f + end -function setup_poisson_problem(grid, interpolation, interpolation_geo, qr) - # Construct Ferrite stuff - dh = DofHandler(grid) - add!(dh, :u, interpolation) - close!(dh); + function setup_poisson_problem(grid, interpolation, interpolation_geo, qr) + # Construct Ferrite stuff + dh = DofHandler(grid) + add!(dh, :u, interpolation) + close!(dh) - ch = ConstraintHandler(dh); - ∂Ω = union( - values(Ferrite.getfacetsets(grid))... - ); - dbc = Dirichlet(:u, ∂Ω, (x, t) -> analytical_solution(x)) - add!(ch, dbc); - close!(ch); + ch = ConstraintHandler(dh) + ∂Ω = union( + values(Ferrite.getfacetsets(grid))... + ) + dbc = Dirichlet(:u, ∂Ω, (x, t) -> analytical_solution(x)) + add!(ch, dbc) + close!(ch) - cellvalues = CellValues(qr, interpolation, interpolation_geo); + cellvalues = CellValues(qr, interpolation, interpolation_geo) - return dh, ch, cellvalues -end + return dh, ch, cellvalues + end end # module ConvergenceTestHelper # These test only for convergence within margins @testset "convergence analysis" begin - @testset failfast=true "$interpolation" for interpolation in ( - Lagrange{RefTriangle, 3}(), - Lagrange{RefTriangle, 4}(), - Lagrange{RefTriangle, 5}(), - Lagrange{RefHexahedron, 1}(), - Lagrange{RefTetrahedron, 1}(), - Lagrange{RefPrism, 1}(), - Lagrange{RefPyramid, 1}(), - # - Serendipity{RefQuadrilateral, 2}(), - Serendipity{RefHexahedron, 2}(), - # - BubbleEnrichedLagrange{RefTriangle, 1}(), - # - CrouzeixRaviart{RefTriangle,1}(), - CrouzeixRaviart{RefTetrahedron,1}(), - RannacherTurek{RefQuadrilateral,1}(), - RannacherTurek{RefHexahedron,1}(), - ) + @testset failfast = true "$interpolation" for interpolation in ( + Lagrange{RefTriangle, 3}(), + Lagrange{RefTriangle, 4}(), + Lagrange{RefTriangle, 5}(), + Lagrange{RefHexahedron, 1}(), + Lagrange{RefTetrahedron, 1}(), + Lagrange{RefPrism, 1}(), + Lagrange{RefPyramid, 1}(), + # + Serendipity{RefQuadrilateral, 2}(), + Serendipity{RefHexahedron, 2}(), + # + BubbleEnrichedLagrange{RefTriangle, 1}(), + # + CrouzeixRaviart{RefTriangle, 1}(), + CrouzeixRaviart{RefTetrahedron, 1}(), + RannacherTurek{RefQuadrilateral, 1}(), + RannacherTurek{RefHexahedron, 1}(), + ) # Generate a grid ... geometry = ConvergenceTestHelper.get_geometry(interpolation) interpolation_geo = geometric_interpolation(geometry) N = ConvergenceTestHelper.get_num_elements(interpolation) - grid = generate_grid(geometry, ntuple(x->N, getrefdim(geometry))); + grid = generate_grid(geometry, ntuple(x -> N, getrefdim(geometry))) # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) qr = QuadratureRule{getrefshape(interpolation)}(qr_order) # ... and then pray to the gods of convergence. dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid, interpolation, interpolation_geo, qr) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) - ConvergenceTestHelper.check_and_compute_convergence_norms(dh, u, cellvalues, 1e-2) + ConvergenceTestHelper.check_and_compute_convergence_norms(dh, u, cellvalues, 1.0e-2) end end # These test also for correct convergence rates @testset "convergence rate" begin - @testset failfast=true "$interpolation" for interpolation in ( - Lagrange{RefLine, 1}(), - Lagrange{RefLine, 2}(), - Lagrange{RefQuadrilateral, 1}(), - Lagrange{RefQuadrilateral, 2}(), - Lagrange{RefQuadrilateral, 3}(), - Lagrange{RefTriangle, 1}(), - Lagrange{RefTriangle, 2}(), - Lagrange{RefHexahedron, 2}(), - Lagrange{RefTetrahedron, 2}(), - Lagrange{RefPrism, 2}(), - CrouzeixRaviart{RefTriangle,1}(), - CrouzeixRaviart{RefTetrahedron,1}(), - RannacherTurek{RefQuadrilateral,1}(), - RannacherTurek{RefHexahedron,1}(), - ) + @testset failfast = true "$interpolation" for interpolation in ( + Lagrange{RefLine, 1}(), + Lagrange{RefLine, 2}(), + Lagrange{RefQuadrilateral, 1}(), + Lagrange{RefQuadrilateral, 2}(), + Lagrange{RefQuadrilateral, 3}(), + Lagrange{RefTriangle, 1}(), + Lagrange{RefTriangle, 2}(), + Lagrange{RefHexahedron, 2}(), + Lagrange{RefTetrahedron, 2}(), + Lagrange{RefPrism, 2}(), + CrouzeixRaviart{RefTriangle, 1}(), + CrouzeixRaviart{RefTetrahedron, 1}(), + RannacherTurek{RefQuadrilateral, 1}(), + RannacherTurek{RefHexahedron, 1}(), + ) # Generate a grid ... geometry = ConvergenceTestHelper.get_geometry(interpolation) interpolation_geo = geometric_interpolation(geometry) # "Coarse case" N₁ = ConvergenceTestHelper.get_num_elements(interpolation) - grid = generate_grid(geometry, ntuple(x->N₁, getrefdim(geometry))); + grid = generate_grid(geometry, ntuple(x -> N₁, getrefdim(geometry))) # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) qr = QuadratureRule{getrefshape(interpolation)}(qr_order) # ... and then pray to the gods of convergence. dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid, interpolation, interpolation_geo, qr) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) - L2₁, H1₁, _ = ConvergenceTestHelper.check_and_compute_convergence_norms(dh, u, cellvalues, 1e-2) + L2₁, H1₁, _ = ConvergenceTestHelper.check_and_compute_convergence_norms(dh, u, cellvalues, 1.0e-2) # "Fine case" - N₂ = 2*N₁ - grid = generate_grid(geometry, ntuple(x->N₂, getrefdim(geometry))); + N₂ = 2 * N₁ + grid = generate_grid(geometry, ntuple(x -> N₂, getrefdim(geometry))) # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) qr = QuadratureRule{getrefshape(interpolation)}(qr_order) # ... and then pray to the gods of convergence. dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid, interpolation, interpolation_geo, qr) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) - L2₂, H1₂, _ = ConvergenceTestHelper.check_and_compute_convergence_norms(dh, u, cellvalues, 5e-3) + L2₂, H1₂, _ = ConvergenceTestHelper.check_and_compute_convergence_norms(dh, u, cellvalues, 5.0e-3) - @test -(log(L2₂)-log(L2₁))/(log(N₂)-log(N₁)) ≈ Ferrite.getorder(interpolation)+1 atol=0.1 - @test -(log(H1₂)-log(H1₁))/(log(N₂)-log(N₁)) ≈ Ferrite.getorder(interpolation) atol=0.1 + @test -(log(L2₂) - log(L2₁)) / (log(N₂) - log(N₁)) ≈ Ferrite.getorder(interpolation) + 1 atol = 0.1 + @test -(log(H1₂) - log(H1₁)) / (log(N₂) - log(N₁)) ≈ Ferrite.getorder(interpolation) atol = 0.1 end end diff --git a/test/test_abstractgrid.jl b/test/test_abstractgrid.jl index 5005c27afc..16d3c8996c 100644 --- a/test/test_abstractgrid.jl +++ b/test/test_abstractgrid.jl @@ -1,37 +1,37 @@ @testset "AbstractGrid" begin - struct SmallGrid{dim,N,C<:Ferrite.AbstractCell} <: Ferrite.AbstractGrid{dim} - nodes_test::Vector{NTuple{dim,Float64}} - cells_test::NTuple{N,C} + struct SmallGrid{dim, N, C <: Ferrite.AbstractCell} <: Ferrite.AbstractGrid{dim} + nodes_test::Vector{NTuple{dim, Float64}} + cells_test::NTuple{N, C} end Ferrite.getcells(grid::SmallGrid) = grid.cells_test Ferrite.getcells(grid::SmallGrid, v::Union{Int, Vector{Int}}) = grid.cells_test[v] - Ferrite.getncells(grid::SmallGrid{dim,N}) where {dim,N} = N + Ferrite.getncells(grid::SmallGrid{dim, N}) where {dim, N} = N Ferrite.getcelltype(grid::SmallGrid) = eltype(grid.cells_test) Ferrite.getcelltype(grid::SmallGrid, i::Int) = typeof(grid.cells_test[i]) - Ferrite.get_node_coordinate(x::NTuple{dim,Float64}) where dim = Vec{dim,Float64}(x) + Ferrite.get_node_coordinate(x::NTuple{dim, Float64}) where {dim} = Vec{dim, Float64}(x) Ferrite.getnodes(grid::SmallGrid) = grid.nodes_test Ferrite.getnodes(grid::SmallGrid, v::Union{Int, Vector{Int}}) = grid.nodes_test[v] Ferrite.getnnodes(grid::SmallGrid) = length(grid.nodes_test) Ferrite.get_coordinate_eltype(::SmallGrid) = Float64 - Ferrite.get_coordinate_type(::SmallGrid{dim}) where dim = Vec{dim,Float64} - Ferrite.nnodes_per_cell(grid::SmallGrid, i::Int=1) = Ferrite.nnodes(grid.cells_test[i]) + Ferrite.get_coordinate_type(::SmallGrid{dim}) where {dim} = Vec{dim, Float64} + Ferrite.nnodes_per_cell(grid::SmallGrid, i::Int = 1) = Ferrite.nnodes(grid.cells_test[i]) - nodes = [(-1.0,-1.0); (0.0,-1.0); (1.0,-1.0); (-1.0,0.0); (0.0,0.0); (1.0,0.0); (-1.0,1.0); (0.0,1.0); (1.0,1.0)] - cells = (Quadrilateral((1,2,5,4)), Quadrilateral((2,3,6,5)), Quadrilateral((4,5,8,7)), Quadrilateral((5,6,9,8))) - subtype_grid = SmallGrid(nodes,cells) - reference_grid = generate_grid(Quadrilateral, (2,2)) + nodes = [(-1.0, -1.0); (0.0, -1.0); (1.0, -1.0); (-1.0, 0.0); (0.0, 0.0); (1.0, 0.0); (-1.0, 1.0); (0.0, 1.0); (1.0, 1.0)] + cells = (Quadrilateral((1, 2, 5, 4)), Quadrilateral((2, 3, 6, 5)), Quadrilateral((4, 5, 8, 7)), Quadrilateral((5, 6, 9, 8))) + subtype_grid = SmallGrid(nodes, cells) + reference_grid = generate_grid(Quadrilateral, (2, 2)) ip = Lagrange{RefQuadrilateral, 1}() qr = QuadratureRule{RefQuadrilateral}(2) - cellvalues = CellValues(qr, ip); + cellvalues = CellValues(qr, ip) dhs = [DofHandler(grid) for grid in (subtype_grid, reference_grid)] u1 = Vector{Float64}(undef, 9) u2 = Vector{Float64}(undef, 9) - ∂Ω = union(getfacetset.((reference_grid, ), ["left", "right", "top", "bottom"])...) + ∂Ω = union(getfacetset.((reference_grid,), ["left", "right", "top", "bottom"])...) dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) function doassemble!(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) @@ -48,7 +48,7 @@ for q_point in 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) for i in 1:n_basefuncs - v = shape_value(cellvalues, q_point, i) + v = shape_value(cellvalues, q_point, i) ∇v = shape_gradient(cellvalues, q_point, i) fe[i] += v * dΩ for j in 1:n_basefuncs @@ -57,33 +57,33 @@ end end end - assemble!(assembler, celldofs(dh,cellid), Ke, fe) + assemble!(assembler, celldofs(dh, cellid), Ke, fe) end return K, f end - for (dh,u) in zip(dhs,(u1,u2)) + for (dh, u) in zip(dhs, (u1, u2)) add!(dh, :u, ip) close!(dh) ch = ConstraintHandler(dh) add!(ch, dbc) close!(ch) update!(ch, 0.0) - K = allocate_matrix(dh); - K, f = doassemble!(cellvalues, K, dh); + K = allocate_matrix(dh) + K, f = doassemble!(cellvalues, K, dh) apply!(K, f, ch) sol = K \ f u .= sol end @test Ferrite.ndofs_per_cell(dhs[1]) == Ferrite.ndofs_per_cell(dhs[2]) - @test Ferrite.celldofs(dhs[1],3) == Ferrite.celldofs(dhs[2],3) + @test Ferrite.celldofs(dhs[1], 3) == Ferrite.celldofs(dhs[2], 3) @test Ferrite.ndofs(dhs[1]) == Ferrite.ndofs(dhs[2]) - @test isapprox(u1,u2,atol=1e-8) + @test isapprox(u1, u2, atol = 1.0e-8) minv, maxv = Ferrite.bounding_box(subtype_grid) - @test minv ≈ Vec((-1.0,-1.0)) - @test maxv ≈ Vec((+1.0,+1.0)) + @test minv ≈ Vec((-1.0, -1.0)) + @test maxv ≈ Vec((+1.0, +1.0)) colors1 = Ferrite.create_coloring(subtype_grid, alg = ColoringAlgorithm.WorkStream) colors2 = Ferrite.create_coloring(reference_grid, alg = ColoringAlgorithm.WorkStream) diff --git a/test/test_apply_analytical.jl b/test/test_apply_analytical.jl index e3b6af7877..303c1184cb 100644 --- a/test/test_apply_analytical.jl +++ b/test/test_apply_analytical.jl @@ -7,7 +7,7 @@ function change_ip_order(ip::Interpolation, order::Int) B = typebase(ip) RefShape = Ferrite.getrefshape(ip) - return B{RefShape,order}() + return B{RefShape, order}() end getcellorder(CT) = Ferrite.getorder(Ferrite.geometric_interpolation(CT)) getcelltypedim(::Type{<:Ferrite.AbstractCell{shape}}) where {dim, shape <: Ferrite.AbstractRefShape{dim}} = dim @@ -17,9 +17,9 @@ dim = getcelltypedim(CT) local grid try - grid = generate_grid(CT, ntuple(_->3, dim)) + grid = generate_grid(CT, ntuple(_ -> 3, dim)) catch e - isa(e, MethodError) && e.f==generate_grid && return nothing + isa(e, MethodError) && e.f == generate_grid && return nothing rethrow(e) end @@ -39,20 +39,20 @@ function testdh_subdomains(dim, ip_order_u, ip_order_p) if dim == 1 nodes = Node.([Vec{1}((x,)) for x in 0.0:1.0:3.0]) - cell1 = Line((1,2)) - cell2 = QuadraticLine((2,3,4)) - grid = Grid([cell1, cell2], nodes; cellsets=Dict("A"=>Set(1:1), "B"=>Set(2:2))) + cell1 = Line((1, 2)) + cell2 = QuadraticLine((2, 3, 4)) + grid = Grid([cell1, cell2], nodes; cellsets = Dict("A" => Set(1:1), "B" => Set(2:2))) elseif dim == 2 - nodes = Node.([Vec{2}((x,y)) for y in 0.0:2 for x in 0.0:1]) - cell1 = Triangle((1,2,3)) - cell2 = Triangle((2,4,3)) - cell3 = Quadrilateral((3,4,6,5)) - grid = Grid([cell1,cell2,cell3], nodes; cellsets=Dict("A"=>Set(1:2), "B"=>Set(3:3))) + nodes = Node.([Vec{2}((x, y)) for y in 0.0:2 for x in 0.0:1]) + cell1 = Triangle((1, 2, 3)) + cell2 = Triangle((2, 4, 3)) + cell3 = Quadrilateral((3, 4, 6, 5)) + grid = Grid([cell1, cell2, cell3], nodes; cellsets = Dict("A" => Set(1:2), "B" => Set(3:3))) else error("Only dim=1 & 2 supported") end - default_ip_A = Ferrite.geometric_interpolation(getcelltype(grid, first(getcellset(grid,"A")))) - default_ip_B = Ferrite.geometric_interpolation(getcelltype(grid, first(getcellset(grid,"B")))) + default_ip_A = Ferrite.geometric_interpolation(getcelltype(grid, first(getcellset(grid, "A")))) + default_ip_B = Ferrite.geometric_interpolation(getcelltype(grid, first(getcellset(grid, "B")))) dh = DofHandler(grid) sdh_A = SubDofHandler(dh, getcellset(grid, "A")) add!(sdh_A, :u, change_ip_order(default_ip_A, ip_order_u)^dim) @@ -88,13 +88,13 @@ @testset "DofHandler" begin for CT in ( - Line, QuadraticLine, - Triangle, QuadraticTriangle, - Quadrilateral, QuadraticQuadrilateral, - Tetrahedron, QuadraticTetrahedron, - Hexahedron, # SerendipityQuadraticHexahedron, - Wedge, Pyramid, - ) + Line, QuadraticLine, + Triangle, QuadraticTriangle, + Quadrilateral, QuadraticQuadrilateral, + Tetrahedron, QuadraticTetrahedron, + Hexahedron, # SerendipityQuadraticHexahedron, + Wedge, Pyramid, + ) for ip_order_u in 1:2 for ip_order_p in 1:2 dh = testdh(CT, ip_order_u, ip_order_p) @@ -106,19 +106,19 @@ a = zeros(ndofs(dh)) f(x) = ones(Ferrite.get_coordinate_type(dh.grid)) apply_analytical!(a, dh, :u, f) - @test sum(a)/length(a) ≈ num_udofs/(num_udofs+num_pdofs) + @test sum(a) / length(a) ≈ num_udofs / (num_udofs + num_pdofs) # If not super/subparametric, compare with ConstraintHandler and node set - if ip_order_u==ip_order_p==getcellorder(CT) + if ip_order_u == ip_order_p == getcellorder(CT) fill!(a, 0) a_ch = copy(a) fp(x) = norm(x)^2 fu(x::Vec{1}) = Vec{1}((sin(x[1]),)) fu(x::Vec{2}) = Vec{2}((sin(x[1]), cos(x[2]))) - fu(x::Vec{3}) = Vec{3}((sin(x[1]), cos(x[2]), x[3]*(x[1]+x[2]))) + fu(x::Vec{3}) = Vec{3}((sin(x[1]), cos(x[2]), x[3] * (x[1] + x[2]))) ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:p, Set(1:getnnodes(dh.grid)), (x,t)->fp(x))) - add!(ch, Dirichlet(:u, Set(1:getnnodes(dh.grid)), (x,t)->fu(x))) + add!(ch, Dirichlet(:p, Set(1:getnnodes(dh.grid)), (x, t) -> fp(x))) + add!(ch, Dirichlet(:u, Set(1:getnnodes(dh.grid)), (x, t) -> fu(x))) close!(ch); update!(ch, 0.0) apply!(a_ch, ch) @@ -144,13 +144,13 @@ a = zeros(ndofs(dh)) f(x) = ones(Vec{dim}) apply_analytical!(a, dh, :u, f) - @test sum(a)/length(a) ≈ num_udofs/(num_udofs+num_pdofs) + @test sum(a) / length(a) ≈ num_udofs / (num_udofs + num_pdofs) # Repeat test with calls for both subdomains separately a = zeros(ndofs(dh)) apply_analytical!(a, dh, :u, f, getcellset(dh.grid, "A")) apply_analytical!(a, dh, :u, f, getcellset(dh.grid, "B")) - @test sum(a)/length(a) ≈ num_udofs/(num_udofs+num_pdofs) + @test sum(a) / length(a) ≈ num_udofs / (num_udofs + num_pdofs) end end end @@ -158,11 +158,11 @@ @testset "Exceptions" begin dh = testdh(Quadrilateral, 1, 1) - @test_throws ErrorException apply_analytical!(zeros(ndofs(dh)), dh, :v, x->0.0) # Missing field - @test_throws ErrorException apply_analytical!(zeros(ndofs(dh)), dh, :u, x->0.0) # Should be f(x)::Vec{2} + @test_throws ErrorException apply_analytical!(zeros(ndofs(dh)), dh, :v, x -> 0.0) # Missing field + @test_throws ErrorException apply_analytical!(zeros(ndofs(dh)), dh, :u, x -> 0.0) # Should be f(x)::Vec{2} mdh = testdh_subdomains(2, 1, 1) - @test_throws ErrorException apply_analytical!(zeros(ndofs(mdh)), mdh, :v, x->0.0) # Missing field - @test_throws ErrorException apply_analytical!(zeros(ndofs(mdh)), mdh, :u, x->0.0) # Should be f(x)::Vec{2} + @test_throws ErrorException apply_analytical!(zeros(ndofs(mdh)), mdh, :v, x -> 0.0) # Missing field + @test_throws ErrorException apply_analytical!(zeros(ndofs(mdh)), mdh, :u, x -> 0.0) # Should be f(x)::Vec{2} end end diff --git a/test/test_apply_rhs.jl b/test/test_apply_rhs.jl index 87eaecbb5f..95bcf39643 100644 --- a/test/test_apply_rhs.jl +++ b/test/test_apply_rhs.jl @@ -1,6 +1,6 @@ function test_apply_rhs() grid = generate_grid(Quadrilateral, (20, 20)) - ip = Lagrange{RefQuadrilateral,1}() + ip = Lagrange{RefQuadrilateral, 1}() qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip) @@ -14,20 +14,20 @@ function test_apply_rhs() ∂Ω = union(getfacetset.((grid,), ["left", "right"])...) dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) - add!(ch, dbc); + add!(ch, dbc) ∂Ω = union(getfacetset.((grid,), ["top", "bottom"])...) dbc = Dirichlet(:u, ∂Ω, (x, t) -> 2) - add!(ch, dbc); + add!(ch, dbc) close!(ch) - update!(ch, 0.0); + update!(ch, 0.0) function doassemble!( - cellvalues::CellValues, - K::SparseMatrixCSC, - dh::DofHandler, - ) + cellvalues::CellValues, + K::SparseMatrixCSC, + dh::DofHandler, + ) n_basefuncs = getnbasefunctions(cellvalues) Ke = zeros(n_basefuncs, n_basefuncs) @@ -42,14 +42,14 @@ function test_apply_rhs() reinit!(cellvalues, cell) - for q_point = 1:getnquadpoints(cellvalues) + for q_point in 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) - for i = 1:n_basefuncs + for i in 1:n_basefuncs v = shape_value(cellvalues, q_point, i) ∇v = shape_gradient(cellvalues, q_point, i) fe[i] += v * dΩ - for j = 1:n_basefuncs + for j in 1:n_basefuncs ∇u = shape_gradient(cellvalues, q_point, j) Ke[i, j] += (∇v ⋅ ∇u) * dΩ end diff --git a/test/test_assemble.jl b/test/test_assemble.jl index d1a586d3eb..47bb29a344 100644 --- a/test/test_assemble.jl +++ b/test/test_assemble.jl @@ -44,13 +44,13 @@ @test length(f) == 10 # assemble with different row and col dofs - rdofs = [1,4,6] - cdofs = [1,7] + rdofs = [1, 4, 6] + cdofs = [1, 7] a = Ferrite.COOAssembler() Ke = rand(length(rdofs), length(cdofs)) assemble!(a, rdofs, cdofs, Ke) K, _ = finish_assemble(a) - @test (K[rdofs,cdofs] .== Ke) |> all + @test (K[rdofs, cdofs] .== Ke) |> all # SparseMatrix assembler K = spzeros(10, 10) @@ -83,7 +83,7 @@ @test fc ≈ f # No zero filling - assembler = start_assemble(Kc, fc; fillzero=false) + assembler = start_assemble(Kc, fc; fillzero = false) @test Kc ≈ K @test fc ≈ f for i in 1:2 @@ -132,17 +132,17 @@ function Base.:+(y::Float64, x::IgnoreMeIfZero) end @testset "assemble! ignoring zeros" begin - store_dofs = [1, 5, 2, 8] + store_dofs = [1, 5, 2, 8] assemble_dofs = [1, 5, 4, 8] - I = repeat(store_dofs; outer=4) - J = repeat(store_dofs; inner=4) + I = repeat(store_dofs; outer = 4) + J = repeat(store_dofs; inner = 4) V = zeros(length(I)) K = sparse(I, J, V) D = zeros(size(K)) # Standard assembler a = start_assemble(K) - ke = rand(4,4); ke[3, :] .= 0; ke[:, 3] .= 0; ke[2,2] = 0 + ke = rand(4, 4); ke[3, :] .= 0; ke[:, 3] .= 0; ke[2, 2] = 0 assemble!(a, assemble_dofs, IgnoreMeIfZero.(ke)) D[assemble_dofs, assemble_dofs] += ke @test K == D @@ -161,13 +161,17 @@ end K = spdiagm(0 => zeros(2)) a = start_assemble(K) as = start_assemble(Symmetric(K)) - errr(i,j) = try Ferrite._missing_sparsity_pattern_error(i, j) catch e e end + errr(i, j) = try + Ferrite._missing_sparsity_pattern_error(i, j) + catch e + e + end ## Errors below diagonal - @test_throws errr(2,1) assemble!(a, [1, 2], [1.0 0.0; 3.0 4.0]) - @test_throws errr(2,1) assemble!(a, [2, 1], [1.0 2.0; 0.0 4.0]) + @test_throws errr(2, 1) assemble!(a, [1, 2], [1.0 0.0; 3.0 4.0]) + @test_throws errr(2, 1) assemble!(a, [2, 1], [1.0 2.0; 0.0 4.0]) ## Errors above diagonal - @test_throws errr(2,2) assemble!(a, [1, 2], [1.0 2.0; 0.0 4.0]) - @test_throws errr(2,2) assemble!(as, [1, 2], [1.0 2.0; 0.0 4.0]) - @test_throws errr(2,2) assemble!(a, [2, 1], [1.0 0.0; 3.0 4.0]) - @test_throws errr(2,2) assemble!(as, [2, 1], [1.0 0.0; 3.0 4.0]) + @test_throws errr(2, 2) assemble!(a, [1, 2], [1.0 2.0; 0.0 4.0]) + @test_throws errr(2, 2) assemble!(as, [1, 2], [1.0 2.0; 0.0 4.0]) + @test_throws errr(2, 2) assemble!(a, [2, 1], [1.0 0.0; 3.0 4.0]) + @test_throws errr(2, 2) assemble!(as, [2, 1], [1.0 0.0; 3.0 4.0]) end diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 6cd66748b5..e8be79127b 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -1,470 +1,472 @@ @testset "CellValues" begin -@testset "ip=$scalar_interpol" for (scalar_interpol, quad_rule) in ( - (Lagrange{RefLine, 1}(), QuadratureRule{RefLine}(2)), - (Lagrange{RefLine, 2}(), QuadratureRule{RefLine}(2)), - (Lagrange{RefQuadrilateral, 1}(), QuadratureRule{RefQuadrilateral}(2)), - (Lagrange{RefQuadrilateral, 2}(), QuadratureRule{RefQuadrilateral}(2)), - (Lagrange{RefTriangle, 1}(), QuadratureRule{RefTriangle}(2)), - (Lagrange{RefTriangle, 2}(), QuadratureRule{RefTriangle}(2)), - (Lagrange{RefTriangle, 3}(), QuadratureRule{RefTriangle}(2)), - (Lagrange{RefTriangle, 4}(), QuadratureRule{RefTriangle}(2)), - (Lagrange{RefTriangle, 5}(), QuadratureRule{RefTriangle}(2)), - (Lagrange{RefHexahedron, 1}(), QuadratureRule{RefHexahedron}(2)), - (Serendipity{RefQuadrilateral, 2}(), QuadratureRule{RefQuadrilateral}(2)), - (Lagrange{RefTriangle, 1}(), QuadratureRule{RefTriangle}(2)), - (Lagrange{RefTetrahedron, 2}(), QuadratureRule{RefTetrahedron}(2)), - (Lagrange{RefPrism, 2}(), QuadratureRule{RefPrism}(2)), - (Lagrange{RefPyramid, 2}(), QuadratureRule{RefPyramid}(2)), - ) - for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)), DiffOrder in 1:2 - (DiffOrder==2 && Ferrite.getorder(func_interpol)==1) && continue #No need to test linear interpolations again - geom_interpol = scalar_interpol # Tests below assume this - n_basefunc_base = getnbasefunctions(scalar_interpol) - update_gradients = true - update_hessians = (DiffOrder==2 && Ferrite.getorder(func_interpol) > 1) - cv = CellValues(quad_rule, func_interpol, geom_interpol; update_gradients, update_hessians) - if update_gradients && !update_hessians # Check correct and type-stable default constructor - cv_default = @inferred CellValues(quad_rule, func_interpol, geom_interpol) - @test typeof(cv) === typeof(cv_default) - @inferred CellValues(quad_rule, func_interpol, geom_interpol; update_gradients=Val(false), update_detJdV=Val(false)) - end - rdim = Ferrite.getrefdim(func_interpol) - n_basefuncs = getnbasefunctions(func_interpol) + @testset "ip=$scalar_interpol" for (scalar_interpol, quad_rule) in ( + (Lagrange{RefLine, 1}(), QuadratureRule{RefLine}(2)), + (Lagrange{RefLine, 2}(), QuadratureRule{RefLine}(2)), + (Lagrange{RefQuadrilateral, 1}(), QuadratureRule{RefQuadrilateral}(2)), + (Lagrange{RefQuadrilateral, 2}(), QuadratureRule{RefQuadrilateral}(2)), + (Lagrange{RefTriangle, 1}(), QuadratureRule{RefTriangle}(2)), + (Lagrange{RefTriangle, 2}(), QuadratureRule{RefTriangle}(2)), + (Lagrange{RefTriangle, 3}(), QuadratureRule{RefTriangle}(2)), + (Lagrange{RefTriangle, 4}(), QuadratureRule{RefTriangle}(2)), + (Lagrange{RefTriangle, 5}(), QuadratureRule{RefTriangle}(2)), + (Lagrange{RefHexahedron, 1}(), QuadratureRule{RefHexahedron}(2)), + (Serendipity{RefQuadrilateral, 2}(), QuadratureRule{RefQuadrilateral}(2)), + (Lagrange{RefTriangle, 1}(), QuadratureRule{RefTriangle}(2)), + (Lagrange{RefTetrahedron, 2}(), QuadratureRule{RefTetrahedron}(2)), + (Lagrange{RefPrism, 2}(), QuadratureRule{RefPrism}(2)), + (Lagrange{RefPyramid, 2}(), QuadratureRule{RefPyramid}(2)), + ) + for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)), DiffOrder in 1:2 + (DiffOrder == 2 && Ferrite.getorder(func_interpol) == 1) && continue # No need to test linear interpolations again + geom_interpol = scalar_interpol # Tests below assume this + n_basefunc_base = getnbasefunctions(scalar_interpol) + update_gradients = true + update_hessians = (DiffOrder == 2 && Ferrite.getorder(func_interpol) > 1) + cv = CellValues(quad_rule, func_interpol, geom_interpol; update_gradients, update_hessians) + if update_gradients && !update_hessians # Check correct and type-stable default constructor + cv_default = @inferred CellValues(quad_rule, func_interpol, geom_interpol) + @test typeof(cv) === typeof(cv_default) + @inferred CellValues(quad_rule, func_interpol, geom_interpol; update_gradients = Val(false), update_detJdV = Val(false)) + end + rdim = Ferrite.getrefdim(func_interpol) + n_basefuncs = getnbasefunctions(func_interpol) - @test getnbasefunctions(cv) == n_basefuncs + @test getnbasefunctions(cv) == n_basefuncs - coords, n = valid_coordinates_and_normals(func_interpol) - reinit!(cv, coords) + coords, n = valid_coordinates_and_normals(func_interpol) + reinit!(cv, coords) - # We test this by applying a given deformation gradient on all the nodes. - # Since this is a linear deformation we should get back the exact values - # from the interpolation. - V, G, H = if func_interpol isa Ferrite.ScalarInterpolation - (rand(), rand(Tensor{1, rdim}), Tensor{2, rdim}((i,j)-> i==j ? rand() : 0.0)) - else - (rand(Tensor{1, rdim}), rand(Tensor{2, rdim}), Tensor{3, rdim}((i,j,k)-> i==j==k ? rand() : 0.0)) - end - - u_funk(x,V,G,H) = begin - if update_hessians - 0.5*x⋅H⋅x + G⋅x + V + # We test this by applying a given deformation gradient on all the nodes. + # Since this is a linear deformation we should get back the exact values + # from the interpolation. + V, G, H = if func_interpol isa Ferrite.ScalarInterpolation + (rand(), rand(Tensor{1, rdim}), Tensor{2, rdim}((i, j) -> i == j ? rand() : 0.0)) else - G⋅x + V + (rand(Tensor{1, rdim}), rand(Tensor{2, rdim}), Tensor{3, rdim}((i, j, k) -> i == j == k ? rand() : 0.0)) end - end - _ue = [u_funk(coords[i],V,G,H) for i in 1:n_basefunc_base] - ue = reinterpret(Float64, _ue) + function u_funk(x, V, G, H) + if update_hessians + 0.5 * x ⋅ H ⋅ x + G ⋅ x + V + else + G ⋅ x + V + end + end - for i in 1:getnquadpoints(cv) - xqp = spatial_coordinate(cv, i, coords) - Hqp, Gqp, Vqp = Tensors.hessian(x -> u_funk(x,V,G,H), xqp, :all) + _ue = [u_funk(coords[i], V, G, H) for i in 1:n_basefunc_base] + ue = reinterpret(Float64, _ue) - @test function_value(cv, i, ue) ≈ Vqp - @test function_gradient(cv, i, ue) ≈ Gqp - if update_hessians - #Note, the jacobian of the element is constant, which makes the hessian (of the mapping) - #zero. So this is not the optimal test - @test Ferrite.function_hessian(cv, i, ue) ≈ Hqp - end - if func_interpol isa Ferrite.VectorInterpolation - @test function_symmetric_gradient(cv, i, ue) ≈ 0.5(Gqp + Gqp') - @test function_divergence(cv, i, ue) ≈ tr(Gqp) - rdim == 3 && @test function_curl(cv, i, ue) ≈ Ferrite.curl_from_gradient(Gqp) - else - @test function_divergence(cv, i, ue) ≈ sum(Gqp) + for i in 1:getnquadpoints(cv) + xqp = spatial_coordinate(cv, i, coords) + Hqp, Gqp, Vqp = Tensors.hessian(x -> u_funk(x, V, G, H), xqp, :all) + + @test function_value(cv, i, ue) ≈ Vqp + @test function_gradient(cv, i, ue) ≈ Gqp + if update_hessians + # Note, the jacobian of the element is constant, which makes the hessian (of the mapping) + # zero. So this is not the optimal test + @test Ferrite.function_hessian(cv, i, ue) ≈ Hqp + end + if func_interpol isa Ferrite.VectorInterpolation + @test function_symmetric_gradient(cv, i, ue) ≈ 0.5(Gqp + Gqp') + @test function_divergence(cv, i, ue) ≈ tr(Gqp) + rdim == 3 && @test function_curl(cv, i, ue) ≈ Ferrite.curl_from_gradient(Gqp) + else + @test function_divergence(cv, i, ue) ≈ sum(Gqp) + end end - end - #Test CellValues when input is a ::Vector{<:Vec} (most of which is deprecated) - ue_vec = [zero(Vec{rdim,Float64}) for i in 1:n_basefunc_base] - G_vector = rand(Tensor{2, rdim}) - for i in 1:n_basefunc_base - ue_vec[i] = G_vector ⋅ coords[i] - end + # Test CellValues when input is a ::Vector{<:Vec} (most of which is deprecated) + ue_vec = [zero(Vec{rdim, Float64}) for i in 1:n_basefunc_base] + G_vector = rand(Tensor{2, rdim}) + for i in 1:n_basefunc_base + ue_vec[i] = G_vector ⋅ coords[i] + end - for i in 1:getnquadpoints(cv) - if func_interpol isa Ferrite.ScalarInterpolation - @test function_gradient(cv, i, ue_vec) ≈ G_vector - else# func_interpol isa Ferrite.VectorInterpolation - @test_throws Ferrite.DeprecationError function_gradient(cv, i, ue_vec) - @test_throws Ferrite.DeprecationError function_symmetric_gradient(cv, i, ue_vec) - @test_throws Ferrite.DeprecationError function_divergence(cv, i, ue_vec) - if rdim == 3 - @test_throws Ferrite.DeprecationError function_curl(cv, i, ue_vec) + for i in 1:getnquadpoints(cv) + if func_interpol isa Ferrite.ScalarInterpolation + @test function_gradient(cv, i, ue_vec) ≈ G_vector + else # func_interpol isa Ferrite.VectorInterpolation + @test_throws Ferrite.DeprecationError function_gradient(cv, i, ue_vec) + @test_throws Ferrite.DeprecationError function_symmetric_gradient(cv, i, ue_vec) + @test_throws Ferrite.DeprecationError function_divergence(cv, i, ue_vec) + if rdim == 3 + @test_throws Ferrite.DeprecationError function_curl(cv, i, ue_vec) + end + @test_throws Ferrite.DeprecationError function_value(cv, i, ue_vec) # no value to test against end - @test_throws Ferrite.DeprecationError function_value(cv, i, ue_vec) #no value to test against end - end - - #Check if the non-linear mapping is correct - #Only do this for one interpolation becuase it relise on AD on "iterative function" - if scalar_interpol === Lagrange{RefQuadrilateral, 2}() - coords_nl = [x+rand(x)*0.01 for x in coords] #add some displacement to nodes - reinit!(cv, coords_nl) - _ue_nl = [u_funk(coords_nl[i],V,G,H) for i in 1:n_basefunc_base] - ue_nl = reinterpret(Float64, _ue_nl) + # Check if the non-linear mapping is correct + # Only do this for one interpolation becuase it relise on AD on "iterative function" + if scalar_interpol === Lagrange{RefQuadrilateral, 2}() + coords_nl = [x + rand(x) * 0.01 for x in coords] # add some displacement to nodes + reinit!(cv, coords_nl) + + _ue_nl = [u_funk(coords_nl[i], V, G, H) for i in 1:n_basefunc_base] + ue_nl = reinterpret(Float64, _ue_nl) + + for i in 1:getnquadpoints(cv) + xqp = spatial_coordinate(cv, i, coords_nl) + Hqp, Gqp, Vqp = Tensors.hessian(x -> function_value_from_physical_coord(func_interpol, coords_nl, x, ue_nl), xqp, :all) + @test function_value(cv, i, ue_nl) ≈ Vqp + @test function_gradient(cv, i, ue_nl) ≈ Gqp + if update_hessians + @test Ferrite.function_hessian(cv, i, ue_nl) ≈ Hqp + end + end + reinit!(cv, coords) # reinit back to old coords + end + # Test of volume + vol = 0.0 for i in 1:getnquadpoints(cv) - xqp = spatial_coordinate(cv, i, coords_nl) - Hqp, Gqp, Vqp = Tensors.hessian(x -> function_value_from_physical_coord(func_interpol, coords_nl, x, ue_nl), xqp, :all) - @test function_value(cv, i, ue_nl) ≈ Vqp - @test function_gradient(cv, i, ue_nl) ≈ Gqp - if update_hessians - @test Ferrite.function_hessian(cv, i, ue_nl) ≈ Hqp - end + vol += getdetJdV(cv, i) end - reinit!(cv, coords) # reinit back to old coords - end + @test vol ≈ calculate_volume(func_interpol, coords) - # Test of volume - vol = 0.0 - for i in 1:getnquadpoints(cv) - vol += getdetJdV(cv,i) - end - @test vol ≈ calculate_volume(func_interpol, coords) - - # Test quadrature rule after reinit! with ref. coords - coords = Ferrite.reference_coordinates(func_interpol) - reinit!(cv, coords) - vol = 0.0 - for i in 1:getnquadpoints(cv) - vol += getdetJdV(cv,i) - end - @test vol ≈ reference_volume(func_interpol) + # Test quadrature rule after reinit! with ref. coords + coords = Ferrite.reference_coordinates(func_interpol) + reinit!(cv, coords) + vol = 0.0 + for i in 1:getnquadpoints(cv) + vol += getdetJdV(cv, i) + end + @test vol ≈ reference_volume(func_interpol) - # Test spatial coordinate (after reinit with ref.coords we should get back the quad_points) - for (i, qp_x) in pairs(Ferrite.getpoints(quad_rule)) - @test spatial_coordinate(cv, i, coords) ≈ qp_x - end + # Test spatial coordinate (after reinit with ref.coords we should get back the quad_points) + for (i, qp_x) in pairs(Ferrite.getpoints(quad_rule)) + @test spatial_coordinate(cv, i, coords) ≈ qp_x + end - @testset "copy(::CellValues)" begin - cvc = copy(cv) - @test typeof(cv) == typeof(cvc) - - # Test that all mutable types in FunctionValues and GeometryMapping have been copied - for key in (:fun_values, :geo_mapping) - val = getfield(cv, key) - valc = getfield(cvc, key) - for fname in fieldnames(typeof(val)) - v = getfield(val, fname) - vc = getfield(valc, fname) - isbits(v) || @test v !== vc + @testset "copy(::CellValues)" begin + cvc = copy(cv) + @test typeof(cv) == typeof(cvc) + + # Test that all mutable types in FunctionValues and GeometryMapping have been copied + for key in (:fun_values, :geo_mapping) + val = getfield(cv, key) + valc = getfield(cvc, key) + for fname in fieldnames(typeof(val)) + v = getfield(val, fname) + vc = getfield(valc, fname) + isbits(v) || @test v !== vc + @test v == vc + end + end + # Test that qr and detJdV is copied as expected. + # Note that qr remain aliased, as defined by `copy(qr)=qr`, see quadrature.jl. + for fname in (:qr, :detJdV) + v = getfield(cv, fname) + vc = getfield(cvc, fname) + fname === :qr || @test v !== vc @test v == vc end end - # Test that qr and detJdV is copied as expected. - # Note that qr remain aliased, as defined by `copy(qr)=qr`, see quadrature.jl. - for fname in (:qr, :detJdV) - v = getfield(cv, fname) - vc = getfield(cvc, fname) - fname === :qr || @test v !== vc - @test v == vc - end end end -end - -@testset "GeometryMapping" begin - grid = generate_grid(Quadrilateral, (1,1)) - cc = first(CellIterator(grid)) - - qr = QuadratureRule{RefQuadrilateral}(1) - ξ = first(Ferrite.getpoints(qr)) - ip = Lagrange{RefQuadrilateral,1}() - - cv0 = CellValues(Float64, qr, ip, ip^2; update_detJdV=false, update_gradients=false, update_hessians=false) - reinit!(cv0, cc) - @test Ferrite.calculate_mapping(cv0.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(0)) - - cv1 = CellValues(Float64, qr, ip, ip^2; update_detJdV=false, update_gradients=true, update_hessians=false) - reinit!(cv1, cc) - @test Ferrite.calculate_mapping(cv1.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(1)) - - cv2 = CellValues(Float64, qr, ip, ip^2; update_detJdV=false, update_gradients=false, update_hessians=true) - reinit!(cv2, cc) - @test Ferrite.calculate_mapping(cv2.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(2)) -end - -@testset "#265: error message for incompatible geometric interpolation" begin - dim = 1 - deg = 1 - grid = generate_grid(Line, (2,)) - ip_fe = Lagrange{RefLine, deg}() - dh = DofHandler(grid) - add!(dh, :u, ip_fe) - close!(dh); - cell = first(CellIterator(dh)) - ip_geo = Lagrange{RefLine, 2}() - qr = QuadratureRule{RefLine}(deg+1) - cv = CellValues(qr, ip_fe, ip_geo) - res = @test_throws ArgumentError reinit!(cv, cell) - @test occursin("265", res.value.msg) - ip_geo = Lagrange{RefLine, 1}() - cv = CellValues(qr, ip_fe, ip_geo) - reinit!(cv, cell) -end - -@testset "error paths in function_* and reinit!" begin - dim = 2 - qp = 1 - ip = Lagrange{RefTriangle,1}() - qr = QuadratureRule{RefTriangle}(1) - qr_f = FacetQuadratureRule{RefTriangle}(1) - csv = CellValues(qr, ip) - cvv = CellValues(qr, VectorizedInterpolation(ip)) - csv_embedded = CellValues(qr, ip, ip^3) - fsv = FacetValues(qr_f, ip) - fvv = FacetValues(qr_f, VectorizedInterpolation(ip)) - fsv_embedded = FacetValues(qr_f, ip, ip^3) - - x, n = valid_coordinates_and_normals(ip) - reinit!(csv, x) - reinit!(cvv, x) - reinit!(fsv, x, 1) - reinit!(fvv, x, 1) - - # Wrong number of coordinates - xx = [x; x] - @test_throws ArgumentError reinit!(csv, xx) - @test_throws ArgumentError reinit!(cvv, xx) - @test_throws ArgumentError reinit!(fsv, xx, 1) - @test_throws ArgumentError reinit!(fvv, xx, 1) - - @test_throws ArgumentError spatial_coordinate(csv, qp, xx) - @test_throws ArgumentError spatial_coordinate(cvv, qp, xx) - @test_throws ArgumentError spatial_coordinate(fsv, qp, xx) - @test_throws ArgumentError spatial_coordinate(fvv, qp, xx) - - # Wrong dimension of coordinates - @test_throws ArgumentError reinit!(csv_embedded, x) - @test_throws ArgumentError reinit!(fsv_embedded, x, 1) - - # Wrong number of (local) dofs - # Scalar values, scalar dofs - ue = rand(getnbasefunctions(csv) + 1) - @test_throws ArgumentError function_value(csv, qp, ue) - @test_throws ArgumentError function_gradient(csv, qp, ue) - # Vector values, scalar dofs - ue = rand(getnbasefunctions(cvv) + 1) - @test_throws ArgumentError function_value(cvv, qp, ue) - @test_throws ArgumentError function_gradient(cvv, qp, ue) - @test_throws ArgumentError function_divergence(cvv, qp, ue) - # Scalar values, vector dofs - ue = [rand(Vec{dim}) for _ in 1:(getnbasefunctions(csv) + 1)] - @test_throws ArgumentError function_value(csv, qp, ue) - @test_throws ArgumentError function_gradient(csv, qp, ue) - @test_throws ArgumentError function_divergence(csv, qp, ue) -end - -@testset "Embedded elements" begin - @testset "Scalar/vector on curves (vdim = $vdim)" for vdim in (0, 1, 2, 3) - ip_base = Lagrange{RefLine,1}() - ip = vdim > 0 ? ip_base^vdim : ip_base - ue = 2 * rand(getnbasefunctions(ip)) - qr = QuadratureRule{RefLine}(1) - # Reference values - csv1 = CellValues(qr, ip) - reinit!(csv1, [Vec((0.0,)), Vec((1.0,))]) - - ## sdim = 2, Consistency with 1D - csv2 = CellValues(qr, ip, ip_base^2) - reinit!(csv2, [Vec((0.0, 0.0)), Vec((1.0, 0.0))]) - # Test spatial interpolation - @test spatial_coordinate(csv2, 1, [Vec((0.0, 0.0)), Vec((1.0, 0.0))]) == Vec{2}((0.5, 0.0)) - # Test volume - @test getdetJdV(csv1, 1) == getdetJdV(csv2, 1) - # Test flip - @test shape_value(csv1, 1, 1) == shape_value(csv2, 1, 1) - @test shape_value(csv1, 1, 2) == shape_value(csv2, 1, 2) - # Test evals - @test function_value(csv1, 1, ue) == function_value(csv2, 1, ue) - if vdim == 0 - @test function_gradient(csv1, 1, ue)[1] == function_gradient(csv2, 1, ue)[1] - @test 0.0 == function_gradient(csv2, 1, ue)[2] - else - @test function_gradient(csv1, 1, ue)[:, 1] == function_gradient(csv2, 1, ue)[:, 1] - @test zeros(vdim) == function_gradient(csv2, 1, ue)[:, 2] - end - ## sdim = 3, Consistency with 1D - csv3 = CellValues(qr, ip, ip_base^3) - reinit!(csv3, [Vec((0.0, 0.0, 0.0)), Vec((1.0, 0.0, 0.0))]) - # Test spatial interpolation - @test spatial_coordinate(csv3, 1, [Vec((0.0, 0.0, 0.0)), Vec((1.0, 0.0, 0.0))]) == Vec{3}((0.5, 0.0, 0.0)) - # Test volume - @test getdetJdV(csv1, 1) == getdetJdV(csv3, 1) - # Test flip - @test shape_value(csv1, 1, 1) == shape_value(csv3, 1, 1) - @test shape_value(csv1, 1, 2) == shape_value(csv3, 1, 2) - # Test evals - @test function_value(csv1, 1, ue) == function_value(csv3, 1, ue) - if vdim == 0 - @test function_gradient(csv1, 1, ue)[1] == function_gradient(csv3, 1, ue)[1] - @test 0.0 == function_gradient(csv3, 1, ue)[2] - @test 0.0 == function_gradient(csv3, 1, ue)[3] - else - @test function_gradient(csv1, 1, ue)[:, 1] == function_gradient(csv3, 1, ue)[:, 1] - @test zeros(vdim, 2) == function_gradient(csv3, 1, ue)[:, 2:3] - end + @testset "GeometryMapping" begin + grid = generate_grid(Quadrilateral, (1, 1)) + cc = first(CellIterator(grid)) - ## sdim = 3, Consistency in 2D - reinit!(csv2, [Vec((-1.0, 2.0)), Vec((3.0, -4.0))]) - reinit!(csv3, [Vec((-1.0, 2.0, 0.0)), Vec((3.0, -4.0, 0.0))]) - # Test spatial interpolation - @test spatial_coordinate(csv2, 1, [Vec((-1.0, 2.0)), Vec((3.0, -4.0))]) == Vec{2}((1.0, -1.0)) - @test spatial_coordinate(csv3, 1, [Vec((-1.0, 2.0, 0.0)), Vec((3.0, -4.0, 0.0))]) == Vec{3}((1.0, -1.0, 0.0)) - # Test volume - @test getdetJdV(csv2, 1) == getdetJdV(csv3, 1) - # Test evals - @test function_value(csv2, 1, ue) == function_value(csv3, 1, ue) - if vdim == 0 - @test function_gradient(csv2, 1, ue)[1:2] == function_gradient(csv3, 1, ue)[1:2] - @test 0.0 == function_gradient(csv3, 1, ue)[3] - else - @test function_gradient(csv2, 1, ue)[:, 1:2] == function_gradient(csv3, 1, ue)[:, 1:2] - @test zeros(vdim) == function_gradient(csv3, 1, ue)[:, 3] - end - ## Change plane - reinit!(csv3, [Vec((-1.0, 0.0, 2.0)), Vec((3.0, 0.0, -4.0))]) - # Test spatial interpolation - @test spatial_coordinate(csv3, 1, [Vec((-1.0, 0.0, 2.0)), Vec((3.0, 0.0, -4.0))]) == Vec{3}((1.0, 0.0, -1.0)) - # Test volume - @test getdetJdV(csv2, 1) == getdetJdV(csv3, 1) - # Test evals - @test function_value(csv2, 1, ue) == function_value(csv3, 1, ue) - if vdim == 0 - @test function_gradient(csv2, 1, ue)[1] == function_gradient(csv3, 1, ue)[1] - @test 0.0 == function_gradient(csv3, 1, ue)[2] - @test function_gradient(csv2, 1, ue)[2] == function_gradient(csv3, 1, ue)[3] - else - @test function_gradient(csv2, 1, ue)[:, 1] == function_gradient(csv3, 1, ue)[:, 1] - @test zeros(vdim) == function_gradient(csv3, 1, ue)[:, 2] - @test function_gradient(csv2, 1, ue)[:, 2] == function_gradient(csv3, 1, ue)[:, 3] - end + qr = QuadratureRule{RefQuadrilateral}(1) + ξ = first(Ferrite.getpoints(qr)) + ip = Lagrange{RefQuadrilateral, 1}() + + cv0 = CellValues(Float64, qr, ip, ip^2; update_detJdV = false, update_gradients = false, update_hessians = false) + reinit!(cv0, cc) + @test Ferrite.calculate_mapping(cv0.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(0)) + + cv1 = CellValues(Float64, qr, ip, ip^2; update_detJdV = false, update_gradients = true, update_hessians = false) + reinit!(cv1, cc) + @test Ferrite.calculate_mapping(cv1.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(1)) + + cv2 = CellValues(Float64, qr, ip, ip^2; update_detJdV = false, update_gradients = false, update_hessians = true) + reinit!(cv2, cc) + @test Ferrite.calculate_mapping(cv2.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(2)) end - @testset "Scalar/vector on surface (vdim = $vdim)" for vdim in (0, 1, 2, 3) - ip_base = Lagrange{RefQuadrilateral,1}() - ip = vdim > 0 ? ip_base^vdim : ip_base - ue = rand(getnbasefunctions(ip)) - qr = QuadratureRule{RefQuadrilateral}(1) - csv2 = CellValues(qr, ip) - csv3 = CellValues(qr, ip, ip_base^3) - reinit!(csv2, [Vec((-1.0,-1.0)), Vec((1.0,-1.0)), Vec((1.0,1.0)), Vec((-1.0,1.0))]) - reinit!(csv3, [Vec((-1.0,-1.0,0.0)), Vec((1.0,-1.0,0.0)), Vec((1.0,1.0,0.0)), Vec((-1.0,1.0,0.0))]) - # Test spatial interpolation - @test spatial_coordinate(csv2, 1, [Vec((-1.0,-1.0)), Vec((1.0,-1.0)), Vec((1.0,1.0)), Vec((-1.0,1.0))]) == Vec{2}((0.0, 0.0)) - @test spatial_coordinate(csv3, 1, [Vec((-1.0,-1.0,0.0)), Vec((1.0,-1.0,0.0)), Vec((1.0,1.0,0.0)), Vec((-1.0,1.0,0.0))]) == Vec{3}((0.0, 0.0, 0.0)) - # Test volume - @test getdetJdV(csv2, 1) == getdetJdV(csv3, 1) - # Test evals - @test function_value(csv2, 1, ue) == function_value(csv3, 1, ue) - if vdim == 0 - @test function_gradient(csv2, 1, ue)[1:2] == function_gradient(csv3, 1, ue)[1:2] - @test 0.0 == function_gradient(csv3, 1, ue)[3] - else - @test function_gradient(csv2, 1, ue)[:, 1:2] == function_gradient(csv3, 1, ue)[:, 1:2] - @test zeros(vdim) == function_gradient(csv3, 1, ue)[:, 3] - end + @testset "#265: error message for incompatible geometric interpolation" begin + dim = 1 + deg = 1 + grid = generate_grid(Line, (2,)) + ip_fe = Lagrange{RefLine, deg}() + dh = DofHandler(grid) + add!(dh, :u, ip_fe) + close!(dh) + cell = first(CellIterator(dh)) + ip_geo = Lagrange{RefLine, 2}() + qr = QuadratureRule{RefLine}(deg + 1) + cv = CellValues(qr, ip_fe, ip_geo) + res = @test_throws ArgumentError reinit!(cv, cell) + @test occursin("265", res.value.msg) + ip_geo = Lagrange{RefLine, 1}() + cv = CellValues(qr, ip_fe, ip_geo) + reinit!(cv, cell) + end + + @testset "error paths in function_* and reinit!" begin + dim = 2 + qp = 1 + ip = Lagrange{RefTriangle, 1}() + qr = QuadratureRule{RefTriangle}(1) + qr_f = FacetQuadratureRule{RefTriangle}(1) + csv = CellValues(qr, ip) + cvv = CellValues(qr, VectorizedInterpolation(ip)) + csv_embedded = CellValues(qr, ip, ip^3) + fsv = FacetValues(qr_f, ip) + fvv = FacetValues(qr_f, VectorizedInterpolation(ip)) + fsv_embedded = FacetValues(qr_f, ip, ip^3) + + x, n = valid_coordinates_and_normals(ip) + reinit!(csv, x) + reinit!(cvv, x) + reinit!(fsv, x, 1) + reinit!(fvv, x, 1) + + # Wrong number of coordinates + xx = [x; x] + @test_throws ArgumentError reinit!(csv, xx) + @test_throws ArgumentError reinit!(cvv, xx) + @test_throws ArgumentError reinit!(fsv, xx, 1) + @test_throws ArgumentError reinit!(fvv, xx, 1) + + @test_throws ArgumentError spatial_coordinate(csv, qp, xx) + @test_throws ArgumentError spatial_coordinate(cvv, qp, xx) + @test_throws ArgumentError spatial_coordinate(fsv, qp, xx) + @test_throws ArgumentError spatial_coordinate(fvv, qp, xx) + + # Wrong dimension of coordinates + @test_throws ArgumentError reinit!(csv_embedded, x) + @test_throws ArgumentError reinit!(fsv_embedded, x, 1) + + # Wrong number of (local) dofs + # Scalar values, scalar dofs + ue = rand(getnbasefunctions(csv) + 1) + @test_throws ArgumentError function_value(csv, qp, ue) + @test_throws ArgumentError function_gradient(csv, qp, ue) + # Vector values, scalar dofs + ue = rand(getnbasefunctions(cvv) + 1) + @test_throws ArgumentError function_value(cvv, qp, ue) + @test_throws ArgumentError function_gradient(cvv, qp, ue) + @test_throws ArgumentError function_divergence(cvv, qp, ue) + # Scalar values, vector dofs + ue = [rand(Vec{dim}) for _ in 1:(getnbasefunctions(csv) + 1)] + @test_throws ArgumentError function_value(csv, qp, ue) + @test_throws ArgumentError function_gradient(csv, qp, ue) + @test_throws ArgumentError function_divergence(csv, qp, ue) end - @testset "CellValues with hessians" begin - ip = Lagrange{RefQuadrilateral,2}() - qr = QuadratureRule{RefQuadrilateral}(2) + @testset "Embedded elements" begin + @testset "Scalar/vector on curves (vdim = $vdim)" for vdim in (0, 1, 2, 3) + ip_base = Lagrange{RefLine, 1}() + ip = vdim > 0 ? ip_base^vdim : ip_base + ue = 2 * rand(getnbasefunctions(ip)) + qr = QuadratureRule{RefLine}(1) + # Reference values + csv1 = CellValues(qr, ip) + reinit!(csv1, [Vec((0.0,)), Vec((1.0,))]) + + ## sdim = 2, Consistency with 1D + csv2 = CellValues(qr, ip, ip_base^2) + reinit!(csv2, [Vec((0.0, 0.0)), Vec((1.0, 0.0))]) + # Test spatial interpolation + @test spatial_coordinate(csv2, 1, [Vec((0.0, 0.0)), Vec((1.0, 0.0))]) == Vec{2}((0.5, 0.0)) + # Test volume + @test getdetJdV(csv1, 1) == getdetJdV(csv2, 1) + # Test flip + @test shape_value(csv1, 1, 1) == shape_value(csv2, 1, 1) + @test shape_value(csv1, 1, 2) == shape_value(csv2, 1, 2) + # Test evals + @test function_value(csv1, 1, ue) == function_value(csv2, 1, ue) + if vdim == 0 + @test function_gradient(csv1, 1, ue)[1] == function_gradient(csv2, 1, ue)[1] + @test 0.0 == function_gradient(csv2, 1, ue)[2] + else + @test function_gradient(csv1, 1, ue)[:, 1] == function_gradient(csv2, 1, ue)[:, 1] + @test zeros(vdim) == function_gradient(csv2, 1, ue)[:, 2] + end + + ## sdim = 3, Consistency with 1D + csv3 = CellValues(qr, ip, ip_base^3) + reinit!(csv3, [Vec((0.0, 0.0, 0.0)), Vec((1.0, 0.0, 0.0))]) + # Test spatial interpolation + @test spatial_coordinate(csv3, 1, [Vec((0.0, 0.0, 0.0)), Vec((1.0, 0.0, 0.0))]) == Vec{3}((0.5, 0.0, 0.0)) + # Test volume + @test getdetJdV(csv1, 1) == getdetJdV(csv3, 1) + # Test flip + @test shape_value(csv1, 1, 1) == shape_value(csv3, 1, 1) + @test shape_value(csv1, 1, 2) == shape_value(csv3, 1, 2) + # Test evals + @test function_value(csv1, 1, ue) == function_value(csv3, 1, ue) + if vdim == 0 + @test function_gradient(csv1, 1, ue)[1] == function_gradient(csv3, 1, ue)[1] + @test 0.0 == function_gradient(csv3, 1, ue)[2] + @test 0.0 == function_gradient(csv3, 1, ue)[3] + else + @test function_gradient(csv1, 1, ue)[:, 1] == function_gradient(csv3, 1, ue)[:, 1] + @test zeros(vdim, 2) == function_gradient(csv3, 1, ue)[:, 2:3] + end + + ## sdim = 3, Consistency in 2D + reinit!(csv2, [Vec((-1.0, 2.0)), Vec((3.0, -4.0))]) + reinit!(csv3, [Vec((-1.0, 2.0, 0.0)), Vec((3.0, -4.0, 0.0))]) + # Test spatial interpolation + @test spatial_coordinate(csv2, 1, [Vec((-1.0, 2.0)), Vec((3.0, -4.0))]) == Vec{2}((1.0, -1.0)) + @test spatial_coordinate(csv3, 1, [Vec((-1.0, 2.0, 0.0)), Vec((3.0, -4.0, 0.0))]) == Vec{3}((1.0, -1.0, 0.0)) + # Test volume + @test getdetJdV(csv2, 1) == getdetJdV(csv3, 1) + # Test evals + @test function_value(csv2, 1, ue) == function_value(csv3, 1, ue) + if vdim == 0 + @test function_gradient(csv2, 1, ue)[1:2] == function_gradient(csv3, 1, ue)[1:2] + @test 0.0 == function_gradient(csv3, 1, ue)[3] + else + @test function_gradient(csv2, 1, ue)[:, 1:2] == function_gradient(csv3, 1, ue)[:, 1:2] + @test zeros(vdim) == function_gradient(csv3, 1, ue)[:, 3] + end + ## Change plane + reinit!(csv3, [Vec((-1.0, 0.0, 2.0)), Vec((3.0, 0.0, -4.0))]) + # Test spatial interpolation + @test spatial_coordinate(csv3, 1, [Vec((-1.0, 0.0, 2.0)), Vec((3.0, 0.0, -4.0))]) == Vec{3}((1.0, 0.0, -1.0)) + # Test volume + @test getdetJdV(csv2, 1) == getdetJdV(csv3, 1) + # Test evals + @test function_value(csv2, 1, ue) == function_value(csv3, 1, ue) + if vdim == 0 + @test function_gradient(csv2, 1, ue)[1] == function_gradient(csv3, 1, ue)[1] + @test 0.0 == function_gradient(csv3, 1, ue)[2] + @test function_gradient(csv2, 1, ue)[2] == function_gradient(csv3, 1, ue)[3] + else + @test function_gradient(csv2, 1, ue)[:, 1] == function_gradient(csv3, 1, ue)[:, 1] + @test zeros(vdim) == function_gradient(csv3, 1, ue)[:, 2] + @test function_gradient(csv2, 1, ue)[:, 2] == function_gradient(csv3, 1, ue)[:, 3] + end + end + + @testset "Scalar/vector on surface (vdim = $vdim)" for vdim in (0, 1, 2, 3) + ip_base = Lagrange{RefQuadrilateral, 1}() + ip = vdim > 0 ? ip_base^vdim : ip_base + ue = rand(getnbasefunctions(ip)) + qr = QuadratureRule{RefQuadrilateral}(1) + csv2 = CellValues(qr, ip) + csv3 = CellValues(qr, ip, ip_base^3) + reinit!(csv2, [Vec((-1.0, -1.0)), Vec((1.0, -1.0)), Vec((1.0, 1.0)), Vec((-1.0, 1.0))]) + reinit!(csv3, [Vec((-1.0, -1.0, 0.0)), Vec((1.0, -1.0, 0.0)), Vec((1.0, 1.0, 0.0)), Vec((-1.0, 1.0, 0.0))]) + # Test spatial interpolation + @test spatial_coordinate(csv2, 1, [Vec((-1.0, -1.0)), Vec((1.0, -1.0)), Vec((1.0, 1.0)), Vec((-1.0, 1.0))]) == Vec{2}((0.0, 0.0)) + @test spatial_coordinate(csv3, 1, [Vec((-1.0, -1.0, 0.0)), Vec((1.0, -1.0, 0.0)), Vec((1.0, 1.0, 0.0)), Vec((-1.0, 1.0, 0.0))]) == Vec{3}((0.0, 0.0, 0.0)) + # Test volume + @test getdetJdV(csv2, 1) == getdetJdV(csv3, 1) + # Test evals + @test function_value(csv2, 1, ue) == function_value(csv3, 1, ue) + if vdim == 0 + @test function_gradient(csv2, 1, ue)[1:2] == function_gradient(csv3, 1, ue)[1:2] + @test 0.0 == function_gradient(csv3, 1, ue)[3] + else + @test function_gradient(csv2, 1, ue)[:, 1:2] == function_gradient(csv3, 1, ue)[:, 1:2] + @test zeros(vdim) == function_gradient(csv3, 1, ue)[:, 3] + end + end + + @testset "CellValues with hessians" begin + ip = Lagrange{RefQuadrilateral, 2}() + qr = QuadratureRule{RefQuadrilateral}(2) - cv_vector = CellValues(qr, ip^2, ip^3; update_hessians = true) - cv_scalar = CellValues(qr, ip, ip^3; update_hessians = true) + cv_vector = CellValues(qr, ip^2, ip^3; update_hessians = true) + cv_scalar = CellValues(qr, ip, ip^3; update_hessians = true) - coords = [Vec{3}((x[1], x[2], 0.0)) for x in Ferrite.reference_coordinates(ip)] - @test_throws ErrorException reinit!(cv_vector, coords) #Not implemented for embedded elements - @test_throws ErrorException reinit!(cv_scalar, coords) + coords = [Vec{3}((x[1], x[2], 0.0)) for x in Ferrite.reference_coordinates(ip)] + @test_throws ErrorException reinit!(cv_vector, coords) # Not implemented for embedded elements + @test_throws ErrorException reinit!(cv_scalar, coords) + end end -end - -@testset "CellValues constructor entry points" begin - qr = QuadratureRule{RefTriangle}(1) - - for fun_ip in (Lagrange{RefTriangle, 1}(), Lagrange{RefTriangle, 2}()^2) - value_type(T) = fun_ip isa ScalarInterpolation ? T : Vec{2, T} - grad_type(T) = fun_ip isa ScalarInterpolation ? Vec{2, T} : Tensor{2, 2, T, 4} - # Quadrature + scalar function - cv = CellValues(qr, fun_ip) - @test Ferrite.shape_value_type(cv) == value_type(Float64) - @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test Ferrite.geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() - # Numeric type + quadrature + scalar function - cv = CellValues(Float32, qr, fun_ip) - @test Ferrite.shape_value_type(cv) == value_type(Float32) - @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test Ferrite.geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() - for geo_ip in (Lagrange{RefTriangle, 2}(), Lagrange{RefTriangle, 2}()^2) - scalar_ip(ip) = ip isa VectorizedInterpolation ? ip.ip : ip - # Quadrature + scalar function + geo - cv = CellValues(qr, fun_ip, geo_ip) + + @testset "CellValues constructor entry points" begin + qr = QuadratureRule{RefTriangle}(1) + + for fun_ip in (Lagrange{RefTriangle, 1}(), Lagrange{RefTriangle, 2}()^2) + value_type(T) = fun_ip isa ScalarInterpolation ? T : Vec{2, T} + grad_type(T) = fun_ip isa ScalarInterpolation ? Vec{2, T} : Tensor{2, 2, T, 4} + # Quadrature + scalar function + cv = CellValues(qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test Ferrite.geometric_interpolation(cv) == scalar_ip(geo_ip) - # Numeric type + quadrature + scalar function + scalar geo - cv = CellValues(Float32, qr, fun_ip, geo_ip) + @test Ferrite.geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() + # Numeric type + quadrature + scalar function + cv = CellValues(Float32, qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test Ferrite.geometric_interpolation(cv) == scalar_ip(geo_ip) + @test Ferrite.geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() + for geo_ip in (Lagrange{RefTriangle, 2}(), Lagrange{RefTriangle, 2}()^2) + scalar_ip(ip) = ip isa VectorizedInterpolation ? ip.ip : ip + # Quadrature + scalar function + geo + cv = CellValues(qr, fun_ip, geo_ip) + @test Ferrite.shape_value_type(cv) == value_type(Float64) + @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) + @test Ferrite.geometric_interpolation(cv) == scalar_ip(geo_ip) + # Numeric type + quadrature + scalar function + scalar geo + cv = CellValues(Float32, qr, fun_ip, geo_ip) + @test Ferrite.shape_value_type(cv) == value_type(Float32) + @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) + @test Ferrite.geometric_interpolation(cv) == scalar_ip(geo_ip) + end end end -end - -@testset "show" begin - cv_quad = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()^2) - showstring = sprint(show, MIME"text/plain"(), cv_quad) - @test startswith(showstring, "CellValues(vdim=2, rdim=2, and sdim=2): 4 quadrature points") - @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()^2") - cv_wedge = CellValues(QuadratureRule{RefPrism}(2), Lagrange{RefPrism,2}()) - showstring = sprint(show, MIME"text/plain"(), cv_wedge) - @test startswith(showstring, "CellValues(scalar, rdim=3, and sdim=3): 5 quadrature points") - @test contains(showstring, "Function interpolation: Lagrange{RefPrism, 2}()") - - pv = PointValues(cv_wedge) - pv_showstring = sprint(show, MIME"text/plain"(), pv) - @test startswith(pv_showstring, "PointValues containing") - @test contains(pv_showstring, "Function interpolation: Lagrange{RefPrism, 2}()") -end - -@testset "CustomCellValues" begin - - @testset "SimpleCellValues" begin - include(joinpath(@__DIR__, "../docs/src/topics/SimpleCellValues_literate.jl")) + @testset "show" begin + cv_quad = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral, 2}()^2) + showstring = sprint(show, MIME"text/plain"(), cv_quad) + @test startswith(showstring, "CellValues(vdim=2, rdim=2, and sdim=2): 4 quadrature points") + @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()^2") + + cv_wedge = CellValues(QuadratureRule{RefPrism}(2), Lagrange{RefPrism, 2}()) + showstring = sprint(show, MIME"text/plain"(), cv_wedge) + @test startswith(showstring, "CellValues(scalar, rdim=3, and sdim=3): 5 quadrature points") + @test contains(showstring, "Function interpolation: Lagrange{RefPrism, 2}()") + + pv = PointValues(cv_wedge) + pv_showstring = sprint(show, MIME"text/plain"(), pv) + @test startswith(pv_showstring, "PointValues containing") + @test contains(pv_showstring, "Function interpolation: Lagrange{RefPrism, 2}()") end - @testset "TestCustomCellValues" begin + @testset "CustomCellValues" begin - struct TestCustomCellValues{CV<:CellValues} <: Ferrite.AbstractValues - cv::CV + @testset "SimpleCellValues" begin + include(joinpath(@__DIR__, "../docs/src/topics/SimpleCellValues_literate.jl")) end - # Check that the list in devdocs/FEValues.md is true - # If changes are made that makes the following tests fails, - # the devdocs should be updated accordingly. - for op = (:shape_value, :shape_gradient, :getnquadpoints, :getnbasefunctions, :geometric_value, :getngeobasefunctions) - @eval Ferrite.$op(cv::TestCustomCellValues, args...; kwargs...) = Ferrite.$op(cv.cv, args...; kwargs...) - end - ip = Lagrange{RefQuadrilateral,1}()^2 - qr = QuadratureRule{RefQuadrilateral}(2) - cv = CellValues(qr, ip) - grid = generate_grid(Quadrilateral, (1,1)) - x = getcoordinates(grid, 1) - cell = getcells(grid, 1) - reinit!(cv, cell, x) - ae = rand(getnbasefunctions(cv)) - q_point = rand(1:getnquadpoints(cv)) - cv_custom = TestCustomCellValues(cv) - for fun in (function_value, function_gradient, - function_divergence, function_symmetric_gradient, function_curl) - @test fun(cv_custom, q_point, ae) == fun(cv, q_point, ae) + + @testset "TestCustomCellValues" begin + + struct TestCustomCellValues{CV <: CellValues} <: Ferrite.AbstractValues + cv::CV + end + # Check that the list in devdocs/FEValues.md is true + # If changes are made that makes the following tests fails, + # the devdocs should be updated accordingly. + for op in (:shape_value, :shape_gradient, :getnquadpoints, :getnbasefunctions, :geometric_value, :getngeobasefunctions) + @eval Ferrite.$op(cv::TestCustomCellValues, args...; kwargs...) = Ferrite.$op(cv.cv, args...; kwargs...) + end + ip = Lagrange{RefQuadrilateral, 1}()^2 + qr = QuadratureRule{RefQuadrilateral}(2) + cv = CellValues(qr, ip) + grid = generate_grid(Quadrilateral, (1, 1)) + x = getcoordinates(grid, 1) + cell = getcells(grid, 1) + reinit!(cv, cell, x) + ae = rand(getnbasefunctions(cv)) + q_point = rand(1:getnquadpoints(cv)) + cv_custom = TestCustomCellValues(cv) + for fun in ( + function_value, function_gradient, + function_divergence, function_symmetric_gradient, function_curl, + ) + @test fun(cv_custom, q_point, ae) == fun(cv, q_point, ae) + end + @test spatial_coordinate(cv_custom, q_point, x) == spatial_coordinate(cv, q_point, x) end - @test spatial_coordinate(cv_custom, q_point, x) == spatial_coordinate(cv, q_point, x) end -end end # of testset diff --git a/test/test_collectionsofviews.jl b/test/test_collectionsofviews.jl index 4bca3ff5a2..4cf2ee6692 100644 --- a/test/test_collectionsofviews.jl +++ b/test/test_collectionsofviews.jl @@ -3,7 +3,7 @@ test_ints = rand(0:99, 100) # Create for 3 different sizehints aovs = map([20, 1, 100]) do sh - Ferrite.ArrayOfVectorViews(Int[], (10,); sizehint=sh) do buf + Ferrite.ArrayOfVectorViews(Int[], (10,); sizehint = sh) do buf for v in test_ints idx = 1 + v ÷ 10 Ferrite.push_at_index!(buf, v, idx) @@ -12,12 +12,12 @@ end # Check correct values for the first one for (idx, v) in enumerate(aovs[1]) - interval = (10 * (idx-1)):(10 * idx - 1) + interval = (10 * (idx - 1)):(10 * idx - 1) @test all(x -> x ∈ interval, v) @test count(x -> x ∈ interval, test_ints) == length(v) end for aov in aovs - @test sum(length, aov; init=0) == length(test_ints) + @test sum(length, aov; init = 0) == length(test_ints) end # Check that the result is independent of sizehint for idx in eachindex(aovs[1]) @@ -29,16 +29,16 @@ # Create an array with random tuple containing and place the tuples # according to the values. Check for 2d and 3d arrays. for N in 2:3 - tvals = [ntuple(i->rand(0:9), N) for _ in 1:1000] - aov = Ferrite.ArrayOfVectorViews(NTuple{N, Int}[], (5,5,5)[1:N]; sizehint=10) do buf + tvals = [ntuple(i -> rand(0:9), N) for _ in 1:1000] + aov = Ferrite.ArrayOfVectorViews(NTuple{N, Int}[], (5, 5, 5)[1:N]; sizehint = 10) do buf for v in tvals idx = 1 .+ v .÷ 2 Ferrite.push_at_index!(buf, v, idx...) end end - @test sum(length, aov; init=0) == length(tvals) + @test sum(length, aov; init = 0) == length(tvals) for (idx, v) in pairs(aov) - intervals = map(i -> (2 * (i-1)):(2 * i - 1), idx.I) + intervals = map(i -> (2 * (i - 1)):(2 * i - 1), idx.I) @test all(x -> all(map((z, r) -> z ∈ r, x, intervals)), v) @test count(x -> all(map((z, r) -> z ∈ r, x, intervals)), tvals) == length(v) end diff --git a/test/test_constraints.jl b/test/test_constraints.jl index 1600d87288..c9c1c732da 100644 --- a/test/test_constraints.jl +++ b/test/test_constraints.jl @@ -5,11 +5,11 @@ Γ = getfacetset(grid, "left") face_map = collect_periodic_facets(grid, "left", "right") dh = DofHandler(grid) - add!(dh, :s, Lagrange{RefTriangle,1}()) - add!(dh, :v, Lagrange{RefTriangle,1}()^2) - add!(dh, :z, DiscontinuousLagrange{RefTriangle,0}()) - add!(dh, :sd, DiscontinuousLagrange{RefTriangle,1}()) - add!(dh, :vd, DiscontinuousLagrange{RefTriangle,1}()^2) + add!(dh, :s, Lagrange{RefTriangle, 1}()) + add!(dh, :v, Lagrange{RefTriangle, 1}()^2) + add!(dh, :z, DiscontinuousLagrange{RefTriangle, 0}()) + add!(dh, :sd, DiscontinuousLagrange{RefTriangle, 1}()) + add!(dh, :vd, DiscontinuousLagrange{RefTriangle, 1}()^2) close!(dh) ch = ConstraintHandler(dh) # Dirichlet @@ -47,7 +47,7 @@ pdbc = PeriodicDirichlet(:s, face_map) add!(ConstraintHandler(dh), pdbc) @test pdbc.components == [1] - pdbc = PeriodicDirichlet(:s, face_map, (x,t) -> 0) + pdbc = PeriodicDirichlet(:s, face_map, (x, t) -> 0) add!(ConstraintHandler(dh), pdbc) @test pdbc.components == [1] pdbc = PeriodicDirichlet(:s, face_map, [1]) @@ -61,13 +61,13 @@ pdbc = PeriodicDirichlet(:v, face_map) add!(ConstraintHandler(dh), pdbc) @test pdbc.components == [1, 2] - pdbc = PeriodicDirichlet(:v, face_map, (x, t) -> 0*x) + pdbc = PeriodicDirichlet(:v, face_map, (x, t) -> 0 * x) add!(ConstraintHandler(dh), pdbc) @test pdbc.components == [1, 2] - pdbc = PeriodicDirichlet(:v, face_map, rand(2,2)) + pdbc = PeriodicDirichlet(:v, face_map, rand(2, 2)) add!(ConstraintHandler(dh), pdbc) @test pdbc.components == [1, 2] - pdbc = PeriodicDirichlet(:v, face_map, rand(1,1)) + pdbc = PeriodicDirichlet(:v, face_map, rand(1, 1)) @test_throws ErrorException("size of rotation matrix does not match the number of components") add!(ConstraintHandler(dh), pdbc) pdbc = PeriodicDirichlet(:v, face_map, (x, t) -> 0, [1]) add!(ConstraintHandler(dh), pdbc) @@ -80,10 +80,10 @@ end @testset "node bc" begin grid = generate_grid(Triangle, (1, 1)) - addnodeset!(grid, "nodeset", x-> x[2] == -1 || x[1] == -1) + addnodeset!(grid, "nodeset", x -> x[2] == -1 || x[1] == -1) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefTriangle,1}()^2) - add!(dh, :p, Lagrange{RefTriangle,1}()) + add!(dh, :u, Lagrange{RefTriangle, 1}()^2) + add!(dh, :p, Lagrange{RefTriangle, 1}()) close!(dh) ch = ConstraintHandler(dh) dbc1 = Dirichlet(:u, getnodeset(grid, "nodeset"), (x, t) -> x, [1, 2]) @@ -98,94 +98,96 @@ end @test ch.inhomogeneities == [-1, -1, 1, -1, -1, 1, 0, 0, 0] ## test node bc with subsets - dim = 2 - mesh = generate_grid(QuadraticQuadrilateral, (2,1)) - addcellset!(mesh, "set1", Set(1)) - addcellset!(mesh, "set2", Set(2)) - addnodeset!(mesh, "bottom", Set(1:5)) - - dh = DofHandler(mesh) - - ip_quadratic = Lagrange{RefQuadrilateral, 2}()^dim - ip_linear = Lagrange{RefQuadrilateral, 1}() - sdh1 = SubDofHandler(dh, getcellset(mesh, "set1")) - add!(sdh1, :u, ip_quadratic) - add!(sdh1, :c, ip_linear) - sdh2 = SubDofHandler(dh, getcellset(mesh, "set2")) - add!(sdh2, :u, ip_quadratic) - close!(dh) - - ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getnodeset(mesh, "bottom"), (x,t)->1.0, 1)) - add!(ch, Dirichlet(:c, getnodeset(mesh, "bottom"), (x,t)->2.0, 1)) - close!(ch) - update!(ch) - - @test ch.prescribed_dofs == [1,3,9,19,20,23,27] - @test ch.inhomogeneities == [1.0, 1.0, 1.0, 2.0, 2.0, 1.0, 1.0] - - ## Fields on subdomain - dim = 2 - mesh = generate_grid(Quadrilateral, (2,1)) - addcellset!(mesh, "set1", Set(1)) - addcellset!(mesh, "set2", Set(2)) - - ip = Lagrange{RefQuadrilateral, 1}() - - dh = DofHandler(mesh) - sdh1 = SubDofHandler(dh, getcellset(mesh, "set1")) - add!(sdh1, :u, ip^dim) - sdh2 = SubDofHandler(dh, getcellset(mesh, "set2")) - add!(sdh2, :u, ip^dim) - add!(sdh2, :c, ip) - close!(dh) - - ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(mesh, "bottom"), (x,t)->1.0, 1)) - add!(ch, Dirichlet(:c, getfacetset(mesh, "bottom"), (x,t)->2.0, 1)) - close!(ch) - update!(ch) - - @test ch.prescribed_dofs == [1,3,9,13,14] - @test ch.inhomogeneities == [1.0, 1.0, 1.0, 2.0, 2.0] + dim = 2 + mesh = generate_grid(QuadraticQuadrilateral, (2, 1)) + addcellset!(mesh, "set1", Set(1)) + addcellset!(mesh, "set2", Set(2)) + addnodeset!(mesh, "bottom", Set(1:5)) + + dh = DofHandler(mesh) + + ip_quadratic = Lagrange{RefQuadrilateral, 2}()^dim + ip_linear = Lagrange{RefQuadrilateral, 1}() + sdh1 = SubDofHandler(dh, getcellset(mesh, "set1")) + add!(sdh1, :u, ip_quadratic) + add!(sdh1, :c, ip_linear) + sdh2 = SubDofHandler(dh, getcellset(mesh, "set2")) + add!(sdh2, :u, ip_quadratic) + close!(dh) + + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getnodeset(mesh, "bottom"), (x, t) -> 1.0, 1)) + add!(ch, Dirichlet(:c, getnodeset(mesh, "bottom"), (x, t) -> 2.0, 1)) + close!(ch) + update!(ch) + + @test ch.prescribed_dofs == [1, 3, 9, 19, 20, 23, 27] + @test ch.inhomogeneities == [1.0, 1.0, 1.0, 2.0, 2.0, 1.0, 1.0] + + ## Fields on subdomain + dim = 2 + mesh = generate_grid(Quadrilateral, (2, 1)) + addcellset!(mesh, "set1", Set(1)) + addcellset!(mesh, "set2", Set(2)) + + ip = Lagrange{RefQuadrilateral, 1}() + + dh = DofHandler(mesh) + sdh1 = SubDofHandler(dh, getcellset(mesh, "set1")) + add!(sdh1, :u, ip^dim) + sdh2 = SubDofHandler(dh, getcellset(mesh, "set2")) + add!(sdh2, :u, ip^dim) + add!(sdh2, :c, ip) + close!(dh) + + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getfacetset(mesh, "bottom"), (x, t) -> 1.0, 1)) + add!(ch, Dirichlet(:c, getfacetset(mesh, "bottom"), (x, t) -> 2.0, 1)) + close!(ch) + update!(ch) + + @test ch.prescribed_dofs == [1, 3, 9, 13, 14] + @test ch.inhomogeneities == [1.0, 1.0, 1.0, 2.0, 2.0] end @testset "edge bc" begin grid = generate_grid(Hexahedron, (1, 1, 1)) - edge = Ferrite.create_edgeset(grid, x-> x[1] ≈ -1.0 && x[3] ≈ -1.0) + edge = Ferrite.create_edgeset(grid, x -> x[1] ≈ -1.0 && x[3] ≈ -1.0) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefHexahedron,1}()^3) - add!(dh, :p, Lagrange{RefHexahedron,1}()) + add!(dh, :u, Lagrange{RefHexahedron, 1}()^3) + add!(dh, :p, Lagrange{RefHexahedron, 1}()) close!(dh) ch = ConstraintHandler(dh) - dbc1 = Dirichlet(:u, edge, (x,t) -> x, [1, 2, 3]) + dbc1 = Dirichlet(:u, edge, (x, t) -> x, [1, 2, 3]) add!(ch, dbc1) close!(ch) update!(ch) - @test ch.prescribed_dofs == [1,2,3,10,11,12] + @test ch.prescribed_dofs == [1, 2, 3, 10, 11, 12] @test ch.inhomogeneities == [-1.0, -1.0, -1.0, -1.0, 1.0, -1.0] #Shell mesh edge bcs - nodes = [Node{3,Float64}(Vec(0.0,0.0,0.0)), Node{3,Float64}(Vec(1.0,0.0,0.0)), - Node{3,Float64}(Vec(1.0,1.0,0.0)), Node{3,Float64}(Vec(0.0,1.0,0.0)), - Node{3,Float64}(Vec(2.0,0.0,0.0)), Node{3,Float64}(Vec(2.0,2.0,0.0))] + nodes = [ + Node{3, Float64}(Vec(0.0, 0.0, 0.0)), Node{3, Float64}(Vec(1.0, 0.0, 0.0)), + Node{3, Float64}(Vec(1.0, 1.0, 0.0)), Node{3, Float64}(Vec(0.0, 1.0, 0.0)), + Node{3, Float64}(Vec(2.0, 0.0, 0.0)), Node{3, Float64}(Vec(2.0, 2.0, 0.0)), + ] - cells = [Quadrilateral((1,2,3,4)), Quadrilateral((2,5,6,3))] - grid = Grid(cells,nodes) + cells = [Quadrilateral((1, 2, 3, 4)), Quadrilateral((2, 5, 6, 3))] + grid = Grid(cells, nodes) #3d quad with 1st order 2d interpolation dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefQuadrilateral,2}()) - add!(dh, :θ, Lagrange{RefQuadrilateral,2}()) + add!(dh, :u, Lagrange{RefQuadrilateral, 2}()) + add!(dh, :θ, Lagrange{RefQuadrilateral, 2}()) close!(dh) edge = Ferrite.create_edgeset(grid, x -> x[2] ≈ 0.0) #bottom edge ch = ConstraintHandler(dh) - dbc1 = Dirichlet(:θ, edge, (x,t) -> (0.0,), [1]) + dbc1 = Dirichlet(:θ, edge, (x, t) -> (0.0,), [1]) add!(ch, dbc1) close!(ch) update!(ch) @@ -195,45 +197,47 @@ end @testset "discontinuous ip constraints" begin grid = generate_grid(Hexahedron, (1, 1, 1)) - bottom_edge = Ferrite.create_edgeset(grid, x-> x[3] ≈ -1.0) + bottom_edge = Ferrite.create_edgeset(grid, x -> x[3] ≈ -1.0) dh = DofHandler(grid) - add!(dh, :u, DiscontinuousLagrange{RefHexahedron,1}()^3) - add!(dh, :p, DiscontinuousLagrange{RefHexahedron,1}()) + add!(dh, :u, DiscontinuousLagrange{RefHexahedron, 1}()^3) + add!(dh, :p, DiscontinuousLagrange{RefHexahedron, 1}()) close!(dh) face_ch = ConstraintHandler(dh) - face_dbc = Dirichlet(:u, getfacetset(grid, "bottom"), (x,t) -> x, [1, 2, 3]) + face_dbc = Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> x, [1, 2, 3]) add!(face_ch, face_dbc) close!(face_ch) update!(face_ch) edge_ch = ConstraintHandler(dh) - edge_dbc = Dirichlet(:u, bottom_edge, (x,t) -> x, [1, 2, 3]) + edge_dbc = Dirichlet(:u, bottom_edge, (x, t) -> x, [1, 2, 3]) add!(edge_ch, edge_dbc) close!(edge_ch) update!(edge_ch) @test edge_ch.prescribed_dofs == face_ch.prescribed_dofs == collect(1:12) - @test edge_ch.inhomogeneities == face_ch.inhomogeneities == reduce(vcat, getcoordinates(grid,1)[1:4]) + @test edge_ch.inhomogeneities == face_ch.inhomogeneities == reduce(vcat, getcoordinates(grid, 1)[1:4]) # This can be merged with the continuous test or removed. # Shell mesh edge bcs - nodes = [Node{3,Float64}(Vec(0.0,0.0,0.0)), Node{3,Float64}(Vec(1.0,0.0,0.0)), - Node{3,Float64}(Vec(1.0,1.0,0.0)), Node{3,Float64}(Vec(0.0,1.0,0.0)), - Node{3,Float64}(Vec(2.0,0.0,0.0)), Node{3,Float64}(Vec(2.0,2.0,0.0))] + nodes = [ + Node{3, Float64}(Vec(0.0, 0.0, 0.0)), Node{3, Float64}(Vec(1.0, 0.0, 0.0)), + Node{3, Float64}(Vec(1.0, 1.0, 0.0)), Node{3, Float64}(Vec(0.0, 1.0, 0.0)), + Node{3, Float64}(Vec(2.0, 0.0, 0.0)), Node{3, Float64}(Vec(2.0, 2.0, 0.0)), + ] - cells = [Quadrilateral((1,2,3,4)), Quadrilateral((2,5,6,3))] - grid = Grid(cells,nodes) + cells = [Quadrilateral((1, 2, 3, 4)), Quadrilateral((2, 5, 6, 3))] + grid = Grid(cells, nodes) # 3d quad with 1st order 2d interpolation dh = DofHandler(grid) - add!(dh, :u, DiscontinuousLagrange{RefQuadrilateral,2}()) - add!(dh, :θ, DiscontinuousLagrange{RefQuadrilateral,2}()) + add!(dh, :u, DiscontinuousLagrange{RefQuadrilateral, 2}()) + add!(dh, :θ, DiscontinuousLagrange{RefQuadrilateral, 2}()) close!(dh) bottom_edge = Ferrite.create_edgeset(grid, x -> x[2] ≈ 0.0) edge_ch = ConstraintHandler(dh) - edge_dbc = Dirichlet(:θ, bottom_edge, (x,t) -> (0.0,), [1]) + edge_dbc = Dirichlet(:θ, bottom_edge, (x, t) -> (0.0,), [1]) add!(edge_ch, edge_dbc) close!(edge_ch) update!(edge_ch) @@ -317,25 +321,29 @@ end grid = generate_grid(Line, (10,)) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefLine,1}()) + add!(dh, :u, Lagrange{RefLine, 1}()) close!(dh) test_acs = [ # Simple homogeneous constraint [AffineConstraint(4, [(7 => 1.0)], 0.0)], # Two dofs and inhomogeneity - [AffineConstraint(2, [(5 => 1.0), (6 =>2.0)], 1.0)], + [AffineConstraint(2, [(5 => 1.0), (6 => 2.0)], 1.0)], # Two linear constraints - [AffineConstraint(2, [9=>1.0], 0.0), - AffineConstraint(3, [9=>1.0], 0.0)], + [ + AffineConstraint(2, [9 => 1.0], 0.0), + AffineConstraint(3, [9 => 1.0], 0.0), + ], # - [AffineConstraint(2, [7=>3.0, 8=>1.0], -1.0), - AffineConstraint(4, [9=>-1.0], 2.0)] + [ + AffineConstraint(2, [7 => 3.0, 8 => 1.0], -1.0), + AffineConstraint(4, [9 => -1.0], 2.0), + ], ] for acs in test_acs ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x,t)->0.0)) + add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0.0)) for lc in acs add!(ch, lc) end @@ -360,7 +368,7 @@ end copies = (K = copy(K), f1 = copy(f), f2 = copy(f)) # Solve by actually condensing the matrix - ff = C' * (f - K * g) + ff = C' * (f - K * g) KK = C' * K * C _aa = KK \ ff aa = C * _aa + g @@ -388,9 +396,9 @@ end # Test nonlinear solution procedure (on linear problem) with affine constraints # using standard assembly (i.e. not local condensation) @testset "nonlinear" begin - params = (k=1.0, f=1.0, a=1.0, b=0.2, tol=1e-10, maxiter=2) - grid = generate_grid(Line, (2,)); addfacetset!(grid, "center", x->x[1]≈0.0) - dh = DofHandler(grid); add!(dh, :u, Lagrange{RefLine,1}()); close!(dh) + params = (k = 1.0, f = 1.0, a = 1.0, b = 0.2, tol = 1.0e-10, maxiter = 2) + grid = generate_grid(Line, (2,)); addfacetset!(grid, "center", x -> x[1] ≈ 0.0) + dh = DofHandler(grid); add!(dh, :u, Lagrange{RefLine, 1}()); close!(dh) function doassemble!(K, r, dh, a, params) # Spring elements @@ -400,15 +408,15 @@ end assembler = start_assemble(K, r) for cell in CellIterator(dh) ae = a[celldofs(cell)] - re = Ke*ae + re = Ke * ae assemble!(assembler, celldofs(cell), Ke, re) end r[3] -= params.f # Force on the main dof (right side) end ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(grid, "center"), (x,t)->Vec{1}((0.0,)))) - add!(ch, AffineConstraint(1, [3=>params.a], params.b)) + add!(ch, Dirichlet(:u, getfacetset(grid, "center"), (x, t) -> Vec{1}((0.0,)))) + add!(ch, AffineConstraint(1, [3 => params.a], params.b)) close!(ch) K = allocate_matrix(dh, ch) @@ -417,11 +425,11 @@ end # Nonlinear solution apply!(a, ch) - for niter = 0:params.maxiter + for niter in 0:params.maxiter doassemble!(K, r, dh, a, params) apply_zero!(K, r, ch) norm(r) < params.tol && break - Δa = -K\r + Δa = -K \ r apply_zero!(Δa, ch) a .+= Δa end @@ -433,22 +441,22 @@ end doassemble!(K, r, dh, a, params) f = zero(r); f[end] = params.f apply!(K, f, ch) - a_linear = K\f + a_linear = K \ f apply!(a_linear, ch) # Analytical comparison - a3 = (params.f/params.k - params.b)/(1 + params.a) - a_analytical = [params.a*a3+params.b; 0.0; a3] + a3 = (params.f / params.k - params.b) / (1 + params.a) + a_analytical = [params.a * a3 + params.b; 0.0; a3] @test a_linear ≈ a_analytical @test a_nonlinear ≈ a_analytical end end # Rotate -pi/2 around dir -function rotpio2(v, dir=3) +function rotpio2(v, dir = 3) v3 = Vec{3}(i -> i <= length(v) ? v[i] : 0.0) z = Vec{3}(i -> i == dir ? 1.0 : 0.0) - rv = Tensors.rotate(v3, z, -pi/2) + rv = Tensors.rotate(v3, z, -pi / 2) return typeof(v)(i -> rv[i]) end @@ -470,7 +478,7 @@ end # │ │ │ # └───────┴───────┘ # 1 1 - for grid in (generate_grid(Quadrilateral, (2, 2)), generate_grid(QuadraticQuadrilateral, (2, 2))) + for grid in (generate_grid(Quadrilateral, (2, 2)), generate_grid(QuadraticQuadrilateral, (2, 2))) correct_map = [ PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(3, 3), 0x00, true), PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(4, 3), 0x00, true), @@ -483,7 +491,8 @@ end @test issetequal(face_map, correct_map) # Brute force path with boundary info - face_map = collect_periodic_facets(grid, + face_map = collect_periodic_facets( + grid, union( getfacetset(grid, "left"), getfacetset(grid, "bottom"), @@ -496,7 +505,8 @@ end @test issetequal(face_map, correct_map) # Brute force, keeping the mirror/image ordering - face_map = collect_periodic_facets(grid, + face_map = collect_periodic_facets( + grid, union( getfacetset(grid, "right"), getfacetset(grid, "top"), @@ -516,19 +526,23 @@ end # More advanced transformation by rotation face_map = collect_periodic_facets(grid, "left", "bottom", rotpio2) collect_periodic_facets!(face_map, grid, "right", "top", rotpio2) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(3, 4), FacetIndex(1, 1), 0x00, false), - PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(2, 1), 0x00, false), - PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(4, 3), 0x00, false), - PeriodicFacetPair(FacetIndex(4, 2), FacetIndex(3, 3), 0x00, false), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(3, 4), FacetIndex(1, 1), 0x00, false), + PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(2, 1), 0x00, false), + PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(4, 3), 0x00, false), + PeriodicFacetPair(FacetIndex(4, 2), FacetIndex(3, 3), 0x00, false), + ] + ) # Rotate and translate face_map = collect_periodic_facets(grid, "bottom", "left", x -> rotpio2(x) - Vec{2}((0.0, 2.0))) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(1, 4), 0x00, true), - PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(3, 4), 0x00, true), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(1, 4), 0x00, true), + PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(3, 4), 0x00, true), + ] + ) end #################################################################### @@ -560,7 +574,8 @@ end @test issetequal(face_map, correct_map) # Brute force path with boundary info - face_map = collect_periodic_facets(grid, + face_map = collect_periodic_facets( + grid, union( getfacetset(grid, "left"), getfacetset(grid, "bottom"), @@ -573,7 +588,8 @@ end @test issetequal(face_map, correct_map) # Brute force, keeping the mirror/image ordering - face_map = collect_periodic_facets(grid, + face_map = collect_periodic_facets( + grid, union( getfacetset(grid, "right"), getfacetset(grid, "top"), @@ -593,19 +609,23 @@ end # More advanced transformation by rotation face_map = collect_periodic_facets(grid, "left", "bottom", rotpio2) collect_periodic_facets!(face_map, grid, "right", "top", rotpio2) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(5, 3), FacetIndex(1, 1), 0x00, false), - PeriodicFacetPair(FacetIndex(1, 3), FacetIndex(3, 1), 0x00, false), - PeriodicFacetPair(FacetIndex(4, 1), FacetIndex(8, 2), 0x00, false), - PeriodicFacetPair(FacetIndex(8, 1), FacetIndex(6, 2), 0x00, false), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(5, 3), FacetIndex(1, 1), 0x00, false), + PeriodicFacetPair(FacetIndex(1, 3), FacetIndex(3, 1), 0x00, false), + PeriodicFacetPair(FacetIndex(4, 1), FacetIndex(8, 2), 0x00, false), + PeriodicFacetPair(FacetIndex(8, 1), FacetIndex(6, 2), 0x00, false), + ] + ) # Rotate and translate face_map = collect_periodic_facets(grid, "bottom", "left", x -> rotpio2(x) - Vec{2}((0.0, 2.0))) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(1, 3), 0x00, true), - PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(5, 3), 0x00, true), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(1, 3), 0x00, true), + PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(5, 3), 0x00, true), + ] + ) end #################################################################### @@ -613,58 +633,66 @@ end # 3D hex grids grid = generate_grid(Hexahedron, (1, 1, 1)) face_map = collect_periodic_facets(grid) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(1, 6), 0x00, true), - PeriodicFacetPair(FacetIndex(1, 2), FacetIndex(1, 4), 0x03, true), - PeriodicFacetPair(FacetIndex(1, 5), FacetIndex(1, 3), 0x00, true), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(1, 6), 0x00, true), + PeriodicFacetPair(FacetIndex(1, 2), FacetIndex(1, 4), 0x03, true), + PeriodicFacetPair(FacetIndex(1, 5), FacetIndex(1, 3), 0x00, true), + ] + ) grid = generate_grid(Hexahedron, (2, 2, 2)) face_map = collect_periodic_facets(grid, "left", "right", x -> x - Vec{3}((2.0, 0.0, 0.0))) collect_periodic_facets!(face_map, grid, "bottom", "top") collect_periodic_facets!(face_map, grid, "front", "back") - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 5), FacetIndex(2, 3), 0x00, true), - PeriodicFacetPair(FacetIndex(3, 5), FacetIndex(4, 3), 0x00, true), - PeriodicFacetPair(FacetIndex(5, 5), FacetIndex(6, 3), 0x00, true), - PeriodicFacetPair(FacetIndex(7, 5), FacetIndex(8, 3), 0x00, true), - PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(5, 6), 0x00, true), - PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(6, 6), 0x00, true), - PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(7, 6), 0x00, true), - PeriodicFacetPair(FacetIndex(4, 1), FacetIndex(8, 6), 0x00, true), - PeriodicFacetPair(FacetIndex(1, 2), FacetIndex(3, 4), 0x03, true), - PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(4, 4), 0x03, true), - PeriodicFacetPair(FacetIndex(5, 2), FacetIndex(7, 4), 0x03, true), - PeriodicFacetPair(FacetIndex(6, 2), FacetIndex(8, 4), 0x03, true), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 5), FacetIndex(2, 3), 0x00, true), + PeriodicFacetPair(FacetIndex(3, 5), FacetIndex(4, 3), 0x00, true), + PeriodicFacetPair(FacetIndex(5, 5), FacetIndex(6, 3), 0x00, true), + PeriodicFacetPair(FacetIndex(7, 5), FacetIndex(8, 3), 0x00, true), + PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(5, 6), 0x00, true), + PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(6, 6), 0x00, true), + PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(7, 6), 0x00, true), + PeriodicFacetPair(FacetIndex(4, 1), FacetIndex(8, 6), 0x00, true), + PeriodicFacetPair(FacetIndex(1, 2), FacetIndex(3, 4), 0x03, true), + PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(4, 4), 0x03, true), + PeriodicFacetPair(FacetIndex(5, 2), FacetIndex(7, 4), 0x03, true), + PeriodicFacetPair(FacetIndex(6, 2), FacetIndex(8, 4), 0x03, true), + ] + ) # Rotation grid = generate_grid(Hexahedron, (2, 2, 2)) face_map = collect_periodic_facets(grid, "left", "front", rotpio2) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 5), FacetIndex(2, 2), 0x03, false), - PeriodicFacetPair(FacetIndex(3, 5), FacetIndex(1, 2), 0x03, false), - PeriodicFacetPair(FacetIndex(5, 5), FacetIndex(6, 2), 0x03, false), - PeriodicFacetPair(FacetIndex(7, 5), FacetIndex(5, 2), 0x03, false), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 5), FacetIndex(2, 2), 0x03, false), + PeriodicFacetPair(FacetIndex(3, 5), FacetIndex(1, 2), 0x03, false), + PeriodicFacetPair(FacetIndex(5, 5), FacetIndex(6, 2), 0x03, false), + PeriodicFacetPair(FacetIndex(7, 5), FacetIndex(5, 2), 0x03, false), + ] + ) # Rotation and translation grid = generate_grid(Hexahedron, (2, 2, 2)) face_map = collect_periodic_facets(grid, "front", "left", x -> rotpio2(x) - Vec{3}((0.0, 2.0, 0.0))) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 2), FacetIndex(1, 5), 0x00, true), - PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(3, 5), 0x00, true), - PeriodicFacetPair(FacetIndex(5, 2), FacetIndex(5, 5), 0x00, true), - PeriodicFacetPair(FacetIndex(6, 2), FacetIndex(7, 5), 0x00, true), - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 2), FacetIndex(1, 5), 0x00, true), + PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(3, 5), 0x00, true), + PeriodicFacetPair(FacetIndex(5, 2), FacetIndex(5, 5), 0x00, true), + PeriodicFacetPair(FacetIndex(6, 2), FacetIndex(7, 5), 0x00, true), + ] + ) # Test with keyword tol grid = generate_grid(Hexahedron, (2, 2, 2)) - face_map = collect_periodic_facets(grid, "bottom", "top") - face_map_TOL = collect_periodic_facets(grid, "bottom", "top"; tol=1e-10) + face_map = collect_periodic_facets(grid, "bottom", "top") + face_map_TOL = collect_periodic_facets(grid, "bottom", "top"; tol = 1.0e-10) @test face_map == face_map_TOL collect_periodic_facets!(face_map, grid, "right", "left") - collect_periodic_facets!(face_map_TOL, grid, "right", "left"; tol=1e-10) + collect_periodic_facets!(face_map_TOL, grid, "right", "left"; tol = 1.0e-10) @test face_map == face_map_TOL #################################################################### @@ -672,69 +700,77 @@ end # 3D tetra grid grid = generate_grid(Tetrahedron, (1, 1, 1)) face_map = collect_periodic_facets(grid) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(4, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(6, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(3, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(5, 1), FacetIndex(4, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(5, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(6, 3), 0x00, true) - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(4, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(6, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(3, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(5, 1), FacetIndex(4, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(5, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(6, 3), 0x00, true) + ] + ) grid = generate_grid(Tetrahedron, (2, 2, 2)) face_map = collect_periodic_facets(grid, "left", "right", x -> x - Vec{3}((2.0, 0.0, 0.0))) collect_periodic_facets!(face_map, grid, "bottom", "top") collect_periodic_facets!(face_map, grid, "front", "back") - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(10, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(12, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(13, 4), FacetIndex(22, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(14, 2), FacetIndex(24, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(25, 4), FacetIndex(34, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(26, 2), FacetIndex(36, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(37, 4), FacetIndex(46, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(38, 2), FacetIndex(48, 1), 0x00, true) - PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(15, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(5, 1), FacetIndex(16, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(8, 1), FacetIndex(21, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(11, 1), FacetIndex(22, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(26, 1), FacetIndex(39, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(29, 1), FacetIndex(40, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(32, 1), FacetIndex(45, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(35, 1), FacetIndex(46, 3), 0x02, true) - PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(29, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(30, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(7, 1), FacetIndex(35, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(9, 1), FacetIndex(36, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(13, 1), FacetIndex(41, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(15, 1), FacetIndex(42, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(19, 1), FacetIndex(47, 3), 0x00, true) - PeriodicFacetPair(FacetIndex(21, 1), FacetIndex(48, 3), 0x00, true) - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(10, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(12, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(13, 4), FacetIndex(22, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(14, 2), FacetIndex(24, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(25, 4), FacetIndex(34, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(26, 2), FacetIndex(36, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(37, 4), FacetIndex(46, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(38, 2), FacetIndex(48, 1), 0x00, true) + PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(15, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(5, 1), FacetIndex(16, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(8, 1), FacetIndex(21, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(11, 1), FacetIndex(22, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(26, 1), FacetIndex(39, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(29, 1), FacetIndex(40, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(32, 1), FacetIndex(45, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(35, 1), FacetIndex(46, 3), 0x02, true) + PeriodicFacetPair(FacetIndex(1, 1), FacetIndex(29, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(3, 1), FacetIndex(30, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(7, 1), FacetIndex(35, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(9, 1), FacetIndex(36, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(13, 1), FacetIndex(41, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(15, 1), FacetIndex(42, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(19, 1), FacetIndex(47, 3), 0x00, true) + PeriodicFacetPair(FacetIndex(21, 1), FacetIndex(48, 3), 0x00, true) + ] + ) # Rotation grid = generate_grid(Tetrahedron, (1, 1, 1)) face_map = collect_periodic_facets(grid, "left", "front", rotpio2) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(2, 1), 0x02, false) - PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(5, 1), 0x00, false) - ]) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(1, 4), FacetIndex(2, 1), 0x02, false) + PeriodicFacetPair(FacetIndex(2, 2), FacetIndex(5, 1), 0x00, false) + ] + ) # Rotation and translation grid = generate_grid(Tetrahedron, (1, 1, 1)) - face_map = collect_periodic_facets(grid, "front", "left", x -> rotpio2(rotate(x, Vec{3}((1., 0., 0.)), 3pi/2)) - Vec{3}((0.0, 2.0, 0.0))) - @test issetequal(face_map, [ - PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(1, 4), 0x01, true) - PeriodicFacetPair(FacetIndex(5, 1), FacetIndex(2, 2), 0x01, true) - ]) + face_map = collect_periodic_facets(grid, "front", "left", x -> rotpio2(rotate(x, Vec{3}((1.0, 0.0, 0.0)), 3pi / 2)) - Vec{3}((0.0, 2.0, 0.0))) + @test issetequal( + face_map, [ + PeriodicFacetPair(FacetIndex(2, 1), FacetIndex(1, 4), 0x01, true) + PeriodicFacetPair(FacetIndex(5, 1), FacetIndex(2, 2), 0x01, true) + ] + ) end # testset @testset "periodic bc: dof mapping" begin grid = generate_grid(Quadrilateral, (2, 2)) function get_dof_map(ch) - m = Dict{Int,Any}() - for (mdof,b,entries) in zip(ch.prescribed_dofs, ch.inhomogeneities, ch.dofcoefficients) + m = Dict{Int, Any}() + for (mdof, b, entries) in zip(ch.prescribed_dofs, ch.inhomogeneities, ch.dofcoefficients) if entries !== nothing @test b == 0 if length(entries) == 1 @@ -778,14 +814,14 @@ end # testset # Scalar dh = DofHandler(grid) - add!(dh, :s, Lagrange{RefQuadrilateral,1}()) + add!(dh, :s, Lagrange{RefQuadrilateral, 1}()) close!(dh) ch = ConstraintHandler(dh) face_map = collect_periodic_facets(grid, "left", "right") collect_periodic_facets!(face_map, grid, "bottom", "top") pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 9, 2 => 7, 5 => 9, @@ -798,7 +834,7 @@ end # testset face_map = collect_periodic_facets(grid, "left", "bottom", rotpio2) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 8 => 5, # 8 -> 1 -> 5 4 => 2, 1 => 5, @@ -809,7 +845,7 @@ end # testset face_map = collect_periodic_facets(grid, "bottom", "left", x -> rotpio2(x) - Vec{2}((0.0, 2.0))) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( # 1 => 1, 2 => 4, 5 => 8, @@ -817,14 +853,14 @@ end # testset # Vector dh = DofHandler(grid) - add!(dh, :v, Lagrange{RefQuadrilateral,1}()^2) + add!(dh, :v, Lagrange{RefQuadrilateral, 1}()^2) close!(dh) ch = ConstraintHandler(dh) face_map = collect_periodic_facets(grid, "left", "right") collect_periodic_facets!(face_map, grid, "bottom", "top") pbc = PeriodicDirichlet(:v, face_map, [1, 2]) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 17, 2 => 18, 3 => 13, 4 => 14, 9 => 17, 10 => 18, @@ -835,7 +871,7 @@ end # testset ch = ConstraintHandler(dh) pbc = PeriodicDirichlet(:v, face_map, 2) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 2 => 18, 4 => 14, 10 => 18, @@ -848,7 +884,7 @@ end # testset ch = ConstraintHandler(dh) pbc = PeriodicDirichlet(:v, face_map, [1, 2]) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 15 => 9, # 15 -> 1 -> 9 16 => 10, # 16 -> 2 -> 10 7 => 3, @@ -860,10 +896,10 @@ end # testset # Rotation with dof rotation face_map = collect_periodic_facets(grid, "left", "bottom", rotpio2) ch = ConstraintHandler(dh) - pbc = PeriodicDirichlet(:v, face_map, rotation_tensor(-π/2), [1, 2]) + pbc = PeriodicDirichlet(:v, face_map, rotation_tensor(-π / 2), [1, 2]) add!(ch, pbc) dof_map = get_dof_map(ch) - correct_dof_map = Dict{Int,Any}( + correct_dof_map = Dict{Int, Any}( 15 => [9 => 0, 10 => 1], # 15 -> 2 -> 10 16 => [9 => -1, 10 => 0], # 16 -> -1 -> -9 7 => [3 => 0, 4 => 1], @@ -883,7 +919,7 @@ end # testset ch = ConstraintHandler(dh) pbc = PeriodicDirichlet(:v, face_map, [1, 2]) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( # 1 => 1, # 2 => 2, 3 => 7, @@ -903,14 +939,14 @@ end # testset # │ │ │ # 1───5───2───12──10 dh = DofHandler(grid) - add!(dh, :s, Lagrange{RefQuadrilateral,2}()) + add!(dh, :s, Lagrange{RefQuadrilateral, 2}()) close!(dh) ch = ConstraintHandler(dh) face_map = collect_periodic_facets(grid, "left", "right") collect_periodic_facets!(face_map, grid, "bottom", "top") pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 22, # 1 -> 10/17 -> 22 8 => 13, 4 => 11, @@ -927,7 +963,7 @@ end # testset face_map = collect_periodic_facets(grid, "left", "bottom", rotpio2) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 17 => 10, # 17 -> 1 -> 10 20 => 5, 4 => 2, @@ -940,7 +976,7 @@ end # testset face_map = collect_periodic_facets(grid, "bottom", "left", x -> rotpio2(x) - Vec{2}((0.0, 2.0))) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( # 1 => 1, 5 => 8, 2 => 4, @@ -963,15 +999,15 @@ end # testset # │ │ │ # 1,2────9,10───3,4───23,24──19,20 dh = DofHandler(grid) - add!(dh, :v, Lagrange{RefQuadrilateral,2}()^2) + add!(dh, :v, Lagrange{RefQuadrilateral, 2}()^2) close!(dh) ch = ConstraintHandler(dh) face_map = collect_periodic_facets(grid, "left", "bottom", rotpio2) - pbc = PeriodicDirichlet(:v, face_map, rotation_tensor(-π/2), [1, 2]) + pbc = PeriodicDirichlet(:v, face_map, rotation_tensor(-π / 2), [1, 2]) add!(ch, pbc) close!(ch) dof_map = get_dof_map(ch) - correct_dof_map = Dict{Int,Any}( + correct_dof_map = Dict{Int, Any}( 33 => [19 => 0, 20 => 1], # 33 -> 2 -> 20 34 => [19 => -1, 20 => 0], # 34 -> -1 -> -19 39 => [9 => 0, 10 => 1], @@ -994,8 +1030,8 @@ end # testset grid = generate_grid(Hexahedron, (1, 1, 1)) face_map = collect_periodic_facets(grid) dh = DofHandler(grid) - add!(dh, :s, Lagrange{RefHexahedron,1}()) - add!(dh, :v, Lagrange{RefHexahedron,1}()^2) + add!(dh, :s, Lagrange{RefHexahedron, 1}()) + add!(dh, :v, Lagrange{RefHexahedron, 1}()^2) close!(dh) ch = ConstraintHandler(dh) @@ -1003,7 +1039,7 @@ end # testset add!(ch, pbc) pbc = PeriodicDirichlet(:v, face_map, [1, 2]) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 7, 2 => 7, 3 => 7, 4 => 7, 5 => 7, 6 => 7, @@ -1023,7 +1059,7 @@ end # testset face_map = collect_periodic_facets(grid, "left", "front", rotpio2) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 2, 4 => 2, # 4 -> 1 -> 2 5 => 6, @@ -1034,7 +1070,7 @@ end # testset face_map = collect_periodic_facets(grid, "front", "left", x -> rotpio2(x) - Vec{3}((0.0, 2.0, 0.0))) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( # 1 => 1, # 5 => 5, 2 => 4, @@ -1044,56 +1080,56 @@ end # testset # Quadratic interpolation grid = generate_grid(Hexahedron, (5, 5, 5)) dh = DofHandler(grid) - add!(dh, :s, Lagrange{RefHexahedron,2}()) - add!(dh, :v, Lagrange{RefHexahedron,2}()^2) + add!(dh, :s, Lagrange{RefHexahedron, 2}()) + add!(dh, :v, Lagrange{RefHexahedron, 2}()^2) close!(dh) compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "left", "right")), - Dirichlet(:s, getfacetset(grid, "left"), (x, t) -> 0.), - Dirichlet(:s, getfacetset(grid, "right"), (x, t) -> 0.), + Dirichlet(:s, getfacetset(grid, "left"), (x, t) -> 0.0), + Dirichlet(:s, getfacetset(grid, "right"), (x, t) -> 0.0), ) compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "left", "right"), [1, 2]), - Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> [0., 0.], [1, 2]), - Dirichlet(:v, getfacetset(grid, "right"), (x, t) -> [0., 0.], [1, 2]), + Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> [0.0, 0.0], [1, 2]), + Dirichlet(:v, getfacetset(grid, "right"), (x, t) -> [0.0, 0.0], [1, 2]), ) compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "left", "right"), [2]), - Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> 0., [2]), - Dirichlet(:v, getfacetset(grid, "right"), (x, t) -> 0., [2]), + Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> 0.0, [2]), + Dirichlet(:v, getfacetset(grid, "right"), (x, t) -> 0.0, [2]), ) # 3D tetra scalar grid = generate_grid(Tetrahedron, (1, 1, 1)) dh = DofHandler(grid) - add!(dh, :s, Lagrange{RefTetrahedron,1}()) + add!(dh, :s, Lagrange{RefTetrahedron, 1}()) close!(dh) face_map = collect_periodic_facets(grid) ch = ConstraintHandler(dh) pbc = PeriodicDirichlet(:s, face_map) add!(ch, pbc) close!(ch) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 7, 2 => 7, 3 => 7, 4 => 7, 5 => 7, 6 => 7, 8 => 7, ) # 3D tetra vector grid = generate_grid(Tetrahedron, (2, 1, 1)) dh = DofHandler(grid) - add!(dh, :v, Lagrange{RefTetrahedron,1}()^2) + add!(dh, :v, Lagrange{RefTetrahedron, 1}()^2) close!(dh) face_map = collect_periodic_facets(grid, "left", "right") ch = ConstraintHandler(dh) pbc = PeriodicDirichlet(:v, face_map, [1, 2]) add!(ch, pbc) close!(ch) - @test get_dof_map(ch) == Dict{Int,Int}( + @test get_dof_map(ch) == Dict{Int, Int}( 1 => 17, 2 => 18, 9 => 23, @@ -1107,16 +1143,16 @@ end # testset # 3D hex vector with dof rotation grid = generate_grid(Hexahedron, (1, 1, 1)) dh = DofHandler(grid) - add!(dh, :v, Lagrange{RefHexahedron,1}()^3) + add!(dh, :v, Lagrange{RefHexahedron, 1}()^3) close!(dh) - rot = rotation_tensor(Vec{3}((0., 1., 0.)), π/2) + rot = rotation_tensor(Vec{3}((0.0, 1.0, 0.0)), π / 2) face_map = collect_periodic_facets(grid, "left", "bottom", x -> rot ⋅ x) ch = ConstraintHandler(dh) pbc = PeriodicDirichlet(:v, face_map, rot, [1, 2, 3]) add!(ch, pbc) close!(ch) dof_map = get_dof_map(ch) - correct_dof_map = Dict{Int,Any}( + correct_dof_map = Dict{Int, Any}( 1 => [4 => 0, 5 => 0, 6 => 1], 2 => [4 => 0, 5 => 1, 6 => 0], 3 => [4 => -1, 5 => 0, 6 => 0], @@ -1138,15 +1174,15 @@ end # testset end for (D, CT, IT) in ( - (2, Quadrilateral, Lagrange{RefQuadrilateral,1}()), - (2, Quadrilateral, Lagrange{RefQuadrilateral,2}()), - (2, Triangle, Lagrange{RefTriangle,1}()), - (2, Triangle, Lagrange{RefTriangle,2}()), - (3, Hexahedron, Lagrange{RefHexahedron,1}()), - (3, Hexahedron, Lagrange{RefHexahedron,2}()), - (3, Tetrahedron, Lagrange{RefTetrahedron,1}()), - (3, Tetrahedron, Lagrange{RefTetrahedron,2}()), - ) + (2, Quadrilateral, Lagrange{RefQuadrilateral, 1}()), + (2, Quadrilateral, Lagrange{RefQuadrilateral, 2}()), + (2, Triangle, Lagrange{RefTriangle, 1}()), + (2, Triangle, Lagrange{RefTriangle, 2}()), + (3, Hexahedron, Lagrange{RefHexahedron, 1}()), + (3, Hexahedron, Lagrange{RefHexahedron, 2}()), + (3, Tetrahedron, Lagrange{RefTetrahedron, 1}()), + (3, Tetrahedron, Lagrange{RefTetrahedron, 2}()), + ) grid = generate_grid(CT, ntuple(i -> 5, D)) dh = DofHandler(grid) add!(dh, :s, IT) @@ -1157,39 +1193,39 @@ end # testset compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "left", "right")), - Dirichlet(:s, getfacetset(grid, "left"), (x,t) -> 0), - Dirichlet(:s, getfacetset(grid, "right"), (x,t) -> 0), + Dirichlet(:s, getfacetset(grid, "left"), (x, t) -> 0), + Dirichlet(:s, getfacetset(grid, "right"), (x, t) -> 0), ) compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "right", "left")), - Dirichlet(:s, getfacetset(grid, "right"), (x,t) -> 0), - Dirichlet(:s, getfacetset(grid, "left"), (x,t) -> 0), + Dirichlet(:s, getfacetset(grid, "right"), (x, t) -> 0), + Dirichlet(:s, getfacetset(grid, "left"), (x, t) -> 0), ) compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "bottom", "top")), - Dirichlet(:s, getfacetset(grid, "bottom"), (x,t) -> 0), - Dirichlet(:s, getfacetset(grid, "top"), (x,t) -> 0), + Dirichlet(:s, getfacetset(grid, "bottom"), (x, t) -> 0), + Dirichlet(:s, getfacetset(grid, "top"), (x, t) -> 0), ) compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "top", "bottom")), - Dirichlet(:s, getfacetset(grid, "top"), (x,t) -> 0), - Dirichlet(:s, getfacetset(grid, "bottom"), (x,t) -> 0), + Dirichlet(:s, getfacetset(grid, "top"), (x, t) -> 0), + Dirichlet(:s, getfacetset(grid, "bottom"), (x, t) -> 0), ) if D == 3 compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "front", "back")), - Dirichlet(:s, getfacetset(grid, "front"), (x,t) -> 0), - Dirichlet(:s, getfacetset(grid, "back"), (x,t) -> 0), + Dirichlet(:s, getfacetset(grid, "front"), (x, t) -> 0), + Dirichlet(:s, getfacetset(grid, "back"), (x, t) -> 0), ) compare_by_dbc( dh, PeriodicDirichlet(:s, collect_periodic_facets(grid, "back", "front")), - Dirichlet(:s, getfacetset(grid, "back"), (x,t) -> 0), - Dirichlet(:s, getfacetset(grid, "front"), (x,t) -> 0), + Dirichlet(:s, getfacetset(grid, "back"), (x, t) -> 0), + Dirichlet(:s, getfacetset(grid, "front"), (x, t) -> 0), ) end @@ -1197,39 +1233,39 @@ end # testset compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "left", "right"), collect(1:D)), - Dirichlet(:v, getfacetset(grid, "left"), (x,t) -> fill(0., D), collect(1:D)), - Dirichlet(:v, getfacetset(grid, "right"), (x,t) -> fill(0., D), collect(1:D)), + Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> fill(0.0, D), collect(1:D)), + Dirichlet(:v, getfacetset(grid, "right"), (x, t) -> fill(0.0, D), collect(1:D)), ) compare_by_dbc( dh, - PeriodicDirichlet(:v, collect_periodic_facets(grid, "right", "left"), [D-1]), - Dirichlet(:v, getfacetset(grid, "right"), (x,t) -> 0, [D-1]), - Dirichlet(:v, getfacetset(grid, "left"), (x,t) -> 0, [D-1]), + PeriodicDirichlet(:v, collect_periodic_facets(grid, "right", "left"), [D - 1]), + Dirichlet(:v, getfacetset(grid, "right"), (x, t) -> 0, [D - 1]), + Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> 0, [D - 1]), ) compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "bottom", "top"), [1, 2]), - Dirichlet(:v, getfacetset(grid, "bottom"), (x,t) -> [0., 0.], [1, 2]), - Dirichlet(:v, getfacetset(grid, "top"), (x,t) -> [0., 0.], [1, 2]), + Dirichlet(:v, getfacetset(grid, "bottom"), (x, t) -> [0.0, 0.0], [1, 2]), + Dirichlet(:v, getfacetset(grid, "top"), (x, t) -> [0.0, 0.0], [1, 2]), ) compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "top", "bottom"), [D]), - Dirichlet(:v, getfacetset(grid, "top"), (x,t) -> 0, [D]), - Dirichlet(:v, getfacetset(grid, "bottom"), (x,t) -> 0, [D]), + Dirichlet(:v, getfacetset(grid, "top"), (x, t) -> 0, [D]), + Dirichlet(:v, getfacetset(grid, "bottom"), (x, t) -> 0, [D]), ) if D == 3 compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "front", "back"), 1:D), - Dirichlet(:v, getfacetset(grid, "front"), (x,t) -> fill(0., D), 1:D), - Dirichlet(:v, getfacetset(grid, "back"), (x,t) -> fill(0., D), 1:D), + Dirichlet(:v, getfacetset(grid, "front"), (x, t) -> fill(0.0, D), 1:D), + Dirichlet(:v, getfacetset(grid, "back"), (x, t) -> fill(0.0, D), 1:D), ) compare_by_dbc( dh, PeriodicDirichlet(:v, collect_periodic_facets(grid, "back", "front"), D), - Dirichlet(:v, getfacetset(grid, "back"), (x,t) -> 0, D), - Dirichlet(:v, getfacetset(grid, "front"), (x,t) -> 0, D), + Dirichlet(:v, getfacetset(grid, "back"), (x, t) -> 0, D), + Dirichlet(:v, getfacetset(grid, "front"), (x, t) -> 0, D), ) end end @@ -1238,7 +1274,7 @@ end # testset @testset "Affine constraints with master dofs that are prescribed" begin grid = generate_grid(Quadrilateral, (2, 2)) - dh = DofHandler(grid); add!(dh, :u, Lagrange{RefQuadrilateral,1}()); close!(dh) + dh = DofHandler(grid); add!(dh, :u, Lagrange{RefQuadrilateral, 1}()); close!(dh) # 8───7───9 # │ │ │ @@ -1246,133 +1282,135 @@ end # testset # │ │ │ # 1───2───5 - ke = [ 1.0 -0.25 -0.5 -0.25 - -0.25 1.0 -0.25 -0.5 - -0.5 -0.25 1.0 -0.25 - -0.25 -0.5 -0.25 1.0 ] + ke = [ + 1.0 -0.25 -0.5 -0.25 + -0.25 1.0 -0.25 -0.5 + -0.5 -0.25 1.0 -0.25 + -0.25 -0.5 -0.25 1.0 + ] fe = rand(4) -@testset "affine constraints before/after Dirichlet" begin - # Construct two ConstraintHandler's which should result in the same end result. - - ## Ordering of constraints for first ConstraintHandler: - ## 1. DBC left: u1 = u4 = u8 = 0 - ## 2. DBC right: u5 = u6 = u9 = 1 - ## 3. Periodic bottom/top: u1 = u8, u2 = u7, u5 = u9 - ## meaning that u1 = 0 and u5 = 1 are overwritten by 3 and we end up with - ## u1 = u8 = 0 - ## u2 = u7 - ## u4 = 0 - ## u5 = u9 = 1 - ## u6 = 1 - ## u8 = 0 - ## u9 = 1 - ## where the inhomogeneity of u1 and u5 have to be resolved at runtime. - ch1 = ConstraintHandler(dh) - add!(ch1, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0)) - add!(ch1, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 1)) - add!(ch1, PeriodicDirichlet(:u, collect_periodic_facets(grid, "bottom", "top"))) - close!(ch1) - update!(ch1, 0) - - ## Ordering of constraints for second ConstraintHandler: - ## 1. Periodic bottom/top: u1 = u8, u2 = u7, u5 = u9 - ## 2. DBC left: u1 = u4 = u8 = 0 - ## 3. DBC right: u5 = u6 = u9 = 1 - ## meaning that u1 = u8 and u5 = u9 are overwritten by 2 and 3 and we end up with - ## u1 = 0 - ## u2 = u7 - ## u4 = 0 - ## u5 = 1 - ## u6 = 1 - ## u8 = 0 - ## u9 = 1 - ch2 = ConstraintHandler(dh) - add!(ch2, PeriodicDirichlet(:u, collect_periodic_facets(grid, "bottom", "top"))) - add!(ch2, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0)) - add!(ch2, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 1)) - close!(ch2) - update!(ch2, 0) - - K1 = allocate_matrix(dh, ch1) - f1 = zeros(ndofs(dh)) - a1 = start_assemble(K1, f1) - K2 = allocate_matrix(dh, ch2) - f2 = zeros(ndofs(dh)) - a2 = start_assemble(K2, f2) - - for cell in CellIterator(dh) - assemble!(a1, celldofs(cell), ke, fe) - assemble!(a2, celldofs(cell), ke, fe) - end - - # Equivalent assembly - @test K1 == K2 - @test f1 == f2 + @testset "affine constraints before/after Dirichlet" begin + # Construct two ConstraintHandler's which should result in the same end result. + + ## Ordering of constraints for first ConstraintHandler: + ## 1. DBC left: u1 = u4 = u8 = 0 + ## 2. DBC right: u5 = u6 = u9 = 1 + ## 3. Periodic bottom/top: u1 = u8, u2 = u7, u5 = u9 + ## meaning that u1 = 0 and u5 = 1 are overwritten by 3 and we end up with + ## u1 = u8 = 0 + ## u2 = u7 + ## u4 = 0 + ## u5 = u9 = 1 + ## u6 = 1 + ## u8 = 0 + ## u9 = 1 + ## where the inhomogeneity of u1 and u5 have to be resolved at runtime. + ch1 = ConstraintHandler(dh) + add!(ch1, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0)) + add!(ch1, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 1)) + add!(ch1, PeriodicDirichlet(:u, collect_periodic_facets(grid, "bottom", "top"))) + close!(ch1) + update!(ch1, 0) + + ## Ordering of constraints for second ConstraintHandler: + ## 1. Periodic bottom/top: u1 = u8, u2 = u7, u5 = u9 + ## 2. DBC left: u1 = u4 = u8 = 0 + ## 3. DBC right: u5 = u6 = u9 = 1 + ## meaning that u1 = u8 and u5 = u9 are overwritten by 2 and 3 and we end up with + ## u1 = 0 + ## u2 = u7 + ## u4 = 0 + ## u5 = 1 + ## u6 = 1 + ## u8 = 0 + ## u9 = 1 + ch2 = ConstraintHandler(dh) + add!(ch2, PeriodicDirichlet(:u, collect_periodic_facets(grid, "bottom", "top"))) + add!(ch2, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0)) + add!(ch2, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 1)) + close!(ch2) + update!(ch2, 0) - # Equivalence after apply! - apply!(K1, f1, ch1) - apply!(K2, f2, ch2) - @test K1 == K2 - @test f1 == f2 - @test apply!(K1 \ f1, ch1) ≈ apply!(K2 \ f2, ch2) -end # subtestset - -@testset "time dependence" begin - ## Pure Dirichlet - ch1 = ConstraintHandler(dh) - add!(ch1, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> 3.0t + 2.0)) - add!(ch1, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> 1.5t + 1.0)) - add!(ch1, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 1.0t)) - add!(ch1, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 2.0t)) - close!(ch1) - ## Dirichlet with corresponding AffineConstraint on dof 2 and 7 - ch2 = ConstraintHandler(dh) - add!(ch2, AffineConstraint(7, [8 => 1.0, 9 => 1.0], 2.0)) - add!(ch2, AffineConstraint(2, [1 => 0.5, 5 => 0.5], 1.0)) - add!(ch2, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 1.0t)) - add!(ch2, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 2.0t)) - close!(ch2) - - K1 = allocate_matrix(dh, ch1) - f1 = zeros(ndofs(dh)) - K2 = allocate_matrix(dh, ch2) - f2 = zeros(ndofs(dh)) - - for t in (1.0, 2.0) - update!(ch1, t) - update!(ch2, t) + K1 = allocate_matrix(dh, ch1) + f1 = zeros(ndofs(dh)) a1 = start_assemble(K1, f1) + K2 = allocate_matrix(dh, ch2) + f2 = zeros(ndofs(dh)) a2 = start_assemble(K2, f2) + for cell in CellIterator(dh) assemble!(a1, celldofs(cell), ke, fe) assemble!(a2, celldofs(cell), ke, fe) end + + # Equivalent assembly @test K1 == K2 @test f1 == f2 + + # Equivalence after apply! apply!(K1, f1, ch1) apply!(K2, f2, ch2) @test K1 == K2 @test f1 == f2 - @test K1 \ f1 ≈ K2 \ f2 - end + @test apply!(K1 \ f1, ch1) ≈ apply!(K2 \ f2, ch2) + end # subtestset -end # subtestset + @testset "time dependence" begin + ## Pure Dirichlet + ch1 = ConstraintHandler(dh) + add!(ch1, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> 3.0t + 2.0)) + add!(ch1, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> 1.5t + 1.0)) + add!(ch1, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 1.0t)) + add!(ch1, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 2.0t)) + close!(ch1) + ## Dirichlet with corresponding AffineConstraint on dof 2 and 7 + ch2 = ConstraintHandler(dh) + add!(ch2, AffineConstraint(7, [8 => 1.0, 9 => 1.0], 2.0)) + add!(ch2, AffineConstraint(2, [1 => 0.5, 5 => 0.5], 1.0)) + add!(ch2, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 1.0t)) + add!(ch2, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 2.0t)) + close!(ch2) + K1 = allocate_matrix(dh, ch1) + f1 = zeros(ndofs(dh)) + K2 = allocate_matrix(dh, ch2) + f2 = zeros(ndofs(dh)) -@testset "error paths" begin - ch = ConstraintHandler(dh) - add!(ch, AffineConstraint(1, [2 => 1.0], 0.0)) - add!(ch, AffineConstraint(2, [3 => 1.0], 0.0)) - @test_throws ErrorException("nested affine constraints currently not supported") close!(ch) -end # subtestset + for t in (1.0, 2.0) + update!(ch1, t) + update!(ch2, t) + a1 = start_assemble(K1, f1) + a2 = start_assemble(K2, f2) + for cell in CellIterator(dh) + assemble!(a1, celldofs(cell), ke, fe) + assemble!(a2, celldofs(cell), ke, fe) + end + @test K1 == K2 + @test f1 == f2 + apply!(K1, f1, ch1) + apply!(K2, f2, ch2) + @test K1 == K2 + @test f1 == f2 + @test K1 \ f1 ≈ K2 \ f2 + end + + end # subtestset + + + @testset "error paths" begin + ch = ConstraintHandler(dh) + add!(ch, AffineConstraint(1, [2 => 1.0], 0.0)) + add!(ch, AffineConstraint(2, [3 => 1.0], 0.0)) + @test_throws ErrorException("nested affine constraints currently not supported") close!(ch) + end # subtestset end # testset @testset "local application of bc" begin - grid = generate_grid(Quadrilateral, (5,5)) + grid = generate_grid(Quadrilateral, (5, 5)) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefQuadrilateral,1}()) + add!(dh, :u, Lagrange{RefQuadrilateral, 1}()) close!(dh) # Dirichlet BC ch_dbc = ConstraintHandler(dh) @@ -1383,7 +1421,7 @@ end # testset # Dirichlet BC as affine constraints ch_ac = ConstraintHandler(dh) for (dof, value) in zip(ch_dbc.prescribed_dofs, ch_dbc.inhomogeneities) - add!(ch_ac, AffineConstraint(dof, Pair{Int,Float64}[], value)) + add!(ch_ac, AffineConstraint(dof, Pair{Int, Float64}[], value)) end close!(ch_ac) update!(ch_ac, 0) @@ -1404,11 +1442,11 @@ end # testset dΩ = getdetJdV(cv, qp) for i in 1:getnbasefunctions(cv) ϕi = shape_value(cv, qp, i) - fe[i] += ( ϕi * b ) * dΩ + fe[i] += (ϕi * b) * dΩ for j in 1:getnbasefunctions(cv) ∇ϕi = shape_gradient(cv, qp, i) ∇ϕj = shape_gradient(cv, qp, j) - ke[i,j] += ( ∇ϕi ⋅ (k * ∇ϕj) ) * dΩ + ke[i, j] += (∇ϕi ⋅ (k * ∇ϕj)) * dΩ end end end @@ -1455,19 +1493,19 @@ end # testset ke = zeros(ndofs_per_cell(dh), ndofs_per_cell(dh)) fe = zeros(ndofs_per_cell(dh)) - cv = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,1}()) + cv = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral, 1}()) for cell in CellIterator(dh) reinit!(cv, cell) global_dofs = celldofs(cell) - element!(ke, fe, cv, cellid(cell), 1/cellid(cell)) + element!(ke, fe, cv, cellid(cell), 1 / cellid(cell)) # Standard application assemble!(assembler_dbc_standard, global_dofs, ke, fe) assemble!(assembler_ac_standard, global_dofs, ke, fe) assemble!(assembler_p_standard, global_dofs, ke, fe) # Assemble with ConstraintHandler let apply_f! = azero === nothing ? apply_assemble! : - (args...) -> apply_assemble!(args...; apply_zero=azero) + (args...) -> apply_assemble!(args...; apply_zero = azero) let ke = copy(ke), fe = copy(fe) apply_f!(assembler_dbc_ch, ch_dbc, global_dofs, ke, fe) end @@ -1480,7 +1518,7 @@ end # testset end # Assemble after apply_local! let apply_f! = azero === nothing ? apply_local! : - (args...) -> apply_local!(args...; apply_zero=azero) + (args...) -> apply_local!(args...; apply_zero = azero) let ke = copy(ke), fe = copy(fe) apply_f!(ke, fe, global_dofs, ch_dbc) assemble!(assembler_dbc_local, global_dofs, ke, fe) @@ -1515,9 +1553,9 @@ end # testset # Everything should be identical now for free entries @test K_dbc_standard[fdofs, fdofs] ≈ K_dbc_ch[fdofs, fdofs] ≈ K_dbc_local[fdofs, fdofs] ≈ - K_ac_standard[fdofs, fdofs] ≈ K_ac_ch[fdofs, fdofs] ≈ K_ac_local[fdofs, fdofs] + K_ac_standard[fdofs, fdofs] ≈ K_ac_ch[fdofs, fdofs] ≈ K_ac_local[fdofs, fdofs] @test f_dbc_standard[fdofs] ≈ f_dbc_ch[fdofs] ≈ f_dbc_local[fdofs] ≈ - f_ac_standard[fdofs] ≈ f_ac_ch[fdofs] ≈ f_ac_local[fdofs] + f_ac_standard[fdofs] ≈ f_ac_ch[fdofs] ≈ f_ac_local[fdofs] fdofs_p = free_dofs(ch_p) @test K_p_standard[fdofs_p, fdofs_p] ≈ K_p_ch[fdofs_p, fdofs_p] @test f_p_standard[fdofs_p] ≈ f_p_ch[fdofs_p] @@ -1559,7 +1597,7 @@ end # testset # write_solution(vtk, dh, u_p, "_p") # end @test K_dbc_standard \ f_dbc_standard ≈ K_dbc_ch \ f_dbc_ch ≈ K_dbc_local \ f_dbc_local ≈ - K_ac_standard \ f_ac_standard ≈ K_ac_ch \ f_ac_ch ≈ K_ac_local \ f_ac_local + K_ac_standard \ f_ac_standard ≈ K_ac_ch \ f_ac_ch ≈ K_ac_local \ f_ac_local let apply_f! = azero === true ? apply_zero! : apply! @test apply_f!(K_p_standard \ f_p_standard, ch_p) ≈ apply_f!(K_p_ch \ f_p_ch, ch_p) end @@ -1569,17 +1607,17 @@ end # testset @testset "Sparsity pattern without constrained dofs" begin grid = generate_grid(Triangle, (5, 5)) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefTriangle,1}()) + add!(dh, :u, Lagrange{RefTriangle, 1}()) close!(dh) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0)) close!(ch) Kfull = allocate_matrix(dh, ch) - K = allocate_matrix(dh, ch; keep_constrained=false) + K = allocate_matrix(dh, ch; keep_constrained = false) # Pattern tests nonzero_edges = Set( (i, j) for d in 1:getncells(grid) - for (i, j) in Iterators.product(celldofs(dh, d), celldofs(dh, d)) + for (i, j) in Iterators.product(celldofs(dh, d), celldofs(dh, d)) ) zero_edges = setdiff(Set(Iterators.product(1:ndofs(dh), 1:ndofs(dh))), nonzero_edges) for (i, j) in zero_edges @@ -1622,7 +1660,7 @@ end # testset # and that the missing values are instead taken from above the diagonal. grid = generate_grid(Line, (2,)) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefLine,1}()) + add!(dh, :u, Lagrange{RefLine, 1}()) close!(dh) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, "left"), x -> 1)) diff --git a/test/test_deprecations.jl b/test/test_deprecations.jl index 1d9bb65add..6bb672e912 100644 --- a/test/test_deprecations.jl +++ b/test/test_deprecations.jl @@ -2,129 +2,133 @@ using Ferrite, Test @testset "Deprecations" begin -@testset "Deprecation of auto-vectorized methods" begin - # Deprecation of auto-selecting the interpolation - grid = generate_grid(Quadrilateral, (1, 1)) - dh = DofHandler(grid) - @test_throws Ferrite.DeprecationError add!(dh, :u, 2) - @test_throws Ferrite.DeprecationError add!(dh, :p, 1) - close!(dh) - @test ndofs(dh) == 0 - # Deprecation of auto-vectorizing - dh = DofHandler(grid) - ip = Lagrange{RefQuadrilateral,1}() - @test_throws Ferrite.DeprecationError add!(dh, :u, 2, ip) - @test_throws Ferrite.DeprecationError add!(dh, :p, 1, ip) - close!(dh) - @test ndofs(dh) == 0 -end + @testset "Deprecation of auto-vectorized methods" begin + # Deprecation of auto-selecting the interpolation + grid = generate_grid(Quadrilateral, (1, 1)) + dh = DofHandler(grid) + @test_throws Ferrite.DeprecationError add!(dh, :u, 2) + @test_throws Ferrite.DeprecationError add!(dh, :p, 1) + close!(dh) + @test ndofs(dh) == 0 + # Deprecation of auto-vectorizing + dh = DofHandler(grid) + ip = Lagrange{RefQuadrilateral, 1}() + @test_throws Ferrite.DeprecationError add!(dh, :u, 2, ip) + @test_throws Ferrite.DeprecationError add!(dh, :p, 1, ip) + close!(dh) + @test ndofs(dh) == 0 + end -@testset "Deprecation of (Cell|Face)(Scalar|Vector)Values" begin - ip = Lagrange{RefQuadrilateral, 1}() - qr = QuadratureRule{RefQuadrilateral}(2) - for CVType in ( - CellScalarValues, CellVectorValues, - FaceScalarValues, FaceVectorValues, - PointScalarValues, PointVectorValues, - ) - err = try CVType(qr, ip) catch e sprint(showerror, e) end - @test occursin("merged into a single type", err) - @test occursin("CHANGELOG", err) + @testset "Deprecation of (Cell|Face)(Scalar|Vector)Values" begin + ip = Lagrange{RefQuadrilateral, 1}() + qr = QuadratureRule{RefQuadrilateral}(2) + for CVType in ( + CellScalarValues, CellVectorValues, + FaceScalarValues, FaceVectorValues, + PointScalarValues, PointVectorValues, + ) + err = try + CVType(qr, ip) + catch e + sprint(showerror, e) + end + @test occursin("merged into a single type", err) + @test occursin("CHANGELOG", err) + end + # Smoke tests to see that old code still loads + function foo(cv::CellScalarValues{D, T, R}) where {D, T, R} end + function bar(cv::CellVectorValues{D, T}) where {D, T} end + function baz(cv::PointVectorValues{D}) where {D} end end - # Smoke tests to see that old code still loads - function foo(cv::CellScalarValues{D,T,R}) where {D, T, R} end - function bar(cv::CellVectorValues{D,T}) where {D, T} end - function baz(cv::PointVectorValues{D}) where {D} end -end -@testset "Deprecation of old RefShapes" begin - # Quadrature/(Cell|Face)Value combinations (sometimes warns in the QR constructor, sometimes it the FEValues constructor) - function test_combo(constructor, qdim, qshape, qargs, ip) - qr = QuadratureRule{qdim, qshape}(qargs...) - constructor(qr, ip) + @testset "Deprecation of old RefShapes" begin + # Quadrature/(Cell|Face)Value combinations (sometimes warns in the QR constructor, sometimes it the FEValues constructor) + function test_combo(constructor, qdim, qshape, qargs, ip) + qr = QuadratureRule{qdim, qshape}(qargs...) + constructor(qr, ip) + end + @test_throws Ferrite.DeprecationError test_combo(CellValues, 1, RefCube, (1,), Lagrange{RefLine, 1}()) + @test_throws Ferrite.DeprecationError test_combo(CellValues, 1, RefCube, (:legendre, 1), Lagrange{RefLine, 1}()) + @test_throws Ferrite.DeprecationError test_combo(CellValues, 2, RefCube, (1,), Lagrange{RefQuadrilateral, 1}()) + @test_throws Ferrite.DeprecationError test_combo(CellValues, 2, RefCube, (:legendre, 1), Lagrange{RefQuadrilateral, 1}()) + @test_throws Ferrite.DeprecationError test_combo(CellValues, 3, RefCube, (1,), Lagrange{RefHexahedron, 1}()) + @test_throws Ferrite.DeprecationError test_combo(CellValues, 3, RefCube, (:legendre, 1), Lagrange{RefHexahedron, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 0, RefCube, (1,), Lagrange{RefLine, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 0, RefCube, (:legendre, 1), Lagrange{RefLine, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (1,), Lagrange{RefQuadrilateral, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (1,), Lagrange{RefQuadrilateral, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (:legendre, 1), Lagrange{RefQuadrilateral, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (:legendre, 1), Lagrange{RefQuadrilateral, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (1,), Lagrange{RefHexahedron, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (1,), Lagrange{RefHexahedron, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (:legendre, 1), Lagrange{RefHexahedron, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (:legendre, 1), Lagrange{RefHexahedron, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefTetrahedron, (1,), Lagrange{RefTriangle, 1}()) + @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefTetrahedron, (:legendre, 1), Lagrange{RefTriangle, 1}()) end - @test_throws Ferrite.DeprecationError test_combo(CellValues, 1, RefCube, (1,), Lagrange{RefLine, 1}()) - @test_throws Ferrite.DeprecationError test_combo(CellValues, 1, RefCube, (:legendre, 1), Lagrange{RefLine, 1}()) - @test_throws Ferrite.DeprecationError test_combo(CellValues, 2, RefCube, (1,), Lagrange{RefQuadrilateral, 1}()) - @test_throws Ferrite.DeprecationError test_combo(CellValues, 2, RefCube, (:legendre, 1), Lagrange{RefQuadrilateral, 1}()) - @test_throws Ferrite.DeprecationError test_combo(CellValues, 3, RefCube, (1,), Lagrange{RefHexahedron, 1}()) - @test_throws Ferrite.DeprecationError test_combo(CellValues, 3, RefCube, (:legendre, 1), Lagrange{RefHexahedron, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 0, RefCube, (1,), Lagrange{RefLine, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 0, RefCube, (:legendre, 1), Lagrange{RefLine, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (1,), Lagrange{RefQuadrilateral, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (1,), Lagrange{RefQuadrilateral, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (:legendre, 1), Lagrange{RefQuadrilateral, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefCube, (:legendre, 1), Lagrange{RefQuadrilateral, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (1,), Lagrange{RefHexahedron, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (1,), Lagrange{RefHexahedron, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (:legendre, 1), Lagrange{RefHexahedron, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 2, RefCube, (:legendre, 1), Lagrange{RefHexahedron, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefTetrahedron, (1,), Lagrange{RefTriangle, 1}()) - @test_throws Ferrite.DeprecationError test_combo(FacetValues, 1, RefTetrahedron, (:legendre, 1), Lagrange{RefTriangle, 1}()) -end -@testset "Ferrite.value and Ferrite.derivative" begin - ip = Lagrange{RefQuadrilateral, 1}() - ξ = zero(Vec{2}) - @test_throws Ferrite.DeprecationError Ferrite.value(ip, ξ) - @test_throws Ferrite.DeprecationError Ferrite.derivative(ip, ξ) - @test_throws Ferrite.DeprecationError Ferrite.value(ip, 1, ξ) -end + @testset "Ferrite.value and Ferrite.derivative" begin + ip = Lagrange{RefQuadrilateral, 1}() + ξ = zero(Vec{2}) + @test_throws Ferrite.DeprecationError Ferrite.value(ip, ξ) + @test_throws Ferrite.DeprecationError Ferrite.derivative(ip, ξ) + @test_throws Ferrite.DeprecationError Ferrite.value(ip, 1, ξ) + end -@testset "facesets" begin - grid = generate_grid(Quadrilateral, (2,2)) - @test_throws Ferrite.DeprecationError addfaceset!(grid, "right_face", x -> x[1] ≈ 1) - @test_throws Ferrite.DeprecationError addfaceset!(grid, "right_face_explicit", Set(Ferrite.FaceIndex(fi[1], fi[2]) for fi in getfacetset(grid, "right"))) -end + @testset "facesets" begin + grid = generate_grid(Quadrilateral, (2, 2)) + @test_throws Ferrite.DeprecationError addfaceset!(grid, "right_face", x -> x[1] ≈ 1) + @test_throws Ferrite.DeprecationError addfaceset!(grid, "right_face_explicit", Set(Ferrite.FaceIndex(fi[1], fi[2]) for fi in getfacetset(grid, "right"))) + end -@testset "vtk_grid" begin - # Ensure no MethodError on pre v1. - @test_throws Ferrite.DeprecationError vtk_grid("old", generate_grid(Line, (1,))) -end + @testset "vtk_grid" begin + # Ensure no MethodError on pre v1. + @test_throws Ferrite.DeprecationError vtk_grid("old", generate_grid(Line, (1,))) + end -@testset "onboundary" begin - msg = "`onboundary` is deprecated, check just the facetset instead of first checking `onboundary`." - @test_throws Ferrite.DeprecationError(msg) onboundary(first(CellIterator(generate_grid(Line, (2,)))), 1) - msg = "`boundary_matrix` is not part of the Grid anymore and thus not a supported keyword argument." - @test_throws Ferrite.DeprecationError(msg) Grid(Triangle[], Node{2,Float64}[]; boundary_matrix = something) -end + @testset "onboundary" begin + msg = "`onboundary` is deprecated, check just the facetset instead of first checking `onboundary`." + @test_throws Ferrite.DeprecationError(msg) onboundary(first(CellIterator(generate_grid(Line, (2,)))), 1) + msg = "`boundary_matrix` is not part of the Grid anymore and thus not a supported keyword argument." + @test_throws Ferrite.DeprecationError(msg) Grid(Triangle[], Node{2, Float64}[]; boundary_matrix = something) + end -@testset "getdim" begin - msg = "`Ferrite.getdim` is deprecated, use `getrefdim` or `getspatialdim` instead" - @test_throws Ferrite.DeprecationError(msg) Ferrite.getdim(generate_grid(Line, (1,))) - @test_throws Ferrite.DeprecationError(msg) Ferrite.getdim(Lagrange{RefTriangle,1}()) - @test_throws Ferrite.DeprecationError(msg) Ferrite.getdim(Line((1,2))) -end + @testset "getdim" begin + msg = "`Ferrite.getdim` is deprecated, use `getrefdim` or `getspatialdim` instead" + @test_throws Ferrite.DeprecationError(msg) Ferrite.getdim(generate_grid(Line, (1,))) + @test_throws Ferrite.DeprecationError(msg) Ferrite.getdim(Lagrange{RefTriangle, 1}()) + @test_throws Ferrite.DeprecationError(msg) Ferrite.getdim(Line((1, 2))) + end -@testset "getfielddim" begin - msg = "`Ferrite.getfielddim(::AbstractDofHandler, args...) is deprecated, use `n_components` instead" - dh = close!(add!(DofHandler(generate_grid(Triangle, (1,1))), :u, Lagrange{RefTriangle,1}())) - @test_throws Ferrite.DeprecationError(msg) Ferrite.getfielddim(dh, Ferrite.find_field(dh, :u)) - @test_throws Ferrite.DeprecationError(msg) Ferrite.getfielddim(dh.subdofhandlers[1], :u) -end + @testset "getfielddim" begin + msg = "`Ferrite.getfielddim(::AbstractDofHandler, args...) is deprecated, use `n_components` instead" + dh = close!(add!(DofHandler(generate_grid(Triangle, (1, 1))), :u, Lagrange{RefTriangle, 1}())) + @test_throws Ferrite.DeprecationError(msg) Ferrite.getfielddim(dh, Ferrite.find_field(dh, :u)) + @test_throws Ferrite.DeprecationError(msg) Ferrite.getfielddim(dh.subdofhandlers[1], :u) + end -@testset "default_interpolation" begin - @test_throws Ferrite.DeprecationError Ferrite.default_interpolation(Triangle) -end + @testset "default_interpolation" begin + @test_throws Ferrite.DeprecationError Ferrite.default_interpolation(Triangle) + end -@testset "start_assemble" begin - @test_throws Ferrite.DeprecationError start_assemble() - @test_throws Ferrite.DeprecationError start_assemble(10) -end + @testset "start_assemble" begin + @test_throws Ferrite.DeprecationError start_assemble() + @test_throws Ferrite.DeprecationError start_assemble(10) + end -@testset "celldofs!(::Vector, ::Cache)" begin - grid = generate_grid(Quadrilateral, (1, 1)) - dh = DofHandler(grid) - ip = Lagrange{RefQuadrilateral, 1}() - add!(dh, :u, ip) - close!(dh) - cc = CellCache(dh) - reinit!(cc, 1) - v = Int[] - @test_throws Ferrite.DeprecationError celldofs!(v, cc) - fc = FacetCache(dh) - reinit!(fc, FacetIndex(1, 1)) - @test_throws Ferrite.DeprecationError celldofs!(v, fc) -end + @testset "celldofs!(::Vector, ::Cache)" begin + grid = generate_grid(Quadrilateral, (1, 1)) + dh = DofHandler(grid) + ip = Lagrange{RefQuadrilateral, 1}() + add!(dh, :u, ip) + close!(dh) + cc = CellCache(dh) + reinit!(cc, 1) + v = Int[] + @test_throws Ferrite.DeprecationError celldofs!(v, cc) + fc = FacetCache(dh) + reinit!(fc, FacetIndex(1, 1)) + @test_throws Ferrite.DeprecationError celldofs!(v, fc) + end end # testset deprecations diff --git a/test/test_dofs.jl b/test/test_dofs.jl index 5379968f2b..f443aa4927 100644 --- a/test/test_dofs.jl +++ b/test/test_dofs.jl @@ -1,5 +1,5 @@ @testset "DofHandler construction" begin - grid = generate_grid(Quadrilateral, (2,1)) + grid = generate_grid(Quadrilateral, (2, 1)) dh = DofHandler(grid) # incompatible refshape (#638) @test_throws ErrorException add!(dh, :u, Lagrange{RefTriangle, 1}()) @@ -10,7 +10,7 @@ # Invalid SubDofHandler construction dh = DofHandler(grid) - sdh1 = Ferrite.SubDofHandler(dh, Set(1,)) + sdh1 = Ferrite.SubDofHandler(dh, Set(1)) # Subdomains not disjoint @test_throws ErrorException Ferrite.SubDofHandler(dh, Set(1:getncells(grid))) # add field to DofHandler that has subdomains @@ -18,8 +18,8 @@ # inconsistent field across several SubDofHandlers dh = DofHandler(grid) - sdh1 = Ferrite.SubDofHandler(dh, Set(1,)) - sdh2 = Ferrite.SubDofHandler(dh, Set(2,)) + sdh1 = Ferrite.SubDofHandler(dh, Set(1)) + sdh2 = Ferrite.SubDofHandler(dh, Set(2)) add!(sdh1, :u, Lagrange{RefQuadrilateral, 1}()) # different number of components in different sdh @test_throws ErrorException add!(sdh2, :u, Lagrange{RefQuadrilateral, 1}()^2) @@ -31,111 +31,117 @@ end # misc dofhandler unit tests @testset "dofs" begin -# set up a test DofHandler -grid = generate_grid(Triangle, (10, 10)) -dh = DofHandler(grid) -add!(dh, :u, Lagrange{RefTriangle,2}()^2) -add!(dh, :p, Lagrange{RefTriangle,1}()) -close!(dh) - -# dof_range -@test (@inferred dof_range(dh, :u)) == 1:12 -@test (@inferred dof_range(dh, :p)) == 13:15 -# dof_range for SubDofHandler -ip = Lagrange{RefTriangle, 1}() -dh = DofHandler(grid) -sdh = SubDofHandler(dh, Set(1:getncells(grid))) -add!(sdh, :u, ip^2) -add!(sdh, :c, ip) - -@test dof_range(sdh, Ferrite.find_field(sdh, :u)) == 1:6 -@test dof_range(sdh, Ferrite.find_field(sdh, :c)) == 7:9 + # set up a test DofHandler + grid = generate_grid(Triangle, (10, 10)) + dh = DofHandler(grid) + add!(dh, :u, Lagrange{RefTriangle, 2}()^2) + add!(dh, :p, Lagrange{RefTriangle, 1}()) + close!(dh) + + # dof_range + @test (@inferred dof_range(dh, :u)) == 1:12 + @test (@inferred dof_range(dh, :p)) == 13:15 + # dof_range for SubDofHandler + ip = Lagrange{RefTriangle, 1}() + dh = DofHandler(grid) + sdh = SubDofHandler(dh, Set(1:getncells(grid))) + add!(sdh, :u, ip^2) + add!(sdh, :c, ip) + + @test dof_range(sdh, Ferrite.find_field(sdh, :u)) == 1:6 + @test dof_range(sdh, Ferrite.find_field(sdh, :c)) == 7:9 end # testset @testset "Dofs for Line2" begin -nodes = [Node{2,Float64}(Vec(0.0,0.0)), Node{2,Float64}(Vec(1.0,1.0)), Node{2,Float64}(Vec(2.0,0.0))] -cells = [Line((1,2)), Line((2,3))] -grid = Grid(cells,nodes) + nodes = [Node{2, Float64}(Vec(0.0, 0.0)), Node{2, Float64}(Vec(1.0, 1.0)), Node{2, Float64}(Vec(2.0, 0.0))] + cells = [Line((1, 2)), Line((2, 3))] + grid = Grid(cells, nodes) -#2d line with 1st order 1d interpolation -dh = DofHandler(grid) -add!(dh, :x, Lagrange{RefLine,1}()^2) -close!(dh) + #2d line with 1st order 1d interpolation + dh = DofHandler(grid) + add!(dh, :x, Lagrange{RefLine, 1}()^2) + close!(dh) -@test celldofs(dh,1) == [1,2,3,4] -@test celldofs(dh,2) == [3,4,5,6] + @test celldofs(dh, 1) == [1, 2, 3, 4] + @test celldofs(dh, 2) == [3, 4, 5, 6] -#2d line with 2nd order 1d interpolation -dh = DofHandler(grid) -add!(dh, :x, Lagrange{RefLine,2}()^2) -close!(dh) + #2d line with 2nd order 1d interpolation + dh = DofHandler(grid) + add!(dh, :x, Lagrange{RefLine, 2}()^2) + close!(dh) -@test celldofs(dh,1) == [1,2,3,4,5,6] -@test celldofs(dh,2) == [3,4,7,8,9,10] + @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6] + @test celldofs(dh, 2) == [3, 4, 7, 8, 9, 10] -#3d line with 2nd order 1d interpolation -dh = DofHandler(grid) -add!(dh, :u, Lagrange{RefLine,2}()^3) -add!(dh, :θ, Lagrange{RefLine,2}()^3) -close!(dh) + #3d line with 2nd order 1d interpolation + dh = DofHandler(grid) + add!(dh, :u, Lagrange{RefLine, 2}()^3) + add!(dh, :θ, Lagrange{RefLine, 2}()^3) + close!(dh) -@test celldofs(dh,1) == collect(1:18) -@test celldofs(dh,2) == [4,5,6,19,20,21,22,23,24, # u - 13,14,15,25,26,27,28,29,30] # θ + @test celldofs(dh, 1) == collect(1:18) + @test celldofs(dh, 2) == [ + 4, 5, 6, 19, 20, 21, 22, 23, 24, # u + 13, 14, 15, 25, 26, 27, 28, 29, 30, # θ + ] end @testset "Dofs for quad in 3d (shell)" begin -nodes = [Node{3,Float64}(Vec(0.0,0.0,0.0)), Node{3,Float64}(Vec(1.0,0.0,0.0)), - Node{3,Float64}(Vec(1.0,1.0,0.0)), Node{3,Float64}(Vec(0.0,1.0,0.0)), - Node{3,Float64}(Vec(2.0,0.0,0.0)), Node{3,Float64}(Vec(2.0,2.0,0.0))] - -cells = [Quadrilateral((1,2,3,4)), Quadrilateral((2,5,6,3))] -grid = Grid(cells,nodes) - -#3d quad with 1st order 2d interpolation -dh = DofHandler(grid) -add!(dh, :u, Lagrange{RefQuadrilateral,1}()^3) -add!(dh, :θ, Lagrange{RefQuadrilateral,1}()^3) -close!(dh) - -@test celldofs(dh,1) == collect(1:24) -@test celldofs(dh,2) == [4,5,6,25,26,27,28,29,30,7,8,9, # u - 16,17,18,31,32,33,34,35,36,19,20,21]# θ - -#3d quads with two quadratic interpolations fields -#Only 1 dim per field for simplicity... -dh = DofHandler(grid) -add!(dh, :u, Lagrange{RefQuadrilateral,2}()) -add!(dh, :θ, Lagrange{RefQuadrilateral,2}()) -close!(dh) - -@test celldofs(dh,1) == collect(1:18) -@test celldofs(dh,2) == [2, 19, 20, 3, 21, 22, 23, 6, 24, 11, 25, 26, 12, 27, 28, 29, 15, 30] - -# test evaluate_at_grid_nodes -## DofHandler -mesh = generate_grid(Quadrilateral, (1,1)) -dh = DofHandler(mesh) -add!(dh, :v, Lagrange{RefQuadrilateral,1}()^2) -add!(dh, :s, Lagrange{RefQuadrilateral,1}()) -close!(dh) - -u = [1.1, 1.2, 2.1, 2.2, 4.1, 4.2, 3.1, 3.2, 1.3, 2.3, 4.3, 3.3] -uv = @view u[1:end] -# :s on solution -s_nodes = evaluate_at_grid_nodes(dh, u, :s) -@test s_nodes ≈ [i+0.3 for i=1:4] -# :s on a view into solution -sv_nodes = evaluate_at_grid_nodes(dh, uv, :s) -@test sv_nodes ≈ [i+0.3 for i=1:4] -# :v on solution -v_nodes = evaluate_at_grid_nodes(dh, u, :v) -@test v_nodes ≈ [Vec{2,Float64}(i -> j+i/10) for j = 1:4] -# :v on a view into solution -vv_nodes = evaluate_at_grid_nodes(dh, uv, :v) -@test vv_nodes ≈ [Vec{2,Float64}(i -> j+i/10) for j = 1:4] + nodes = [ + Node{3, Float64}(Vec(0.0, 0.0, 0.0)), Node{3, Float64}(Vec(1.0, 0.0, 0.0)), + Node{3, Float64}(Vec(1.0, 1.0, 0.0)), Node{3, Float64}(Vec(0.0, 1.0, 0.0)), + Node{3, Float64}(Vec(2.0, 0.0, 0.0)), Node{3, Float64}(Vec(2.0, 2.0, 0.0)), + ] + + cells = [Quadrilateral((1, 2, 3, 4)), Quadrilateral((2, 5, 6, 3))] + grid = Grid(cells, nodes) + + #3d quad with 1st order 2d interpolation + dh = DofHandler(grid) + add!(dh, :u, Lagrange{RefQuadrilateral, 1}()^3) + add!(dh, :θ, Lagrange{RefQuadrilateral, 1}()^3) + close!(dh) + + @test celldofs(dh, 1) == collect(1:24) + @test celldofs(dh, 2) == [ + 4, 5, 6, 25, 26, 27, 28, 29, 30, 7, 8, 9, # u + 16, 17, 18, 31, 32, 33, 34, 35, 36, 19, 20, 21, # θ + ] + + #3d quads with two quadratic interpolations fields + #Only 1 dim per field for simplicity... + dh = DofHandler(grid) + add!(dh, :u, Lagrange{RefQuadrilateral, 2}()) + add!(dh, :θ, Lagrange{RefQuadrilateral, 2}()) + close!(dh) + + @test celldofs(dh, 1) == collect(1:18) + @test celldofs(dh, 2) == [2, 19, 20, 3, 21, 22, 23, 6, 24, 11, 25, 26, 12, 27, 28, 29, 15, 30] + + # test evaluate_at_grid_nodes + ## DofHandler + mesh = generate_grid(Quadrilateral, (1, 1)) + dh = DofHandler(mesh) + add!(dh, :v, Lagrange{RefQuadrilateral, 1}()^2) + add!(dh, :s, Lagrange{RefQuadrilateral, 1}()) + close!(dh) + + u = [1.1, 1.2, 2.1, 2.2, 4.1, 4.2, 3.1, 3.2, 1.3, 2.3, 4.3, 3.3] + uv = @view u[1:end] + # :s on solution + s_nodes = evaluate_at_grid_nodes(dh, u, :s) + @test s_nodes ≈ [i + 0.3 for i in 1:4] + # :s on a view into solution + sv_nodes = evaluate_at_grid_nodes(dh, uv, :s) + @test sv_nodes ≈ [i + 0.3 for i in 1:4] + # :v on solution + v_nodes = evaluate_at_grid_nodes(dh, u, :v) + @test v_nodes ≈ [Vec{2, Float64}(i -> j + i / 10) for j in 1:4] + # :v on a view into solution + vv_nodes = evaluate_at_grid_nodes(dh, uv, :v) + @test vv_nodes ≈ [Vec{2, Float64}(i -> j + i / 10) for j in 1:4] end @@ -144,14 +150,14 @@ end local dh, mdh, ch grid = generate_grid(Triangle, (10, 10)) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefTriangle,1}()) + add!(dh, :u, Lagrange{RefTriangle, 1}()) close!(dh) # subdomains mdh = DofHandler(grid) - sdh1 = SubDofHandler(mdh, Set(1:getncells(grid)÷2)) - add!(sdh1, :u, Lagrange{RefTriangle,1}()) - sdh2 = SubDofHandler(mdh, Set((getncells(grid)÷2+1):getncells(grid))) - add!(sdh2, :u, Lagrange{RefTriangle,1}()) + sdh1 = SubDofHandler(mdh, Set(1:(getncells(grid) ÷ 2))) + add!(sdh1, :u, Lagrange{RefTriangle, 1}()) + sdh2 = SubDofHandler(mdh, Set((getncells(grid) ÷ 2 + 1):getncells(grid))) + add!(sdh2, :u, Lagrange{RefTriangle, 1}()) close!(mdh) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0)) @@ -220,8 +226,8 @@ end local grid, dh, ch grid = generate_grid(Quadrilateral, (2, 1)) dh = DofHandler(grid) - add!(dh, :v, Lagrange{RefQuadrilateral,1}()^2) - add!(dh, :s, Lagrange{RefQuadrilateral,1}()) + add!(dh, :v, Lagrange{RefQuadrilateral, 1}()^2) + add!(dh, :s, Lagrange{RefQuadrilateral, 1}()) close!(dh) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> 0, [2])) @@ -305,7 +311,7 @@ end function test_dhch_subdomain() local grid, dh, ch grid = generate_grid(Quadrilateral, (2, 1)) - ip = Lagrange{RefQuadrilateral,1}() + ip = Lagrange{RefQuadrilateral, 1}() dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :v, ip^2) @@ -394,7 +400,7 @@ end # │ 1 │ 2 │ │ 1 │ 2 │ # 11,1──12,2──15,5 7─────8──── @test celldofs(dh, 1) == [11, 1, 12, 2, 13, 3, 14, 4, 7, 8, 9, 10] - @test celldofs(dh, 2) == [12, 2, 15, 5, 16, 6, 13, 3, ] + @test celldofs(dh, 2) == [12, 2, 15, 5, 16, 6, 13, 3] @test ch.prescribed_dofs == sort!([1, 4, 7, 10, 15]) dof_range_v1 = dof_range(dh.subdofhandlers[1], :v) dof_range_s1 = dof_range(dh.subdofhandlers[1], :s) @@ -412,15 +418,15 @@ end dh, ch = testdhch() renumber!(dh, DofOrder.Ext{Metis}()) @test_throws ErrorException renumber!(dh, ch, DofOrder.Ext{Metis}()) - renumber!(dh, DofOrder.Ext{Metis}(coupling=[true true; true false])) - @test_throws ErrorException renumber!(dh, ch, DofOrder.Ext{Metis}(coupling=[true true; true false])) + renumber!(dh, DofOrder.Ext{Metis}(coupling = [true true; true false])) + @test_throws ErrorException renumber!(dh, ch, DofOrder.Ext{Metis}(coupling = [true true; true false])) end @testset "dof coupling" begin grid = generate_grid(Quadrilateral, (1, 1)) dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefQuadrilateral,1}()^2) - add!(dh, :p, Lagrange{RefQuadrilateral,1}()) + add!(dh, :u, Lagrange{RefQuadrilateral, 1}()^2) + add!(dh, :p, Lagrange{RefQuadrilateral, 1}()) close!(dh) ch = ConstraintHandler(dh) close!(ch) @@ -452,12 +458,12 @@ end # Field coupling coupling = [ - # u p + # u p true true # v true false # q ] sparsity_pattern = init_sparsity_pattern(dh) - add_sparsity_entries!(sparsity_pattern, dh; coupling=coupling) + add_sparsity_entries!(sparsity_pattern, dh; coupling = coupling) K = allocate_matrix(sparsity_pattern) # Kch = allocate_matrix(dh, ch; coupling=coupling) # @test K.rowval == Kch.rowval @@ -484,13 +490,13 @@ end # Component coupling coupling = [ - # u1 u2 p + # u1 u2 p true true false # v1 true false true # v2 false true true # q ] sparsity_pattern = init_sparsity_pattern(dh) - add_sparsity_entries!(sparsity_pattern, dh; coupling=coupling) + add_sparsity_entries!(sparsity_pattern, dh; coupling = coupling) K = allocate_matrix(sparsity_pattern) # KS = create_symmetric_sparsity_pattern(dh; coupling=coupling) for j in u1dofs, i in vdofs @@ -525,7 +531,7 @@ end end # Error paths - @test_throws ErrorException("coupling not square") allocate_matrix(dh; coupling=[true true]) + @test_throws ErrorException("coupling not square") allocate_matrix(dh; coupling = [true true]) # @test_throws ErrorException("coupling not symmetric") create_symmetric_sparsity_pattern(dh; coupling=[true true; false true]) # @test_throws ErrorException("could not create coupling") create_symmetric_sparsity_pattern(dh; coupling=falses(100, 100)) @@ -533,10 +539,10 @@ end grid = generate_grid(Quadrilateral, (1, 2)) dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) - add!(sdh1, :u, Lagrange{RefQuadrilateral,1}()^2) - add!(sdh1, :p, Lagrange{RefQuadrilateral,1}()) + add!(sdh1, :u, Lagrange{RefQuadrilateral, 1}()^2) + add!(sdh1, :p, Lagrange{RefQuadrilateral, 1}()) sdh2 = SubDofHandler(dh, Set(2)) - add!(sdh2, :u, Lagrange{RefQuadrilateral,1}()^2) + add!(sdh2, :u, Lagrange{RefQuadrilateral, 1}()^2) close!(dh) sparsity_pattern = init_sparsity_pattern(dh) @@ -635,9 +641,9 @@ end i_dofs_v = i_dofs[dim1:vdim[1]:end] j_dofs_v = j_dofs[dim2:vdim[2]:end] for i_idx in i_dofs_v, j_idx in j_dofs_v - i = celldofs(dh,cell_idx)[i_idx] - j = celldofs(dh,cell2_idx)[j_idx] - is_cross_element && (i ∈ celldofs(dh,cell2_idx) || j ∈ celldofs(dh,cell_idx)) && continue + i = celldofs(dh, cell_idx)[i_idx] + j = celldofs(dh, cell2_idx)[j_idx] + is_cross_element && (i ∈ celldofs(dh, cell2_idx) || j ∈ celldofs(dh, cell_idx)) && continue @test is_stored(K, i, j) == coupling[coupling_idx...] end coupling_idx[2] += 1 @@ -651,34 +657,34 @@ end function check_coupling(dh, topology, K, coupling, interface_coupling) for cell_idx in eachindex(getcells(dh.grid)) sdh = dh.subdofhandlers[dh.cell_to_subdofhandler[cell_idx]] - coupling_idx = [1,1] - interface_coupling_idx = [1,1] - vdim = [1,1] + coupling_idx = [1, 1] + interface_coupling_idx = [1, 1] + vdim = [1, 1] # test inner coupling _check_dofs(K, dh, sdh, cell_idx, coupling, coupling_idx, vdim, [cell_idx], false) # test cross-element coupling neighborhood = Ferrite.get_facet_facet_neighborhood(topology, grid) neighbors = [neighborhood[cell_idx, i] for i in 1:size(neighborhood, 2)] - _check_dofs(K, dh, sdh, cell_idx, interface_coupling, interface_coupling_idx, vdim, [i[1][1] for i in neighbors[.!isempty.(neighbors)]], true) + _check_dofs(K, dh, sdh, cell_idx, interface_coupling, interface_coupling_idx, vdim, [i[1][1] for i in neighbors[.!isempty.(neighbors)]], true) end end grid = generate_grid(Quadrilateral, (2, 2)) topology = ExclusiveTopology(grid) dh = DofHandler(grid) - add!(dh, :u, DiscontinuousLagrange{RefQuadrilateral,1}()^2) - add!(dh, :p, DiscontinuousLagrange{RefQuadrilateral,1}()) - add!(dh, :w, Lagrange{RefQuadrilateral,1}()) + add!(dh, :u, DiscontinuousLagrange{RefQuadrilateral, 1}()^2) + add!(dh, :p, DiscontinuousLagrange{RefQuadrilateral, 1}()) + add!(dh, :w, Lagrange{RefQuadrilateral, 1}()) close!(dh) for coupling in couplings, interface_coupling in couplings - K = allocate_matrix(dh; coupling=coupling, topology = topology, interface_coupling = interface_coupling) + K = allocate_matrix(dh; coupling = coupling, topology = topology, interface_coupling = interface_coupling) all(coupling) && @test K == allocate_matrix(dh, topology = topology, interface_coupling = interface_coupling) check_coupling(dh, topology, K, coupling, interface_coupling) end # Error paths - @test_throws ErrorException("coupling not square") allocate_matrix(dh; coupling=[true true]) + @test_throws ErrorException("coupling not square") allocate_matrix(dh; coupling = [true true]) # @test_throws ErrorException("coupling not symmetric") allocate_matrix(dh; coupling=[true true; false true]) - @test_throws ErrorException("could not create coupling") allocate_matrix(dh; coupling=falses(100, 100)) + @test_throws ErrorException("could not create coupling") allocate_matrix(dh; coupling = falses(100, 100)) # Test coupling with subdomains # Note: `check_coupling` works for this case only because the second domain has dofs from the first domain in order. Otherwise tests like in continuous ip are required. @@ -687,15 +693,15 @@ end dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) - add!(sdh1, :u, DiscontinuousLagrange{RefQuadrilateral,1}()^2) - add!(sdh1, :y, DiscontinuousLagrange{RefQuadrilateral,1}()) - add!(sdh1, :p, Lagrange{RefQuadrilateral,1}()) + add!(sdh1, :u, DiscontinuousLagrange{RefQuadrilateral, 1}()^2) + add!(sdh1, :y, DiscontinuousLagrange{RefQuadrilateral, 1}()) + add!(sdh1, :p, Lagrange{RefQuadrilateral, 1}()) sdh2 = SubDofHandler(dh, Set(2)) - add!(sdh2, :u, DiscontinuousLagrange{RefQuadrilateral,1}()^2) + add!(sdh2, :u, DiscontinuousLagrange{RefQuadrilateral, 1}()^2) close!(dh) for coupling in couplings, interface_coupling in couplings - K = allocate_matrix(dh; coupling=coupling, topology = topology, interface_coupling = interface_coupling) + K = allocate_matrix(dh; coupling = coupling, topology = topology, interface_coupling = interface_coupling) all(coupling) && @test K == allocate_matrix(dh, topology = topology, interface_coupling = interface_coupling) check_coupling(dh, topology, K, coupling, interface_coupling) end @@ -704,11 +710,11 @@ end grid = generate_grid(Triangle, (2, 1)) topology = ExclusiveTopology(grid) dh = DofHandler(grid) - add!(dh, :u, CrouzeixRaviart{RefTriangle,1}()) + add!(dh, :u, CrouzeixRaviart{RefTriangle, 1}()) close!(dh) - coupling = trues(3,3) - K = allocate_matrix(dh; coupling=coupling, topology = topology, interface_coupling = coupling) - K_cont = allocate_matrix(dh; coupling=coupling, topology = topology, interface_coupling = falses(3,3)) + coupling = trues(3, 3) + K = allocate_matrix(dh; coupling = coupling, topology = topology, interface_coupling = coupling) + K_cont = allocate_matrix(dh; coupling = coupling, topology = topology, interface_coupling = falses(3, 3)) K_default = allocate_matrix(dh) @test K == K_cont == K_default end @@ -723,12 +729,12 @@ end # 1 ____ 2 2 dim = 2 - grid = generate_grid(Quadrilateral, (1,1)) - line1 = Line((2,4)) + grid = generate_grid(Quadrilateral, (1, 1)) + line1 = Line((2, 4)) grid = Grid([grid.cells[1], line1], grid.nodes) order = 2 - ip_solid = Lagrange{RefQuadrilateral, order}()#^dim + ip_solid = Lagrange{RefQuadrilateral, order}() #^dim ip_shell = Lagrange{RefLine, order}() dh = DofHandler(grid) @@ -756,12 +762,12 @@ end # 2--------4 dim = 2 - grid = generate_grid(Hexahedron, (1,1,1)) - shell = Quadrilateral((4,3,7,8)) + grid = generate_grid(Hexahedron, (1, 1, 1)) + shell = Quadrilateral((4, 3, 7, 8)) grid = Grid([grid.cells[1], shell], grid.nodes) order = 2 - ip_solid = Lagrange{RefHexahedron, order}()#^dim + ip_solid = Lagrange{RefHexahedron, order}() #^dim ip_shell = Lagrange{RefQuadrilateral, order}() dh = DofHandler(grid) @@ -781,9 +787,9 @@ end #facedofs!(dofs, dh, FaceIndex(1,4)) #Shared node dofs - @test dofsshell[1:4] == [3,4,8,7] + @test dofsshell[1:4] == [3, 4, 8, 7] #Shared edge dofs - @test dofsshell[5:8] == [11,20,15,19] + @test dofsshell[5:8] == [11, 20, 15, 19] #Shared face dof @test dofsshell[9] == 24 end diff --git a/test/test_examples.jl b/test/test_examples.jl index dca1ed73cf..bb3b10a0d4 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -44,8 +44,9 @@ module TestComputationalHomogenization include(joinpath(@__DIR__, "../docs/download_resources.jl")) mktempdir() do dir cd(dir) do - cp(joinpath(@__DIR__, "../docs/src/tutorials/periodic-rve.msh"), - joinpath(dir, "periodic-rve.msh") + cp( + joinpath(@__DIR__, "../docs/src/tutorials/periodic-rve.msh"), + joinpath(dir, "periodic-rve.msh") ) include(joinpath(@__DIR__, "../docs/src/literate-tutorials/computational_homogenization.jl")) end @@ -53,14 +54,14 @@ module TestComputationalHomogenization end module TestStokesFlow -if !Sys.iswindows() - mktempdir() do dir - cd(dir) do - include(joinpath(@__DIR__, "../docs/src/literate-tutorials/stokes-flow.jl")) + if !Sys.iswindows() + mktempdir() do dir + cd(dir) do + include(joinpath(@__DIR__, "../docs/src/literate-tutorials/stokes-flow.jl")) + end end end end -end module TestMultiThreading mktempdir() do dir diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index 9420eee442..672713b315 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -1,193 +1,193 @@ @testset "FacetValues" begin -for (scalar_interpol, quad_rule) in ( - (Lagrange{RefLine, 1}(), FacetQuadratureRule{RefLine}(2)), - (Lagrange{RefLine, 2}(), FacetQuadratureRule{RefLine}(2)), - (Lagrange{RefQuadrilateral, 1}(), FacetQuadratureRule{RefQuadrilateral}(2)), - (Lagrange{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), - (Lagrange{RefTriangle, 1}(), FacetQuadratureRule{RefTriangle}(2)), - (Lagrange{RefTriangle, 2}(), FacetQuadratureRule{RefTriangle}(2)), - (Lagrange{RefHexahedron, 1}(), FacetQuadratureRule{RefHexahedron}(2)), - (Serendipity{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), - (Lagrange{RefTetrahedron, 1}(), FacetQuadratureRule{RefTetrahedron}(2)), - (Lagrange{RefTetrahedron, 2}(), FacetQuadratureRule{RefTetrahedron}(2)), - (Lagrange{RefPyramid, 2}(), FacetQuadratureRule{RefPyramid}(2)), - (Lagrange{RefPrism, 2}(), FacetQuadratureRule{RefPrism}(2)), - ) - for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)), DiffOrder in 1:2 - (DiffOrder==2 && Ferrite.getorder(func_interpol)==1) && continue #No need to test linear interpolations again - geom_interpol = scalar_interpol # Tests below assume this - n_basefunc_base = getnbasefunctions(scalar_interpol) - update_gradients = true - update_hessians = (DiffOrder==2 && Ferrite.getorder(func_interpol) > 1) - fv = FacetValues(quad_rule, func_interpol, geom_interpol; update_gradients, update_hessians) - if update_gradients && !update_hessians # Check correct and type-stable default constructor - fv_default = @inferred FacetValues(quad_rule, func_interpol, geom_interpol) - @test typeof(fv) === typeof(fv_default) - @inferred FacetValues(quad_rule, func_interpol, geom_interpol; update_hessians=Val(true)) - end + for (scalar_interpol, quad_rule) in ( + (Lagrange{RefLine, 1}(), FacetQuadratureRule{RefLine}(2)), + (Lagrange{RefLine, 2}(), FacetQuadratureRule{RefLine}(2)), + (Lagrange{RefQuadrilateral, 1}(), FacetQuadratureRule{RefQuadrilateral}(2)), + (Lagrange{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), + (Lagrange{RefTriangle, 1}(), FacetQuadratureRule{RefTriangle}(2)), + (Lagrange{RefTriangle, 2}(), FacetQuadratureRule{RefTriangle}(2)), + (Lagrange{RefHexahedron, 1}(), FacetQuadratureRule{RefHexahedron}(2)), + (Serendipity{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), + (Lagrange{RefTetrahedron, 1}(), FacetQuadratureRule{RefTetrahedron}(2)), + (Lagrange{RefTetrahedron, 2}(), FacetQuadratureRule{RefTetrahedron}(2)), + (Lagrange{RefPyramid, 2}(), FacetQuadratureRule{RefPyramid}(2)), + (Lagrange{RefPrism, 2}(), FacetQuadratureRule{RefPrism}(2)), + ) + for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)), DiffOrder in 1:2 + (DiffOrder == 2 && Ferrite.getorder(func_interpol) == 1) && continue # No need to test linear interpolations again + geom_interpol = scalar_interpol # Tests below assume this + n_basefunc_base = getnbasefunctions(scalar_interpol) + update_gradients = true + update_hessians = (DiffOrder == 2 && Ferrite.getorder(func_interpol) > 1) + fv = FacetValues(quad_rule, func_interpol, geom_interpol; update_gradients, update_hessians) + if update_gradients && !update_hessians # Check correct and type-stable default constructor + fv_default = @inferred FacetValues(quad_rule, func_interpol, geom_interpol) + @test typeof(fv) === typeof(fv_default) + @inferred FacetValues(quad_rule, func_interpol, geom_interpol; update_hessians = Val(true)) + end - rdim = Ferrite.getrefdim(func_interpol) - n_basefuncs = getnbasefunctions(func_interpol) + rdim = Ferrite.getrefdim(func_interpol) + n_basefuncs = getnbasefunctions(func_interpol) - @test getnbasefunctions(fv) == n_basefuncs + @test getnbasefunctions(fv) == n_basefuncs - coords, n = valid_coordinates_and_normals(func_interpol) - for face in 1:Ferrite.nfacets(func_interpol) - reinit!(fv, coords, face) - @test Ferrite.getcurrentfacet(fv) == face + coords, n = valid_coordinates_and_normals(func_interpol) + for face in 1:Ferrite.nfacets(func_interpol) + reinit!(fv, coords, face) + @test Ferrite.getcurrentfacet(fv) == face - # We test this by applying a given deformation gradient on all the nodes. - # Since this is a linear deformation we should get back the exact values - # from the interpolation. - V, G, H = if func_interpol isa Ferrite.ScalarInterpolation - (rand(), rand(Tensor{1, rdim}), Tensor{2, rdim}((i,j)-> i==j ? rand() : 0.0)) - else - (rand(Tensor{1, rdim}), rand(Tensor{2, rdim}), Tensor{3, rdim}((i,j,k)-> i==j==k ? rand() : 0.0)) - end - - u_funk(x,V,G,H) = begin - if update_hessians - 0.5*x⋅H⋅x + G⋅x + V + # We test this by applying a given deformation gradient on all the nodes. + # Since this is a linear deformation we should get back the exact values + # from the interpolation. + V, G, H = if func_interpol isa Ferrite.ScalarInterpolation + (rand(), rand(Tensor{1, rdim}), Tensor{2, rdim}((i, j) -> i == j ? rand() : 0.0)) else - G⋅x + V + (rand(Tensor{1, rdim}), rand(Tensor{2, rdim}), Tensor{3, rdim}((i, j, k) -> i == j == k ? rand() : 0.0)) end - end - _ue = [u_funk(coords[i],V,G,H) for i in 1:n_basefunc_base] - ue = reinterpret(Float64, _ue) + function u_funk(x, V, G, H) + if update_hessians + 0.5 * x ⋅ H ⋅ x + G ⋅ x + V + else + G ⋅ x + V + end + end - for i in 1:getnquadpoints(fv) - xqp = spatial_coordinate(fv, i, coords) - Hqp, Gqp, Vqp = Tensors.hessian(x -> u_funk(x,V,G,H), xqp, :all) + _ue = [u_funk(coords[i], V, G, H) for i in 1:n_basefunc_base] + ue = reinterpret(Float64, _ue) - @test function_value(fv, i, ue) ≈ Vqp - @test function_gradient(fv, i, ue) ≈ Gqp - if update_hessians - #Note, the jacobian of the element is constant, which makes the hessian (of the mapping) - #zero. So this is not the optimal test - @test Ferrite.function_hessian(fv, i, ue) ≈ Hqp - end - if func_interpol isa Ferrite.VectorInterpolation - @test function_symmetric_gradient(fv, i, ue) ≈ 0.5(Gqp + Gqp') - @test function_divergence(fv, i, ue) ≈ tr(Gqp) - rdim == 3 && @test function_curl(fv, i, ue) ≈ Ferrite.curl_from_gradient(Gqp) - else - @test function_divergence(fv, i, ue) ≈ sum(Gqp) + for i in 1:getnquadpoints(fv) + xqp = spatial_coordinate(fv, i, coords) + Hqp, Gqp, Vqp = Tensors.hessian(x -> u_funk(x, V, G, H), xqp, :all) + + @test function_value(fv, i, ue) ≈ Vqp + @test function_gradient(fv, i, ue) ≈ Gqp + if update_hessians + # Note, the jacobian of the element is constant, which makes the hessian (of the mapping) + # zero. So this is not the optimal test + @test Ferrite.function_hessian(fv, i, ue) ≈ Hqp + end + if func_interpol isa Ferrite.VectorInterpolation + @test function_symmetric_gradient(fv, i, ue) ≈ 0.5(Gqp + Gqp') + @test function_divergence(fv, i, ue) ≈ tr(Gqp) + rdim == 3 && @test function_curl(fv, i, ue) ≈ Ferrite.curl_from_gradient(Gqp) + else + @test function_divergence(fv, i, ue) ≈ sum(Gqp) + end end - end - #Test CellValues when input is a ::Vector{<:Vec} (most of which is deprecated) - ue_vec = [zero(Vec{rdim,Float64}) for i in 1:n_basefunc_base] - G_vector = rand(Tensor{2, rdim}) - for i in 1:n_basefunc_base - ue_vec[i] = G_vector ⋅ coords[i] - end + # Test CellValues when input is a ::Vector{<:Vec} (most of which is deprecated) + ue_vec = [zero(Vec{rdim, Float64}) for i in 1:n_basefunc_base] + G_vector = rand(Tensor{2, rdim}) + for i in 1:n_basefunc_base + ue_vec[i] = G_vector ⋅ coords[i] + end - for i in 1:getnquadpoints(fv) - if func_interpol isa Ferrite.ScalarInterpolation - @test function_gradient(fv, i, ue_vec) ≈ G_vector - else# func_interpol isa Ferrite.VectorInterpolation - @test_throws Ferrite.DeprecationError function_gradient(fv, i, ue_vec) - @test_throws Ferrite.DeprecationError function_symmetric_gradient(fv, i, ue_vec) - @test_throws Ferrite.DeprecationError function_divergence(fv, i, ue_vec) - if rdim == 3 - @test_throws Ferrite.DeprecationError function_curl(fv, i, ue_vec) + for i in 1:getnquadpoints(fv) + if func_interpol isa Ferrite.ScalarInterpolation + @test function_gradient(fv, i, ue_vec) ≈ G_vector + else # func_interpol isa Ferrite.VectorInterpolation + @test_throws Ferrite.DeprecationError function_gradient(fv, i, ue_vec) + @test_throws Ferrite.DeprecationError function_symmetric_gradient(fv, i, ue_vec) + @test_throws Ferrite.DeprecationError function_divergence(fv, i, ue_vec) + if rdim == 3 + @test_throws Ferrite.DeprecationError function_curl(fv, i, ue_vec) + end + @test_throws Ferrite.DeprecationError function_value(fv, i, ue_vec) # no value to test against end - @test_throws Ferrite.DeprecationError function_value(fv, i, ue_vec) #no value to test against end - end - #Check if the non-linear mapping is correct - #Only do this for one interpolation becuase it relise on AD on "iterative function" - if scalar_interpol === Lagrange{RefQuadrilateral, 2}() - coords_nl = [x+rand(x)*0.01 for x in coords] #add some displacement to nodes - reinit!(fv, coords_nl, face) + # Check if the non-linear mapping is correct + # Only do this for one interpolation becuase it relise on AD on "iterative function" + if scalar_interpol === Lagrange{RefQuadrilateral, 2}() + coords_nl = [x + rand(x) * 0.01 for x in coords] # add some displacement to nodes + reinit!(fv, coords_nl, face) + + _ue_nl = [u_funk(coords_nl[i], V, G, H) for i in 1:n_basefunc_base] + ue_nl = reinterpret(Float64, _ue_nl) + + for i in 1:getnquadpoints(fv) + xqp = spatial_coordinate(fv, i, coords_nl) + Hqp, Gqp, Vqp = Tensors.hessian(x -> function_value_from_physical_coord(func_interpol, coords_nl, x, ue_nl), xqp, :all) + @test function_value(fv, i, ue_nl) ≈ Vqp + @test function_gradient(fv, i, ue_nl) ≈ Gqp + if update_hessians + @test Ferrite.function_hessian(fv, i, ue_nl) ≈ Hqp + end + end + reinit!(fv, coords, face) # reinit back to old coords + end - _ue_nl = [u_funk(coords_nl[i],V,G,H) for i in 1:n_basefunc_base] - ue_nl = reinterpret(Float64, _ue_nl) + # Test of volume + vol = 0.0 for i in 1:getnquadpoints(fv) - xqp = spatial_coordinate(fv, i, coords_nl) - Hqp, Gqp, Vqp = Tensors.hessian(x -> function_value_from_physical_coord(func_interpol, coords_nl, x, ue_nl), xqp, :all) - @test function_value(fv, i, ue_nl) ≈ Vqp - @test function_gradient(fv, i, ue_nl) ≈ Gqp - if update_hessians - @test Ferrite.function_hessian(fv, i, ue_nl) ≈ Hqp - end + vol += getdetJdV(fv, i) + end + let ip_base = func_interpol isa VectorizedInterpolation ? func_interpol.ip : func_interpol + x_face = coords[[Ferrite.facetdof_indices(ip_base)[face]...]] + @test vol ≈ calculate_facet_area(ip_base, x_face, face) end - reinit!(fv, coords, face) # reinit back to old coords - end + # Test quadrature rule after reinit! with ref. coords + x = Ferrite.reference_coordinates(func_interpol) + reinit!(fv, x, face) + vol = 0.0 + for i in 1:getnquadpoints(fv) + vol += getdetJdV(fv, i) + end + @test vol ≈ reference_face_area(func_interpol, face) - # Test of volume - vol = 0.0 - for i in 1:getnquadpoints(fv) - vol += getdetJdV(fv,i) - end - let ip_base = func_interpol isa VectorizedInterpolation ? func_interpol.ip : func_interpol - x_face = coords[[Ferrite.facetdof_indices(ip_base)[face]...]] - @test vol ≈ calculate_facet_area(ip_base, x_face, face) - end + # Test spatial coordinate (after reinit with ref.coords we should get back the quad_points) + # # TODO: Renable somehow after quad rule is no longer stored in FacetValues + # for (i, qp_x) in enumerate(getpoints(quad_rule)) + # @test spatial_coordinate(fv, i, x) ≈ qp_x + # end - # Test quadrature rule after reinit! with ref. coords - x = Ferrite.reference_coordinates(func_interpol) - reinit!(fv, x, face) - vol = 0.0 - for i in 1:getnquadpoints(fv) - vol += getdetJdV(fv, i) end - @test vol ≈ reference_face_area(func_interpol, face) - - # Test spatial coordinate (after reinit with ref.coords we should get back the quad_points) - # TODO: Renable somehow after quad rule is no longer stored in FacetValues - #for (i, qp_x) in enumerate(getpoints(quad_rule)) - # @test spatial_coordinate(fv, i, x) ≈ qp_x - #end - - end - @testset "copy(::FacetValues)" begin - fvc = copy(fv) - @test typeof(fv) == typeof(fvc) - - # Test that all mutable types in FunctionValues and GeometryMapping have been copied - for key in (:fun_values, :geo_mapping) - for i in eachindex(getfield(fv, key)) - val = getfield(fv, key)[i] - valc = getfield(fvc, key)[i] - for fname in fieldnames(typeof(val)) - v = getfield(val, fname) - vc = getfield(valc, fname) - isbits(v) || @test v !== vc - @test v == vc + @testset "copy(::FacetValues)" begin + fvc = copy(fv) + @test typeof(fv) == typeof(fvc) + + # Test that all mutable types in FunctionValues and GeometryMapping have been copied + for key in (:fun_values, :geo_mapping) + for i in eachindex(getfield(fv, key)) + val = getfield(fv, key)[i] + valc = getfield(fvc, key)[i] + for fname in fieldnames(typeof(val)) + v = getfield(val, fname) + vc = getfield(valc, fname) + isbits(v) || @test v !== vc + @test v == vc + end end end - end - # Test that fqr, detJdV, and normals, are copied as expected. - # Note that qr remain aliased, as defined by `copy(qr)=qr`, see quadrature.jl. - for fname in (:fqr, :detJdV, :normals) - v = getfield(fv, fname) - vc = getfield(fvc, fname) - if fname !== :fqr # Test unaliased - @test v !== vc + # Test that fqr, detJdV, and normals, are copied as expected. + # Note that qr remain aliased, as defined by `copy(qr)=qr`, see quadrature.jl. + for fname in (:fqr, :detJdV, :normals) + v = getfield(fv, fname) + vc = getfield(fvc, fname) + if fname !== :fqr # Test unaliased + @test v !== vc + end + @test v == vc end - @test v == vc end end end -end - -@testset "show" begin - # Just smoke test to make sure show doesn't error. - fv = FacetValues(FacetQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) - showstring = sprint(show, MIME"text/plain"(), fv) - @test startswith(showstring, "FacetValues(scalar, rdim=2, sdim=2): 2 quadrature points per face") - @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()") - @test contains(showstring, "Geometric interpolation: Lagrange{RefQuadrilateral, 1}()^2") - fv2 = copy(fv) - push!(Ferrite.getweights(fv2.fqr.face_rules[1]), 1) - showstring = sprint(show, MIME"text/plain"(), fv2) - @test startswith(showstring, "FacetValues(scalar, rdim=2, sdim=2): (3, 2, 2, 2) quadrature points on each face") -end + + @testset "show" begin + # Just smoke test to make sure show doesn't error. + fv = FacetValues(FacetQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral, 2}()) + showstring = sprint(show, MIME"text/plain"(), fv) + @test startswith(showstring, "FacetValues(scalar, rdim=2, sdim=2): 2 quadrature points per face") + @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()") + @test contains(showstring, "Geometric interpolation: Lagrange{RefQuadrilateral, 1}()^2") + fv2 = copy(fv) + push!(Ferrite.getweights(fv2.fqr.face_rules[1]), 1) + showstring = sprint(show, MIME"text/plain"(), fv2) + @test startswith(showstring, "FacetValues(scalar, rdim=2, sdim=2): (3, 2, 2, 2) quadrature points on each face") + end end # of testset diff --git a/test/test_grid_addboundaryset.jl b/test/test_grid_addboundaryset.jl index 33514c3bda..ed3ca549ad 100644 --- a/test/test_grid_addboundaryset.jl +++ b/test/test_grid_addboundaryset.jl @@ -1,57 +1,63 @@ @testset "grid boundary" begin function _extractboundary(grid::Ferrite.AbstractGrid{3}, topology::ExclusiveTopology, _ftype::Function) - return union(( _ftype(grid, topology, x -> x[3] ≈ -1.0), - _ftype(grid, topology, x -> x[3] ≈ 1.0), - _ftype(grid, topology, x -> x[1] ≈ 1.0), - _ftype(grid, topology, x -> x[1] ≈ -1.0), - _ftype(grid, topology, x -> x[2] ≈ 1.0), - _ftype(grid, topology, x -> x[2] ≈ -1.0))...) + return union( + _ftype(grid, topology, x -> x[3] ≈ -1.0), + _ftype(grid, topology, x -> x[3] ≈ 1.0), + _ftype(grid, topology, x -> x[1] ≈ 1.0), + _ftype(grid, topology, x -> x[1] ≈ -1.0), + _ftype(grid, topology, x -> x[2] ≈ 1.0), + _ftype(grid, topology, x -> x[2] ≈ -1.0), + ) end function _extractboundary(grid::Ferrite.AbstractGrid{2}, topology::ExclusiveTopology, _ftype::Function) - return union(( _ftype(grid, topology, x -> x[1] ≈ 1.0), - _ftype(grid, topology, x -> x[1] ≈ -1.0), - _ftype(grid, topology, x -> x[2] ≈ 1.0), - _ftype(grid, topology, x -> x[2] ≈ -1.0))...) + return union( + _ftype(grid, topology, x -> x[1] ≈ 1.0), + _ftype(grid, topology, x -> x[1] ≈ -1.0), + _ftype(grid, topology, x -> x[2] ≈ 1.0), + _ftype(grid, topology, x -> x[2] ≈ -1.0), + ) end function extractboundary(grid::Ferrite.AbstractGrid{3}, topology::ExclusiveTopology) - facets = _extractboundary(grid, topology, Ferrite.create_boundaryfacetset) - faces = _extractboundary(grid, topology, Ferrite.create_boundaryfaceset) - edges = _extractboundary(grid, topology, Ferrite.create_boundaryedgeset) + facets = _extractboundary(grid, topology, Ferrite.create_boundaryfacetset) + faces = _extractboundary(grid, topology, Ferrite.create_boundaryfaceset) + edges = _extractboundary(grid, topology, Ferrite.create_boundaryedgeset) vertices = _extractboundary(grid, topology, Ferrite.create_boundaryvertexset) return union(facets, faces, edges, vertices) end function extractboundary(grid::Ferrite.AbstractGrid{2}, topology::ExclusiveTopology) - facets = _extractboundary(grid, topology, Ferrite.create_boundaryfacetset) - edges = _extractboundary(grid, topology, Ferrite.create_boundaryedgeset) + facets = _extractboundary(grid, topology, Ferrite.create_boundaryfacetset) + edges = _extractboundary(grid, topology, Ferrite.create_boundaryedgeset) vertices = _extractboundary(grid, topology, Ferrite.create_boundaryvertexset) return union(facets, edges, vertices) end function _extractboundarycheck(grid::Ferrite.AbstractGrid{3}, _ftype::Function) - return union(( - _ftype(grid, x -> x[3] ≈ -1.0), - _ftype(grid, x -> x[3] ≈ 1.0), - _ftype(grid, x -> x[1] ≈ 1.0), - _ftype(grid, x -> x[1] ≈ -1.0), - _ftype(grid, x -> x[2] ≈ 1.0), - _ftype(grid, x -> x[2] ≈ -1.0))...) + return union( + _ftype(grid, x -> x[3] ≈ -1.0), + _ftype(grid, x -> x[3] ≈ 1.0), + _ftype(grid, x -> x[1] ≈ 1.0), + _ftype(grid, x -> x[1] ≈ -1.0), + _ftype(grid, x -> x[2] ≈ 1.0), + _ftype(grid, x -> x[2] ≈ -1.0), + ) end function _extractboundarycheck(grid::Ferrite.AbstractGrid{2}, _ftype::Function) - return union(( - _ftype(grid, x -> x[1] ≈ 1.0), - _ftype(grid, x -> x[1] ≈ -1.0), - _ftype(grid, x -> x[2] ≈ 1.0), - _ftype(grid, x -> x[2] ≈ -1.0))...) + return union( + _ftype(grid, x -> x[1] ≈ 1.0), + _ftype(grid, x -> x[1] ≈ -1.0), + _ftype(grid, x -> x[2] ≈ 1.0), + _ftype(grid, x -> x[2] ≈ -1.0), + ) end function extractboundarycheck(grid::Ferrite.AbstractGrid{3}) - faces = _extractboundarycheck(grid, Ferrite.create_faceset) - facets = _extractboundarycheck(grid, Ferrite.create_facetset) - edges = _extractboundarycheck(grid, Ferrite.create_edgeset) + faces = _extractboundarycheck(grid, Ferrite.create_faceset) + facets = _extractboundarycheck(grid, Ferrite.create_facetset) + edges = _extractboundarycheck(grid, Ferrite.create_edgeset) vertices = _extractboundarycheck(grid, Ferrite.create_vertexset) return union(facets, faces, edges, vertices) end function extractboundarycheck(grid::Ferrite.AbstractGrid{2}) - facets = _extractboundarycheck(grid, Ferrite.create_facetset) - edges = _extractboundarycheck(grid, Ferrite.create_edgeset) + facets = _extractboundarycheck(grid, Ferrite.create_facetset) + edges = _extractboundarycheck(grid, Ferrite.create_edgeset) vertices = _extractboundarycheck(grid, Ferrite.create_vertexset) return union(facets, edges, vertices) end @@ -164,27 +170,27 @@ end =# @testset "addboundaryset ($cell_type)" for cell_type in [ - # Line, # topology construction error - # QuadraticLine, # topology construction error + # Line, # topology construction error + # QuadraticLine, # topology construction error - Triangle, - QuadraticTriangle, + Triangle, + QuadraticTriangle, - Quadrilateral, - QuadraticQuadrilateral, + Quadrilateral, + QuadraticQuadrilateral, - Tetrahedron, - # QuadraticTetrahedron, # grid construction error + Tetrahedron, + # QuadraticTetrahedron, # grid construction error - Hexahedron, - # QuadraticHexahedron, # grid construction error - # Wedge, # grid construction error (nfacepoints) + Hexahedron, + # QuadraticHexahedron, # grid construction error + # Wedge, # grid construction error (nfacepoints) - # SerendipityQuadraticQuadrilateral, # grid construction error - SerendipityQuadraticHexahedron - ] + # SerendipityQuadraticQuadrilateral, # grid construction error + SerendipityQuadraticHexahedron, + ] # Grid tests - Regression test for https://github.com/Ferrite-FEM/Ferrite.jl/discussions/565 - grid = generate_grid(cell_type, ntuple(i->3, Ferrite.getrefdim(cell_type))) + grid = generate_grid(cell_type, ntuple(i -> 3, Ferrite.getrefdim(cell_type))) topology = ExclusiveTopology(grid) @test extractboundary(grid, topology) == extractboundarycheck(grid) diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index 1d0d06125b..9f1ad073cd 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -8,30 +8,32 @@ else end @testset "Grid, DofHandler, vtk" begin - for (celltype, dim) in ((Line, 1), - (QuadraticLine, 1), - (Quadrilateral, 2), - (QuadraticQuadrilateral, 2), - (Triangle, 2), - (QuadraticTriangle, 2), - (Hexahedron, 3), - (SerendipityQuadraticHexahedron, 3), - (Tetrahedron, 3), - (Wedge, 3), - (Pyramid, 3)) + for (celltype, dim) in ( + (Line, 1), + (QuadraticLine, 1), + (Quadrilateral, 2), + (QuadraticQuadrilateral, 2), + (Triangle, 2), + (QuadraticTriangle, 2), + (Hexahedron, 3), + (SerendipityQuadraticHexahedron, 3), + (Tetrahedron, 3), + (Wedge, 3), + (Pyramid, 3), + ) # create test grid, do some operations on it and then test # the resulting sha1 of the stored vtk file # after manually checking the exported vtk - nels = ntuple(x->5, dim) - right = Vec{dim, Float64}(ntuple(x->1.5, dim)) + nels = ntuple(x -> 5, dim) + right = Vec{dim, Float64}(ntuple(x -> 1.5, dim)) left = -right grid = generate_grid(celltype, nels, left, right) - transform_coordinates!(grid, x-> 2x) + transform_coordinates!(grid, x -> 2x) - radius = 2*1.5 - addcellset!(grid, "cell-1", [1,]) + radius = 2 * 1.5 + addcellset!(grid, "cell-1", [1]) addcellset!(grid, "middle-cells", x -> norm(x) < radius) addfacetset!(grid, "middle-facetset", x -> norm(x) < radius) addfacetset!(grid, "right-facetset", getfacetset(grid, "right")) @@ -45,12 +47,12 @@ end end # test the sha of the file - sha = bytes2hex(open(SHA.sha1, gridfilename*".vtu")) + sha = bytes2hex(open(SHA.sha1, gridfilename * ".vtu")) if OVERWRITE_CHECKSUMS write(csio, sha, "\n") else @test sha in split(chomp(readline(csio))) - rm(gridfilename*".vtu") + rm(gridfilename * ".vtu") end # Create a DofHandler, add some things, write to file and @@ -62,12 +64,12 @@ end add!(dofhandler, :displacement, ip^dim) close!(dofhandler) ch = ConstraintHandler(dofhandler) - dbc = Dirichlet(:temperature, getfacetset(grid, "right-facetset"), (x,t)->1) + dbc = Dirichlet(:temperature, getfacetset(grid, "right-facetset"), (x, t) -> 1) add!(ch, dbc) - dbc = Dirichlet(:temperature, getfacetset(grid, "left"), (x,t)->4) + dbc = Dirichlet(:temperature, getfacetset(grid, "left"), (x, t) -> 4) add!(ch, dbc) for d in 1:dim - dbc = Dirichlet(:displacement, union(getfacetset(grid, "left")), (x,t) -> d, d) + dbc = Dirichlet(:displacement, union(getfacetset(grid, "left")), (x, t) -> d, d) add!(ch, dbc) end close!(ch) @@ -84,12 +86,12 @@ end end # test the sha of the file - sha = bytes2hex(open(SHA.sha1, dofhandlerfilename*".vtu")) + sha = bytes2hex(open(SHA.sha1, dofhandlerfilename * ".vtu")) if OVERWRITE_CHECKSUMS write(csio, sha, "\n") else @test sha in split(chomp(readline(csio))) - rm(dofhandlerfilename*".vtu") + rm(dofhandlerfilename * ".vtu") end minv, maxv = Ferrite.bounding_box(grid) @@ -97,10 +99,10 @@ end @test maxv ≈ 2right # Consistency check for topological queries - @test Ferrite.reference_vertices(getcells(grid,1)) == Ferrite.reference_vertices(Ferrite.getrefshape(celltype)) - @test Ferrite.reference_edges(getcells(grid,1)) == Ferrite.reference_edges(Ferrite.getrefshape(celltype)) - @test Ferrite.reference_faces(getcells(grid,1)) == Ferrite.reference_faces(Ferrite.getrefshape(celltype)) - @test Ferrite.reference_facets(getcells(grid,1)) == Ferrite.reference_facets(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_vertices(getcells(grid, 1)) == Ferrite.reference_vertices(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_edges(getcells(grid, 1)) == Ferrite.reference_edges(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_faces(getcells(grid, 1)) == Ferrite.reference_faces(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_facets(getcells(grid, 1)) == Ferrite.reference_facets(Ferrite.getrefshape(celltype)) end end # of testset @@ -117,11 +119,11 @@ close(csio) end # 3D grid - grid = generate_grid(Hexahedron, (1,1,1)) + grid = generate_grid(Hexahedron, (1, 1, 1)) - sym_tensor_data = [SymmetricTensor{2,3}(ntuple(i->i, 6)) for j=1.0:8.0] - tensor_data = [Tensor{2,3}(ntuple(i->i, 9)) for j=1.0:8.0] - vector_data = [Vec{3}(ntuple(i->i, 3)) for j=1:8] + sym_tensor_data = [SymmetricTensor{2, 3}(ntuple(i -> i, 6)) for j in 1.0:8.0] + tensor_data = [Tensor{2, 3}(ntuple(i -> i, 9)) for j in 1.0:8.0] + vector_data = [Vec{3}(ntuple(i -> i, 3)) for j in 1:8] filename_3d = "test_vtk_3d" VTKGridFile(filename_3d, grid) do vtk::VTKGridFile @@ -131,12 +133,12 @@ close(csio) end # 2D grid - grid = generate_grid(Quadrilateral, (1,1)) + grid = generate_grid(Quadrilateral, (1, 1)) - sym_tensor_data = [SymmetricTensor{2,2}(ntuple(i->i, 3)) for j=1.0:4.0] - tensor_data = [Tensor{2,2}(ntuple(i->i, 4)) for j=1.0:4.0] - tensor_data_1D = [SymmetricTensor{2,1}(ntuple(i->i, 1)) for j=1.0:4.0] - vector_data = [Vec{2}(ntuple(i->i, 2)) for j=1:4] + sym_tensor_data = [SymmetricTensor{2, 2}(ntuple(i -> i, 3)) for j in 1.0:4.0] + tensor_data = [Tensor{2, 2}(ntuple(i -> i, 4)) for j in 1.0:4.0] + tensor_data_1D = [SymmetricTensor{2, 1}(ntuple(i -> i, 1)) for j in 1.0:4.0] + vector_data = [Vec{2}(ntuple(i -> i, 2)) for j in 1:4] filename_2d = "test_vtk_2d" VTKGridFile(filename_2d, grid) do vtk::VTKGridFile @@ -149,12 +151,12 @@ close(csio) # test the shas of the files files = [filename_3d, filename_2d] for filename in files - sha = bytes2hex(open(SHA.sha1, filename*".vtu")) + sha = bytes2hex(open(SHA.sha1, filename * ".vtu")) if OVERWRITE_CHECKSUMS write(csio, sha, "\n") else @test sha in split(chomp(readline(csio))) - rm(filename*".vtu") + rm(filename * ".vtu") end end @@ -163,9 +165,9 @@ end @testset "Grid utils" begin - grid = Ferrite.generate_grid(QuadraticQuadrilateral, (1, 1), Vec((0.,0.)), Vec((1.,1.))) + grid = Ferrite.generate_grid(QuadraticQuadrilateral, (1, 1), Vec((0.0, 0.0)), Vec((1.0, 1.0))) - addcellset!(grid, "cell_set", [1]); + addcellset!(grid, "cell_set", [1]) node_set = Set(1:getnnodes(grid)) addnodeset!(grid, "node_set", node_set) @@ -180,7 +182,7 @@ end @test getcells(grid, "cell_set") == [getcells(grid, 1)] # CellIterator on a grid without DofHandler - grid = generate_grid(Triangle, (4,4)) + grid = generate_grid(Triangle, (4, 4)) n = 0 ci = CellIterator(grid) @test length(ci) == getncells(grid) @@ -189,10 +191,10 @@ end getnodes(c) n += cellid(c) end - @test n == div(getncells(grid)*(getncells(grid) + 1), 2) + @test n == div(getncells(grid) * (getncells(grid) + 1), 2) # FacetCache - grid = generate_grid(Triangle, (3,3)) + grid = generate_grid(Triangle, (3, 3)) fc = FacetCache(grid) facetindex = first(getfacetset(grid, "left")) cell_id, facet_id = facetindex @@ -228,230 +230,238 @@ end end # InterfaceCache - grid = generate_grid(Quadrilateral, (2,1)) + grid = generate_grid(Quadrilateral, (2, 1)) ic = InterfaceCache(grid) - reinit!(ic, FaceIndex(1,2), FaceIndex(2,4)) + reinit!(ic, FaceIndex(1, 2), FaceIndex(2, 4)) @test interfacedofs(ic) == Int[] # Empty because no DofHandler given ip = DiscontinuousLagrange{RefQuadrilateral, 1}() dh = DofHandler(grid); add!(dh, :u, ip); close!(dh) ic = InterfaceCache(dh) - reinit!(ic, FaceIndex(1,2), FaceIndex(2,4)) + reinit!(ic, FaceIndex(1, 2), FaceIndex(2, 4)) @test interfacedofs(ic) == collect(1:8) # Mixed Elements dim = 2 nodes = [Node((-1.0, 0.0)), Node((0.0, 0.0)), Node((1.0, 0.0)), Node((-1.0, -1.0)), Node((0.0, 1.0))] cells = [ - Quadrilateral((1,2,5,4)), - Triangle((3,5,2)), - ] + Quadrilateral((1, 2, 5, 4)), + Triangle((3, 5, 2)), + ] grid = Grid(cells, nodes) ip1 = DiscontinuousLagrange{RefQuadrilateral, 1}() ip2 = DiscontinuousLagrange{RefTriangle, 1}() - dh = DofHandler(grid); - sdh1 = SubDofHandler(dh, Set([1])); add!(sdh1, :u, ip1); - sdh2 = SubDofHandler(dh, Set([2])); add!(sdh2, :u, ip2); + dh = DofHandler(grid) + sdh1 = SubDofHandler(dh, Set([1])); add!(sdh1, :u, ip1) + sdh2 = SubDofHandler(dh, Set([2])); add!(sdh2, :u, ip2) close!(dh) ic = InterfaceCache(dh) - reinit!(ic, FaceIndex(1,2), FaceIndex(2,3)) + reinit!(ic, FaceIndex(1, 2), FaceIndex(2, 3)) @test interfacedofs(ic) == collect(1:7) # Unit test of some utilities - mixed_grid = Grid([Quadrilateral((1, 2, 3, 4)),Triangle((3, 2, 5))], - [Node(coord) for coord in zeros(Vec{2,Float64}, 5)]) + mixed_grid = Grid( + [Quadrilateral((1, 2, 3, 4)), Triangle((3, 2, 5))], + [Node(coord) for coord in zeros(Vec{2, Float64}, 5)] + ) cellset = Set(1:getncells(mixed_grid)) facetset = Set(FacetIndex(i, 1) for i in 1:getncells(mixed_grid)) @test_throws ErrorException Ferrite._check_same_celltype(mixed_grid, cellset) @test_throws ErrorException Ferrite._check_same_celltype(mixed_grid, facetset) - std_grid = generate_grid(Quadrilateral, (getncells(mixed_grid),1)) + std_grid = generate_grid(Quadrilateral, (getncells(mixed_grid), 1)) @test Ferrite._check_same_celltype(std_grid, cellset) === nothing @test Ferrite._check_same_celltype(std_grid, facetset) === nothing end @testset "Grid sets" begin - grid = Ferrite.generate_grid(Hexahedron, (1, 1, 1), Vec((0.,0., 0.)), Vec((1.,1.,1.))) + grid = Ferrite.generate_grid(Hexahedron, (1, 1, 1), Vec((0.0, 0.0, 0.0)), Vec((1.0, 1.0, 1.0))) #Test manual add - addcellset!(grid, "cell_set", [1]); + addcellset!(grid, "cell_set", [1]) addnodeset!(grid, "node_set", [1]) - addfacetset!(grid, "face_set", [FacetIndex(1,1)]) - addvertexset!(grid, "vert_set", [VertexIndex(1,1)]) + addfacetset!(grid, "face_set", [FacetIndex(1, 1)]) + addvertexset!(grid, "vert_set", [VertexIndex(1, 1)]) #Test function add - addfacetset!(grid, "left_face", (x)-> x[1] ≈ 0.0) - left_lower_edge = Ferrite.create_edgeset(grid, (x)-> x[1] ≈ 0.0 && x[3] ≈ 0.0) - addvertexset!(grid, "left_corner", (x)-> x[1] ≈ 0.0 && x[2] ≈ 0.0 && x[3] ≈ 0.0) + addfacetset!(grid, "left_face", (x) -> x[1] ≈ 0.0) + left_lower_edge = Ferrite.create_edgeset(grid, (x) -> x[1] ≈ 0.0 && x[3] ≈ 0.0) + addvertexset!(grid, "left_corner", (x) -> x[1] ≈ 0.0 && x[2] ≈ 0.0 && x[3] ≈ 0.0) @test 1 in Ferrite.getnodeset(grid, "node_set") - @test FacetIndex(1,5) in getfacetset(grid, "left_face") - @test EdgeIndex(1,4) in left_lower_edge - @test VertexIndex(1,1) in getvertexset(grid, "left_corner") + @test FacetIndex(1, 5) in getfacetset(grid, "left_face") + @test EdgeIndex(1, 4) in left_lower_edge + @test VertexIndex(1, 1) in getvertexset(grid, "left_corner") end @testset "Grid topology" begin # Error paths - mixed_rdim_grid = Grid([Triangle((1,2,3)), Line((2,3))], [Node((0.0, 0.0)), Node((1.0, 0.0)), Node((1.0, 1.1))]) + mixed_rdim_grid = Grid([Triangle((1, 2, 3)), Line((2, 3))], [Node((0.0, 0.0)), Node((1.0, 0.0)), Node((1.0, 1.1))]) @test_throws ErrorException ExclusiveTopology(mixed_rdim_grid) top_line = ExclusiveTopology(generate_grid(Line, (3,))) @test_throws ArgumentError Ferrite.get_facet_facet_neighborhood(top_line, mixed_rdim_grid) - @test_throws ArgumentError Ferrite.getneighborhood(top_line, mixed_rdim_grid, FacetIndex(1,2)) + @test_throws ArgumentError Ferrite.getneighborhood(top_line, mixed_rdim_grid, FacetIndex(1, 2)) -# -# (1) (2) (3) (4) -# +---+---+---+ -# - linegrid = generate_grid(Line,(3,)) + # + # (1) (2) (3) (4) + # +---+---+---+ + # + linegrid = generate_grid(Line, (3,)) linetopo = ExclusiveTopology(linegrid) - @test linetopo.vertex_vertex_neighbor[1,2] == [VertexIndex(2,1)] - @test getneighborhood(linetopo, linegrid, VertexIndex(1,2)) == [VertexIndex(2,1)] - @test linetopo.vertex_vertex_neighbor[2,1] == [VertexIndex(1,2)] - @test getneighborhood(linetopo, linegrid, VertexIndex(2,1)) == [VertexIndex(1,2)] - @test linetopo.vertex_vertex_neighbor[2,2] == [VertexIndex(3,1)] - @test getneighborhood(linetopo, linegrid, VertexIndex(2,2)) == [VertexIndex(3,1)] - @test linetopo.vertex_vertex_neighbor[3,1] == [VertexIndex(2,2)] - @test getneighborhood(linetopo, linegrid, VertexIndex(3,1)) == [VertexIndex(2,2)] - @test getneighborhood(linetopo, linegrid, FacetIndex(3,1)) == getneighborhood(linetopo, linegrid, VertexIndex(3,1)) + @test linetopo.vertex_vertex_neighbor[1, 2] == [VertexIndex(2, 1)] + @test getneighborhood(linetopo, linegrid, VertexIndex(1, 2)) == [VertexIndex(2, 1)] + @test linetopo.vertex_vertex_neighbor[2, 1] == [VertexIndex(1, 2)] + @test getneighborhood(linetopo, linegrid, VertexIndex(2, 1)) == [VertexIndex(1, 2)] + @test linetopo.vertex_vertex_neighbor[2, 2] == [VertexIndex(3, 1)] + @test getneighborhood(linetopo, linegrid, VertexIndex(2, 2)) == [VertexIndex(3, 1)] + @test linetopo.vertex_vertex_neighbor[3, 1] == [VertexIndex(2, 2)] + @test getneighborhood(linetopo, linegrid, VertexIndex(3, 1)) == [VertexIndex(2, 2)] + @test getneighborhood(linetopo, linegrid, FacetIndex(3, 1)) == getneighborhood(linetopo, linegrid, VertexIndex(3, 1)) linefaceskeleton = Ferrite.facetskeleton(linetopo, linegrid) - quadlinegrid = generate_grid(QuadraticLine,(3,)) + quadlinegrid = generate_grid(QuadraticLine, (3,)) quadlinetopo = ExclusiveTopology(quadlinegrid) quadlinefaceskeleton = Ferrite.facetskeleton(quadlinetopo, quadlinegrid) # Test faceskeleton - @test Set(linefaceskeleton) == Set(quadlinefaceskeleton) == Set([ - FacetIndex(1,1), FacetIndex(1,2), FacetIndex(2,2),FacetIndex(3,2), - ]) - -# (11) -# (10)+-----+-----+(12) -# | 5 | 6 | -# (7) +-----+-----+(9) -# | 3 | 4 | -# (4) +-----+-----+(6) -# | 1 | 2 | -# (1) +-----+-----+(3) -# (2) - quadgrid = generate_grid(Quadrilateral,(2,3)) + @test Set(linefaceskeleton) == Set(quadlinefaceskeleton) == Set( + [ + FacetIndex(1, 1), FacetIndex(1, 2), FacetIndex(2, 2), FacetIndex(3, 2), + ] + ) + + # (11) + # (10)+-----+-----+(12) + # | 5 | 6 | + # (7) +-----+-----+(9) + # | 3 | 4 | + # (4) +-----+-----+(6) + # | 1 | 2 | + # (1) +-----+-----+(3) + # (2) + quadgrid = generate_grid(Quadrilateral, (2, 3)) topology = ExclusiveTopology(quadgrid) faceskeleton = Ferrite.facetskeleton(topology, quadgrid) #test vertex neighbors maps cellid and local vertex id to neighbor id and neighbor local vertex id - @test topology.vertex_vertex_neighbor[1,3] == [VertexIndex(4,1)] - @test topology.vertex_vertex_neighbor[2,4] == [VertexIndex(3,2)] - @test Set(getneighborhood(topology, quadgrid, VertexIndex(2,4))) == Set([VertexIndex(1,3), VertexIndex(3,2), VertexIndex(4,1)]) - @test topology.vertex_vertex_neighbor[3,3] == [VertexIndex(6,1)] - @test topology.vertex_vertex_neighbor[3,2] == [VertexIndex(2,4)] - @test topology.vertex_vertex_neighbor[4,1] == [VertexIndex(1,3)] - @test topology.vertex_vertex_neighbor[4,4] == [VertexIndex(5,2)] - @test topology.vertex_vertex_neighbor[5,2] == [VertexIndex(4,4)] - @test topology.vertex_vertex_neighbor[6,1] == [VertexIndex(3,3)] - @test isempty(getneighborhood(topology, quadgrid, VertexIndex(2,2))) - @test length(getneighborhood(topology, quadgrid, VertexIndex(2,4))) == 3 + @test topology.vertex_vertex_neighbor[1, 3] == [VertexIndex(4, 1)] + @test topology.vertex_vertex_neighbor[2, 4] == [VertexIndex(3, 2)] + @test Set(getneighborhood(topology, quadgrid, VertexIndex(2, 4))) == Set([VertexIndex(1, 3), VertexIndex(3, 2), VertexIndex(4, 1)]) + @test topology.vertex_vertex_neighbor[3, 3] == [VertexIndex(6, 1)] + @test topology.vertex_vertex_neighbor[3, 2] == [VertexIndex(2, 4)] + @test topology.vertex_vertex_neighbor[4, 1] == [VertexIndex(1, 3)] + @test topology.vertex_vertex_neighbor[4, 4] == [VertexIndex(5, 2)] + @test topology.vertex_vertex_neighbor[5, 2] == [VertexIndex(4, 4)] + @test topology.vertex_vertex_neighbor[6, 1] == [VertexIndex(3, 3)] + @test isempty(getneighborhood(topology, quadgrid, VertexIndex(2, 2))) + @test length(getneighborhood(topology, quadgrid, VertexIndex(2, 4))) == 3 #test face neighbor maps cellid and local face id to neighbor id and neighbor local face id - @test topology.edge_edge_neighbor[1,2] == [EdgeIndex(2,4)] - @test topology.edge_edge_neighbor[1,3] == [EdgeIndex(3,1)] - @test topology.edge_edge_neighbor[2,3] == [EdgeIndex(4,1)] - @test topology.edge_edge_neighbor[2,4] == [EdgeIndex(1,2)] - @test topology.edge_edge_neighbor[3,1] == [EdgeIndex(1,3)] - @test topology.edge_edge_neighbor[3,2] == [EdgeIndex(4,4)] - @test topology.edge_edge_neighbor[3,3] == [EdgeIndex(5,1)] - @test topology.edge_edge_neighbor[4,1] == [EdgeIndex(2,3)] - @test topology.edge_edge_neighbor[4,3] == [EdgeIndex(6,1)] - @test topology.edge_edge_neighbor[4,4] == [EdgeIndex(3,2)] - @test topology.edge_edge_neighbor[5,1] == [EdgeIndex(3,3)] - @test topology.edge_edge_neighbor[5,2] == [EdgeIndex(6,4)] - @test isempty(topology.edge_edge_neighbor[5,3]) - @test isempty(topology.edge_edge_neighbor[5,4]) - @test topology.edge_edge_neighbor[6,1] == [EdgeIndex(4,3)] - @test isempty(topology.edge_edge_neighbor[6,2]) - @test isempty(topology.edge_edge_neighbor[6,3]) - @test topology.edge_edge_neighbor[6,4] == [EdgeIndex(5,2)] - @test getneighborhood(topology, quadgrid, EdgeIndex(6, 4), false) == [EdgeIndex(5,2)] - @test Set(getneighborhood(topology, quadgrid, EdgeIndex(6, 4), true)) == Set([EdgeIndex(6, 4), EdgeIndex(5,2)]) - - @test getneighborhood(topology, quadgrid, EdgeIndex(2,4)) == getneighborhood(topology, quadgrid, FacetIndex(2,4)) - - quadquadgrid = generate_grid(QuadraticQuadrilateral,(2,3)) + @test topology.edge_edge_neighbor[1, 2] == [EdgeIndex(2, 4)] + @test topology.edge_edge_neighbor[1, 3] == [EdgeIndex(3, 1)] + @test topology.edge_edge_neighbor[2, 3] == [EdgeIndex(4, 1)] + @test topology.edge_edge_neighbor[2, 4] == [EdgeIndex(1, 2)] + @test topology.edge_edge_neighbor[3, 1] == [EdgeIndex(1, 3)] + @test topology.edge_edge_neighbor[3, 2] == [EdgeIndex(4, 4)] + @test topology.edge_edge_neighbor[3, 3] == [EdgeIndex(5, 1)] + @test topology.edge_edge_neighbor[4, 1] == [EdgeIndex(2, 3)] + @test topology.edge_edge_neighbor[4, 3] == [EdgeIndex(6, 1)] + @test topology.edge_edge_neighbor[4, 4] == [EdgeIndex(3, 2)] + @test topology.edge_edge_neighbor[5, 1] == [EdgeIndex(3, 3)] + @test topology.edge_edge_neighbor[5, 2] == [EdgeIndex(6, 4)] + @test isempty(topology.edge_edge_neighbor[5, 3]) + @test isempty(topology.edge_edge_neighbor[5, 4]) + @test topology.edge_edge_neighbor[6, 1] == [EdgeIndex(4, 3)] + @test isempty(topology.edge_edge_neighbor[6, 2]) + @test isempty(topology.edge_edge_neighbor[6, 3]) + @test topology.edge_edge_neighbor[6, 4] == [EdgeIndex(5, 2)] + @test getneighborhood(topology, quadgrid, EdgeIndex(6, 4), false) == [EdgeIndex(5, 2)] + @test Set(getneighborhood(topology, quadgrid, EdgeIndex(6, 4), true)) == Set([EdgeIndex(6, 4), EdgeIndex(5, 2)]) + + @test getneighborhood(topology, quadgrid, EdgeIndex(2, 4)) == getneighborhood(topology, quadgrid, FacetIndex(2, 4)) + + quadquadgrid = generate_grid(QuadraticQuadrilateral, (2, 3)) quadtopology = ExclusiveTopology(quadquadgrid) quadfaceskeleton = Ferrite.facetskeleton(quadtopology, quadquadgrid) # Test faceskeleton - @test Set(faceskeleton) == Set(quadfaceskeleton) == Set([ - FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), FacetIndex(1,4), - FacetIndex(2,1), FacetIndex(2,2), FacetIndex(2,3), - FacetIndex(3,2), FacetIndex(3,3), FacetIndex(3,4), - FacetIndex(4,2), FacetIndex(4,3), - FacetIndex(5,2), FacetIndex(5,3), FacetIndex(5,4), - FacetIndex(6,2), FacetIndex(6,3), - ]) - -# (8) -# (7) +-----+-----+(9) -# | 3 | 4 | -# (4) +-----+-----+(6) bottom view -# | 1 | 2 | -# (1) +-----+-----+(3) -# (2) -# (15) -# (16) +-----+-----+(17) -# | 3 | 4 | -# (13) +-----+-----+(15) top view -# | 1 | 2 | -# (10) +-----+-----+(12) -# (11) - hexgrid = generate_grid(Hexahedron,(2,2,1)) + @test Set(faceskeleton) == Set(quadfaceskeleton) == Set( + [ + FacetIndex(1, 1), FacetIndex(1, 2), FacetIndex(1, 3), FacetIndex(1, 4), + FacetIndex(2, 1), FacetIndex(2, 2), FacetIndex(2, 3), + FacetIndex(3, 2), FacetIndex(3, 3), FacetIndex(3, 4), + FacetIndex(4, 2), FacetIndex(4, 3), + FacetIndex(5, 2), FacetIndex(5, 3), FacetIndex(5, 4), + FacetIndex(6, 2), FacetIndex(6, 3), + ] + ) + + # (8) + # (7) +-----+-----+(9) + # | 3 | 4 | + # (4) +-----+-----+(6) bottom view + # | 1 | 2 | + # (1) +-----+-----+(3) + # (2) + # (15) + # (16) +-----+-----+(17) + # | 3 | 4 | + # (13) +-----+-----+(15) top view + # | 1 | 2 | + # (10) +-----+-----+(12) + # (11) + hexgrid = generate_grid(Hexahedron, (2, 2, 1)) topology = ExclusiveTopology(hexgrid) - @test topology.edge_edge_neighbor[1,11] == [EdgeIndex(4,9)] - @test Set(getneighborhood(topology,hexgrid,EdgeIndex(1,11),true)) == Set([EdgeIndex(4,9),EdgeIndex(2,12),EdgeIndex(3,10),EdgeIndex(1,11)]) - @test Set(getneighborhood(topology,hexgrid,EdgeIndex(1,11),false)) == Set([EdgeIndex(4,9),EdgeIndex(2,12),EdgeIndex(3,10)]) - @test topology.edge_edge_neighbor[2,12] == [EdgeIndex(3,10)] - @test Set(getneighborhood(topology,hexgrid,EdgeIndex(2,12),true)) == Set([EdgeIndex(3,10),EdgeIndex(1,11),EdgeIndex(4,9),EdgeIndex(2,12)]) - @test Set(getneighborhood(topology,hexgrid,EdgeIndex(2,12),false)) == Set([EdgeIndex(3,10),EdgeIndex(1,11),EdgeIndex(4,9)]) - @test topology.edge_edge_neighbor[3,10] == [EdgeIndex(2,12)] - @test topology.edge_edge_neighbor[4,9] == [EdgeIndex(1,11)] - @test getneighborhood(topology,hexgrid,FaceIndex((1,3))) == [FaceIndex((2,5))] - @test getneighborhood(topology,hexgrid,FaceIndex((1,4))) == [FaceIndex((3,2))] - @test getneighborhood(topology,hexgrid,FaceIndex((2,4))) == [FaceIndex((4,2))] - @test getneighborhood(topology,hexgrid,FaceIndex((2,5))) == [FaceIndex((1,3))] - @test getneighborhood(topology,hexgrid,FaceIndex((3,2))) == [FaceIndex((1,4))] - @test getneighborhood(topology,hexgrid,FaceIndex((3,3))) == [FaceIndex((4,5))] - @test getneighborhood(topology,hexgrid,FaceIndex((4,2))) == [FaceIndex((2,4))] - @test getneighborhood(topology,hexgrid,FaceIndex((4,5))) == [FaceIndex((3,3))] - @test Set(getneighborhood(topology, hexgrid, FaceIndex((4,5)), true)) == Set([FaceIndex((3,3)), FaceIndex((4,5))]) - - @test getneighborhood(topology, hexgrid, FaceIndex(2,4)) == getneighborhood(topology, hexgrid, FacetIndex(2,4)) + @test topology.edge_edge_neighbor[1, 11] == [EdgeIndex(4, 9)] + @test Set(getneighborhood(topology, hexgrid, EdgeIndex(1, 11), true)) == Set([EdgeIndex(4, 9), EdgeIndex(2, 12), EdgeIndex(3, 10), EdgeIndex(1, 11)]) + @test Set(getneighborhood(topology, hexgrid, EdgeIndex(1, 11), false)) == Set([EdgeIndex(4, 9), EdgeIndex(2, 12), EdgeIndex(3, 10)]) + @test topology.edge_edge_neighbor[2, 12] == [EdgeIndex(3, 10)] + @test Set(getneighborhood(topology, hexgrid, EdgeIndex(2, 12), true)) == Set([EdgeIndex(3, 10), EdgeIndex(1, 11), EdgeIndex(4, 9), EdgeIndex(2, 12)]) + @test Set(getneighborhood(topology, hexgrid, EdgeIndex(2, 12), false)) == Set([EdgeIndex(3, 10), EdgeIndex(1, 11), EdgeIndex(4, 9)]) + @test topology.edge_edge_neighbor[3, 10] == [EdgeIndex(2, 12)] + @test topology.edge_edge_neighbor[4, 9] == [EdgeIndex(1, 11)] + @test getneighborhood(topology, hexgrid, FaceIndex((1, 3))) == [FaceIndex((2, 5))] + @test getneighborhood(topology, hexgrid, FaceIndex((1, 4))) == [FaceIndex((3, 2))] + @test getneighborhood(topology, hexgrid, FaceIndex((2, 4))) == [FaceIndex((4, 2))] + @test getneighborhood(topology, hexgrid, FaceIndex((2, 5))) == [FaceIndex((1, 3))] + @test getneighborhood(topology, hexgrid, FaceIndex((3, 2))) == [FaceIndex((1, 4))] + @test getneighborhood(topology, hexgrid, FaceIndex((3, 3))) == [FaceIndex((4, 5))] + @test getneighborhood(topology, hexgrid, FaceIndex((4, 2))) == [FaceIndex((2, 4))] + @test getneighborhood(topology, hexgrid, FaceIndex((4, 5))) == [FaceIndex((3, 3))] + @test Set(getneighborhood(topology, hexgrid, FaceIndex((4, 5)), true)) == Set([FaceIndex((3, 3)), FaceIndex((4, 5))]) + + @test getneighborhood(topology, hexgrid, FaceIndex(2, 4)) == getneighborhood(topology, hexgrid, FacetIndex(2, 4)) # regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/518 - serendipitygrid = generate_grid(SerendipityQuadraticHexahedron,(2,2,1)) + serendipitygrid = generate_grid(SerendipityQuadraticHexahedron, (2, 2, 1)) stopology = ExclusiveTopology(serendipitygrid) @test all(stopology.face_face_neighbor .== topology.face_face_neighbor) @test all(stopology.vertex_vertex_neighbor .== topology.vertex_vertex_neighbor) # Test faceskeleton faceskeleton = Ferrite.facetskeleton(topology, hexgrid) sfaceskeleton = Ferrite.facetskeleton(stopology, serendipitygrid) - @test Set(faceskeleton) == Set(sfaceskeleton) == Set([ - FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), FacetIndex(1,4), FacetIndex(1,5), FacetIndex(1,6), - FacetIndex(2,1), FacetIndex(2,2), FacetIndex(2,3), FacetIndex(2,4), FacetIndex(2,6), - FacetIndex(3,1), FacetIndex(3,3), FacetIndex(3,4), FacetIndex(3,5), FacetIndex(3,6), - FacetIndex(4,1), FacetIndex(4,3), FacetIndex(4,4), FacetIndex(4,6), - ]) - -# +-----+-----+ -# |\ 6 |\ 8 | -# | \ | \ | -# | 5 \| 7 \| -# +-----+-----+ -# |\ 2 |\ 4 | -# | \ | \ | -# | 1 \| 3 \| -# +-----+-----+ -# test for multiple vertex_neighbors as in e.g. ele 3, local vertex 3 (middle node) - trigrid = generate_grid(Triangle,(2,2)) + @test Set(faceskeleton) == Set(sfaceskeleton) == Set( + [ + FacetIndex(1, 1), FacetIndex(1, 2), FacetIndex(1, 3), FacetIndex(1, 4), FacetIndex(1, 5), FacetIndex(1, 6), + FacetIndex(2, 1), FacetIndex(2, 2), FacetIndex(2, 3), FacetIndex(2, 4), FacetIndex(2, 6), + FacetIndex(3, 1), FacetIndex(3, 3), FacetIndex(3, 4), FacetIndex(3, 5), FacetIndex(3, 6), + FacetIndex(4, 1), FacetIndex(4, 3), FacetIndex(4, 4), FacetIndex(4, 6), + ] + ) + + # +-----+-----+ + # |\ 6 |\ 8 | + # | \ | \ | + # | 5 \| 7 \| + # +-----+-----+ + # |\ 2 |\ 4 | + # | \ | \ | + # | 1 \| 3 \| + # +-----+-----+ + # test for multiple vertex_neighbors as in e.g. ele 3, local vertex 3 (middle node) + trigrid = generate_grid(Triangle, (2, 2)) topology = ExclusiveTopology(trigrid) - tri_vert_nbset = Set([VertexIndex(5,2), VertexIndex(6,1), VertexIndex(7,1)]) # Exclusive neighbors - @test Set(topology.vertex_vertex_neighbor[3,3]) == tri_vert_nbset + tri_vert_nbset = Set([VertexIndex(5, 2), VertexIndex(6, 1), VertexIndex(7, 1)]) # Exclusive neighbors + @test Set(topology.vertex_vertex_neighbor[3, 3]) == tri_vert_nbset union!(tri_vert_nbset, [VertexIndex(4, 3), VertexIndex(2, 2)]) # Add vertices shared via edges as well - @test Set(getneighborhood(topology, trigrid, VertexIndex(3,3), false)) == tri_vert_nbset - union!(tri_vert_nbset, [VertexIndex(3,3)]) # Add self - @test Set(getneighborhood(topology, trigrid, VertexIndex(3,3), true)) == tri_vert_nbset + @test Set(getneighborhood(topology, trigrid, VertexIndex(3, 3), false)) == tri_vert_nbset + union!(tri_vert_nbset, [VertexIndex(3, 3)]) # Add self + @test Set(getneighborhood(topology, trigrid, VertexIndex(3, 3), true)) == tri_vert_nbset - quadtrigrid = generate_grid(QuadraticTriangle,(2,2)) + quadtrigrid = generate_grid(QuadraticTriangle, (2, 2)) quadtopology = ExclusiveTopology(trigrid) # add more regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/518 @test all(quadtopology.face_face_neighbor .== topology.face_face_neighbor) @@ -459,48 +469,53 @@ end # Test faceskeleton trifaceskeleton = Ferrite.facetskeleton(topology, trigrid) quadtrifaceskeleton = Ferrite.facetskeleton(quadtopology, quadtrigrid) - @test Set(trifaceskeleton) == Set(quadtrifaceskeleton) == Set([ - FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), - FacetIndex(2,1), FacetIndex(2,2), - FacetIndex(3,1), FacetIndex(3,2), - FacetIndex(4,1), FacetIndex(4,2), - FacetIndex(5,2), FacetIndex(5,3), - FacetIndex(6,1), FacetIndex(6,2), - FacetIndex(7,2), - FacetIndex(8,1), FacetIndex(8,2), - ]) -# Test tetrahedron faceskeleton - tetgrid = generate_grid(Tetrahedron, (1,1,1)) + @test Set(trifaceskeleton) == Set(quadtrifaceskeleton) == Set( + [ + FacetIndex(1, 1), FacetIndex(1, 2), FacetIndex(1, 3), + FacetIndex(2, 1), FacetIndex(2, 2), + FacetIndex(3, 1), FacetIndex(3, 2), + FacetIndex(4, 1), FacetIndex(4, 2), + FacetIndex(5, 2), FacetIndex(5, 3), + FacetIndex(6, 1), FacetIndex(6, 2), + FacetIndex(7, 2), + FacetIndex(8, 1), FacetIndex(8, 2), + ] + ) + # Test tetrahedron faceskeleton + tetgrid = generate_grid(Tetrahedron, (1, 1, 1)) topology = ExclusiveTopology(tetgrid) tetfaceskeleton = Ferrite.facetskeleton(topology, tetgrid) - @test Set(tetfaceskeleton) == Set([ - FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), FacetIndex(1,4), - FacetIndex(2,1), FacetIndex(2,2), FacetIndex(2,3), - FacetIndex(3,1), FacetIndex(3,2), FacetIndex(3,3), - FacetIndex(4,1), FacetIndex(4,2), FacetIndex(4,3), - FacetIndex(5,1), FacetIndex(5,3), FacetIndex(5,4), - FacetIndex(6,1), FacetIndex(6,3), - ]) -# test grids with mixed celltypes of same refdim -# (4)---(5) -# | | \ -# | 1 |2 \ -# (1)---(2)-(3) + @test Set(tetfaceskeleton) == Set( + [ + FacetIndex(1, 1), FacetIndex(1, 2), FacetIndex(1, 3), FacetIndex(1, 4), + FacetIndex(2, 1), FacetIndex(2, 2), FacetIndex(2, 3), + FacetIndex(3, 1), FacetIndex(3, 2), FacetIndex(3, 3), + FacetIndex(4, 1), FacetIndex(4, 2), FacetIndex(4, 3), + FacetIndex(5, 1), FacetIndex(5, 3), FacetIndex(5, 4), + FacetIndex(6, 1), FacetIndex(6, 3), + ] + ) + # test grids with mixed celltypes of same refdim + # (4)---(5) + # | | \ + # | 1 |2 \ + # (1)---(2)-(3) tet_quad_grid = Grid( [Quadrilateral((1, 2, 5, 4)), Triangle((2, 3, 5))], - [Node(Float64.((x, y))) for (x, y) in ((0,0), (1,0), (2,0), (0,1), (1,1))]) + [Node(Float64.((x, y))) for (x, y) in ((0, 0), (1, 0), (2, 0), (0, 1), (1, 1))] + ) top = ExclusiveTopology(tet_quad_grid) - @test getneighborhood(top, tet_quad_grid, FacetIndex(1, 2)) == [EdgeIndex(2,3)] - @test Set(getneighborhood(top, tet_quad_grid, FacetIndex(1, 2), true)) == Set([EdgeIndex(1,2), EdgeIndex(2,3)]) + @test getneighborhood(top, tet_quad_grid, FacetIndex(1, 2)) == [EdgeIndex(2, 3)] + @test Set(getneighborhood(top, tet_quad_grid, FacetIndex(1, 2), true)) == Set([EdgeIndex(1, 2), EdgeIndex(2, 3)]) -# test grids with mixed refdim + # test grids with mixed refdim cells = [ Hexahedron((1, 2, 3, 4, 5, 6, 7, 8)), Hexahedron((11, 13, 14, 12, 15, 16, 17, 18)), Quadrilateral((2, 9, 10, 3)), Quadrilateral((9, 11, 12, 10)), - ] - nodes = [Node(coord) for coord in zeros(Vec{3,Float64}, 18)] + ] + nodes = [Node(coord) for coord in zeros(Vec{3, Float64}, 18)] grid = Grid(cells, nodes) @test_throws ErrorException ExclusiveTopology(grid) # topology = ExclusiveTopology(grid) @@ -519,8 +534,8 @@ end Quadrilateral((3, 4, 8, 7)), Line((2, 3)), Line((6, 7)), - ] - nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 18)] + ] + nodes = [Node(coord) for coord in zeros(Vec{2, Float64}, 18)] grid = Grid(cells, nodes) @test_throws ErrorException ExclusiveTopology(grid) # topology = ExclusiveTopology(grid) @@ -528,51 +543,55 @@ end # @test_throws ArgumentError getneighborhood(topology, grid, FacetIndex(1,1)) # @test_throws ArgumentError Ferrite.get_facet_facet_neighborhood(topology, grid) -# -# +-----+-----+-----+ -# | 7 | 8 | 9 | -# +-----+-----+-----+ -# | 4 | 5 | 6 | -# +-----+-----+-----+ -# | 1 | 2 | 3 | -# +-----+-----+-----+ -# test application: form level 1 neighborhood patches of elements - quadgrid = generate_grid(Quadrilateral,(3,3)) + # + # +-----+-----+-----+ + # | 7 | 8 | 9 | + # +-----+-----+-----+ + # | 4 | 5 | 6 | + # +-----+-----+-----+ + # | 1 | 2 | 3 | + # +-----+-----+-----+ + # test application: form level 1 neighborhood patches of elements + quadgrid = generate_grid(Quadrilateral, (3, 3)) topology = ExclusiveTopology(quadgrid) patches = Vector{Int}[Ferrite.getneighborhood(topology, quadgrid, CellIndex(i)) for i in 1:getncells(quadgrid)] - @test issubset([4,5,2], patches[1]) # neighbor elements of element 1 are 4 5 and 2 - @test issubset([1,4,5,6,3], patches[2]) - @test issubset([2,5,6], patches[3]) - @test issubset([7,8,5,2,1], patches[4]) - @test issubset([1,2,3,4,6,7,8,9], patches[5]) - @test issubset([3,2,5,8,9], patches[6]) - @test issubset([4,5,8], patches[7]) - @test issubset([7,4,5,6,9], patches[8]) - @test issubset([8,5,6], patches[9]) + @test issubset([4, 5, 2], patches[1]) # neighbor elements of element 1 are 4 5 and 2 + @test issubset([1, 4, 5, 6, 3], patches[2]) + @test issubset([2, 5, 6], patches[3]) + @test issubset([7, 8, 5, 2, 1], patches[4]) + @test issubset([1, 2, 3, 4, 6, 7, 8, 9], patches[5]) + @test issubset([3, 2, 5, 8, 9], patches[6]) + @test issubset([4, 5, 8], patches[7]) + @test issubset([7, 4, 5, 6, 9], patches[8]) + @test issubset([8, 5, 6], patches[9]) @test 5 ∉ getneighborhood(topology, quadgrid, CellIndex(5)) @test 5 ∈ getneighborhood(topology, quadgrid, CellIndex(5), true) -# test star stencils + # test star stencils stars = Ferrite.vertex_star_stencils(topology, quadgrid) - @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(1,1))) == Set([VertexIndex(1,2), VertexIndex(1,4), VertexIndex(1,1)]) - @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(2,1))) == Set([VertexIndex(1,1), VertexIndex(1,3), VertexIndex(2,2), VertexIndex(2,4), VertexIndex(1,2), VertexIndex(2,1)]) - @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(5,4))) == Set([VertexIndex(4,2), VertexIndex(4,4), VertexIndex(5,1), VertexIndex(5,3), VertexIndex(7,1), VertexIndex(7,3), VertexIndex(8,2), VertexIndex(8,4), VertexIndex(4,3), VertexIndex(5,4), VertexIndex(7,2), VertexIndex(8,1)]) - @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(1,1)))) == Set([1,2,5]) - @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(2,1)))) == Set([2,1,6,3]) - @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(5,4)))) == Set([10,6,9,11,14]) + @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(1, 1))) == Set([VertexIndex(1, 2), VertexIndex(1, 4), VertexIndex(1, 1)]) + @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(2, 1))) == Set([VertexIndex(1, 1), VertexIndex(1, 3), VertexIndex(2, 2), VertexIndex(2, 4), VertexIndex(1, 2), VertexIndex(2, 1)]) + @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(5, 4))) == Set([VertexIndex(4, 2), VertexIndex(4, 4), VertexIndex(5, 1), VertexIndex(5, 3), VertexIndex(7, 1), VertexIndex(7, 3), VertexIndex(8, 2), VertexIndex(8, 4), VertexIndex(4, 3), VertexIndex(5, 4), VertexIndex(7, 2), VertexIndex(8, 1)]) + @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(1, 1)))) == Set([1, 2, 5]) + @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(2, 1)))) == Set([2, 1, 6, 3]) + @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(5, 4)))) == Set([10, 6, 9, 11, 14]) face_skeleton = Ferrite.facetskeleton(topology, quadgrid) - @test Set(face_skeleton) == Set([FacetIndex(1,1),FacetIndex(1,2),FacetIndex(1,3),FacetIndex(1,4), - FacetIndex(2,1),FacetIndex(2,2),FacetIndex(2,3), - FacetIndex(3,1),FacetIndex(3,2),FacetIndex(3,3), - FacetIndex(4,2),FacetIndex(4,3),FacetIndex(4,4), - FacetIndex(5,2),FacetIndex(5,3),FacetIndex(6,2),FacetIndex(6,3), - FacetIndex(7,2),FacetIndex(7,3),FacetIndex(7,4), - FacetIndex(8,2),FacetIndex(8,3),FacetIndex(9,2),FacetIndex(9,3)]) - @test length(face_skeleton) == 4*3 + 3*4 - - quadratic_quadgrid = generate_grid(QuadraticQuadrilateral,(3,3)) + @test Set(face_skeleton) == Set( + [ + FacetIndex(1, 1), FacetIndex(1, 2), FacetIndex(1, 3), FacetIndex(1, 4), + FacetIndex(2, 1), FacetIndex(2, 2), FacetIndex(2, 3), + FacetIndex(3, 1), FacetIndex(3, 2), FacetIndex(3, 3), + FacetIndex(4, 2), FacetIndex(4, 3), FacetIndex(4, 4), + FacetIndex(5, 2), FacetIndex(5, 3), FacetIndex(6, 2), FacetIndex(6, 3), + FacetIndex(7, 2), FacetIndex(7, 3), FacetIndex(7, 4), + FacetIndex(8, 2), FacetIndex(8, 3), FacetIndex(9, 2), FacetIndex(9, 3), + ] + ) + @test length(face_skeleton) == 4 * 3 + 3 * 4 + + quadratic_quadgrid = generate_grid(QuadraticQuadrilateral, (3, 3)) quadgrid_topology = ExclusiveTopology(quadratic_quadgrid) #quadface_skeleton = Ferrite.facetskeleton(topology, quadgrid) #@test quadface_skeleton == face_skeleton @@ -583,25 +602,25 @@ end quadratic_patches = Vector{Int}[Ferrite.getneighborhood(quadgrid_topology, quadratic_quadgrid, CellIndex(i)) for i in 1:getncells(quadratic_quadgrid)] @test all(patches .== quadratic_patches) -# -# +-----+-----+-----+ -# | 7 | 8 | 9 | -# +-----+-----+-----+ -# | 4 | 5 | 6 | -# +-----+-----+-----+ -# | 1 | 2 | 3 | -# +-----+-----+-----+ -# test application: integrate jump across element boundary 5 + # + # +-----+-----+-----+ + # | 7 | 8 | 9 | + # +-----+-----+-----+ + # | 4 | 5 | 6 | + # +-----+-----+-----+ + # | 1 | 2 | 3 | + # +-----+-----+-----+ + # test application: integrate jump across element boundary 5 ip = DiscontinuousLagrange{RefQuadrilateral, 1}()^2 qr_facet = FacetQuadratureRule{RefQuadrilateral}(2) iv = InterfaceValues(qr_facet, ip) dh = DofHandler(quadgrid) add!(dh, :u, ip) close!(dh) - u = [5.0 for _ in 1:4*9*2] - u[4*4*2+1 : 5*4*2] .= 3.0 - jump_int = 0. - jump_abs = 0. + u = [5.0 for _ in 1:(4 * 9 * 2)] + u[(4 * 4 * 2 + 1):(5 * 4 * 2)] .= 3.0 + jump_int = 0.0 + jump_abs = 0.0 # Test interface Iterator for ic in InterfaceIterator(dh) any(cellid.([ic.a, ic.b]) .== 5) || continue @@ -614,16 +633,16 @@ end jump_abs += abs(function_value_jump(iv, q_point, u_interface) ⋅ normal_a) * dΩ end end - @test isapprox(jump_abs, 2/3*2*4,atol=1e-6) # 2*4*0.66666, jump is always 2, 4 sides, length =0.66 - @test isapprox(jump_int, 0.0, atol=1e-6) + @test isapprox(jump_abs, 2 / 3 * 2 * 4, atol = 1.0e-6) # 2*4*0.66666, jump is always 2, 4 sides, length =0.66 + @test isapprox(jump_int, 0.0, atol = 1.0e-6) end @testset "grid coloring" begin - function test_coloring(grid, cellset=1:getncells(grid)) + function test_coloring(grid, cellset = 1:getncells(grid)) for alg in (ColoringAlgorithm.Greedy, ColoringAlgorithm.WorkStream) - color_vectors = create_coloring(grid, cellset; alg=alg) - @test sum(length, color_vectors, init=0) == length(cellset) - @test union!(Set{Int}(), color_vectors...) == Set(cellset) + color_vectors = create_coloring(grid, cellset; alg = alg) + @test sum(length, color_vectors, init = 0) == length(cellset) + @test union!(Set{Int}(), color_vectors...) == Set(cellset) conn = Ferrite.create_incidence_matrix(grid, cellset) for color in color_vectors, c1 in color, c2 in color @test !conn[c1, c2] @@ -643,10 +662,10 @@ end # color only a subset test_coloring(generate_grid(Line, (5,)), 1:3) - test_coloring(generate_grid(Triangle, (5, 5)), Set{Int}(1:3^2)) - test_coloring(generate_grid(Quadrilateral, (5, 5)), Set{Int}(1:3^2)) - test_coloring(generate_grid(Tetrahedron, (5, 5, 5)), Set{Int}(1:3^3)) - test_coloring(generate_grid(Hexahedron, (5, 5, 5)), Set{Int}(1:3^3)) + test_coloring(generate_grid(Triangle, (5, 5)), Set{Int}(1:(3^2))) + test_coloring(generate_grid(Quadrilateral, (5, 5)), Set{Int}(1:(3^2))) + test_coloring(generate_grid(Tetrahedron, (5, 5, 5)), Set{Int}(1:(3^3))) + test_coloring(generate_grid(Hexahedron, (5, 5, 5)), Set{Int}(1:(3^3))) # unconnected subset test_coloring(generate_grid(Triangle, (10, 10)), union(Set(1:10), Set(70:80))) @@ -674,7 +693,7 @@ end # | \ \ | # 1-4---5-2 2 dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefTriangle,3}()) + add!(dh, :u, Lagrange{RefTriangle, 3}()) close!(dh) @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 9, 8, 10] @test celldofs(dh, 2) == [2, 11, 3, 12, 13, 15, 14, 7, 6, 16] @@ -690,64 +709,64 @@ end # | \ \ | # 1-7---9-3 3 dh = DofHandler(grid) - add!(dh, :u, Lagrange{RefTriangle,3}()^2) + add!(dh, :u, Lagrange{RefTriangle, 3}()^2) close!(dh) @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 15, 16, 19, 20] @test celldofs(dh, 2) == [3, 4, 21, 22, 5, 6, 23, 24, 25, 26, 29, 30, 27, 28, 13, 14, 11, 12, 31, 32] end @testset "vectorization layer compat" begin - struct VectorLagrangeTest{shape,order,vdim} <: ScalarInterpolation{shape,order} end + struct VectorLagrangeTest{shape, order, vdim} <: ScalarInterpolation{shape, order} end Ferrite.adjust_dofs_during_distribution(ip::VectorLagrangeTest{<:Any, order}) where {order} = order > 2 @testset "1d" begin grid = generate_grid(Line, (2,)) - Ferrite.vertexdof_indices(::VectorLagrangeTest{RefLine,1,2}) = ((1,2),(3,4)) + Ferrite.vertexdof_indices(::VectorLagrangeTest{RefLine, 1, 2}) = ((1, 2), (3, 4)) dh1 = DofHandler(grid) - add!(dh1, :u, VectorLagrangeTest{RefLine,1,2}()) + add!(dh1, :u, VectorLagrangeTest{RefLine, 1, 2}()) close!(dh1) dh2 = DofHandler(grid) # TODO: Why was this RefQuadrilateral? Check it is correct to test with RefLine - add!(dh2, :u, Lagrange{RefLine,1}()^2) + add!(dh2, :u, Lagrange{RefLine, 1}()^2) close!(dh2) @test dh1.cell_dofs == dh2.cell_dofs - Ferrite.vertexdof_indices(::VectorLagrangeTest{RefLine,1,3}) = ((1,2,3),(4,5,6)) + Ferrite.vertexdof_indices(::VectorLagrangeTest{RefLine, 1, 3}) = ((1, 2, 3), (4, 5, 6)) dh1 = DofHandler(grid) - add!(dh1, :u, VectorLagrangeTest{RefLine,1,3}()) + add!(dh1, :u, VectorLagrangeTest{RefLine, 1, 3}()) close!(dh1) dh2 = DofHandler(grid) # TODO: Why was this RefQuadrilateral? Check it is correct to test with RefLine - add!(dh2, :u, Lagrange{RefLine,1}()^3) + add!(dh2, :u, Lagrange{RefLine, 1}()^3) close!(dh2) @test dh1.cell_dofs == dh2.cell_dofs end @testset "2d" begin - grid = generate_grid(Quadrilateral, (2,2)) - Ferrite.vertexdof_indices(::VectorLagrangeTest{RefQuadrilateral,1,2}) = ((1,2),(3,4),(5,6),(7,8)) + grid = generate_grid(Quadrilateral, (2, 2)) + Ferrite.vertexdof_indices(::VectorLagrangeTest{RefQuadrilateral, 1, 2}) = ((1, 2), (3, 4), (5, 6), (7, 8)) dh1 = DofHandler(grid) - add!(dh1, :u, VectorLagrangeTest{RefQuadrilateral,1,2}()) + add!(dh1, :u, VectorLagrangeTest{RefQuadrilateral, 1, 2}()) close!(dh1) dh2 = DofHandler(grid) - add!(dh2, :u, Lagrange{RefQuadrilateral,1}()^2) + add!(dh2, :u, Lagrange{RefQuadrilateral, 1}()^2) close!(dh2) @test dh1.cell_dofs == dh2.cell_dofs - Ferrite.vertexdof_indices(::VectorLagrangeTest{RefQuadrilateral,1,3}) = ((1,2,3),(4,5,6),(7,8,9),(10,11,12)) - Ferrite.facedof_indices(::VectorLagrangeTest{RefQuadrilateral,1,3}) = ((1,2,3,4,5,6), (4,5,6,7,8,9), (7,8,9,10,11,12), (10,11,12,1,2,3)) + Ferrite.vertexdof_indices(::VectorLagrangeTest{RefQuadrilateral, 1, 3}) = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + Ferrite.facedof_indices(::VectorLagrangeTest{RefQuadrilateral, 1, 3}) = ((1, 2, 3, 4, 5, 6), (4, 5, 6, 7, 8, 9), (7, 8, 9, 10, 11, 12), (10, 11, 12, 1, 2, 3)) dh1 = DofHandler(grid) - add!(dh1, :u, VectorLagrangeTest{RefQuadrilateral,1,3}()) + add!(dh1, :u, VectorLagrangeTest{RefQuadrilateral, 1, 3}()) close!(dh1) dh2 = DofHandler(grid) - add!(dh2, :u, Lagrange{RefQuadrilateral,1}()^3) + add!(dh2, :u, Lagrange{RefQuadrilateral, 1}()^3) close!(dh2) @test dh1.cell_dofs == dh2.cell_dofs end @testset "paraview_collection" begin - grid = generate_grid(Triangle, (2,2)) + grid = generate_grid(Triangle, (2, 2)) celldata = rand(getncells(grid)) pvd = WriteVTK.paraview_collection("collection") vtk1 = VTKGridFile("file1", grid) diff --git a/test/test_grid_generators.jl b/test/test_grid_generators.jl index 618455eba8..3d07089e33 100644 --- a/test/test_grid_generators.jl +++ b/test/test_grid_generators.jl @@ -4,17 +4,19 @@ function test_generate_grid(T::Type) cell_types = [ Line, QuadraticLine, Quadrilateral, QuadraticQuadrilateral, Triangle, QuadraticTriangle, - Hexahedron, Wedge, Pyramid, Tetrahedron, SerendipityQuadraticHexahedron] + Hexahedron, Wedge, Pyramid, Tetrahedron, SerendipityQuadraticHexahedron, + ] # Loop over all cell types and test grid generation for CT in cell_types rdim = Ferrite.getrefdim(CT) nels = ntuple(i -> 2, rdim) - left = - ones(Vec{rdim,T}) - right = ones(Vec{rdim,T}) + left = - ones(Vec{rdim, T}) + right = ones(Vec{rdim, T}) grid = generate_grid(CT, nels, left, right) @test isa(grid, Grid{rdim, CT, T}) end + return end # Run tests for different floating-point types diff --git a/test/test_interfacevalues.jl b/test/test_interfacevalues.jl index 44254fee5a..8fb31dc741 100644 --- a/test/test_interfacevalues.jl +++ b/test/test_interfacevalues.jl @@ -15,8 +15,10 @@ @test nqp == getnquadpoints(iv.here) == getnquadpoints(iv.there) for qp in 1:nqp # If correctly synced quadrature points coordinates should match - @test isapprox(spatial_coordinate(iv, qp, coords_here, coords_there; here = true), - spatial_coordinate(iv, qp, coords_here, coords_there; here = false); atol = tol) + @test isapprox( + spatial_coordinate(iv, qp, coords_here, coords_there; here = true), + spatial_coordinate(iv, qp, coords_here, coords_there; here = false); atol = tol + ) for i in 1:getnbasefunctions(iv) here = i <= getnbasefunctions(iv.here) shapevalue = shape_value(iv, qp, i; here = here) @@ -27,7 +29,7 @@ shapegrad_avg = shape_gradient_average(iv, qp, i) shapegrad_jump = shape_gradient_jump(iv, qp, i) - normal = getnormal(iv, qp; here=false) + normal = getnormal(iv, qp; here = false) # Test values (May be removed as it mirrors implementation) if i > getnbasefunctions(iv.here) @test shapevalue ≈ shape_value(iv.there, qp, i - getnbasefunctions(iv.here)) @@ -114,20 +116,20 @@ end getcelltypedim(::Type{<:Ferrite.AbstractCell{shape}}) where {dim, shape <: Ferrite.AbstractRefShape{dim}} = dim for (cell_shape, scalar_interpol, quad_rule) in ( - #TODO: update interfaces for lines - (Line, DiscontinuousLagrange{RefLine, 1}(), FacetQuadratureRule{RefLine}(2)), - (QuadraticLine, DiscontinuousLagrange{RefLine, 2}(), FacetQuadratureRule{RefLine}(2)), - (Quadrilateral, DiscontinuousLagrange{RefQuadrilateral, 1}(), FacetQuadratureRule{RefQuadrilateral}(2)), - (QuadraticQuadrilateral, DiscontinuousLagrange{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), - (Triangle, DiscontinuousLagrange{RefTriangle, 1}(), FacetQuadratureRule{RefTriangle}(2)), - (QuadraticTriangle, DiscontinuousLagrange{RefTriangle, 2}(), FacetQuadratureRule{RefTriangle}(2)), - (Hexahedron, DiscontinuousLagrange{RefHexahedron, 1}(), FacetQuadratureRule{RefHexahedron}(2)), - # (QuadraticQuadrilateral, Serendipity{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), - (Tetrahedron, DiscontinuousLagrange{RefTetrahedron, 1}(), FacetQuadratureRule{RefTetrahedron}(2)), - # (QuadraticTetrahedron, Lagrange{RefTetrahedron, 2}(), FacetQuadratureRule{RefTetrahedron}(2)), - (Wedge, DiscontinuousLagrange{RefPrism, 1}(), FacetQuadratureRule{RefPrism}(2)), - (Pyramid, DiscontinuousLagrange{RefPyramid, 1}(), FacetQuadratureRule{RefPyramid}(2)), - ) + #TODO: update interfaces for lines + (Line, DiscontinuousLagrange{RefLine, 1}(), FacetQuadratureRule{RefLine}(2)), + (QuadraticLine, DiscontinuousLagrange{RefLine, 2}(), FacetQuadratureRule{RefLine}(2)), + (Quadrilateral, DiscontinuousLagrange{RefQuadrilateral, 1}(), FacetQuadratureRule{RefQuadrilateral}(2)), + (QuadraticQuadrilateral, DiscontinuousLagrange{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), + (Triangle, DiscontinuousLagrange{RefTriangle, 1}(), FacetQuadratureRule{RefTriangle}(2)), + (QuadraticTriangle, DiscontinuousLagrange{RefTriangle, 2}(), FacetQuadratureRule{RefTriangle}(2)), + (Hexahedron, DiscontinuousLagrange{RefHexahedron, 1}(), FacetQuadratureRule{RefHexahedron}(2)), + # (QuadraticQuadrilateral, Serendipity{RefQuadrilateral, 2}(), FacetQuadratureRule{RefQuadrilateral}(2)), + (Tetrahedron, DiscontinuousLagrange{RefTetrahedron, 1}(), FacetQuadratureRule{RefTetrahedron}(2)), + # (QuadraticTetrahedron, Lagrange{RefTetrahedron, 2}(), FacetQuadratureRule{RefTetrahedron}(2)), + (Wedge, DiscontinuousLagrange{RefPrism, 1}(), FacetQuadratureRule{RefPrism}(2)), + (Pyramid, DiscontinuousLagrange{RefPyramid, 1}(), FacetQuadratureRule{RefPyramid}(2)), + ) dim = getcelltypedim(cell_shape) grid = generate_grid(cell_shape, ntuple(i -> 2, dim)) ip = scalar_interpol isa DiscontinuousLagrange ? Lagrange{Ferrite.getrefshape(scalar_interpol), Ferrite.getorder(scalar_interpol)}() : scalar_interpol @@ -142,9 +144,9 @@ end @testset "error paths" begin cell = getcells(grid, 1) - dim == 1 && @test_throws ErrorException("1D elements don't use transformations for interfaces.") Ferrite.InterfaceOrientationInfo(cell,cell,1,1) - @test_throws ArgumentError("unknown facet number") Ferrite.element_to_facet_transformation(Vec{dim,Float64}(ntuple(_->0.0, dim)), Ferrite.getrefshape(cell), 100) - @test_throws ArgumentError("unknown facet number") Ferrite.facet_to_element_transformation(Vec{dim-1,Float64}(ntuple(_->0.0, dim-1)), Ferrite.getrefshape(cell), 100) + dim == 1 && @test_throws ErrorException("1D elements don't use transformations for interfaces.") Ferrite.InterfaceOrientationInfo(cell, cell, 1, 1) + @test_throws ArgumentError("unknown facet number") Ferrite.element_to_facet_transformation(Vec{dim, Float64}(ntuple(_ -> 0.0, dim)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError("unknown facet number") Ferrite.facet_to_element_transformation(Vec{dim - 1, Float64}(ntuple(_ -> 0.0, dim - 1)), Ferrite.getrefshape(cell), 100) end func_interpol = scalar_interpol for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)) @@ -175,12 +177,12 @@ end @testset "error paths" begin cell = getcells(grid, 1) - @test_throws ArgumentError("unknown facet number") Ferrite.element_to_facet_transformation(Vec{dim,Float64}(ntuple(_->0.0, dim)), Ferrite.getrefshape(cell), 100) - @test_throws ArgumentError("unknown facet number") Ferrite.facet_to_element_transformation(Vec{dim-1,Float64}(ntuple(_->0.0, dim-1)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError("unknown facet number") Ferrite.element_to_facet_transformation(Vec{dim, Float64}(ntuple(_ -> 0.0, dim)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError("unknown facet number") Ferrite.facet_to_element_transformation(Vec{dim - 1, Float64}(ntuple(_ -> 0.0, dim - 1)), Ferrite.getrefshape(cell), 100) end for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)) iv = InterfaceValues(quad_rule, func_interpol) - test_interfacevalues(grid, iv; tol = 5*eps(Float64)) + test_interfacevalues(grid, iv; tol = 5 * eps(Float64)) end end # @testset "Mixed elements 2D grids" begin # TODO: this shouldn't work because it should change the FacetValues object @@ -199,22 +201,25 @@ # end @testset "Unordered nodes 3D" begin dim = 2 - nodes = [Node((-1.0, 0.0, 0.0)), Node((0.0, 0.0, 0.0)), Node((1.0, 0.0, 0.0)), - Node((-1.0, 1.0, 0.0)), Node((0.0, 1.0, 0.0)), Node((1.0, 1.0, 0.0)), - Node((-1.0, 0.0, 1.0)), Node((0.0, 0.0, 1.0)), Node((1.0, 0.0, 1.0)), - Node((-1.0, 1.0, 1.0)), Node((0.0, 1.0, 1.0)), Node((1.0, 1.0, 1.0)), - ] + nodes = [ + Node((-1.0, 0.0, 0.0)), Node((0.0, 0.0, 0.0)), Node((1.0, 0.0, 0.0)), + Node((-1.0, 1.0, 0.0)), Node((0.0, 1.0, 0.0)), Node((1.0, 1.0, 0.0)), + Node((-1.0, 0.0, 1.0)), Node((0.0, 0.0, 1.0)), Node((1.0, 0.0, 1.0)), + Node((-1.0, 1.0, 1.0)), Node((0.0, 1.0, 1.0)), Node((1.0, 1.0, 1.0)), + ] cells = [ - Hexahedron((1,2,5,4,7,8,11,10)), - Hexahedron((5,6,12,11,2,3,9,8)), - ] + Hexahedron((1, 2, 5, 4, 7, 8, 11, 10)), + Hexahedron((5, 6, 12, 11, 2, 3, 9, 8)), + ] grid = Grid(cells, nodes) - test_interfacevalues(grid, - InterfaceValues(FacetQuadratureRule{RefHexahedron}(2), DiscontinuousLagrange{RefHexahedron, 1}())) + test_interfacevalues( + grid, + InterfaceValues(FacetQuadratureRule{RefHexahedron}(2), DiscontinuousLagrange{RefHexahedron, 1}()) + ) end @testset "Interface dof_range" begin - grid = generate_grid(Quadrilateral,(3,3)) + grid = generate_grid(Quadrilateral, (3, 3)) ip_u = DiscontinuousLagrange{RefQuadrilateral, 1}()^2 ip_p = DiscontinuousLagrange{RefQuadrilateral, 1}() qr_facet = FacetQuadratureRule{RefQuadrilateral}(2) @@ -253,7 +258,7 @@ @test_throws ArgumentError("transformation is not implemented") Ferrite.get_transformation_matrix(it) end @testset "show" begin - iv = InterfaceValues(FacetQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) + iv = InterfaceValues(FacetQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral, 2}()) showstring = sprint(show, MIME"text/plain"(), iv) @test contains(showstring, "InterfaceValues with") end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index c8a45c1b05..3b4a497e1c 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -2,177 +2,177 @@ using Ferrite: reference_shape_value, reference_shape_gradient @testset "interpolations" begin -@testset "Value Type $value_type" for value_type in (Float32, Float64) - @testset "Correctness of $interpolation" for interpolation in ( - Lagrange{RefLine, 1}(), - Lagrange{RefLine, 2}(), - Lagrange{RefQuadrilateral, 1}(), - Lagrange{RefQuadrilateral, 2}(), - Lagrange{RefQuadrilateral, 3}(), - Lagrange{RefTriangle, 1}(), - Lagrange{RefTriangle, 2}(), - Lagrange{RefTriangle, 3}(), - Lagrange{RefTriangle, 4}(), - Lagrange{RefTriangle, 5}(), - Lagrange{RefHexahedron, 1}(), - Lagrange{RefHexahedron, 2}(), - Serendipity{RefQuadrilateral, 2}(), - Serendipity{RefHexahedron, 2}(), - Lagrange{RefTetrahedron, 1}(), - Lagrange{RefTetrahedron, 2}(), - Lagrange{RefPrism, 1}(), - Lagrange{RefPrism, 2}(), - Lagrange{RefPyramid, 1}(), - Lagrange{RefPyramid, 2}(), - # - DiscontinuousLagrange{RefLine, 0}(), - DiscontinuousLagrange{RefQuadrilateral, 0}(), - DiscontinuousLagrange{RefHexahedron, 0}(), - DiscontinuousLagrange{RefTriangle, 0}(), - DiscontinuousLagrange{RefTetrahedron, 0}(), - DiscontinuousLagrange{RefLine, 1}(), - DiscontinuousLagrange{RefQuadrilateral, 1}(), - DiscontinuousLagrange{RefHexahedron, 1}(), - DiscontinuousLagrange{RefTriangle, 1}(), - DiscontinuousLagrange{RefTetrahedron, 1}(), - DiscontinuousLagrange{RefPrism, 1}(), - DiscontinuousLagrange{RefPyramid, 1}(), - # - BubbleEnrichedLagrange{RefTriangle, 1}(), - # - CrouzeixRaviart{RefTriangle,1}(), - CrouzeixRaviart{RefTetrahedron,1}(), - RannacherTurek{RefQuadrilateral,1}(), - RannacherTurek{RefHexahedron,1}(), - ) - # Test of utility functions - ref_dim = Ferrite.getrefdim(interpolation) - ref_shape = Ferrite.getrefshape(interpolation) - func_order = Ferrite.getorder(interpolation) - @test typeof(interpolation) <: Interpolation{ref_shape,func_order} + @testset "Value Type $value_type" for value_type in (Float32, Float64) + @testset "Correctness of $interpolation" for interpolation in ( + Lagrange{RefLine, 1}(), + Lagrange{RefLine, 2}(), + Lagrange{RefQuadrilateral, 1}(), + Lagrange{RefQuadrilateral, 2}(), + Lagrange{RefQuadrilateral, 3}(), + Lagrange{RefTriangle, 1}(), + Lagrange{RefTriangle, 2}(), + Lagrange{RefTriangle, 3}(), + Lagrange{RefTriangle, 4}(), + Lagrange{RefTriangle, 5}(), + Lagrange{RefHexahedron, 1}(), + Lagrange{RefHexahedron, 2}(), + Serendipity{RefQuadrilateral, 2}(), + Serendipity{RefHexahedron, 2}(), + Lagrange{RefTetrahedron, 1}(), + Lagrange{RefTetrahedron, 2}(), + Lagrange{RefPrism, 1}(), + Lagrange{RefPrism, 2}(), + Lagrange{RefPyramid, 1}(), + Lagrange{RefPyramid, 2}(), + # + DiscontinuousLagrange{RefLine, 0}(), + DiscontinuousLagrange{RefQuadrilateral, 0}(), + DiscontinuousLagrange{RefHexahedron, 0}(), + DiscontinuousLagrange{RefTriangle, 0}(), + DiscontinuousLagrange{RefTetrahedron, 0}(), + DiscontinuousLagrange{RefLine, 1}(), + DiscontinuousLagrange{RefQuadrilateral, 1}(), + DiscontinuousLagrange{RefHexahedron, 1}(), + DiscontinuousLagrange{RefTriangle, 1}(), + DiscontinuousLagrange{RefTetrahedron, 1}(), + DiscontinuousLagrange{RefPrism, 1}(), + DiscontinuousLagrange{RefPyramid, 1}(), + # + BubbleEnrichedLagrange{RefTriangle, 1}(), + # + CrouzeixRaviart{RefTriangle, 1}(), + CrouzeixRaviart{RefTetrahedron, 1}(), + RannacherTurek{RefQuadrilateral, 1}(), + RannacherTurek{RefHexahedron, 1}(), + ) + # Test of utility functions + ref_dim = Ferrite.getrefdim(interpolation) + ref_shape = Ferrite.getrefshape(interpolation) + func_order = Ferrite.getorder(interpolation) + @test typeof(interpolation) <: Interpolation{ref_shape, func_order} - # Note that not every element formulation exists for every order and dimension. - applicable(Ferrite.getlowerorder, interpolation) && @test typeof(Ferrite.getlowerorder(interpolation)) <: Interpolation{ref_shape,func_order-1} - @testset "transform face points" begin - # Test both center point and random points on the face - ref_coord = Ferrite.reference_coordinates(Lagrange{ref_shape, 1}()) - for face in 1:nfacets(interpolation) - face_nodes = Ferrite.reference_facets(ref_shape)[face] - center_coord = [0.0 for _ in 1:ref_dim] - rand_coord = [0.0 for _ in 1:ref_dim] - rand_weights = rand(length(face_nodes)) - rand_weights /= sum(rand_weights) - for (i, node) in pairs(face_nodes) - center_coord += ref_coord[node] / length(face_nodes) - rand_coord += rand_weights[i] .* ref_coord[node] - end - for point in (center_coord, rand_coord) - vec_point = Vec{ref_dim}(point) - cell_to_face = Ferrite.element_to_facet_transformation(vec_point, ref_shape, face) - face_to_cell = Ferrite.facet_to_element_transformation(cell_to_face, ref_shape, face) - @test vec_point ≈ face_to_cell + # Note that not every element formulation exists for every order and dimension. + applicable(Ferrite.getlowerorder, interpolation) && @test typeof(Ferrite.getlowerorder(interpolation)) <: Interpolation{ref_shape, func_order - 1} + @testset "transform face points" begin + # Test both center point and random points on the face + ref_coord = Ferrite.reference_coordinates(Lagrange{ref_shape, 1}()) + for face in 1:nfacets(interpolation) + face_nodes = Ferrite.reference_facets(ref_shape)[face] + center_coord = [0.0 for _ in 1:ref_dim] + rand_coord = [0.0 for _ in 1:ref_dim] + rand_weights = rand(length(face_nodes)) + rand_weights /= sum(rand_weights) + for (i, node) in pairs(face_nodes) + center_coord += ref_coord[node] / length(face_nodes) + rand_coord += rand_weights[i] .* ref_coord[node] + end + for point in (center_coord, rand_coord) + vec_point = Vec{ref_dim}(point) + cell_to_face = Ferrite.element_to_facet_transformation(vec_point, ref_shape, face) + face_to_cell = Ferrite.facet_to_element_transformation(cell_to_face, ref_shape, face) + @test vec_point ≈ face_to_cell + end end end - end - n_basefuncs = getnbasefunctions(interpolation) - coords = Ferrite.reference_coordinates(interpolation) - @test length(coords) == n_basefuncs - f(x) = [reference_shape_value(interpolation, Tensor{1, ref_dim}(x), i) for i in 1:n_basefuncs] + n_basefuncs = getnbasefunctions(interpolation) + coords = Ferrite.reference_coordinates(interpolation) + @test length(coords) == n_basefuncs + f(x) = [reference_shape_value(interpolation, Tensor{1, ref_dim}(x), i) for i in 1:n_basefuncs] - #TODO prefer this test style after 1.6 is removed from CI - # @testset let x = sample_random_point(ref_shape) # not compatible with Julia 1.6 - x = Vec{ref_dim,value_type}(sample_random_point(ref_shape)) - random_point_testset = @testset "Random point test" begin - # Check gradient evaluation - @test vec(ForwardDiff.jacobian(f, Array(x))') ≈ - reinterpret(value_type, [reference_shape_gradient(interpolation, x, i) for i in 1:n_basefuncs]) - # Check partition of unity at random point. - @test sum([reference_shape_value(interpolation, x, i) for i in 1:n_basefuncs]) ≈ 1.0 - # Check if the important functions are consistent - @test_throws ArgumentError reference_shape_value(interpolation, x, n_basefuncs+1) - # Idempotency test - @test reference_shape_value(interpolation, x, n_basefuncs) == reference_shape_value(interpolation, x, n_basefuncs) - end - # Remove after 1.6 is removed from CI (see above) - # Show coordinate in case failure (see issue #811) - !isempty(random_point_testset.results) && println("^^^^^Random point test failed at $x for $interpolation !^^^^^") + #TODO prefer this test style after 1.6 is removed from CI + # @testset let x = sample_random_point(ref_shape) # not compatible with Julia 1.6 + x = Vec{ref_dim, value_type}(sample_random_point(ref_shape)) + random_point_testset = @testset "Random point test" begin + # Check gradient evaluation + @test vec(ForwardDiff.jacobian(f, Array(x))') ≈ + reinterpret(value_type, [reference_shape_gradient(interpolation, x, i) for i in 1:n_basefuncs]) + # Check partition of unity at random point. + @test sum([reference_shape_value(interpolation, x, i) for i in 1:n_basefuncs]) ≈ 1.0 + # Check if the important functions are consistent + @test_throws ArgumentError reference_shape_value(interpolation, x, n_basefuncs + 1) + # Idempotency test + @test reference_shape_value(interpolation, x, n_basefuncs) == reference_shape_value(interpolation, x, n_basefuncs) + end + # Remove after 1.6 is removed from CI (see above) + # Show coordinate in case failure (see issue #811) + !isempty(random_point_testset.results) && println("^^^^^Random point test failed at $x for $interpolation !^^^^^") - # Test whether we have for each entity corresponding dof indices (possibly empty) - @test length(Ferrite.vertexdof_indices(interpolation)) == Ferrite.nvertices(interpolation) - @test length(Ferrite.facedof_indices(interpolation)) == Ferrite.nfaces(interpolation) - @test length(Ferrite.facedof_interior_indices(interpolation)) == Ferrite.nfaces(interpolation) - @test length(Ferrite.edgedof_indices(interpolation)) == Ferrite.nedges(interpolation) - @test length(Ferrite.edgedof_interior_indices(interpolation)) == Ferrite.nedges(interpolation) - # We have at least as many edge/face dofs as we have edge/face interior dofs - @test all(length.(Ferrite.facedof_interior_indices(interpolation)) .<= length.(Ferrite.facedof_indices(interpolation))) - @test all(length.(Ferrite.edgedof_interior_indices(interpolation)) .<= length.(Ferrite.edgedof_indices(interpolation))) - # The total number of dofs must match the number of base functions - totaldofs = sum(length.(Ferrite.vertexdof_indices(interpolation));init=0) - totaldofs += sum(length.(Ferrite.facedof_interior_indices(interpolation));init=0) - totaldofs += sum(length.(Ferrite.edgedof_interior_indices(interpolation));init=0) - totaldofs += length(Ferrite.volumedof_interior_indices(interpolation)) - @test totaldofs == n_basefuncs + # Test whether we have for each entity corresponding dof indices (possibly empty) + @test length(Ferrite.vertexdof_indices(interpolation)) == Ferrite.nvertices(interpolation) + @test length(Ferrite.facedof_indices(interpolation)) == Ferrite.nfaces(interpolation) + @test length(Ferrite.facedof_interior_indices(interpolation)) == Ferrite.nfaces(interpolation) + @test length(Ferrite.edgedof_indices(interpolation)) == Ferrite.nedges(interpolation) + @test length(Ferrite.edgedof_interior_indices(interpolation)) == Ferrite.nedges(interpolation) + # We have at least as many edge/face dofs as we have edge/face interior dofs + @test all(length.(Ferrite.facedof_interior_indices(interpolation)) .<= length.(Ferrite.facedof_indices(interpolation))) + @test all(length.(Ferrite.edgedof_interior_indices(interpolation)) .<= length.(Ferrite.edgedof_indices(interpolation))) + # The total number of dofs must match the number of base functions + totaldofs = sum(length.(Ferrite.vertexdof_indices(interpolation)); init = 0) + totaldofs += sum(length.(Ferrite.facedof_interior_indices(interpolation)); init = 0) + totaldofs += sum(length.(Ferrite.edgedof_interior_indices(interpolation)); init = 0) + totaldofs += length(Ferrite.volumedof_interior_indices(interpolation)) + @test totaldofs == n_basefuncs - # The dof indices are valid. - @test all([all(0 .< i .<= n_basefuncs) for i ∈ Ferrite.vertexdof_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i ∈ Ferrite.facedof_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i ∈ Ferrite.facedof_interior_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i ∈ Ferrite.edgedof_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i ∈ Ferrite.edgedof_interior_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i ∈ Ferrite.volumedof_interior_indices(interpolation)]) + # The dof indices are valid. + @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.vertexdof_indices(interpolation)]) + @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.facedof_indices(interpolation)]) + @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.facedof_interior_indices(interpolation)]) + @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.edgedof_indices(interpolation)]) + @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.edgedof_interior_indices(interpolation)]) + @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.volumedof_interior_indices(interpolation)]) - # Check for evaluation type correctness of interpolation - @testset "return type correctness dof $dof" for dof in 1:n_basefuncs - @test (@inferred reference_shape_value(interpolation, x, dof)) isa value_type - @test (@inferred reference_shape_gradient(interpolation, x, dof)) isa Vec{ref_dim, value_type} - end + # Check for evaluation type correctness of interpolation + @testset "return type correctness dof $dof" for dof in 1:n_basefuncs + @test (@inferred reference_shape_value(interpolation, x, dof)) isa value_type + @test (@inferred reference_shape_gradient(interpolation, x, dof)) isa Vec{ref_dim, value_type} + end - # Check for dirac delta property of interpolation - @testset "dirac delta property of dof $dof" for dof in 1:n_basefuncs - for k in 1:n_basefuncs - N_dof = reference_shape_value(interpolation, coords[dof], k) - if k == dof - @test N_dof ≈ 1.0 - else - factor = interpolation isa Lagrange{RefQuadrilateral, 3} ? 200 : 4 - @test N_dof ≈ 0.0 atol = factor * eps(value_type) + # Check for dirac delta property of interpolation + @testset "dirac delta property of dof $dof" for dof in 1:n_basefuncs + for k in 1:n_basefuncs + N_dof = reference_shape_value(interpolation, coords[dof], k) + if k == dof + @test N_dof ≈ 1.0 + else + factor = interpolation isa Lagrange{RefQuadrilateral, 3} ? 200 : 4 + @test N_dof ≈ 0.0 atol = factor * eps(value_type) + end end end - end - # Test that facedof_indices(...) return in counter clockwise order (viewing from the outside) - if interpolation isa Lagrange - function __outward_normal(coords::Vector{<:Vec{1}}, nodes) - n = coords[nodes[1]] - return n / norm(n) - end - function __outward_normal(coords::Vector{<:Vec{2}}, nodes) - p1 = coords[nodes[1]] - p2 = coords[nodes[2]] - n = Vec{2}((p2[2] - p1[2], - p2[1] + p1[1])) - return n / norm(n) - end - function __outward_normal(coords::Vector{<:Vec{3}}, nodes) - p1 = coords[nodes[1]] - p2 = coords[nodes[2]] - p3 = coords[nodes[3]] - n = (p3 - p2) × (p1 - p2) - return n / norm(n) - end - _bfunc = if ref_dim == 3 - Ferrite.facedof_indices(interpolation) - elseif ref_dim == 2 - Ferrite.edgedof_indices(interpolation) - elseif ref_dim == 1 - Ferrite.vertexdof_indices(interpolation) - end - for (facenodes, normal) in zip(_bfunc, reference_normals(interpolation)) - @test __outward_normal(coords, facenodes) ≈ normal - end - end + # Test that facedof_indices(...) return in counter clockwise order (viewing from the outside) + if interpolation isa Lagrange + function __outward_normal(coords::Vector{<:Vec{1}}, nodes) + n = coords[nodes[1]] + return n / norm(n) + end + function __outward_normal(coords::Vector{<:Vec{2}}, nodes) + p1 = coords[nodes[1]] + p2 = coords[nodes[2]] + n = Vec{2}((p2[2] - p1[2], - p2[1] + p1[1])) + return n / norm(n) + end + function __outward_normal(coords::Vector{<:Vec{3}}, nodes) + p1 = coords[nodes[1]] + p2 = coords[nodes[2]] + p3 = coords[nodes[3]] + n = (p3 - p2) × (p1 - p2) + return n / norm(n) + end + _bfunc = if ref_dim == 3 + Ferrite.facedof_indices(interpolation) + elseif ref_dim == 2 + Ferrite.edgedof_indices(interpolation) + elseif ref_dim == 1 + Ferrite.vertexdof_indices(interpolation) + end + for (facenodes, normal) in zip(_bfunc, reference_normals(interpolation)) + @test __outward_normal(coords, facenodes) ≈ normal + end + end - # regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/520 - #=interpolation_type = typeof(interpolation).name.wrapper + # regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/520 + #=interpolation_type = typeof(interpolation).name.wrapper if func_order > 1 && interpolation_type != Ferrite.Serendipity first_order = interpolation_type{ref_shape,1}() for (highorderface, firstorderface) in zip(Ferrite.facedof_indices(interpolation), Ferrite.facedof_indices(first_order)) @@ -189,81 +189,81 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end=# - # VectorizedInterpolation - v_interpolation_1 = interpolation^2 - v_interpolation_2 = (d = 2; interpolation^d) - @test getnbasefunctions(v_interpolation_1) == - getnbasefunctions(v_interpolation_2) == - getnbasefunctions(interpolation) * 2 - # pretty printing - @test repr("text/plain", v_interpolation_1) == repr(v_interpolation_1.ip) * "^2" + # VectorizedInterpolation + v_interpolation_1 = interpolation^2 + v_interpolation_2 = (d = 2; interpolation^d) + @test getnbasefunctions(v_interpolation_1) == + getnbasefunctions(v_interpolation_2) == + getnbasefunctions(interpolation) * 2 + # pretty printing + @test repr("text/plain", v_interpolation_1) == repr(v_interpolation_1.ip) * "^2" - # Check for evaluation type correctness of vectorized interpolation - v_interpolation_3 = interpolation^ref_dim - @testset "vectorized case of return type correctness of dof $dof" for dof in 1:n_basefuncs - @test @inferred(reference_shape_value(v_interpolation_1, x, dof)) isa Vec{2, value_type} - @test @inferred(reference_shape_gradient(v_interpolation_3, x, dof)) isa Tensor{2, ref_dim, value_type} - end - end # correctness testset + # Check for evaluation type correctness of vectorized interpolation + v_interpolation_3 = interpolation^ref_dim + @testset "vectorized case of return type correctness of dof $dof" for dof in 1:n_basefuncs + @test @inferred(reference_shape_value(v_interpolation_1, x, dof)) isa Vec{2, value_type} + @test @inferred(reference_shape_gradient(v_interpolation_3, x, dof)) isa Tensor{2, ref_dim, value_type} + end + end # correctness testset - @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTriangle,0}()) ≈ [Vec{2,Float64}((1/3,1/3))] - @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefQuadrilateral,0}()) ≈ [Vec{2,Float64}((0,0))] - @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTetrahedron,0}()) ≈ [Vec{3,Float64}((1/4,1/4,1/4))] - @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefHexahedron,0}()) ≈ [Vec{3,Float64}((0,0,0))] + @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTriangle, 0}()) ≈ [Vec{2, Float64}((1 / 3, 1 / 3))] + @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefQuadrilateral, 0}()) ≈ [Vec{2, Float64}((0, 0))] + @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTetrahedron, 0}()) ≈ [Vec{3, Float64}((1 / 4, 1 / 4, 1 / 4))] + @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefHexahedron, 0}()) ≈ [Vec{3, Float64}((0, 0, 0))] - # Test discontinuous interpolations related functions - d_ip = DiscontinuousLagrange{RefQuadrilateral,1}() - d_ip_t = DiscontinuousLagrange{RefQuadrilateral,1} + # Test discontinuous interpolations related functions + d_ip = DiscontinuousLagrange{RefQuadrilateral, 1}() + d_ip_t = DiscontinuousLagrange{RefQuadrilateral, 1} - ip = Lagrange{RefQuadrilateral,1}() - ip_t = Lagrange{RefQuadrilateral,1} + ip = Lagrange{RefQuadrilateral, 1}() + ip_t = Lagrange{RefQuadrilateral, 1} - @test Ferrite.is_discontinuous(ip) == false - @test Ferrite.is_discontinuous(ip_t) == false - @test Ferrite.is_discontinuous(d_ip) == true - @test Ferrite.is_discontinuous(d_ip_t) == true -end + @test Ferrite.is_discontinuous(ip) == false + @test Ferrite.is_discontinuous(ip_t) == false + @test Ferrite.is_discontinuous(d_ip) == true + @test Ferrite.is_discontinuous(d_ip_t) == true + end -@testset "Correctness of AD of embedded interpolations" begin - ip = Lagrange{RefHexahedron,2}()^3 - ξ = rand(Vec{3,Float64}) - for I in 1:getnbasefunctions(ip) - #Call StaticArray-version - H_sa, G_sa, V_sa = Ferrite._reference_shape_hessian_gradient_and_value_static_array(ip, ξ, I) - #Call tensor AD version - H, G, V = Ferrite.reference_shape_hessian_gradient_and_value(ip, ξ, I) + @testset "Correctness of AD of embedded interpolations" begin + ip = Lagrange{RefHexahedron, 2}()^3 + ξ = rand(Vec{3, Float64}) + for I in 1:getnbasefunctions(ip) + #Call StaticArray-version + H_sa, G_sa, V_sa = Ferrite._reference_shape_hessian_gradient_and_value_static_array(ip, ξ, I) + #Call tensor AD version + H, G, V = Ferrite.reference_shape_hessian_gradient_and_value(ip, ξ, I) - @test V ≈ V_sa - @test G ≈ G_sa - @test H ≈ H_sa - end + @test V ≈ V_sa + @test G ≈ G_sa + @test H ≈ H_sa + end - ips = Lagrange{RefQuadrilateral,2}() - vdim = 3 - ipv = ips^vdim - ξ = rand(Vec{2, Float64}) - for ipv_ind in 1:getnbasefunctions(ipv) - ips_ind, v_ind = fldmod1(ipv_ind, vdim) - H, G, V = Ferrite.reference_shape_hessian_gradient_and_value(ipv, ξ, ipv_ind) - h, g, v = Ferrite.reference_shape_hessian_gradient_and_value(ips, ξ, ips_ind) - @test h ≈ H[v_ind, :, :] - @test g ≈ G[v_ind, :] - @test v ≈ V[v_ind] + ips = Lagrange{RefQuadrilateral, 2}() + vdim = 3 + ipv = ips^vdim + ξ = rand(Vec{2, Float64}) + for ipv_ind in 1:getnbasefunctions(ipv) + ips_ind, v_ind = fldmod1(ipv_ind, vdim) + H, G, V = Ferrite.reference_shape_hessian_gradient_and_value(ipv, ξ, ipv_ind) + h, g, v = Ferrite.reference_shape_hessian_gradient_and_value(ips, ξ, ips_ind) + @test h ≈ H[v_ind, :, :] + @test g ≈ G[v_ind, :] + @test v ≈ V[v_ind] + end end -end -@testset "Errors for entitydof_indices on VectorizedInterpolations" begin - ip = Lagrange{RefQuadrilateral,2}()^2 - @test_throws ArgumentError Ferrite.vertexdof_indices(ip) - @test_throws ArgumentError Ferrite.edgedof_indices(ip) - @test_throws ArgumentError Ferrite.facedof_indices(ip) - @test_throws ArgumentError Ferrite.facetdof_indices(ip) + @testset "Errors for entitydof_indices on VectorizedInterpolations" begin + ip = Lagrange{RefQuadrilateral, 2}()^2 + @test_throws ArgumentError Ferrite.vertexdof_indices(ip) + @test_throws ArgumentError Ferrite.edgedof_indices(ip) + @test_throws ArgumentError Ferrite.facedof_indices(ip) + @test_throws ArgumentError Ferrite.facetdof_indices(ip) - @test_throws ArgumentError Ferrite.edgedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) -end + @test_throws ArgumentError Ferrite.edgedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) + end end # testset diff --git a/test/test_l2_projection.jl b/test/test_l2_projection.jl index ea72b99980..f2c7cd3844 100644 --- a/test/test_l2_projection.jl +++ b/test/test_l2_projection.jl @@ -1,9 +1,8 @@ - # Tests a L2-projection of integration point values (to nodal values), # determined from the function y = 1 + x[1]^2 + (2x[2])^2 function test_projection(order, refshape) element = refshape == RefQuadrilateral ? Quadrilateral : Triangle - grid = generate_grid(element, (1, 1), Vec((0.,0.)), Vec((1.,1.))) + grid = generate_grid(element, (1, 1), Vec((0.0, 0.0)), Vec((1.0, 1.0))) ip = Lagrange{refshape, order}() ip_geom = Lagrange{refshape, 1}() @@ -47,49 +46,49 @@ function test_projection(order, refshape) @test point_vars ≈ point_vars_2 ≈ ae # Vec - f_vector(x) = Vec{1,Float64}((f(x),)) + f_vector(x) = Vec{1, Float64}((f(x),)) qp_values = analytical(f_vector) point_vars = project(proj, qp_values, qr) if order == 1 - ae = [Vec{1,Float64}((f_approx(j),)) for j in 1:4] + ae = [Vec{1, Float64}((f_approx(j),)) for j in 1:4] elseif order == 2 ae = zeros(length(point_vars)) apply_analytical!(ae, proj.dh, :_, x -> f_vector(x)[1]) - ae = reinterpret(Vec{1,Float64}, ae) + ae = reinterpret(Vec{1, Float64}, ae) end @test point_vars ≈ ae # Tensor - f_tensor(x) = Tensor{2,2,Float64}((f(x),2*f(x),3*f(x),4*f(x))) + f_tensor(x) = Tensor{2, 2, Float64}((f(x), 2 * f(x), 3 * f(x), 4 * f(x))) qp_values = analytical(f_tensor) qp_values_matrix = reduce(hcat, qp_values)::Matrix point_vars = project(proj, qp_values, qr) point_vars_2 = project(proj, qp_values_matrix, qr) if order == 1 - ae = [Tensor{2,2,Float64}((f_approx(i),2*f_approx(i),3*f_approx(i),4*f_approx(i))) for i in 1:4] + ae = [Tensor{2, 2, Float64}((f_approx(i), 2 * f_approx(i), 3 * f_approx(i), 4 * f_approx(i))) for i in 1:4] elseif order == 2 ae = zeros(4, length(point_vars)) for i in 1:4 apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f_tensor(x)[i]) end - ae = reinterpret(reshape, Tensor{2,2,Float64,4}, ae) + ae = reinterpret(reshape, Tensor{2, 2, Float64, 4}, ae) end @test point_vars ≈ point_vars_2 ≈ ae # SymmetricTensor - f_stensor(x) = SymmetricTensor{2,2,Float64}((f(x),2*f(x),3*f(x))) + f_stensor(x) = SymmetricTensor{2, 2, Float64}((f(x), 2 * f(x), 3 * f(x))) qp_values = analytical(f_stensor) qp_values_matrix = reduce(hcat, qp_values) point_vars = project(proj, qp_values, qr) point_vars_2 = project(proj, qp_values_matrix, qr) if order == 1 - ae = [SymmetricTensor{2,2,Float64}((f_approx(i),2*f_approx(i),3*f_approx(i))) for i in 1:4] + ae = [SymmetricTensor{2, 2, Float64}((f_approx(i), 2 * f_approx(i), 3 * f_approx(i))) for i in 1:4] elseif order == 2 ae = zeros(3, length(point_vars)) for i in 1:3 apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f_stensor(x).data[i]) end - ae = reinterpret(reshape, SymmetricTensor{2,2,Float64,3}, ae) + ae = reinterpret(reshape, SymmetricTensor{2, 2, Float64, 3}, ae) end @test point_vars ≈ point_vars_2 ≈ ae @@ -99,7 +98,8 @@ function test_projection(order, refshape) else bad_order = 1 end - @test_throws LinearAlgebra.PosDefException L2Projector(ip, grid; qr_lhs=QuadratureRule{refshape}(bad_order)) + @test_throws LinearAlgebra.PosDefException L2Projector(ip, grid; qr_lhs = QuadratureRule{refshape}(bad_order)) + return end function make_mixedgrid_l2_tests() @@ -107,9 +107,11 @@ function make_mixedgrid_l2_tests() # 5 --- 6 --- 7 --- 8 # | 1 | 2/3 | 4 | # 1 --- 2 --- 3 --- 4 - nodes = [Node(Float64.((x,y))) for (x, y) in - # 1, 2, 3, 4, 5, 6, 7, 8 - ((0, 0), (1, 0), (2, 0), (3, 0), (0, 1), (1, 1), (2, 1), (3, 1))] + nodes = [ + Node(Float64.((x, y))) for (x, y) in + # 1, 2, 3, 4, 5, 6, 7, 8 + ((0, 0), (1, 0), (2, 0), (3, 0), (0, 1), (1, 1), (2, 1), (3, 1)) + ] cells = [Quadrilateral((1, 2, 6, 5)), Triangle((2, 7, 6)), Triangle((2, 3, 7)), Quadrilateral((3, 4, 8, 7))] @@ -126,12 +128,12 @@ function test_projection_subset_of_mixedgrid() order = 2 ip = Lagrange{RefQuadrilateral, order}() ip_geom = Lagrange{RefQuadrilateral, 1}() - qr = QuadratureRule{RefQuadrilateral}(order+1) + qr = QuadratureRule{RefQuadrilateral}(order + 1) cv = CellValues(qr, ip, ip_geom) # Create node values for the 1st cell # use a SymmetricTensor here for testing the symmetric version of project - f(x) = SymmetricTensor{2,2,Float64}((1 + x[1]^2, 2x[2]^2, x[1]*x[2])) + f(x) = SymmetricTensor{2, 2, Float64}((1 + x[1]^2, 2x[2]^2, x[1] * x[2])) xe = getcoordinates(mesh, 1) # analytical values @@ -146,7 +148,7 @@ function test_projection_subset_of_mixedgrid() # Assume f would only exist on the first cell, we project it to the nodes of the # 1st cell while ignoring the rest of the domain. NaNs should be stored in all # nodes that do not belong to the 1st cell - proj = L2Projector(ip, mesh; set=quadset) + proj = L2Projector(ip, mesh; set = quadset) point_vars = project(proj, qp_values, qr) point_vars_2 = project(proj, qp_values_matrix, qr) point_vars_3 = project(proj, qp_values_dict, qr) @@ -162,7 +164,7 @@ function test_projection_subset_of_mixedgrid() for i in 1:3 apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f(x).data[i], quadset) end - ae = reinterpret(reshape, SymmetricTensor{2,2,Float64,3}, ae) + ae = reinterpret(reshape, SymmetricTensor{2, 2, Float64, 3}, ae) @test point_vars ≈ point_vars_2 ≈ ae @test point_vars_3 ≈ ae @@ -173,9 +175,9 @@ function test_projection_subset_of_mixedgrid() cv = CellValues(qr, ip, ip_geom) nqp = getnquadpoints(cv) - qp_values_tria = [SymmetricTensor{2,2,Float64,3}[] for _ in 1:getncells(mesh)] - qp_values_matrix_tria = [zero(SymmetricTensor{2,2}) * NaN for _ in 1:nqp, _ in 1:getncells(mesh)] - qp_values_dict = Dict{Int, Vector{SymmetricTensor{2,2,Float64,3}}}() + qp_values_tria = [SymmetricTensor{2, 2, Float64, 3}[] for _ in 1:getncells(mesh)] + qp_values_matrix_tria = [zero(SymmetricTensor{2, 2}) * NaN for _ in 1:nqp, _ in 1:getncells(mesh)] + qp_values_dict = Dict{Int, Vector{SymmetricTensor{2, 2, Float64, 3}}}() for (ic, cellid) in enumerate(triaset) xe = getcoordinates(mesh, cellid) # analytical values @@ -186,7 +188,7 @@ function test_projection_subset_of_mixedgrid() end #tria - proj = L2Projector(ip, mesh; set=triaset) + proj = L2Projector(ip, mesh; set = triaset) point_vars = project(proj, qp_values_tria, qr) point_vars_2 = project(proj, qp_values_matrix_tria, qr) projection_at_nodes = evaluate_at_grid_nodes(proj, point_vars) @@ -201,8 +203,9 @@ function test_projection_subset_of_mixedgrid() for i in 1:3 apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f(x).data[i], triaset) end - ae = reinterpret(reshape, SymmetricTensor{2,2,Float64,3}, ae) + ae = reinterpret(reshape, SymmetricTensor{2, 2, Float64, 3}, ae) @test point_vars ≈ point_vars_2 ≈ ae + return end function calculate_function_value_in_qpoints!(qp_data, sdh, cv, dofvector::Vector) @@ -218,8 +221,8 @@ function calculate_function_value_in_qpoints!(qp_data, sdh, cv, dofvector::Vecto end function test_add_projection_grid() - grid = generate_grid(Triangle, (3,3)) - set1 = Set(1:getncells(grid)÷2) + grid = generate_grid(Triangle, (3, 3)) + set1 = Set(1:(getncells(grid) ÷ 2)) set2 = setdiff(1:getncells(grid), set1) dh = DofHandler(grid) @@ -262,6 +265,7 @@ function test_add_projection_grid() @test projected1_at_nodes ≈ solution_at_nodes @test projected2_at_nodes ≈ solution_at_nodes + return end function test_projection_mixedgrid() @@ -319,18 +323,19 @@ function test_projection_mixedgrid() @test projected_at_nodes[check_nodes] ≈ solution_at_nodes[check_nodes] end + return end -function test_export(;subset::Bool) +function test_export(; subset::Bool) grid = generate_grid(Quadrilateral, (2, 1)) qr = QuadratureRule{RefQuadrilateral}(2) - ip = Lagrange{RefQuadrilateral,1}() + ip = Lagrange{RefQuadrilateral, 1}() cv = CellValues(qr, ip) nqp = getnquadpoints(cv) qpdata_scalar = [zeros(nqp) for _ in 1:getncells(grid)] qpdata_vec = [zeros(Vec{2}, nqp) for _ in 1:getncells(grid)] - qpdata_tens = [zeros(Tensor{2,2}, nqp) for _ in 1:getncells(grid)] - qpdata_stens = [zeros(SymmetricTensor{2,2}, nqp) for _ in 1:getncells(grid)] + qpdata_tens = [zeros(Tensor{2, 2}, nqp) for _ in 1:getncells(grid)] + qpdata_stens = [zeros(SymmetricTensor{2, 2}, nqp) for _ in 1:getncells(grid)] function f(x) if subset && x[1] > 0.001 return NaN @@ -345,15 +350,15 @@ function test_export(;subset::Bool) x = spatial_coordinate(cv, qp, xh) qpdata_scalar[cellid(cell)][qp] = f(x) qpdata_vec[cellid(cell)][qp] = Vec{2}(i -> i * f(x)) - qpdata_tens[cellid(cell)][qp] = Tensor{2,2}((i,j) -> i * j * f(x)) - qpdata_stens[cellid(cell)][qp] = SymmetricTensor{2,2}((i,j) -> i * j * f(x)) + qpdata_tens[cellid(cell)][qp] = Tensor{2, 2}((i, j) -> i * j * f(x)) + qpdata_stens[cellid(cell)][qp] = SymmetricTensor{2, 2}((i, j) -> i * j * f(x)) end end - p = subset ? L2Projector(ip, grid; set=1:1) : L2Projector(ip, grid) + p = subset ? L2Projector(ip, grid; set = 1:1) : L2Projector(ip, grid) p_scalar = project(p, qpdata_scalar, qr)::Vector{Float64} p_vec = project(p, qpdata_vec, qr)::Vector{<:Vec{2}} - p_tens = project(p, qpdata_tens, qr)::Vector{<:Tensor{2,2}} - p_stens = project(p, qpdata_stens, qr)::Vector{<:SymmetricTensor{2,2}} + p_tens = project(p, qpdata_tens, qr)::Vector{<:Tensor{2, 2}} + p_stens = project(p, qpdata_stens, qr)::Vector{<:SymmetricTensor{2, 2}} # reshaping for export with evaluate_at_grid_nodes fnodes = [f(x.x) for x in grid.nodes] @@ -372,7 +377,7 @@ function test_export(;subset::Bool) let r = evaluate_at_grid_nodes(p, p_vec), rv = Ferrite._evaluate_at_grid_nodes(p, p_vec, Val(true)) @test size(r) == (6,) - @test getindex.(r[findex], 1) ≈ fnodes[findex] + @test getindex.(r[findex], 1) ≈ fnodes[findex] @test getindex.(r[findex], 2) ≈ 2fnodes[findex] @test all(y -> all(isnan, y), r[nindex]) @test rv[1:2, findex] ≈ reshape(reinterpret(Float64, r), (2, 6))[:, findex] @@ -382,7 +387,7 @@ function test_export(;subset::Bool) let r = evaluate_at_grid_nodes(p, p_tens), rv = Ferrite._evaluate_at_grid_nodes(p, p_tens, Val(true)) @test size(r) == (6,) - @test getindex.(r[findex], 1) ≈ fnodes[findex] # 11-components + @test getindex.(r[findex], 1) ≈ fnodes[findex] # 11-components @test getindex.(r[findex], 2) ≈ 2fnodes[findex] # 12-components @test getindex.(r[findex], 3) ≈ 2fnodes[findex] # 21-components @test getindex.(r[findex], 4) ≈ 4fnodes[findex] # 22-components @@ -394,7 +399,7 @@ function test_export(;subset::Bool) let r = evaluate_at_grid_nodes(p, p_stens), rv = Ferrite._evaluate_at_grid_nodes(p, p_stens, Val(true)) @test size(r) == (6,) - @test getindex.(r[findex], 1) ≈ fnodes[findex] # 11-components + @test getindex.(r[findex], 1) ≈ fnodes[findex] # 11-components @test getindex.(r[findex], 2) ≈ 2fnodes[findex] # 21-components @test getindex.(r[findex], 3) ≈ 2fnodes[findex] # 12-components @test getindex.(r[findex], 4) ≈ 4fnodes[findex] # 22-components @@ -415,17 +420,18 @@ function test_export(;subset::Bool) # The following test may fail due to floating point inaccuracies # These could occur due to e.g. changes in system architecture. if Sys.islinux() && Sys.ARCH === :x86_64 - @test bytes2hex(open(SHA.sha1, fname*".vtu", "r")) == ( + @test bytes2hex(open(SHA.sha1, fname * ".vtu", "r")) == ( subset ? "b3fef3de9f38ca9ddd92f2f67a1606d07ca56d67" : - "bc2ec8f648f9b8bccccf172c1fc48bf03340329b" + "bc2ec8f648f9b8bccccf172c1fc48bf03340329b" ) end end + return end function test_show_l2() - grid = generate_grid(Triangle, (2,2)) + grid = generate_grid(Triangle, (2, 2)) ip = Lagrange{RefTriangle, 1}() proj = L2Projector(ip, grid) @test repr("text/plain", proj) == repr(typeof(proj)) * "\n projection on: 8/8 cells in grid\n function interpolation: Lagrange{RefTriangle, 1}()\n geometric interpolation: Lagrange{RefTriangle, 1}()\n" @@ -441,13 +447,14 @@ function test_show_l2() @test contains(showstr, "L2Projector") @test contains(showstr, "4/8 cells in grid") @test contains(showstr, "Split into 2 sets") + return end function test_l2proj_errorpaths() - grid = generate_grid(Triangle, (2,3)) + grid = generate_grid(Triangle, (2, 3)) ip = Lagrange{RefTriangle, 1}() proj = L2Projector(grid) # Multiple subdomains - proj1 = L2Projector(ip, grid; set=collect(1:4)) # Single sub-domain case + proj1 = L2Projector(ip, grid; set = collect(1:4)) # Single sub-domain case qr_tria = QuadratureRule{RefTriangle}(2) qr_quad = QuadratureRule{RefQuadrilateral}(2) @@ -483,7 +490,7 @@ function test_l2proj_errorpaths() wrongnqp_exception = ErrorException("The number of variables per cell doesn't match the number of quadrature points") @test_throws wrongnqp_exception project(proj1, data_invalid2, qr_tria) @test_throws wrongnqp_exception project(proj1, data_invalid3, qr_tria) - + return end @testset "Test L2-Projection" begin @@ -494,8 +501,8 @@ end test_projection_subset_of_mixedgrid() test_add_projection_grid() test_projection_mixedgrid() - test_export(subset=false) - test_export(subset=true) + test_export(subset = false) + test_export(subset = true) test_show_l2() test_l2proj_errorpaths() end diff --git a/test/test_mixeddofhandler.jl b/test/test_mixeddofhandler.jl index 5cb5a93a0c..ec4a7206b8 100644 --- a/test/test_mixeddofhandler.jl +++ b/test/test_mixeddofhandler.jl @@ -1,17 +1,17 @@ # Some helper functions function get_cellset(cell_type, cells) - return Set(findall(c-> typeof(c) == cell_type, cells)) + return Set(findall(c -> typeof(c) == cell_type, cells)) end function get_2d_grid() # GIVEN: two cells, a quad and a triangle sharing one face cells = [ Quadrilateral((1, 2, 3, 4)), - Triangle((3, 2, 5)) - ] - coords = zeros(Vec{2,Float64}, 5) - nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 5)] - return Grid(cells,nodes) + Triangle((3, 2, 5)), + ] + coords = zeros(Vec{2, Float64}, 5) + nodes = [Node(coord) for coord in zeros(Vec{2, Float64}, 5)] + return Grid(cells, nodes) end function test_1d_bar_beam() @@ -22,17 +22,17 @@ function test_1d_bar_beam() Line((1, 2)), Line((2, 3)), Line((1, 3)), - ] - nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 3)] + ] + nodes = [Node(coord) for coord in zeros(Vec{2, Float64}, 3)] grid = Grid(cells, nodes) ip = Lagrange{RefLine, 1}() - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(3)) add!(sdh1, :u, ip^2) add!(sdh1, :θ, ip) - sdh2 = SubDofHandler(dh, OrderedSet((1,2))) + sdh2 = SubDofHandler(dh, OrderedSet((1, 2))) add!(sdh2, :u, ip^2) close!(dh) @test ndofs(dh) == 8 @@ -45,13 +45,14 @@ function test_1d_bar_beam() # / \ # /_________\ # 1,2,5 3,4,6 + return end function test_2d_scalar() grid = get_2d_grid() # WHEN: adding a scalar field for each cell and generating dofs - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :u, Lagrange{RefQuadrilateral, 1}()) sdh2 = SubDofHandler(dh, Set(2)) @@ -63,20 +64,22 @@ function test_2d_scalar() @test dh.cell_dofs == [1, 2, 3, 4, 3, 2, 5] @test celldofs(dh, 1) == [1, 2, 3, 4] @test celldofs(dh, 2) == [3, 2, 5] + return end function test_2d_error() grid = get_2d_grid() # the refshape of the field must be the same as the refshape of the elements it is added to - dh = DofHandler(grid); + dh = DofHandler(grid) # wrong refshape compared to cell sdh1 = SubDofHandler(dh, Set(1)) - @test_throws ErrorException add!(sdh1, :u, Lagrange{RefTriangle, 1}()); + @test_throws ErrorException add!(sdh1, :u, Lagrange{RefTriangle, 1}()) sdh2 = SubDofHandler(dh, Set(2)) @test_throws ErrorException add!(sdh2, :u, Lagrange{RefQuadrilateral, 1}()) # all cells within a SubDofHandler should be of the same celltype - @test_throws ErrorException SubDofHandler(dh, Set((1,2))) + @test_throws ErrorException SubDofHandler(dh, Set((1, 2))) + return end function test_2d_vector() @@ -94,13 +97,13 @@ function test_2d_vector() @test ndofs(dh) == 10 @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8] @test celldofs(dh, 2) == [5, 6, 3, 4, 9, 10] - + return end function test_2d_mixed_1_el() grid = get_2d_grid() ## mixed field of same order - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :u, Lagrange{RefQuadrilateral, 1}()^2) add!(sdh1, :p, Lagrange{RefQuadrilateral, 1}()) @@ -112,13 +115,14 @@ function test_2d_mixed_1_el() @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] @test Set(Ferrite.getfieldnames(dh)) == Set(Ferrite.getfieldnames(dh.subdofhandlers[1])) + return end function test_2d_mixed_2_el() grid = get_2d_grid() ## mixed field of same order - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :u, Lagrange{RefQuadrilateral, 1}()^2) add!(sdh1, :p, Lagrange{RefQuadrilateral, 1}()) @@ -137,16 +141,17 @@ function test_2d_mixed_2_el() @test_throws ErrorException Ferrite.nnodes_per_cell(grid) @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] @test celldofs(dh, 2) == [5, 6, 3, 4, 13, 14, 11, 10, 15] + return end function test_face_dofs_2_tri() cells = [ Triangle((1, 2, 3)), - Triangle((2, 4, 3)) - ] - nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 4)] - grid = Grid(cells, nodes); - dh = DofHandler(grid); + Triangle((2, 4, 3)), + ] + nodes = [Node(coord) for coord in zeros(Vec{2, Float64}, 4)] + grid = Grid(cells, nodes) + dh = DofHandler(grid) add!(dh, :u, Lagrange{RefTriangle, 2}()^2) close!(dh) @@ -154,6 +159,7 @@ function test_face_dofs_2_tri() @test ndofs(dh) == 18 @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] @test celldofs(dh, 2) == [3, 4, 13, 14, 5, 6, 15, 16, 17, 18, 9, 10] + return end function test_3d_tetrahedrons() @@ -164,28 +170,29 @@ function test_3d_tetrahedrons() Tetrahedron((2, 8, 4, 7)), Tetrahedron((2, 5, 6, 7)), Tetrahedron((2, 6, 8, 7)), - ] - nodes = [Node(coord) for coord in zeros(Vec{3,Float64}, 8)] + ] + nodes = [Node(coord) for coord in zeros(Vec{3, Float64}, 8)] grid = Grid(cells, nodes) - dh = DofHandler(grid); + dh = DofHandler(grid) add!(dh, :u, Lagrange{RefTetrahedron, 2}()^3) close!(dh) # reference using the regular DofHandler - tet_grid = generate_grid(Tetrahedron, (1, 1,1)) + tet_grid = generate_grid(Tetrahedron, (1, 1, 1)) tet_dh = DofHandler(tet_grid) - add!(tet_dh, :u, Lagrange{RefTetrahedron,2}()^3) + add!(tet_dh, :u, Lagrange{RefTetrahedron, 2}()^3) close!(tet_dh) for i in 1:6 @test celldofs(dh, i) == celldofs(tet_dh, i) end + return end function test_face_dofs_quad_tri() # quadratic quad and quadratic triangle grid = get_2d_grid() - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :u, Lagrange{RefQuadrilateral, 2}()^2) sdh2 = SubDofHandler(dh, Set(2)) @@ -196,23 +203,25 @@ function test_face_dofs_quad_tri() @test ndofs(dh) == 24 @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] @test celldofs(dh, 2) == [5, 6, 3, 4, 19, 20, 11, 12, 21, 22, 23, 24] + return end function test_serendipity_quad_tri() # bi-quadratic quad (Serendipity) and quadratic triangle grid = get_2d_grid() interpolation = Serendipity{RefQuadrilateral, 2}() - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :u, interpolation^2) sdh2 = SubDofHandler(dh, Set(2)) - add!(sdh2, :u, Lagrange{RefTriangle,2}()^2) + add!(sdh2, :u, Lagrange{RefTriangle, 2}()^2) close!(dh) # THEN: @test ndofs(dh) == 22 @test celldofs(dh, 1) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] @test celldofs(dh, 2) == [5, 6, 3, 4, 17, 18, 11, 12, 19, 20, 21, 22] + return end function test_2d_mixed_field_triangles() @@ -221,17 +230,18 @@ function test_2d_mixed_field_triangles() # celltypes: 2 Triangles cells = [ Triangle((1, 2, 3)), - Triangle((2, 4, 3)) - ] - nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 4)] + Triangle((2, 4, 3)), + ] + nodes = [Node(coord) for coord in zeros(Vec{2, Float64}, 4)] grid = Grid(cells, nodes) - dh = DofHandler(grid); + dh = DofHandler(grid) add!(dh, :u, Lagrange{RefTriangle, 2}()^2) add!(dh, :p, Lagrange{RefTriangle, 1}()) close!(dh) @test ndofs(dh) == 22 @test celldofs(dh, 1) == collect(1:15) @test celldofs(dh, 2) == [3, 4, 16, 17, 5, 6, 18, 19, 20, 21, 9, 10, 14, 22, 15] + return end function test_2d_mixed_field_mixed_celltypes() @@ -240,7 +250,7 @@ function test_2d_mixed_field_mixed_celltypes() # celltypes: 1 Quadrilateral and 1 Triangle grid = get_2d_grid() - dh = DofHandler(grid); + dh = DofHandler(grid) sdh_quad = SubDofHandler(dh, Set(1)) add!(sdh_quad, :u, Lagrange{RefQuadrilateral, 2}()^2) add!(sdh_quad, :p, Lagrange{RefQuadrilateral, 1}()) @@ -251,6 +261,7 @@ function test_2d_mixed_field_mixed_celltypes() @test ndofs(dh) == 29 @test celldofs(dh, 1) == collect(1:22) @test celldofs(dh, 2) == [5, 6, 3, 4, 23, 24, 11, 12, 25, 26, 27, 28, 21, 20, 29] + return end function test_3d_mixed_field_mixed_celltypes() @@ -261,11 +272,11 @@ function test_3d_mixed_field_mixed_celltypes() cells = [ Hexahedron((1, 2, 3, 4, 5, 6, 7, 8)), Quadrilateral((3, 2, 9, 10)), - ] - nodes = [Node(coord) for coord in zeros(Vec{3,Float64}, 10)] + ] + nodes = [Node(coord) for coord in zeros(Vec{3, Float64}, 10)] grid = Grid(cells, nodes) - dh = DofHandler(grid); + dh = DofHandler(grid) # E.g. 3d continuum el -> 3dofs/node sdh_hex = SubDofHandler(dh, Set(1)) add!(sdh_hex, :u, Lagrange{RefHexahedron, 1}()^3) @@ -278,6 +289,7 @@ function test_3d_mixed_field_mixed_celltypes() @test ndofs(dh) == 42 @test celldofs(dh, 1) == collect(1:24) @test celldofs(dh, 2) == [7, 8, 9, 4, 5, 6, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42] + return end function test_2_element_heat_eq() @@ -286,7 +298,7 @@ function test_2_element_heat_eq() grid = generate_grid(Quadrilateral, (2, 1)) - dh = DofHandler(grid); + dh = DofHandler(grid) sdh1 = SubDofHandler(dh, Set(1)) add!(sdh1, :u, Lagrange{RefQuadrilateral, 1}()) sdh2 = SubDofHandler(dh, Set(2)) @@ -294,13 +306,13 @@ function test_2_element_heat_eq() close!(dh) # Create two Dirichlet boundary conditions - one for each field. - ch = ConstraintHandler(dh); + ch = ConstraintHandler(dh) ∂Ω1 = getfacetset(grid, "left") ∂Ω2 = getfacetset(grid, "right") dbc1 = Dirichlet(:u, ∂Ω1, (x, t) -> 0) dbc2 = Dirichlet(:u, ∂Ω2, (x, t) -> 0) - add!(ch, dbc1); - add!(ch, dbc2); + add!(ch, dbc1) + add!(ch, dbc2) close!(ch) function doassemble(cellset, cellvalues, assembler, dh) @@ -322,7 +334,7 @@ function test_2_element_heat_eq() dΩ = getdetJdV(cellvalues, q_point) for i in 1:n_basefuncs - v = shape_value(cellvalues, q_point, i) + v = shape_value(cellvalues, q_point, i) ∇v = shape_gradient(cellvalues, q_point, i) fe[i] += v * dΩ for j in 1:n_basefuncs @@ -333,11 +345,12 @@ function test_2_element_heat_eq() end assemble!(assembler, eldofs, Ke, fe) end + return end K = allocate_matrix(dh) - f = zeros(ndofs(dh)); - assembler = start_assemble(K, f); + f = zeros(ndofs(dh)) + assembler = start_assemble(K, f) # Use the same assemble function since it is the same weak form for both cell-types for sdh in dh.subdofhandlers qr = QuadratureRule{RefQuadrilateral}(2) @@ -346,26 +359,26 @@ function test_2_element_heat_eq() doassemble(sdh.cellset, cellvalues, assembler, dh) end - update!(ch, 0.0); + update!(ch, 0.0) apply!(K, f, ch) - u = K \ f; + u = K \ f # tested against heat_equation.jl (in the examples folder) using 2x1 cells and no # dbc on top and bottom boundary @test u == [0.0, 0.5, 0.5, 0.0, 0.0, 0.0] gridfilename = "mixed_grid" - addcellset!(grid, "cell-1", [1,]) - addcellset!(grid, "cell-2", [2,]) + addcellset!(grid, "cell-1", [1]) + addcellset!(grid, "cell-2", [2]) VTKGridFile(gridfilename, grid) do vtk Ferrite.write_cellset(vtk, grid, "cell-1") Ferrite.write_cellset(vtk, grid, "cell-2") write_solution(vtk, dh, u) # Ferrite.write_constraints(vtk, ch) #FIXME end - sha = bytes2hex(open(SHA.sha1, gridfilename*".vtu")) + sha = bytes2hex(open(SHA.sha1, gridfilename * ".vtu")) @test sha in ("e96732c000b0b385db7444f002461468b60b3b2c", "7b26edc27b5e59a2f60907374cd5a5790cc37a6a") - + return end @@ -379,24 +392,25 @@ function test_element_order() cells = [ Triangle((1, 2, 3)), Quadrilateral((2, 4, 5, 3)), - Triangle((4, 6, 5)) - ] - nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 6)] + Triangle((4, 6, 5)), + ] + nodes = [Node(coord) for coord in zeros(Vec{2, Float64}, 6)] grid = Grid(cells, nodes) - dh = DofHandler(grid); + dh = DofHandler(grid) # Note the jump in cell numbers - sdh_tri = SubDofHandler(dh, OrderedSet((1,3))) - add!(sdh_tri, :u, Lagrange{RefTriangle,1}()^2) + sdh_tri = SubDofHandler(dh, OrderedSet((1, 3))) + add!(sdh_tri, :u, Lagrange{RefTriangle, 1}()^2) sdh_quad = SubDofHandler(dh, Set(2)) - add!(sdh_quad, :u, Lagrange{RefQuadrilateral,1}()^2) + add!(sdh_quad, :u, Lagrange{RefQuadrilateral, 1}()^2) # Dofs are first created for cell 1 and 3, thereafter cell 2 close!(dh) @test ndofs(dh) == 12 @test celldofs(dh, 1) == collect(1:6) @test celldofs(dh, 2) == [3, 4, 7, 8, 11, 12, 5, 6] - @test celldofs(dh, 3) == [7,8, 9, 10, 11, 12] + @test celldofs(dh, 3) == [7, 8, 9, 10, 11, 12] + return end function test_field_on_subdomain() @@ -405,8 +419,8 @@ function test_field_on_subdomain() # assume two fields: a scalar field :s and a vector field :v # :v lives on both cells, :s lives only on the triangle - ip_tri = Lagrange{RefTriangle,1}() - ip_quad = Lagrange{RefQuadrilateral,1}() + ip_tri = Lagrange{RefTriangle, 1}() + ip_quad = Lagrange{RefQuadrilateral, 1}() sdh_quad = SubDofHandler(dh, Set(1)) add!(sdh_quad, :v, ip_quad^2) @@ -418,13 +432,14 @@ function test_field_on_subdomain() # retrieve field dimensions @test Ferrite.n_components(dh, :v) == 2 - @test Ferrite.n_components(dh, :s) ==1 + @test Ferrite.n_components(dh, :s) == 1 # find field in SubDofHandler @test Ferrite.find_field(dh.subdofhandlers[1], :v) == 1 @test Ferrite.find_field(dh.subdofhandlers[2], :v) == 1 @test Ferrite.find_field(dh.subdofhandlers[2], :s) == 2 @test_throws ErrorException Ferrite.find_field(dh.subdofhandlers[1], :s) + return end function test_evaluate_at_grid_nodes() @@ -437,21 +452,25 @@ function test_evaluate_at_grid_nodes() # | | # 1_______2 - nodes = [Node((0.0, 0.0)), - Node((1.0, 0.0)), - Node((0.0, 1.0)), - Node((1.0, 1.0)), - Node((0.0, 2.0)), - Node((1.0, 2.0))] - cells = Ferrite.AbstractCell[Quadrilateral((1,2,4,3)), - Triangle((3,4,6)), - Triangle((3,6,5))] + nodes = [ + Node((0.0, 0.0)), + Node((1.0, 0.0)), + Node((0.0, 1.0)), + Node((1.0, 1.0)), + Node((0.0, 2.0)), + Node((1.0, 2.0)), + ] + cells = Ferrite.AbstractCell[ + Quadrilateral((1, 2, 4, 3)), + Triangle((3, 4, 6)), + Triangle((3, 6, 5)), + ] mesh = Grid(cells, nodes) addcellset!(mesh, "quads", Set((1,))) addcellset!(mesh, "tris", OrderedSet((2, 3))) - ip_quad = Lagrange{RefQuadrilateral,1}() - ip_tri = Lagrange{RefTriangle,1}() + ip_quad = Lagrange{RefQuadrilateral, 1}() + ip_tri = Lagrange{RefTriangle, 1}() dh = DofHandler(mesh) sdh_tri = SubDofHandler(dh, getcellset(mesh, "tris")) @@ -461,47 +480,53 @@ function test_evaluate_at_grid_nodes() add!(sdh_quad, :s, ip_quad) # scalar field :s only on quad close!(dh) - u = collect(1.:16.) + u = collect(1.0:16.0) uv = @view u[1:end] # :s on thesolution s_nodes = evaluate_at_grid_nodes(dh, u, :s) - @test s_nodes[1:4] ≈ [13., 14., 16., 15.] + @test s_nodes[1:4] ≈ [13.0, 14.0, 16.0, 15.0] @test all(isnan.(s_nodes[5:6])) # :s on a view into solution sv_nodes = evaluate_at_grid_nodes(dh, uv, :s) - @test sv_nodes[1:4] ≈ [13., 14., 16., 15.] + @test sv_nodes[1:4] ≈ [13.0, 14.0, 16.0, 15.0] @test all(isnan.(sv_nodes[5:6])) # :v on the solution - v_nodes = evaluate_at_grid_nodes(dh, u, :v) - @test v_nodes ≈ hcat( [9., 10., 0.], - [11., 12., 0.], - [1., 2., 0.], - [3., 4., 0.], - [7., 8., 0.], - [5., 6., 0.]) + v_nodes = evaluate_at_grid_nodes(dh, u, :v) + @test v_nodes ≈ hcat( + [9.0, 10.0, 0.0], + [11.0, 12.0, 0.0], + [1.0, 2.0, 0.0], + [3.0, 4.0, 0.0], + [7.0, 8.0, 0.0], + [5.0, 6.0, 0.0] + ) # :v on a view into solution vv_nodes = evaluate_at_grid_nodes(dh, uv, :v) - @test vv_nodes ≈ hcat( [9., 10., 0.], - [11., 12., 0.], - [1., 2., 0.], - [3., 4., 0.], - [7., 8., 0.], - [5., 6., 0.]) + @test vv_nodes ≈ hcat( + [9.0, 10.0, 0.0], + [11.0, 12.0, 0.0], + [1.0, 2.0, 0.0], + [3.0, 4.0, 0.0], + [7.0, 8.0, 0.0], + [5.0, 6.0, 0.0] + ) + return end function test_mixed_grid_show() grid = get_2d_grid() str = sprint(show, MIME("text/plain"), grid) @test occursin("2 Quadrilateral/Triangle cells", str) + return end # regression tests for https://github.com/KristofferC/JuAFEM.jl/issues/315 function test_subparametric_quad() #linear geometry - grid = generate_grid(Quadrilateral, (1,1)) - ip = Lagrange{RefQuadrilateral,2}() + grid = generate_grid(Quadrilateral, (1, 1)) + ip = Lagrange{RefQuadrilateral, 2}() dh = DofHandler(grid) add!(dh, :u, ip^2) @@ -512,15 +537,16 @@ function test_subparametric_quad() add!(ch, dbc1) close!(ch) update!(ch, 1.0) - @test getnbasefunctions(Ferrite.getfieldinterpolation(dh.subdofhandlers[1],1)) == 18 # algebraic nbasefunctions + @test getnbasefunctions(Ferrite.getfieldinterpolation(dh.subdofhandlers[1], 1)) == 18 # algebraic nbasefunctions @test celldofs(dh, 1) == [i for i in 1:18] + return end function test_subparametric_triangle() #linear geometry - grid = generate_grid(Triangle, (1,1)) + grid = generate_grid(Triangle, (1, 1)) - ip = Lagrange{RefTriangle,2}() + ip = Lagrange{RefTriangle, 2}() dh = DofHandler(grid) add!(dh, :u, ip^2) @@ -531,15 +557,16 @@ function test_subparametric_triangle() add!(ch, dbc1) close!(ch) update!(ch, 1.0) - @test getnbasefunctions(Ferrite.getfieldinterpolation(dh.subdofhandlers[1],1)) == 12 # algebraic nbasefunctions + @test getnbasefunctions(Ferrite.getfieldinterpolation(dh.subdofhandlers[1], 1)) == 12 # algebraic nbasefunctions @test celldofs(dh, 1) == [i for i in 1:12] + return end function test_celliterator_subdomain() for celltype in (Line, Quadrilateral, Hexahedron) ip = Ferrite.geometric_interpolation(celltype) dim = Ferrite.getrefdim(ip) - grid = generate_grid(celltype, ntuple(i->i==1 ? 2 : 1, dim)) # 2 cells + grid = generate_grid(celltype, ntuple(i -> i == 1 ? 2 : 1, dim)) # 2 cells dh = DofHandler(grid) sdh = SubDofHandler(dh, Set(2)) # only cell 2, cell 1 is not part of dh add!(sdh, :u, ip) @@ -549,6 +576,7 @@ function test_celliterator_subdomain() reinit!(ci.cc, 2) @test celldofs(ci.cc) == collect(1:length(ci.cc.dofs)) end + return end function test_separate_fields_on_separate_domains() @@ -560,21 +588,25 @@ function test_separate_fields_on_separate_domains() # | | # 1_______2 # Given: a vector field :q defined on the quad and a scalar field :t defined on the triangles - nodes = [Node((0.0, 0.0)), - Node((1.0, 0.0)), - Node((0.0, 1.0)), - Node((1.0, 1.0)), - Node((0.0, 2.0)), - Node((1.0, 2.0))] - cells = Ferrite.AbstractCell[Quadrilateral((1,2,4,3)), - Triangle((3,4,5)), - Triangle((4,6,5))] + nodes = [ + Node((0.0, 0.0)), + Node((1.0, 0.0)), + Node((0.0, 1.0)), + Node((1.0, 1.0)), + Node((0.0, 2.0)), + Node((1.0, 2.0)), + ] + cells = Ferrite.AbstractCell[ + Quadrilateral((1, 2, 4, 3)), + Triangle((3, 4, 5)), + Triangle((4, 6, 5)), + ] mesh = Grid(cells, nodes) addcellset!(mesh, "quads", Set((1,))) addcellset!(mesh, "tris", OrderedSet((2, 3))) - ip_tri = Lagrange{RefTriangle,1}() - ip_quad = Lagrange{RefQuadrilateral,1}() + ip_tri = Lagrange{RefTriangle, 1}() + ip_quad = Lagrange{RefQuadrilateral, 1}() dh = DofHandler(mesh) sdh_quad = SubDofHandler(dh, getcellset(mesh, "quads")) @@ -589,7 +621,7 @@ function test_separate_fields_on_separate_domains() @test celldofs(dh, 1) == [i for i in 1:8] @test celldofs(dh, 2) == [9, 10, 11] @test celldofs(dh, 3) == [10, 12, 11] - + return end function test_unique_cellsets() @@ -597,27 +629,29 @@ function test_unique_cellsets() set_u = OrderedSet(1:2) set_v = OrderedSet(1:1) - ip = Lagrange{RefQuadrilateral,1}() + ip = Lagrange{RefQuadrilateral, 1}() # bug dh = DofHandler(grid) sdh_u = SubDofHandler(dh, set_u) @test_throws ErrorException SubDofHandler(dh, set_v) + return end function test_show() # single SubDofHandler - grid = generate_grid(Triangle, (1,1)) + grid = generate_grid(Triangle, (1, 1)) dh = DofHandler(grid) add!(dh, :u, Lagrange{RefTriangle, 1}()^2) close!(dh) @test repr("text/plain", dh) == string( repr("text/plain", typeof(dh)), "\n Fields:\n :u, ", - repr("text/plain", dh.subdofhandlers[1].field_interpolations[1]), "\n Dofs per cell: 6\n Total dofs: 8") + repr("text/plain", dh.subdofhandlers[1].field_interpolations[1]), "\n Dofs per cell: 6\n Total dofs: 8" + ) # multiple SubDofHandlers grid = get_2d_grid() - dh = DofHandler(grid); + dh = DofHandler(grid) sdh_quad = SubDofHandler(dh, Set(1)) add!(sdh_quad, :u, Lagrange{RefQuadrilateral, 1}()^2) sdh_tri = SubDofHandler(dh, Set(2)) @@ -626,20 +660,25 @@ function test_show() @test repr("text/plain", dh) == repr(typeof(dh)) * "\n Fields:\n :u, Vec{2}\n Total dofs: 10" @test repr("text/plain", dh.subdofhandlers[1]) == string( repr("text/plain", typeof(dh.subdofhandlers[1])), "\n Cell type: Quadrilateral\n Fields:\n :u, ", - repr("text/plain", dh.subdofhandlers[1].field_interpolations[1]), "\n Dofs per cell: 8\n") + repr("text/plain", dh.subdofhandlers[1].field_interpolations[1]), "\n Dofs per cell: 8\n" + ) + return end function test_vtk_export() - nodes = Node.([Vec(0.0, 0.0), - Vec(1.0, 0.0), - Vec(1.0, 1.0), - Vec(0.0, 1.0), - Vec(2.0, 0.0), - ]) + nodes = Node.( + [ + Vec(0.0, 0.0), + Vec(1.0, 0.0), + Vec(1.0, 1.0), + Vec(0.0, 1.0), + Vec(2.0, 0.0), + ] + ) cells = [ Quadrilateral((1, 2, 3, 4)), - Triangle((3, 2, 5)) - ] + Triangle((3, 2, 5)), + ] grid = Grid(cells, nodes) ip_tri = Lagrange{RefTriangle, 1}() ip_quad = Lagrange{RefQuadrilateral, 1}() @@ -654,17 +693,17 @@ function test_vtk_export() VTKGridFile(filename, dh) do vtk write_solution(vtk, dh, u) end - sha = bytes2hex(open(SHA.sha1, filename*".vtu")) + sha = bytes2hex(open(SHA.sha1, filename * ".vtu")) @test sha == "339ab8a8a613c2f38af684cccd695ae816671607" - rm(filename*".vtu") # clean up + return rm(filename * ".vtu") # clean up end function test_celliterator_on_true_subdomain_smoketest() - grid = generate_grid(Hexahedron, (2,2,2)) + grid = generate_grid(Hexahedron, (2, 2, 2)) dh = DofHandler(grid) - sdh = SubDofHandler(dh, [1,2,3]) - ip = Lagrange{RefHexahedron,1}() + sdh = SubDofHandler(dh, [1, 2, 3]) + ip = Lagrange{RefHexahedron, 1}() add!(sdh, :u, ip) close!(dh) @@ -673,7 +712,7 @@ function test_celliterator_on_true_subdomain_smoketest() for cell in CellIterator(sdh) end - for cell in CellIterator(dh, [1,2,3]) + for cell in CellIterator(dh, [1, 2, 3]) end for cell in CellIterator(dh) @@ -683,31 +722,32 @@ function test_celliterator_on_true_subdomain_smoketest() @test length(celldofs(cell)) == 0 end end + return end @testset "DofHandler" begin - test_1d_bar_beam(); - test_2d_scalar(); - test_2d_error(); - test_2d_vector(); - test_2d_mixed_1_el(); - test_2d_mixed_2_el(); - test_face_dofs_2_tri(); - test_face_dofs_quad_tri(); - test_3d_tetrahedrons(); - test_serendipity_quad_tri(); - test_2d_mixed_field_triangles(); - test_2d_mixed_field_mixed_celltypes(); - test_3d_mixed_field_mixed_celltypes(); - test_2_element_heat_eq(); - test_element_order(); - test_field_on_subdomain(); - test_mixed_grid_show(); - test_subparametric_quad(); - test_subparametric_triangle(); + test_1d_bar_beam() + test_2d_scalar() + test_2d_error() + test_2d_vector() + test_2d_mixed_1_el() + test_2d_mixed_2_el() + test_face_dofs_2_tri() + test_face_dofs_quad_tri() + test_3d_tetrahedrons() + test_serendipity_quad_tri() + test_2d_mixed_field_triangles() + test_2d_mixed_field_mixed_celltypes() + test_3d_mixed_field_mixed_celltypes() + test_2_element_heat_eq() + test_element_order() + test_field_on_subdomain() + test_mixed_grid_show() + test_subparametric_quad() + test_subparametric_triangle() # test_evaluate_at_grid_nodes() test_mixed_grid_show() - test_separate_fields_on_separate_domains(); + test_separate_fields_on_separate_domains() test_unique_cellsets() test_celliterator_subdomain() test_show() diff --git a/test/test_pointevaluation.jl b/test/test_pointevaluation.jl index 2309db4666..f0570c8dd0 100644 --- a/test/test_pointevaluation.jl +++ b/test/test_pointevaluation.jl @@ -3,15 +3,15 @@ using Ferrite, Test function test_pe_scalar_field() # isoparametric approximation mesh = generate_grid(QuadraticQuadrilateral, (3, 3)) - perturbate_standard_grid!(mesh, 1/10) + perturbate_standard_grid!(mesh, 1 / 10) - f(x) = x[1]+x[2] + f(x) = x[1] + x[2] - ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation - ip_g = Lagrange{RefQuadrilateral,2}() # geometry interpolation + ip_f = Lagrange{RefQuadrilateral, 2}() # function interpolation + ip_g = Lagrange{RefQuadrilateral, 2}() # geometry interpolation # points where we want to retrieve field values - points = Vec{2,Float64}[] + points = Vec{2, Float64}[] # compute values in quadrature points qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate field @@ -32,7 +32,7 @@ function test_pe_scalar_field() projector_vals = project(projector, qp_vals, qr) # set up PointEvalHandler and retrieve values - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) @@ -42,20 +42,21 @@ function test_pe_scalar_field() # alternatively retrieve vals from nodal values TODO: make this work? # vals = evaluate_at_points(ph, nodal_vals) # @test f.(points) ≈ vals + return end function test_pe_embedded() mesh = generate_grid(QuadraticQuadrilateral, (3, 3)) - perturbate_standard_grid!(mesh, 1/10) - mesh = Grid(mesh.cells, map(x->Node(Vec((x.x[1], x.x[2], x.x[1]+x.x[2]))), mesh.nodes)) + perturbate_standard_grid!(mesh, 1 / 10) + mesh = Grid(mesh.cells, map(x -> Node(Vec((x.x[1], x.x[2], x.x[1] + x.x[2]))), mesh.nodes)) - f(x) = x[1]+x[2] + f(x) = x[1] + x[2] - ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation - ip_g = Lagrange{RefQuadrilateral,2}()^3 # geometry interpolation + ip_f = Lagrange{RefQuadrilateral, 2}() # function interpolation + ip_g = Lagrange{RefQuadrilateral, 2}()^3 # geometry interpolation # points where we want to retrieve field values - points = Vec{3,Float64}[] + points = Vec{3, Float64}[] # compute values in quadrature points qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate quadratic field @@ -77,29 +78,30 @@ function test_pe_embedded() projector_vals = project(projector, qp_vals, qr) # set up PointEvalHandler and retrieve values - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) @test f.(points) ≈ vals + return end function test_pe_vector_field() ## vector field # isoparametric approximation mesh = generate_grid(QuadraticQuadrilateral, (3, 3)) - perturbate_standard_grid!(mesh, 1/10) + perturbate_standard_grid!(mesh, 1 / 10) f(x) = Vec((x[1], x[2])) nodal_vals = [f(p.x) for p in mesh.nodes] - ip_f = Lagrange{RefQuadrilateral,2}()^2 # function interpolation - ip_g = Lagrange{RefQuadrilateral,2}() # geometry interpolation + ip_f = Lagrange{RefQuadrilateral, 2}()^2 # function interpolation + ip_g = Lagrange{RefQuadrilateral, 2}() # geometry interpolation # compute values in quadrature points qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate field cv = CellValues(qr, ip_f, ip_g) - qp_vals = [Vector{Vec{2,Float64}}(undef, getnquadpoints(cv)) for i=1:getncells(mesh)] + qp_vals = [Vector{Vec{2, Float64}}(undef, getnquadpoints(cv)) for i in 1:getncells(mesh)] for cellid in eachindex(mesh.cells) xe = getcoordinates(mesh, cellid) reinit!(cv, xe) @@ -114,10 +116,10 @@ function test_pe_vector_field() # projector_vals = convert(Vector{Float64}, reinterpret(Float64, projector_vals)) # points where we want to retrieve field values - points = [Vec((x, 0.52)) for x in range(0.0; stop=1.0, length=100)] + points = [Vec((x, 0.52)) for x in range(0.0; stop = 1.0, length = 100)] # set up PointEvalHandler and retrieve values - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) @@ -126,19 +128,20 @@ function test_pe_vector_field() # alternatively retrieve vals from nodal values# TODO # vals = evaluate_at_points(ph, nodal_vals) # @test f.(points) ≈ vals + return end function test_pe_superparametric() # superparametric approximation mesh = generate_grid(Quadrilateral, (3, 3)) - perturbate_standard_grid!(mesh, 1/10) + perturbate_standard_grid!(mesh, 1 / 10) f(x) = x - ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation + ip_f = Lagrange{RefQuadrilateral, 2}() # function interpolation # compute values in quadrature points qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate field cv = CellValues(qr, ip_f) - qp_vals = [Vector{Vec{2,Float64}}(undef, getnquadpoints(cv)) for i=1:getncells(mesh)] + qp_vals = [Vector{Vec{2, Float64}}(undef, getnquadpoints(cv)) for i in 1:getncells(mesh)] for cellid in eachindex(mesh.cells) xe = getcoordinates(mesh, cellid) reinit!(cv, xe) @@ -152,29 +155,30 @@ function test_pe_superparametric() projector_vals = project(projector, qp_vals, qr) # points where we want to retrieve field values - points = [Vec((x, 0.52)) for x in range(0.0; stop=1.0, length=100)] + points = [Vec((x, 0.52)) for x in range(0.0; stop = 1.0, length = 100)] # set up PointEvalHandler and retrieve values - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) # can recover a quadratic field by a quadratic approximation @test f.(points) ≈ vals + return end function test_pe_dofhandler() mesh = generate_grid(Quadrilateral, (2, 2)) - perturbate_standard_grid!(mesh, 1/10) - dof_vals = [1., 2., 5., 4., 3., 6., 8., 7., 9.] + perturbate_standard_grid!(mesh, 1 / 10) + dof_vals = [1.0, 2.0, 5.0, 4.0, 3.0, 6.0, 8.0, 7.0, 9.0] points = [node.x for node in mesh.nodes] # same as nodes dh = DofHandler(mesh) - add!(dh, :s, Lagrange{RefQuadrilateral,1}()) # a scalar field + add!(dh, :s, Lagrange{RefQuadrilateral, 1}()) # a scalar field close!(dh) - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, dh, dof_vals, :s) @@ -183,6 +187,7 @@ function test_pe_dofhandler() # TODO # vals = evaluate_at_points(ph, collect(1.0:9.0)) # @test vals ≈ 1.0:9.0 + return end function _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) @@ -204,7 +209,7 @@ function _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) x = spatial_coordinate(csv, qp, getcoordinates(cell)) for i in 1:getnbasefunctions(csv) δui = shape_value(csv, qp, i) - fe[s_dofs[i]] += ( δui * f_s(x) ) * dΩ + fe[s_dofs[i]] += (δui * f_s(x)) * dΩ for j in 1:getnbasefunctions(csv) δuj = shape_value(csv, qp, j) me[s_dofs[i], s_dofs[j]] += δui * δuj * dΩ @@ -212,7 +217,7 @@ function _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) end for i in 1:getnbasefunctions(cvv) δui = shape_value(cvv, qp, i) - fe[v_dofs[i]] += ( δui ⋅ f_v(x) ) * dΩ + fe[v_dofs[i]] += (δui ⋅ f_v(x)) * dΩ for j in 1:getnbasefunctions(cvv) δuj = shape_value(cvv, qp, j) me[v_dofs[i], v_dofs[j]] += δui ⋅ δuj * dΩ @@ -225,32 +230,32 @@ function _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) end -function test_pe_dofhandler2(;three_dimensional=true) +function test_pe_dofhandler2(; three_dimensional = true) # Computes the L2 projection of a quadratic field exactly # but not using L2Projector since we want the DofHandler dofs if (three_dimensional) mesh = generate_grid(Hexahedron, (3, 3, 3)) - perturbate_standard_grid!(mesh, 1/10) + perturbate_standard_grid!(mesh, 1 / 10) f_s = x -> 1.0 + x[1] + x[2] + x[1] * x[2] + x[2] * x[3] - f_v = x -> Vec{3}((1.0 + x[1] + x[2] + x[1] * x[2], 2.0 - x[1] - x[2] - x[1] * x[2], 4.0 + x[1] - x[2] + x[3] - x[1] * x[3] - x[2] * x[3])) - points = [Vec((x, x, x)) for x in range(0; stop=1, length=100)] - ip_f = Lagrange{RefHexahedron,2}() + f_v = x -> Vec{3}((1.0 + x[1] + x[2] + x[1] * x[2], 2.0 - x[1] - x[2] - x[1] * x[2], 4.0 + x[1] - x[2] + x[3] - x[1] * x[3] - x[2] * x[3])) + points = [Vec((x, x, x)) for x in range(0; stop = 1, length = 100)] + ip_f = Lagrange{RefHexahedron, 2}() ip_f_v = ip_f^3 qr = QuadratureRule{RefHexahedron}(3) else mesh = generate_grid(Quadrilateral, (3, 3)) - perturbate_standard_grid!(mesh, 1/10) - f_s = x -> 1.0 + x[1] + x[2] + x[1] * x[2] + perturbate_standard_grid!(mesh, 1 / 10) + f_s = x -> 1.0 + x[1] + x[2] + x[1] * x[2] f_v = x -> Vec{2}((1.0 + x[1] + x[2] + x[1] * x[2], 2.0 - x[1] - x[2] - x[1] * x[2])) - points = [Vec((x, x, )) for x in range(0; stop=1, length=100)] - ip_f = Lagrange{RefQuadrilateral,2}() + points = [Vec((x, x)) for x in range(0; stop = 1, length = 100)] + ip_f = Lagrange{RefQuadrilateral, 2}() ip_f_v = ip_f^2 qr = QuadratureRule{RefQuadrilateral}(3) end csv = CellValues(qr, ip_f) cvv = CellValues(qr, ip_f_v) - dh = DofHandler(mesh); + dh = DofHandler(mesh) add!(dh, :s, ip_f) add!(dh, :v, ip_f_v) close!(dh) @@ -259,7 +264,7 @@ function test_pe_dofhandler2(;three_dimensional=true) v_dofs = dof_range(dh, :v) uh = _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) psv = PointValues(ip_f) @@ -269,23 +274,24 @@ function test_pe_dofhandler2(;three_dimensional=true) # Test scalar field reinit!(psv, point) @test function_value(psv, uh[celldofs(dh, cellid(point))], s_dofs) ≈ - function_value(psv, uh[celldofs(dh, cellid(point))][s_dofs]) ≈ - f_s(x) + function_value(psv, uh[celldofs(dh, cellid(point))][s_dofs]) ≈ + f_s(x) @test function_gradient(psv, uh[celldofs(dh, cellid(point))], s_dofs) ≈ - function_gradient(psv, uh[celldofs(dh, cellid(point))][s_dofs]) ≈ - Tensors.gradient(f_s, x) + function_gradient(psv, uh[celldofs(dh, cellid(point))][s_dofs]) ≈ + Tensors.gradient(f_s, x) # Test vector field reinit!(pvv, point) @test function_value(pvv, uh[celldofs(dh, cellid(point))], v_dofs) ≈ - function_value(pvv, uh[celldofs(dh, cellid(point))][v_dofs]) ≈ - f_v(x) + function_value(pvv, uh[celldofs(dh, cellid(point))][v_dofs]) ≈ + f_v(x) @test function_gradient(pvv, uh[celldofs(dh, cellid(point))], v_dofs) ≈ - function_gradient(pvv, uh[celldofs(dh, cellid(point))][v_dofs]) ≈ - Tensors.gradient(f_v, x) + function_gradient(pvv, uh[celldofs(dh, cellid(point))][v_dofs]) ≈ + Tensors.gradient(f_v, x) @test function_symmetric_gradient(pvv, uh[celldofs(dh, cellid(point))], v_dofs) ≈ - function_symmetric_gradient(pvv, uh[celldofs(dh, cellid(point))][v_dofs]) ≈ - symmetric(Tensors.gradient(f_v, x)) + function_symmetric_gradient(pvv, uh[celldofs(dh, cellid(point))][v_dofs]) ≈ + symmetric(Tensors.gradient(f_v, x)) end + return end function test_pe_mixed_grid() @@ -299,23 +305,27 @@ function test_pe_mixed_grid() # | | # 1_______2 - nodes = [Node((0.0, 0.0)), - Node((1.0, 0.0)), - Node((0.0, 1.0)), - Node((1.0, 1.0)), - Node((0.0, 2.0)), - Node((1.0, 2.0))] - - cells = Ferrite.AbstractCell[Quadrilateral((1,2,4,3)), - Triangle((3,4,6)), - Triangle((3,6,5))] + nodes = [ + Node((0.0, 0.0)), + Node((1.0, 0.0)), + Node((0.0, 1.0)), + Node((1.0, 1.0)), + Node((0.0, 2.0)), + Node((1.0, 2.0)), + ] + + cells = Ferrite.AbstractCell[ + Quadrilateral((1, 2, 4, 3)), + Triangle((3, 4, 6)), + Triangle((3, 6, 5)), + ] mesh = Grid(cells, nodes) addcellset!(mesh, "quads", Set{Int}((1,))) addcellset!(mesh, "tris", Set{Int}((2, 3))) - ip_quad = Lagrange{RefQuadrilateral,1}() - ip_tri = Lagrange{RefTriangle,1}() + ip_quad = Lagrange{RefQuadrilateral, 1}() + ip_tri = Lagrange{RefTriangle, 1}() f(x) = x[1] @@ -333,13 +343,13 @@ function test_pe_mixed_grid() end # construct projector - projector = L2Projector(ip_quad, mesh; set=getcellset(mesh, "quads")) + projector = L2Projector(ip_quad, mesh; set = getcellset(mesh, "quads")) - points = [Vec((x, 2x)) for x in range(0.0; stop=1.0, length=10)] + points = [Vec((x, 2x)) for x in range(0.0; stop = 1.0, length = 10)] # first alternative: L2Projection to dofs projector_values = project(projector, qp_vals_quads, qr) - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_values) @@ -354,28 +364,29 @@ function test_pe_mixed_grid() add!(sdh_tri, :v, ip_tri^2) close!(dh) - dof_vals = [1., 1., 2., 2., 4., 4., 3., 3., 6., 6., 5., 5.] + dof_vals = [1.0, 1.0, 2.0, 2.0, 4.0, 4.0, 3.0, 3.0, 6.0, 6.0, 5.0, 5.0] points = [node.x for node in mesh.nodes] - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, dh, dof_vals, :v) - @test vals ≈ [Vec((i, i)) for i=1.0:6.0] + @test vals ≈ [Vec((i, i)) for i in 1.0:6.0] + return end function test_pe_oneD() # isoparametric approximation mesh = generate_grid(Line, (2,)) - perturbate_standard_grid!(mesh, 1/10) + perturbate_standard_grid!(mesh, 1 / 10) f(x) = x[1] nodal_vals = [f(p.x) for p in mesh.nodes] - ip_f = Lagrange{RefLine,1}() # function interpolation + ip_f = Lagrange{RefLine, 1}() # function interpolation # compute values in quadrature points qr = QuadratureRule{RefLine}(2) cv = CellValues(qr, ip_f) - qp_vals = [Vector{Float64}(undef, getnquadpoints(cv)) for i=1:getncells(mesh)] + qp_vals = [Vector{Float64}(undef, getnquadpoints(cv)) for i in 1:getncells(mesh)] for cellid in eachindex(mesh.cells) xe = getcoordinates(mesh, cellid) reinit!(cv, xe) @@ -389,10 +400,10 @@ function test_pe_oneD() projector_values = project(projector, qp_vals, qr) # points where we want to retrieve field values - points = [Vec((x,)) for x in range(-1.0; stop=1.0, length=5)] + points = [Vec((x,)) for x in range(-1.0; stop = 1.0, length = 5)] # set up PointEvalHandler and retrieve values - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_values) @@ -402,16 +413,18 @@ function test_pe_oneD() # TODO # vals = evaluate_at_points(ph, nodal_vals) # @test f.(points) ≈ vals + return end function test_pe_first_point_missing() mesh = generate_grid(Quadrilateral, (1, 1)) points = [Vec(2.0, 0.0), Vec(0.0, 0.0)] - @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points; warn=false) - ph = PointEvalHandler(mesh, points; warn=false) + @test_logs min_level = Logging.Warn PointEvalHandler(mesh, points; warn = false) + ph = PointEvalHandler(mesh, points; warn = false) @test isnothing(ph.local_coords[1]) @test ph.local_coords[2] ≈ Vec(0.0, 0.0) + return end @testset "PointEvalHandler" begin @@ -426,8 +439,8 @@ end @testset "dofhandler interaction" begin test_pe_dofhandler() - test_pe_dofhandler2(;three_dimensional=false) - test_pe_dofhandler2(;three_dimensional=true) + test_pe_dofhandler2(; three_dimensional = false) + test_pe_dofhandler2(; three_dimensional = true) end @testset "superparametric" begin @@ -448,10 +461,10 @@ end end @testset "PointValues" begin - ip_f = Lagrange{RefQuadrilateral,2}() - x = Vec{2,Float64}.([(0.0, 0.0), (2.0, 0.5), (2.5, 2.5), (0.5, 2.0)]) - ξ₁ = Vec{2,Float64}((0.12, -0.34)) - ξ₂ = Vec{2,Float64}((0.56, -0.78)) + ip_f = Lagrange{RefQuadrilateral, 2}() + x = Vec{2, Float64}.([(0.0, 0.0), (2.0, 0.5), (2.5, 2.5), (0.5, 2.0)]) + ξ₁ = Vec{2, Float64}((0.12, -0.34)) + ξ₂ = Vec{2, Float64}((0.56, -0.78)) qr = QuadratureRule{RefQuadrilateral}([2.0, 2.0], [ξ₁, ξ₂]) # PointScalarValues diff --git a/test/test_quadrules.jl b/test/test_quadrules.jl index d6240e58c4..c6bc68ea00 100644 --- a/test/test_quadrules.jl +++ b/test/test_quadrules.jl @@ -15,14 +15,14 @@ using Ferrite: reference_shape_value # Hypercube @testset "Exactness for integration on hypercube of $rulename" for (rulename, orderrange) in [ - (:legendre, 1:4), - (:lobatto, 2:4), - ] - for (dim, shape) = ((1, RefLine), (2, RefQuadrilateral), (3, RefHexahedron)) + (:legendre, 1:4), + (:lobatto, 2:4), + ] + for (dim, shape) in ((1, RefLine), (2, RefQuadrilateral), (3, RefHexahedron)) for order in orderrange f = (x, p) -> sum([x[i]^p for i in 1:length(x)]) qr = QuadratureRule{shape}(rulename, order) - @test integrate(qr, (x) -> f(x, 2*order-1)) < 1e-14 + @test integrate(qr, (x) -> f(x, 2 * order - 1)) < 1.0e-14 @test sum(qr.weights) ≈ ref_square_vol(dim) @test sum(Ferrite.getweights(qr)) ≈ ref_square_vol(dim) end @@ -33,9 +33,9 @@ using Ferrite: reference_shape_value # Triangle # http://www.wolframalpha.com/input/?i=integrate+sqrt(x%2By)+from+x+%3D+0+to+1,+y+%3D+0+to+1-x @testset "Exactness for integration on triangles of $rulename" for (rulename, orderrange) in [ - (:dunavant, 1:8), - (:gaussjacobi, 9:15), - ] + (:dunavant, 1:8), + (:gaussjacobi, 9:15), + ] g = (x) -> sqrt(sum(x)) dim = 2 for order in orderrange @@ -51,10 +51,10 @@ using Ferrite: reference_shape_value # Table 1: # http://www.m-hikari.com/ijma/ijma-2011/ijma-1-4-2011/venkateshIJMA1-4-2011.pdf @testset "Exactness for integration on tetrahedra of $rulename" for (rulename, orderrange) in [ - (:jinyun, 1:3), - (:keast_minimal, 1:5), - (:keast_positive, 1:5) - ] + (:jinyun, 1:3), + (:keast_minimal, 1:5), + (:keast_positive, 1:5), + ] g = (x) -> sqrt(sum(x)) dim = 3 for order in orderrange @@ -69,12 +69,12 @@ using Ferrite: reference_shape_value # Wedge # ∫ √(x₁ + x₂) x₃² @testset "Exactness for integration on prisms of $rulename" for (rulename, orderrange) in [ - (:polyquad, 1:10), - ] - g = (x) -> √(x[1] + x[2])*x[3]^2 + (:polyquad, 1:10), + ] + g = (x) -> √(x[1] + x[2]) * x[3]^2 for order in 1:10 qr = QuadratureRule{RefPrism}(:polyquad, order) - @test integrate(qr, g) - 2/15 < 0.01 + @test integrate(qr, g) - 2 / 15 < 0.01 @test sum(qr.weights) ≈ ref_prism_vol() end end @@ -82,35 +82,36 @@ using Ferrite: reference_shape_value @test_throws ArgumentError QuadratureRule{RefPrism}(0) @testset "Generic quadrature rule properties for $ref_cell" for ref_cell in ( - Line, - Quadrilateral, - Triangle, - Hexahedron, - Tetrahedron, - Wedge, - Pyramid) + Line, + Quadrilateral, + Triangle, + Hexahedron, + Tetrahedron, + Wedge, + Pyramid, + ) refshape = ref_cell.super.parameters[1] dim = refshape.super.parameters[1] dim > 1 && @testset "$refshape face-cell spatial coordinates" begin - grid = generate_grid(ref_cell, ntuple(_->3, dim)) + grid = generate_grid(ref_cell, ntuple(_ -> 3, dim)) for cellid in 1:getncells(grid) cell = grid.cells[cellid] ccoords = getcoordinates(grid, cellid) - Vec_t = Vec{dim,Float64} - Vec_face_t = Vec{dim-1,Float64} + Vec_t = Vec{dim, Float64} + Vec_face_t = Vec{dim - 1, Float64} for lfaceid in Ferrite.nfacets(refshape) facenodes = Ferrite.facets(cell)[lfaceid] fcoords = zeros(Vec_t, length(facenodes)) - for (i,nodeid) in enumerate(facenodes) + for (i, nodeid) in enumerate(facenodes) x = grid.nodes[nodeid].x fcoords[i] = x end - ipcell = Lagrange{refshape,1}() - ipface = Lagrange{getfacerefshape(cell,lfaceid),1}() + ipcell = Lagrange{refshape, 1}() + ipface = Lagrange{getfacerefshape(cell, lfaceid), 1}() - ξface = rand(Vec_face_t)/4 + ξface = rand(Vec_face_t) / 4 ξcell = Ferrite.facet_to_element_transformation(ξface, refshape, lfaceid) xface = zero(Vec_t) @@ -131,14 +132,14 @@ using Ferrite: reference_shape_value @testset "$ref_cell unknown facet error path" begin for face in (-1, 0, 100) err = ArgumentError("unknown facet number") - @test_throws err Ferrite.weighted_normal(Tensor{2,dim}(zeros(dim^2)), refshape, face) - pt = Vec{dim-1, Float64}(ntuple(i -> 0.0, dim-1)) + @test_throws err Ferrite.weighted_normal(Tensor{2, dim}(zeros(dim^2)), refshape, face) + pt = Vec{dim - 1, Float64}(ntuple(i -> 0.0, dim - 1)) @test_throws err Ferrite.facet_to_element_transformation(pt, refshape, face) end end @testset "Type checks for $refshape (T=$T)" for T in (Float32, Float64) - qr = QuadratureRule{refshape}(T, 1) + qr = QuadratureRule{refshape}(T, 1) qrw = Ferrite.getweights(qr) qrp = Ferrite.getpoints(qr) @test qrw isa Vector @@ -156,7 +157,7 @@ using Ferrite: reference_shape_value @test eltype(sqrw) === T @test eltype(eltype(sqrp)) === T - fqr = FacetQuadratureRule{refshape}(T, 1) + fqr = FacetQuadratureRule{refshape}(T, 1) for f in 1:nfacets(refshape) fqrw = Ferrite.getweights(fqr, f) fqrp = Ferrite.getpoints(fqr, f) @@ -176,10 +177,10 @@ using Ferrite: reference_shape_value end sfqr = FacetQuadratureRule( - ntuple(f->sqr_for_facet(fqr, f), nfacets(refshape)) + ntuple(f -> sqr_for_facet(fqr, f), nfacets(refshape)) ) for f in 1:nfacets(refshape) - sfqrw = Ferrite.getweights(sfqr,f) + sfqrw = Ferrite.getweights(sfqr, f) sfqrp = Ferrite.getpoints(sfqr, f) @test sfqrw isa SVector @test sfqrp isa SVector @@ -191,7 +192,7 @@ using Ferrite: reference_shape_value [sqr_for_facet(fqr, f) for f in 1:nfacets(refshape)] ) for f in 1:nfacets(refshape) - sfqrw = Ferrite.getweights(sfqr2,f) + sfqrw = Ferrite.getweights(sfqr2, f) sfqrp = Ferrite.getpoints(sfqr2, f) @test sfqrw isa SVector @test sfqrp isa SVector @@ -203,36 +204,36 @@ using Ferrite: reference_shape_value # Check explicitly if the defaults changed, as this might affect users negatively @testset "Volume defaults for $refshape" for (refshape, sym) in ( - (RefLine, :legendre), - (RefQuadrilateral, :legendre), - (RefHexahedron, :legendre), - (RefTriangle, :dunavant), - (RefTetrahedron, :keast_minimal), - (RefPrism, :polyquad), - (RefPyramid, :polyquad), - ) + (RefLine, :legendre), + (RefQuadrilateral, :legendre), + (RefHexahedron, :legendre), + (RefTriangle, :dunavant), + (RefTetrahedron, :keast_minimal), + (RefPrism, :polyquad), + (RefPyramid, :polyquad), + ) for order in 1:3 - qr = QuadratureRule{refshape}(sym, order) + qr = QuadratureRule{refshape}(sym, order) qr_default = QuadratureRule{refshape}(order) @test Ferrite.getweights(qr) == Ferrite.getweights(qr_default) @test Ferrite.getpoints(qr) == Ferrite.getpoints(qr_default) end end @testset "Facet defaults for $refshape" for (refshape, sym) in ( - # (RefLine, :legendre), # There is no choice for the rule on lines, as it only is a point eval - (RefQuadrilateral, :legendre), - (RefHexahedron, :legendre), - (RefTriangle, :legendre), - (RefTetrahedron, :dunavant), - # (RefPrism, ...), # Not implemented yet (see discussion in #1007) - # (RefPyramid, ...), # Not implement yet (see discussion in #1007) - ) + # (RefLine, :legendre), # There is no choice for the rule on lines, as it only is a point eval + (RefQuadrilateral, :legendre), + (RefHexahedron, :legendre), + (RefTriangle, :legendre), + (RefTetrahedron, :dunavant), + # (RefPrism, ...), # Not implemented yet (see discussion in #1007) + # (RefPyramid, ...), # Not implement yet (see discussion in #1007) + ) for order in 1:3 - fqr = FacetQuadratureRule{refshape}(sym, order) + fqr = FacetQuadratureRule{refshape}(sym, order) fqr_default = FacetQuadratureRule{refshape}(order) for f in 1:nfacets(refshape) - @test Ferrite.getweights(fqr,f) == Ferrite.getweights(fqr_default,f) - @test Ferrite.getpoints(fqr,f) == Ferrite.getpoints(fqr_default,f) + @test Ferrite.getweights(fqr, f) == Ferrite.getweights(fqr_default, f) + @test Ferrite.getpoints(fqr, f) == Ferrite.getpoints(fqr_default, f) end end end diff --git a/test/test_sparsity_patterns.jl b/test/test_sparsity_patterns.jl index d9f063d192..cb4bbd8c24 100644 --- a/test/test_sparsity_patterns.jl +++ b/test/test_sparsity_patterns.jl @@ -27,11 +27,12 @@ Ferrite.eachrow(tp::TestPattern, r::Int) = tp.data[r] function compare_patterns(p1, px...) @test all(p -> Ferrite.getnrows(p1) == Ferrite.getnrows(p), px) @test all(p -> Ferrite.getncols(p1) == Ferrite.getncols(p), px) - for rs in zip(Ferrite.eachrow.((p1, px...,))...) + for rs in zip(Ferrite.eachrow.((p1, px...))...) for cs in zip(rs...) @test all(c -> cs[1] == c, cs) end end + return end # Compare the storage of SparseMatrixCSC @@ -67,16 +68,16 @@ end # Error paths @test_throws BoundsError Ferrite.add_entry!(dsp, 0, 1) @test_throws BoundsError Ferrite.add_entry!(dsp, 1, 0) - @test_throws BoundsError Ferrite.add_entry!(dsp, m+1, 1) - @test_throws BoundsError Ferrite.add_entry!(dsp, 1, n+1) + @test_throws BoundsError Ferrite.add_entry!(dsp, m + 1, 1) + @test_throws BoundsError Ferrite.add_entry!(dsp, 1, n + 1) end function testdhch() local grid, dh, ch grid = generate_grid(Quadrilateral, (2, 1)) dh = DofHandler(grid) - add!(dh, :v, Lagrange{RefQuadrilateral,1}()^2) - add!(dh, :s, Lagrange{RefQuadrilateral,1}()) + add!(dh, :v, Lagrange{RefQuadrilateral, 1}()^2) + add!(dh, :s, Lagrange{RefQuadrilateral, 1}()) close!(dh) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:v, getfacetset(grid, "left"), (x, t) -> 0, [2])) @@ -225,7 +226,7 @@ end add!(dh, :p, Lagrange{RefHexahedron, 1}()) close!(dh) ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:p, union(getfacetset.(Ref(grid), ("left", "right", "top", "bottom", "front", "back"),)...), x -> 0)) + add!(ch, Dirichlet(:p, union(getfacetset.(Ref(grid), ("left", "right", "top", "bottom", "front", "back"))...), x -> 0)) add!(ch, PeriodicDirichlet(:u, collect_periodic_facets(grid))) close!(ch) @@ -270,7 +271,7 @@ end # Ignore constrained dofs ps = make_patterns(dh) for p in ps - add_sparsity_entries!(p, dh, ch; keep_constrained=false) + add_sparsity_entries!(p, dh, ch; keep_constrained = false) # Test that prescribed dofs only have diagonal entry for row in ch.prescribed_dofs r = Ferrite.eachrow(p, row) @@ -319,14 +320,14 @@ end BlockSparsityPattern([ndofs(dh) ÷ 2, ndofs(dh) - ndofs(dh) ÷ 2]), ) for p in patterns - @test_throws ErrorException add_sparsity_entries!(p, dh; keep_constrained=false) + @test_throws ErrorException add_sparsity_entries!(p, dh; keep_constrained = false) end ch_open = ConstraintHandler(dh) for p in patterns - @test_throws ErrorException add_sparsity_entries!(p, dh, ch_open; keep_constrained=false) + @test_throws ErrorException add_sparsity_entries!(p, dh, ch_open; keep_constrained = false) end ch_bad = ConstraintHandler(close!(DofHandler(grid))) for p in patterns - @test_throws ErrorException add_sparsity_entries!(p, dh, ch_bad; keep_constrained=false) + @test_throws ErrorException add_sparsity_entries!(p, dh, ch_bad; keep_constrained = false) end end diff --git a/test/test_utils.jl b/test/test_utils.jl index 512f7fc265..7f2696ffd8 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -7,22 +7,24 @@ using Ferrite: reference_shape_value ##################################### reference_volume(::Interpolation{Ferrite.RefHypercube{dim}}) where {dim} = 2^dim reference_volume(::Interpolation{Ferrite.RefSimplex{dim}}) where {dim} = 1 / factorial(dim) -reference_volume(::Interpolation{RefPrism}) = 1/2 -reference_volume(::Interpolation{RefPyramid}) = 1/3 +reference_volume(::Interpolation{RefPrism}) = 1 / 2 +reference_volume(::Interpolation{RefPyramid}) = 1 / 3 # For faces reference_face_area(fs::VectorizedInterpolation, f::Int) = reference_face_area(fs.ip, f) -reference_face_area(fs::Interpolation{Ferrite.RefHypercube{dim}}, face::Int) where {dim} = 2^(dim-1) +reference_face_area(fs::Interpolation{Ferrite.RefHypercube{dim}}, face::Int) where {dim} = 2^(dim - 1) reference_face_area(fs::Interpolation{RefTriangle}, face::Int) = face == 1 ? sqrt(2) : 1.0 reference_face_area(fs::Interpolation{RefTetrahedron}, face::Int) = face == 3 ? sqrt(2 * 1.5) / 2.0 : 0.5 function reference_face_area(fs::Interpolation{RefPrism}, face::Int) face == 4 && return √2 - face ∈ [1,5] && return 0.5 - face ∈ [2,3] && return 1.0 + face ∈ [1, 5] && return 0.5 + face ∈ [2, 3] && return 1.0 + error("Invalid face index") end function reference_face_area(fs::Interpolation{RefPyramid}, face::Int) face == 1 && return 1.0 - face ∈ [2,3] && return 0.5 - face ∈ [4,5] && return sqrt(2)/2 + face ∈ [2, 3] && return 0.5 + face ∈ [4, 5] && return sqrt(2) / 2 + error("Invalid face index") end ###################################################### @@ -33,59 +35,73 @@ reference_normals(ip::VectorizedInterpolation) = reference_normals(ip.ip) # Lagrange{1, RefLine} function reference_normals(::Lagrange{RefLine}) - return [Vec{1, Float64}((-1.0,)), - Vec{1, Float64}(( 1.0,))] + return [ + Vec{1, Float64}((-1.0,)), + Vec{1, Float64}((1.0,)), + ] end # Lagrange{2, RefQuadrilateral} function reference_normals(::Lagrange{RefQuadrilateral}) - return [Vec{2, Float64}(( 0.0, -1.0)), - Vec{2, Float64}(( 1.0, 0.0)), - Vec{2, Float64}(( 0.0, 1.0,)), - Vec{2, Float64}((-1.0, 0.0,))] + return [ + Vec{2, Float64}((0.0, -1.0)), + Vec{2, Float64}((1.0, 0.0)), + Vec{2, Float64}((0.0, 1.0)), + Vec{2, Float64}((-1.0, 0.0)), + ] end # Lagrange{2, RefTriangle} function reference_normals(::Lagrange{RefTriangle}) - return [Vec{2, Float64}((1/√2, 1/√2)), - Vec{2, Float64}((-1.0, 0.0)), - Vec{2, Float64}((0.0, -1.0))] + return [ + Vec{2, Float64}((1 / √2, 1 / √2)), + Vec{2, Float64}((-1.0, 0.0)), + Vec{2, Float64}((0.0, -1.0)), + ] end # Lagrange{3, RefTetrahedron} function reference_normals(::Lagrange{RefTetrahedron}) - return [Vec{3, Float64}((0.0, 0.0, -1.0)), - Vec{3, Float64}((0.0 ,-1.0, 0.0)), - Vec{3, Float64}((1/√3, 1/√3, 1/√3)), - Vec{3, Float64}((-1.0, 0.0, 0.0))] + return [ + Vec{3, Float64}((0.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 0.0)), + Vec{3, Float64}((1 / √3, 1 / √3, 1 / √3)), + Vec{3, Float64}((-1.0, 0.0, 0.0)), + ] end # Lagrange{3, Cube} function reference_normals(::Lagrange{RefHexahedron}) - return [Vec{3, Float64}(( 0.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, -1.0, 0.0)), - Vec{3, Float64}(( 1.0, 0.0, 0.0)), - Vec{3, Float64}(( 0.0, 1.0, 0.0)), - Vec{3, Float64}((-1.0, 0.0, 0.0)), - Vec{3, Float64}(( 0.0, 0.0, 1.0))] + return [ + Vec{3, Float64}((0.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 0.0)), + Vec{3, Float64}((1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 1.0, 0.0)), + Vec{3, Float64}((-1.0, 0.0, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + ] end # Lagrange{3, Wedge} function reference_normals(::Lagrange{RefPrism}) - return [Vec{3, Float64}(( 0.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, -1.0, 0.0)), - Vec{3, Float64}((-1.0, 0.0, 0.0)), - Vec{3, Float64}((1/√2, 1/√2, 0.0)), - Vec{3, Float64}(( 0.0, 0.0, 1.0))] + return [ + Vec{3, Float64}((0.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 0.0)), + Vec{3, Float64}((-1.0, 0.0, 0.0)), + Vec{3, Float64}((1 / √2, 1 / √2, 0.0)), + Vec{3, Float64}((0.0, 0.0, 1.0)), + ] end # Lagrange{3, RefPyramid} function reference_normals(::Lagrange{RefPyramid}) - return [Vec{3, Float64}(( 0.0, 0.0, -1.0)), - Vec{3, Float64}(( 0.0, -1.0, 0.0)), - Vec{3, Float64}((-1.0, 0.0, 0.0)), - Vec{3, Float64}((1/√2, 0.0, 1/√2)), - Vec{3, Float64}((0.0, 1/√2, 1/√2)),] + return [ + Vec{3, Float64}((0.0, 0.0, -1.0)), + Vec{3, Float64}((0.0, -1.0, 0.0)), + Vec{3, Float64}((-1.0, 0.0, 0.0)), + Vec{3, Float64}((1 / √2, 0.0, 1 / √2)), + Vec{3, Float64}((0.0, 1 / √2, 1 / √2)), + ] end # Serendipity{2, RefQuadrilateral} @@ -95,17 +111,17 @@ reference_normals(::Serendipity{RefQuadrilateral, 2}) = reference_normals(Lagran # Valid coordinates by expanding # # and rotating reference shape # ################################## -function rotmat(dim, θ=π/6) +function rotmat(dim, θ = π / 6) if dim == 1 - R = Tensor{2,1}((cos(θ),)) + R = Tensor{2, 1}((cos(θ),)) return R elseif dim == 2 - R = Tensor{2,2}((cos(θ), sin(θ), -sin(θ), cos(θ))) + R = Tensor{2, 2}((cos(θ), sin(θ), -sin(θ), cos(θ))) return R else u = Vec{3}((1.0, 2.0, 3.0)); u /= norm(u) - ux = Tensor{2,3}((0.0, u[3], -u[2], -u[3], 0.0, u[1], u[2], -u[1], 0.0)) - R = cos(θ)*one(Tensor{2,3}) + sin(θ)*ux + (1-cos(θ))*u⊗u + ux = Tensor{2, 3}((0.0, u[3], -u[2], -u[3], 0.0, u[1], u[2], -u[1], 0.0)) + R = cos(θ) * one(Tensor{2, 3}) + sin(θ) * ux + (1 - cos(θ)) * u ⊗ u return R end end @@ -114,7 +130,7 @@ function valid_coordinates_and_normals(fs::Interpolation{shape, order}) where {d x = Ferrite.reference_coordinates(fs) n = reference_normals(fs) R = rotmat(dim) - return [2.0 * (R ⋅ x[i]) for i in 1:length(x)] , [(R ⋅ n[i]) / norm((R ⋅ n[i])) for i in 1:length(n)] + return [2.0 * (R ⋅ x[i]) for i in 1:length(x)], [(R ⋅ n[i]) / norm((R ⋅ n[i])) for i in 1:length(n)] end ####################################### @@ -129,25 +145,25 @@ function calculate_volume(::Lagrange{RefLine, 1}, x::Vector{Vec{dim, T}}) where end function calculate_volume(::Lagrange{RefLine, 2}, x::Vector{Vec{dim, T}}) where {T, dim} - vol = norm(x[3] - x[1]) + norm(x[2]-x[3]) + vol = norm(x[3] - x[1]) + norm(x[2] - x[3]) return vol end function calculate_volume(::Lagrange{RefQuadrilateral, 1}, x::Vector{Vec{dim, T}}) where {T, dim} vol = norm((x[4] - x[1]) × (x[2] - x[1])) * 0.5 + - norm((x[4] - x[3]) × (x[2] - x[3])) * 0.5 + norm((x[4] - x[3]) × (x[2] - x[3])) * 0.5 return vol end function calculate_volume(::Lagrange{RefQuadrilateral, 2}, x::Vector{Vec{dim, T}}) where {T, dim} vol = norm((x[8] - x[1]) × (x[5] - x[1])) * 0.5 + - norm((x[8] - x[9]) × (x[5] - x[9])) * 0.5 + - norm((x[5] - x[2]) × (x[6] - x[2])) * 0.5 + - norm((x[5] - x[9]) × (x[6] - x[9])) * 0.5 + - norm((x[6] - x[3]) × (x[7] - x[3])) * 0.5 + - norm((x[6] - x[9]) × (x[7] - x[9])) * 0.5 + - norm((x[7] - x[4]) × (x[8] - x[4])) * 0.5 + - norm((x[7] - x[9]) × (x[8] - x[9])) * 0.5 + norm((x[8] - x[9]) × (x[5] - x[9])) * 0.5 + + norm((x[5] - x[2]) × (x[6] - x[2])) * 0.5 + + norm((x[5] - x[9]) × (x[6] - x[9])) * 0.5 + + norm((x[6] - x[3]) × (x[7] - x[3])) * 0.5 + + norm((x[6] - x[9]) × (x[7] - x[9])) * 0.5 + + norm((x[7] - x[4]) × (x[8] - x[4])) * 0.5 + + norm((x[7] - x[9]) × (x[8] - x[9])) * 0.5 return vol end @@ -158,9 +174,9 @@ end function calculate_volume(::Lagrange{RefTriangle, 2}, x::Vector{Vec{dim, T}}) where {T, dim} vol = norm((x[6] - x[3]) × (x[5] - x[3])) * 0.5 + - norm((x[6] - x[4]) × (x[5] - x[4])) * 0.5 + - norm((x[1] - x[6]) × (x[4] - x[6])) * 0.5 + - norm((x[4] - x[5]) × (x[2] - x[5])) * 0.5 + norm((x[6] - x[4]) × (x[5] - x[4])) * 0.5 + + norm((x[1] - x[6]) × (x[4] - x[6])) * 0.5 + + norm((x[4] - x[5]) × (x[2] - x[5])) * 0.5 return vol end @@ -175,12 +191,12 @@ function calculate_volume(::Lagrange{RefTetrahedron, order}, x::Vector{Vec{3, T} return vol end -function calculate_volume(::Lagrange{RefHexahedron, 1}, x::Vector{Vec{3, T}}) where T +function calculate_volume(::Lagrange{RefHexahedron, 1}, x::Vector{Vec{3, T}}) where {T} vol = norm((x[1] - x[5]) ⋅ ((x[2] - x[5]) × (x[4] - x[5]))) / 6.0 + - norm((x[2] - x[7]) ⋅ ((x[3] - x[7]) × (x[4] - x[7]))) / 6.0 + - norm((x[2] - x[7]) ⋅ ((x[4] - x[7]) × (x[5] - x[7]))) / 6.0 + - norm((x[2] - x[7]) ⋅ ((x[5] - x[7]) × (x[6] - x[7]))) / 6.0 + - norm((x[4] - x[8]) ⋅ ((x[5] - x[8]) × (x[7] - x[8]))) / 6.0 + norm((x[2] - x[7]) ⋅ ((x[3] - x[7]) × (x[4] - x[7]))) / 6.0 + + norm((x[2] - x[7]) ⋅ ((x[4] - x[7]) × (x[5] - x[7]))) / 6.0 + + norm((x[2] - x[7]) ⋅ ((x[5] - x[7]) × (x[6] - x[7]))) / 6.0 + + norm((x[4] - x[8]) ⋅ ((x[5] - x[8]) × (x[7] - x[8]))) / 6.0 return vol end @@ -194,39 +210,39 @@ function calculate_volume(::Lagrange{RefPyramid, order}, x::Vector{Vec{3, T}}) w return vol end -function calculate_volume(::Serendipity{RefQuadrilateral, 2}, x::Vector{Vec{2, T}}) where T +function calculate_volume(::Serendipity{RefQuadrilateral, 2}, x::Vector{Vec{2, T}}) where {T} vol = norm((x[5] - x[1]) × (x[8] - x[1])) * 0.5 + - norm((x[6] - x[2]) × (x[5] - x[2])) * 0.5 + - norm((x[7] - x[3]) × (x[6] - x[3])) * 0.5 + - norm((x[8] - x[4]) × (x[7] - x[4])) * 0.5 + - norm((x[6] - x[5]) × (x[8] - x[5])) * 0.5 + - norm((x[6] - x[7]) × (x[8] - x[7])) * 0.5 + norm((x[6] - x[2]) × (x[5] - x[2])) * 0.5 + + norm((x[7] - x[3]) × (x[6] - x[3])) * 0.5 + + norm((x[8] - x[4]) × (x[7] - x[4])) * 0.5 + + norm((x[6] - x[5]) × (x[8] - x[5])) * 0.5 + + norm((x[6] - x[7]) × (x[8] - x[7])) * 0.5 return vol end function calculate_facet_area(ip::Union{Lagrange{RefLine}, DiscontinuousLagrange{RefLine}}, x::Vector{<:Vec}, faceindex::Int) return one(eltype(eltype(x))) end -function calculate_facet_area(ip::Union{Lagrange{RefQuadrilateral, order}, DiscontinuousLagrange{RefQuadrilateral, order}}, x::Vector{<:Vec}, faceindex::Int) where order +function calculate_facet_area(ip::Union{Lagrange{RefQuadrilateral, order}, DiscontinuousLagrange{RefQuadrilateral, order}}, x::Vector{<:Vec}, faceindex::Int) where {order} return calculate_volume(Lagrange{RefLine, order}(), x) end -function calculate_facet_area(ip::Union{Lagrange{RefTriangle, order}, DiscontinuousLagrange{RefTriangle, order}}, x::Vector{<:Vec}, faceindex::Int) where order +function calculate_facet_area(ip::Union{Lagrange{RefTriangle, order}, DiscontinuousLagrange{RefTriangle, order}}, x::Vector{<:Vec}, faceindex::Int) where {order} return calculate_volume(Lagrange{RefLine, order}(), x) end -function calculate_facet_area(ip::Union{Lagrange{RefHexahedron, order}, DiscontinuousLagrange{RefHexahedron, order}}, x::Vector{<:Vec}, faceindex::Int) where order +function calculate_facet_area(ip::Union{Lagrange{RefHexahedron, order}, DiscontinuousLagrange{RefHexahedron, order}}, x::Vector{<:Vec}, faceindex::Int) where {order} return calculate_volume(Lagrange{RefQuadrilateral, order}(), x) end -function calculate_facet_area(ip::Serendipity{RefQuadrilateral, order}, x::Vector{<:Vec}, faceindex::Int) where order +function calculate_facet_area(ip::Serendipity{RefQuadrilateral, order}, x::Vector{<:Vec}, faceindex::Int) where {order} return calculate_volume(Lagrange{RefLine, order}(), x) end -function calculate_facet_area(p::Union{Lagrange{RefTetrahedron, order}, DiscontinuousLagrange{RefTetrahedron, order}}, x::Vector{<:Vec}, faceindex::Int) where order +function calculate_facet_area(p::Union{Lagrange{RefTetrahedron, order}, DiscontinuousLagrange{RefTetrahedron, order}}, x::Vector{<:Vec}, faceindex::Int) where {order} return calculate_volume(Lagrange{RefTriangle, order}(), x) end -function calculate_facet_area(p::Union{Lagrange{RefPrism, order}, DiscontinuousLagrange{RefPrism, order}}, x::Vector{<:Vec}, faceindex::Int) where order - faceindex ∈ [1,5] && return calculate_volume(Lagrange{RefTriangle, order}(), x) +function calculate_facet_area(p::Union{Lagrange{RefPrism, order}, DiscontinuousLagrange{RefPrism, order}}, x::Vector{<:Vec}, faceindex::Int) where {order} + faceindex ∈ [1, 5] && return calculate_volume(Lagrange{RefTriangle, order}(), x) return calculate_volume(Lagrange{RefQuadrilateral, order}(), x) end -function calculate_facet_area(p::Union{Lagrange{RefPyramid, order}, DiscontinuousLagrange{RefPyramid, order}}, x::Vector{<:Vec}, faceindex::Int) where order +function calculate_facet_area(p::Union{Lagrange{RefPyramid, order}, DiscontinuousLagrange{RefPyramid, order}}, x::Vector{<:Vec}, faceindex::Int) where {order} faceindex != 1 && return calculate_volume(Lagrange{RefTriangle, order}(), x) return calculate_volume(Lagrange{RefQuadrilateral, order}(), x) end @@ -234,23 +250,23 @@ end coords_on_faces(x, ::Lagrange{RefLine, 1}) = ([x[1]], [x[2]]) coords_on_faces(x, ::Lagrange{RefLine, 2}) = ([x[1]], [x[2]]) coords_on_faces(x, ::Lagrange{RefQuadrilateral, 1}) = - ([x[1],x[2]], [x[2],x[3]], [x[3],x[4]], [x[1],x[4]]) + ([x[1], x[2]], [x[2], x[3]], [x[3], x[4]], [x[1], x[4]]) coords_on_faces(x, ::Lagrange{RefQuadrilateral, 2}) = - ([x[1],x[2],x[5]], [x[2],x[3],x[6]], [x[3],x[4],x[7]], [x[1],x[4],x[8]]) + ([x[1], x[2], x[5]], [x[2], x[3], x[6]], [x[3], x[4], x[7]], [x[1], x[4], x[8]]) coords_on_faces(x, ::Lagrange{RefTriangle, 1}) = - ([x[1],x[2]], [x[2],x[3]], [x[1],x[3]]) + ([x[1], x[2]], [x[2], x[3]], [x[1], x[3]]) coords_on_faces(x, ::Lagrange{RefTriangle, 2}) = - ([x[1],x[2],x[4]], [x[2],x[3],x[5]], [x[1],x[3],x[6]]) + ([x[1], x[2], x[4]], [x[2], x[3], x[5]], [x[1], x[3], x[6]]) coords_on_faces(x, ::Lagrange{RefTetrahedron, 1}) = - ([x[1],x[2],x[3]], [x[1],x[2],x[4]], [x[2],x[3],x[4]], [x[1],x[3],x[4]]) + ([x[1], x[2], x[3]], [x[1], x[2], x[4]], [x[2], x[3], x[4]], [x[1], x[3], x[4]]) coords_on_faces(x, ::Lagrange{RefTetrahedron, 2}) = - ([x[1],x[2],x[3],x[5],x[6],x[7]], [x[1],x[2],x[4],x[5],x[8],x[9]], [x[2],x[3],x[4],x[6],x[9],x[10]], [x[1],x[3],x[4],x[7],x[8],x[10]]) + ([x[1], x[2], x[3], x[5], x[6], x[7]], [x[1], x[2], x[4], x[5], x[8], x[9]], [x[2], x[3], x[4], x[6], x[9], x[10]], [x[1], x[3], x[4], x[7], x[8], x[10]]) coords_on_faces(x, ::Lagrange{RefHexahedron, 1}) = - ([x[1],x[2],x[3],x[4]], [x[1],x[2],x[5],x[6]], [x[2],x[3],x[6],x[7]],[x[3],x[4],x[7],x[8]],[x[1],x[4],x[5],x[8]],[x[5],x[6],x[7],x[8]]) + ([x[1], x[2], x[3], x[4]], [x[1], x[2], x[5], x[6]], [x[2], x[3], x[6], x[7]], [x[3], x[4], x[7], x[8]], [x[1], x[4], x[5], x[8]], [x[5], x[6], x[7], x[8]]) coords_on_faces(x, ::Serendipity{RefHexahedron, 2}) = - ([x[1],x[2],x[5]], [x[2],x[3],x[6]], [x[3],x[4],x[7]], [x[1],x[4],x[8]]) + ([x[1], x[2], x[5]], [x[2], x[3], x[6]], [x[3], x[4], x[7]], [x[1], x[4], x[8]]) -check_equal_or_nan(a::Any, b::Any) = a==b || (isnan(a) && isnan(b)) +check_equal_or_nan(a::Any, b::Any) = a == b || (isnan(a) && isnan(b)) check_equal_or_nan(a::Union{Tensor, Array}, b::Union{Tensor, Array}) = all(check_equal_or_nan.(a, b)) ###################################################### @@ -260,18 +276,18 @@ getfacerefshape(::Union{Quadrilateral, Triangle}, ::Int) = RefLine getfacerefshape(::Hexahedron, ::Int) = RefQuadrilateral getfacerefshape(::Tetrahedron, ::Int) = RefTriangle getfacerefshape(::Pyramid, face::Int) = face == 1 ? RefQuadrilateral : RefTriangle -getfacerefshape(::Wedge, face::Int) = face ∈ (1,5) ? RefTriangle : RefQuadrilateral +getfacerefshape(::Wedge, face::Int) = face ∈ (1, 5) ? RefTriangle : RefQuadrilateral -function perturbate_standard_grid!(grid::Ferrite.AbstractGrid{dim}, strength) where dim - function perturbate(x::Vec{dim}) where dim +function perturbate_standard_grid!(grid::Ferrite.AbstractGrid{dim}, strength) where {dim} + function perturbate(x::Vec{dim}) where {dim} for d in 1:dim if x[d] ≈ 1.0 || x[d] ≈ -1.0 return x end end - return x + Vec{dim}(0.5*strength .* (2 .* rand(Vec{dim}) .- 1.0)) + return x + Vec{dim}(0.5 * strength .* (2 .* rand(Vec{dim}) .- 1.0)) end - transform_coordinates!(grid, perturbate) + return transform_coordinates!(grid, perturbate) end ###################################################### @@ -279,7 +295,7 @@ end ###################################################### module DummyRefShapes import Ferrite - struct RefDodecahedron <: Ferrite.AbstractRefShape{3} end + struct RefDodecahedron <: Ferrite.AbstractRefShape{3} end function Ferrite.reference_faces(::Type{RefDodecahedron}) return ( (1, 5, 4, 3, 2), @@ -288,7 +304,7 @@ module DummyRefShapes end # Hypercube is simply ⨂ᵈⁱᵐ Line :) -sample_random_point(::Type{Ferrite.RefHypercube{ref_dim}}) where {ref_dim} = Vec{ref_dim}(ntuple(_ -> 2.0*rand()-1.0, ref_dim)) +sample_random_point(::Type{Ferrite.RefHypercube{ref_dim}}) where {ref_dim} = Vec{ref_dim}(ntuple(_ -> 2.0 * rand() - 1.0, ref_dim)) # Dirichlet type sampling # @@ -303,8 +319,8 @@ sample_random_point(::Type{Ferrite.RefHypercube{ref_dim}}) where {ref_dim} = Vec # # A nice geometric sketch of this process is given in this stackexchange post: https://stats.stackexchange.com/a/296779 function sample_random_point(::Type{Ferrite.RefSimplex{ref_dim}}) where {ref_dim} # Note that "ref_dim = d" in the text above - ξₜ = ntuple(_ -> -log(rand()), ref_dim+1) - return Vec{ref_dim}(ntuple(i->ξₜ[i], ref_dim) ./ sum(ξₜ)) + ξₜ = ntuple(_ -> -log(rand()), ref_dim + 1) + return Vec{ref_dim}(ntuple(i -> ξₜ[i], ref_dim) ./ sum(ξₜ)) end # Wedge = Triangle ⊗ Line @@ -316,7 +332,7 @@ end # TODO what to do here? The samplig is not uniform... function sample_random_point(::Type{RefPyramid}) - ξ₃ = (1-1e-3)*rand(Float64) # Derivative is discontinuous at the top + ξ₃ = (1 - 1.0e-3) * rand(Float64) # Derivative is discontinuous at the top # If we fix a z coordinate we get a Quad with extends (1-ξ₃) (ξ₁, ξ₂) = (1.0 - ξ₃) .* Vec{2}(ntuple(_ -> rand(), 2)) return Vec{3}((ξ₁, ξ₂, ξ₃)) @@ -325,11 +341,11 @@ end ############################################################ # Inverse parametric mapping ξ = ϕ(x) for testing hessians # ############################################################ -function function_value_from_physical_coord(interpolation::Interpolation, cell_coordinates, X::Vec{dim,T}, ue) where {dim,T} +function function_value_from_physical_coord(interpolation::Interpolation, cell_coordinates, X::Vec{dim, T}, ue) where {dim, T} n_basefuncs = getnbasefunctions(interpolation) scalar_ip = interpolation isa Ferrite.ScalarInterpolation ? interpolation : interpolation.ip @assert length(ue) == n_basefuncs - _, ξ = Ferrite.find_local_coordinate(scalar_ip, cell_coordinates, X, Ferrite.NewtonLineSearchPointFinder(residual_tolerance=1e-16)) + _, ξ = Ferrite.find_local_coordinate(scalar_ip, cell_coordinates, X, Ferrite.NewtonLineSearchPointFinder(residual_tolerance = 1.0e-16)) u = zero(reference_shape_value(interpolation, ξ, 1)) for j in 1:n_basefuncs N = reference_shape_value(interpolation, ξ, j) diff --git a/test/test_vtk_export.jl b/test/test_vtk_export.jl index 7c5fc8f30b..7388fd2502 100644 --- a/test/test_vtk_export.jl +++ b/test/test_vtk_export.jl @@ -1,7 +1,7 @@ @testset "VTKGridFile" begin #TODO: Move all vtk tests here @testset "show(::VTKGridFile)" begin mktempdir() do tmp - grid = generate_grid(Quadrilateral, (2,2)) + grid = generate_grid(Quadrilateral, (2, 2)) vtk = VTKGridFile(joinpath(tmp, "showfile"), grid) showstring_open = sprint(show, MIME"text/plain"(), vtk) @test startswith(showstring_open, "VTKGridFile for the open file") @@ -21,7 +21,7 @@ @test Ferrite.write_cell_colors(vtk, grid, colors) === vtk end @test v isa VTKGridFile - @test bytes2hex(open(SHA.sha1, fname*".vtu")) == "b804d0b064121b672d8e35bcff8446eda361cac3" + @test bytes2hex(open(SHA.sha1, fname * ".vtu")) == "b804d0b064121b672d8e35bcff8446eda361cac3" end end @testset "constraints" begin @@ -40,14 +40,14 @@ @test Ferrite.write_constraints(vtk, ch) === vtk end @test v isa VTKGridFile - @test bytes2hex(open(SHA.sha1, fname*".vtu")) == "31b506bd9729b11992f8bcb79a2191eb65d223bf" + @test bytes2hex(open(SHA.sha1, fname * ".vtu")) == "31b506bd9729b11992f8bcb79a2191eb65d223bf" end end @testset "write_cellset" begin # More tests in `test_grid_dofhandler_vtk.jl`, this just validates writing all sets in the grid # which is not tested there, see https://github.com/Ferrite-FEM/Ferrite.jl/pull/948 mktempdir() do tmp - grid = generate_grid(Quadrilateral, (2,2)) + grid = generate_grid(Quadrilateral, (2, 2)) addcellset!(grid, "set1", 1:2) addcellset!(grid, "set2", 1:4) manual = joinpath(tmp, "manual") @@ -60,7 +60,7 @@ @test Ferrite.write_cellset(vtk, grid) === vtk end @test v isa VTKGridFile - @test bytes2hex(open(SHA.sha1, manual*".vtu")) == bytes2hex(open(SHA.sha1, auto*".vtu")) + @test bytes2hex(open(SHA.sha1, manual * ".vtu")) == bytes2hex(open(SHA.sha1, auto * ".vtu")) end end end