From ecebd9bc839083b021234a84827f4908f6ec6c45 Mon Sep 17 00:00:00 2001
From: Stephan Hilb <stephan@ecshi.net>
Date: Sat, 3 Jul 2021 21:58:10 +0200
Subject: [PATCH] implement basic operator assembly

---
 Manifest.toml           |  90 --------------------------------
 Project.toml            |   5 +-
 src/SemiSmoothNewton.jl |   1 +
 src/function.jl         |  68 +++++++++++++++---------
 src/mesh.jl             |   3 ++
 src/operator.jl         | 111 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 162 insertions(+), 116 deletions(-)
 create mode 100644 src/operator.jl

diff --git a/Manifest.toml b/Manifest.toml
index 9a1f950..0a76bde 100644
--- a/Manifest.toml
+++ b/Manifest.toml
@@ -37,22 +37,6 @@ version = "3.31.0"
 deps = ["Artifacts", "Libdl"]
 uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
 
-[[Crayons]]
-git-tree-sha1 = "3f71217b538d7aaee0b69ab47d9b7724ca8afa0d"
-uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
-version = "4.0.4"
-
-[[DataAPI]]
-git-tree-sha1 = "ee400abb2298bd13bfc3df1c412ed228061a2385"
-uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
-version = "1.7.0"
-
-[[DataStructures]]
-deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
-git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677"
-uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
-version = "0.18.9"
-
 [[Dates]]
 deps = ["Printf"]
 uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
@@ -87,24 +71,6 @@ version = "0.8.5"
 deps = ["ArgTools", "LibCURL", "NetworkOptions"]
 uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
 
-[[FastGaussQuadrature]]
-deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"]
-git-tree-sha1 = "5829b25887e53fb6730a9df2ff89ed24baa6abf6"
-uuid = "442a2c76-b920-505d-bb47-c5924d526838"
-version = "0.4.7"
-
-[[Ferrite]]
-deps = ["LinearAlgebra", "Reexport", "SparseArrays", "Tensors", "WriteVTK"]
-path = "/home/stev47/.julia/dev/Ferrite"
-uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992"
-version = "0.3.0"
-
-[[Fewaku]]
-deps = ["FastGaussQuadrature", "ForwardDiff", "LinearAlgebra", "LinearMaps", "StaticArrays", "WriteVTK"]
-path = "/home/stev47/stuff/Fewaku.jl"
-uuid = "eaeb555d-1c71-5d84-9937-0fdf4ae0c3db"
-version = "0.1.0"
-
 [[FillArrays]]
 deps = ["LinearAlgebra", "Random", "SparseArrays"]
 git-tree-sha1 = "31939159aeb8ffad1d4d8ee44d07f8558273120a"
@@ -162,12 +128,6 @@ version = "0.9.0"
 deps = ["Libdl"]
 uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
 
-[[LinearMaps]]
-deps = ["LinearAlgebra", "SparseArrays"]
-git-tree-sha1 = "ebec45cc8cfe3c947b01c2c491e7e1eefba73c4f"
-uuid = "7a12625a-238d-50fd-b39a-03d52299707e"
-version = "3.3.0"
-
 [[LogExpFunctions]]
 deps = ["DocStringExtensions", "LinearAlgebra"]
 git-tree-sha1 = "1ba664552f1ef15325e68dc4c05c3ef8c2d5d885"
@@ -191,12 +151,6 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
 deps = ["Artifacts", "Libdl"]
 uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
 
-[[Missings]]
-deps = ["DataAPI"]
-git-tree-sha1 = "4ea90bd5d3985ae1f9a908bd4500ae88921c5ce7"
-uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
-version = "1.0.0"
-
 [[Mmap]]
 uuid = "a63ad114-7e13-5084-954f-fe012c677804"
 
@@ -217,11 +171,6 @@ git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
 uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
 version = "0.5.5+0"
 
-[[OrderedCollections]]
-git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
-uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
-version = "1.4.1"
-
 [[Pkg]]
 deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
 uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
@@ -244,19 +193,9 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
 deps = ["Serialization"]
 uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
 
-[[Reexport]]
-git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220"
-uuid = "189a3867-3050-52da-a836-e630ba90ab69"
-version = "1.1.0"
-
 [[SHA]]
 uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
 
-[[SIMD]]
-git-tree-sha1 = "9ba33637b24341aba594a2783a502760aa0bff04"
-uuid = "fdea26ae-647d-5447-a871-4b548cad5224"
-version = "3.3.1"
-
 [[Serialization]]
 uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
 
@@ -267,12 +206,6 @@ uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"
 [[Sockets]]
 uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
 
-[[SortingAlgorithms]]
-deps = ["DataStructures"]
-git-tree-sha1 = "2ec1962eba973f383239da22e75218565c390a96"
-uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
-version = "1.0.0"
-
 [[SparseArrays]]
 deps = ["LinearAlgebra", "Random"]
 uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
@@ -293,17 +226,6 @@ version = "1.2.4"
 deps = ["LinearAlgebra", "SparseArrays"]
 uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
 
-[[StatsAPI]]
-git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
-uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
-version = "1.0.0"
-
-[[StatsBase]]
-deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
-git-tree-sha1 = "2f6792d523d7448bbe2fec99eca9218f06cc746d"
-uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
-version = "0.33.8"
-
 [[TOML]]
 deps = ["Dates"]
 uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
@@ -312,12 +234,6 @@ uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
 deps = ["ArgTools", "SHA"]
 uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
 
-[[Tensors]]
-deps = ["ForwardDiff", "LinearAlgebra", "SIMD", "Statistics"]
-git-tree-sha1 = "09a1b69613b0b5014a02a9ab619fa722417e5c3a"
-uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4"
-version = "1.5.0"
-
 [[Test]]
 deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
 uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
@@ -335,12 +251,6 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
 [[Unicode]]
 uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
 
-[[UnicodePlots]]
-deps = ["Crayons", "Dates", "SparseArrays", "StatsBase"]
-git-tree-sha1 = "1a63e6eea76b291378ff9f95801f8b6d96213208"
-uuid = "b8865327-cd53-5732-bb35-84acbb429228"
-version = "1.3.0"
-
 [[WriteVTK]]
 deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "Random", "TranscodingStreams"]
 git-tree-sha1 = "2d559f58ff021c0f0f3d2394d2688a84a84257aa"
diff --git a/Project.toml b/Project.toml
index 146afff..d5f735f 100644
--- a/Project.toml
+++ b/Project.toml
@@ -4,9 +4,8 @@ authors = ["Stephan Hilb <stephan@ecshi.net>"]
 version = "0.1.0"
 
 [deps]
-Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992"
-Fewaku = "eaeb555d-1c71-5d84-9937-0fdf4ae0c3db"
+ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
+LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
 SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
 Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
-UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"
 WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192"
diff --git a/src/SemiSmoothNewton.jl b/src/SemiSmoothNewton.jl
index 6be98b4..47b4137 100644
--- a/src/SemiSmoothNewton.jl
+++ b/src/SemiSmoothNewton.jl
@@ -3,6 +3,7 @@ module SemiSmoothNewton
 include("image.jl")
 include("mesh.jl")
 include("function.jl")
+include("operator.jl")
 
 function clip_line(r0, r1, l0, l1)
     d_l = -(l1[1] - l0[1])
diff --git a/src/function.jl b/src/function.jl
index dfc14eb..7e0e751 100644
--- a/src/function.jl
+++ b/src/function.jl
@@ -6,35 +6,21 @@ struct P1 end
 struct DP0 end
 struct DP1 end
 
+
 # Fe: finite element type
-struct Mapper{Fe}
-    mesh
+struct Mapper{M,Fe}
+    mesh::M
     ndofs::Int
     map::Array{Int, 2} # (ldof, cid) -> gdof
 end
 
-# dof ordering for vector valued functions:
-#   (ldof, fdims...)
-
-struct FeFunction
-    mapper::Mapper
-    size::NTuple
-    data::Array{Float64, 2} # (rdim, gdof) -> data
-    name::String
-end
-
-function FeFunction(mapper, size=(1,); name=string(gensym("f")))
-    data = Array{Float64, 2}(undef, prod(size), mapper.ndofs)
-    return FeFunction(mapper, size, data, name)
-end
-
 # P1 Elements (1st order Lagrange)
 
 function Mapper{P1}(mesh)
     # special case for P1: dofs correspond to vertices
     ndofs = size(mesh.vertices, 2)
     map = copy(mesh.cells)
-    return Mapper{P1}(mesh, ndofs, map)
+    return Mapper{typeof(mesh),P1}(mesh, ndofs, map)
 end
 
 # DP0 Elements (0th order discontinuous Lagrange)
@@ -43,12 +29,46 @@ function Mapper{DP0}(mesh)
     # special case for DP0: dofs correspond to cells
     ndofs = size(mesh.cells, 2)
     map = collect(reshape(axes(mesh.cells, 2), 1, :))
-    return Mapper{DP0}(mesh, ndofs, map)
+    return Mapper{typeof(mesh),DP0}(mesh, ndofs, map)
+end
+
+# DP1 Elements (1st order discontinuous Lagrange)
+
+function Mapper{DP1}(mesh)
+    # special case for DP0: dofs correspond to cells
+    ndofs = size(mesh.cells, 2)
+    map = collect(reshape(axes(mesh.cells, 2), 1, :))
+    return Mapper{typeof(mesh),DP0}(mesh, ndofs, map)
 end
 
+Base.show(io::IO, ::MIME"text/plain", m::Mapper) =
+    print("$(nameof(typeof(m))), $(element(m)) elements, $(size(m.map, 1)) local dofs each")
+
+element(::Mapper{<:Any,Fe}) where Fe = Fe
+
+
+
+# dof ordering for vector valued functions:
+#   (ldof, fdims...)
+
+struct FeFunction
+    mapper::Mapper
+    size::NTuple
+    data::Array{Float64, 2} # (rdim, gdof) -> data
+    name::String
+end
+
+function FeFunction(mapper, size=(1,); name=string(gensym("f")))
+    data = Array{Float64, 2}(undef, prod(size), mapper.ndofs)
+    return FeFunction(mapper, size, data, name)
+end
+
+Base.show(io::IO, ::MIME"text/plain", f::FeFunction) =
+    print("$(nameof(typeof(f))), size $(f.size) with $(length(f.data)) dofs")
+
 interpolate!(dst::FeFunction, src::Function) = interpolate!(dst, dst.mapper, src)
 
-function interpolate!(dst::FeFunction, mapper::Mapper{P1}, src::Function)
+function interpolate!(dst::FeFunction, mapper::Mapper{<:Any,P1}, src::Function)
     mesh = mapper.mesh
     for cid in axes(mesh.cells, 2)
 	for ldof in axes(mapper.map, 1)
@@ -62,7 +82,7 @@ function interpolate!(dst::FeFunction, mapper::Mapper{P1}, src::Function)
     end
 end
 
-function interpolate!(dst::FeFunction, mapper::Mapper{DP0}, src::Function)
+function interpolate!(dst::FeFunction, mapper::Mapper{<:Any,DP0}, src::Function)
     mesh = mapper.mesh
     for cid in axes(mesh.cells, 2)
 	vertices = mesh.vertices[:, mesh.cells[:, cid]]
@@ -75,10 +95,12 @@ end
 
 append_data!(vtkfile, f::FeFunction) = append_data!(vtkfile, f, f.mapper)
 
-function append_data!(vtkfile, f::FeFunction, ::Mapper{P1})
+function append_data!(vtkfile, f::FeFunction, ::Mapper{<:Any, P1})
     vtk_point_data(vtkfile, f.data, f.name)
 end
 
-function append_data!(vtkfile, f::FeFunction, ::Mapper{DP0})
+function append_data!(vtkfile, f::FeFunction, ::Mapper{<:Any, DP0})
     vtk_cell_data(vtkfile, f.data, f.name)
 end
+
+
diff --git a/src/mesh.jl b/src/mesh.jl
index 4a894ed..867703c 100644
--- a/src/mesh.jl
+++ b/src/mesh.jl
@@ -8,6 +8,9 @@ struct Mesh
     cells::Array{Int, 2}
 end
 
+Base.show(io::IO, ::MIME"text/plain", f::Mesh) =
+    print("$(nameof(typeof(f))), $(size(f.cells, 2)) cells")
+
 function init_grid(n)
     r = LinRange(0., 1., n + 1)
     coords = collect(Iterators.product(r, r))
diff --git a/src/operator.jl b/src/operator.jl
new file mode 100644
index 0000000..cb8c263
--- /dev/null
+++ b/src/operator.jl
@@ -0,0 +1,111 @@
+using SparseArrays: sparse
+using LinearAlgebra: det, dot
+using ForwardDiff: jacobian
+
+export Poisson, L2Projection, init_point!, assemble
+
+abstract type Operator end
+
+struct L2Projection <: Operator
+    f::FeFunction # target
+    # space info
+    mapper::Mapper
+    size::NTuple
+end
+
+L2Projection(f, gspace) = L2Projection(f, g.mapper, g.size)
+
+a(op::L2Projection, xloc, u, du, v, dv) = dot(u, v)
+l(op::L2Projection, xloc, v, dv) = dot(op.f, v)
+
+
+struct Poisson{M,S} <: Operator
+    mapper::M
+    size::S
+end
+
+a(op::Poisson, xloc, u, du, v, dv) = dot(du, dv)
+
+
+
+# degree 2 quadrature on triangle
+quadrature() = [1/6, 1/6, 1/6], [1/6 4/6 1/6; 1/6 1/6 4/6]
+
+# evaluate all (1-dim) local basis functions against x
+# linear 1st order lagrange
+localbasis(x) = [1 - x[1] - x[2], x[1], x[2]]
+
+elmap(mesh, cell, x) =
+    mesh.vertices[:, mesh.cells[:, cell]] * [1 - x[1] - x[2], x[1], x[2]]
+
+
+function assemble(op::Operator)
+    mapper = op.mapper
+    mesh = mapper.mesh
+
+    # precompute basis at quadrature points
+
+    qw, qx = quadrature()
+
+    d = size(qx, 1) # domain dimension
+    nrdims = prod(op.size)
+    nldofs = size(mapper.map, 1) # number of local dofs (not counting range dimensions)
+    nqpts = length(qw) # number of quadrature points
+
+    qphi = zeros(nrdims, nqpts, nrdims, nldofs)
+    dqphi = zeros(nrdims, d, nqpts, nrdims, nldofs)
+    for r in 1:nrdims
+	for k in axes(qx, 2)
+	    qphi[r, k, r, :] .= localbasis(qx[:, k])
+	    dqphi[r, :, k, r, :] .= transpose(jacobian(localbasis, qx[:, k])::Array{Float64,2})
+	end
+    end
+
+    I = Float64[]
+    J = Float64[]
+    V = Float64[]
+    gdof = LinearIndices((nrdims, mapper.ndofs))
+    for cell in axes(mesh.cells, 2)
+	delmap = jacobian(x -> elmap(mesh, cell, x), [0., 0.])::Array{Float64,2} # constant on element
+	delmapinv = inv(delmap) # constant on element
+	intel = abs(det(delmap))
+
+	# local dofs
+	for idim in 1:nrdims, ldofi in 1:nldofs
+	    mdofi = mapper.map[ldofi, cell]
+	    gdofi = gdof[idim, mdofi]
+	    for jdim in 1:nrdims, ldofj in 1:nldofs
+		mdofj = mapper.map[ldofj, cell]
+		gdofj = gdof[jdim, mdofj]
+
+		# quadrature points
+		for k in axes(qx, 2)
+		    xhat = qx[:, k]
+		    phii = reshape(qphi[:, k, idim, ldofi], op.size)
+		    dphii = reshape(dqphi[:, :, k, idim, ldofi] * delmapinv, (op.size..., :))
+		    phij = reshape(qphi[:, k, jdim, ldofj], op.size)
+		    dphij = reshape(dqphi[:, :, k, jdim, ldofj] * delmapinv, (op.size..., :))
+
+		    gdofv = qw[k] * a(op, xhat, phii, dphii, phij, dphij) * intel
+
+		    push!(I, gdofi)
+		    push!(J, gdofj)
+		    push!(V, gdofv)
+
+		    #display(phii)
+		    #display(dphii)
+		    #display(phij)
+		    #display(dphij)
+
+		    #println("$cell, $gdofi, $gdofj, $gdofv")
+		end
+	    end
+	end
+    end
+
+    ngdofs = nrdims * mapper.ndofs
+    A = sparse(I, J, V, ngdofs, ngdofs)
+    return A
+end
+
+
-- 
GitLab