FrontISTR  6.0.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 :: SMOOTHING_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 
208  p_matrix = 0.0d0
209 
210  ! Identity matrix
211  i3 = 0.0d0
212  i3(1,1) = 1.0d0
213  i3(2,2) = 1.0d0
214  i3(3,3) = 1.0d0
215 
216  select case(etype)
217  case(fe_tri3n)
218  call getshapefunc(fe_tri6n, lpos, n)
219  call reorder_normals_tri3n_to_tri6n(vertex_normals, normals_tri6n)
220 
221  ! Compute Cab for fe_tri6n edges: (st-xi), (xi-et), (et-st)
222  call compute_cab(normals_tri6n(:,1), normals_tri6n(:,2), cab_31_3, cab_31_1)
223  call compute_cab(normals_tri6n(:,2), normals_tri6n(:,3), cab_12_1, cab_12_2)
224  call compute_cab(normals_tri6n(:,3), normals_tri6n(:,1), cab_23_2, cab_23_3)
225 
226  ! P matrices for fe_tri6n: st, xi, et
227  p1 = n(1) * i3 + n(4) * cab_31_3 + n(6) * cab_23_3
228  p2 = n(2) * i3 + n(4) * cab_31_1 + n(5) * cab_12_1
229  p3 = n(3) * i3 + n(5) * cab_12_2 + n(6) * cab_23_2
230 
231  ! Assemble P in fe_tri3n order: xi, et, st
232  p_matrix(1:3, 1:3) = p2
233  p_matrix(1:3, 4:6) = p3
234  p_matrix(1:3, 7:9) = p1
235 
236  case(fe_quad4n)
237  call getshapefunc(fe_quad8n, lpos, n)
238 
239  ! Compute Cab for 4 edges: (1,2), (2,3), (3,4), (4,1)
240  call compute_cab(vertex_normals(:,1), vertex_normals(:,2), cab_12_1, cab_12_2)
241  call compute_cab(vertex_normals(:,2), vertex_normals(:,3), cab_23_2, cab_23_3)
242  call compute_cab(vertex_normals(:,3), vertex_normals(:,4), cab_34_3, cab_34_4)
243  call compute_cab(vertex_normals(:,4), vertex_normals(:,1), cab_41_4, cab_41_1)
244 
245  ! P matrices for fe_quad8n: node 1, 2, 3, 4
246  p1 = n(1) * i3 + n(5) * cab_12_1 + n(8) * cab_41_1
247  p2 = n(2) * i3 + n(5) * cab_12_2 + n(6) * cab_23_2
248  p3 = n(3) * i3 + n(6) * cab_23_3 + n(7) * cab_34_3
249  p4 = n(4) * i3 + n(7) * cab_34_4 + n(8) * cab_41_4
250 
251  ! Assemble P in fe_quad4n order: 1, 2, 3, 4
252  p_matrix(1:3, 1:3) = p1
253  p_matrix(1:3, 4:6) = p2
254  p_matrix(1:3, 7:9) = p3
255  p_matrix(1:3, 10:12) = p4
256 
257  case default
258  write(*,*) "Error: compute_interpolation_matrix_P - Unsupported element type for Nagata patch.",etype
259  stop
260  end select
261 
262  end subroutine compute_interpolation_matrix_p
263 
264  subroutine create_intermediate_points( surf, currpos, contact_name )
265  type(tsurfelement), intent(inout) :: surf(:)
266  real(kind=kreal), intent(in) :: currpos(:)
267  character(len=HECMW_NAME_LEN), intent(in) :: contact_name
268 
269  integer(kind=kint) :: i
270 
271  if (size(surf) == 0) return
272 
273  !$omp parallel do private(i)
274  do i = 1, size(surf)
275  call create_intermediate_points_single(surf(i), currpos)
276  enddo
277  !$omp end parallel do
278 
279  if( smoothing_debug > 0 ) then
280  call print_surface_elements_to_vtk(surf, currpos, contact_name)
281  end if
282 
283  end subroutine create_intermediate_points
284 
285  subroutine create_intermediate_points_single( surf, currpos )
286  type(tsurfelement), intent(inout) :: surf
287  real(kind=kreal), intent(in) :: currpos(:)
288 
289  integer(kind=kint) :: i, etype
290  real(kind=kreal) :: elem(3,4)
291  real(kind=kreal), pointer :: vertex_normals(:,:)
292  real(kind=kreal) :: cab_12_1(3,3), cab_12_2(3,3)
293  real(kind=kreal) :: cab_23_2(3,3), cab_23_3(3,3)
294  real(kind=kreal) :: cab_31_3(3,3), cab_31_1(3,3)
295  real(kind=kreal) :: cab_34_3(3,3), cab_34_4(3,3)
296  real(kind=kreal) :: cab_41_4(3,3), cab_41_1(3,3)
297 
298  etype = surf%etype
299  vertex_normals => surf%vertex_normals
300 
301  select case(etype)
302  case(fe_tri3n)
303 
304  if (.not. associated(surf%intermediate_points)) then
305  allocate(surf%intermediate_points(3,3))
306  end if
307 
308  do i=1,3
309  elem(1:3,i) = currpos(3*(surf%nodes(i)-1)+1 : 3*surf%nodes(i))
310  enddo
311 
312  ! Compute Cab for 3 edges: (1,2), (2,3), (3,1)
313  call compute_cab(vertex_normals(:,1), vertex_normals(:,2), cab_12_1, cab_12_2)
314  call compute_cab(vertex_normals(:,2), vertex_normals(:,3), cab_23_2, cab_23_3)
315  call compute_cab(vertex_normals(:,3), vertex_normals(:,1), cab_31_3, cab_31_1)
316 
317  ! intermediate point
318  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))
319  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))
320  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))
321 
322  case(fe_quad4n)
323 
324  if (.not. associated(surf%intermediate_points)) then
325  allocate(surf%intermediate_points(3,4))
326  end if
327 
328  do i=1,4
329  elem(1:3,i) = currpos(3*(surf%nodes(i)-1)+1 : 3*surf%nodes(i))
330  enddo
331 
332  ! Compute Cab for 4 edges: (1,2), (2,3), (3,4), (4,1)
333  call compute_cab(vertex_normals(:,1), vertex_normals(:,2), cab_12_1, cab_12_2)
334  call compute_cab(vertex_normals(:,2), vertex_normals(:,3), cab_23_2, cab_23_3)
335  call compute_cab(vertex_normals(:,3), vertex_normals(:,4), cab_34_3, cab_34_4)
336  call compute_cab(vertex_normals(:,4), vertex_normals(:,1), cab_41_4, cab_41_1)
337 
338  ! intermediate points
339  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))
340  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))
341  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))
342  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))
343 
344  case default
345  !$omp critical
346  write(*,*) "Error: create_intermediate_points - Unsupported element type for Nagata patch.",etype
347  !$omp end critical
348  stop
349  end select
350 
351  end subroutine create_intermediate_points_single
352 
353  subroutine print_surface_elements_to_vtk( surf, currpos, contact_name )
354  type(tsurfelement), intent(in) :: surf(:)
355  real(kind=kreal), intent(in) :: currpos(:)
356  character(len=HECMW_NAME_LEN), intent(in) :: contact_name
357 
358  integer(kind=kint), parameter :: vtk_unit = 99
359  integer(kind=kint) :: i, j, nn, n_tri, n_quad, n_elem, n_points, n_cells_size, node_id, inode
360  real(kind=kreal) :: coord(3), normal(3)
361  character(len=256) :: filename
362 
363  ! Count valid elements
364  n_tri = 0
365  n_quad = 0
366  do i = 1, size(surf)
367  if (surf(i)%etype == fe_tri3n) then
368  n_tri = n_tri + 1
369  else if (surf(i)%etype == fe_quad4n) then
370  n_quad = n_quad + 1
371  endif
372  enddo
373 
374  n_elem = n_tri + n_quad
375  if (n_elem == 0) return
376 
377  ! Calculate total points and cells size
378  n_points = n_tri * 6 + n_quad * 8
379  n_cells_size = n_tri * 7 + n_quad * 9 ! tri: 1+6, quad: 1+8
380 
381  ! Open VTK file
382  filename = trim(contact_name) // '_nagata_debug.vtk'
383  open(unit=vtk_unit, file=filename, status='replace', action='write', form='formatted')
384 
385  ! Write header
386  write(vtk_unit, '(A)') '# vtk DataFile Version 3.0'
387  write(vtk_unit, '(A,A)') 'Nagata patch debug: ', trim(contact_name)
388  write(vtk_unit, '(A)') 'ASCII'
389  write(vtk_unit, '(A)') 'DATASET UNSTRUCTURED_GRID'
390  write(vtk_unit, *)
391 
392  ! Write POINTS
393  write(vtk_unit, '(A,I0,A)') 'POINTS ', n_points, ' float'
394  do i = 1, size(surf)
395  if (surf(i)%etype == fe_tri3n .or. surf(i)%etype == fe_quad4n) then
396  nn = size(surf(i)%nodes)
397  ! Output vertex coordinates
398  do j = 1, nn
399  inode = surf(i)%nodes(j)
400  coord(1:3) = currpos(3*(inode-1)+1 : 3*inode)
401  write(vtk_unit, '(3(E15.7,1X))') coord(1), coord(2), coord(3)
402  enddo
403  ! Output intermediate point coordinates
404  do j = 1, nn
405  coord(1:3) = surf(i)%intermediate_points(1:3, j)
406  write(vtk_unit, '(3(E15.7,1X))') coord(1), coord(2), coord(3)
407  enddo
408  endif
409  enddo
410  write(vtk_unit, *)
411 
412  ! Write CELLS
413  write(vtk_unit, '(A,I0,1X,I0)') 'CELLS ', n_elem, n_cells_size
414  node_id = 0
415  do i = 1, size(surf)
416  if (surf(i)%etype == fe_tri3n) then
417  write(vtk_unit, '(I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0)') &
418  6, node_id, node_id+1, node_id+2, node_id+3, node_id+4, node_id+5
419  node_id = node_id + 6
420  else if (surf(i)%etype == fe_quad4n) then
421  write(vtk_unit, '(I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0,1X,I0)') &
422  8, node_id, node_id+1, node_id+2, node_id+3, node_id+4, node_id+5, node_id+6, node_id+7
423  node_id = node_id + 8
424  endif
425  enddo
426  write(vtk_unit, *)
427 
428  ! Write CELL_TYPES
429  write(vtk_unit, '(A,I0)') 'CELL_TYPES ', n_elem
430  do i = 1, size(surf)
431  if (surf(i)%etype == fe_tri3n) then
432  write(vtk_unit, '(I0)') 22 ! VTK_QUADRATIC_TRIANGLE
433  else if (surf(i)%etype == fe_quad4n) then
434  write(vtk_unit, '(I0)') 23 ! VTK_QUADRATIC_QUAD
435  endif
436  enddo
437  write(vtk_unit, *)
438 
439  ! Write POINT_DATA - VECTORS (vertex normals)
440  write(vtk_unit, '(A,I0)') 'POINT_DATA ', n_points
441  write(vtk_unit, '(A)') 'VECTORS vertex_normal float'
442  do i = 1, size(surf)
443  if (surf(i)%etype == fe_tri3n .or. surf(i)%etype == fe_quad4n) then
444  nn = size(surf(i)%nodes)
445  ! Output vertex normals
446  do j = 1, nn
447  normal(1:3) = surf(i)%vertex_normals(1:3, j)
448  write(vtk_unit, '(3(E15.7,1X))') normal(1), normal(2), normal(3)
449  enddo
450  ! Output zero normals for intermediate points
451  do j = 1, nn
452  write(vtk_unit, '(A)') '0.0 0.0 0.0'
453  enddo
454  endif
455  enddo
456 
457  close(vtk_unit)
458 
459  write(*,'(A,A)') 'VTK debug file written: ', trim(filename)
460 
461  end subroutine print_surface_elements_to_vtk
462 
463 
464 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:270
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