Skip to content

Commit 351b311

Browse files
authored
Merge pull request #101 from WIAS-PDELib/feature/high-level-coupling
Add high-level abstract coupling restriction
2 parents 6a62533 + 4de2ce6 commit 351b311

15 files changed

Lines changed: 323 additions & 47 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# CHANGES
22

3+
## v1.9.0
4+
5+
### Added
6+
- It is now possible to create a `CoupledDofsRestriction(unknown, source_region, target_region)` only from abstract data without knowledge of the grid or the FES.
7+
It is assumed that the coupling is given between the source and target region and that these regions are parallel hyperplanes.
8+
- New `SolverConfig` key word argument `:compress_restrictions` (default = `true`) to call a QR decomposition on all restriction matrices to eliminate linearly dependent columns.
9+
- New example `Example313_PeriodicPoisson` to cover corner cases of multiple restrictions at once.
10+
311
## v1.8.0
412

513
### Added

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ExtendableFEM"
22
uuid = "a722555e-65e0-4074-a036-ca7ce79a4aed"
3-
version = "1.8.0"
3+
version = "1.9.0"
44
authors = ["Christian Merdon <merdon@wias-berlin.de>", "Patrick Jaap <patrick.jaap@wias-berlin.de>"]
55

66
[deps]

docs/make.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function make_all(; with_examples::Bool = true, modules = :all, run_examples::Bo
2727
"Example207_AdvectionUpwindDG.jl",
2828
"Example210_LshapeAdaptivePoissonProblem.jl",
2929
"Example211_LshapeAdaptiveEQPoissonProblem.jl",
30-
"Example212_PeriodicBoundary2D.jl",
30+
"Example212_PeriodicElasticity2D.jl",
3131
"Example220_ReactionConvectionDiffusion.jl",
3232
"Example225_ObstacleProblem.jl",
3333
"Example226_Thermoforming.jl",
@@ -51,7 +51,8 @@ function make_all(; with_examples::Bool = true, modules = :all, run_examples::Bo
5151
"Example295_SlidingDroplet.jl",
5252
"Example301_PoissonProblem.jl",
5353
"Example310_DivFreeBasis.jl",
54-
"Example312_PeriodicBoundary3D.jl",
54+
"Example312_PeriodicElasticity3D.jl",
55+
"Example313_PeriodicPoisson.jl",
5556
"Example330_HyperElasticity.jl",
5657
]
5758
end

docs/src/combinedofs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ get_periodic_coupling_matrix
1919

2020
## Example: Periodic Boundary Coupling (extract from Example212)
2121

22-
Suppose you want to enforce periodicity between the left and right boundaries of a 2D domain. You can use `get_periodic_coupling_matrix` to find the corresponding DOFs, and then use `CombineDofs` to couple them in the system. The following code is adapted from [Example212_PeriodicBoundary2D.jl](https://github.com/WIAS-PDELib/ExtendableFEM.jl/blob/main/examples/Example212_PeriodicBoundary2D.jl):
22+
Suppose you want to enforce periodicity between the left and right boundaries of a 2D domain. You can use `get_periodic_coupling_matrix` to find the corresponding DOFs, and then use `CombineDofs` to couple them in the system. The following code is adapted from [Example212_PeriodicElasticity2D.jl](https://github.com/WIAS-PDELib/ExtendableFEM.jl/blob/main/examples/Example212_PeriodicElasticity2D.jl):
2323

2424
```julia
2525
# Define a function to map points on the left to the right boundary

docs/src/examples_intro.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ The examples in this package are designed to be practical, reproducible, and edu
1111

1212
## Running the Examples
1313

14-
To run an example (e.g., `Example212_PeriodicBoundary2D`):
14+
To run an example (e.g., `Example212_PeriodicElasticity2D`):
1515

1616
1. Download the example file (see the source code link at the top of the example page).
1717
2. Ensure all required packages are installed in your Julia environment.
1818
3. In the Julia REPL:
1919

2020
```julia
21-
julia> include("Example212_PeriodicBoundary2D.jl")
22-
julia> Example212_PeriodicBoundary2D.main()
21+
julia> include("Example212_PeriodicElasticity2D.jl")
22+
julia> Example212_PeriodicElasticity2D.main()
2323
```
2424

2525
4. Some examples offer visual output via the optional argument `Plotter = PyPlot` or `Plotter = GLMakie` (provided the package is installed and loaded):
2626

2727
```julia
28-
julia> Example212_PeriodicBoundary2D.main(Plotter = PyPlot)
28+
julia> Example212_PeriodicElasticity2D.main(Plotter = PyPlot)
2929
```

examples/Example212_PeriodicBoundary2D.jl renamed to examples/Example212_PeriodicElasticity2D.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ with periodic boundary coupling along the x-axis.
1111
![](example212.png)
1212
=#
1313

14-
module Example212_PeriodicBoundary2D
14+
module Example212_PeriodicElasticity2D
1515

1616
using ExtendableFEM
1717
using ExtendableGrids
@@ -176,7 +176,7 @@ function main(;
176176
return sol, plt
177177
end
178178

179-
generateplots = ExtendableFEM.default_generateplots(Example212_PeriodicBoundary2D, "example212.png") #hide
179+
generateplots = ExtendableFEM.default_generateplots(Example212_PeriodicElasticity2D, "example212.png") #hide
180180
function runtests() #hide
181181
sol1, _ = main(use_LM_restrictions = false, threads = 1) #hide
182182
@test abs(maximum(view(sol1[1])) - 1.3447465095618172) < 1.0e-3 #hide

examples/Example312_PeriodicBoundary3D.jl renamed to examples/Example312_PeriodicElasticity3D.jl

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ with periodic coupling along the x-axis.
1111
![](example312.png)
1212
=#
1313

14-
module Example312_PeriodicBoundary3D
14+
module Example312_PeriodicElasticity3D
1515

1616
using ExtendableFEM
1717
using ExtendableGrids
@@ -129,8 +129,7 @@ end
129129

130130
function main(;
131131
order = 1,
132-
periodic = true,
133-
use_LM_restrictions = true,
132+
periodic_coupling = :none, # :restriction, :operator, :high_level_restriction
134133
Plotter = nothing,
135134
force = 1.0,
136135
h = 1.0e-4,
@@ -164,16 +163,22 @@ function main(;
164163

165164
assign_operator!(PD, HomogeneousBoundaryData(u; regions = [reg_dirichlet]))
166165

167-
if periodic
166+
if periodic_coupling == :high_level_restriction
167+
168+
# new high-level call
169+
assign_restriction!(PD, CoupledDofsRestriction(u, reg_left, reg_right; parallel = threads > 1, threads))
170+
171+
elseif periodic_coupling != :none
172+
168173
function give_opposite!(y, x)
169174
y .= x
170175
y[1] = width - x[1]
171176
return nothing
172177
end
173178
@showtime coupling_matrix = get_periodic_coupling_matrix(FES, reg_left, reg_right, give_opposite!; parallel = threads > 1, threads)
174-
if use_LM_restrictions
179+
if periodic_coupling == :restriction
175180
assign_restriction!(PD, CoupledDofsRestriction(coupling_matrix))
176-
else
181+
else # :operator
177182
assign_operator!(PD, CombineDofs(u, u, coupling_matrix; kwargs...))
178183
end
179184
end
@@ -189,17 +194,20 @@ function main(;
189194

190195
end
191196

192-
generateplots = ExtendableFEM.default_generateplots(Example312_PeriodicBoundary3D, "example312.png") #hide
197+
generateplots = ExtendableFEM.default_generateplots(Example312_PeriodicElasticity3D, "example312.png") #hide
193198
function runtests() #hide
194-
sol1, _ = main(use_LM_restrictions = false, threads = 1) #hide
199+
sol1, _ = main(periodic_coupling = :operator, threads = 1) #hide
195200
@test abs(maximum(view(sol1[1])) - 1.8004602502175202) < 2.0e-3 #hide
196201

197-
sol2, _ = main(use_LM_restrictions = false, threads = 4) #hide
202+
sol2, _ = main(periodic_coupling = :operator, threads = 4) #hide
198203
@test sol1.entries sol2.entries #hide
199204

200-
sol3, _ = main(use_LM_restrictions = true, threads = 4) #hide
205+
sol3, _ = main(periodic_coupling = :restriction, threads = 4) #hide
201206
@test sol1.entries sol3.entries #hide
202207

208+
sol4, _ = main(periodic_coupling = :high_level_restriction, threads = 4) #hide
209+
@test sol1.entries sol4.entries #hide
210+
203211
return nothing #hide
204212
end #hide
205213

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#=
2+
3+
# 312 : Periodic Poisson 3D
4+
([source code](@__SOURCE_URL__))
5+
6+
This is a simple demonstration and validation of the new restriction based periodic boundary operator.
7+
8+
An unstructured cube grid is coupled along two axes periodically and restricted to non-zero Dirichlet values at the other to faces.
9+
10+
The result is a linear function and the error is measured directly.
11+
12+
Note that the result is independent of the periodic coupling! Therefore the correctness of the
13+
new QR based column compression is covered by this example.
14+
15+
![](example313.png)
16+
=#
17+
18+
module Example313_PeriodicPoisson
19+
20+
using ExtendableFEM
21+
using ExtendableGrids
22+
using SimplexGridFactory
23+
using GridVisualize
24+
using TetGen
25+
using UnicodePlots
26+
using StaticArrays
27+
using LinearAlgebra
28+
using Test #hide
29+
30+
const reg_left = 1
31+
const reg_right = 2
32+
const reg_front = 3
33+
const reg_back = 4
34+
const reg_bottom = 5
35+
const reg_top = 6
36+
37+
38+
function create_grid(; h)
39+
builder = SimplexGridBuilder(; Generator = TetGen)
40+
41+
## bottom points
42+
b00 = point!(builder, 0, 0, 0)
43+
b01 = point!(builder, 0, 1, 0)
44+
b10 = point!(builder, 1, 0, 0)
45+
b11 = point!(builder, 1, 1, 0)
46+
47+
## top points
48+
t00 = point!(builder, 0, 0, 1)
49+
t01 = point!(builder, 0, 1, 1)
50+
t10 = point!(builder, 1, 0, 1)
51+
t11 = point!(builder, 1, 1, 1)
52+
53+
## left face
54+
facetregion!(builder, reg_left)
55+
facet!(builder, b00, b01, t01, t00)
56+
57+
## right face
58+
facetregion!(builder, reg_right)
59+
facet!(builder, b10, b11, t11, t10)
60+
61+
## front face
62+
facetregion!(builder, reg_front)
63+
facet!(builder, b00, b10, t10, t00)
64+
65+
## back face
66+
facetregion!(builder, reg_back)
67+
facet!(builder, b01, b11, t11, t01)
68+
69+
## top face
70+
facetregion!(builder, reg_top)
71+
facet!(builder, t00, t10, t11, t01)
72+
73+
## bottom face
74+
facetregion!(builder, reg_bottom)
75+
facet!(builder, b00, b10, b11, b01)
76+
77+
78+
cellregion!(builder, 1)
79+
maxvolume!(builder, h)
80+
regionpoint!(builder, 0.5, 0.5, 0.5)
81+
82+
return simplexgrid(builder)
83+
end
84+
85+
function main(;
86+
Plotter = nothing,
87+
h = 1.0e-3,
88+
periodic = true,
89+
kwargs...
90+
)
91+
92+
xgrid = create_grid(; h)
93+
94+
FES = FESpace{H1P1{1}}(xgrid)
95+
96+
## problem description
97+
PD = ProblemDescription("Periodic Poisson Problem")
98+
u = Unknown("u"; name = "temperature")
99+
assign_unknown!(PD, u)
100+
101+
assign_operator!(PD, BilinearOperator([grad(u)]; kwargs...))
102+
103+
assign_restriction!(PD, BoundaryDataRestriction(u; value = -1, regions = [reg_bottom]))
104+
assign_restriction!(PD, BoundaryDataRestriction(u; value = +1, regions = [reg_top]))
105+
106+
if periodic
107+
assign_restriction!(PD, CoupledDofsRestriction(u, reg_left, reg_right))
108+
assign_restriction!(PD, CoupledDofsRestriction(u, reg_front, reg_back))
109+
end
110+
111+
## solve
112+
sol, SC = solve(PD, FES; return_config = true, kwargs...)
113+
residual(SC) < 1.0e-10 || error("Residual is not zero!")
114+
115+
function exact_error!(out, u, qpinfo)
116+
# exact solution here is u(x,y,z) = 2z - 1
117+
val = qpinfo.x[3] * 2 - 1
118+
out[1] = (val - u[1])^2
119+
return nothing
120+
end
121+
122+
plt = plot([grid(u), id(u)], sol; Plotter, width = 1200, height = 800, scene3d = :LScene, slice = :y => 0.5)
123+
124+
ErrorIntegrator = ItemIntegrator(exact_error!, [id(u)]; quadorder = 2)
125+
L2error = sqrt(sum(evaluate(ErrorIntegrator, sol)))
126+
127+
@show L2error
128+
129+
return L2error, plt
130+
131+
end
132+
133+
generateplots = ExtendableFEM.default_generateplots(Example313_PeriodicPoisson, "example313.png") #hide
134+
function runtests() #hide
135+
error1, _ = main(periodic = true) #hide
136+
@test error1 < 1.0e-12 #hide
137+
138+
error2, _ = main(periodic = false) #hide
139+
@test error2 < 1.0e-12 #hide
140+
141+
return nothing #hide
142+
end #hide
143+
144+
end # module

src/ExtendableFEM.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ using ExtendableFEMBase: ExtendableFEMBase, AbstractFiniteElement,
4040
norms, unicode_gridplot, unicode_scalarplot,
4141
update_basis!, SymmetricGradient
4242
using ExtendableGrids: ExtendableGrids, AT_NODES, AbstractElementGeometry,
43-
Adjacency, AssemblyType, BEdgeNodes, BFaceFaces,
43+
Adjacency, AssemblyType, BEdgeNodes, BFaceFaces, BFaceNormals,
4444
BFaceNodes, BFaceRegions, CellAssemblyGroups,
4545
CellFaceOrientations, CellFaces, CellGeometries,
4646
CellNodes, CellRegions, Coordinates, EdgeNodes,

src/common_restrictions/boundarydata_restriction.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ function assemble!(R::BoundaryDataRestriction, sol, SC; kwargs...)
3434

3535
## assemble boundary operator (-> computes fixed dofs and vals)
3636
assemble!(nothing, SC.b, sol, R.boundary_operator, SC; kwargs...)
37+
apply_penalties!(
38+
nothing, SC.b, sol, R.boundary_operator, SC;
39+
assemble_matrix = false,
40+
assemble_rhs = false,
41+
assemble_sol = true, # only modify the solution with boundary data
42+
kwargs...
43+
)
44+
3745
fixeddofs = fixed_dofs(R.boundary_operator)
3846
fixedvals = fixed_vals(R.boundary_operator)
3947
nvals = length(fixeddofs)

0 commit comments

Comments
 (0)