1!/*****************************************************************************/
2! *
3! *  Elmer/Ice, a glaciological add-on to Elmer
4! *  http://elmerice.elmerfem.org
5! *
6! *
7! *  This program is free software; you can redistribute it and/or
8! *  modify it under the terms of the GNU General Public License
9! *  as published by the Free Software Foundation; either version 2
10! *  of the License, or (at your option) any later version.
11! *
12! *  This program is distributed in the hope that it will be useful,
13! *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14! *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15! *  GNU General Public License for more details.
16! *
17! *  You should have received a copy of the GNU General Public License
18! *  along with this program (in file fem/GPL-2); if not, write to the
19! *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20! *  Boston, MA 02110-1301, USA.
21! *
22! *****************************************************************************/
23! ******************************************************************************
24! *
25! *  Authors: Joe Todd
26! *  Email:
27! *  Web:     http://elmerice.elmerfem.org
28! *
29! *
30! *****************************************************************************
31
32!A routine for getting frontal advance in 3D, given velocity and melt
33
34 SUBROUTINE FrontAdvance3D ( Model, Solver, dt, TransientSimulation )
35
36   USE CalvingGeometry
37   USE DefUtils
38   IMPLICIT NONE
39
40!-----------------------------------------------
41   TYPE(Model_t) :: Model
42   TYPE(Solver_t), TARGET :: Solver
43   REAL(KIND=dp) :: dt
44   LOGICAL :: TransientSimulation
45!-----------------------------------------------
46   TYPE(Mesh_t), POINTER :: Mesh
47   TYPE(Nodes_t), TARGET :: FrontNodes, ColumnNodes
48   TYPE(ValueList_t), POINTER :: Params
49   TYPE(Variable_t), POINTER :: Var, VeloVar, MeltVar, NormalVar, TangledVar
50   INTEGER :: i, j, k, m, n, DOFs, TotalNodes, NodesPerLevel, ExtrudedLevels,&
51        FaceNodeCount, DummyInt, col, hits, ierr, FrontLineCount, county,&
52        ShiftIdx, ShiftToIdx, NoTangledGroups, PivotIdx, CornerIdx, &
53        SecondIdx, FirstTangleIdx, LastTangleIdx
54   INTEGER, POINTER :: Perm(:), FrontPerm(:)=>NULL(), TopPerm(:)=>NULL(), &
55        FrontNodeNums(:)=>NULL()
56   INTEGER, ALLOCATABLE :: FNColumns(:), FrontColumnList(:), FrontLocalNodeNumbers(:), &
57        NodeNumbers(:), TangledGroup(:), TangledPivotIdx(:), UpdatedDirection(:)
58   REAL(KIND=dp) :: FrontOrientation(3),RotationMatrix(3,3),&
59        NodeVelo(3), NodeMelt(3), NodeNormal(3), ColumnNormal(3), CornerDirection(2),&
60        MeltRate, Displace(3), NodeHolder(3), DangerGrad, ShiftTo, direction, &
61        ShiftDist, y_coord(2), epsShift, ShiftToY, LongRangeLimit, MaxDisplacement, LimitZ, &
62        p1(2),p2(2),q1(2),q2(2),intersect(2), LeftY, RightY, EpsTangle,thisEps,Shift, thisY
63   REAL(KIND=dp), POINTER :: Advance(:)
64   REAL(KIND=dp), ALLOCATABLE :: Rot_y_coords(:,:), Rot_z_coords(:,:), ColumnNormals(:,:), &
65        TangledShiftTo(:)
66   LOGICAL :: Found, Debug, Parallel, Boss, ShiftLeft, LeftToRight, MovedOne, ShiftSecond, &
67        Protrusion, SqueezeLeft, SqueezeRight, FirstTime=.TRUE., intersect_flag, FrontMelting, &
68        IgnoreVelo
69   LOGICAL, ALLOCATABLE :: DangerZone(:), WorkLogical(:), UpdatedColumn(:),&
70        Tangled(:), DontMove(:)
71   CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, VeloVarName, MeltVarName, &
72        NormalVarName, FrontMaskName, TopMaskName, TangledVarName
73
74   SAVE :: FirstTime
75
76   !-----------------------------------------------
77   ! Initialisation
78   !-----------------------------------------------
79
80   Debug = .FALSE.
81   Parallel = (ParEnv % PEs > 1)
82   Boss = ParEnv % MyPE == 0
83
84   SolverName = "FrontAdvance3D"
85   Params => Solver % Values
86   Mesh => Solver % Mesh
87
88   !The main solver var contains the magnitude of front advance
89   Var => Solver % Variable
90   Advance => Var % Values
91   Perm => Var % Perm
92   DOFs = Var % DOFs
93   IF(Var % DOFs /= 3) CALL Fatal(SolverName, "Variable should have 3 DOFs...")
94
95   IgnoreVelo = ListGetLogical(Params, "Ignore Velocity", Found)
96   IF(.NOT. Found) THEN
97     IgnoreVelo = .FALSE.
98   ELSE
99     CALL Info(SolverName, "Ignoring velocity (melt undercutting only)")
100   END IF
101
102   IF(.NOT. IgnoreVelo) THEN
103     !Get the flow solution
104     VeloVarName = ListGetString(Params, "Flow Solution Variable Name", Found)
105     IF(.NOT. Found) THEN
106       CALL Info(SolverName, "Flow Solution Variable Name not found, assuming 'Flow Solution'")
107       VeloVarName = "Flow Solution"
108     END IF
109     VeloVar => VariableGet(Mesh % Variables, VeloVarName, .TRUE., UnfoundFatal=.TRUE.)
110   END IF
111
112   !Get melt rate
113   MeltVarName = ListGetString(Params, "Melt Variable Name", Found)
114   IF(.NOT. Found) THEN
115     CALL Info(SolverName, "Melt Variable Name not found, assuming no frontal melting")
116     FrontMelting = .FALSE.
117   ELSE
118     FrontMelting = .TRUE.
119     MeltVar => VariableGet(Mesh % Variables, MeltVarName, .TRUE., UnfoundFatal=.TRUE.)
120   END IF
121
122   TangledVarName = "Tangled"
123   TangledVar => VariableGet(Mesh % Variables, TangledVarName, .TRUE., UnfoundFatal=.TRUE.)
124   TangledVar % Values = 0.0_dp
125
126   !Get front normal vector
127   NormalVarName = ListGetString(Params, "Normal Vector Variable Name", UnfoundFatal=.TRUE.)
128   NormalVar => VariableGet(Mesh % Variables, NormalVarName, .TRUE., UnfoundFatal=.TRUE.)
129
130   !Get the orientation of the calving front, compute rotation matrix
131   !TODO: generalize and link
132   FrontOrientation = GetFrontOrientation(Model)
133   RotationMatrix = ComputeRotationMatrix(FrontOrientation)
134
135   DangerGrad = ListGetConstReal(Params, "Front Gradient Threshold", Found)
136   IF(.NOT.Found) THEN
137     CALL Info(SolverName, "'Front Gradient Threshold' not found, setting to 5.0")
138     DangerGrad = 5.0
139   END IF
140
141   !When correcting for unprojectability, to prevent interp problems,
142   !ensure that it is SLIGHTLY beyond beyond parallel, by adding this
143   !value (metres) to the displacement
144   epsShift = ListGetConstReal(Params, "Front Projectability Epsilon", Found)
145   IF(.NOT.Found) THEN
146     CALL Info(SolverName, "'Front Projectability Epsilon' not found, setting to 1.0")
147     epsShift = 1.0
148   END IF
149
150   epsTangle = ListGetConstReal(Params, "Front Tangle Epsilon", Found)
151   IF(.NOT.Found) THEN
152     CALL Info(SolverName, "'Front Tangle Epsilon' not found, setting to 1.0")
153     epsTangle = 1.0
154   END IF
155
156   LongRangeLimit = ListGetConstReal(Params, "Column Max Longitudinal Range", Found)
157   IF(.NOT.Found) THEN
158     CALL Info(SolverName, "'Column Max Longitudinal Range' not found, setting to 300.0")
159     LongRangeLimit = 300.0_dp
160   END IF
161
162   MaxDisplacement = ListGetConstReal(Params, "Maximum Node Displacement", Found)
163   IF(.NOT.Found) THEN
164     CALL Info(SolverName, "'Maximum Node Displacement' not found, setting to 1.0E4.")
165     MaxDisplacement = 1.0E4_dp
166   END IF
167
168   !-------------------------------------------
169   ! Find FNColumns
170   CALL MPI_AllReduce(MAXVAL(Mesh % ParallelInfo % GlobalDOFs), TotalNodes, &
171        1, MPI_INTEGER, MPI_MAX, ELMER_COMM_WORLD,ierr)
172
173   ExtrudedLevels = ListGetInteger(CurrentModel % Simulation,'Extruded Mesh Levels',Found)
174   IF(.NOT. Found) ExtrudedLevels = &
175        ListGetInteger(CurrentModel % Simulation,'Remesh Extruded Mesh Levels',Found)
176   IF(.NOT. Found) CALL Fatal(SolverName,&
177        "Unable to find 'Extruded Mesh Levels' or 'Remesh Extruded Mesh Levels'")
178   NodesPerLevel = TotalNodes / ExtrudedLevels
179
180   ALLOCATE(FNColumns(Mesh % NumberOfNodes))
181   FNColumns = MOD(Mesh % ParallelInfo % GlobalDOFs, NodesPerLevel)
182
183   !Get the front line
184   FrontMaskName = "Calving Front Mask"
185   TopMaskName = "Top Surface Mask"
186
187   CALL MakePermUsingMask( Model, Solver, Mesh, TopMaskName, &
188        .FALSE., TopPerm, dummyint)
189
190   CALL MakePermUsingMask( Model, Solver, Mesh, FrontMaskName, &
191        .FALSE., FrontPerm, FaceNodeCount)
192
193   CALL GetDomainEdge(Model, Mesh, TopPerm, FrontNodes, &
194        FrontNodeNums, Parallel, FrontMaskName, Simplify=.FALSE.)
195
196   !Pass FrontNodeNums to all CPUs
197   IF(Boss) FrontLineCount = SIZE(FrontNodeNums)
198   CALL MPI_BCAST(FrontLineCount , 1, MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)
199
200   !Determine whether front columns are arranged
201   !left to right, and reorder if not
202   IF(Boss) THEN
203
204     NodeHolder(1) = FrontNodes % x(1)
205     NodeHolder(2) = FrontNodes % y(1)
206     NodeHolder(3) = FrontNodes % z(1)
207     NodeHolder = MATMUL(RotationMatrix, NodeHolder)
208     y_coord(1) = NodeHolder(2)
209
210     NodeHolder(1) = FrontNodes % x(FrontLineCount)
211     NodeHolder(2) = FrontNodes % y(FrontLineCount)
212     NodeHolder(3) = FrontNodes % z(FrontLineCount)
213     NodeHolder = MATMUL(RotationMatrix, NodeHolder)
214     y_coord(2) = NodeHolder(2)
215
216     LeftToRight = y_coord(2) > y_coord(1)
217
218     IF(.NOT. LeftToRight) THEN
219       IF(Debug) PRINT *,'Debug, switching to LeftToRight'
220       FrontNodeNums = FrontNodeNums(FrontLineCount:1:-1)
221       FrontNodes % x = FrontNodes % x(FrontLineCount:1:-1)
222       FrontNodes % y = FrontNodes % y(FrontLineCount:1:-1)
223       FrontNodes % z = FrontNodes % z(FrontLineCount:1:-1)
224     END IF
225   END IF
226
227   IF(.NOT. Boss) ALLOCATE(FrontNodeNums(FrontLineCount))
228   CALL MPI_BCAST(FrontNodeNums , FrontLineCount, MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)
229
230   !FrontNodeNums is the GlobalDOFs of the nodes on the front
231   !So this can be easily converted into a FNColumn like list
232   ALLOCATE(FrontColumnList(FrontLineCount), &
233        FrontLocalNodeNumbers(FrontLineCount), &
234        DangerZone(FrontLineCount), &
235        ColumnNormals(FrontLineCount,3))
236
237   FrontColumnList = MOD(FrontNodeNums, NodesPerLevel)
238   FrontLocalNodeNumbers = -1
239   DangerZone = .FALSE.
240   ColumnNormals = 0.0_dp
241
242   DO i=1,FrontLineCount
243     n = FrontNodeNums(i)
244     DO j=1,Mesh % NumberOfNodes
245       IF(Mesh % ParallelInfo % GlobalDOFs(j) == n) THEN
246         FrontLocalNodeNumbers(i) = j
247       END IF
248     END DO
249   END DO
250
251   !--------------------------------------
252   ! Action: Compute lagrangian displacement for all nodes
253   !         This is the main function of the Solver.
254   !         Everything following this DO loop is taking care of
255   !         unprojectability/high gradient etc
256   !--------------------------------------
257
258   DO i=1,Mesh % NumberOfNodes
259     IF(Perm(i) <= 0) CYCLE
260
261     IF(FrontMelting) THEN
262       IF(MeltVar % Perm(i) <= 0) &
263            CALL Fatal(SolverName, "Permutation error on front node!")
264
265
266       !Scalar melt value from Plume solver
267       MeltRate = MeltVar % Values(MeltVar % Perm(i))
268
269       NodeNormal(1) = NormalVar % Values(((NormalVar % Perm(i)-1)*NormalVar % DOFs) + 1)
270       NodeNormal(2) = NormalVar % Values(((NormalVar % Perm(i)-1)*NormalVar % DOFs) + 2)
271       NodeNormal(3) = NormalVar % Values(((NormalVar % Perm(i)-1)*NormalVar % DOFs) + 3)
272
273       NodeMelt = NodeNormal * MeltRate
274     ELSE
275       NodeMelt = 0.0_dp
276     END IF
277
278     IF(IgnoreVelo) THEN
279       NodeVelo = 0.0
280     ELSE
281       !Compute front normal component of velocity
282       NodeVelo(1) = VeloVar % Values(((VeloVar % Perm(i)-1)*VeloVar % DOFs) + 1)
283       NodeVelo(2) = VeloVar % Values(((VeloVar % Perm(i)-1)*VeloVar % DOFs) + 2)
284       NodeVelo(3) = VeloVar % Values(((VeloVar % Perm(i)-1)*VeloVar % DOFs) + 3)
285     END IF
286
287     Displace = 0.0
288
289     Displace(1) =  NodeVelo(1) - NodeMelt(1)
290     Displace(2) =  NodeVelo(2) - NodeMelt(2)
291     Displace(3) =  NodeVelo(3) - NodeMelt(3)
292
293     Displace = Displace * dt
294
295     IF(MAXVAL(Displace) > MaxDisplacement) THEN
296       WRITE(Message,'(A,i0,A)') "Maximum allowable front displacement exceeded for node ",i,". Limiting..."
297       CALL Warn(SolverName, Message)
298       Displace = Displace * (MaxDisplacement/MAXVAL(Displace))
299     END IF
300
301     Advance((Perm(i)-1)*DOFs + 1) = Displace(1)
302     Advance((Perm(i)-1)*DOFs + 2) = Displace(2)
303     Advance((Perm(i)-1)*DOFs + 3) = Displace(3)
304
305     !First and last (i.e. lateral margin) columns don't move
306     IF((FNColumns(i) == FrontColumnList(1)) .OR. &
307          (FNColumns(i) == FrontColumnList(FrontLineCount))) THEN
308       Advance((Perm(i)-1)*DOFs + 1) = 0.0_dp
309       Advance((Perm(i)-1)*DOFs + 2) = 0.0_dp
310       Advance((Perm(i)-1)*DOFs + 3) = 0.0_dp
311     END IF
312   END DO
313
314
315   !----------------------------------------------------------
316   ! Now we need to look for and limit  regions on the
317   ! where high gradient + 'straightness' makes
318   ! remeshing into vertical columns troublesome.
319   !----------------------------------------------------------
320   !
321   ! Five things:
322   !  - Limit horizontal range (otherwise, Remesh will smear a column of nodes
323   !    all over the place)
324   !  - Limit undercut range (longitudinal range) to prevent mesh degeneracy
325   !  - Prevent retreat at node near lateral margins
326   !  - Check for 'tangled' regions, where fixing unprojectability one way
327   !    causes unprojectability the other way.
328   !  - Prevent Unprojectability - requires knowledge of neighbouring columns
329   !----------------------------------------------------------
330
331   !-------------------------------------------
332   ! Cycle columns, looking at average normal for the column
333   !-------------------------------------------
334   ! Normal vector is limited by projectability, so if average is high,
335   ! all are high
336   !
337   ! Note that we are cycling the parallel gathered line, so each part
338   ! will only have a few columns (if any)
339   ! So, mark troublesome columns where present, then communicate
340   !-------------------------------------------
341
342   DO i=1,FrontLineCount
343     col = FrontColumnList(i)
344
345     hits = COUNT(FNColumns == col)
346     IF(hits == 0) CYCLE
347
348     ColumnNormal = 0.0_dp
349
350     !Gather normal vector
351     DO j=1,Mesh % NumberOfNodes
352       IF(FNColumns(j) == col) THEN
353         ColumnNormal(1) = ColumnNormal(1) + &
354              NormalVar % Values(((NormalVar % Perm(j)-1)*NormalVar % DOFs) + 1)
355         ColumnNormal(2) = ColumnNormal(2) + &
356              NormalVar % Values(((NormalVar % Perm(j)-1)*NormalVar % DOFs) + 2)
357         ColumnNormal(3) = ColumnNormal(3) + &
358              NormalVar % Values(((NormalVar % Perm(j)-1)*NormalVar % DOFs) + 3)
359       END IF
360     END DO
361
362     !unit vector
363     ColumnNormal = ColumnNormal / hits
364     !Convert to front coordinate system
365     ColumnNormal = MATMUL( RotationMatrix, ColumnNormal )
366     !Save for later
367     ColumnNormals(i,1:3) = ColumnNormal(1:3)
368
369     !We're concerned with the lateral (rather than vertical) component
370     IF( ABS(ColumnNormal(2) / ColumnNormal(3)) > DangerGrad ) THEN
371       DangerZone(i) = .TRUE.
372     END IF
373   END DO
374
375   !Communicate between partitions
376   !This is an 'OR' condition insofar as only one partition
377   !will have actually computed the gradient
378   IF(Parallel) THEN
379     DO i=1,FrontLineCount
380       CALL SParIterAllReduceOR(DangerZone(i))
381     END DO
382   END IF
383
384   !Mark before and after dangerzone too
385   ALLOCATE(WorkLogical(FrontLineCount))
386   WorkLogical = .FALSE.
387
388   DO i=1,FrontLineCount
389     IF(DangerZone(i)) WorkLogical(i) = .TRUE.
390
391     IF(i > 1) THEN
392       IF(DangerZone(i-1)) WorkLogical(i) = .TRUE.
393     END IF
394
395     IF(i < FrontLineCount) THEN
396       IF(DangerZone(i+1)) WorkLogical(i) = .TRUE.
397     END IF
398   END DO
399
400   DangerZone = WorkLogical
401   DEALLOCATE(WorkLogical)
402
403   !Find and store leftmost and rightmost (rotated) coordinate of
404   !each column for checking projectability later
405   !Rot_y_coords(:,1) is leftmost, and (:,2) is right most y coord of
406   !each column's nodes, and Rot_z_coords(:,:) is the min(1)/max(2) z
407   ALLOCATE(Rot_y_coords(FrontLineCount,2),&
408        Rot_z_coords(FrontLineCount,2))
409   Rot_y_coords(:,1) = HUGE(0.0_dp)
410   Rot_y_coords(:,2) = -HUGE(0.0_dp)
411   Rot_z_coords(:,1) = HUGE(0.0_dp)
412   Rot_z_coords(:,2) = -HUGE(0.0_dp)
413
414   DO i=1,FrontLineCount
415     col = FrontColumnList(i)
416     IF(COUNT(FNColumns == col) == 0) CYCLE
417
418     DO j=1,Mesh % NumberOfNodes
419       IF(FNColumns(j) /= col) CYCLE
420
421       NodeHolder(1) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)
422       NodeHolder(2) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)
423       NodeHolder(3) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)
424       NodeHolder = MATMUL(RotationMatrix, NodeHolder)
425
426       Rot_y_coords(i,1) = MIN(Rot_y_coords(i,1), NodeHolder(2))
427       Rot_y_coords(i,2) = MAX(Rot_y_coords(i,2), NodeHolder(2))
428
429       Rot_z_coords(i,1) = MIN(Rot_z_coords(i,1), NodeHolder(3))
430       Rot_z_coords(i,2) = MAX(Rot_z_coords(i,2), NodeHolder(3))
431     END DO
432   END DO
433
434   !-----------------------------------
435   ! Limit lateral range (to 0) in DangerZone
436   !-----------------------------------
437   ! The physical interpretation of this is that, when melt undercutting occurs
438   ! in a region of high gradient, the unmelted parts of that column also shift
439   ! with the melt.
440   ! This is a minor limitation, but better than Free Surface Equation!
441
442   DO i=1,FrontLineCount
443     IF(.NOT. DangerZone(i)) CYCLE
444
445     col = FrontColumnList(i)
446     hits = COUNT(FNColumns == col)
447
448     IF(hits == 0) CYCLE
449
450     ALLOCATE(NodeNumbers(hits), &
451          ColumnNodes % x(hits),&
452          ColumnNodes % y(hits),&
453          ColumnNodes % z(hits))
454
455     ColumnNodes % NumberOfNodes = hits
456
457     !Gather nodenumbers in column
458     county = 0
459     DO j=1,Mesh % NumberOfNodes
460       IF(FNColumns(j) /= col) CYCLE
461       county = county + 1
462
463       NodeNumbers(county) = j
464       ColumnNodes % x(county) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)
465       ColumnNodes % y(county) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)
466       ColumnNodes % z(county) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)
467     END DO
468
469     !direction - which way is this part of the front pointing?
470     ShiftLeft = ColumnNormals(i,2) > 0
471     IF(ShiftLeft) THEN
472       ShiftTo = HUGE(ShiftTo)
473     ELSE
474       ShiftTo = -HUGE(ShiftTo)
475     END IF
476
477     !Rotate points and find furthest left (or right)
478     DO j=1,ColumnNodes % NumberOfNodes
479       NodeHolder(1) = ColumnNodes % x(j)
480       NodeHolder(2) = ColumnNodes % y(j)
481       NodeHolder(3) = ColumnNodes % z(j)
482       NodeHolder = MATMUL(RotationMatrix, NodeHolder)
483
484       IF(ShiftLeft) THEN
485         IF(NodeHolder(2) < ShiftTo) ShiftTo = NodeHolder(2)
486       ELSE
487         IF(NodeHolder(2) > ShiftTo) ShiftTo = NodeHolder(2)
488       END IF
489     END DO
490
491     !Now, for each node in column, compute the displacement (in rotated y coordinate)
492     DO j=1,ColumnNodes % NumberOfNodes
493       NodeHolder(1) = ColumnNodes % x(j)
494       NodeHolder(2) = ColumnNodes % y(j)
495       NodeHolder(3) = ColumnNodes % z(j)
496       NodeHolder = MATMUL(RotationMatrix, NodeHolder)
497
498       ShiftDist = ShiftTo - NodeHolder(2)
499
500       Displace = 0.0_dp
501       Displace(2) = ShiftDist
502
503       Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)
504
505       !Adjust the variable values to shift nodes in line.
506       Advance((Perm(NodeNumbers(j))-1)*DOFs + 1) = &
507            Advance((Perm(NodeNumbers(j))-1)*DOFs + 1) + Displace(1)
508       Advance((Perm(NodeNumbers(j))-1)*DOFs + 2) = &
509            Advance((Perm(NodeNumbers(j))-1)*DOFs + 2) + Displace(2)
510
511       IF(Debug) PRINT *,'node: ',NodeNumbers(j),' shifting: ',ShiftDist,' to ',ShiftTo,' point: ', &
512            ColumnNodes % x(j),ColumnNodes % y(j),ColumnNodes % z(j)
513     END DO
514
515     DEALLOCATE(NodeNumbers, &
516          ColumnNodes % x, &
517          ColumnNodes % y, &
518          ColumnNodes % z)
519   END DO
520
521   !-----------------------------------
522   ! Limit longitudinal range everywhere
523   !-----------------------------------
524   ! Two options for how to do this:
525   ! Drag nodes back with melt, or effectively
526   ! limit melt (keeping nodes forward)
527   ! Opt for the latter, or else we're
528   ! forcing melting to have an effect
529   !-----------------------------------
530   DO i=1,FrontLineCount
531
532     col = FrontColumnList(i)
533     hits = COUNT(FNColumns == col)
534
535     IF(hits == 0) CYCLE
536
537     IF((Rot_z_coords(i,2) - Rot_z_coords(i,1)) > LongRangeLimit) THEN
538
539       LimitZ = Rot_z_coords(i,2) - LongRangeLimit
540       Rot_z_coords(i,1) = LimitZ
541
542       DO j=1,Mesh % NumberOfNodes
543         IF(FNColumns(j) /= col) CYCLE
544
545         NodeHolder(1) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)
546         NodeHolder(2) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)
547         NodeHolder(3) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)
548         NodeHolder = MATMUL(RotationMatrix, NodeHolder)
549
550         IF(NodeHolder(3) >= LimitZ) CYCLE
551
552         Displace = 0.0_dp
553         Displace(3) = LimitZ - NodeHolder(3)
554
555         Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)
556
557         !Adjust the variable values to shift nodes in line.
558         Advance((Perm(j)-1)*DOFs + 1) = &
559              Advance((Perm(j)-1)*DOFs + 1) + Displace(1)
560         Advance((Perm(j)-1)*DOFs + 2) = &
561              Advance((Perm(j)-1)*DOFs + 2) + Displace(2)
562         IF(Debug) PRINT *,ParEnv % MyPE,' Debug, limiting node range: col ',&
563              i,' node ',j,' limit: ',LimitZ,' displace: ',Displace
564       END DO
565
566     END IF
567   END DO
568
569
570   !-----------------------------------
571   ! Now check projectability
572   !-----------------------------------
573   ! If an unprojectable portion of the front is found,
574   ! the inland nodes remain in place, while those further
575   ! advanced shift sideways to maintain projectability
576   !
577   ! Typical case is melt plume at end of recently calved
578   ! straight edge.
579   !
580   ! requires MPI comms
581
582   !Gather rotated y coord of all columns
583   DO i=1,FrontLineCount
584     CALL MPI_AllReduce(MPI_IN_PLACE, Rot_y_coords(i,1), &
585          1, MPI_DOUBLE_PRECISION, MPI_MIN, ELMER_COMM_WORLD,ierr)
586     CALL MPI_AllReduce(MPI_IN_PLACE, Rot_y_coords(i,2), &
587          1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD,ierr)
588
589     CALL MPI_AllReduce(MPI_IN_PLACE, Rot_z_coords(i,1), &
590          1, MPI_DOUBLE_PRECISION, MPI_MIN, ELMER_COMM_WORLD,ierr)
591     CALL MPI_AllReduce(MPI_IN_PLACE, Rot_z_coords(i,2), &
592          1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD,ierr)
593
594     IF(Boss .AND. Debug) PRINT *,'Debug, rot_y_coords: ',i,rot_y_coords(i,:)
595     IF(Boss .AND. Debug) PRINT *,'Debug, rot_z_coords: ',i,rot_z_coords(i,:)
596   END DO
597
598   ALLOCATE(UpdatedColumn(FrontLineCount), &
599        UpdatedDirection(FrontLineCount), &
600        Tangled(FrontLineCount),&
601        TangledGroup(FrontLineCount),&
602        TangledPivotIdx(FrontLineCount),&
603        TangledShiftTo(FrontLineCount),&
604        DontMove(FrontLineCount))
605
606   UpdatedColumn = .FALSE.
607   UpdatedDirection = 0
608   Tangled = .FALSE.
609   TangledGroup = 0
610   TangledPivotIdx = 0
611   TangledShiftTo = 0.0_dp
612   DontMove = .FALSE.
613
614   DontMove(1) = .TRUE.
615   DontMove(FrontLineCount) = .TRUE.
616
617   !TODO: Deal with DontMove and tangling properly...
618   ! Ensure that comms is not necessary for DontMove
619
620   !Test for thin sliver at lateral margins - potential to get
621   !stuck like this, so impose an angle here
622   !We require that the 2nd node from the end not retreat beyond
623   !a 45 degree angle inland. PYTHAGORAS
624   !Shift the 2nd column in the lateral direction only,
625   !to the point where it makes a 45 degree angle w/
626   !the corner.
627   !         *
628   !       / |
629   !     /   |
630   !    * <- *
631
632   DO i=1,2
633
634     IF(i==1) THEN
635       CornerIdx = 1
636       SecondIdx = 2
637       direction = 1.0_dp
638     ELSE
639       CornerIdx = FrontLineCount
640       SecondIdx = FrontLineCount-1
641       direction = -1.0_dp
642     END IF
643
644     !Conveniently, first time round we want the left most coord (Rot_y_coords(:,1))
645     !whereas second time we want the right (Rot_y_cords(:,2))
646     CornerDirection(1) = Rot_y_coords(SecondIdx,i) - Rot_y_coords(CornerIdx,1)
647     CornerDirection(2) = Rot_z_coords(SecondIdx,i) - Rot_z_coords(CornerIdx,1)
648     !Unit vector
649     CornerDirection = CornerDirection / (SUM(CornerDirection ** 2.0)**0.5)
650
651     IF(Debug) PRINT *,ParEnv % MyPE, 'CornerDirection ',i,': ',CornerDirection
652
653     IF( CornerDirection(2) < (-1.0_dp / (2.0_dp ** 0.5)) ) THEN
654       !Shift 2nd node and mark DontMove
655
656       ShiftToY = Rot_y_coords(CornerIdx,1) + &
657            direction * ((Rot_z_coords(CornerIdx,1) - Rot_z_coords(SecondIdx,1)))
658
659       Rot_y_coords(SecondIdx,1) = ShiftToY
660       Rot_y_coords(SecondIdx,2) = ShiftToY
661
662       DontMove(SecondIdx) = .TRUE.
663       IF(Debug) PRINT *,ParEnv % MyPE,' debug, side ',i,' of front is too inwards'
664
665       DO k=1,Mesh % NumberOfNodes
666         IF(FNColumns(k) /= FrontColumnList(SecondIdx)) CYCLE
667
668         NodeHolder(1) = Mesh % Nodes % x(k) + Advance((Perm(k)-1)*DOFs + 1)
669         NodeHolder(2) = Mesh % Nodes % y(k) + Advance((Perm(k)-1)*DOFs + 2)
670         NodeHolder(3) = Mesh % Nodes % z(k) + Advance((Perm(k)-1)*DOFs + 3)
671
672         NodeHolder = MATMUL(RotationMatrix, NodeHolder)
673
674         Displace = 0.0_dp
675         Displace(2) = ShiftToY - NodeHolder(2)
676
677         Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)
678
679         IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shifting next to corner node ',k,&
680              NodeHolder,' by ',Displace
681
682         !Adjust the variable values to shift nodes in line.
683         Advance((Perm(k)-1)*DOFs + 1) = Advance((Perm(k)-1)*DOFs + 1) + Displace(1)
684         Advance((Perm(k)-1)*DOFs + 2) = Advance((Perm(k)-1)*DOFs + 2) + Displace(2)
685       END DO
686
687     END IF
688   END DO
689
690   !-----------------------------------------------
691   ! Cycle the line, looking for unprojectable
692   ! (but not tangled) regions.
693   ! e.g. plume melting behind a vertical portion
694   !-----------------------------------------------
695
696   !In case shifting nodes affects another partition, we loop so long as
697   !at least one partition has MovedOne
698   MovedOne = .TRUE.
699   county = 0
700   DO WHILE(MovedOne)
701     county = county + 1
702     IF(county > 100) CALL Fatal(SolverName, "Infinite loop!")
703     IF(Debug) PRINT *,ParEnv % MyPE, 'Debug, iterating projectability ', county
704
705     MovedOne = .FALSE.
706     UpdatedColumn = .FALSE.
707
708     DO i=2,FrontLineCount
709
710       !If already detangled, skip
711       !IF(Tangled(i)) CYCLE
712
713       !epsShift here
714       IF((Rot_y_coords(i,1) - Rot_y_coords(i-1,2)) < 0.9*epsShift) THEN
715
716         IF(Debug) PRINT *, 'Debug diff ',i,i-1, &
717              (Rot_y_coords(i,1) - Rot_y_coords(i-1,2)), epsShift
718
719         IF(DontMove(i) .AND. DontMove(i-1)) THEN
720           CALL Warn(SolverName,&
721                "Both nodes are marked Dont Move, stopping shifting.")
722           EXIT
723         END IF
724         !Work out which to shift
725         !If one is marked DontMove (corner node, or 2nd inland and weird shape)
726         ! then move the other
727         !Otherwise, shift whichever is further forward
728         IF(DontMove(i)) THEN
729           ShiftSecond = .FALSE.
730           DontMove(i-1) = .TRUE.
731           IF(Debug) PRINT *,ParEnv % MyPE,'Debug, do not move second: ',i
732         ELSE IF(DontMove(i-1)) THEN
733           ShiftSecond = .TRUE.
734           DontMove(i) = .TRUE.
735           IF(Debug) PRINT *,ParEnv % MyPE,'Debug, do not move first: ',i-1
736         ELSE
737           IF(SUM(Rot_z_coords(i,:)) > SUM(Rot_z_coords(i-1,:))) THEN
738             ShiftSecond = .TRUE.
739           ELSE
740             ShiftSecond = .FALSE.
741           END IF
742         END IF
743
744         IF(ShiftSecond) THEN
745           ShiftIdx = i
746           ShiftToIdx = i-1
747         ELSE
748           ShiftIdx = i-1
749           ShiftToIdx = i
750         END IF
751
752         !Calculate ShiftTo, depends on ShiftSecond
753         !Also update shifted Rot_y_coords
754         IF(ShiftSecond) THEN
755           !If this node has already been moved the other way, leave it alone
756           IF((UpdatedDirection(ShiftIdx) < 0) .AND. .NOT. DontMove(ShiftToIdx)) CYCLE
757
758           ShiftTo = Rot_y_coords(i-1,2) + epsShift
759           IF(Rot_y_coords(i,2) < ShiftTo) UpdatedDirection(ShiftIdx) = 1
760
761           Rot_y_coords(i,1) = MAX(Rot_y_coords(i,1), ShiftTo)
762           Rot_y_coords(i,2) = MAX(Rot_y_coords(i,2), ShiftTo)
763         ELSE
764           !If this node has already been moved the other way, leave it alone
765           IF((UpdatedDirection(ShiftIdx) > 0) .AND. .NOT. DontMove(ShiftToIdx)) CYCLE
766
767           ShiftTo = Rot_y_coords(i,1) - epsShift
768           IF(Rot_y_coords(i,1) > ShiftTo) UpdatedDirection(ShiftIdx) = -1
769
770           Rot_y_coords(i-1,1) = MIN(Rot_y_coords(i-1,1), ShiftTo)
771           Rot_y_coords(i-1,2) = MIN(Rot_y_coords(i-1,2), ShiftTo)
772         END IF
773
774         IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, updating col ',ShiftIdx,&
775              ' rot_y_coords: ',Rot_y_coords(ShiftIdx,:)
776
777         UpdatedColumn(ShiftIdx) = .TRUE.
778
779         !Shift all the nodes in this column
780         DO j=1,Mesh % NumberOfNodes
781           IF(FNColumns(j) /= FrontColumnList(ShiftIdx)) CYCLE
782
783           NodeHolder(1) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)
784           NodeHolder(2) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)
785           NodeHolder(3) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)
786           NodeHolder = MATMUL(RotationMatrix, NodeHolder)
787
788           Displace = 0.0_dp
789           Displace(2) = ShiftTo - NodeHolder(2)
790
791           !Check if node already projectable
792           IF(ShiftSecond) THEN
793             IF(Displace(2) < 0 ) CYCLE
794           ELSE
795             IF(Displace(2) > 0) CYCLE
796           END IF
797
798           Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)
799
800           IF(Debug) PRINT *,ParEnv % MyPE, 'Debug, shifting node ',j,' col ',ShiftIdx,'xyz: ',&
801                Mesh % Nodes % x(j)+Advance((Perm(j)-1)*DOFs + 1),&
802                Mesh % Nodes % y(j)+Advance((Perm(j)-1)*DOFs + 2),&
803                Mesh % Nodes % z(j)+Advance((Perm(j)-1)*DOFs + 3),&
804                ' by ',Displace,' to ensure projectability. 1'
805
806
807           !Adjust the variable values to shift nodes in line.
808           Advance((Perm(j)-1)*DOFs + 1) = Advance((Perm(j)-1)*DOFs + 1) + Displace(1)
809           Advance((Perm(j)-1)*DOFs + 2) = Advance((Perm(j)-1)*DOFs + 2) + Displace(2)
810
811         END DO
812       END IF
813     END DO
814
815     IF(ANY(UpdatedColumn)) MovedOne = .TRUE.
816
817     IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, MovedOne: ',MovedOne
818     IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, UpdatedColumn: ',UpdatedColumn
819
820   END DO
821
822
823   !-----------------------------------------------
824   ! Check for pinnacles or rifts which can't be made
825   ! projectable. Set the columns in a line and mark them
826   ! for removal by Remesh.F90
827   !-----------------------------------------------
828   !TODO: At least 3 separate issues here:
829   !
830   !     *This one should be fixed now, at the end we check for groups disappearing*
831   ! 1 : If there's a tangled group, then another, superset tangled
832   !     group, this causes "programming error, tangled group has zero nodes"
833   !     potential solution here is to just remove this error message, because
834   !     its not really a problem *I DON'T THINK*. However, if so, need to
835   !     check for it, and shift TangledGroups down one, so Remesh behaves
836   !
837   !     *This one should now be fixed, because we do all the unprojectable shifting first:*
838   ! 2 : Here we iteratively: check tangled, then check unprojectable
839   !     If correcting unprojectability causes further tangling, this
840   !     isn't caught. Potential solutions:
841   !            Improve parallel unprojectability checker (dirty fix, won't catch every poss case)
842   !            Better: recheck previously tangled regions
843   !
844   ! 3 : Long straight sides which also happen to tangle with the end of their headlands
845   !     are not well handled. All the nodes along these sides are shifted out the way
846   !     in the new mesh
847
848
849   NoTangledGroups = 0
850   DO i=1,FrontLineCount-2
851     IF(Tangled(i)) CYCLE
852     j = i+2
853
854     !If the column two columns away is less than 2*epsShift away, and there
855     !is a change of direction, problems...
856     IF((Rot_y_coords(j,1) - Rot_y_coords(i,2)) < 2*epsShift) THEN
857
858       IF(((Rot_z_coords(i,1) < Rot_z_coords(i+1,1)) .NEQV. &
859            (Rot_z_coords(i+1,1) < Rot_z_coords(j,1))) .OR. &
860            ((Rot_z_coords(i,2) < Rot_z_coords(i+1,2)) .NEQV. &
861            (Rot_z_coords(i+1,2) < Rot_z_coords(j,2)))) THEN
862
863         !Either a pinnacle or a slit (i.e protrusion or rift)
864         Protrusion = SUM(Rot_z_coords(i+1,:)) > SUM(Rot_z_coords(i,:))
865         IF(Debug) PRINT *,'Debug, protrusion: ',Protrusion
866
867         NoTangledGroups = NoTangledGroups + 1
868         PivotIdx = i+1
869
870         DO k=2,PivotIdx
871           p1(1) = Rot_y_coords(k,2)
872           p1(2) = Rot_z_coords(k,2)
873
874           p2(1) = Rot_y_coords(k-1,2)
875           p2(2) = Rot_z_coords(k-1,2)
876
877           DO m=FrontLineCount-1,PivotIdx,-1
878             IF(k==m) CYCLE !first two will always intersect by definition, not what we want
879             q1(1) = Rot_y_coords(m,1)
880             q1(2) = Rot_z_coords(m,1)
881
882             q2(1) = Rot_y_coords(m+1,1)
883             q2(2) = Rot_z_coords(m+1,1)
884
885             CALL LineSegmentsIntersect ( p1, p2, q1, q2, intersect, intersect_flag )
886
887             IF(intersect_flag) EXIT
888           END DO
889           IF(intersect_flag) EXIT
890         END DO
891
892         IF(intersect_flag) THEN
893
894           !Found an intersection point (intersect)
895           PRINT *,'Debug, found tangle intersection ',intersect,' leaving last tangled nodes: ',k,m
896
897           Tangled(k:m) = .TRUE.
898           TangledPivotIdx(k:m) = PivotIdx
899           TangledShiftTo(k:m) = intersect(1)
900
901           IF(Protrusion) THEN
902             TangledGroup(k:m) = NoTangledGroups
903           ELSE
904             TangledGroup(k:m) = -NoTangledGroups
905           END IF
906
907         ELSE
908           PRINT *,'Debug, found no intersection, so nodes are not QUITE tangled: ',i,j
909
910           Tangled(i:j) = .TRUE.
911           TangledPivotIdx(i:j) = PivotIdx
912           TangledShiftTo(i:j) = (SUM(rot_y_coords(i,:)) + SUM(rot_y_coords(j,:))) / 4.0_dp
913
914           IF(Protrusion) THEN
915             TangledGroup(i:j) = NoTangledGroups
916           ELSE
917             TangledGroup(i:j) = -NoTangledGroups
918           END IF
919
920         END IF
921       END IF
922     END IF
923   END DO
924
925   !Check for a tangled group 'swallowing' another
926   county = 0
927   DO i=1,NoTangledGroups
928     IF(COUNT(TangledGroup == i) > 0) CYCLE
929     county = county + 1
930     !Shift group numbers down one
931     DO j=1,SIZE(TangledGroup)
932       IF(TangledGroup(j) > i) TangledGroup(j) = TangledGroup(j) - 1
933     END DO
934   END DO
935   NoTangledGroups = NoTangledGroups - county
936
937   !Strategy:
938   ! Shift all nodes to near (offset) the y-coordinate
939   ! of the tangle pivot (i+1, above)
940   DO i=1,NoTangledGroups
941     n = COUNT(TangledGroup == i)
942     IF(n==0) THEN
943       n = COUNT(TangledGroup == -i)
944       Protrusion = .FALSE.
945     ELSE
946       Protrusion = .TRUE.
947     END IF
948     IF(n==0) CALL Fatal(SolverName, "Programming error: tangled group has 0 nodes?")
949
950     !Get the pivot node index, ShiftToY, and tangled node range
951     FirstTangleIdx = 0
952     LastTangleIdx = 0
953     DO j=1,FrontLineCount
954       IF(TangledGroup(j) == i .OR. TangledGroup(j) == -i) THEN
955         IF(FirstTangleIdx == 0) FirstTangleIdx = j
956         LastTangleIdx = j
957         PivotIdx = TangledPivotIdx(j)
958         ShiftToY = TangledShiftTo(j)
959         !EXIT
960       END IF
961     END DO
962
963     IF(LastTangleIdx - FirstTangleIdx /= n-1) CALL Fatal(SolverName, &
964          "Programming error: wrong number of nodes in TangledGroup")
965     IF(Debug) PRINT *,'Debug, tangled pivot index, ShiftToY: ', PivotIdx, ShiftToY
966
967     LeftY =  ShiftToY - (epsTangle * ((n-1) / 2.0_dp))
968     RightY = ShiftToY + (epsTangle * ((n-1) / 2.0_dp))
969
970     !Potentially 'squeezed' on either side by neighbour columns
971     SqueezeLeft = .FALSE.
972     SqueezeRight = .FALSE.
973
974     IF(LeftY < Rot_y_coords(FirstTangleIdx-1,2) + epsTangle) THEN
975       SqueezeLeft = .TRUE.
976       IF( ( (Rot_y_coords(LastTangleIdx+1,1) - epsTangle) - (epsTangle * (n-1)) ) < &
977            (Rot_y_coords(FirstTangleIdx-1,2) + epsTangle)) THEN
978         SqueezeRight = .TRUE.
979       END IF
980     END IF
981     IF(RightY > (Rot_y_coords(LastTangleIdx+1,1) - epsTangle)) THEN
982       SqueezeRight = .TRUE.
983       IF( ( (Rot_y_coords(LastTangleIdx+1,1) - epsTangle) - (epsTangle * (n-1)) ) < &
984            (Rot_y_coords(FirstTangleIdx-1,2) + epsTangle)) THEN
985         SqueezeLeft = .TRUE.
986       END IF
987     END IF
988
989     IF(Debug) PRINT *,'Debug, LeftY: ',LeftY,' RightY: ',RightY,' SqueezeL: ',&
990          SqueezeLeft,' SqueezeR: ',SqueezeRight,' prev: ',Rot_y_coords(FirstTangleIdx-1,2),&
991          ' next: ',Rot_y_coords(LastTangleIdx+1,1)
992
993     !If squeezed, adjust the new y coord of the tangled nodes
994     IF(SqueezeLeft .AND. SqueezeRight) THEN
995       thisEps = (Rot_y_coords(LastTangleIdx+1,1) - Rot_y_coords(FirstTangleIdx-1,2)) / (n+1)
996       LeftY = Rot_y_coords(FirstTangleIdx-1,2) + thisEps
997       RightY = Rot_y_coords(LastTangleIdx+1,1) - thisEps
998       ShiftToY = (RightY + LeftY) / 2.0_dp !midpoint
999     ELSE IF(SqueezeLeft) THEN
1000       Shift = (Rot_y_coords(FirstTangleIdx-1,2) + epsTangle) - LeftY
1001       LeftY = LeftY + Shift
1002       RightY = RightY + Shift
1003       ShiftToY = ShiftToY + Shift
1004     ELSE IF(SqueezeRight) THEN
1005       Shift = (Rot_y_coords(LastTangleIdx+1,1) - epsTangle) - RightY
1006       LeftY = LeftY + Shift
1007       RightY = RightY + Shift
1008       ShiftToY = ShiftToY + Shift
1009     END IF
1010
1011     !Where tangling occurs, we shift the tangled columns to be
1012     !1m apart in a series. Then Remesh gets rid of them
1013     DO j=FirstTangleIdx,LastTangleIdx
1014       IF(.NOT. (TangledGroup(j) == i .OR. TangledGroup(j) == -i)) &
1015            CALL Fatal(SolverName, "Programming error: node in specified idx range not tangled?")
1016
1017       thisY = LeftY + ((REAL(j - FirstTangleIdx)/(n-1)) * (RightY - LeftY))
1018       IF(Debug) PRINT *,'Debug, thisY: ',thisY, j
1019
1020       DO k=1,Mesh % NumberOfNodes
1021         IF(FNColumns(k) /= FrontColumnList(j)) CYCLE
1022
1023         NodeHolder(1) = Mesh % Nodes % x(k) + Advance((Perm(k)-1)*DOFs + 1)
1024         NodeHolder(2) = Mesh % Nodes % y(k) + Advance((Perm(k)-1)*DOFs + 2)
1025         NodeHolder(3) = Mesh % Nodes % z(k) + Advance((Perm(k)-1)*DOFs + 3)
1026         IF(Debug) PRINT *, ParEnv % MyPE, ' Debug, pre shift tangled node ',k,': ',NodeHolder
1027
1028         NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1029
1030         Displace = 0.0_dp
1031         Displace(2) = thisY - NodeHolder(2)
1032
1033         Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)
1034
1035         IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shifting node ',k, ' col ',j,&
1036              ' by ',Displace,' to detangle.'
1037
1038         !Adjust the variable values to shift nodes in line.
1039         Advance((Perm(k)-1)*DOFs + 1) = Advance((Perm(k)-1)*DOFs + 1) + Displace(1)
1040         Advance((Perm(k)-1)*DOFs + 2) = Advance((Perm(k)-1)*DOFs + 2) + Displace(2)
1041         !Set this variable so that Remesh knows
1042         TangledVar % Values(TangledVar % Perm(k)) = 1.0_dp * ABS(TangledGroup(j))
1043       END DO
1044     END DO
1045   END DO
1046
1047
1048   !---------------------------------------
1049   !Done, just deallocations
1050
1051   FirstTime = .FALSE.
1052
1053   DEALLOCATE(FrontPerm, &
1054        TopPerm, &
1055        FrontNodeNums, &
1056        Rot_y_coords, &
1057        UpdatedColumn, &
1058        UpdatedDirection, &
1059        Tangled, &
1060        TangledGroup, &
1061        TangledPivotIdx, &
1062        TangledShiftTo, &
1063        FrontColumnList, &
1064        FrontLocalNodeNumbers)
1065
1066 END SUBROUTINE FrontAdvance3D
1067