Skip to content

Commit bb591d4

Browse files
authored
Merge pull request #164 from fverdugo/gallery
2 parents 46ae438 + 71cd312 commit bb591d4

File tree

6 files changed

+397
-1
lines changed

6 files changed

+397
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Split format support for `PVector`.
1313
- Helper functions to build partitioned sparse matrices and vectors in split format, `pvector_from_split_blocks` and `psparse_from_split_blocks`.
14+
- Gallery function `linear_elasticity_fem`.
15+
- Function `global_to_owner`.
1416

1517
### Deprecated
1618

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1414
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1515
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1616
SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1"
17+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1718

1819
[compat]
1920
CircularArrays = "1"
@@ -22,6 +23,7 @@ FillArrays = "0.10, 0.11, 0.12, 0.13, 1"
2223
IterativeSolvers = "0.9"
2324
MPI = "0.16, 0.17, 0.18, 0.19, 0.20"
2425
SparseMatricesCSR = "0.6"
26+
StaticArrays = "1"
2527
julia = "1.1"
2628

2729
[extras]

src/PartitionedArrays.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ using SparseMatricesCSR
55
using LinearAlgebra
66
using Printf
77
using CircularArrays
8+
using StaticArrays
89
import MPI
910
import IterativeSolvers
1011
import Distances
@@ -94,6 +95,7 @@ export own_to_local
9495
export ghost_to_local
9596
export local_to_own
9697
export local_to_ghost
98+
export global_to_owner
9799
export replace_ghost
98100
export remove_ghost
99101
export union_ghost
@@ -177,6 +179,9 @@ include("p_timer.jl")
177179

178180
export laplacian_fdm
179181
export laplacian_fem
182+
export linear_elasticity_fem
183+
export node_coorinates_unit_cube
184+
export near_nullspace_linear_elasticity
180185
include("gallery.jl")
181186

182187
end # module

src/gallery.jl

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,326 @@ function laplacian_fem(
226226
I,J,V,node_partition,node_partition
227227
end
228228

229+
function linear_elasticity_fem(
230+
nodes_per_dir, # free (== interior) nodes
231+
parts_per_dir,
232+
parts,
233+
;
234+
E = 1,
235+
ν = 0.25,
236+
index_type::Type{Ti} = Int64,
237+
value_type::Type{Tv} = Float64,
238+
) where {Ti,Tv}
239+
240+
cells_per_dir = nodes_per_dir .+ 1
241+
242+
function is_boundary_node(node_1d,nodes_1d)
243+
!(node_1d in 1:nodes_1d)
244+
end
245+
function ref_matrix(cartesian_local_nodes,h_per_dir,::Type{value_type}) where value_type
246+
D = ndims(cartesian_local_nodes)
247+
gp_1d = value_type[-sqrt(3)/3,sqrt(3)/3]
248+
sf_1d = zeros(value_type,length(gp_1d),2)
249+
sf_1d[:,1] = 0.5 .* (1 .- gp_1d)
250+
sf_1d[:,2] = 0.5 .* (gp_1d .+ 1)
251+
sg_1d = zeros(value_type,length(gp_1d),2)
252+
sg_1d[:,1] .= - 0.5
253+
sg_1d[:,2] .= 0.5
254+
cartesian_points = CartesianIndices(ntuple(d->length(gp_1d),Val{D}()))
255+
cartesian_local_node_to_local_node = LinearIndices(cartesian_local_nodes)
256+
cartesian_point_to_point = LinearIndices(cartesian_points)
257+
n = 2^D
258+
sg = Matrix{NTuple{D,value_type}}(undef,n,length(gp_1d)^D)
259+
for cartesian_local_node in cartesian_local_nodes
260+
local_node = cartesian_local_node_to_local_node[cartesian_local_node]
261+
local_node_tuple = Tuple(cartesian_local_node)
262+
for cartesian_point in cartesian_points
263+
point = cartesian_point_to_point[cartesian_point]
264+
point_tuple = Tuple(cartesian_point)
265+
v = ntuple(Val{D}()) do d
266+
prod(1:D) do i
267+
if i == d
268+
(2/h_per_dir[d])*sg_1d[local_node_tuple[d],point_tuple[d]]
269+
else
270+
sf_1d[local_node_tuple[i],point_tuple[i]]
271+
end
272+
end
273+
end
274+
sg[local_node,point] = v
275+
end
276+
end
277+
Aref = zeros(value_type,n*D,n*D)
278+
dV = prod(h_per_dir)/(2^D)
279+
ε_i = zeros(value_type,D,D)
280+
ε_j = zeros(value_type,D,D)
281+
λ = (E*ν)/((1+ν)*(1-2*ν))
282+
μ = E/(2*(1+ν))
283+
for i in 1:n
284+
for j in 1:n
285+
for ci in 1:D
286+
for cj in 1:D
287+
idof = (i-1)*D+ci
288+
jdof = (j-1)*D+cj
289+
ε_i .= 0
290+
ε_j .= 0
291+
for k in 1:n
292+
ε_i[ci,:] = collect(sg[k,i])
293+
ε_j[cj,:] = collect(sg[k,j])
294+
ε_i .= 0.5 .* ( ε_i .+ transpose(ε_i))
295+
ε_j .= 0.5 .* ( ε_j .+ transpose(ε_j))
296+
σ_j = λ*tr(ε_j)*one(ε_j) + 2*μ*ε_j
297+
Aref[idof,jdof] += tr(ε_i*σ_j)
298+
end
299+
end
300+
end
301+
end
302+
end
303+
Aref
304+
end
305+
function setup(cells,::Type{index_type},::Type{value_type}) where {index_type,value_type}
306+
D = length(nodes_per_dir)
307+
h_per_dir = map(i->1/(i+1),nodes_per_dir)
308+
ttt = ntuple(d->2,Val{D}())
309+
cartesian_local_nodes = CartesianIndices(ttt)
310+
Aref = ref_matrix(cartesian_local_nodes,h_per_dir,value_type)#ones(value_type,2^D,2^D)
311+
cell_to_cartesian_cell = CartesianIndices(cells_per_dir)
312+
first_cartesian_cell = cell_to_cartesian_cell[first(cells)]
313+
last_cartesian_cell = cell_to_cartesian_cell[last(cells)]
314+
ranges = map(:,Tuple(first_cartesian_cell),Tuple(last_cartesian_cell))
315+
cartesian_cells = CartesianIndices(ranges)
316+
offset = CartesianIndex(ttt)
317+
cartesian_local_node_to_local_node = LinearIndices(cartesian_local_nodes)
318+
cartesian_node_to_node = LinearIndices(nodes_per_dir)
319+
nnz = 0
320+
for cartesian_cell in cartesian_cells
321+
for cartesian_local_node_i in cartesian_local_nodes
322+
local_node_i = cartesian_local_node_to_local_node[cartesian_local_node_i]
323+
# This is ugly to support Julia 1.6 (idem below)
324+
cartesian_node_i = CartesianIndex(Tuple(cartesian_cell) .+ Tuple(cartesian_local_node_i) .- Tuple(offset))
325+
boundary = any(map(is_boundary_node,Tuple(cartesian_node_i),nodes_per_dir))
326+
if boundary
327+
continue
328+
end
329+
node_i = cartesian_node_to_node[cartesian_node_i]
330+
for cartesian_local_node_j in cartesian_local_nodes
331+
local_node_j = cartesian_local_node_to_local_node[cartesian_local_node_j]
332+
cartesian_node_j = CartesianIndex(Tuple(cartesian_cell) .+ Tuple(cartesian_local_node_j) .- Tuple(offset))
333+
boundary = any(map(is_boundary_node,Tuple(cartesian_node_j),nodes_per_dir))
334+
if boundary
335+
continue
336+
end
337+
node_j = cartesian_node_to_node[cartesian_node_j]
338+
nnz += D*D
339+
end
340+
end
341+
end
342+
myI = zeros(index_type,nnz)
343+
myJ = zeros(index_type,nnz)
344+
myV = zeros(value_type,nnz)
345+
k = 0
346+
for cartesian_cell in cartesian_cells
347+
for cartesian_local_node_i in cartesian_local_nodes
348+
local_node_i = cartesian_local_node_to_local_node[cartesian_local_node_i]
349+
cartesian_node_i = CartesianIndex(Tuple(cartesian_cell) .+ Tuple(cartesian_local_node_i) .- Tuple(offset))
350+
boundary = any(map(is_boundary_node,Tuple(cartesian_node_i),nodes_per_dir))
351+
if boundary
352+
continue
353+
end
354+
node_i = cartesian_node_to_node[cartesian_node_i]
355+
for cartesian_local_node_j in cartesian_local_nodes
356+
local_node_j = cartesian_local_node_to_local_node[cartesian_local_node_j]
357+
cartesian_node_j = CartesianIndex(Tuple(cartesian_cell) .+ Tuple(cartesian_local_node_j) .- Tuple(offset))
358+
boundary = any(map(is_boundary_node,Tuple(cartesian_node_j),nodes_per_dir))
359+
if boundary
360+
continue
361+
end
362+
node_j = cartesian_node_to_node[cartesian_node_j]
363+
for ci in 1:D
364+
for cj in 1:D
365+
dof_i = (node_i-1)*D + ci
366+
dof_j = (node_j-1)*D + cj
367+
local_dof_i = (local_node_i-1)*D + ci
368+
local_dof_j = (local_node_j-1)*D + cj
369+
k += 1
370+
myI[k] = dof_i
371+
myJ[k] = dof_j
372+
myV[k] = Aref[local_dof_i,local_dof_j]
373+
end
374+
end
375+
end
376+
end
377+
end
378+
myI,myJ,myV
379+
end
380+
node_partition = uniform_partition(parts,parts_per_dir,nodes_per_dir)
381+
dof_partition = node_to_dof_partition(node_partition,length(nodes_per_dir))
382+
cell_partition = uniform_partition(parts,parts_per_dir,cells_per_dir)
383+
I,J,V = map(cell_partition) do cells
384+
setup(cells,Ti,Tv)
385+
end |> tuple_of_arrays
386+
I,J,V,dof_partition,dof_partition
387+
end
388+
389+
function node_to_dof_partition(node_partition,D)
390+
global_node_to_owner = global_to_owner(node_partition)
391+
dof_partition = map(node_partition) do mynodes
392+
@assert ghost_length(mynodes) == 0
393+
own_to_global_node = own_to_global(mynodes)
394+
n_own_nodes = length(own_to_global_node)
395+
own_to_global_dof = zeros(Int,D*n_own_nodes)
396+
for own_node in 1:n_own_nodes
397+
for ci in 1:D
398+
own_dof = (own_node-1)*D+ci
399+
global_node = own_to_global_node[own_node]
400+
global_dof = (global_node-1)*D+ci
401+
own_to_global_dof[own_dof] = global_dof
402+
end
403+
end
404+
n_global_dofs = global_length(mynodes)*D
405+
owner = part_id(mynodes)
406+
own_dofs = OwnIndices(n_global_dofs,owner,own_to_global_dof)
407+
ghost_dofs = GhostIndices(n_global_dofs,Int[],Int32[])
408+
global_dof_to_owner = global_dof -> begin
409+
global_node = div(global_dof-1,D)+1
410+
global_node_to_owner[global_node]
411+
end
412+
mydofs = OwnAndGhostIndices(own_dofs,ghost_dofs,global_dof_to_owner)
413+
mydofs
414+
end
415+
dof_partition
416+
end
417+
418+
function node_coorinates_unit_cube(
419+
nodes_per_dir, # free (== interior) nodes
420+
parts_per_dir,
421+
parts,
422+
;
423+
split_format = Val(true),
424+
value_type::Type{Tv} = Float64,) where Tv
425+
426+
function setup!(own_x,mynodes)
427+
D = length(nodes_per_dir)
428+
h_per_dir = map(i->1/(i+1),nodes_per_dir)
429+
global_node_to_cartesian_global_node = CartesianIndices(nodes_per_dir)
430+
n_own_nodes = own_length(mynodes)
431+
own_to_global_node = own_to_global(mynodes)
432+
for own_node in 1:n_own_nodes
433+
global_node = own_to_global_node[own_node]
434+
cartesian_global_node = global_node_to_cartesian_global_node[global_node]
435+
xi = Tuple(cartesian_global_node)
436+
own_x[own_node] = h_per_dir .* xi
437+
end
438+
end
439+
node_partition = uniform_partition(parts,parts_per_dir,nodes_per_dir)
440+
T = SVector{length(nodes_per_dir),Tv}
441+
x = pzeros(T,node_partition;split_format)
442+
foreach(setup!,own_values(x),node_partition)
443+
x
444+
end
445+
446+
function near_nullspace_linear_elasticity(x,
447+
row_partition = node_to_dof_partition(partition(axes(x,1)),length(eltype(x)))
448+
)
449+
T = eltype(x)
450+
D = length(T)
451+
Tv = eltype(T)
452+
if D == 1
453+
nb = 1
454+
elseif D==2
455+
nb=3
456+
elseif D == 3
457+
nb = 6
458+
else
459+
error("case not implemented")
460+
end
461+
dof_partition = row_partition
462+
split_format = Val(eltype(partition(x)) <: SplitVector)
463+
B = [ pzeros(Tv,dof_partition;split_format) for _ in 1:nb ]
464+
near_nullspace_linear_elasticity!(B,x)
465+
end
466+
467+
function near_nullspace_linear_elasticity!(B,x)
468+
D = length(eltype(x))
469+
if D == 1
470+
foreach(own_values(B[1])) do own_b
471+
fill!(own_b,1)
472+
end
473+
elseif D==2
474+
foreach(own_values(B[1]),own_values(B[2]),own_values(x)) do own_b1,own_b2,own_x
475+
T = eltype(own_b1)
476+
n_own_nodes = length(own_x)
477+
for own_node in 1:n_own_nodes
478+
dof_x1 = (own_node-1)*2 + 1
479+
dof_x2 = (own_node-1)*2 + 2
480+
#
481+
own_b1[dof_x1] = one(T)
482+
own_b1[dof_x2] = zero(T)
483+
#
484+
own_b2[dof_x1] = zero(T)
485+
own_b2[dof_x2] = one(T)
486+
end
487+
end
488+
foreach(own_values(B[3]),own_values(x)) do own_b3,own_x
489+
T = eltype(own_b3)
490+
n_own_nodes = length(own_x)
491+
for own_node in 1:n_own_nodes
492+
x1,x2 = own_x[own_node]
493+
dof_x1 = (own_node-1)*2 + 1
494+
dof_x2 = (own_node-1)*2 + 2
495+
#
496+
own_b3[dof_x1] = -x2
497+
own_b3[dof_x2] = x1
498+
end
499+
end
500+
elseif D == 3
501+
foreach(own_values(B[1]),own_values(B[2]),own_values(B[3]),own_values(x)) do own_b1,own_b2,own_b3,own_x
502+
T = eltype(own_b1)
503+
n_own_nodes = length(own_x)
504+
for own_node in 1:n_own_nodes
505+
dof_x1 = (own_node-1)*3 + 1
506+
dof_x2 = (own_node-1)*3 + 2
507+
dof_x3 = (own_node-1)*3 + 3
508+
#
509+
own_b1[dof_x1] = one(T)
510+
own_b1[dof_x2] = zero(T)
511+
own_b1[dof_x3] = zero(T)
512+
#
513+
own_b2[dof_x1] = zero(T)
514+
own_b2[dof_x2] = one(T)
515+
own_b2[dof_x3] = zero(T)
516+
#
517+
own_b3[dof_x1] = zero(T)
518+
own_b3[dof_x2] = zero(T)
519+
own_b3[dof_x3] = one(T)
520+
end
521+
end
522+
foreach(own_values(B[4]),own_values(B[5]),own_values(B[6]),own_values(x)) do own_b4,own_b5,own_b6,own_x
523+
T = eltype(own_b4)
524+
n_own_nodes = length(own_x)
525+
for own_node in 1:n_own_nodes
526+
x1,x2,x3 = own_x[own_node]
527+
dof_x1 = (own_node-1)*3 + 1
528+
dof_x2 = (own_node-1)*3 + 2
529+
dof_x3 = (own_node-1)*3 + 3
530+
#
531+
own_b4[dof_x1] = -x2
532+
own_b4[dof_x2] = x1
533+
own_b4[dof_x3] = zero(T)
534+
#
535+
own_b5[dof_x1] = zero(T)
536+
own_b5[dof_x2] = -x3
537+
own_b5[dof_x3] = x2
538+
#
539+
own_b6[dof_x1] = x3
540+
own_b6[dof_x2] = zero(T)
541+
own_b6[dof_x3] = -x1
542+
end
543+
end
544+
else
545+
error("case not implemented")
546+
end
547+
B
548+
end
549+
550+
229551

0 commit comments

Comments
 (0)