ADflow  v1.0
ADflow is a finite volume RANS solver tailored for gradient-based aerodynamic design optimization.
stringOps.F90
Go to the documentation of this file.
1 module stringops
2 
3  ! Import oversetString becuase every routine uses this.
4  use oversetdata, only: oversetstring
5 contains
6 
7  subroutine nullifystring(string)
8 
9  use constants
10  implicit none
11 
12  type(oversetstring) :: string
13 
14  nullify (string%nodeData, &
15  string%x, &
16  string%norm, &
17  string%perpNorm, &
18  string%h, &
19  string%intNodeData, &
20  string%ind, &
21  string%cluster, &
22  string%family, &
23  string%conn, &
24  string%pNodes, &
25  string%pElems, &
26  string%cNodes, &
27  string%otherID, &
28  string%nte, &
29  string%subStr, &
30  string%elemUsed, &
31  string%XzipNodeUsed, &
32  string%tris, &
33  string%surfCellID)
34 
35  end subroutine nullifystring
36 
37  subroutine deallocatestring(string)
38 
39  use constants
40  implicit none
41 
42  type(oversetstring) :: string
43  integer(kind=intType) :: i
44 
45  if (associated(string%nodeData)) &
46  deallocate (string%nodeData)
47 
48  if (associated(string%intNodeData)) &
49  deallocate (string%intNodeData)
50 
51  if (associated(string%conn)) &
52  deallocate (string%conn)
53 
54  if (associated(string%pNodes)) &
55  deallocate (string%pNodes)
56 
57  if (associated(string%pElems)) &
58  deallocate (string%pElems)
59 
60  if (associated(string%cNodes)) &
61  deallocate (string%cNodes)
62 
63  if (associated(string%otherID)) &
64  deallocate (string%otherID)
65 
66  if (associated(string%nte)) &
67  deallocate (string%nte)
68 
69  if (associated(string%subStr)) &
70  deallocate (string%subStr)
71 
72  if (associated(string%elemUsed)) &
73  deallocate (string%elemUsed)
74 
75  if (associated(string%xZipNodeUsed)) &
76  deallocate (string%xZipNodeUsed)
77 
78  if (associated(string%tris)) &
79  deallocate (string%tris)
80 
81  if (associated(string%surfCellID)) &
82  deallocate (string%surfCellID)
83 
84  call nullifystring(string)
85 
86  end subroutine deallocatestring
87 
88  subroutine setstringpointers(string)
89 
90  use constants
91  implicit none
92  type(oversetstring) :: string
93  string%x => string%nodeData(1:3, :)
94  string%norm => string%nodeData(4:6, :)
95  string%perpNorm => string%nodeData(7:9, :)
96  string%h => string%nodeData(10, :)
97 
98  string%ind => string%intNodeData(1, :)
99  string%cluster => string%intNodeData(2, :)
100  string%family => string%intNodeData(3, :)
101 
102  end subroutine setstringpointers
103 
104  subroutine createorderedstrings(master, strings, nString)
105 
106  use constants
107  implicit none
108 
109  ! Input/Output
110  type(oversetstring) :: master
111  type(oversetstring), dimension(:), allocatable :: strings
112 
113  ! Working
114  integer(kind=intType) :: nElems, nNodes, curElem, nString, iStart, i, firstElem
115  type(oversetstring), pointer :: stringsLL, str
116 
117  ! The next step is to create ordered strings based on the
118  ! connectivity. This is a purely logical operation. We don't know
119  ! how many actual strings we will need so we will use a linked
120  ! list as we go.
121  call createnodetoelem(master)
122 
123  ! Allocate some additional arrays we need for doing the chain
124  ! searches.
125  nelems = master%nElems
126  nnodes = master%nNodes
127  allocate (master%elemUsed(nelems), master%subStr(2, nelems), &
128  master%cNodes(2, nnodes))
129 
130  master%cNodes = 0
131  master%elemUsed = 0
132  curelem = 1
133  nstring = 0
134  do while (curelem < master%nElems)
135 
136  ! Arbitrarily get the first node for my element:
137  istart = master%conn(1, curelem)
138  nelems = master%nte(1, istart)
139 
140  ! ----------------------
141  ! First side of chain:
142  ! ----------------------
143  firstelem = master%nte(2, istart)
144  master%subStr(1, 1) = firstelem
145  call dochain(master, istart, 1)
146 
147  ! ----------------------
148  ! Second side of chain:
149  ! ----------------------
150  if (nelems > 1) then
151  firstelem = master%nte(3, istart)
152 
153  ! Make sure the second one wasn't wrapped around on a
154  ! periodic chain
155  if (master%elemUsed(firstelem) == 0) then
156 
157  master%subStr(2, 1) = firstelem
158  call dochain(master, istart, 2)
159  call combinechainbuffers(master)
160  end if
161  end if
162 
163  ! We now have a boundary string stored in master%subString(1,
164  ! :nSubStr(1)). These are actually the element numbers of the
165  ! master that form a continuous chain.
166 
167  ! Create or add a new string to our linked list
168  ! "stringsLL".
169  if (nstring == 0) then
170  allocate (stringsll)
171  nstring = 1
172  stringsll%next => stringsll
173  str => stringsll
174  else
175  allocate (str%next)
176  str%next%next => stringsll
177  str => str%next
178  nstring = nstring + 1
179  end if
180 
181  ! Create a substring from master based on the elements we
182  ! have in the buffer
183  call createsubstringfromelems(master, str, nstring)
184 
185  ! Scan through until we find the next unused element:
186  do while ((master%elemUsed(curelem) == 1) .and. (curelem < master%nElems))
187  curelem = curelem + 1
188  end do
189  end do
190 
191  ! Put the strings into an regular array which will be easier to
192  ! manipulate.
193  allocate (strings(nstring))
194  str => stringsll
195  i = 0
196  do while (i < nstring)
197  i = i + 1
198  strings(i) = str ! This is derived type assigment.
199  call nullifystring(str)
200  str => str%next
201  end do
202 
203  end subroutine createorderedstrings
204 
205  subroutine performselfzip(master, strings, nStrings, debugZipper)
206 
207  use constants
208  use kdtree2_module
209  use inputoverset, only: selfzipcutoff
210  implicit none
211 
212  ! Input/Output
213  type(oversetstring) :: master
214  integer(kind=intType) :: nStrings
215  type(oversetstring), dimension(nStrings), target :: strings
216  logical, intent(in) :: debugZipper
217 
218  ! Workging
219  type(oversetstring), pointer :: str
220  real(kind=realtype) :: cutoff
221  integer(kind=intType) :: i, j, nZipped
222 
223  ! Now determine if there are any "holes" or periodic strings
224  ! without anything inside of it. Ie closed loops. If there isn't
225  ! we can self zip. Otherwise, we falg it so that it isn't touched
226  ! and automatically pocket zipped at th end.
227 
228  do i = 1, nstrings
229  str => strings(i)
230  str%isPocket = .true.
231  do j = 1, str%nNodes
232  if (str%otherID(1, j) /= -1) then
233  str%isPocket = .false.
234  end if
235  end do
236 
237  if (.not. str%isPocket) then
238  zipperloop: do j = 1, 5
239  if (j == 1) then
240  cutoff = selfzipcutoff
241  else
242  cutoff = 90_realtype
243  end if
244  call selfzip(strings(i), cutoff, nzipped)
245  if (nzipped == 0) then
246  exit zipperloop
247  end if
248  end do zipperloop
249  end if
250  end do
251 
252  ! Now we need to redo the string matching becuase the self-zip
253  ! shortened the strings
254  call stringmatch(strings, nstrings, debugzipper)
255 
256  end subroutine performselfzip
257 
258  subroutine reducegapstring(string)
259 
260  ! Generic routine for removing duplicate nodes on the given
261  ! string. The string is returned with the nodes and connectivities
262  ! adjusted accordingly.
263 
264  use constants
265  use utils, only: pointreduce, mynorm2
266  implicit none
267 
268  ! Input/Ouput
269  type(oversetstring), intent(inout) :: string
270 
271  ! Working:
272  real(kind=realtype) :: minedge
273  integer(kind=intType) :: nUnqiue, i, n1, n2, nUnique, idx
274  integer(kind=intType), dimension(:), allocatable :: link
275  real(kind=realtype), dimension(:, :), allocatable :: uniquenodes
276  real(kind=realtype), dimension(:, :), pointer :: nodedataptr
277  integer(kind=intType), dimension(:, :), pointer :: intNodeDataPtr
278  integer(kind=intType), dimension(:), allocatable :: normCounter
279  real(kind=realtype), dimension(:, :), allocatable :: uniquenorms
280 
281  ! We will do a sort of adaptive tolernace here: Get the minium edge
282  ! length and base the tolerance on that:
283 
284  minedge = huge(1.0d0)
285 
286  do i = 1, string%nElems
287  n1 = string%conn(1, i)
288  n2 = string%conn(2, i)
289  minedge = min(minedge, mynorm2(string%x(:, n1) - string%x(:, n2)))
290  end do
291 
292  allocate (link(string%nNodes), uniquenodes(3, string%nNodes))
293 
294  call pointreduce(string%x, string%nNodes, minedge / 1000.0, uniquenodes, link, nunique)
295 
296  ! Now average the normals for any duplicate nodes. This is to handle any discrepancies
297  ! for h-type mesh topologies where the surface block is fully represented by the volume connectivity
298  allocate (normcounter(nunique), uniquenorms(3, nunique))
299  normcounter(:) = zero
300  uniquenorms(:, :) = zero
301  ! sum the norms for the unique node and count how many duplicates there are for a given node
302  do i = 1, string%nNodes
303  idx = link(i)
304  uniquenorms(:, idx) = uniquenorms(:, idx) + string%nodeData(4:6, i)
305  normcounter(idx) = normcounter(idx) + 1
306  end do
307 
308  ! Now divide to get the average and assign back to original data storage
309  do i = 1, string%nNodes
310  idx = link(i)
311  string%nodeData(4:6, i) = uniquenorms(:, idx) / normcounter(idx)
312  end do
313  deallocate (normcounter, uniquenorms)
314  ! Averageing is complete
315 
316  ! Update the connectivity to use the new set of nodes
317  do i = 1, string%nElems
318  string%conn(1, i) = link(string%conn(1, i))
319  string%conn(2, i) = link(string%conn(2, i))
320  end do
321 
322  ! Reallocate the node based data to the correct size. Set pointers
323  ! to original data first.
324  nodedataptr => string%nodeData
325  intnodedataptr => string%intNodeData
326  allocate (string%nodeData(10, nunique), string%intNodeData(3, nunique))
327 
328  ! Reset the pointers
329  call setstringpointers(string)
330 
331  do i = 1, string%nNodes
332  string%nodeData(:, link(i)) = nodedataptr(:, i)
333  string%intNodeData(:, link(i)) = intnodedataptr(:, i)
334  end do
335  string%nNodes = nunique
336 
337  ! deallocate the pointer data which is actually the original data
338  deallocate (nodedataptr, intnodedataptr, link, uniquenodes)
339 
340  end subroutine reducegapstring
341 
342  recursive subroutine createnodetoelem(string)
343 
344  ! Produce the inverse of the connectivity...the nodeToElem
345  ! array. Each node should point to 1 element (at a
346  ! boundary) or two elements for a normal part of a chain.
347 
348  use constants
349  use utils, only: terminate
350  implicit none
351 
352  ! Input/Output
353  type(oversetstring) :: string
354 
355  ! Working
356  integer(kind=intType) :: i, j, ii, jj, n(2), m(2), curelem, ndup
357  integer(kind=intType), dimension(string%nElems) :: duplicated
358  integer(kind=intType), dimension(:, :), pointer :: tmpconn
359  logical :: duplicateelement
360 
361  allocate (string%nte(3, string%nNodes))
362  string%nte = 0
363  duplicated = 0
364 
365  do i = 1, string%nElems
366  ! Node numbers we're working with:
367  n = string%conn(:, i)
368 
369  ! For each node check which elements (if any) are already
370  ! connected. We need to check them again the node numbers n1 and n2
371 
372  duplicateelement = .false.
373  do jj = 1, 2
374 
375  do j = 1, string%nte(1, n(jj)) ! Loop over the element numbers already here:
376  curelem = string%nte(j + 1, n(jj))
377 
378  m = string%conn(:, curelem)
379 
380  if (m(1) == n(1) .and. m(2) == n(2)) then
381  duplicateelement = .true.
382  else if (m(1) == n(2) .and. m(2) == n(1)) then
383  ! Element exists, but it is the wrong order...don't
384  ! know what to do with this, probably an error or
385  ! maybe a corner case I haven't thought of.
386  call terminate("makeBoundaryString", "Inconsistent duplicate edge.")
387  end if
388  end do
389  end do
390 
391  if (.not. duplicateelement) then
392  do jj = 1, 2
393  string%nte(1, n(jj)) = string%nte(1, n(jj)) + 1
394  ii = string%nte(1, n(jj))
395  string%nte(ii + 1, n(jj)) = i
396  end do
397  else
398  ! Well, we've figured out that this element is actually a
399  ! duplicate so we'll make a note of that
400  duplicated(i) = 1
401  end if
402  end do
403 
404  ! If we have duplicated elements, modify the conn to adjust for this.
405  ndup = sum(duplicated)
406  if (ndup > 0) then
407  tmpconn => string%conn
408 
409  allocate (string%conn(2, string%nElems - ndup))
410 
411  j = 0
412  do i = 1, string%nElems
413  if (duplicated(i) == 0) then
414  j = j + 1
415  string%conn(:, j) = tmpconn(:, i)
416 
417  end if
418  end do
419 
420  ! Set the new number of elements
421  string%nElems = string%nElems - ndup
422 
423  ! Don't forget to deallocate the tmpConn pointer which is
424  ! actually the original conn data.
425  deallocate (tmpconn)
426 
427  ! Destroy nte and call myself again to get the final correct nte
428  ! without the duplicates.
429  deallocate (string%nte)
430  call createnodetoelem(string)
431  end if
432  end subroutine createnodetoelem
433 
434  subroutine dochain(master, iStart, iSub)
435 
436  use constants
437  implicit none
438  ! Input/OUtput
439  type(oversetstring) :: master
440  integer(kind=intType), intent(in) :: iStart, iSub
441 
442  ! Working
443  integer(Kind=intType) :: i, j, jj, c, n1, n2, curNode, nextNode
444  integer(Kind=intType) :: elem1, elem2, curElem, nextElem
445  integer(kind=intType) :: N
446 
447  ! The number of elements in this substring
448  n = 1
449 
450  curnode = istart
451 
452  chainloop: do
453 
454  ! Get the currnet element
455  curelem = master%subStr(isub, n)
456 
457  ! Flag the element as used:
458  master%elemUsed(curelem) = 1
459 
460  ! Get the two nodes for the current element:
461  n1 = master%conn(1, curelem)
462  n2 = master%conn(2, curelem)
463 
464  if (n1 == curnode) then
465  nextnode = n2
466  else
467  nextnode = n1
468  end if
469 
470  ! Exit condition 1: Next node was our starting node:
471  if (nextnode == istart) then
472  exit chainloop
473  end if
474 
475  ! Exit condition 2: The next node has only 1 element, (the one
476  ! we're currently on) so that means the the chain is finished
477  c = master%nte(1, nextnode)
478 
479  if (c == 1) then
480  exit chainloop
481 
482  else if (c == 2) then
483  ! With c=2 this easy, just extract the two elements
484  elem1 = master%nte(2, nextnode)
485  elem2 = master%nte(3, nextnode)
486 
487  if (elem1 == curelem) then
488  nextelem = elem2
489  else
490  nextelem = elem1
491  end if
492  end if
493 
494  ! Now add the "nextElem" to our chain:
495  n = n + 1
496  master%subStr(isub, n) = nextelem
497 
498  ! Flag this elemet as being used
499  master%elemUsed(nextelem) = 1
500 
501  ! Finally set the nextNode back to the current node for the next
502  ! iteration
503  curnode = nextnode
504  end do chainloop
505  master%nSubStr(isub) = n
506  end subroutine dochain
507 
508  subroutine createsubstringfromelems(p, s, id)
509 
510  use constants
511  implicit none
512 
513  ! Input/output
514  type(oversetstring), target, intent(in) :: p
515  type(oversetstring), intent(out) :: s
516  integer(kind=intType), intent(in) :: id
517 
518  ! Working
519  integer(kind=intType) :: i, j, n1, n2, k
520  integer(kind=intType), dimension(:), allocatable :: nodeUsed
521 
522  ! First thing we always have to do with a new string is to nullify
523  ! all the poitners
524  call nullifystring(s)
525 
526  ! Firstly we can set the number of elements, since we know precisely
527  ! what this is:
528 
529  s%nElems = p%nSubStr(1)
530  s%myID = id
531 
532  ! Next determine the number of nodes. This is done by flagging the
533  ! nodes in the parent that are used by 's'
534  allocate (nodeused(p%nNodes))
535  nodeused = 0
536  k = 0
537  do i = 1, s%nElems
538  n1 = p%conn(1, p%subStr(1, i))
539  n2 = p%conn(2, p%subStr(1, i))
540  if (nodeused(n1) == 0) then
541  k = k + 1
542  nodeused(n1) = k
543  end if
544 
545  if (nodeused(n2) == 0) then
546  k = k + 1
547  nodeused(n2) = k
548  end if
549  end do
550 
551  ! We can now set the number of nodes the substring has
552  s%nNodes = k
553 
554  ! The number of nodes will equal the number of elements iff the
555  ! string is period. Otherwise we will have 1 more node than element.
556 
557  if (s%nNodes == s%nElems) then
558  s%isPeriodic = .true.
559  end if
560 
561  ! Allocate and set the node and element parent information
562  allocate (s%pElems(s%nElems), s%pNodes(s%nNodes))
563 
564  do i = 1, s%nElems
565  s%pElems(i) = p%subStr(1, i)
566  end do
567 
568  ! Now create the pNodes ("link") array such that pNodes(i) points to
569  ! the node index in the parent
570  j = 0
571  do i = 1, p%nNodes
572  if (nodeused(i) /= 0) then
573  s%pNodes(nodeused(i)) = i
574  end if
575  end do
576 
577  ! Set the parent's cNode to point to my nodes
578  do i = 1, s%nNodes
579  p%cNodes(:, s%pNodes(i)) = (/s%myID, i/)
580  end do
581 
582  ! Now that we know the mapping between by local nodes-based
583  ! quantities and the parent, we can allocate and set all the
584  ! node-based quantities.
585 
586  allocate (s%nodeData(10, s%nNodes), s%intNodeData(3, s%nNodes))
587 
588  ! Set the string pointers
589  call setstringpointers(s)
590 
591  do i = 1, s%nNodes
592  s%nodeData(:, i) = p%nodeData(:, s%pNodes(i))
593  s%intNodeData(:, i) = p%intNodeData(:, s%pNodes(i))
594  end do
595 
596  ! We can now create the local conn too, *USING THE LOCAL NODE NUMBERS*
597  allocate (s%conn(2, s%nElems))
598 
599  do i = 1, s%nElems
600  s%conn(1, i) = nodeused(p%conn(1, s%pElems(i)))
601  s%conn(2, i) = nodeused(p%conn(2, s%pElems(i)))
602  end do
603 
604  ! Set the pointer to my parent.
605  s%p => p
606 
607  deallocate (nodeused)
608 
609  ! Last thing we can do is create the nodeToElem for the substring.
610  call createnodetoelem(s)
611  end subroutine createsubstringfromelems
612 
613  subroutine combinechainbuffers(s)
614 
615  use constants
616  implicit none
617  type(oversetstring), intent(inout) :: s
618  integer(kind=intType) :: N1, N2
619 
620  n1 = s%nSubStr(1)
621  n2 = s%nSubStr(2)
622 
623  ! First reverse the direction of string 2 of the nodes we found
624  s%subStr(2, 1:n2) = s%subStr(2, n2:1:-1)
625 
626  ! Now String 1 can be tacked on the end of string2
627  s%subStr(2, n2 + 1:n2 + n1) = s%subStr(1, 1:n1)
628 
629  ! And finally copied back to string1
630  s%subStr(1, 1:n1 + n2) = s%subStr(2, 1:n1 + n2)
631  s%nSubStr(1) = n1 + n2
632 
633  end subroutine combinechainbuffers
634 
635  subroutine selfzip(s, cutOff, nZipped)
636 
637  use constants
638  use kdtree2_module
639  use utils, only: mynorm2, cross_prod
640  implicit none
641 
642  ! Input/Output
643  type(oversetstring), intent(inout), target :: s
644  integer(Kind=intType), intent(out) :: nZipped
645  real(kind=realtype), intent(in) :: cutoff
646 
647  ! Working
648  integer(kind=intType) :: i, j, k, N, ii, im1, ip1
649  logical :: lastNodeZippered, added
650  real(kind=realtype), dimension(3) :: v1, v2, norm
651  real(kind=realtype) :: coscutoff, costheta, r2, v1nrm, v2nrm
652  integer(Kind=intType), dimension(:), allocatable :: nodeMap
653  type(kdtree2_result), dimension(:), allocatable :: results
654 
655  ! Perform self zipping on the supplied string. The string at this
656  ! point should be either peroidic or since sinded --- no multiple
657  ! loops should be left. Therefore, we can count on the nodes being
658  ! in order.
659 
660  allocate (results(25))
661  allocate (nodemap(s%nNodes))
662  nodemap = 1
663 
664  coscutoff = cos(cutoff * pi / 180)
665  nzipped = 0
666 
667  ! Peroidic string starts at node 1, and uses node 'N' as the previous
668  ! node. Single chains start at node 2 and only go to the N-1 node.
669  if (s%isPeriodic) then
670  im1 = s%nNodes
671  ii = 1
672  ip1 = 2
673  n = s%nNodes
674  else
675  im1 = 1
676  ii = 2
677  ip1 = 3
678  n = s%nNodes - 1
679  end if
680 
681  do while (ii <= n)
682 
683  ! Peroidic string at end...loop around
684  if (s%isPeriodic .and. ii == n) then
685  ip1 = 1
686  end if
687 
688  lastnodezippered = .false.
689 
690  ! Determine the anlge between the vectors
691  v1 = s%x(:, ip1) - s%x(:, ii)
692  v2 = s%x(:, im1) - s%x(:, ii)
693  v1nrm = mynorm2(v1)
694  v2nrm = mynorm2(v2)
695  call cross_prod(v2, v1, norm)
696  norm = norm / mynorm2(norm)
697 
698  if (dot_product(norm, s%norm(:, ii)) > zero) then
699 
700  ! the dot product of the im1 and ip1 nodes have to be close
701  if (dot_product(s%norm(:, ip1), s%norm(:, im1)) > 0.80) then
702 
703  costheta = dot_product(v1, v2) / (v1nrm * v2nrm)
704 
705  if (costheta > coscutoff) then
706 
707  call addpotentialtriangle(s, im1, ii, ip1, nodemap, &
708  results, added)
709 
710  if (added) then
711  nzipped = nzipped + 1
712  lastnodezippered = .true.
713  end if
714  end if
715  end if
716  end if
717 
718  if (lastnodezippered) then
719  ! Skip the next node...we'll get it on the next pass
720  ii = ii + 2
721  im1 = ii - 1
722  ip1 = ii + 1
723  else
724  ! Just shuffle along
725  ii = ii + 1
726  ip1 = ii + 1
727  im1 = ii - 1
728  end if
729  end do
730 
731  ! Now we will modify our string to remove the elements and nodes
732  ! that got knocked off due to self zipping. This way the calling
733  ! process still sees the same string, it just gets a little
734  ! shorter.
735 
736  call shortenstring(s, nodemap)
737  deallocate (results, nodemap)
738 
739  end subroutine selfzip
740 
741  subroutine crosszip(str1, N1, N2, str2, N3, N4, debugZipper, failed)
742 
743  use constants
744  use utils, only: mynorm2, cross_prod
745 
746  implicit none
747 
748  type(oversetstring), intent(inout) :: str1, str2
749  integer(kind=intType) :: N1, N2, N3, N4
750  logical :: debugZipper, failed
751  ! Working
752  type(oversetstring), pointer :: p
753  integer(kind=intType) :: stepsA, stepsB, nStepsA, nStepsB
754  integer(kind=intType) :: nTriToAdd, ii, i, j, k, A, B, Ap, Bp
755  integer(kind=intType) :: aPrev, bPrev
756  real(kind=realtype), dimension(3) :: pta, ptb, ptap, ptbp
757  !real(kind=realType), dimension(3) :: ptAPrev, ptBPRev
758  real(kind=realtype), dimension(3) :: aoff, boff, apoff, bpoff
759  real(kind=realtype), dimension(3) :: norma, normb, normap, normbp
760  real(kind=realtype), dimension(3) :: perpa, perpb, perpap, perpbp
761  !real(kind=realType), dimension(3) :: normAPrev, normBPrev
762  !real(kind=realType), dimension(3) :: perpAPrev, perpBPrev
763  real(kind=realtype), dimension(3) :: trinorm1, quadnorm1
764  real(kind=realtype), dimension(3) :: trinorm2, quadnorm2
765  logical :: aValid, bValid, advanceA, aPreferred, area1, area2
766  logical :: advanceB
767  logical :: changeA, changeB
768  logical :: aValidPrev, bValidPrev, advanceAPrev, advanceBPrev
769  real(kind=realtype) :: sum1, sum2, h, dpa, dpb
770  !am real(kind=realType), parameter :: cutOff = 0.95*3
771  real(kind=realtype), parameter :: cutoff = 0.85 * 3
772  ! First determine the the total number of triangles we will add
773  ! total. It is equal to the total number of triangles on each
774  ! string. This will form the index on the do loop.
775  failed = .false.
776  ! Str1 goes forward
777  if (n2 > n1) then
778  nstepsa = n2 - n1
779  else if (n2 < n1) then
780  nstepsa = n2 + str1%nNodes - n1
781  else ! N1 == N2
782  nstepsa = str1%nElems
783  end if
784 
785  ! Str2 goes backwards
786  if (n3 < n4) then
787  nstepsb = n3 + str2%nNodes - n4
788  else if (n3 > n4) then
789  nstepsb = n3 - n4
790  else ! N3 == N4
791  nstepsb = str2%nElems
792  end if
793 
794  ! Initialize these out of bounds incase something goes very wrong.
795  aprev = -1
796  bprev = -1
797 
798  ! The number of steps we've performed in each edge
799  stepsa = 0
800  stepsb = 0
801 
802  ! Initialize the front:
803  a = n1
804  b = n3
805  pta = str1%x(:, a)
806  ptb = str2%x(:, b)
807 
808  norma = str1%norm(:, a)
809  normb = str2%norm(:, b)
810 
811  perpa = str1%perpNorm(:, a)
812  perpb = str2%perpNorm(:, b)
813 
814  ap = nextnode(str1, a, .true.)
815  bp = nextnode(str2, b, .false.)
816  ptap = str1%x(:, ap)
817  ptbp = str2%x(:, bp)
818  normap = str1%norm(:, ap)
819  normbp = str2%norm(:, bp)
820  perpap = str1%perpNorm(:, ap)
821  perpbp = str2%perpNorm(:, bp)
822 
823  ! Cross zip nodes N1 to N2 on str1 to nodes N3 to N4 on str2
824  ii = 0
825  do while (ii < nstepsa + nstepsb)
826 
827  avalid = .true.
828  bvalid = .true.
829  ! ---------------------------------------------------------------
830  ! Check 1: Point-in-Triangle test: This test considers the
831  ! triangle ABA+ and determines if any of the neighbouring points
832  ! on either of the two strings is contained inside the
833  ! triangle. If the test is positive, A+ must be rejected. The
834  ! same test is repeated for B+.
835  ! ---------------------------------------------------------------
836 
837  if (trioverlap(pta, ptb, ptap, str1, a, ap) .or. &
838  trioverlap(pta, ptb, ptap, str2, b, b)) then
839  avalid = .false.
840  end if
841 
842  if (trioverlap(pta, ptb, ptbp, str1, a, a) .or. &
843  trioverlap(pta, ptb, ptbp, str2, b, bp)) then
844  bvalid = .false.
845  end if
846 
847  ! ---------------------------------------------------------------
848  ! Check 2: Convex quadrilaterl test: This test considers the
849  ! quadrilateral ABB+A+ and determines if it is convex. For
850  ! connection to point A+ to be valid, the vector areas of
851  ! triangles ABA+ and BB+A+ should have the same size. For
852  ! connection to B+ to be valid, the vector areas of trianges ABB+
853  ! and AB+A+ should be the same sign. NOTE THAT THIS TEST DOES NOT
854  ! ACTUALLY WORK. IT IS 100% INCORRECT!!! THERE ARE CASES WHERE
855  ! THE SIGN OF BOTH AREAS ARE OPPOSITE! IT CANNOT BE SAFELY USED.
856  ! ---------------------------------------------------------------
857 
858  ! area1 = positiveTriArea(ptA, ptB, ptAp, normB)
859  ! area2 = positiveTriArea(ptB, ptBp, ptAp, normB)
860 
861  ! if (area1 .neqv. area2) then
862  ! aValid = .False.
863  ! end if
864 
865  ! area1 = positiveTriArea(ptA, ptB, ptBp, normA)
866  ! area2 = positiveTriArea(ptAp, ptBp, ptA, normA)
867 
868  ! if (area1 .neqv. area2) then
869  ! bValid = .False.
870  ! end if
871 
872  ! Instead, check if the triangle we're going to add has a
873  ! positive or negative vector area
874 
875  area1 = positivetriarea(pta, ptb, ptap, norma)
876  if (area1 .eqv. .false.) then
877  avalid = .false.
878  end if
879 
880  area2 = positivetriarea(pta, ptb, ptbp, normb)
881  if (area2 .eqv. .false.) then
882  bvalid = .false.
883  end if
884 
885  ! ---------------------------------------------------------------
886  ! Check 3: Prism volume test: Using the surface normals,
887  ! "extrude" a prisim in the direction of each surface normal and
888  ! find it's volume. It is is not positive, reject the
889  ! triangle. Since we don't have the node off wall, we will have
890  ! to make do with the normal vectors and average cell size. We
891  ! average the cell size and divide by 1000 to give an approximate
892  ! offwall distance. Then we use the norm veectors to offset in
893  ! that distance to produce the "off" points.
894  ! ---------------------------------------------------------------
895 
896  ! h = quarter*(str1%h(A) + str1%h(Ap) + str2%h(B) + str2%h(Bp)) / 1000
897  ! AOff = ptA + normA * h
898  ! BOff = ptB + Bnorm * h
899  ! ApOff = ptAp + normAp * h
900  ! BpOff = ptBp + normBp * h
901 
902  ! if (prismVol(A, B, Ap, Aoff, Boff, ApOff) < zero) then
903  ! aValid = .False.
904  ! end if
905 
906  ! if (prismVol(A, B, Bp, Aoff, Boff, BpOff) < zero) then
907  ! bValid = .False.
908  ! end if
909 
910  ! ---------------------------------------------------------------
911  ! Check 4: Interpolation stencil test: This one isn't implemented
912  ! ---------------------------------------------------------------
913 
914  ! ---------------------------------------------------------------
915  ! Check 5: Surface normal compatibility test. The surface normal
916  ! from the triangle should be pointing (mostly) in the same
917  ! direction as the normal of the quad that this triangle shares
918  ! and edge with. THIS ALSO DOES NOT WORK! What we have to do
919  ! instead, is check the normal tri normal against the node
920  ! normals it would be using. This is simplier and is vastly
921  ! superior.
922  ! ---------------------------------------------------------------
923 
924  call cross_prod(ptb - pta, ptap - pta, trinorm1)
925  trinorm1 = trinorm1 / mynorm2(trinorm1)
926 
927  ! Compute the sum of the dot product of the nodal norms with the triNorm
928  sum1 = dot_product(trinorm1, norma) + dot_product(trinorm1, normb) + &
929  dot_product(trinorm1, normap)
930 
931  call cross_prod(ptb - pta, ptbp - pta, trinorm2)
932  trinorm2 = trinorm2 / mynorm2(trinorm2)
933 
934  sum2 = dot_product(trinorm2, norma) + dot_product(trinorm2, normb) + &
935  dot_product(trinorm2, normbp)
936 
937  ! Only use this to help pick one if both are still valid:
938  if (avalid .and. bvalid .and. dot_product(trinorm1, trinorm2) < 0.8) then
939 
940  ! Only use this to help pick one if both are still valid:
941 
942  if (sum1 < cutoff .and. sum2 > cutoff) then
943  avalid = .false.
944 
945  else if (sum2 < cutoff .and. sum1 > cutoff) then
946  bvalid = .false.
947 
948  else if (sum1 < cutoff .and. sum2 < cutoff) then
949  ! Both bad. Take the least bad one
950  if (sum1 > sum2) then
951  bvalid = .false.
952  else
953  avalid = .false.
954  end if
955  end if
956  end if
957 
958  ! ---------------------------------------------------------------
959  ! Check 6: Front angle test: Try to keep the front as close as
960  ! possible to the gap edges.
961  ! ---------------------------------------------------------------
962 
963  ! Triangle ABA+. Original implemnetation
964  sum1 = abs(vecangle(pta - ptap, ptb - ptap)) + abs(vecangle(ptbp - ptb, ptap - ptb))
965  sum2 = abs(vecangle(pta - ptbp, ptb - ptbp)) + abs(vecangle(ptbp - pta, ptap - pta))
966 
967  if (sum1 > sum2) then
968  apreferred = .true.
969  else
970  apreferred = .false.
971  end if
972 
973  ! ---------------------------------------------------------------
974  ! Check 7: End of string test
975  ! ---------------------------------------------------------------
976 
977  if (a == ap) then
978  avalid = .false.
979  bvalid = .true.
980  end if
981 
982  if (b == bp) then
983  bvalid = .false.
984  avalid = .true.
985  end if
986 
987  ! ---------------------------------------------------------------
988  ! Decide on the triangle we want to take.
989  ! ---------------------------------------------------------------
990 
991  if (avalid .and. .not. bvalid) then
992 
993  ! We have no choice but to take A+
994  call addtri(a, str1, b, str2, ap, str1)
995  advancea = .true.
996  advanceb = .false.
997  ! Flag the nodes as used
998  str1%xZipNOdeUsed(a) = 1
999  str1%xZipNOdeUsed(ap) = 1
1000  str2%xZipNOdeUsed(b) = 1
1001 
1002  else if (bvalid .and. .not. avalid) then
1003 
1004  ! We have no choice but to take B+
1005 
1006  call addtri(a, str1, b, str2, bp, str2)
1007  str1%xZipNOdeUsed(a) = 1
1008  str2%xZipNOdeUsed(b) = 1
1009  str2%xZipNOdeUsed(bp) = 1
1010 
1011  advancea = .false.
1012  advanceb = .true.
1013 
1014  else if (avalid .and. bvalid) then
1015 
1016  ! We could take either. Use the preferred triangle.
1017  if (apreferred) then
1018 
1019  call addtri(a, str1, b, str2, ap, str1)
1020 
1021  str1%xZipNOdeUsed(a) = 1
1022  str1%xZipNOdeUsed(ap) = 1
1023  str2%xZipNOdeUsed(b) = 1
1024 
1025  advancea = .true.
1026  advanceb = .false.
1027 
1028  else
1029 
1030  call addtri(a, str1, b, str2, bp, str2)
1031  str1%xZipNOdeUsed(a) = 1
1032  str2%xZipNOdeUsed(b) = 1
1033  str2%xZipNOdeUsed(bp) = 1
1034 
1035  advancea = .false.
1036  advanceb = .true.
1037 
1038  end if
1039 
1040  else
1041 
1042  ! Things are not looking good...but
1043 
1044  if (avalidprev .and. bvalidprev) then
1045  ! We might be able to save it! The last triangle we added
1046  ! was a choice..both were valid, but we picked one
1047  ! because it was preferred. Now we know the one we did
1048  ! pick screwed us for the next triangle...go back and
1049  ! pick the other one instead!
1050 
1051  ! First 'delete' the triangle by decrementing the tri
1052  ! counter and edge counters
1053  p => str1%p
1054  p%nTris = p%nTris - 1
1055  p%nEdges = p%nEdges - 3
1056 
1057  ! Now we determine which one was actually added and add
1058  ! the other one instead
1059 
1060  if (advanceaprev) then
1061  ! We need to add the old B triangle instead, which
1062  ! means the A triangle we had added was bad
1063  avalidprev = .false.
1064  stepsb = stepsb + 1
1065  stepsa = stepsa - 1
1066 
1067  call addtri(aprev, str1, b, str2, bp, str2)
1068 
1069  ! Reset the 'A' data by shuffling backwards: The 'A'
1070  ! data is copied to 'Ap' and the 'A' data is restored from Aprev
1071 
1072  ap = a
1073  ptap = pta
1074  normap = norma
1075  perpap = perpa
1076 
1077  a = aprev
1078  pta = str1%x(:, a)
1079  norma = str1%norm(:, a)
1080  perpa = str1%perpNorm(:, a)
1081 
1082  ! Increment the 'B' data since we actually used B
1083 
1084  b = bp
1085  ptb = ptbp
1086  normb = normbp
1087  perpb = perpbp
1088 
1089  ! And get the new data for Bp
1090  bp = nextnode(str2, b, .false.)
1091  ptbp = str2%x(:, bp)
1092  normbp = str2%norm(:, bp)
1093  perpbp = str2%perpNorm(:, bp)
1094 
1095  ! We *actually* advanced B so..
1096  advancebprev = .true.
1097  advanceaprev = .false.
1098  else
1099 
1100  ! We need to add the old A triangle, which means the B
1101  ! triangle we had added was bad
1102  bvalidprev = .false.
1103  stepsb = stepsb - 1
1104  stepsa = stepsa + 1
1105  call addtri(a, str1, bprev, str2, ap, str1)
1106 
1107  ! Reset the 'B' data by shuffling backwards: The 'B'
1108  ! data is copied to 'Bp' and the 'B' data is restored from Bprev
1109 
1110  bp = b
1111  ptbp = ptb
1112  normbp = normb
1113  perpbp = perpb
1114 
1115  b = bprev
1116  ptb = str2%x(:, b)
1117  normb = str2%norm(:, b)
1118  perpb = str2%perpNorm(:, b)
1119 
1120  ! Increment the 'A' data since we actually used A
1121 
1122  a = ap
1123  pta = ptap
1124  norma = normap
1125  perpa = perpap
1126 
1127  ! And get the new data for Ap
1128  ap = nextnode(str2, a, .true.)
1129  ptap = str1%x(:, ap)
1130  normap = str1%norm(:, ap)
1131  perpap = str1%perpNorm(:, ap)
1132  ! We *actually* advanced A so..
1133  advanceaprev = .true.
1134  advancebprev = .false.
1135 
1136  end if
1137 
1138  ! We *don't* increment ii since this is in essence still
1139  ! the "last" iteration. We just cycle and try the current
1140  ! one again.
1141  if (debugzipper) then
1142  print *, 'Saved cross zip from bad front.'
1143  end if
1144  cycle
1145 
1146  end if
1147 
1148  advancea = .false.
1149  advanceb = .false.
1150 
1151  ! Ewww. neither triangle is valid. Do not add the triangle
1152  ! just return and let the cross zip restart. This should
1153  ! skip over the bad area and the pocket zip can do the bad
1154  ! region.
1155  failed = .true.
1156  return
1157 
1158  end if
1159 
1160  ! Now we have to shuffle along the string.
1161  if (advancea .and. .not. advanceb) then
1162 
1163  stepsa = stepsa + 1
1164 
1165  ! Save a copy of the previous A info
1166  aprev = a
1167 
1168  ! Copy the Ap to A
1169  a = ap
1170  pta = ptap
1171  norma = normap
1172  perpa = perpap
1173 
1174  ! And get the new data for Ap
1175  ap = nextnode(str1, a, .true.)
1176  ptap = str1%x(:, ap)
1177  normap = str1%norm(:, ap)
1178  perpap = str1%perpNorm(:, ap)
1179 
1180  else if (advanceb .and. .not. advancea) then
1181 
1182  stepsb = stepsb + 1
1183 
1184  ! Save a copy of the previous B info incase we need it
1185  bprev = b
1186 
1187  ! Copy the Bp to B
1188  b = bp
1189  ptb = ptbp
1190  normb = normbp
1191  perpb = perpbp
1192 
1193  ! And get the new data for Bp
1194  bp = nextnode(str2, b, .false.)
1195  ptbp = str2%x(:, bp)
1196  normbp = str2%norm(:, bp)
1197  perpbp = str2%perpNorm(:, bp)
1198  end if
1199 
1200  ! Save the prevoius valid triangles and what was advanced
1201  avalidprev = avalid
1202  bvalidprev = bvalid
1203  advanceaprev = advancea
1204  advancebprev = advanceb
1205 
1206  ! Finally increment the number of triangles we've used so far.
1207  ii = ii + 1
1208  end do
1209 
1210  contains
1211 
1212  function nextnode(str, i, pos)
1213 
1214  implicit none
1215  type(oversetstring), intent(iN) :: str
1216  integer(kind=intType), intent(in) :: i
1217  logical, intent(in) :: pos
1218  integer(kind=intType) :: nextnode
1219 
1220  if (pos) then
1221  if (stepsa == nstepsa) then
1222  nextnode = i
1223  else
1224  nextnode = i + 1
1225  if (nextnode > str%nNodes) then
1226  if (str%isPeriodic) then
1227  ! Loop back around
1228  nextnode = 1
1229  else
1230  ! Leave it at the same node
1231  nextnode = i
1232  end if
1233  end if
1234  end if
1235  else
1236  if (stepsb == nstepsb) then
1237  nextnode = i
1238  else
1239  nextnode = i - 1
1240  if (nextnode < 1) then
1241  if (str%isPeriodic) then
1242  ! Loop back around
1243  nextnode = str%nNodes
1244  else
1245  ! Leave it at the same node
1246  nextnode = i
1247  end if
1248  end if
1249  end if
1250  end if
1251  end function nextnode
1252 
1253  function vecangle(vec1, vec2)
1254  implicit none
1255 
1256  ! Input/Output
1257  real(kind=realtype), dimension(3), intent(in) :: vec1, vec2
1258  real(kind=realtype) :: vecangle
1259 
1260  ! Working
1261  real(kind=realtype), dimension(3) :: veca, vecb
1262 
1263  veca = vec1 / mynorm2(vec1)
1264  vecb = vec2 / mynorm2(vec2)
1265 
1266  vecangle = acos(dot_product(veca, vecb))
1267 
1268  end function vecangle
1269 
1270  function elembetweennodes(str, a, b)
1271  implicit none
1272 
1273  ! Input/Output
1274  type(oversetstring), intent(in) :: str
1275  integer(kind=intType), intent(in) :: a, b
1276  integer(kind=intType) :: elembetweennodes
1277 
1278  ! Working
1279  integer(kind=intType) :: e1, e2, e3, e4
1280 
1281  if (str%nte(1, a) == 1) then
1282  e1 = str%nte(2, a)
1283  e2 = e1
1284  else
1285  e1 = str%nte(2, a)
1286  e2 = str%nte(3, a)
1287  end if
1288 
1289  if (str%nte(1, b) == 1) then
1290  e3 = str%nte(2, b)
1291  e4 = e3
1292  else
1293  e3 = str%nte(2, b)
1294  e4 = str%nte(3, b)
1295  end if
1296 
1297  ! Two of the edges are the same. And this is the one that must
1298  ! be between the two nodes.
1299 
1300  if (e1 == e3 .or. e1 == e4) then
1301  elembetweennodes = e1
1302  else
1303  elembetweennodes = e2
1304  end if
1305 
1306  end function elembetweennodes
1307 
1308  function triarea(pt1, pt2, pt3)
1309 
1310  use constants
1311  use utils, only: mynorm2, cross_prod
1312  implicit none
1313 
1314  ! Input/Output
1315  real(kind=realtype), intent(in), dimension(3) :: pt1, pt2, pt3
1316  real(kind=realtype) :: triarea
1317 
1318  ! Working
1319  real(kind=realtype), dimension(3) :: norm
1320 
1321  call cross_prod(pt2 - pt1, pt3 - pt1, norm)
1322  triarea = half * mynorm2(norm)
1323 
1324  end function triarea
1325 
1326  end subroutine crosszip
1327 
1328  subroutine addtri(A, sA, B, sB, C, sC)
1329 
1330  ! Form a triangle from index 'A' on string 'sA' , index 'B' on
1331  ! string 'sB' and index 'C' on string 'sC'
1332 
1333  use constants
1334  implicit none
1335 
1336  ! Input/Output
1337  integer(kind=intType), intent(in) :: a, b, c
1338  type(oversetstring), intent(in) :: sA, sB, sC
1339 
1340  ! Working
1341  type(oversetstring), pointer :: p
1342  integer(kind=intType) :: mn1, mn2, mn3
1343  p => sa%p
1344 
1345  p%nTris = p%nTris + 1
1346 
1347  ! mn = master node
1348  mn1 = sa%pNodes(a)
1349  mn2 = sb%pNodes(b)
1350  mn3 = sc%pNodes(c)
1351 
1352  p%tris(:, p%nTris) = (/mn1, mn2, mn3/)
1353 
1354  ! Add these three edges to master list of edges
1355 
1356  ! Edge 1:
1357  p%nEdges = p%nEdges + 1
1358  p%edges(p%nEdges)%n1 = mn1
1359  p%edges(p%nEdges)%n2 = mn2
1360 
1361  ! Edge 2:
1362  p%nEdges = p%nEdges + 1
1363  p%edges(p%nEdges)%n1 = mn2
1364  p%edges(p%nEdges)%n2 = mn3
1365 
1366  ! Edge 3:
1367  p%nEdges = p%nEdges + 1
1368  p%edges(p%nEdges)%n1 = mn3
1369  p%edges(p%nEdges)%n2 = mn1
1370 
1371  end subroutine addtri
1372 
1373  subroutine makecrosszip(p, strings, nStrings, debugZipper)
1374 
1375  use constants
1376  implicit none
1377 
1378  ! Input/output
1379  integer(kind=intType), intent(in) :: nStrings
1380  type(oversetstring), intent(inout), target :: p, strings(nStrings)
1381  type(oversetstring), pointer :: s, s1, s2
1382  logical, intent(in) :: debugZipper
1383  ! Working
1384  integer(kind=intType) :: i, iStart, iEnd, jStart, jEnd, iStart_j, iEnd_j
1385  integer(kind=intType) :: curOtherID, iString, ii, nextI, curIStart, nIElems_j
1386  integer(kind=intType) :: nIElemsBeg, nJElemsBeg, nElem1, nElem2
1387  integer(kind=intType) :: iStart_orig, iEnd_orig, jStart_orig, jEnd_orig
1388  logical :: fullLoop1, fullLoop2, dummy, failed
1389  ! The purpose of this routine is to determine the ranges on two
1390  ! paired strings that are continuously paired and suitable for
1391  ! performing cross zipping.
1392 
1393  ! Allocate arrays to keep track of nodes that have already been
1394  ! used in cross zipping.
1395  do i = 1, nstrings
1396  s => strings(i)
1397  allocate (s%XzipNodeUsed(s%nNodes))
1398  s%xZipNodeUsed = 0
1399  end do
1400 
1401  strloop: do istring = 1, nstrings
1402 
1403  ! Skip strings that were pocekts
1404  if (strings(istring)%isPocket) then
1405  cycle
1406  end if
1407 
1408  ! S1 is the curent '1' string we are working with
1409  s1 => strings(istring)
1410 
1411  ! Find the lowest node number that isn't used:
1412  curistart = startnode(s1)
1413  do while (curistart > 0)
1414 
1415  if (debugzipper) then
1416  print *, '------------------------------------------------'
1417  print *, 'Starting string ', s1%myid, 'at index ', curistart
1418  print *, '------------------------------------------------'
1419  end if
1420 
1421  istart = curistart
1422  ! Other ID is the string attached at the current pt.
1423  curotherid = s1%otherID(1, istart)
1424 
1425  if (curotherid == -1) then
1426  print *, '*************************************************************************'
1427  print *, 'Error during makeCrossZip: Point ', istart, 'does not have a matching point'
1428  print *, 'Position: ', s1%x(:, istart)
1429  print *, '*************************************************************************'
1430  stop
1431  end if
1432 
1433  ! S2 is the current '2' string we are working with
1434  s2 => strings(curotherid)
1435  jstart = s1%otherID(2, istart)
1436 
1437  ! ---------------- s1 increments -------------
1438  ! The goal is to increment s1 as far as we can go in the
1439  ! NEGATIVE direction.
1440  call tracematch(s1, istart, .false., curotherid, iend, fullloop1)
1441 
1442  if (.not. fullloop1) then
1443  ! Now set iStart to iEnd. Basically we start right at the
1444  ! negative end the chain and traverse in the POSITIVE
1445  ! direction.
1446  istart = iend
1447  call tracematch(s1, istart, .true., curotherid, iend, dummy)
1448  end if
1449 
1450  ! Now, iStart -> iEnd (in the positive order) is the maximum
1451  ! possible extent that s1 could be connected to s1
1452  ! over. However, s2 may have something to say about that. We
1453  ! do the same operation for s2. Note that the orders are reversed.
1454 
1455  ! ---------------- s2 increments -------------
1456  call tracematch(s2, jstart, .true., s1%myID, jend, fullloop2)
1457 
1458  ! If the first jnode isnt' actually matched to me, like I am
1459  ! to him. Therefore skip me, and go to the next one.
1460  if (jstart == jend .and. .not. fullloop2 .and. &
1461  s2%otherID(1, jstart) /= s1%myID) then
1462  s1%xZipNodeUsed(curistart) = 1
1463  curistart = startnode(s1)
1464  cycle
1465  end if
1466 
1467  if (.not. fullloop2) then
1468  jstart = jend
1469  call tracematch(s2, jstart, .false., s1%myID, jend, dummy)
1470  end if
1471 
1472  if ((istart == iend .and. .not. fullloop1) .or. &
1473  (jstart == jend .and. .not. fullloop2)) then
1474  ! Can't go anywhere. Flag this node and the next.
1475 
1476  s1%xZipNodeUsed(curistart) = 1
1477  curistart = startnode(s1)
1478  cycle
1479  end if
1480 
1481  if (debugzipper) then
1482  print *, 'Initial Range s1:', istart, iend, fullloop1
1483  print *, 'Initial Range s2:', jstart, jend, fullloop2
1484  end if
1485 
1486  ! Save the original start/endn locations
1487  istart_orig = istart
1488  iend_orig = iend
1489  jstart_orig = jstart
1490  jend_orig = jend
1491 
1492  if ((istart == iend .and. fullloop1) .and. &
1493  (jstart == jend .and. fullloop2)) then
1494  ! s1 fully attached to s2
1495 
1496  call closestsymmetricnode(s1, s2, istart, jstart)
1497  iend = istart
1498  jend = jstart
1499 
1500  else if ((istart == iend .and. fullloop1) .and. .not. fullloop2) then
1501 
1502  ! Project jStart and jEnd onto s1
1503  istart = s2%otherID(2, jstart)
1504  iend = s2%otherID(2, jend)
1505 
1506  else if ((jstart == jend .and. fullloop2) .and. .not. fullloop1) then
1507 
1508  ! Project iStart and iEnd onto s2
1509  jstart = s1%otherID(2, istart)
1510  jend = s1%otherID(2, iend)
1511 
1512  else
1513 
1514  ! part of s1 is attached to part of s2
1515 
1516  nielemsbeg = elemsforrange(s1, istart, iend, .true.)
1517  njelemsbeg = elemsforrange(s2, jstart, jend, .false.)
1518 
1519  ! This the "projection" of the 'j' string on the 'i'
1520  ! string. Basically this is the range the 'j' string
1521  ! wants to "use up" on the i string.
1522  istart_j = s2%otherID(2, jstart)
1523  iend_j = s2%otherID(2, jend)
1524 
1525  ! These could match up with iStart and iEnd or they could
1526  ! not. That's what we need to determine here.
1527 
1528  ! Need to check if iStart_j is "larger" than istart. We
1529  ! just increment using nextNode to take care of periodic
1530  ! boundaries.
1531  if (istart_j /= istart) then
1532  ! The starting points are different. Increment iStart
1533  ! until we find
1534  i = istart
1535  do ii = 1, nielemsbeg
1536  i = nextnode(s1, i)
1537  if (i == istart_j) then
1538  istart = istart_j
1539  exit
1540  end if
1541  end do
1542  end if
1543 
1544  if (iend_j /= iend) then
1545  ! The starting points are different. Decrement jEnd
1546  ! until we find
1547  i = iend
1548  do ii = 1, nielemsbeg
1549  i = prevnode(s1, i)
1550  if (i == iend_j) then
1551  iend = iend_j
1552  exit
1553  end if
1554  end do
1555  end if
1556 
1557  ! Now with the updated range. Project the iRange back to the
1558  ! the final J range.
1559  jstart = s1%otherID(2, istart)
1560  jend = s1%otherID(2, iend)
1561 
1562  end if
1563 
1564  if (debugzipper) then
1565  print *, 'Zipping string: ', s1%myid, ' with ', s2%myid
1566  print *, 's1 range:', istart, iend
1567  print *, 's2 range:', jstart, jend
1568  end if
1569 
1570  ! Before we do the zip, make sure the ranges have not
1571  ! degenerated to 0 elements
1572  nelem1 = 1 ! Doesn't matter, just not zero
1573  nelem2 = 1 ! Doesn't matter, just not zero
1574  if (.not. fullloop1) then
1575  nelem1 = elemsforrange(s1, istart, iend, .true.)
1576  if (istart == iend) then
1577  nelem1 = 0
1578  end if
1579 
1580  end if
1581  if (.not. fullloop2) then
1582  nelem2 = elemsforrange(s2, jstart, jend, .false.)
1583  if (jstart == jend) then
1584  nelem2 = 0
1585  end if
1586  end if
1587 
1588  if (nelem1 > 0 .and. nelem2 > 0) then
1589  ! Do actual cross zip if we still have elements left on both strings
1590  call crosszip(s1, istart, iend, s2, jstart, jend, debugzipper, failed)
1591 
1592  ! If we succefully cross zippered what we were suppoed to
1593  ! flag all the nodes from the original region as done.
1594  if (.not. failed) then
1595  call flagnodesused(s1, istart_orig, iend_orig, .true.)
1596  call flagnodesused(s2, jstart_orig, jend_orig, .false.)
1597  else
1598  ! UhOh. We got stopped part way through. Flag just the
1599  ! nodes at the beginning that we didn't use. Leave
1600  ! those for the pocket. The nodes up to where theh
1601  ! cross zip stopped were flagged internally in cross zip.
1602  call flagnodesused(s1, istart_orig, istart, .true.)
1603  call flagnodesused(s2, jstart_orig, jstart, .false.)
1604  end if
1605  else
1606 
1607  ! Flag the full range of elements are consumed even
1608  ! though we didn't do the cross zip. Leave it for the
1609  ! pocket zipping.
1610  call flagnodesused(s1, istart_orig, iend_orig, .true.)
1611  call flagnodesused(s2, jstart_orig, jend_orig, .false.)
1612  end if
1613 
1614  ! Find the next starting index:
1615  curistart = startnode(s1)
1616 
1617  end do
1618  end do strloop
1619 
1620  contains
1621 
1622  function startnode(s)
1623  ! Determine the lowest index of a non-used xzip node for
1624  ! string 's'.
1625  implicit none
1626  type(oversetstring) :: s
1627  integer(kind=intType) :: startnode, i
1628 
1629  ! This will be the return value if all nodes are used:
1630  startnode = 0
1631  nodeloop: do i = 1, s%nNodes
1632  if (s%xZipNodeUsed(i) == 0) then
1633  startnode = i
1634  exit nodeloop
1635  end if
1636  end do nodeloop
1637  end function startnode
1638 
1639  function nextnode(s, i)
1640 
1641  implicit none
1642  type(oversetstring), intent(iN) :: s
1643  integer(kind=intType), intent(in) :: i
1644  integer(kind=intType) :: nextnode
1645 
1646  ! Normally just increment:
1647  nextnode = i + 1
1648 
1649  if (i == s%nNodes) then
1650  if (s%isPeriodic) then
1651  nextnode = 1
1652  else
1653  ! Can't go any further
1654  nextnode = i
1655  end if
1656  end if
1657 
1658  ! If the next node is used. The next node is set the current
1659  ! one.
1660  if (s%xZipNodeUsed(nextnode) == 1) then
1661  nextnode = i
1662  end if
1663  end function nextnode
1664 
1665  function simplenextnode(s, i)
1666 
1667  implicit none
1668  type(oversetstring), intent(iN) :: s
1669  integer(kind=intType), intent(in) :: i
1670  integer(kind=intType) :: simplenextnode
1671 
1672  ! Normally just increment:
1673  simplenextnode = i + 1
1674 
1675  if (i == s%nNodes) then
1676  if (s%isPeriodic) then
1677  simplenextnode = 1
1678  else
1679  ! Can't go any further
1680  simplenextnode = i
1681  end if
1682  end if
1683  end function simplenextnode
1684 
1685  function prevnode(s, i)
1686 
1687  implicit none
1688  type(oversetstring), intent(iN) :: s
1689  integer(kind=intType), intent(in) :: i
1690  integer(kind=intTYpe) :: prevnode
1691  ! Normally just increment:
1692  prevnode = i - 1
1693 
1694  if (i == 1) then
1695  if (s%isPeriodic) then
1696  prevnode = s%nNodes
1697  else
1698  ! Can't go any further
1699  prevnode = i
1700  end if
1701  end if
1702 
1703  ! If the next node is used. The next node is set the current
1704  ! one.
1705  if (s%xZipNodeUsed(prevnode) == 1) then
1706  prevnode = i
1707  end if
1708  end function prevnode
1709 
1710  subroutine tracematch(s, iStart, pos, checkID, iEnd, fullLoop)
1711 
1712  implicit none
1713 
1714  ! Given a starting position 'iStart' on string 's', traverse in
1715  ! the 'POSitive' or '.not. POSitive' direction checking that the
1716  ! otherID still matches "checkID". Return the ending position
1717  ! 'iEnd'.
1718 
1719  ! Input/Output
1720  type(oversetstring) :: s
1721  integer(kind=intType), intent(in) :: iStart, checkID
1722  logical, intent(in) :: pos
1723  integer(kind=intType), intent(out) :: iEnd
1724  logical, intent(out) :: fullLoop
1725 
1726  ! Working
1727  integer(kind=intType) :: i, nextI
1728 
1729  i = istart
1730  fullloop = .false.
1731 
1732  traverseloop: do
1733  if (pos) then
1734  nexti = nextnode(s, i)
1735  else
1736  nexti = prevnode(s, i)
1737  end if
1738  if (nexti == i .or. s%otherID(1, nexti) /= checkid) then
1739  ! We can't go any further than we already are
1740  iend = i
1741  exit traverseloop
1742  end if
1743 
1744  ! Continue to the next one.
1745  i = nexti
1746 
1747  if (i == istart) then
1748  fullloop = .true.
1749  iend = i
1750  exit traverseloop
1751  end if
1752  end do traverseloop
1753  end subroutine tracematch
1754 
1755  subroutine flagnodesused(s, N1, N2, pos)
1756 
1757  implicit none
1758 
1759  ! Input/Output
1760  type(oversetstring) :: s
1761  integer(kind=intType), intent(in) :: N1, N2
1762  logical, intent(in) :: pos
1763 
1764  ! Working
1765  integer(kind=intType) :: nSteps, i, nextI
1766 
1767  if (pos) then
1768  if (n2 > n1) then
1769  nsteps = n2 - n1
1770  else if (n2 < n1) then
1771  nsteps = n2 + s%nNodes - n1
1772  else ! N1 == N2
1773  nsteps = s%nElems
1774  end if
1775  else
1776  if (n1 < n2) then
1777  nsteps = n1 + s%nNodes - n2
1778  else if (n1 > n2) then
1779  nsteps = n1 - n2
1780  else ! N3 == N4
1781  nsteps = s%nElems
1782  end if
1783  end if
1784 
1785  s%xZipNodeUsed(n1) = 1
1786  i = n1
1787  do ii = 1, nsteps
1788  if (pos) then
1789  nexti = nextnode(s, i)
1790  else
1791  nexti = prevnode(s, i)
1792  end if
1793 
1794  s%xZipNodeUsed(nexti) = 1
1795  i = nexti
1796  end do
1797  end subroutine flagnodesused
1798 
1799  function elemsforrange(s, N1, N2, pos)
1800  ! Determine the number of elements between N1 and N2 for for the
1801  ! "POSitive" or "not POSIitive" (negative) direction.
1802 
1803  implicit none
1804  type(oversetstring) :: s
1805  integer(kind=intType), intent(in) :: n1, n2
1806  logical :: pos
1807  integer(kind=intType) :: elemsforrange
1808 
1809  if (.not. s%isPeriodic) then
1810  if (pos) then
1811  elemsforrange = n2 - n1
1812  else
1813  elemsforrange = n1 - n2
1814  end if
1815  else ! Periodic
1816  if (pos) then
1817  if (n2 == n1) then
1818  elemsforrange = s%nElems
1819  else if (n2 > n1) then
1820  elemsforrange = n2 - n1
1821  else
1822  elemsforrange = n2 + s%nNodes - n1
1823  end if
1824  else
1825  if (n1 == n2) then
1826  elemsforrange = s%nElems
1827  else if (n1 > n2) then
1828  elemsforrange = n1 - n2
1829  else
1830  elemsforrange = n1 + s%nNodes - n2
1831  end if
1832  end if
1833  end if
1834  end function elemsforrange
1835 
1836  end subroutine makecrosszip
1837 
1838  subroutine makepocketzip(p, strings, nStrings, pocketMaster, debugZipper)
1839  use constants
1841  use oversetutilities, only: qsortedgetype
1842  use kdtree2_module
1843  implicit none
1844 
1845  ! Input/output
1846  integer(kind=intType), intent(in) :: nStrings
1847  type(oversetstring), intent(in) :: p, strings(nStrings)
1848  type(oversetstring) :: pocketMaster
1849  logical, intent(in) :: debugZipper
1850 
1851  ! Local variables
1852  integer(kind=intType) :: i, j, nsum1, nsum2, ndiff1, ndiff2, ipedge, icur
1853  integer(kind=intType) :: n1, n2, npolyEdges
1854  integer(kind=intType) :: nNodes1, nNodes2, cn1, cn2, str1, str2
1855  type(oversetedge), allocatable, dimension(:) :: polyEdges
1856  type(oversetedge) :: e1, e2
1857  type(oversetstring), pointer :: stringsLL, str
1858  integer(kind=intType) :: npocketEdges, nFullStrings, nNodes
1859  integer(kind=intType) :: ip, curElem, nElems, iStart, firstElem
1860  type(oversetstring), allocatable, dimension(:), target :: pocketStringsArr
1861 
1862  ! ---------------------------------------------------------------
1863  ! PocketZip 1:
1864  ! First sort the edges.
1865  ! ---------------------------------------------------------------
1866  call qsortedgetype(p%Edges, p%nEdges)
1867 
1868  ! Now gather up the left-over edges for pocket zipping.
1869 
1870  ! Over estimate of remaining pocket edges to zip
1871  allocate (polyedges(p%nEdges))
1872 
1873  ! Eliminate the edges going through the ordered edges.
1874  ! The sorted opposite edges are canceled in pairs.
1875  npolyedges = 0
1876  i = 1
1877  do while (i <= p%nEdges)
1878 
1879  if (i == p%nEdges) then
1880  ! This must be the last free edge:
1881  e1 = p%Edges(i)
1882  npolyedges = npolyedges + 1
1883  polyedges(npolyedges) = e1
1884  i = i + 1
1885  cycle
1886  end if
1887 
1888  ! Two edges in sequence
1889  e1 = p%Edges(i)
1890  e2 = p%Edges(i + 1)
1891 
1892  ! First determine if e1 is at the end of two single ended
1893  ! chains. In this case the edge *will* not be paired and that's
1894  ! correct.
1895 
1896  str1 = p%cNodes(1, e1%n1) ! node1's child fullStrings ID
1897  cn1 = p%cNodes(2, e1%n1) ! node1's child fullStrings node index
1898  nnodes1 = strings(str1)%nNodes ! node1's child fullStrings nNodes size
1899 
1900  str2 = p%cNodes(1, e1%n2) ! node2's child fullStrings ID
1901  cn2 = p%cNodes(2, e1%n2) ! node2's child fullStrings node index
1902  nnodes2 = strings(str2)%nNodes ! node2's child fullStrings nNodes size
1903 
1904  if (str1 /= str2) then
1905  if (.not. strings(str1)%isperiodic .and. &
1906  .not. strings(str2)%isperiodic .and. &
1907  (cn1 == 1 .or. cn1 == nnodes1) .and. (cn2 == 1 .or. cn2 == nnodes2)) then
1908  ! Increment just 1 in 1 to skip over edge e1.
1909  i = i + 1
1910  cycle
1911  end if
1912  end if
1913 
1914  ! The sum and difference:
1915  nsum1 = e1%n1 + e1%n2
1916  nsum2 = e2%n1 + e2%n2
1917 
1918  ndiff1 = e1%n2 - e1%n1
1919  ndiff2 = e2%n2 - e2%n1
1920 
1921  if (nsum1 == nsum2 .and. ndiff1 + ndiff2 == 0) then
1922  ! These edges cancel. Great.
1923  i = i + 2
1924  cycle
1925  else
1926  ! Add just the first edge
1927  npolyedges = npolyedges + 1
1928  polyedges(npolyedges) = e1
1929  i = i + 1
1930  end if
1931  end do
1932 
1933  ! Define pocketMaster string
1934  call nullifystring(pocketmaster)
1935  pocketmaster%myID = 88
1936  pocketmaster%nElems = npolyedges
1937  pocketmaster%nNodes = npolyedges * 2
1938  pocketmaster%nEdges = 0
1939  allocate (pocketmaster%nodeData(10, 2 * npolyedges), &
1940  pocketmaster%intNodeData(3, 2 * npolyedges), &
1941  pocketmaster%conn(2, npolyedges))
1942 
1943  ! Dump the data into the pocketMaster
1944  do i = 1, npolyedges
1945  pocketmaster%nodeData(:, 2 * i - 1) = p%nodeData(:, polyedges(i)%n1)
1946  pocketmaster%intNodeData(:, 2 * i - 1) = p%intNodeData(:, polyedges(i)%n1)
1947 
1948  pocketmaster%nodeData(:, 2 * i) = p%nodeData(:, polyedges(i)%n2)
1949  pocketmaster%intNodeData(:, 2 * i) = p%intNodeData(:, polyedges(i)%n2)
1950  pocketmaster%conn(:, i) = (/2 * i, 2 * i - 1/)
1951  end do
1952 
1953  call setstringpointers(pocketmaster)
1954  call reducegapstring(pocketmaster)
1955  call createnodetoelem(pocketmaster)
1956 
1957  ! The next step is to create ordered strings based on the
1958  ! connectivity. This is a purely logical operation. We don't know
1959  ! how many actual strings we will need so we will use a linked
1960  ! list as we go.
1961 
1962  ! Allocate some additional arrays we need for doing the chain
1963  ! searches.
1964  nelems = pocketmaster%nElems
1965  nnodes = pocketmaster%nNodes
1966  allocate (pocketmaster%elemUsed(nelems), pocketmaster%subStr(2, nelems), &
1967  pocketmaster%cNodes(2, nnodes))
1968 
1969  pocketmaster%cNodes = 0
1970  pocketmaster%elemUsed = 0
1971  curelem = 1
1972  nfullstrings = 0
1973 
1974  do while (curelem < pocketmaster%nElems)
1975 
1976  ! Arbitrarily get the first node for my element:
1977  istart = pocketmaster%conn(1, curelem)
1978  nelems = pocketmaster%nte(1, istart)
1979 
1980  firstelem = pocketmaster%nte(2, istart)
1981  pocketmaster%subStr(1, 1) = firstelem
1982  call dochain(pocketmaster, istart, 1)
1983 
1984  ! We now have a boundary string stored in master%subString(1,
1985  ! :nSubStr(1)). These are actually the element numbers of the
1986  ! master that form a continuous chain.
1987 
1988  ! Create or add a new string to our linked list
1989  ! "stringsLL".
1990  if (nfullstrings == 0) then
1991  allocate (stringsll)
1992  nfullstrings = 1
1993  stringsll%next => stringsll
1994  str => stringsll
1995  else
1996  allocate (str%next)
1997  str%next%next => stringsll
1998  str => str%next
1999  nfullstrings = nfullstrings + 1
2000  end if
2001 
2002  ! Create a substring from master based on the elements we
2003  ! have in the buffer
2004  call createsubstringfromelems(pocketmaster, str, nfullstrings)
2005 
2006  ! Scan through until we find the next unused element:
2007  do while ((pocketmaster%elemUsed(curelem) == 1) .and. &
2008  (curelem < pocketmaster%nElems))
2009  curelem = curelem + 1
2010  end do
2011  end do
2012 
2013  ! Temporary strings array for plotting and pocketZipping
2014  allocate (pocketstringsarr(nfullstrings))
2015  str => stringsll
2016  i = 0
2017  do while (i < nfullstrings)
2018  i = i + 1
2019  pocketstringsarr(i) = str ! Derived type assignment
2020  call nullifystring(str)
2021  str => str%next
2022  end do
2023 
2024  ! Allocate space for pocket triangles.
2025  ! (n-sided polygon -> n-2 triangles)
2026  allocate (pocketmaster%tris(3, 10 * pocketmaster%nElems))
2027  allocate (pocketmaster%edges(4 * pocketmaster%nElems))
2028  pocketmaster%nTris = 0
2029 
2030  ! Build the pocketMaster tree
2031  pocketmaster%tree => kdtree2_create(pocketmaster%x, sort=.true.)
2032 
2033  if (debugzipper) then
2034  open (unit=101, file="strings_pocket.dat", form='formatted')
2035  write (101, *) 'TITLE = "PocketStrings Data" '
2036 
2037  write (101, *) 'Variables = "X" "Y" "Z" "Nx" "Ny" "Nz" "Vx" "Vy" "Vz" "ind" &
2038  &"gapID" "gapIndex" "otherID" "otherIndex" "ratio"'
2039  do i = 1, nfullstrings
2040  ! Temporarily allocate otherID
2041  allocate (pocketstringsarr(i)%otherID(2, pocketstringsarr(i)%nNodes))
2042  pocketstringsarr(i)%otherID = -1
2043 
2044  call writeoversetstring(pocketstringsarr(i), pocketstringsarr, &
2045  nfullstrings, 101)
2046  end do
2047  close (101)
2048  end if
2049 
2050  ! Loop over pocketStrings and begin pocketZip starting
2051  ! from smallest convex ear.
2052  do i = 1, nfullstrings
2053  if (debugzipper) then
2054  print *, 'Pocket Zipping String ', i, ' of ', nfullstrings
2055  end if
2056  pocketziploop: do while (pocketstringsarr(i)%nNodes > 2)
2057  ! Each pass zips one triangle. Keep zipping
2058  ! until last triangle is zipped in the pocket polygon.
2059  call pocketzip(pocketstringsarr(i))
2060  end do pocketziploop
2061  end do
2062 
2063  ! Destroy the strings array
2064  do i = 1, nfullstrings
2065  call deallocatestring(pocketstringsarr(i))
2066  end do
2067  deallocate (pocketstringsarr, polyedges)
2068 
2069  end subroutine makepocketzip
2070 
2071  subroutine pocketzip(s)
2072 
2073  use constants
2074  use kdtree2_module
2075  use utils, only: mynorm2, cross_prod
2076 
2077  implicit none
2078 
2079  ! Input parameters
2080  type(oversetstring), intent(inout), target :: s
2081 
2082  ! Local variables
2083  integer(kind=intType) :: i, j, k, ii, im1, ip1, N
2084  integer(kind=intType) :: nNodes, nElems, iimin
2085  real(kind=realtype), dimension(3) :: v1, v2, norm, c
2086  real(kind=realtype) :: coscutoff, costheta, r2, v1nrm, v2nrm, costhetamax
2087  real(kind=realtype) :: dp, dpmax
2088 
2089  integer(Kind=intType), dimension(:), allocatable :: nodeMap, badNode
2090  type(kdtree2_result), dimension(:), allocatable :: results
2091  logical :: added, iiMinSet
2092  real(kind=realtype), parameter :: fact = 0.95_realtype
2093  n = s%nNodes
2094  allocate (results(25), nodemap(n), badnode(n))
2095  nodemap = 1
2096  badnode = 0 ! Will become 1 if bad
2097  outerziploop: do
2098 
2099  ! No choice for the last triangle:
2100  if (n == 3) then
2101  ii = 1
2102  im1 = prevnode(ii)
2103  ip1 = nextnode(ii)
2104  ! We don't call addPotentialTriangle because we don't have a
2105  ! choice anymore. Just call the raw addTri command
2106  call addtri(ip1, s, ii, s, im1, s)
2107  ! and flag the node as gone
2108  nodemap(ii) = 0
2109  exit outerziploop
2110  end if
2111 
2112  iiminset = .false.
2113 
2114  ! First find the largest dot product:
2115  dpmax = -one
2116  nodeloop1: do ii = 1, n
2117 
2118  if (badnode(ii) == 1) then
2119  cycle nodeloop1
2120  end if
2121 
2122  ip1 = nextnode(ii)
2123  im1 = prevnode(ii)
2124 
2125  ! Determine the angle between the vectors
2126  v1 = s%x(:, im1) - s%x(:, ii)
2127  v2 = s%x(:, ip1) - s%x(:, ii)
2128  v1nrm = mynorm2(v1)
2129  v2nrm = mynorm2(v2)
2130  call cross_prod(v2, v1, norm)
2131  norm = norm / mynorm2(norm)
2132  dpmax = max(dpmax, dot_product(norm, s%norm(:, ii)))
2133  end do nodeloop1
2134 
2135  ! Next find the largest cosTheta that is winthin a factor
2136  ! of dpMax
2137  costhetamax = -large
2138  nodeloop2: do ii = 1, n
2139 
2140  if (badnode(ii) == 1) then
2141  cycle nodeloop2
2142  end if
2143 
2144  ip1 = nextnode(ii)
2145  im1 = prevnode(ii)
2146 
2147  ! Determine the angle between the vectors
2148  v1 = s%x(:, im1) - s%x(:, ii)
2149  v2 = s%x(:, ip1) - s%x(:, ii)
2150  v1nrm = mynorm2(v1)
2151  v2nrm = mynorm2(v2)
2152  call cross_prod(v2, v1, norm)
2153  norm = norm / mynorm2(norm)
2154  dp = dot_product(norm, s%norm(:, ii))
2155  if (dp > dpmax * fact) then ! We take this
2156  costheta = dot_product(v1, v2) / (v1nrm * v2nrm)
2157  if (costheta > costhetamax) then
2158  costhetamax = costheta
2159  iiminset = .true.
2160  iimin = ii
2161  end if
2162  end if
2163  end do nodeloop2
2164 
2165  if (iiminset) then
2166  ! Zip about node "iimin" if it was set:
2167  ii = iimin
2168  ip1 = nextnode(ii)
2169  im1 = prevnode(ii)
2170  call addpotentialtriangle(s, ip1, ii, im1, nodemap, results, added)
2171  if (added) then
2172  ! This triangle was good!
2173  exit outerziploop
2174  else
2175  ! Bad node. Need to cycle through rest of pocket nodes.
2176  ! Remember this bad node in next cycle.
2177  badnode(ii) = 1
2178  cycle outerziploop
2179  end if
2180  else
2181  ! What does this mean? We didn't find any node to zip. Are they all bad?
2182  print *, 'Problem with pocket zipper. Somehow we were not able to find "&
2183  &"node to add a triangle on. This should not happen. Contact the "&
2184  &"Developers.'
2185  stop
2186  end if
2187  end do outerziploop
2188 
2189  ! Modify the pocketStrings to remove the two elements and the node
2190  ! that got eliminated due to pocketZipping.
2191  call shortenstring(s, nodemap)
2192  deallocate (nodemap, badnode, results)
2193 
2194  contains
2195  function nextnode(ii)
2196  implicit none
2197  integer(kind=intType) :: ii, nextnode
2198  nextnode = ii + 1
2199  if (ii == n) then
2200  nextnode = 1
2201  end if
2202  end function nextnode
2203 
2204  function prevnode(ii)
2205  implicit none
2206  integer(kind=intType) :: ii, prevnode
2207  prevnode = ii - 1
2208  if (ii == 1) then
2209  prevnode = n
2210  end if
2211  end function prevnode
2212  end subroutine pocketzip
2213 
2214  subroutine computetrisurfarea(master, area)
2215 
2216  ! Computes area sum of all triangles belonging to object master
2217  use constants
2218  use utils, only: mynorm2, cross_prod
2219  implicit none
2220 
2221  ! Input parameters
2222  type(oversetstring), intent(in) :: master
2223  real(kind=realtype), intent(out) :: area
2224 
2225  ! Local variables
2226  integer(kind=intType) :: i, n1, n2, n3
2227  real(kind=realtype), dimension(3) :: v1, v2, norm
2228 
2229  area = 0.0
2230  do i = 1, master%nTris
2231  n1 = master%tris(1, i)
2232  n2 = master%tris(2, i)
2233  n3 = master%tris(3, i)
2234 
2235  v1 = master%x(:, n2) - master%x(:, n1)
2236  v2 = master%x(:, n3) - master%x(:, n1)
2237  call cross_prod(v1, v2, norm)
2238  area = area + half * mynorm2(norm)
2239  end do
2240 
2241  end subroutine computetrisurfarea
2242 
2243  function trioverlap(pt1, pt2, pt3, str, i1, i2)
2244 
2245  use constants
2246  use utils, only: mynorm2, cross_prod
2247  implicit none
2248 
2249  ! Input/Output
2250  real(kind=realtype), dimension(3), intent(in) :: pt1, pt2, pt3
2251  integer(kind=intType), intent(in) :: i1, i2
2252  type(oversetstring), intent(in) :: str
2253 
2254  ! Working
2255  logical :: trioverlap, intri
2256  integer(kind=intType) :: i
2257  real(kind=realtype) :: trinorm(3)
2258 
2259  ! Note: This is a dumb loop. We need to do a spatial serch here to
2260  ! only check the nodes around the current point.
2261 
2262  call cross_prod(pt2 - pt1, pt3 - pt1, trinorm)
2263  trinorm = trinorm / mynorm2(trinorm)
2264 
2265  trioverlap = .false.
2266  do i = 1, str%nNodes
2267  if (i /= i1 .and. i /= i2) then
2268  if (dot_product(str%norm(:, i), trinorm) > 0.8) then
2269  call pointintriangle(pt1, pt2, pt3, str%x(:, i), intri)
2270  if (intri) then
2271  trioverlap = .true.
2272  exit
2273  end if
2274  end if
2275  end if
2276  end do
2277  end function trioverlap
2278 
2279  subroutine shortenstring(s, nodeMap)
2280 
2281  ! This is an auxilary routine that take a string 's', and a node
2282  ! map of len s%nNodes, with 1 or 0. A 1 means that the node will
2283  ! be in the shortened string, 0 means that the node should be
2284  ! deleted.
2285  use constants
2286  implicit none
2287 
2288  ! Input/Output
2289  type(oversetstring) :: s
2290  integer(kind=intType), dimension(:), intent(inout) :: nodemap
2291 
2292  ! Working
2293  integer(kind=intType) :: nnodes, nelems, nremoved, i, j
2294  real(kind=realtype), dimension(:, :), pointer :: nodedatatmp
2295  integer(kind=intType), dimension(:, :), pointer :: conntmp, intnodedatatmp
2296  integer(kind=intType), dimension(:), pointer :: pnodestmp
2297 
2298  ! Now we will modify our string to remove the elements and nodes
2299  ! that got knocked off due to self zipping. This way the calling
2300  ! process still sees the same string, it just gets a little
2301  ! shorter.
2302 
2303  ! Save pointers to existing data
2304  nnodes = s%nNodes
2305  nelems = s%nElems
2306  nodedatatmp => s%nodeData
2307  intnodedatatmp => s%intNodeData
2308  conntmp => s%conn
2309  pnodestmp => s%pNodes
2310 
2311  ! Convert the nodeMap which currently contains a one if the node
2312  ! still exists and 0 if it doesn't. This will convert it to the new
2313  ! node numbers. Ie nodeMap(i) gives the new node index of the
2314  ! shorted chain. If nodeMap(i) = 0, it is no longer part of the
2315  ! chain.
2316  j = 0
2317  nremoved = 0
2318  do i = 1, s%nNodes
2319  if (nodemap(i) == 1) then
2320  j = j + 1
2321  nodemap(i) = j
2322  else
2323  nremoved = nremoved + 1
2324  end if
2325  end do
2326 
2327  ! Update the cNodes in the parent so they point to the updated node
2328  ! numbers. Note that the nodes that have been eliminated, have cNode
2329  ! = 0, which will identify that it no longer has a child node.
2330  do i = 1, s%nNodes
2331  s%p%cNodes(:, s%pNodes(i)) = (/s%myID, nodemap(i)/)
2332  end do
2333 
2334  ! Update the number of nodes/elems in our shorted chain. Every
2335  ! zipper reduces the number of nodes and number of elems by 1
2336  s%nNodes = s%nNodes - nremoved
2337  s%nElems = s%nElems - nremoved
2338 
2339  allocate (s%nodeData(10, s%nNodes), s%intNodeData(3, s%nNodes), &
2340  s%pNodes(s%nNodes), s%conn(2, s%nElems))
2341 
2342  ! Set the pointers for the new string
2343  call setstringpointers(s)
2344 
2345  do i = 1, nnodes
2346  if (nodemap(i) /= 0) then
2347  s%nodeData(:, nodemap(i)) = nodedatatmp(:, i)
2348  s%intNodeData(:, nodemap(i)) = intnodedatatmp(:, i)
2349  s%pNodes(nodemap(i)) = pnodestmp(i)
2350  end if
2351  end do
2352 
2353  ! Since we know the string was in order, we can simply redo the connectivity
2354  do i = 1, s%nElems
2355  s%conn(:, i) = (/i, i + 1/)
2356  end do
2357 
2358  if (s%isPeriodic) then
2359  s%conn(2, s%nElems) = 1
2360  end if
2361 
2362  ! Dellocate the existing memory
2363  deallocate (nodedatatmp, intnodedatatmp, conntmp, pnodestmp)
2364 
2365  ! Recreate the node to elem
2366  if (s%nNodes >= 3) then
2367  call createnodetoelem(s)
2368  end if
2369 
2370  end subroutine shortenstring
2371 
2372  subroutine addpotentialtriangle(s, im1, ii, ip1, nodeMap, results, added)
2373 
2374  ! Common routine (for pocketZip and selfZip) to potentially add a
2375  ! triangle resulting from a single string.
2376  use constants
2378  use kdtree2_module
2379  implicit none
2380 
2381  ! Input/Output
2382  type(oversetstring) :: s
2383  integer(kind=intType), intent(in) :: im1, ii, ip1
2384  integer(kind=intType), intent(inout), dimension(:) :: nodeMap
2385  type(kdtree2_result), dimension(:), allocatable :: results
2386  logical, intent(out) :: added
2387  ! Working:
2388  real(kind=realtype) :: r2
2389  real(kind=realtype), dimension(3) :: v1, v2, norm, c
2390  integer(kind=intType) :: nFound, nalloc, idx, k, j, i
2391  logical :: overlapFound, inTri
2392 
2393  ! We may have a valid triangle. We need to make sure we
2394  ! don't overlap anyone else.
2395  !
2396  ! xim1 +
2397  ! | \
2398  ! | \
2399  ! | c
2400  ! | \
2401  ! | \
2402  ! +----------+
2403  ! xi xip1
2404  ! We do a ball search based at 'c' which is just the
2405  ! (average of xip1 and xim1) using a radius defined as the
2406  ! maximum of (the distance between 'c' and 'xi', half
2407  ! length of xip1 to xim1)
2408  !
2409  added = .false.
2410 
2411  c = half * (s%x(:, ip1) + s%x(:, im1))
2412  r2 = (c(1) - s%x(1, ii))**2 + (c(2) - s%x(2, ii))**2 + (c(3) - s%x(3, ii))**2
2413 
2414  r2 = max(r2, (s%x(1, ip1) - s%x(1, im1))**2 + (s%x(2, ip1) - s%x(2, im1))**2 + &
2415  (s%x(3, ip1) - s%x(3, im1))**2)
2416 
2417  nfound = 0
2418  outerloop: do
2419  nalloc = size(results)
2420  call kdtree2_r_nearest(s%p%tree, c, r2, nfound, nalloc, results)
2421  if (nfound < nalloc) then
2422  exit outerloop
2423  end if
2424 
2425  ! Allocate more space and keep going
2426  deallocate (results)
2427  nalloc = nalloc * 2
2428  allocate (results(nalloc))
2429  end do outerloop
2430 
2431  ! We can now be sure that we have all the points inside our
2432  ! ball. Next we proceed to systematically check them.
2433  overlapfound = .false.
2434  nodefoundloop: do k = 1, nfound
2435  ! Note that we do check nodes from our own string,
2436  ! except for the the three nodes we're dealing
2437  ! with. Remember that we are working in our parent's
2438  ! ording here.
2439  idx = results(k)%idx
2440 
2441  notpartoftriangle: if (idx /= s%pNodes(im1) .and. &
2442  idx /= s%pNodes(ii) .and. idx /= s%pNodes(ip1)) then
2443 
2444  ! Only check if the node normal of the point we're
2445  ! checking is in the same direction as the triangle.
2446  if (dot_product(s%norm(:, ii), s%p%norm(:, idx)) > zero) then
2447 
2448  ! Finally do the actual trianlge test
2449  call pointintriangle(s%x(:, ip1), s%x(:, ii), s%x(:, im1), &
2450  s%p%x(:, idx), intri)
2451  if (intri) then
2452  ! As soon as 1 is in the triangle, we know the
2453  ! triangle is no good.
2454  overlapfound = .true.
2455  exit nodefoundloop
2456  end if
2457  end if
2458  end if notpartoftriangle
2459  end do nodefoundloop
2460 
2461  if (.not. overlapfound) then
2462 
2463  ! This triangle is good!
2464  added = .true.
2465 
2466  ! Call the generic addTri Routine. Here all the ndoes from the
2467  ! triangle come from the same string.
2468  call addtri(ip1, s, ii, s, im1, s)
2469 
2470  ! Flag this node as gone
2471  nodemap(ii) = 0
2472 
2473  end if
2474  end subroutine addpotentialtriangle
2475 
2476  subroutine closestsymmetricnode(s1, s2, i, j)
2477  use constants
2478  use utils, only: mynorm2
2479 
2480  implicit none
2481 
2482  ! Input/Output
2483  type(oversetstring) :: s1, s2
2484  integer(kind=intType), intent(out) :: i, j
2485  real(kind=realtype) :: mindist, dist
2486  integer(kind=intType) :: ii
2487 
2488  ! Working:
2489  mindist = large
2490 
2491  do ii = 1, s1%nNodes
2492  ! "The other index of the matching node on the other string is
2493  ! me" ie. "I point to you and you point to me"
2494 
2495  if (s2%otherID(2, s1%otherID(2, ii)) == ii) then
2496 
2497  dist = mynorm2(s1%x(:, ii) - s2%x(:, s1%otherID(2, ii)))
2498 
2499  if (dist < mindist) then
2500  mindist = dist
2501  i = ii
2502  j = s1%otherID(2, ii)
2503  end if
2504  end if
2505  end do
2506 
2507  end subroutine closestsymmetricnode
2508 
2509  subroutine stringmatch(strings, nStrings, debugZipper)
2510  use constants
2512  use kdtree2_module
2513  use utils, only: mynorm2
2514 
2515  implicit none
2516 
2517  ! Input/output
2518  integer(kind=intType), intent(in) :: nStrings
2519  type(oversetstring), dimension(nstrings), target :: strings
2520  logical, intent(in) :: debugZipper
2521 
2522  ! Working
2523  integer(kind=intType) :: i, j, k, idx, oid(4)
2524  integer(kind=intType) :: nAlloc, nUnique, nSearch
2525  type(kdtree2_result), allocatable, dimension(:) :: results
2526  type(oversetstring), pointer :: str, master
2527  logical :: checkLeft, checkRight, concave
2528  logical :: checkLeft2, checkRight2, concave2
2529  logical :: leftOK, rightOK, isEndNode
2530  real(kind=realtype), dimension(3) :: xj, xjp1, xjm1, normj
2531  real(kind=realtype), dimension(3) :: xk, xkp1, xkm1, normk
2532  real(kind=realtype), dimension(3) :: mypt, otherpt, enorm
2533  real(kind=realtype) :: fact, dstar, curdist, mindist, edgelength
2534  integer(kind=intTYpe) :: otherID, otherIndex, closestOtherIndex, closestOtherString
2535  integer(kind=intType) :: id, index
2536  real(kind=realtype) :: timea, pt(3), v(3), costheta, cutoff, dist, maxh, ratio
2537 
2538  if (nstrings == 0) then
2539  return
2540  end if
2541 
2542  ! Now make we determine the nearest point on another substring
2543  ! for each point.
2544  nalloc = 50
2545  allocate (results(nalloc))
2546  master => strings(1)%p
2547 
2548  ! Loop over the fullStrings
2549  do i = 1, nstrings
2550  str => strings(i) ! Easier readability
2551 
2552  ! No need to do anything with the pocket string.
2553  if (str%isPocket) then
2554  cycle
2555  end if
2556 
2557  ! Allocate space for otherID as it is not done yet
2558  if (associated(str%otherID)) then
2559  deallocate (str%otherID)
2560  end if
2561 
2562  allocate (str%otherID(2, str%nNodes))
2563  str%otherID = -1
2564 
2565  ! Loop over my nodes and search for it in master tree
2566  nodeloop: do j = 1, str%nNodes
2567 
2568  ! Set the initial maximum number of neighbours
2569  ! This can be at most the total number of nodes
2570  nsearch = min(nalloc, master%nNodes)
2571 
2572  ! We have to be careful since single-sided chains have only
2573  ! 1 neighbour at each end.
2574 
2575  call getnodeinfo(str, j, checkleft, checkright, concave, &
2576  xj, xjm1, xjp1, normj)
2577  isendnode = .false.
2578  if (.not. (checkleft .eqv. checkright)) then
2579  ! Since we don't need to check one side, this means we're
2580  ! at the end of the chain. This is important since this
2581  ! node *MUST* be attached to another node on another
2582  ! chain at the end
2583  isendnode = .true.
2584  end if
2585 
2586  outerloop: do
2587  mindist = large
2588  closestotherindex = -1
2589  call kdtree2_n_nearest(master%tree, xj, nsearch, results)
2590 
2591  ! Only check edges connected to nodes within the
2592  ! distance the maximum element size of my self or the
2593  ! closest node. We put in a fudge factor of 1.5.
2594 
2595  innerloop: do k = 1, nsearch
2596 
2597  ! Since we know the results are sorted, if the
2598  ! distance(k) > than our current minDist, we can stop
2599  ! since there is no possible way that any of the
2600  ! remaining points can be closer given that the modified
2601  ! D* is always larger than the original D
2602 
2603  ! Extract current information to make things a little
2604  ! easier to read
2605  curdist = sqrt(results(k)%dis)
2606  idx = results(k)%idx
2607  pt = master%x(:, idx)
2608 
2609  ! ---------------------------------------------
2610  ! Exit Condition: We can stop the loop if the current
2611  ! uncorrected distance is larger than our current
2612  ! minimum. This guarantees the minimum corrected
2613  ! distance is found.
2614  ! ---------------------------------------------
2615 
2616  if (curdist > mindist) then
2617  exit outerloop
2618  end if
2619 
2620  ! ---------------------------------------------
2621  ! Check 1: If the node we found isn't on our
2622  ! substring. we don't need to do anything
2623  ! ---------------------------------------------
2624 
2625  if (master%cNodes(1, idx) == str%myID) then
2626  cycle innerloop
2627  end if
2628 
2629  ! ---------------------------------------------
2630 
2631  ! Check 1b: If the node we found has been removed due
2632  ! to self zipping, we can just keep going
2633  ! --------------------------------------------
2634  if (master%cNodes(2, idx) == 0) then
2635  cycle innerloop
2636  end if
2637 
2638  ! The first time we make it here, idx will be the
2639  ! index of the closest node on another string that
2640  ! isn't me.
2641  if (closestotherindex == -1) then
2642  closestotherstring = master%cNodes(1, idx)
2643  closestotherindex = master%cNodes(2, idx)
2644  end if
2645 
2646  ! ---------------------------------------------
2647  ! Check 2: Check if the node we found violates the
2648  ! the "in front" test. For a concave corner TWO
2649  ! triangle areas formed by the point and the two
2650  ! edges must be positive. For a convex corner only
2651  ! one of the triangle areas needs to be positive.
2652  ! ---------------------------------------------
2653  if (.not. nodeinfrontofedges(pt, concave, checkleft, checkright, &
2654  xj, xjm1, xjp1, normj)) then
2655  cycle innerloop
2656  end if
2657 
2658  ! ---------------------------------------------
2659  ! Check 3: This is the *reverse* of check 2: Is the
2660  ! node we're searching for visible from the potential
2661  ! closest other node.
2662  ! ---------------------------------------------
2663  otherid = master%cNodes(1, idx)
2664  otherindex = master%cNodes(2, idx)
2665 
2666  call getnodeinfo(strings(otherid), otherindex, checkleft2, &
2667  checkright2, concave2, xk, xkm1, xkp1, normk)
2668 
2669  if (.not. nodeinfrontofedges(xj, concave2, checkleft2, &
2670  checkright2, xk, xkm1, xkp1, normk)) then
2671  cycle innerloop
2672  end if
2673 
2674  ! ---------------------------------------------
2675  ! Check 4a: Check if the potential node intersects
2676  ! itself.
2677  ! ---------------------------------------------
2678  if (overlappededges(str, j, pt)) then
2679  cycle
2680  end if
2681 
2682  ! ---------------------------------------------
2683  ! Check 4b: OR if the other node would have to
2684  ! intersect *ITSELF* to get back to me. This is used
2685  ! to catch closest points crossing over thin strips.
2686  ! ---------------------------------------------
2687 
2688  if (overlappededges(strings(otherid), otherindex, xj)) then
2689  cycle
2690  end if
2691 
2692  ! ---------------------------------------------
2693  ! Check 4c: Make sure it doesn't inersect the closest
2694  ! string if that happens to be different from the
2695  ! cloest one. string. This should only check very
2696  ! rare cases the other checks miss.
2697  ! ---------------------------------------------
2698 
2699  if (otherid /= closestotherstring) then
2700  if (overlappededges2( &
2701  strings(closestotherstring), xj, normj, pt)) then
2702  cycle
2703  end if
2704  end if
2705 
2706  ! ---------------------------------------------
2707  ! Check 4d: If this is an end node, we need to check
2708  ! if the potential canditate is also a end node
2709  ! ---------------------------------------------
2710  if (isendnode) then
2711  if (checkright2 .eqv. checkleft2) then
2712  cycle
2713  end if
2714  end if
2715 
2716  ! ---------------------------------------------
2717  ! Check 5: Now that the point has passed the previous
2718  ! checks, we can compute the agumented distance
2719  ! function and see if it better than the exisitng min
2720  ! distance.
2721  ! ---------------------------------------------
2722 
2723  ! Now calculate our new distance
2724  v = pt - xj
2725  v = v / mynorm2(v)
2726 
2727  ! Recompute the distance function
2728  costheta = abs(dot_product(normj, v))
2729 
2730  ! Update distFunction
2731  dstar = curdist / (max(1 - costheta, 1e-6))
2732 
2733  if (dstar < mindist) then
2734  ! Save the string ID and the index.
2735  mindist = dstar
2736  str%otherID(:, j) = master%cNodes(:, idx)
2737  end if
2738  end do innerloop
2739 
2740  ! If we have already searched the max, we have to quit the loop
2741  if (nsearch == master%Nnodes) then
2742  exit outerloop
2743  end if
2744 
2745  ! We are not 100% sure that we found the minium
2746  ! yet. Make nAlloc twice as big and start over.
2747  nsearch = nsearch * 2
2748  nsearch = min(nsearch, master%nNodes)
2749  if (nsearch > nalloc) then
2750  deallocate (results)
2751  nalloc = nalloc * 2
2752  allocate (results(nalloc))
2753  end if
2754  end do outerloop
2755  end do nodeloop
2756 
2757  ! Do a sanity check to fix some extraordinary cases. If a node
2758  ! hasn't found a neighbouring string but each of the two nodes
2759  ! either side have, and they found the *same* string, just
2760  ! accept that.
2761 
2762  do j = 3, str%nNodes - 2
2763  if (str%otherID(1, j) == -1) then
2764  ! Bad node:
2765  oid(1) = str%otherID(1, j - 2)
2766  oid(2) = str%otherID(1, j - 1)
2767  oid(3) = str%otherID(1, j + 1)
2768  oid(4) = str%otherID(1, j + 2)
2769 
2770  if (oid(1) /= -1 .and. &
2771  oid(1) == oid(2) .and. &
2772  oid(1) == oid(3) .and. &
2773  oid(1) == oid(4)) then
2774 
2775  if (debugzipper) then
2776  print *, '****************************************************************'
2777  print *, 'Warning: Fixing a bad association on string ', i, 'at index', j
2778  print *, '****************************************************************'
2779  end if
2780 
2781  ! We have a '-1' surrounded by the same gap string
2782 
2783  ! Set the stringID
2784  str%otherID(1, j) = oid(1)
2785 
2786  ! Estimate what the other index should be. Since this
2787  ! is in the middle of the string, the exact index
2788  ! shouldn't matter.
2789  str%otherID(2, j) = str%otherID(2, j - 1)
2790  end if
2791  end if
2792  end do
2793 
2794  end do
2795  end subroutine stringmatch
2796 
2797  subroutine writeoversetstring(str, strings, n, fileID)
2798 
2799  use constants
2800  use utils, only: mynorm2
2801  use commonformats, only: sci12, int5
2802  implicit none
2803 
2804  integer(kind=intType), intent(in) :: fileID, n
2805  type(oversetstring), intent(in) :: str
2806  type(oversetstring), intent(in), dimension(n) :: strings
2807  integer(kind=intType) :: i, j, id, index
2808  real(kind=realtype), dimension(3) :: mypt, otherpt, vec
2809  real(kind=realtype) :: maxh, dist, ratio
2810 
2811  character(80) :: zoneName
2812 
2813  write (zonename, "(a,I5.5)") "Zone T=gap_", str%myID
2814  write (fileid, *) trim(zonename)
2815 
2816  write (fileid, *) "Nodes = ", str%nNodes, " Elements= ", str%nElems, " ZONETYPE=FELINESEG"
2817  write (fileid, *) "DATAPACKING=BLOCK"
2818 
2819  ! Nodes
2820  do j = 1, 3
2821  do i = 1, str%nNodes
2822  write (fileid, sci12) str%x(j, i)
2823  end do
2824  end do
2825 
2826  ! Node normal
2827  do j = 1, 3
2828  do i = 1, str%nNodes
2829  write (fileid, sci12) str%norm(j, i)
2830  end do
2831  end do
2832 
2833  ! Vector between closest points
2834  do j = 1, 3
2835  do i = 1, str%nNodes
2836  mypt = str%x(:, i)
2837  id = str%otherID(1, i)
2838  if (id /= -1) then
2839  index = str%otherID(2, i)
2840  otherpt = strings(id)%x(:, index)
2841  vec = otherpt - mypt
2842  else
2843  vec = zero
2844  end if
2845 
2846  write (fileid, sci12) vec(j)
2847  end do
2848  end do
2849 
2850  ! global node ID
2851  do i = 1, str%nNodes
2852  write (fileid, sci12) real(str%ind(i))
2853  end do
2854 
2855  ! gapID
2856  do i = 1, str%nNodes
2857  write (fileid, sci12) real(str%myID)
2858  end do
2859 
2860  ! gap Index
2861  do i = 1, str%nNodes
2862  write (fileid, sci12) real(i)
2863  end do
2864 
2865  if (associated(str%otherID)) then
2866  ! otherID
2867  do i = 1, str%nNodes
2868  write (fileid, sci12) real(str%otherID(1, i))
2869  end do
2870 
2871  ! other Index
2872  do i = 1, str%nNodes
2873  write (fileid, sci12) real(str%otherID(2, i))
2874  end do
2875  else
2876  do i = 1, 2 * str%nNodes
2877  write (fileid, sci12) zero
2878  end do
2879  end if
2880 
2881  do i = 1, str%nNodes
2882  mypt = str%x(:, i)
2883  id = str%otherID(1, i)
2884  if (id /= -1) then
2885  index = str%otherID(2, i)
2886  otherpt = strings(id)%x(:, index)
2887  dist = mynorm2(mypt - otherpt)
2888  maxh = max(str%h(i), strings(id)%h(index))
2889  ratio = dist / maxh
2890  else
2891  ratio = zero
2892  end if
2893 
2894  write (fileid, sci12) ratio
2895  end do
2896 
2897  do i = 1, str%nElems
2898  write (fileid, int5) str%conn(1, i), str%conn(2, i)
2899  end do
2900 
2901  end subroutine writeoversetstring
2902 
2903  subroutine writeoversetmaster(str, fileID)
2904 
2905  use constants
2906  use utils, only: mynorm2
2907  use commonformats, only: sci12, int5
2908  implicit none
2909 
2910  type(oversetstring), intent(in) :: str
2911  integer(kind=intType), intent(in) :: fileID
2912  integer(kind=intType) :: i, j, id, index
2913  real(kind=realtype), dimension(3) :: mypt, otherpt, vec
2914  real(kind=realtype) :: maxh, dist, ratio
2915 
2916  character(80) :: zoneName
2917 
2918  write (zonename, "(a,I5.5)") "Zone T=gap_", str%myID
2919  write (fileid, *) trim(zonename)
2920 
2921  write (fileid, *) "Nodes = ", str%nNodes, " Elements= ", str%nElems, " ZONETYPE=FELINESEG"
2922  write (fileid, *) "DATAPACKING=BLOCK"
2923 
2924  ! Nodes
2925  do j = 1, 3
2926  do i = 1, str%nNodes
2927  write (fileid, sci12) str%x(j, i)
2928  end do
2929  end do
2930 
2931  do i = 1, str%nElems
2932  write (fileid, int5) str%conn(1, i), str%conn(2, i)
2933  end do
2934 
2935  end subroutine writeoversetmaster
2936 
2937  subroutine writeoversettriangles(string, fileName, startTri, endTri)
2938 
2939  use constants
2940  use commonformats, only: sci12
2941  implicit none
2942 
2943  type(oversetstring), intent(inout) :: string
2944  integer(kind=intType), intent(in) :: startTri, endTri
2945  character(*) :: fileName
2946  integer(kind=intType) :: i, j
2947  character(80) :: zoneName
2948 
2949  open (unit=101, file=trim(filename), form='formatted')
2950  write (101, *) 'TITLE = "Triangles"'
2951  write (101, *) 'Variables = "X", "Y", "Z"'
2952 
2953  write (zonename, "(a,I5.5)") "Zone T=triangles_", string%myID
2954  write (101, *) trim(zonename)
2955 
2956  write (101, *) "Nodes = ", string%nNodes, " Elements= ", (endtri - starttri + 1), " ZONETYPE=FETRIANGLE"
2957  write (101, *) "DATAPACKING=POINT"
2958 
2959  ! Write all the coordinates
2960  do i = 1, string%nNodes
2961  do j = 1, 3
2962  write (101, sci12, advance='no') string%x(j, i)
2963  end do
2964  write (101, "(1x)")
2965  end do
2966 
2967  do i = starttri, endtri
2968  write (101, "(*(I7))") string%tris(1, i), string%tris(2, i), string%tris(3, i)
2969  end do
2970  close (101)
2971  end subroutine writeoversettriangles
2972 
2973  subroutine writezipperdebug(str)
2974 
2975  ! Save the state of an unsplit string such that it can be debugged
2976  ! later without running overset interpolation.
2977 
2978  use constants
2979  implicit none
2980 
2981  type(oversetstring) :: str
2982  integer(kind=intType) :: i, j
2983 
2984  open (unit=101, file="debug.zipper", form='formatted')
2985  write (101, *) str%nNodes
2986  write (101, *) str%nElems
2987  do i = 1, str%nNodes
2988  do j = 1, 10
2989  write (101, *) str%nodeData(j, i)
2990  end do
2991  end do
2992 
2993  do i = 1, str%nElems
2994  do j = 1, 2
2995  write (101, *) str%conn(j, i)
2996  end do
2997  end do
2998 
2999  do i = 1, str%nNodes
3000  do j = 1, 3
3001  write (101, *) str%intNodeData(j, i)
3002  end do
3003  end do
3004  close (101)
3005  end subroutine writezipperdebug
3006 
3007  subroutine loadzipperdebug(fileName, str)
3008 
3009  ! Save the state of an unsplit string such that it can be debugged
3010  ! later without running overset interpolation.
3011 
3012  use constants
3013  implicit none
3014 
3015  character(*), intent(in) :: fileName
3016  type(oversetstring) :: str
3017  integer(kind=intType) :: i, j
3018 
3019  open (unit=101, file=filename, form='formatted')
3020  read (101, *) str%nNodes
3021  read (101, *) str%nElems
3022  call nullifystring(str)
3023 
3024  allocate (str%nodeData(10, str%nNodes))
3025  allocate (str%conn(2, str%nElems))
3026  allocate (str%intNodeData(3, str%nNodes))
3027 
3028  do i = 1, str%nNodes
3029  do j = 1, 10
3030  read (101, *) str%nodeData(j, i)
3031  end do
3032  end do
3033 
3034  do i = 1, str%nElems
3035  do j = 1, 2
3036  read (101, *) str%conn(j, i)
3037  end do
3038  end do
3039 
3040  do i = 1, str%nNodes
3041  do j = 1, 3
3042  read (101, *) str%intNodeData(j, i)
3043  end do
3044  end do
3045  close (101)
3046 
3047  call setstringpointers(str)
3048 
3049  end subroutine loadzipperdebug
3050 
3051  subroutine pointintriangle(x1, x2, x3, pt, inTri)
3052 
3053  use constants
3054  use utils, only: cross_prod
3055  implicit none
3056  real(kind=realtype), dimension(3), intent(in) :: x1, x2, x3, pt
3057  logical, intent(out) :: inTri
3058 
3059  if (sameside(pt, x1, x2, x3) .and. sameside(pt, x2, x1, x3) .and. sameside(pt, x3, x1, x2)) then
3060  intri = .true.
3061  else
3062  intri = .false.
3063  end if
3064 
3065  contains
3066  function sameside(p1, p2, a, b)
3067 
3068  implicit none
3069  logical :: sameside
3070  real(kind=realtype), dimension(3) :: p1, p2, a, b, cp1, cp2
3071 
3072  sameside = .false.
3073  call cross_prod(b - a, p1 - a, cp1)
3074  call cross_prod(b - a, p2 - a, cp2)
3075  if (dot_product(cp1, cp2) >= zero) then
3076  sameside = .true.
3077  end if
3078  end function sameside
3079  end subroutine pointintriangle
3080 
3081  function positivetriarea(p1, p2, p3, norm)
3082 
3083  use constants
3084  use utils, only: cross_prod
3085  implicit none
3086  real(kind=realtype), intent(in), dimension(3) :: p1, p2, p3, norm
3087  real(kind=realtype), dimension(3) :: n
3088  logical :: positivetriarea
3089 
3090  call cross_prod(p2 - p1, p3 - p1, n)
3091  if (dot_product(n, norm) > zero) then
3092  positivetriarea = .true.
3093  else
3094  positivetriarea = .false.
3095  end if
3096  end function positivetriarea
3097 
3098  subroutine getnodeinfo(str, j, checkLeft, checkRight, concave, xj, xjm1, xjp1, normj)
3099 
3100  use constants
3101  use utils, only: cross_prod
3102  implicit none
3103 
3104  type(oversetstring) :: str
3105  integer(kind=intType) :: j
3106  logical :: checkLeft, checkRight, concave
3107  real(kind=realtype), dimension(3) :: xj, xjm1, xjp1, normj
3108  real(kind=realtype), dimension(3) :: v
3109  checkleft = .true.
3110  checkright = .true.
3111  xj = str%x(:, j)
3112  normj = str%norm(:, j)
3113  concave = .false.
3114  if (str%isPeriodic) then
3115  if (j > 1 .and. j < str%nNodes) then
3116  xjm1 = str%x(:, j - 1)
3117  xjp1 = str%x(:, j + 1)
3118  else if (j == 1) then
3119  xjm1 = str%x(:, str%nNodes)
3120  xjp1 = str%x(:, j + 1)
3121  else if (j == str%nNodes) then
3122  xjm1 = str%x(:, j - 1)
3123  xjp1 = str%x(:, 1)
3124  end if
3125  else
3126  ! Not periodic. Assume the ends are concave. This will
3127  ! forces checking if both the left and right are ok,
3128  ! which since the leftOK and rightOK's default to
3129  ! .True., it just checks the one triangle which is what
3130  ! we want.
3131  if (j == 1) then
3132  checkleft = .false.
3133  concave = .true.
3134  end if
3135 
3136  if (j == str%nNodes) then
3137  checkright = .false.
3138  concave = .true.
3139  end if
3140 
3141  if (checkleft) &
3142  xjm1 = str%x(:, j - 1)
3143  if (checkright) &
3144  xjp1 = str%x(:, j + 1)
3145  end if
3146 
3147  if (checkleft .and. checkright) then
3148 
3149  ! Determine if the point is convex or concave provided
3150  ! we have both neighbours.
3151  call cross_prod(xjm1 - xj, xjp1 - xj, v)
3152 
3153  if (dot_product(v, normj) > zero) then
3154  concave = .true.
3155  end if
3156  end if
3157 
3158  end subroutine getnodeinfo
3159 
3160  function nodeinfrontofedges(pt, concave, checkLeft, checkRight, xj, xjm1, xjp1, normj)
3161 
3162  use constants
3163  implicit none
3164 
3165  real(kind=realtype), dimension(3), intent(in) :: pt, xj, xjm1, xjp1, normj
3166  logical, intent(in) :: concave, checkleft, checkright
3167  logical :: nodeinfrontofedges
3168  logical :: leftok, rightok
3169 
3170  nodeinfrontofedges = .true.
3171  leftok = .true.
3172  rightok = .true.
3173  if (checkleft .and. .not. positivetriarea(xj, xjm1, pt, normj)) then
3174  leftok = .false.
3175  end if
3176 
3177  if (checkright .and. .not. positivetriarea(xjp1, xj, pt, normj)) then
3178  rightok = .false.
3179  end if
3180 
3181  if (concave) then
3182  if (.not. (leftok .and. rightok)) then
3183  nodeinfrontofedges = .false.
3184  end if
3185  else
3186  if (.not. (leftok .or. rightok)) then
3187  nodeinfrontofedges = .false.
3188  end if
3189  end if
3190  end function nodeinfrontofedges
3191 
3192  function overlappededges(str, j, pt)
3193 
3194  use constants
3195  use utils, only: mynorm2, cross_prod
3196 
3197  implicit none
3198 
3199  ! Input/output
3200  real(kind=realtype), dimension(3), intent(in) :: pt
3201  type(oversetstring), intent(in) :: str
3202  integer(kind=intType), intent(in) :: j
3203  logical :: overlappededges
3204 
3205  ! Working
3206  integer(kind=intType) :: i
3207  real(kind=realtype), dimension(3) :: v, p1, p2, u, norma, normb, x0, norm
3208  real(kind=realtype) :: unrm, x1, x2, x3, x4, y1, y2, y3, y4, idet, px, py
3209  real(kind=realtype) :: u1, u2, v1, v2, w1, w2
3210  real(kind=realtype) :: s1, s2, tmp, line(2), vec(2), tol
3211  overlappededges = .false.
3212  tol = 1e-6
3213  ! We will conver this completely into a 2D problem by projecting
3214  ! everything onto the plane defined by norm. x0 is at the origin of
3215  ! the 2D system and the xaxis point from x0 to pt
3216 
3217  x0 = str%x(:, j)
3218  norm = str%norm(:, j)
3219 
3220  u = pt - x0
3221  unrm = mynorm2(u)
3222  u = u / unrm
3223 
3224  call cross_prod(norm, u, v)
3225  v = v / mynorm2(v)
3226 
3227  ! Now u,v,norm is an orthogonal coordinate system
3228  x1 = zero
3229  y1 = zero
3230  x2 = unrm
3231  y2 = zero
3232  overlappededges = .false.
3233 
3234  ! Loop over the number of edges on my string
3235  elemloop: do i = 1, str%nElems
3236  ! Don't check the ones right next to me, since they will
3237  ! "overlap" exactly at x0
3238 
3239  if (str%conn(1, i) == j .or. str%conn(2, i) == j) then
3240  cycle
3241  end if
3242 
3243  ! Project the two points into the plane
3244  p1 = str%x(:, str%conn(1, i))
3245  p2 = str%x(:, str%conn(2, i))
3246 
3247  norma = str%norm(:, str%conn(1, i))
3248  normb = str%norm(:, str%conn(2, i))
3249 
3250  ! Make sure the edges are on the same plane, otherwise this is
3251  ! meaningless
3252  if (dot_product(norma, norm) < half .or. dot_product(normb, norm) < half) then
3253  cycle
3254  end if
3255  ! Project the two points onto the plane
3256  p1 = p1 - norm * dot_product(p1 - x0, norm)
3257  p2 = p2 - norm * dot_product(p2 - x0, norm)
3258 
3259  ! Now get the 2D coordinates
3260  x3 = dot_product(p1 - x0, u)
3261  y3 = dot_product(p1 - x0, v)
3262  x4 = dot_product(p2 - x0, u)
3263  y4 = dot_product(p2 - x0, v)
3264 
3265  u1 = x2 - x1
3266  y2 = y2 - y1
3267 
3268  v1 = x4 - x3
3269  v2 = y4 - y3
3270 
3271  w1 = x1 - x3
3272  w2 = y1 - y3
3273 
3274  s1 = (v2 * w1 - v1 * w2) / (v1 * u2 - v2 * u1)
3275  s2 = (u1 * w2 - u2 * w1) / (u1 * v2 - u2 * v1)
3276 
3277  if (s1 > tol .and. s1 < one - tol .and. s2 > tol .and. s2 < one - tol) then
3278  overlappededges = .true.
3279  exit elemloop
3280  end if
3281  end do elemloop
3282 
3283  end function overlappededges
3284 
3285  function overlappededges2(str, pt1, norm, pt2)
3286 
3287  use constants
3288  use utils, only: mynorm2, cross_prod
3289  implicit none
3290 
3291  ! Input/output
3292  real(kind=realtype), dimension(3), intent(in) :: pt1, pt2, norm
3293  type(oversetstring), intent(in) :: str
3294  logical :: overlappededges2
3295 
3296  ! Working
3297  integer(kind=intType) :: i
3298  real(kind=realtype), dimension(3) :: v, p1, p2, u, norma, normb, x0
3299  real(kind=realtype) :: unrm, x1, x2, x3, x4, y1, y2, y3, y4, idet, px, py
3300  real(kind=realtype) :: u1, u2, v1, v2, w1, w2
3301  real(kind=realtype) :: s1, s2, tmp, line(2), vec(2), tol
3302 
3303  tol = 1e-6
3304  ! We will conver this completely into a 2D problem by projecting
3305  ! everything onto the plane defined by norm. x0 is at the origin of
3306  ! the 2D system and the xaxis point from x0 to pt
3307 
3308  x0 = pt1
3309 
3310  u = pt2 - x0
3311  unrm = mynorm2(u)
3312  u = u / unrm
3313 
3314  call cross_prod(norm, u, v)
3315  v = v / mynorm2(v)
3316 
3317  ! Now u,v,norm is an orthogonal coordinate system
3318  x1 = zero
3319  y1 = zero
3320  x2 = unrm
3321  y2 = zero
3322  overlappededges2 = .false.
3323 
3324  ! Loop over the number of edges on my string
3325  elemloop: do i = 1, str%nElems
3326 
3327  ! Project the two points into the plane
3328  p1 = str%x(:, str%conn(1, i))
3329  p2 = str%x(:, str%conn(2, i))
3330 
3331  norma = str%norm(:, str%conn(1, i))
3332  normb = str%norm(:, str%conn(2, i))
3333 
3334  ! Make sure the edges are on the same plane, otherwise this is
3335  ! meaningless
3336  if (dot_product(norma, norm) < half .or. dot_product(normb, norm) < half) then
3337  cycle
3338  end if
3339  ! Project the two points onto the plane
3340  p1 = p1 - norm * dot_product(p1 - x0, norm)
3341  p2 = p2 - norm * dot_product(p2 - x0, norm)
3342 
3343  ! Now get the 2D coordinates
3344  x3 = dot_product(p1 - x0, u)
3345  y3 = dot_product(p1 - x0, v)
3346  x4 = dot_product(p2 - x0, u)
3347  y4 = dot_product(p2 - x0, v)
3348 
3349  u1 = x2 - x1
3350  y2 = y2 - y1
3351 
3352  v1 = x4 - x3
3353  v2 = y4 - y3
3354 
3355  w1 = x1 - x3
3356  w2 = y1 - y3
3357 
3358  s1 = (v2 * w1 - v1 * w2) / (v1 * u2 - v2 * u1)
3359  s2 = (u1 * w2 - u2 * w1) / (u1 * v2 - u2 * v1)
3360 
3361  if (s1 > tol .and. s1 < one - tol .and. s2 > tol .and. s2 < one - tol) then
3362  overlappededges2 = .true.
3363  exit elemloop
3364  end if
3365  end do elemloop
3366 
3367  end function overlappededges2
3368 
3369 end module stringops
character(len=maxstringlen) int5
character(len=maxstringlen) sci12
real(kind=realtype), parameter zero
Definition: constants.F90:71
real(kind=realtype), parameter pi
Definition: constants.F90:22
real(kind=realtype), parameter one
Definition: constants.F90:72
real(kind=realtype), parameter half
Definition: constants.F90:80
real(kind=realtype), parameter large
Definition: constants.F90:24
real(kind=realtype) selfzipcutoff
Definition: inputParam.F90:888
type(kdtree2) function, pointer, public kdtree2_create(input_data, dim, sort, rearrange)
Definition: kd_tree.f90:588
subroutine, public kdtree2_n_nearest(tp, qv, nn, results)
Definition: kd_tree.f90:1023
subroutine, public kdtree2_r_nearest(tp, qv, r2, nfound, nalloc, results)
Definition: kd_tree.f90:1110
subroutine qsortedgetype(arr, nn)
Definition: sa.F90:5
subroutine createsubstringfromelems(p, s, id)
Definition: stringOps.F90:509
subroutine pocketzip(s)
Definition: stringOps.F90:2072
subroutine crosszip(str1, N1, N2, str2, N3, N4, debugZipper, failed)
Definition: stringOps.F90:742
subroutine loadzipperdebug(fileName, str)
Definition: stringOps.F90:3008
subroutine writeoversettriangles(string, fileName, startTri, endTri)
Definition: stringOps.F90:2938
subroutine stringmatch(strings, nStrings, debugZipper)
Definition: stringOps.F90:2510
subroutine combinechainbuffers(s)
Definition: stringOps.F90:614
logical function trioverlap(pt1, pt2, pt3, str, i1, i2)
Definition: stringOps.F90:2244
subroutine makepocketzip(p, strings, nStrings, pocketMaster, debugZipper)
Definition: stringOps.F90:1839
subroutine writeoversetstring(str, strings, n, fileID)
Definition: stringOps.F90:2798
logical function positivetriarea(p1, p2, p3, norm)
Definition: stringOps.F90:3082
subroutine selfzip(s, cutOff, nZipped)
Definition: stringOps.F90:636
logical function overlappededges(str, j, pt)
Definition: stringOps.F90:3193
logical function overlappededges2(str, pt1, norm, pt2)
Definition: stringOps.F90:3286
recursive subroutine createnodetoelem(string)
Definition: stringOps.F90:343
subroutine addpotentialtriangle(s, im1, ii, ip1, nodeMap, results, added)
Definition: stringOps.F90:2373
subroutine writezipperdebug(str)
Definition: stringOps.F90:2974
subroutine makecrosszip(p, strings, nStrings, debugZipper)
Definition: stringOps.F90:1374
subroutine performselfzip(master, strings, nStrings, debugZipper)
Definition: stringOps.F90:206
subroutine dochain(master, iStart, iSub)
Definition: stringOps.F90:435
subroutine addtri(A, sA, B, sB, C, sC)
Definition: stringOps.F90:1329
subroutine shortenstring(s, nodeMap)
Definition: stringOps.F90:2280
subroutine writeoversetmaster(str, fileID)
Definition: stringOps.F90:2904
subroutine computetrisurfarea(master, area)
Definition: stringOps.F90:2215
subroutine reducegapstring(string)
Definition: stringOps.F90:259
subroutine pointintriangle(x1, x2, x3, pt, inTri)
Definition: stringOps.F90:3052
subroutine nullifystring(string)
Definition: stringOps.F90:8
subroutine deallocatestring(string)
Definition: stringOps.F90:38
subroutine setstringpointers(string)
Definition: stringOps.F90:89
logical function nodeinfrontofedges(pt, concave, checkLeft, checkRight, xj, xjm1, xjp1, normj)
Definition: stringOps.F90:3161
subroutine getnodeinfo(str, j, checkLeft, checkRight, concave, xj, xjm1, xjp1, normj)
Definition: stringOps.F90:3099
subroutine closestsymmetricnode(s1, s2, i, j)
Definition: stringOps.F90:2477
subroutine createorderedstrings(master, strings, nString)
Definition: stringOps.F90:105
Definition: utils.F90:1
subroutine cross_prod(a, b, c)
Definition: utils.F90:1721
real(kind=realtype) function mynorm2(x)
Definition: utils.F90:1697
subroutine pointreduce(pts, N, tol, uniquePts, link, nUnique)
Definition: utils.F90:4170
subroutine terminate(routineName, errorMessage)
Definition: utils.F90:502
integer(kind=inttype) function prevnode(s, i)
Definition: stringOps.F90:1686
integer(kind=inttype) function elembetweennodes(str, a, b)
Definition: stringOps.F90:1271
integer(kind=inttype) function elemsforrange(s, N1, N2, pos)
Definition: stringOps.F90:1800
real(kind=realtype) function triarea(pt1, pt2, pt3)
Definition: stringOps.F90:1309
real(kind=realtype) function vecangle(vec1, vec2)
Definition: stringOps.F90:1254
integer(kind=inttype) function simplenextnode(s, i)
Definition: stringOps.F90:1666
integer(kind=inttype) function startnode(s)
Definition: stringOps.F90:1623
subroutine flagnodesused(s, N1, N2, pos)
Definition: stringOps.F90:1756
logical function sameside(p1, p2, a, b)
Definition: stringOps.F90:3067
integer(kind=inttype) function nextnode(str, i, pos)
Definition: stringOps.F90:1213
subroutine tracematch(s, iStart, pos, checkID, iEnd, fullLoop)
Definition: stringOps.F90:1711