diff --git a/NEWS.md b/NEWS.md index ee1b7dd55..1f74b4593 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + - Added VectorWithEntryInserted and VectorWithEntryRemoved. Since PR [#401](https://github.com/gridap/Gridap.jl/pull/401/) + ## [0.14.0] - 2020-08-27 ### Removed diff --git a/src/Arrays/Arrays.jl b/src/Arrays/Arrays.jl index b224759b7..87a9c1462 100644 --- a/src/Arrays/Arrays.jl +++ b/src/Arrays/Arrays.jl @@ -114,6 +114,9 @@ export autodiff_array_gradient export autodiff_array_jacobian export autodiff_array_hessian +export VectorWithEntryRemoved +export VectorWithEntryInserted + import Base: size import Base: getindex, setindex! import Base: similar @@ -158,4 +161,9 @@ include("VectorsOfBlockArrayCoo.jl") include("Autodiff.jl") +include("VectorsWithEntryRemoved.jl") + +include("VectorsWithEntryInserted.jl") + + end # module diff --git a/src/Arrays/VectorsWithEntryInserted.jl b/src/Arrays/VectorsWithEntryInserted.jl new file mode 100644 index 000000000..d5fd68338 --- /dev/null +++ b/src/Arrays/VectorsWithEntryInserted.jl @@ -0,0 +1,38 @@ +mutable struct VectorWithEntryInserted{T,A} <: AbstractVector{T} + a::A + index::Int + value::T + function VectorWithEntryInserted(a::AbstractVector,index::Integer,value) + A = typeof(a) + T = eltype(a) + @assert 1 <= index <= length(a)+1 + new{T,A}(a,index,value) + end +end + +Base.IndexStyle(::Type{<:VectorWithEntryInserted}) = IndexLinear() + +function Base.getindex(v::VectorWithEntryInserted,i::Integer) + i < v.index ? v.a[i] : + (i==v.index ? v.value : v.a[i-1]) +end + +Base.size(v::VectorWithEntryInserted) = (length(v.a)+1,) + +function array_cache(v::VectorWithEntryInserted) + array_cache(v.a) +end + +function getindex!(cache,v::VectorWithEntryInserted,i::Integer) + i < v.index ? getindex!(cache,v.a,i) : + (i==v.index ? v.value : getindex!(cache,v.a,i-1)) +end + +function Base.sum(a::VectorWithEntryInserted) + sum(a.a) + a.value +end + +function Base.setindex!(a::VectorWithEntryInserted,v,i::Integer) + i < a.index ? (a.a[i] = v) : + (i==a.index ? a.value = v : a.a[i-1]=v) +end diff --git a/src/Arrays/VectorsWithEntryRemoved.jl b/src/Arrays/VectorsWithEntryRemoved.jl new file mode 100644 index 000000000..a2207328d --- /dev/null +++ b/src/Arrays/VectorsWithEntryRemoved.jl @@ -0,0 +1,34 @@ +struct VectorWithEntryRemoved{T,A} <: AbstractVector{T} + a::A + index::Int + function VectorWithEntryRemoved(a::AbstractVector,index::Integer) + A = typeof(a) + T = eltype(a) + @assert 1 <= index <= length(a) + new{T,A}(a,index) + end +end + +Base.IndexStyle(::Type{<:VectorWithEntryRemoved}) = IndexLinear() + +function Base.getindex(v::VectorWithEntryRemoved,i::Integer) + i < v.index ? v.a[i] : v.a[i+1] +end + +Base.size(v::VectorWithEntryRemoved) = (length(v.a)-1,) + +function array_cache(v::VectorWithEntryRemoved) + array_cache(v.a) +end + +function getindex!(cache,v::VectorWithEntryRemoved,i::Integer) + i < v.index ? getindex!(cache,v.a,i) : getindex!(cache,v.a,i+1) +end + +function Base.sum(a::VectorWithEntryRemoved) + sum(a.a) - a.a[a.index] +end + +function Base.setindex!(a::VectorWithEntryRemoved,v,i::Integer) + i < a.index ? (a.a[i] = v) : (a.a[i+1] = v) +end diff --git a/src/FESpaces/FESpaces.jl b/src/FESpaces/FESpaces.jl index db847a3f0..ae8d6fa6b 100644 --- a/src/FESpaces/FESpaces.jl +++ b/src/FESpaces/FESpaces.jl @@ -204,7 +204,8 @@ export collect_cell_jacobian export collect_cell_jacobian_and_residual export collect_cell_residual -export FESpaceWithLastDofRemoved +export FESpaceWithConstantFixed + export ZeroMeanFESpace export CLagrangianFESpace export ConformingFESpace @@ -252,7 +253,7 @@ include("FEOperatorsFromTerms.jl") include("FESolvers.jl") -include("FESpacesWithLastDofRemoved.jl") +include("FESpacesWithConstantFixed.jl") include("ZeroMeanFESpaces.jl") diff --git a/src/FESpaces/FESpacesWithConstantFixed.jl b/src/FESpaces/FESpacesWithConstantFixed.jl new file mode 100644 index 000000000..eacaabcd0 --- /dev/null +++ b/src/FESpaces/FESpacesWithConstantFixed.jl @@ -0,0 +1,180 @@ +abstract type ConstantApproach end; +struct FixConstant <: ConstantApproach end; +struct DoNotFixConstant <: ConstantApproach end; + +#""" +# struct FESpaceWithConstantFixed{CS,<:ConstantApproach} <: SingleFieldFESpace +# space::SingleFieldFESpace +# constraint_style::Val{CS} +# dof_to_fix::Int +# end +#""" +struct FESpaceWithConstantFixed{CS,CA<:ConstantApproach} <: SingleFieldFESpace + space::SingleFieldFESpace + constraint_style::Val{CS} + dof_to_fix::Int + @doc """ + FESpaceWithConstantFixed(space::SingleFieldFESpace, fix_constant::Bool, + dof_to_fix::Int=num_free_dofs(space)) + """ + function FESpaceWithConstantFixed(space::SingleFieldFESpace, + fix_constant::Bool, + dof_to_fix::Int=num_free_dofs(space)) + cs = constraint_style(space) + CS = get_val_parameter(cs) + if (fix_constant && num_dirichlet_dofs(space)==0) + new{CS,FixConstant}(space,cs,dof_to_fix) + else + new{CS,DoNotFixConstant}(space,cs,dof_to_fix) + end + end +end + +# Genuine functions +function num_free_dofs(f::FESpaceWithConstantFixed{CS,FixConstant}) where {CS} + num_free_dofs(f.space)-1 +end + +function num_free_dofs(f::FESpaceWithConstantFixed{CS,DoNotFixConstant}) where {CS} + num_free_dofs(f.space) +end + +function zero_free_values(f::FESpaceWithConstantFixed) + zeros(num_free_dofs(f)) +end + +function get_cell_dofs(f::FESpaceWithConstantFixed{CS,FixConstant}) where {CS} + cell_dofs = get_cell_dofs(f.space) + CellDofsWithDofFixed(cell_dofs,f.dof_to_fix) +end + +function get_cell_dofs(f::FESpaceWithConstantFixed{CS,DoNotFixConstant}) where {CS} + get_cell_dofs(f.space) +end + + +num_dirichlet_dofs(f::FESpaceWithConstantFixed{CS,FixConstant}) where {CS} = 1 + +num_dirichlet_dofs(f::FESpaceWithConstantFixed{CS,DoNotFixConstant}) where {CS} = 0 + +function zero_dirichlet_values(f::FESpaceWithConstantFixed) + T = Float64 # TODO + zeros(T,num_dirichlet_dofs(f)) +end + + +num_dirichlet_tags(f::FESpaceWithConstantFixed{CS,FixConstant}) where {CS} = 1 + +num_dirichlet_tags(f::FESpaceWithConstantFixed{CS,DoNotFixConstant}) where {CS} = 0 + +get_dirichlet_dof_tag(f::FESpaceWithConstantFixed{CS,FixConstant}) where {CS} = Int8[1,] + +get_dirichlet_dof_tag(f::FESpaceWithConstantFixed{CS,DoNotFixConstant}) where {CS} = Int8[] + +function scatter_free_and_dirichlet_values( + f::FESpaceWithConstantFixed{CS,FixConstant},fv,dv) where {CS} + @assert length(dv) == 1 + _dv = similar(dv,eltype(dv),0) + _fv = VectorWithEntryInserted(fv,f.dof_to_fix,dv[1]) + scatter_free_and_dirichlet_values(f.space,_fv,_dv) +end + +function scatter_free_and_dirichlet_values( + f::FESpaceWithConstantFixed{CS,DoNotFixConstant},fv,dv) where {CS} + @assert length(dv) == 0 + scatter_free_and_dirichlet_values(f.space,fv,dv) +end + +function gather_free_and_dirichlet_values( + f::FESpaceWithConstantFixed{CS,FixConstant},cv) where {CS} + _fv, _dv = gather_free_and_dirichlet_values(f.space,cv) + @assert length(_dv) == 0 + fv = VectorWithEntryRemoved(_fv,f.dof_to_fix) + dv = _fv[f.dof_to_fix:f.dof_to_fix] + (fv, dv) +end + +function gather_free_and_dirichlet_values( + f::FESpaceWithConstantFixed{CS,DoNotFixConstant},cv) where {CS} + gather_free_and_dirichlet_values(f.space,cv) +end + +function gather_free_and_dirichlet_values!( + fv,dv,f::FESpaceWithConstantFixed{CS,FixConstant},cv) where {CS} + _fv, _dv = gather_free_and_dirichlet_values(f.space,cv) + @assert length(_dv) == 0 + fv .= VectorWithEntryRemoved(_fv,f.dof_to_fix) + dv[1] = _fv[f.dof_to_fix] + (fv, dv) +end + +function gather_free_and_dirichlet_values!( + fv,dv,f::FESpaceWithConstantFixed{CS,DoNotFixConstant},cv) where {CS} + gather_free_and_dirichlet_values(f.space,cv) +end + +function TrialFESpace(f::FESpaceWithConstantFixed{CS,CA}) where {CS,CA} + U = TrialFESpace(f.space) + FESpaceWithConstantFixed(U,CA==FixConstant,f.dof_to_fix) +end + +# Delegated functions + +function get_cell_basis(f::FESpaceWithConstantFixed) + get_cell_basis(f.space) +end + +function get_cell_dof_basis(f::FESpaceWithConstantFixed) + get_cell_dof_basis(f.space) +end + +get_cell_axes(t::FESpaceWithConstantFixed)= get_cell_axes(t.space) + +get_cell_axes_with_constraints(t::FESpaceWithConstantFixed)= get_cell_axes_with_constraints(t.space) + +CellData.CellField(t::FESpaceWithConstantFixed,cell_vals) = CellField(t.space,cell_vals) + +constraint_style(::Type{<:FESpaceWithConstantFixed{CS}}) where CS = Val{CS}() + +# Helpers + +struct CellDofsWithDofFixed{A<:AbstractArray} <: AbstractVector{Vector{Int}} + cell_dofs::A + dof_to_fix::Int +end + +Base.size(a::CellDofsWithDofFixed) = (length(a.cell_dofs),) + +Base.IndexStyle(::Type{<:CellDofsWithDofFixed}) = IndexLinear() + +function Base.getindex(a::CellDofsWithDofFixed,i::Integer) + cache = array_cache(a) + getindex!(cache,a,i) +end + +function array_cache(a::CellDofsWithDofFixed) + @assert eltype(a.cell_dofs) == Vector{Int} + b = testitem(a.cell_dofs) + c = CachedArray(b) + cache = array_cache(a.cell_dofs) + (c, cache) +end + +@inline function getindex!(d,a::CellDofsWithDofFixed,i::Integer) + c, cache = d + b = getindex!(cache,a.cell_dofs,i) + setsize!(c,size(b)) + r = c.array + for j in 1:length(b) + bj = b[j] + if bj == a.dof_to_fix + rj = -1 + elseif bj < a.dof_to_fix + rj = bj + else + rj = bj-1 + end + r[j] = rj + end + r +end diff --git a/src/FESpaces/FESpacesWithLastDofRemoved.jl b/src/FESpaces/FESpacesWithLastDofRemoved.jl deleted file mode 100644 index 7be8a42b3..000000000 --- a/src/FESpaces/FESpacesWithLastDofRemoved.jl +++ /dev/null @@ -1,140 +0,0 @@ - -""" - struct FESpaceWithLastDofRemoved <: SingleFieldFESpace - space::SingleFieldFESpace - end -""" -struct FESpaceWithLastDofRemoved{B} <: SingleFieldFESpace - space::SingleFieldFESpace - constraint_style::Val{B} - @doc """ - FESpaceWithLastDofRemoved(space::SingleFieldFESpace) - """ - function FESpaceWithLastDofRemoved(space::SingleFieldFESpace) - s = "FESpaceWithLastDofRemoved can only be constructed from spaces without dirichlet dofs." - @notimplementedif num_dirichlet_dofs(space) != 0 s - cs = constraint_style(space) - B = get_val_parameter(cs) - new{B}(space,cs) - end -end - -# Genuine functions - -function num_free_dofs(f::FESpaceWithLastDofRemoved) - num_free_dofs(f.space) - 1 -end - -function zero_free_values(f::FESpaceWithLastDofRemoved) - zeros(num_free_dofs(f)) -end - -function get_cell_dofs(f::FESpaceWithLastDofRemoved) - cell_dofs = get_cell_dofs(f.space) - n = num_free_dofs(f) - CellDofsWithLastRemoved(cell_dofs,n) -end - -num_dirichlet_dofs(f::FESpaceWithLastDofRemoved) = 1 - -function zero_dirichlet_values(f::FESpaceWithLastDofRemoved) - T = Float64 # TODO - zeros(T,num_dirichlet_dofs(f)) -end - -num_dirichlet_tags(f::FESpaceWithLastDofRemoved) = 1 - -get_dirichlet_dof_tag(f::FESpaceWithLastDofRemoved) = Int8[1,] - -function scatter_free_and_dirichlet_values( - f::FESpaceWithLastDofRemoved,fv,dv) - @assert length(dv) == 1 - _dv = similar(dv,eltype(dv),0) - _fv = vcat(fv,dv) # TODO lazy append - scatter_free_and_dirichlet_values(f.space,_fv,_dv) -end - -function gather_free_and_dirichlet_values( - f::FESpaceWithLastDofRemoved,cv) - _fv, _dv = gather_free_and_dirichlet_values(f.space,cv) - @assert length(_dv) == 0 - l = length(_fv) - fv = SubVector(_fv,1,l-1) - dv = SubVector(_fv,l,l) - (fv, dv) -end - -function gather_free_and_dirichlet_values!( - fv,dv,f::FESpaceWithLastDofRemoved,cv) - _fv , _dv = gather_free_and_dirichlet_values(f.space,cv) - @assert length(_dv) == 0 - l = length(_fv) - fv .= SubVector(_fv,1,l-1) - dv .= SubVector(_fv,l,l) - (fv, dv) -end - -function TrialFESpace(f::FESpaceWithLastDofRemoved) - U = TrialFESpace(f.space) - FESpaceWithLastDofRemoved(U) -end - -# Delegated functions - -function get_cell_basis(f::FESpaceWithLastDofRemoved) - get_cell_basis(f.space) -end - -function get_cell_dof_basis(f::FESpaceWithLastDofRemoved) - get_cell_dof_basis(f.space) -end - -get_cell_axes(t::FESpaceWithLastDofRemoved)= get_cell_axes(t.space) - -get_cell_axes_with_constraints(t::FESpaceWithLastDofRemoved)= get_cell_axes_with_constraints(t.space) - -CellData.CellField(t::FESpaceWithLastDofRemoved,cell_vals) = CellField(t.space,cell_vals) - -constraint_style(::Type{FESpaceWithLastDofRemoved{B}}) where B = Val{B}() - -# Helpers - -struct CellDofsWithLastRemoved{A<:AbstractArray} <: AbstractVector{Vector{Int}} - cell_dofs::A - n::Int -end - -Base.size(a::CellDofsWithLastRemoved) = (length(a.cell_dofs),) - -Base.IndexStyle(::Type{<:CellDofsWithLastRemoved}) = IndexLinear() - -function Base.getindex(a::CellDofsWithLastRemoved,i::Integer) - cache = array_cache(a) - getindex!(cache,a,i) -end - -function array_cache(a::CellDofsWithLastRemoved) - @assert eltype(a.cell_dofs) == Vector{Int} - b = testitem(a.cell_dofs) - c = CachedArray(b) - cache = array_cache(a.cell_dofs) - (c, cache) -end - -@inline function getindex!(d,a::CellDofsWithLastRemoved,i::Integer) - c, cache = d - b = getindex!(cache,a.cell_dofs,i) - setsize!(c,size(b)) - r = c.array - for j in 1:length(b) - bj = b[j] - if bj > a.n - rj = -1 - else - rj = bj - end - r[j] = rj - end - r -end - diff --git a/src/FESpaces/ZeroMeanFESpaces.jl b/src/FESpaces/ZeroMeanFESpaces.jl index 3b977f573..5b44d446a 100644 --- a/src/FESpaces/ZeroMeanFESpaces.jl +++ b/src/FESpaces/ZeroMeanFESpaces.jl @@ -5,7 +5,7 @@ end """ struct ZeroMeanFESpace{B} <: SingleFieldFESpace - space::FESpaceWithLastDofRemoved + space::FESpaceWithConstantFixed vol_i::Vector{Float64} vol::Float64 constraint_style::Val{B} @@ -20,7 +20,9 @@ end function ZeroMeanFESpace( space::SingleFieldFESpace,trian::Triangulation,quad::CellQuadrature) - _space = FESpaceWithLastDofRemoved(space) + _space = FESpaceWithConstantFixed(space, + true, + num_free_dofs(space)) vol_i, vol = _setup_vols(space,trian,quad) ZeroMeanFESpace(_space,vol_i,vol,constraint_style(_space)) end @@ -49,8 +51,11 @@ function FEFunction( f::ZeroMeanFESpace, free_values::AbstractVector, dirichlet_values::AbstractVector) - - c = _compute_new_fixedval(free_values,dirichlet_values,f.vol_i,f.vol) + c = _compute_new_fixedval(free_values, + dirichlet_values, + f.vol_i, + f.vol, + f.space.dof_to_fix) fv = apply(+,free_values,Fill(c,length(free_values))) dv = dirichlet_values .+ c FEFunction(f.space,fv,dv) @@ -60,14 +65,17 @@ function EvaluationFunction(f::ZeroMeanFESpace,free_values) FEFunction(f.space,free_values) end -function _compute_new_fixedval(fv,dv,vol_i,vol) +function _compute_new_fixedval(fv,dv,vol_i,vol,fixed_dof) @assert length(fv) + 1 == length(vol_i) @assert length(dv) == 1 - c = 0.0 - for (i,vi) in enumerate(fv) - c += vi*vol_i[i] + c = zero(eltype(vol_i)) + for i=1:fixed_dof-1 + c += fv[i]*vol_i[i] + end + c += first(dv)*vol_i[fixed_dof] + for i=fixed_dof+1:length(vol_i) + c += fv[i-1]*vol_i[i] end - c += vol_i[end]*first(dv) c = -c/vol c end @@ -119,4 +127,3 @@ gather_dirichlet_values!(dv,f::ZeroMeanFESpace,cv) = gather_dirichlet_values!(dv gather_free_values(f::ZeroMeanFESpace,cv) = gather_free_values(f.space,cv) gather_free_values!(fv,f::ZeroMeanFESpace,cv) = gather_free_values!(fv,f.space,cv) - diff --git a/test/ArraysTests/VectorWithEntryInsertedTests.jl b/test/ArraysTests/VectorWithEntryInsertedTests.jl new file mode 100644 index 000000000..66b69ba0a --- /dev/null +++ b/test/ArraysTests/VectorWithEntryInsertedTests.jl @@ -0,0 +1,24 @@ +module VectorWithEntryInsertedTests + +using Test +using Gridap.Arrays + +a = collect(1:10) +for i=1:length(a) + b = VectorWithEntryInserted(a,i,i) + test_array(vcat(a[1:i],[i],a[i+1:end]),b) +end + +for i=1:length(a) + b = VectorWithEntryInserted(a,i,i) + pos = rand(1:length(b)) + val = rand(Int) + b[pos]=val + if (pos == i) + test_array(vcat(a[1:i-1],[val],a[i:end]),b) + else + test_array(vcat(a[1:i-1],[i],a[i:end]),b) + end +end + +end # module diff --git a/test/ArraysTests/VectorWithEntryRemovedTests.jl b/test/ArraysTests/VectorWithEntryRemovedTests.jl new file mode 100644 index 000000000..bd0d94d79 --- /dev/null +++ b/test/ArraysTests/VectorWithEntryRemovedTests.jl @@ -0,0 +1,18 @@ +module VectorWithEntryRemovedTests + +using Test +using Gridap.Arrays + +a = collect(1:10) +for i=1:length(a) + b = VectorWithEntryRemoved(a,i) + test_array(vcat(a[1:i-1],a[i+1:end]),b) +end + +for i=1:length(a) + b = VectorWithEntryRemoved(a,i) + b[rand(1:length(a)-1)]=rand(Int) + test_array(vcat(a[1:i-1],a[i+1:end]),b) +end + +end # module diff --git a/test/ArraysTests/runtests.jl b/test/ArraysTests/runtests.jl index a64e6116d..1c64d55cf 100644 --- a/test/ArraysTests/runtests.jl +++ b/test/ArraysTests/runtests.jl @@ -36,4 +36,8 @@ using Test @testset "AutodiffTests" begin include("AutodiffTests.jl") end +@testset "VectorWithEntryRemovedTests" begin include("VectorWithEntryRemovedTests.jl") end + +@testset "VectorWithEntryInsertedTests" begin include("VectorWithEntryInsertedTests.jl") end + end # module diff --git a/test/FESpacesTests/FESpacesWithLastDofRemovedTests.jl b/test/FESpacesTests/FESpacesWithConstantFixedTests.jl similarity index 82% rename from test/FESpacesTests/FESpacesWithLastDofRemovedTests.jl rename to test/FESpacesTests/FESpacesWithConstantFixedTests.jl index b55d7614c..a1f5b7ced 100644 --- a/test/FESpacesTests/FESpacesWithLastDofRemovedTests.jl +++ b/test/FESpacesTests/FESpacesWithConstantFixedTests.jl @@ -1,4 +1,4 @@ -module FESpacesWithLastDofRemovedTests +module FESpaceWithConstantFixedTests using Gridap.Geometry using Gridap.FESpaces @@ -18,7 +18,7 @@ V = TestFESpace( order=order, conformity=:L2) -V0 = FESpaceWithLastDofRemoved(V) +V0 = FESpaceWithConstantFixed(V,true,rand(1:num_free_dofs(V))) test_single_field_fe_space(V0) uh0 = interpolate(V0) do x diff --git a/test/FESpacesTests/runtests_2.jl b/test/FESpacesTests/runtests_2.jl index 2e1a9f740..b0e13afbf 100644 --- a/test/FESpacesTests/runtests_2.jl +++ b/test/FESpacesTests/runtests_2.jl @@ -2,7 +2,7 @@ module FESpacesTests2 using Test -@testset "FESpacesWithLastDofRemoved" begin include("FESpacesWithLastDofRemovedTests.jl") end +@testset "FESpacesWithConstantFixed" begin include("FESpacesWithConstantFixedTests.jl") end @testset "ZeroMeanFESpaces" begin include("ZeroMeanFESpacesTests.jl") end