@@ -226,4 +226,326 @@ function laplacian_fem(
226226 I,J,V,node_partition,node_partition
227227end
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