FrontISTR  5.9.0
Large-scale structural analysis program with finit element method
fstr_contact_smoothing.f90
Go to the documentation of this file.
1 !-------------------------------------------------------------------------------
2 ! Copyright (c) 2019 FrontISTR Commons
3 ! This software is released under the MIT License, see LICENSE.txt
4 !-------------------------------------------------------------------------------
7  use hecmw, only: kint, kreal
9  use elementinfo
10  use m_utilities, only: calinverse
11  use msurfelement, only: tsurfelement
12  implicit none
13 
14  ! Tikhonov regularization parameter
15  real(kind=kreal), parameter, private :: nagata_epsilon = 1.0d-4
16 
17  integer(kind=kint), parameter, private :: DEBUG = 0
18 
19  private
20  public :: compute_cab
22  public :: update_surface_normal
24  public :: reorder_tri3n_to_tri6n
25 
26 contains
27 
31  subroutine reorder_tri3n_to_tri6n(vertices_in, midpoints_in, nodes_out)
32  real(kind=kreal), intent(in) :: vertices_in(3,3) ! fe_tri3n vertices
33  real(kind=kreal), intent(in) :: midpoints_in(3,3) ! fe_tri3n midpoints
34  real(kind=kreal), intent(out) :: nodes_out(3,6) ! fe_tri6n nodes
35 
36  ! Reorder vertices: xi,et,st -> st,xi,et
37  nodes_out(1:3, 1) = vertices_in(1:3, 3) ! st
38  nodes_out(1:3, 2) = vertices_in(1:3, 1) ! xi
39  nodes_out(1:3, 3) = vertices_in(1:3, 2) ! et
40 
41  ! Reorder midpoints: xi-et,et-st,st-xi -> xi-st,xi-et,et-st
42  nodes_out(1:3, 4) = midpoints_in(1:3, 3) ! xi-st (was st-xi, same point)
43  nodes_out(1:3, 5) = midpoints_in(1:3, 1) ! xi-et
44  nodes_out(1:3, 6) = midpoints_in(1:3, 2) ! et-st
45  end subroutine reorder_tri3n_to_tri6n
46 
48  subroutine reorder_normals_tri3n_to_tri6n(normals_in, normals_out)
49  real(kind=kreal), intent(in) :: normals_in(3,3) ! fe_tri3n order
50  real(kind=kreal), intent(out) :: normals_out(3,3) ! fe_tri6n order
51 
52  ! xi,et,st -> st,xi,et
53  normals_out(1:3, 1) = normals_in(1:3, 3) ! st
54  normals_out(1:3, 2) = normals_in(1:3, 1) ! xi
55  normals_out(1:3, 3) = normals_in(1:3, 2) ! et
56  end subroutine reorder_normals_tri3n_to_tri6n
57 
58  subroutine update_surface_normal( surf, currpos, hecMESH )
61  type(tsurfelement), intent(inout) :: surf(:)
62  real(kind=kreal), intent(in) :: currpos(:)
63  type(hecmwst_local_mesh), intent(in), optional :: hecmesh
64 
65  integer(kind=kint) :: i, j, nn, gnode, n_node
66  real(kind=kreal) :: normal(3), norm_len
67  real(kind=kreal), allocatable :: vnormal(:)
68 
69  ! Calculate geometric normals at vertices (skip if no local surf elements)
70  if (size(surf) > 0) then
71  call calc_all_surf_vertex_normals(surf, currpos)
72  endif
73 
74  ! Determine array size
75  if (present(hecmesh)) then
76  n_node = hecmesh%n_node
77  else
78  if (size(surf) == 0) return
79  n_node = maxval([(maxval(surf(i)%nodes), i=1,size(surf))])
80  endif
81 
82  ! Allocate flat array for MPI communication: vnormal(3*n_node)
83  allocate(vnormal(3*n_node))
84  vnormal = 0.0d0
85 
86  ! Accumulate vertex normals from all local surface elements
87  do i = 1, size(surf)
88  nn = size(surf(i)%nodes)
89  do j = 1, nn
90  gnode = surf(i)%nodes(j)
91  vnormal(3*(gnode-1)+1:3*(gnode-1)+3) = &
92  vnormal(3*(gnode-1)+1:3*(gnode-1)+3) &
93  + surf(i)%vertex_normals(:, j)
94  enddo
95  enddo
96 
97  ! MPI communication: allreduce (sum) across processes
98  ! All processes must participate even if they have no local surf elements
99  if (present(hecmesh)) then
100  call hecmw_assemble_r(hecmesh, vnormal, n_node, 3)
101  call hecmw_update_r(hecmesh, vnormal, n_node, 3)
102  endif
103 
104  ! Normalize and write back to surf%vertex_normals
105  do i = 1, size(surf)
106  nn = size(surf(i)%nodes)
107  do j = 1, nn
108  gnode = surf(i)%nodes(j)
109  normal(:) = vnormal(3*(gnode-1)+1:3*(gnode-1)+3)
110  norm_len = dsqrt(dot_product(normal, normal))
111  if (norm_len > 1.0d-20) then
112  surf(i)%vertex_normals(:, j) = normal / norm_len
113  endif
114  enddo
115  enddo
116 
117  deallocate(vnormal)
118 
119  end subroutine update_surface_normal
120 
121  subroutine compute_cab(na_vec, nb_vec, Cab_a, Cab_b)
122  implicit none
123  real(kind=kreal), intent(in) :: na_vec(3) ! unit normal at vertex a (n1)
124  real(kind=kreal), intent(in) :: nb_vec(3) ! unit normal at vertex b (n2)
125  real(kind=kreal), intent(out) :: cab_a(3,3)
126  real(kind=kreal), intent(out) :: cab_b(3,3)
127 
128  real(kind=kreal) :: i3(3,3)
129  real(kind=kreal) :: m(3), mnorm
130  real(kind=kreal) :: a1, a2
131  real(kind=kreal) :: beta, den, k
132  real(kind=kreal) :: r(3) ! r = a2*nb - a1*na
133  real(kind=kreal) :: q(3,3) ! outer(m, r)
134  integer(kind=kint) :: i, j
135 
136  ! Identity
137  i3 = 0.0d0
138  do i=1,3
139  i3(i,i) = 1.0d0
140  enddo
141 
142  ! m = normalize(na + nb)
143  m(:) = na_vec(:) + nb_vec(:)
144  mnorm = sqrt( m(1)*m(1) + m(2)*m(2) + m(3)*m(3) )
145 
146  if (mnorm < 1.0d-12) then
147  m(:) = na_vec(:)
148  mnorm = 1.0d0
149  else
150  m(:) = m(:) / mnorm
151  endif
152 
153  a1 = m(1)*na_vec(1) + m(2)*na_vec(2) + m(3)*na_vec(3)
154  a2 = m(1)*nb_vec(1) + m(2)*nb_vec(2) + m(3)*nb_vec(3)
155 
156  ! Regularization for alpha (beta >= 0)
157  ! Option 1: fixed dimensionless parameter
158  beta = nagata_epsilon ! << define e.g. 1e-3 .. 1e-1 (see notes below)
159 
160  den = 16.0d0*(a1*a1 + a2*a2) + beta
161 
162  ! If den too small, do no curvature (midpoint)
163  if (den < 1.0d-20) then
164  cab_a = 0.5d0 * i3
165  cab_b = 0.5d0 * i3
166  return
167  endif
168 
169  k = 4.0d0 / den
170 
171  ! r = a2*nb - a1*na (3-vector)
172  r(:) = a2*nb_vec(:) - a1*na_vec(:)
173 
174  ! Q = outer(m, r) = m * r^T (3x3)
175  do i=1,3
176  do j=1,3
177  q(i,j) = m(i) * r(j)
178  enddo
179  enddo
180 
181  ! Cab_a = 0.5*I - K*Q
182  ! Cab_b = 0.5*I + K*Q
183  cab_a = 0.5d0*i3 - k*q
184  cab_b = 0.5d0*i3 + k*q
185 
186  end subroutine compute_cab
187 
190  subroutine compute_interpolation_matrix_p(etype, nnode, lpos, vertex_normals, P_matrix)
191  implicit none
192  integer(kind=kint), intent(in) :: etype ! element type
193  integer(kind=kint), intent(in) :: nnode ! number of nodes
194  real(kind=kreal), intent(in) :: lpos(2) ! local coordinates (xi, eta)
195  real(kind=kreal), intent(in) :: vertex_normals(3,nnode) ! unit normals at vertices
196  real(kind=kreal), intent(out) :: p_matrix(3,nnode*3) ! interpolation matrix
197 
198  real(kind=kreal) :: n(8)
199  real(kind=kreal) :: cab_12_1(3,3), cab_12_2(3,3)
200  real(kind=kreal) :: cab_23_2(3,3), cab_23_3(3,3)
201  real(kind=kreal) :: cab_31_3(3,3), cab_31_1(3,3)
202  real(kind=kreal) :: cab_34_3(3,3), cab_34_4(3,3)
203  real(kind=kreal) :: cab_41_4(3,3), cab_41_1(3,3)
204  real(kind=kreal) :: p1(3,3), p2(3,3), p3(3,3), p4(3,3)
205  real(kind=kreal) :: i3(3,3)
206  real(kind=kreal) :: normals_tri6n(3,3)
207  integer(kind=kint) :: i, j
208 
209  p_matrix = 0.0d0
210 
211  ! Identity matrix
212  i3 = 0.0d0
213  i3(1,1) = 1.0d0
214  i3(2,2) = 1.0d0
215  i3(3,3) = 1.0d0
216 
217  select case(etype)
218  case(fe_tri3n)
219  call getshapefunc(fe_tri6n, lpos, n)
220  call reorder_normals_tri3n_to_tri6n(vertex_normals, normals_tri6n)
221 
222  ! Compute Cab for fe_tri6n edges: (st-xi), (xi-et), (et-st)
223  call compute_cab(normals_tri6n(:,1), normals_tri6n(:,2), cab_31_3, cab_31_1)
224  call compute_cab(normals_tri6n(:,2), normals_tri6n(:,3), cab_12_1, cab_12_2)
225  call compute_cab(normals_tri6n(:,3), normals_tri6n(:,1), cab_23_2, cab_23_3)
226 
227  ! P matrices for fe_tri6n: st, xi, et
228  p1 = n(1) * i3 + n(4) * cab_31_3 + n(6) * cab_23_3
229  p2 = n(2) * i3 + n(4) * cab_31_1 + n(5) * cab_12_1
230  p3 = n(3) * i3 + n(5) * cab_12_2 + n(6) * cab_23_2
231 
232  ! Assemble P in fe_tri3n order: xi, et, st
233  p_matrix(1:3, 1:3) = p2
234  p_matrix(1:3, 4:6) = p3
235  p_matrix(1:3, 7:9) = p1
236 
237  case(fe_quad4n)
238  call getshapefunc(fe_quad8n, lpos, n)
239 
240  ! Compute Cab for 4 edges: (1,2), (2,3), (3,4), (4,1)
241  call compute_cab(vertex_normals(:,1), vertex_normals(:,2), cab_12_1, cab_12_2)
242  call compute_cab(vertex_normals(:,2), vertex_normals(:,3), cab_23_2, cab_23_3)
243  call compute_cab(vertex_normals(:,3), vertex_normals(:,4), cab_34_3, cab_34_4)
244  call compute_cab(vertex_normals(:,4), vertex_normals(:,1), cab_41_4, cab_41_1)
245 
246  ! P matrices for fe_quad8n: node 1, 2, 3, 4
247  p1 = n(1) * i3 + n(5) * cab_12_1 + n(8) * cab_41_1
248  p2 = n(2) * i3 + n(5) * cab_12_2 + n(6) * cab_23_2
249  p3 = n(3) * i3 + n(6) * cab_23_3 + n(7) * cab_34_3
250  p4 = n(4) * i3 + n(7) * cab_34_4 + n(8) * cab_41_4
251 
252  ! Assemble P in fe_quad4n order: 1, 2, 3, 4
253  p_matrix(1:3, 1:3) = p1
254  p_matrix(1:3, 4:6) = p2
255  p_matrix(1:3, 7:9) = p3
256  p_matrix(1:3, 10:12) = p4
257 
258  case default
259  write(*,*) "Error: compute_interpolation_matrix_P - Unsupported element type for Nagata patch.",etype
260  stop
261  end select
262 
263  end subroutine compute_interpolation_matrix_p
264 
265  subroutine create_intermediate_points( surf, currpos, contact_name )
266  type(tsurfelement), intent(inout) :: surf(:)
267  real(kind=kreal), intent(in) :: currpos(:)
268  character(len=HECMW_NAME_LEN), intent(in) :: contact_name
269 
270  integer(kind=kint) :: i
271 
272  if (size(surf) == 0) return
273 
274  !$omp parallel do private(i)
275  do i = 1, size(surf)
276  call create_intermediate_points_single(surf(i), currpos)
277  enddo
278  !$omp end parallel do
279 
280  if( debug > 0 ) then
281  call print_surface_elements_to_vtk(surf, currpos, contact_name)
282  end if
283 
284  end subroutine create_intermediate_points
285 
286  subroutine create_intermediate_points_single( surf, currpos )
287  type(tsurfelement), intent(inout) :: surf
288  real(kind=kreal), intent(in) :: currpos(:)
289 
290  integer(kind=kint) :: i, etype
291  real(kind=kreal) :: elem(3,4)
292  real(kind=kreal), pointer :: vertex_normals(:,:)
293  real(kind=kreal) :: cab_12_1(3,3), cab_12_2(3,3)
294  real(kind=kreal) :: cab_23_2(3,3), cab_23_3(3,3)
295  real(kind=kreal) :: cab_31_3(3,3), cab_31_1(3,3)
296  real(kind=kreal) :: cab_34_3(3,3), cab_34_4(3,3)
297  real(kind=kreal) :: cab_41_4(3,3), cab_41_1(3,3)
298 
299  etype = surf%etype
300  vertex_normals => surf%vertex_normals
301 
302  select case(etype)
303  case(fe_tri3n)
304 
305  if (.not. associated(surf%intermediate_points)) then
306  allocate(surf%intermediate_points(3,3))
307  end if
308 
309  do i=1,3
310  elem(1:3,i) = currpos(3*(surf%nodes(i)-1)+1 : 3*surf%nodes(i))
311  enddo
312 
313  ! Compute Cab for 3 edges: (1,2), (2,3), (3,1)
314  call compute_cab(vertex_normals(:,1), vertex_normals(:,2), cab_12_1, cab_12_2)
315  call compute_cab(vertex_normals(:,2), vertex_normals(:,3), cab_23_2, cab_23_3)
316  call compute_cab(vertex_normals(:,3), vertex_normals(:,1), cab_31_3, cab_31_1)
317 
318  ! intermediate point
319  surf%intermediate_points(:,1) = matmul(cab_12_1(1:3,1:3), elem(1:3,1)) + matmul(cab_12_2(1:3,1:3), elem(1:3,2))
320  surf%intermediate_points(:,2) = matmul(cab_23_2(1:3,1:3), elem(1:3,2)) + matmul(cab_23_3(1:3,1:3), elem(1:3,3))
321  surf%intermediate_points(:,3) = matmul(cab_31_3(1:3,1:3), elem(1:3,3)) + matmul(cab_31_1(1:3,1:3), elem(1:3,1))
322 
323  case(fe_quad4n)
324 
325  if (.not. associated(surf%intermediate_points)) then
326  allocate(surf%intermediate_points(3,4))
327  end if
328 
329  do i=1,4
330  elem(1:3,i) = currpos(3*(surf%nodes(i)-1)+1 : 3*surf%nodes(i))
331  enddo
332 
333  ! Compute Cab for 4 edges: (1,2), (2,3), (3,4), (4,1)
334  call compute_cab(vertex_normals(:,1), vertex_normals(:,2), cab_12_1, cab_12_2)
335  call compute_cab(vertex_normals(:,2), vertex_normals(:,3), cab_23_2, cab_23_3)
336  call compute_cab(vertex_normals(:,3), vertex_normals(:,4), cab_34_3, cab_34_4)
337  call compute_cab(vertex_normals(:,4), vertex_normals(:,1), cab_41_4, cab_41_1)
338 
339  ! intermediate points
340  surf%intermediate_points(:,1) = matmul(cab_12_1(1:3,1:3), elem(1:3,1)) + matmul(cab_12_2(1:3,1:3), elem(1:3,2))
341  surf%intermediate_points(:,2) = matmul(cab_23_2(1:3,1:3), elem(1:3,2)) + matmul(cab_23_3(1:3,1:3), elem(1:3,3))
342  surf%intermediate_points(:,3) = matmul(cab_34_3(1:3,1:3), elem(1:3,3)) + matmul(cab_34_4(1:3,1:3), elem(1:3,4))
343  surf%intermediate_points(:,4) = matmul(cab_41_4(1:3,1:3), elem(1:3,4)) + matmul(cab_41_1(1:3,1:3), elem(1:3,1))
344 
345  case default
346  write(*,*) "Error: create_intermediate_points - Unsupported element type for Nagata patch.",etype
347  stop
348  end select
349 
350  end subroutine create_intermediate_points_single
351 
352  subroutine print_surface_elements_to_vtk( surf, currpos, contact_name )
353  type(tsurfelement), intent(in) :: surf(:)
354  real(kind=kreal), intent(in) :: currpos(:)
355  character(len=HECMW_NAME_LEN), intent(in) :: contact_name
356 
357  integer(kind=kint), parameter :: vtk_unit = 99
358  integer(kind=kint) :: i, j, nn, n_tri, n_quad, n_elem, n_points, n_cells_size, node_id, inode
359  real(kind=kreal) :: coord(3), normal(3)
360  character(len=256) :: filename
361 
362  ! Count valid elements
363  n_tri = 0
364  n_quad = 0
365  do i = 1, size(surf)
366  if (surf(i)%etype == fe_tri3n) then
367  n_tri = n_tri + 1
368  else if (surf(i)%etype == fe_quad4n) then
369  n_quad = n_quad + 1
370  endif
371  enddo
372 
373  n_elem = n_tri + n_quad
374  if (n_elem == 0) return
375 
376  ! Calculate total points and cells size
377  n_points = n_tri * 6 + n_quad * 8
378  n_cells_size = n_tri * 7 + n_quad * 9 ! tri: 1+6, quad: 1+8
379 
380  ! Open VTK file
381  filename = trim(contact_name) // '_nagata_debug.vtk'
382  open(unit=vtk_unit, file=filename, status='replace', action='write', form='formatted')
383 
384  ! Write header
385  write(vtk_unit, '(A)') '# vtk DataFile Version 3.0'
386  write(vtk_unit, '(A,A)') 'Nagata patch debug: ', trim(contact_name)
387  write(vtk_unit, '(A)') 'ASCII'
388  write(vtk_unit, '(A)') 'DATASET UNSTRUCTURED_GRID'
389  write(vtk_unit, *)
390 
391  ! Write POINTS
392  write(vtk_unit, '(A,I0,A)') 'POINTS ', n_points, ' float'
393  do i = 1, size(surf)
394  if (surf(i)%etype == fe_tri3n .or. surf(i)%etype == fe_quad4n) then
395  nn = size(surf(i)%nodes)
396  ! Output vertex coordinates
397  do j = 1, nn
398  inode = surf(i)%nodes(j)
399  coord(1:3) = currpos(3*(inode-1)+1 : 3*inode)
400  write(vtk_unit, '(3(E15.7,1X))') coord(1), coord(2), coord(3)
401  enddo
402  ! Output intermediate point coordinates
403  do j = 1, nn
404  coord(1:3) = surf(i)%intermediate_points(1:3, j)
405  write(vtk_unit, '(3(E15.7,1X))') coord(1), coord(2), coord(3)
406  enddo
407  endif
408  enddo
409  write(vtk_unit, *)
410 
411  ! Write CELLS
412  write(vtk_unit, '(A,I0,1X,I0)') 'CELLS ', n_elem, n_cells_size
413  node_id = 0
414  do i = 1, size(surf)
415  if (surf(i)%etype == fe_tri3n) then
416  write(vtk_unit, '(I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0)') &
417  6, node_id, node_id+1, node_id+2, node_id+3, node_id+4, node_id+5
418  node_id = node_id + 6
419  else if (surf(i)%etype == fe_quad4n) then
420  write(vtk_unit, '(I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0)') &
421  8, node_id, node_id+1, node_id+2, node_id+3, node_id+4, node_id+5, node_id+6, node_id+7
422  node_id = node_id + 8
423  endif
424  enddo
425  write(vtk_unit, *)
426 
427  ! Write CELL_TYPES
428  write(vtk_unit, '(A,I0)') 'CELL_TYPES ', n_elem
429  do i = 1, size(surf)
430  if (surf(i)%etype == fe_tri3n) then
431  write(vtk_unit, '(I0)') 22 ! VTK_QUADRATIC_TRIANGLE
432  else if (surf(i)%etype == fe_quad4n) then
433  write(vtk_unit, '(I0)') 23 ! VTK_QUADRATIC_QUAD
434  endif
435  enddo
436  write(vtk_unit, *)
437 
438  ! Write POINT_DATA - VECTORS (vertex normals)
439  write(vtk_unit, '(A,I0)') 'POINT_DATA ', n_points
440  write(vtk_unit, '(A)') 'VECTORS vertex_normal float'
441  do i = 1, size(surf)
442  if (surf(i)%etype == fe_tri3n .or. surf(i)%etype == fe_quad4n) then
443  nn = size(surf(i)%nodes)
444  ! Output vertex normals
445  do j = 1, nn
446  normal(1:3) = surf(i)%vertex_normals(1:3, j)
447  write(vtk_unit, '(3(E15.7,1X))') normal(1), normal(2), normal(3)
448  enddo
449  ! Output zero normals for intermediate points
450  do j = 1, nn
451  write(vtk_unit, '(A)') '0.0 0.0 0.0'
452  enddo
453  endif
454  enddo
455 
456  close(vtk_unit)
457 
458  write(*,'(A,A)') 'VTK debug file written: ', trim(filename)
459 
460  end subroutine print_surface_elements_to_vtk
461 
462 
463 end module m_fstr_contact_smoothing
This module encapsulate the basic functions of all elements provide by this software.
Definition: element.f90:43
subroutine getshapefunc(fetype, localcoord, func)
Calculate the shape function in natural coordinate system.
Definition: element.f90:754
integer, parameter fe_tri6n
Definition: element.f90:71
integer, parameter fe_tri3n
Definition: element.f90:70
integer, parameter fe_quad4n
Definition: element.f90:73
integer, parameter fe_quad8n
Definition: element.f90:74
I/O and Utility.
Definition: hecmw_util_f.F90:7
integer(kind=kint), parameter hecmw_name_len
Definition: hecmw.f90:6
Contact surface smoothing using Nagata patch interpolation.
subroutine, public reorder_tri3n_to_tri6n(vertices_in, midpoints_in, nodes_out)
Reorder triangle vertices and midpoints from fe_tri3n to fe_tri6n order fe_tri3n: v1(xi),...
subroutine, public compute_interpolation_matrix_p(etype, nnode, lpos, vertex_normals, P_matrix)
Compute interpolation matrix P for Nagata patch P_matrix(3, nnode*3) represents x = P * [X1; X2; X3; ...
subroutine, public compute_cab(na_vec, nb_vec, Cab_a, Cab_b)
subroutine, public update_surface_normal(surf, currpos, hecMESH)
subroutine, public create_intermediate_points(surf, currpos, contact_name)
subroutine hecmw_assemble_r(hecMESH, val, n, m)
subroutine hecmw_update_r(hecMESH, val, n, m)
This module provides aux functions.
Definition: utilities.f90:6
subroutine calinverse(NN, A)
calculate inverse of matrix a
Definition: utilities.f90:295
This module manages surface elements in 3D It provides basic definition of surface elements (triangla...
Definition: surf_ele.f90:8
subroutine calc_all_surf_vertex_normals(surf, currpos)
Calculate vertex normals for all surface elements (geometric calculation only)
Definition: surf_ele.f90:253
Structure to define surface group.
Definition: surf_ele.f90:23