From 7523bd9682b0dc3e573e5d6102700a28a1b75a7a Mon Sep 17 00:00:00 2001 From: Stephan Hilb <stephan.hilb@mathematik.uni-stuttgart.de> Date: Wed, 12 Sep 2018 14:27:18 +0200 Subject: [PATCH] code: split off FD project --- FD/Manifest.toml | 65 ++++++ FD/Project.toml | 8 + FD/src/FD.jl | 485 ++++++++++++++++++++++++++++++++++++++++++++ FD/src/filtering.jl | 110 ++++++++++ Manifest.toml | 224 ++++++++++++++++++++ Project.toml | 4 + chambollefp.jl | 22 +- chambollefp_dd.jl | 130 ++++++++---- fd.jl | 19 +- 9 files changed, 1010 insertions(+), 57 deletions(-) create mode 100644 FD/Manifest.toml create mode 100644 FD/Project.toml create mode 100644 FD/src/FD.jl create mode 100644 FD/src/filtering.jl create mode 100644 Manifest.toml create mode 100644 Project.toml diff --git a/FD/Manifest.toml b/FD/Manifest.toml new file mode 100644 index 0000000..6453c7c --- /dev/null +++ b/FD/Manifest.toml @@ -0,0 +1,65 @@ +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[InteractiveUtils]] +deps = ["LinearAlgebra", "Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[OffsetArrays]] +deps = ["DelimitedFiles", "Test"] +git-tree-sha1 = "f8cd140824f74f2948ff8660bc2292e16d29c1b7" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "0.8.1" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[StaticArrays]] +deps = ["InteractiveUtils", "LinearAlgebra", "Random", "Statistics", "Test"] +git-tree-sha1 = "d432c79bef174a830304f8601427a4357dfdbfb7" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "0.8.3" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/FD/Project.toml b/FD/Project.toml new file mode 100644 index 0000000..47f67e5 --- /dev/null +++ b/FD/Project.toml @@ -0,0 +1,8 @@ +name = "FD" +uuid = "edd53c04-b66a-11e8-3f8d-297d8a5291a5" +authors = ["Stephan Hilb <stephan.hilb@mathematik.uni-stuttgart.de>"] +version = "0.1.0" + +[deps] +OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" diff --git a/FD/src/FD.jl b/FD/src/FD.jl new file mode 100644 index 0000000..6bd0c1b --- /dev/null +++ b/FD/src/FD.jl @@ -0,0 +1,485 @@ +module FD + +using StaticArrays +#using SparseArrays +using OffsetArrays +#using LinearMaps +#using IterativeSolvers + +include("filtering.jl") + +"Finite Differences" +module FDiff + +using OffsetArrays + +const forward = OffsetVector([-1, 1], -1) +const backward = OffsetVector([-1, 1], -2) +const central = OffsetVector([-1, 0, 1], -2) +function dirfd(fd, ndims, dir) + dir <= ndims || throw(ArgumentError("need dir <= ndims!")) + dfd = OffsetArray(zeros(ntuple(i -> i == dir ? length(fd) : 1, ndims)), ntuple(i -> i == dir ? fd.offsets[1] : -1, ndims)) + # Workaround: LinearIndices(::OffsetVector) are not 1-based, so copyto! chockes + copyto!(dfd, fd.parent) +end + +end + + +using .Filtering +using .FDiff + +""" + _fdkern(fd, ndims, dir) + +Create from `fd` a finite difference kernel usable in `ndims` dimension +oriented in dimension `dir`. +""" +# TODO: make it typestable on ndims +function _fdkern(fd::OffsetArray{T,1,Array{T,1}}, ndims::Integer, dir::Integer) where T + 1 <= dir <= ndims || throw(ArgumentError("dimesion $(dir) out of range 1:$(ndims)")) + dfd = OffsetArray{T}(undef, Tuple(i == dir ? axes(fd, 1) : (0:0) for i in 1:ndims)) + # copyto! from OffsetArray fails, broadcasting for differing axes fails too + # workaround: copyto! from parent + copyto!(dfd, fd.parent) +end + + +# Grid + + +abstract type AbstractGrid{d} <: AbstractArray{Float64, d} end + +Base.ndims(grid::AbstractGrid{d}) where d = d +Base.axes(grid::AbstractGrid) = Base.OneTo.(size(grid)) +Base.length(grid::AbstractGrid) = prod(size(grid)) +Base.IndexStyle(::Type{<:AbstractGrid}) = IndexCartesian() + +# aka `StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}` +const FDStepRange = typeof(0:0.1:1) + +struct Grid{d} <: AbstractGrid{d} + domain::NTuple{d, FDStepRange} +end +Grid(domain::FDStepRange...) = Grid(domain) +Base.show(io::IO, grid::Grid) = + print("Grid from $(first.(grid.domain)) to $(last.(grid.domain)) ", + "with spacing $(step.(grid.domain))") + +Base.size(grid::Grid) = ntuple(i -> grid.domain[i].len, ndims(grid)) + +Base.iterate(grid::Grid) = iterate(Iterators.product(grid.domain...)) +Base.iterate(grid::Grid, state) = iterate(Iterators.product(grid.domain...), state) + +Base.getindex(grid::Grid, idx...) = SVector(getindex.(grid.domain, idx)) +Base.getindex(grid::Grid, I::CartesianIndex) = SVector(getindex.(grid.domain, Tuple(I))) + + +# GridView + +struct GridView{d} <: AbstractGrid{d} + grid::AbstractGrid{d} + indices::NTuple{d, UnitRange{Int}} +end +GridView(grid, indices::UnitRange{Int}...) = GridView(grid, indices) + +Base.size(grid::GridView) = length.(grid.indices) + +# FIXME: this is terrible! +Base.iterate(grid::GridView) = iterate(grid.grid[I] for I in CartesianIndices(grid.indices)) +Base.iterate(grid::GridView, state) = iterate([grid.grid[I] for I in CartesianIndices(grid.indices)], state) + +Base.getindex(grid::GridView, idx...) = getindex(grid.grid, (first.(grid.indices) .- 1 .+ Base.to_indices(grid, idx))...) + +# MetaGrid + + +struct MetaGrid{d} <: AbstractGrid{d} + grid::Grid{d} + indices::Array{NTuple{d, UnitRange{Int}}, d} + overlap::Int +end + +""" + MetaGrid(domain::NTuple{d, FDStepRange}, pnum::NTuple{d, Int}, overlap::Int) + +Create a grid on `domain`, partitioned along dimensions according to `pnum` +respecting `overlap`. +""" +function MetaGrid(domain::NTuple{d, FDStepRange}, pnum::NTuple{d, Int}, overlap::Int) where d + grid = Grid(domain) + tsize = size(grid) .+ (pnum .- 1) .* overlap + + psize = tsize .÷ pnum + osize = tsize .- pnum .* psize + + overhang(I, j) = I[j] == pnum[j] ? osize[j] : 0 + + indices = Array{NTuple{d, UnitRange{Int}}, d}(undef, pnum) + for I in CartesianIndices(pnum) + indices[I] = ntuple(j -> ((I[j] - 1) * psize[j] - (I[j] - 1) * overlap + 1) : + ( I[j] * psize[j] - (I[j] - 1) * overlap + overhang(I, j)), d) + end + MetaGrid{d}(grid, indices, overlap) +end + +Base.size(grid::MetaGrid) = size(grid.grid) + +Base.iterate(grid::MetaGrid) = iterate(grid.grid) +Base.iterate(grid::MetaGrid, state) = iterate(grid.grid, state) + +Base.getindex(grid::MetaGrid, idx...) = getindex(grid.grid, idx...) + +indices(grid::MetaGrid, idx...) = getindex(grid.indices, idx...) + +Base.view(grid::MetaGrid, idx...) = GridView(grid, indices(grid, idx...)) + +parts(grid::MetaGrid) = CartesianIndices(grid.indices) + + +## GridFunction + +abstract type AbstractGridFunction{G,T,d,n} <: AbstractArray{T,1} end + +rangedim(f::AbstractGridFunction{G,T,d,n}) where {G,T,d,n} = n +domaindim(f::AbstractGridFunction{G,T,d}) where {G,T,d} = d + +Base.similar(f::F, ::Type{S}, dims::Int...) where {F<:AbstractGridFunction, S} = + GridFunction(f.grid, Array{S}(undef, length(f))) + + +""" + GridFunction{G<:AbstractGrid,T,d,n,D} + +`n`-dimensional function values of type `T` on a Grid `G` of dimension `d`. + +`D == d + 1` +""" +struct GridFunction{G<:AbstractGrid,T,d,n,A,D} <: AbstractGridFunction{G,T,d,n} + grid::G + data::A + function GridFunction{G,T,d,n,A,D}(grid::G, data::A) where {G,T,d,n,D,A<:AbstractArray{T,D}} + D == d + 1 || throw(ArgumentError) + d == ndims(grid) || throw(ArgumentError) + (size(grid)..., n) == size(data) || throw(ArgumentError("data does not match given size")) + new(grid, data) + end +end +# TODO: check size(data) +#GridFunction(grid::G, data::AbstractArray{T,D}) where {d,G<:AbstractGrid{d},T,D} = +# GridFunction{G,T,d,size(data, d+1),typeof(data),d+1}(grid, data) +function GridFunction(grid::G, data::AbstractArray, ::Val{n}) where {d,G<:AbstractGrid{d}, n} + if length(data) ÷ length(grid) == 1 && n > 1 + data = repeat(data, inner=ntuple(i -> i == d+1 ? n : 1, d+1)) + else + # TODO: check size(data) + data = reshape(data, size(grid)..., n) + end + length(data) ÷ length(grid) == n || throw(ArgumentError("dimensions don't match")) + GridFunction{G,eltype(data),d,n,typeof(data),d+1}(grid, data) +end +function GridFunction(grid::G, data::AbstractArray) where {d,G<:AbstractGrid{d}} + GridFunction(grid, data, Val(length(data) ÷ length(grid))) +end +function GridFunction(f::Function, n::Int, grid::G) where {G<:AbstractGrid} + data = Array{Float64,ndims(grid)+1}(undef, size(grid)..., n) + for I in CartesianIndices(grid) + data[Tuple(I)..., :] .= f(grid[I]) + end + GridFunction(grid, data) +end +function GridFunction(grid::AbstractGrid, ::UndefInitializer, ::Val{n} = Val(1)) where n + data = Array{Float64}(undef, size(grid)..., 1) + GridFunction{typeof(grid), eltype(data), ndims(grid), n, typeof(data), ndims(grid) + 1}(grid, data) +end + +#Base.show(io::IO, grid::GridFunction) = +# print("GridFunction on $(length(grid)) grid points") + +Base.size(f::GridFunction) = (length(f.data),) +#Base.axes(f::GridFunction) = (Base.OneTo(length(f.data)),) +Base.getindex(f::GridFunction, idx::Int) = getindex(f.data, idx) +Base.setindex!(f::GridFunction, v, idx::Int) = setindex!(f.data, v, idx) + +## TODO: using this prints #undef for display(f) +#Base.getindex(f::GridFunction, idx...) = getindex(f.data, idx...) +#Base.setindex!(f::GridFunction, v, idx...) = setindex!(f.data, v, idx...) + +#Base.IndexStyle(::Type{<:GridFunction}) = IndexLinear() + +#component(f::GridFunction, k::Integer) = view(reshape(f.data, + +Base.view(f::AbstractGridFunction{G}, idx...) where G<:MetaGrid = + GridFunction(GridView(f.grid, indices(f.grid, idx...)), view(f.data, indices(f.grid, idx...)..., :)) + +grid(f::GridFunction) = f.grid + + +Base.:*(a::Number, b::GridFunction) = GridFunction(b.grid, a * b.data, Val(rangedim(b))) + + + + +## Operations + +function fdfilter(f::GridFunction{G,T,d,n}, kern) where {G,T,d,n} + A = zeros(axes(f.data)) + for k = 1:n + vidx = ntuple(i -> i <= d ? Colon() : k, d + 1) + imfilter!(view(A, vidx...), f.data[vidx...], kern) + end + GridFunction(f.grid, A) +end + +derivate(f::GridFunction, dir::Integer; fd = FDiff.forward) = fdfilter(f, _fdkern(fd, ndims(f.grid), dir)) + + +# currently gradient and divergence are specific implementations (as needed for +# chambolles algorithm), other choices may be reasonable + +function gradient(f::GridFunction{G,T,d,1}; fd=FDiff.forward) where {G,T,d} + #A = Array{T,d+1}(undef, (size(f.grid)..., d)) + A = zeros(size(f.grid)..., d) + for k = 1:d + vidx = ntuple(i -> i <= d ? Colon() : k, d + 1) + vidx2 = ntuple(i -> i <= d ? Colon() : 1, d + 1) + imfilter!(view(A, vidx...), f.data[vidx2...], bidx -> trim_kernel(_fdkern(fd, ndims(f.grid), k), bidx)) + # FIXME: hackish, only for forward fd + selectdim(selectdim(A, d+1, k), k, size(f.grid, k)) .= 0 + end + GridFunction(f.grid, A) +end + +function divergence(f::GridFunction{G,T,d}; fd=FDiff.backward) where {d,G<:AbstractGrid{d},T} + A = zeros(size(f.grid)..., 1) + for k = 1:d + vidx = ntuple(i -> i <= d ? Colon() : 1, d + 1) + vidx2 = ntuple(i -> i <= d ? Colon() : k, d + 1) + imfilter!(view(A, vidx...), f.data[vidx2...], bidx -> trim_kernel(_fdkern(fd, ndims(f.grid), k), bidx)) + end + GridFunction(f.grid, A) +end + +function pw_norm(f::GridFunction) + d = domaindim(f) + GridFunction(f.grid, sqrt.(sum(f.data.^2, dims=d+1)), Val(1)) +end + +function my_norm(f::GridFunction) + d = domaindim(f) + GridFunction(f.grid, sqrt.(sum(f.data.^2, dims=d+1)), Val(rangedim(f))) +end + +function pwmul(f::GridFunction{G,T,d,1}, g::GridFunction{G,T,d}) where {G,T,d} + # TODO: maybe comprehension? + data = similar(g.data) + for I in CartesianIndices(f.grid) + data[Tuple(I)..., :] = f.data[Tuple(I)..., 1] * g.data[Tuple(I)..., :] + end + GridFunction(f.grid, data, Val(rangedim(g))) +end + + + + +""" + neuman_kernel(kernel, bidx) + +Modify `kernel` to be usable on a boundary `bixd` in a neumannn boundary setting. + +Ghost nodes are eliminated through central finite differences. Currently only +the basic 5-star Laplace is supported. +""" +function neumann_kernel(kernel, bidx) + out = copy(kernel) + for (i, bi) in enumerate(bidx) + colrange = (i == j ? Colon() : 0 for j in 1:ndims(kernel)) + out[colrange...] .+= bi * FDiff.forward + end + out /= 2 ^ count(!iszero, bidx) + trim_kernel(out, bidx) +end + +function divergence(grid::Grid{d}, field) where d + field = reshape(field, (size(grid)..., ndims(grid))) + size(field, d+1) == d || throw(ArgumentError("need a d-vectorfield on a d-grid")) + dst = zeros(size(field)) + for k in 1:d + kern = FDiff.dirfd(FDiff.backward, d, k) + imfilter!(selectdim(dst, d+1, k), selectdim(field, d+1, k), bidx -> trim_kernel(kern, bidx)) + end + out = dropdims(sum(dst, dims=d+1), dims=d+1) + out[:] +end + +function gradient(grid::Grid, img::AbstractArray) + img = reshape(img, size(grid)) + d = ndims(img) + dst = zeros(size(img)..., d) + for k in 1:d + kern = FDiff.dirfd(FDiff.forward, d, k) + imfilter!(selectdim(dst, d+1, k), img, bidx -> trim_kernel(kern, bidx)) + # FIXME: hack + selectdim(selectdim(dst, d+1, k), k, size(grid, k)) .= 0 + end + reshape(dst, length(grid) * ndims(grid)) +end + +function my_norm(grid::Grid, img::AbstractArray) + img = reshape(img, (size(grid)..., ndims(grid))) + d = ndims(grid) + + out = sqrt.(sum(img.^2, dims=d+1)) + out = repeat(out, inner=ntuple(i -> i == d+1 ? size(img, d+1) : 1, d+1)) + + out[:] +end + + +function apply(kern, flatimg, size) + img = reshape(flatimg, size) + out = imfilter(kern, img) + reshape(out, prod(size)) +end + +function solve(pde, shape::NTuple{d, Integer}) where d + ndims(pde.stencil) == d || throw("dimension mismatch") + length(pde.f) == prod(shape) || throw("bounds mismatch") + + A = LinearMap{Float64}(u -> apply(pde.stencil, u, shape), prod(shape), issymmetric=true, isposdef=true) + + u = zeros(prod(shape)) + cg!(u, A, pde.f) + return u +end + +end + + +#module FD +# +#using OffsetArrays +#include("filtering.jl") +# +# +#"FD-Kernels" +#module Kernels +# +#using OffsetArrays +# +#const forward = OffsetVector([-1, 1], 0:1) +#const backward = OffsetVector([-1, 1], -1:0) +#const central = OffsetVector([-1, 0, 1], -1:1) +# +#end +# +#using .Filtering +#using .Kernels +# +# +# +### Implementation +# +# +### Grid +# +## aka `StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}` +#const FDStepRange = typeof(0:0.1:1) +# +#struct Grid{d} +# domain::NTuple{d, FDStepRange} +#end +# +#Grid(domain::FDStepRange...) = Grid(domain) +#Grid(ndims::Integer, range::FDStepRange) = Grid(ntuple(i -> range, ndims)) +# +#Base.show(io::IO, grid::Grid) = +# print("FDGrid from $(first.(grid.domain)) to $(last.(grid.domain)) ", +# "with spacing $(step.(grid.domain))") +# +## this is not type-stable? +##Base.size(grid::Grid) = getproperty.(grid.domain, :len) +#Base.size(grid::Grid) = ntuple(i -> grid.domain[i].len, ndims(grid)) +#Base.getindex(grid::Grid, idx...) = SVector(getindex.(grid.domain, idx)) +# +#Base.ndims(grid::Grid{d}) where d = d +#Base.axes(grid::Grid) = Base.OneTo.(size(grid)) +#Base.length(grid::Grid) = prod(size(grid)) +# +## TODO: there might be a better way to wrap that product iterator +#Base.iterate(grid::Grid) = iterate(Iterators.product(grid.domain...)) +#Base.iterate(grid::Grid, state) = iterate(Iterators.product(grid.domain...), state) +# +# +### GridFunction +# +#struct GridFunction{T,d} <: AbstractArray{T,1} +# grid::Grid{d} +# data::Array{T,1} +#end +# +#Base.show(io::IO, grid::GridFunction) = +# print("GridFunction on $(length(grid)) grid points") +# +#Base.size(f::GridFunction) = size(f.data) +#Base.getindex(f::GridFunction, idx...) = getindex(f.data, idx...) +#Base.setindex!(f::GridFunction, v, idx...) = setindex!(f.data, v, idx...) +# +#Base.IndexStyle(::Type{<:GridFunction}) = IndexLinear() +#Base.similar(f::GridFunction, ::Type{S}, size::Int...) where S = +# GridFunction(f.grid, Array{S}(undef, size)) +# +### Operations +# +#function fdfilter(f::GridFunction, kern) +# A = imfilter(reshape(f.data, size(f.grid)), kern) +# GridFunction(f.grid, A[:]) +#end +# +#derivate(f::GridFunction, dir::Integer; fd = FDiff.forward) = fdfilter(f, _fdkern(fd, ndims(f.grid), dir)) +# +##gradient(f::GridFunction) = GridFunction(f.grid, +# +# +#""" +# neuman_kernel(kernel, bidx) +# +#Modify `kernel` to be usable on a boundary `bixd` in a neumannn boundary setting. +# +#Ghost nodes are eliminated through central finite differences. Currently only +#the basic 5-star Laplace is supported. +#""" +#function neumann_kernel(kernel, bidx) +# out = copy(kernel) +# for (i, bi) in enumerate(bidx) +# colrange = (i == j ? Colon() : 0 for j in 1:ndims(kernel)) +# out[colrange...] .+= bi * FDiff.forward +# end +# out /= 2 ^ count(!iszero, bidx) +# trim_kernel(out, bidx) +#end +# +#function divergence(grid::Grid{d}, field) where d +# field = reshape(field, (size(grid)..., ndims(grid))) +# size(field, d+1) == d || throw(ArgumentError("need a d-vectorfield on a d-grid")) +# dst = zeros(size(field)) +# for k in 1:d +# kern = FDiff.dirfd(FDiff.backward, d, k) +# imfilter!(selectdim(dst, d+1, k), selectdim(field, d+1, k), kern) +# end +# out = dropdims(sum(dst, dims=d+1), dims=d+1) +# reshape(out, length(grid)) +#end +# +#function gradient(grid::Grid, img::AbstractArray) +# img = reshape(img, size(grid)) +# d = ndims(img) +# dst = zeros(size(img)..., d) +# for k in 1:d +# kern = FDiff.dirfd(FDiff.forward, d, k) +# imfilter!(selectdim(dst, d+1, k), img, kern) +# end +# reshape(dst, length(grid) * ndims(grid)) +#end +# +#end diff --git a/FD/src/filtering.jl b/FD/src/filtering.jl new file mode 100644 index 0000000..11fa7b2 --- /dev/null +++ b/FD/src/filtering.jl @@ -0,0 +1,110 @@ +module Filtering + +export imfilter, imfilter!, trim_kernel + +@inline function _shift_range(range, shift::Integer) + Base.UnitRange(first(range)+shift : last(range)+shift) +end + +## TODO: allow border to be tuple +#function _innershift_indices(a::Array{T, d}, shift::NTuple{d, Integer}, border = abs(maximum(shift))) where {T, d} +# abs(maximum(shift)) <= border || throw("cannot shift by $shift with border $border") +# range(i) = (1 + shift[i] + border):(lastindex(a, i) + shift[i] - border) +# ntuple(range, Val(ndims(a))) +#end +# +#function innershift_view(a::Array{T, d}, shift::NTuple{d, Integer} = ntuple(i -> 0, d), border = abs(maximum(shift))) where {T, d} +# view(a, _innershift_indices(a, shift, border)...) +#end + +@inline function _bidx_to_range(range::AbstractUnitRange, bidx::Integer) + if bidx > 0 + return last(range):last(range) + elseif bidx < 0 + return first(range):first(range) + else + return first(range)+1:last(range)-1 + end +end + + +# we return Base.Slice since that is what `OffsetArray` uses for `axes()` +function _trim_kernel_axes(kaxes, bidx) + ntuple(Val(length(kaxes))) do i + if bidx[i] < 0 + return Base.Slice(0:last(kaxes[i])) + elseif bidx[i] > 0 + return Base.Slice(first(kaxes[i]):0) + else + return Base.Slice(kaxes[i]) + end + end +end + +""" + trim_kernel(kernel, bidx) + +Trim `kernel` so that the resulting kernel is usable on the boundary +indicated by `bidx` +""" +function trim_kernel(kernel, bidx) + dst_axes = _trim_kernel_axes(axes(kernel), bidx) + out = similar(kernel, dst_axes) + range = CartesianIndices(out) + copyto!(out, range, kernel, range) +end + + +""" + imfilter!(dst, img, kern, slice=:inner) + +Filters `img` applying `kern` and write to `dst`. `dst` is added to, so you +should initialize with zeros. +""" +# TODO: weirdly Base.Slice instead of Base.UnitRange leads to axes that are not +# compatible when broadcasting, figure out why! +function imfilter!(dst, img, kern, slice=_bidx_to_range.(axes(img), ntuple(i->0, ndims(img)))) + ndims(dst) == ndims(img) == ndims(kern) == length(slice) || + throw(ArgumentError("dst, image, kernel or slice dimensions disagree ($(ndims(dst)) vs $(ndims(img)) vs $(ndims(kern)) vs $(length(slice)))")) + axes(dst) == axes(img) || throw(ArgumentError("output and input image axes disagree")) + all(size(kern) .<= size(img)) || throw(ArgumentError("kernel bigger than image")) + last.(slice) .+ last.(axes(kern)) <= last.(axes(img)) || first.(slice) .+ first.(axes(kern)) >= first.(axes(img)) || + throw(ArgumentError("kern and/or slice out of bounds for image")) + + dstslice = view(dst, UnitRange.(slice)...) + for i in CartesianIndices(kern) + iszero(kern[i]) && continue + dstslice .+= kern[i] .* view(img, (_shift_range.(slice, Tuple(i)))...) + end + dst +end + +function imfilter!(dst, img, kernelf::Function) + for bidx in Iterators.product(ntuple(i -> -1:1, ndims(img))...) + range = _bidx_to_range.(axes(img), bidx) + imfilter!(dst, img, kernelf(bidx), range) + end + dst +end + +""" + imfilter(img, kernelf) + +Filter `img` using a kernel function `kernelf(bidx) = kernel` specifying a +kernel for each type of node. +""" +# need zeros here, because `imfilter!` acts additively +imfilter(img, kernelf::Function) = imfilter!(zeros(size(img)), img, kernelf) + + +""" + imfilter(img, kern) + +Filter `img` by `kern` using correlation. +Boundary is zeroed out. +""" +imfilter(img, kern) = imfilter(img, bidx -> trim_kernel(kern, bidx)) +# TODO: use keyword arguments +imfilter_inner(img, kern) = imfilter(img, bidx -> all(bidx .== 0) ? kern : zeros(ntuple(i -> 0, ndims(img)))) + +end diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 0000000..daf8153 --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,224 @@ +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[ColorTypes]] +deps = ["FixedPointNumbers", "Random", "Test"] +git-tree-sha1 = "0e3209ba7418aed732e5c3818076b4400ee36c08" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.7.4" + +[[Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "InteractiveUtils", "Pkg", "Printf", "Reexport", "Test"] +git-tree-sha1 = "8c89e0a9a583954eae3efcf6a531e51c02b38cee" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.9.4" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "ae262fa91da6a74e8937add6b613f58cd56cdad4" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "1.1.0" + +[[Contour]] +deps = ["LinearAlgebra", "StaticArrays", "Test"] +git-tree-sha1 = "b974e164358fea753ef853ce7bad97afec15bb80" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.5.1" + +[[DataStructures]] +deps = ["InteractiveUtils", "REPL", "Random", "Serialization", "Test"] +git-tree-sha1 = "5162c360c52e9265e0bfafb71415c31f746f7e04" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.12.0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[FD]] +deps = ["OffsetArrays", "StaticArrays"] +path = "FD" +uuid = "edd53c04-b66a-11e8-3f8d-297d8a5291a5" +version = "0.1.0" + +[[FixedPointNumbers]] +deps = ["Pkg", "Test"] +git-tree-sha1 = "b8045033701c3b10bf2324d7203404be7aef88ba" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.5.3" + +[[GR]] +deps = ["Base64", "DelimitedFiles", "Pkg", "Random", "Serialization", "Sockets", "Test"] +git-tree-sha1 = "0437921cbf8ee555ab7a2a3115c1d0907289e816" +uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" +version = "0.34.1" + +[[InteractiveUtils]] +deps = ["LinearAlgebra", "Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JSON]] +deps = ["Dates", "Distributed", "Mmap", "Pkg", "Sockets", "Test", "Unicode"] +git-tree-sha1 = "fec8e4d433072731466d37ed0061b3ba7f70eeb9" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.19.0" + +[[LibGit2]] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[Measures]] +deps = ["Pkg", "Test"] +git-tree-sha1 = "ddfd6d13e330beacdde2c80de27c1c671945e7d9" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.0" + +[[Missings]] +deps = ["Dates", "InteractiveUtils", "SparseArrays", "Test"] +git-tree-sha1 = "196528fa1df03df435025f52f9c1ff8356f94738" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "0.3.0" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[NaNMath]] +deps = ["Compat"] +git-tree-sha1 = "ce3b85e484a5d4c71dd5316215069311135fa9f2" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "0.3.2" + +[[OffsetArrays]] +deps = ["DelimitedFiles", "Test"] +git-tree-sha1 = "f8cd140824f74f2948ff8660bc2292e16d29c1b7" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "0.8.1" + +[[Pkg]] +deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[PlotThemes]] +deps = ["PlotUtils", "Requires", "Test"] +git-tree-sha1 = "f3afd2d58e1f6ac9be2cea46e4a9083ccc1d990b" +uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" +version = "0.3.0" + +[[PlotUtils]] +deps = ["Colors", "Dates", "Printf", "Random", "Reexport", "Test"] +git-tree-sha1 = "78553a920c4869d20742ba66193e3692369086ec" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "0.5.4" + +[[Plots]] +deps = ["Base64", "Contour", "Dates", "FixedPointNumbers", "GR", "JSON", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "Printf", "Random", "RecipesBase", "Reexport", "Requires", "Showoff", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "Test", "UUIDs"] +git-tree-sha1 = "fa4f80b7e2173b8605353bdadf10d3ee8569efc6" +uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +version = "0.20.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[RecipesBase]] +deps = ["Pkg", "Random", "Test"] +git-tree-sha1 = "0b3cb370ee4dc00f47f1193101600949f3dcf884" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "0.6.0" + +[[Reexport]] +deps = ["Pkg"] +git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "0.2.0" + +[[Requires]] +deps = ["Test"] +git-tree-sha1 = "f6fbf4ba64d295e146e49e021207993b6b48c7d1" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "0.5.2" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Showoff]] +deps = ["Compat", "Pkg"] +git-tree-sha1 = "276b24f3ace98bec911be7ff2928d497dc759085" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "0.2.1" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SortingAlgorithms]] +deps = ["DataStructures", "Random", "Test"] +git-tree-sha1 = "03f5898c9959f8115e30bc7226ada7d0df554ddd" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "0.3.1" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[StaticArrays]] +deps = ["InteractiveUtils", "LinearAlgebra", "Random", "Statistics", "Test"] +git-tree-sha1 = "d432c79bef174a830304f8601427a4357dfdbfb7" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "0.8.3" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[StatsBase]] +deps = ["DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "Test"] +git-tree-sha1 = "723193a13e8078cec6dcd0b8fe245c8bfd81690e" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.25.0" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[UUIDs]] +deps = ["Random"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..9add2e9 --- /dev/null +++ b/Project.toml @@ -0,0 +1,4 @@ +[deps] +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" +FD = "edd53c04-b66a-11e8-3f8d-297d8a5291a5" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/chambollefp.jl b/chambollefp.jl index 5e0274c..70e9833 100644 --- a/chambollefp.jl +++ b/chambollefp.jl @@ -1,5 +1,3 @@ -include("fd.jl") - using StaticArrays using SparseArrays using OffsetArrays @@ -7,16 +5,18 @@ using LinearAlgebra using Colors using Plots +using FD + imf(f, grid) = [f(x) for x in grid] imshow(grid, img) = plot(Gray.((reshape(float.(img), size(grid)) .- minimum(float.(img))) / (maximum(float.(img)) - minimum(float.(img))) ), show=true) function _fpiter(grid, p, f, A, α, β, τ) - x = (A'*A + β*I) \ (FDM.divergence(grid, p) .+ A'*f ./ α) - q = FDM.gradient(grid, x) + x = (A'*A + β*I) \ (FD.divergence(grid, p) .+ A'*f ./ α) + q = FD.gradient(grid, x) - p_new = (p .+ τ .* q) ./ (1 .+ τ .* FDM.my_norm(grid, q)) + p_new = (p .+ τ .* q) ./ (1 .+ τ .* FD.my_norm(grid, q)) reserr = maximum(sum((p_new .- p).^2, dims=ndims(p))) (p_new, reserr) @@ -32,7 +32,7 @@ function fpalg(grid, f, A; α, β, τ = 1/8, ϵ = 1e-4) #sleep(0.1 / k) (p, reserr) = _fpiter(grid, p, f, A, α, β, τ) - uimg = (A'*A + β*I) \ (α * FDM.divergence(grid, p) .+ A'*f) + uimg = (A'*A + β*I) \ (α * FD.divergence(grid, p) .+ A'*f) imshow(grid, uimg) println("[$(lpad(k, 4))] res = $(round(reserr, digits=5))") @@ -43,8 +43,8 @@ end #st = OffsetArray([0 -1 0; -1 4 -1; 0 -1 0], (-2, -2)) -grid = FDM.Grid(0:0.01:1, 0:0.01:1) -#grid = FDM.Grid(0:0.5:1, 0:0.5:1) +grid = FD.Grid(0:0.01:1, 0:0.01:1) +#grid = FD.Grid(0:0.5:1, 0:0.5:1) n = 1 #A = spdiagm(collect(k => fill(1/(2n+1), length(grid)-abs(k)) for k in -n:n)...) @@ -68,9 +68,9 @@ img = fpalg(grid, f, A, α=1, β=1, τ=1/100, ϵ=1e-7) #A = Array{Float64}(undef, (length(grid), length(grid))) #for i in 1:length(grid) # ei = float.(1:length(grid) .== i) -# #A[:, i] = FDM.divergence(grid, FDM.gradient(grid, ei)) -# #A[:, i] = FDM.divergence(grid, append!(ei, zeros(length(grid)))) -# A[:, i] = reshape(reshape(FDM.gradient(grid, ei), size(grid)..., 2)[:,:,2], length(grid)) +# #A[:, i] = FD.divergence(grid, FD.gradient(grid, ei)) +# #A[:, i] = FD.divergence(grid, append!(ei, zeros(length(grid)))) +# A[:, i] = reshape(reshape(FD.gradient(grid, ei), size(grid)..., 2)[:,:,2], length(grid)) #end #A diff --git a/chambollefp_dd.jl b/chambollefp_dd.jl index e6de6a8..7a942f9 100644 --- a/chambollefp_dd.jl +++ b/chambollefp_dd.jl @@ -1,103 +1,143 @@ -include("fd.jl") +#include("fd.jl") using LinearAlgebra using SparseArrays -import .FDM +import FD + + +import Colors: Gray +import Plots: plot + +# TODO: add clamp option +my_plot(img::FD.AbstractGridFunction{G,T,d,1}) where {G,T,d} = + plot(reshape(Gray.((img .- minimum(img)) ./ (maximum(img) - minimum(img))), size(img.grid)), show=true) + "partition of unity function" function theta(grid, idx...) - vidx = FDM.indices(grid, idx...) - - slices = Vector{Vector{Float64}}(undef, length(idx)) - for (k, kglobrange) in enumerate(vidx) - krange = Base.OneTo(length(kglobrange)) - slices[k] = min.(krange, reverse(krange), grid.overlap) ./ grid.overlap - # FIXME: dirty hack - if first(kglobrange) == 1 - slices[k][1:grid.overlap] .= 1.0 - end - if last(kglobrange) == size(grid, k) - slices[k][end-grid.overlap:end] .= 1.0 - end + function h(r, rp, i) + i <= first(r) + grid.overlap && return 1.0 + i >= last(r) - grid.overlap && return 1.0 + (min(i - first(rp), last(rp) - i, grid.overlap) + 1) / (grid.overlap + 1) end - #dst = Array{Float64}(undef, size(grid)...) + vidx = FD.indices(grid, idx...) + ctheta = map(x -> [h(x[1], x[2], i) for i in x[2]], zip(axes(grid), vidx)) + dst = zeros(size(grid)) for (I,J) in zip(CartesianIndices(vidx), CartesianIndices(length.(vidx))) - dst[I] = prod(getindex.(slices, Tuple(J))) + dst[I] = prod(getindex.(ctheta, Tuple(J))) end - dst - - FDM.GridFunction(grid, dst) + FD.GridFunction(grid, dst) end function p2u(grid, p, A, f; α, β) - FDM.GridFunction(grid, (A'*A + β*I) \ (α * FDM.divergence(p).data[:] .+ A'*f.data[:])) + FD.GridFunction(grid, (A'*A + β*I) \ (α .* FD.divergence(p).data[:] .+ A'*f.data[:])) end # TODO: investigate matrix multiplication performance with GridFunctions -function fpiter(grid, p, f, A, α, β, τ) - x = FDM.GridFunction(grid, (A'*A + β*I) \ (FDM.divergence(p).data[:] .+ A'*f.data[:] ./ α)) - q = FDM.gradient(x) +function fpiter(grid, p, w, A, α, β, τ) + x = FD.GridFunction(grid, (A'*A + β*I) \ (FD.divergence(p).data[:] .+ w ./ α)) + q = FD.gradient(x) #display(x.data) - #FDM.my_plot(x) + #FD.my_plot(x) #checknan(q) - p_new = FDM.GridFunction(grid, (p .+ τ .* q) ./ (1 .+ τ .* FDM.my_norm(q))) + p_new = FD.GridFunction(grid, FD.pwmul(FD.GridFunction(grid, 1 ./ (1 .+ τ .* FD.pw_norm(q))), FD.GridFunction(grid, p .+ τ .* q))) reserr = maximum(sum((p_new .- p).^2, dims=ndims(p))) (p_new, reserr) end -function fpalg(grid, f, A; α, β, τ = 1/8, ϵ = 1e-4) +function fpalg(grid, w, A; α, β, τ = 1/8, ϵ = 1e-4) local uimg - p = FDM.GridFunction(x -> 0, ndims(grid), grid) + p = FD.GridFunction(x -> 0, ndims(grid), grid) reserr = 1 k = 0 while reserr > ϵ && k < 30e1 k += 1 - #sleep(0.1 / k) - (p, reserr) = fpiter(grid, p, f, A, α, β, τ) + (p, reserr) = fpiter(grid, p, w, A, α, β, τ) - u = p2u(grid, p, A, f, α=α, β=β) - - #FDM.my_plot(uimg) - println("[$(lpad(k, 4))] res = $(round(reserr, digits=5))") - println("min/max: $(minimum(uimg)) / $(maximum(uimg))") - println("min/max: $(minimum(p)) / $(maximum(p))") + #FD.my_plot(uimg) + #println("[$(lpad(k, 4))] res = $(round(reserr, digits=5))") + #println("min/max: $(minimum(uimg)) / $(maximum(uimg))") + #println("min/max: $(minimum(p)) / $(maximum(p))") end p #uimg end -grid = FDM.MetaGrid((0:0.01:1, 0:0.01:1), (3, 3), 5) +grid = FD.MetaGrid((0:0.01:1, 0:0.01:1), (3, 3), 5) A = I #A = spdiagm(0 => ones(length(grid)), 1 => ones(length(grid)-1) ) #f = A * f -f = FDM.GridFunction(x -> float(norm(x .- [0.5, 0.5]) < 0.4), 1, grid) -g = FDM.GridFunction(x -> norm(x), 2, grid) +f = FD.GridFunction(x -> float(norm(x .- [0.5, 0.5]) < 0.4), 1, grid) +g = FD.GridFunction(x -> norm(x), 2, grid) + +α=1 +β=1 +τ=1/100 +ϵ=1e-7 +ρ=0.25 + +p = FD.GridFunction(x -> 0, 2, grid) + +function dditer(grid, p, A, f; α, β, τ, ϵ, ρ) + p .= (1-ρ) .* p + for part in FD.parts(grid) + gridv = view(grid, Tuple(part)...) + fv = view(f, Tuple(part)...) + #p.data .= 0 + pv = view(p, Tuple(part)...) -for part in parts(grid) - gridv = view(grid, part...) - fv = view(f, part...) - fpalg(gridv, fv, A, α=1, β=1, τ=1/100, ϵ=1e-7) + θ = theta(grid, Tuple(part)...) + θv = view(θ, Tuple(part)...) + w = FD.GridFunction(grid, A' * f.data[:] - FD.divergence(FD.pwmul(FD.GridFunction(grid, 1 .- θ), p))) + wv = view(w, Tuple(part)...) + + #wv = A' * fv + + pv .+= ρ .* fpalg(gridv, wv, A, α=α*θv, β=β, τ=τ, ϵ=ϵ) + + #u = p2u(grid, p, A, f, α=α*θ, β=β) + u = p2u(grid, p, A, f, α=α, β=β) + uv = view(u, Tuple(part)...) + + my_plot(θv) + #sleep(1) + #my_plot(FD.pw_norm(pv)) + @show maximum(pv), minimum(pv) + #sleep(1) + #my_plot(θv) + return + end end +dditer(grid, p, A, f, α=α, β=β, τ=τ, ϵ=ϵ, ρ=ρ) + +#for n = 1:10 +# println("iteration $n") +# dditer(grid, p, A, f, α=α, β=β, τ=τ, ϵ=ϵ, ρ=ρ) +#end + + + + #gridv = view(grid, 1, 1) #fv = view(f, 1, 1) #g = view(g, 1, 1) -FDM.my_plot(f) +#FD.my_plot(f) -#FDM.plot(f) +#FD.plot(f) diff --git a/fd.jl b/fd.jl index 4319963..19d3c07 100644 --- a/fd.jl +++ b/fd.jl @@ -210,10 +210,13 @@ Base.setindex!(f::GridFunction, v, idx::Int) = setindex!(f.data, v, idx) #component(f::GridFunction, k::Integer) = view(reshape(f.data, Base.view(f::AbstractGridFunction{G}, idx...) where G<:MetaGrid = - GridFunction(GridView(f.grid, indices(f.grid, idx...)), view(f.data, indices(f.grid, idx...)...)) + GridFunction(GridView(f.grid, indices(f.grid, idx...)), view(f.data, indices(f.grid, idx...)..., :)) +grid(f::GridFunction) = f.grid +Base.:*(a::Number, b::GridFunction) = GridFunction(b.grid, a * b.data, Val(rangedim(b))) + import Colors: Gray @@ -263,11 +266,25 @@ function divergence(f::GridFunction{G,T,d}; fd=FD.backward) where {d,G<:Abstract GridFunction(f.grid, A) end +function pw_norm(f::GridFunction) + d = domaindim(f) + GridFunction(f.grid, sqrt.(sum(f.data.^2, dims=d+1)), Val(1)) +end + function my_norm(f::GridFunction) d = domaindim(f) GridFunction(f.grid, sqrt.(sum(f.data.^2, dims=d+1)), Val(rangedim(f))) end +function pwmul(f::GridFunction{G,T,d,1}, g::GridFunction{G,T,d}) where {G,T,d} + # TODO: maybe comprehension? + data = similar(g.data) + for I in CartesianIndices(f.grid) + data[Tuple(I)..., :] = f.data[Tuple(I)..., 1] * g.data[Tuple(I)..., :] + end + GridFunction(f.grid, data, Val(rangedim(g))) +end + -- GitLab