Path: blob/devel/elmerice/Solvers/CalvingFrontAdvance3D.F90
3204 views
!/*****************************************************************************/1! *2! * Elmer/Ice, a glaciological add-on to Elmer3! * http://elmerice.elmerfem.org4! *5! *6! * This program is free software; you can redistribute it and/or7! * modify it under the terms of the GNU General Public License8! * as published by the Free Software Foundation; either version 29! * of the License, or (at your option) any later version.10! *11! * This program is distributed in the hope that it will be useful,12! * but WITHOUT ANY WARRANTY; without even the implied warranty of13! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14! * GNU General Public License for more details.15! *16! * You should have received a copy of the GNU General Public License17! * along with this program (in file fem/GPL-2); if not, write to the18! * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,19! * Boston, MA 02110-1301, USA.20! *21! *****************************************************************************/22! ******************************************************************************23! *24! * Authors: Joe Todd25! * Email:26! * Web: http://elmerice.elmerfem.org27! *28! *29! *****************************************************************************3031!A routine for getting frontal advance in 3D, given velocity and melt3233SUBROUTINE FrontAdvance3D ( Model, Solver, dt, TransientSimulation )3435USE CalvingGeometry36USE DefUtils37IMPLICIT NONE3839!-----------------------------------------------40TYPE(Model_t) :: Model41TYPE(Solver_t), TARGET :: Solver42REAL(KIND=dp) :: dt43LOGICAL :: TransientSimulation44!-----------------------------------------------45TYPE(Mesh_t), POINTER :: Mesh46TYPE(Nodes_t), TARGET :: FrontNodes, ColumnNodes47TYPE(ValueList_t), POINTER :: Params48TYPE(Variable_t), POINTER :: Var, VeloVar, MeltVar, NormalVar, TangledVar49INTEGER :: i, j, k, m, n, DOFs, TotalNodes, NodesPerLevel, ExtrudedLevels,&50FaceNodeCount, DummyInt, col, hits, ierr, FrontLineCount, county,&51ShiftIdx, ShiftToIdx, NoTangledGroups, PivotIdx, CornerIdx, &52SecondIdx, FirstTangleIdx, LastTangleIdx53INTEGER, POINTER :: Perm(:), FrontPerm(:)=>NULL(), TopPerm(:)=>NULL(), &54FrontNodeNums(:)=>NULL()55INTEGER, ALLOCATABLE :: FNColumns(:), FrontColumnList(:), FrontLocalNodeNumbers(:), &56NodeNumbers(:), TangledGroup(:), TangledPivotIdx(:), UpdatedDirection(:)57REAL(KIND=dp) :: FrontOrientation(3),RotationMatrix(3,3),&58NodeVelo(3), NodeMelt(3), NodeNormal(3), ColumnNormal(3), CornerDirection(2),&59MeltRate, Displace(3), NodeHolder(3), DangerGrad, ShiftTo, direction, &60ShiftDist, y_coord(2), epsShift, ShiftToY, LongRangeLimit, MaxDisplacement, LimitZ, &61p1(2),p2(2),q1(2),q2(2),intersect(2), LeftY, RightY, EpsTangle,thisEps,Shift, thisY62REAL(KIND=dp), POINTER :: Advance(:)63REAL(KIND=dp), ALLOCATABLE :: Rot_y_coords(:,:), Rot_z_coords(:,:), ColumnNormals(:,:), &64TangledShiftTo(:)65#ifdef ELMER_BROKEN_MPI_IN_PLACE66REAL(KIND=dp) :: buffer67#endif68LOGICAL :: Found, Debug, Parallel, Boss, ShiftLeft, LeftToRight, MovedOne, ShiftSecond, &69Protrusion, SqueezeLeft, SqueezeRight, FirstTime=.TRUE., intersect_flag, FrontMelting, &70IgnoreVelo71LOGICAL, ALLOCATABLE :: DangerZone(:), WorkLogical(:), UpdatedColumn(:),&72Tangled(:), DontMove(:)73CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, VeloVarName, MeltVarName, &74NormalVarName, FrontMaskName, TopMaskName, TangledVarName7576SAVE :: FirstTime7778!-----------------------------------------------79! Initialisation80!-----------------------------------------------8182Debug = .FALSE.83Parallel = (ParEnv % PEs > 1)84Boss = ParEnv % MyPE == 08586SolverName = "FrontAdvance3D"87Params => Solver % Values88Mesh => Solver % Mesh8990!The main solver var contains the magnitude of front advance91Var => Solver % Variable92Advance => Var % Values93Perm => Var % Perm94DOFs = Var % DOFs95IF(Var % DOFs /= 3) CALL Fatal(SolverName, "Variable should have 3 DOFs...")9697IgnoreVelo = ListGetLogical(Params, "Ignore Velocity", Found)98IF(.NOT. Found) THEN99IgnoreVelo = .FALSE.100ELSE101CALL Info(SolverName, "Ignoring velocity (melt undercutting only)")102END IF103104IF(.NOT. IgnoreVelo) THEN105!Get the flow solution106VeloVarName = ListGetString(Params, "Flow Solution Variable Name", Found)107IF(.NOT. Found) THEN108CALL Info(SolverName, "Flow Solution Variable Name not found, assuming 'Flow Solution'")109VeloVarName = "Flow Solution"110END IF111VeloVar => VariableGet(Mesh % Variables, VeloVarName, .TRUE., UnfoundFatal=.TRUE.)112END IF113114!Get melt rate115MeltVarName = ListGetString(Params, "Melt Variable Name", Found)116IF(.NOT. Found) THEN117CALL Info(SolverName, "Melt Variable Name not found, assuming no frontal melting")118FrontMelting = .FALSE.119ELSE120FrontMelting = .TRUE.121MeltVar => VariableGet(Mesh % Variables, MeltVarName, .TRUE., UnfoundFatal=.TRUE.)122END IF123124TangledVarName = "Tangled"125TangledVar => VariableGet(Mesh % Variables, TangledVarName, .TRUE., UnfoundFatal=.TRUE.)126TangledVar % Values = 0.0_dp127128!Get front normal vector129NormalVarName = ListGetString(Params, "Normal Vector Variable Name", UnfoundFatal=.TRUE.)130NormalVar => VariableGet(Mesh % Variables, NormalVarName, .TRUE., UnfoundFatal=.TRUE.)131132!Get the orientation of the calving front, compute rotation matrix133!TODO: generalize and link134FrontOrientation = GetFrontOrientation(Model)135RotationMatrix = ComputeRotationMatrix(FrontOrientation)136137DangerGrad = ListGetConstReal(Params, "Front Gradient Threshold", Found)138IF(.NOT.Found) THEN139CALL Info(SolverName, "'Front Gradient Threshold' not found, setting to 5.0")140DangerGrad = 5.0141END IF142143!When correcting for unprojectability, to prevent interp problems,144!ensure that it is SLIGHTLY beyond beyond parallel, by adding this145!value (metres) to the displacement146epsShift = ListGetConstReal(Params, "Front Projectability Epsilon", Found)147IF(.NOT.Found) THEN148CALL Info(SolverName, "'Front Projectability Epsilon' not found, setting to 1.0")149epsShift = 1.0150END IF151152epsTangle = ListGetConstReal(Params, "Front Tangle Epsilon", Found)153IF(.NOT.Found) THEN154CALL Info(SolverName, "'Front Tangle Epsilon' not found, setting to 1.0")155epsTangle = 1.0156END IF157158LongRangeLimit = ListGetConstReal(Params, "Column Max Longitudinal Range", Found)159IF(.NOT.Found) THEN160CALL Info(SolverName, "'Column Max Longitudinal Range' not found, setting to 300.0")161LongRangeLimit = 300.0_dp162END IF163164MaxDisplacement = ListGetConstReal(Params, "Maximum Node Displacement", Found)165IF(.NOT.Found) THEN166CALL Info(SolverName, "'Maximum Node Displacement' not found, setting to 1.0E4.")167MaxDisplacement = 1.0E4_dp168END IF169170!-------------------------------------------171! Find FNColumns172CALL MPI_AllReduce(MAXVAL(Mesh % ParallelInfo % GlobalDOFs), TotalNodes, &1731, MPI_INTEGER, MPI_MAX, ELMER_COMM_WORLD,ierr)174175ExtrudedLevels = ListGetInteger(CurrentModel % Simulation,'Extruded Mesh Levels',Found)176IF(.NOT. Found) ExtrudedLevels = &177ListGetInteger(CurrentModel % Simulation,'Remesh Extruded Mesh Levels',Found)178IF(.NOT. Found) CALL Fatal(SolverName,&179"Unable to find 'Extruded Mesh Levels' or 'Remesh Extruded Mesh Levels'")180NodesPerLevel = TotalNodes / ExtrudedLevels181182ALLOCATE(FNColumns(Mesh % NumberOfNodes))183FNColumns = MOD(Mesh % ParallelInfo % GlobalDOFs, NodesPerLevel)184185!Get the front line186FrontMaskName = "Calving Front Mask"187TopMaskName = "Top Surface Mask"188189CALL MakePermUsingMask( Model, Solver, Mesh, TopMaskName, &190.FALSE., TopPerm, dummyint)191192CALL MakePermUsingMask( Model, Solver, Mesh, FrontMaskName, &193.FALSE., FrontPerm, FaceNodeCount)194195CALL GetDomainEdge(Model, Mesh, TopPerm, FrontNodes, &196FrontNodeNums, Parallel, FrontMaskName, Simplify=.FALSE.)197198!Pass FrontNodeNums to all CPUs199IF(Boss) FrontLineCount = SIZE(FrontNodeNums)200CALL MPI_BCAST(FrontLineCount , 1, MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)201202!Determine whether front columns are arranged203!left to right, and reorder if not204IF(Boss) THEN205206NodeHolder(1) = FrontNodes % x(1)207NodeHolder(2) = FrontNodes % y(1)208NodeHolder(3) = FrontNodes % z(1)209NodeHolder = MATMUL(RotationMatrix, NodeHolder)210y_coord(1) = NodeHolder(2)211212NodeHolder(1) = FrontNodes % x(FrontLineCount)213NodeHolder(2) = FrontNodes % y(FrontLineCount)214NodeHolder(3) = FrontNodes % z(FrontLineCount)215NodeHolder = MATMUL(RotationMatrix, NodeHolder)216y_coord(2) = NodeHolder(2)217218LeftToRight = y_coord(2) > y_coord(1)219220IF(.NOT. LeftToRight) THEN221IF(Debug) PRINT *,'Debug, switching to LeftToRight'222FrontNodeNums = FrontNodeNums(FrontLineCount:1:-1)223FrontNodes % x = FrontNodes % x(FrontLineCount:1:-1)224FrontNodes % y = FrontNodes % y(FrontLineCount:1:-1)225FrontNodes % z = FrontNodes % z(FrontLineCount:1:-1)226END IF227END IF228229IF(.NOT. Boss) ALLOCATE(FrontNodeNums(FrontLineCount))230CALL MPI_BCAST(FrontNodeNums , FrontLineCount, MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)231232!FrontNodeNums is the GlobalDOFs of the nodes on the front233!So this can be easily converted into a FNColumn like list234ALLOCATE(FrontColumnList(FrontLineCount), &235FrontLocalNodeNumbers(FrontLineCount), &236DangerZone(FrontLineCount), &237ColumnNormals(FrontLineCount,3))238239FrontColumnList = MOD(FrontNodeNums, NodesPerLevel)240FrontLocalNodeNumbers = -1241DangerZone = .FALSE.242ColumnNormals = 0.0_dp243244DO i=1,FrontLineCount245n = FrontNodeNums(i)246DO j=1,Mesh % NumberOfNodes247IF(Mesh % ParallelInfo % GlobalDOFs(j) == n) THEN248FrontLocalNodeNumbers(i) = j249END IF250END DO251END DO252253!--------------------------------------254! Action: Compute lagrangian displacement for all nodes255! This is the main function of the Solver.256! Everything following this DO loop is taking care of257! unprojectability/high gradient etc258!--------------------------------------259260DO i=1,Mesh % NumberOfNodes261IF(Perm(i) <= 0) CYCLE262263IF(FrontMelting) THEN264IF(MeltVar % Perm(i) <= 0) &265CALL Fatal(SolverName, "Permutation error on front node!")266267268!Scalar melt value from Plume solver269MeltRate = MeltVar % Values(MeltVar % Perm(i))270271NodeNormal(1) = NormalVar % Values(((NormalVar % Perm(i)-1)*NormalVar % DOFs) + 1)272NodeNormal(2) = NormalVar % Values(((NormalVar % Perm(i)-1)*NormalVar % DOFs) + 2)273NodeNormal(3) = NormalVar % Values(((NormalVar % Perm(i)-1)*NormalVar % DOFs) + 3)274275NodeMelt = NodeNormal * MeltRate276ELSE277NodeMelt = 0.0_dp278END IF279280IF(IgnoreVelo) THEN281NodeVelo = 0.0282ELSE283!Compute front normal component of velocity284NodeVelo(1) = VeloVar % Values(((VeloVar % Perm(i)-1)*VeloVar % DOFs) + 1)285NodeVelo(2) = VeloVar % Values(((VeloVar % Perm(i)-1)*VeloVar % DOFs) + 2)286NodeVelo(3) = VeloVar % Values(((VeloVar % Perm(i)-1)*VeloVar % DOFs) + 3)287END IF288289Displace = 0.0290291Displace(1) = NodeVelo(1) - NodeMelt(1)292Displace(2) = NodeVelo(2) - NodeMelt(2)293Displace(3) = NodeVelo(3) - NodeMelt(3)294295Displace = Displace * dt296297IF(MAXVAL(Displace) > MaxDisplacement) THEN298WRITE(Message,'(A,i0,A)') "Maximum allowable front displacement exceeded for node ",i,". Limiting..."299CALL Warn(SolverName, Message)300Displace = Displace * (MaxDisplacement/MAXVAL(Displace))301END IF302303Advance((Perm(i)-1)*DOFs + 1) = Displace(1)304Advance((Perm(i)-1)*DOFs + 2) = Displace(2)305Advance((Perm(i)-1)*DOFs + 3) = Displace(3)306307!First and last (i.e. lateral margin) columns don't move308IF((FNColumns(i) == FrontColumnList(1)) .OR. &309(FNColumns(i) == FrontColumnList(FrontLineCount))) THEN310Advance((Perm(i)-1)*DOFs + 1) = 0.0_dp311Advance((Perm(i)-1)*DOFs + 2) = 0.0_dp312Advance((Perm(i)-1)*DOFs + 3) = 0.0_dp313END IF314END DO315316317!----------------------------------------------------------318! Now we need to look for and limit regions on the319! where high gradient + 'straightness' makes320! remeshing into vertical columns troublesome.321!----------------------------------------------------------322!323! Five things:324! - Limit horizontal range (otherwise, Remesh will smear a column of nodes325! all over the place)326! - Limit undercut range (longitudinal range) to prevent mesh degeneracy327! - Prevent retreat at node near lateral margins328! - Check for 'tangled' regions, where fixing unprojectability one way329! causes unprojectability the other way.330! - Prevent Unprojectability - requires knowledge of neighbouring columns331!----------------------------------------------------------332333!-------------------------------------------334! Cycle columns, looking at average normal for the column335!-------------------------------------------336! Normal vector is limited by projectability, so if average is high,337! all are high338!339! Note that we are cycling the parallel gathered line, so each part340! will only have a few columns (if any)341! So, mark troublesome columns where present, then communicate342!-------------------------------------------343344DO i=1,FrontLineCount345col = FrontColumnList(i)346347hits = COUNT(FNColumns == col)348IF(hits == 0) CYCLE349350ColumnNormal = 0.0_dp351352!Gather normal vector353DO j=1,Mesh % NumberOfNodes354IF(FNColumns(j) == col) THEN355ColumnNormal(1) = ColumnNormal(1) + &356NormalVar % Values(((NormalVar % Perm(j)-1)*NormalVar % DOFs) + 1)357ColumnNormal(2) = ColumnNormal(2) + &358NormalVar % Values(((NormalVar % Perm(j)-1)*NormalVar % DOFs) + 2)359ColumnNormal(3) = ColumnNormal(3) + &360NormalVar % Values(((NormalVar % Perm(j)-1)*NormalVar % DOFs) + 3)361END IF362END DO363364!unit vector365ColumnNormal = ColumnNormal / hits366!Convert to front coordinate system367ColumnNormal = MATMUL( RotationMatrix, ColumnNormal )368!Save for later369ColumnNormals(i,1:3) = ColumnNormal(1:3)370371!We're concerned with the lateral (rather than vertical) component372IF( ABS(ColumnNormal(2) / ColumnNormal(3)) > DangerGrad ) THEN373DangerZone(i) = .TRUE.374END IF375END DO376377!Communicate between partitions378!This is an 'OR' condition insofar as only one partition379!will have actually computed the gradient380IF(Parallel) THEN381DO i=1,FrontLineCount382CALL SParIterAllReduceOR(DangerZone(i))383END DO384END IF385386!Mark before and after dangerzone too387ALLOCATE(WorkLogical(FrontLineCount))388WorkLogical = .FALSE.389390DO i=1,FrontLineCount391IF(DangerZone(i)) WorkLogical(i) = .TRUE.392393IF(i > 1) THEN394IF(DangerZone(i-1)) WorkLogical(i) = .TRUE.395END IF396397IF(i < FrontLineCount) THEN398IF(DangerZone(i+1)) WorkLogical(i) = .TRUE.399END IF400END DO401402DangerZone = WorkLogical403DEALLOCATE(WorkLogical)404405!Find and store leftmost and rightmost (rotated) coordinate of406!each column for checking projectability later407!Rot_y_coords(:,1) is leftmost, and (:,2) is right most y coord of408!each column's nodes, and Rot_z_coords(:,:) is the min(1)/max(2) z409ALLOCATE(Rot_y_coords(FrontLineCount,2),&410Rot_z_coords(FrontLineCount,2))411Rot_y_coords(:,1) = HUGE(0.0_dp)412Rot_y_coords(:,2) = -HUGE(0.0_dp)413Rot_z_coords(:,1) = HUGE(0.0_dp)414Rot_z_coords(:,2) = -HUGE(0.0_dp)415416DO i=1,FrontLineCount417col = FrontColumnList(i)418IF(COUNT(FNColumns == col) == 0) CYCLE419420DO j=1,Mesh % NumberOfNodes421IF(FNColumns(j) /= col) CYCLE422423NodeHolder(1) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)424NodeHolder(2) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)425NodeHolder(3) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)426NodeHolder = MATMUL(RotationMatrix, NodeHolder)427428Rot_y_coords(i,1) = MIN(Rot_y_coords(i,1), NodeHolder(2))429Rot_y_coords(i,2) = MAX(Rot_y_coords(i,2), NodeHolder(2))430431Rot_z_coords(i,1) = MIN(Rot_z_coords(i,1), NodeHolder(3))432Rot_z_coords(i,2) = MAX(Rot_z_coords(i,2), NodeHolder(3))433END DO434END DO435436!-----------------------------------437! Limit lateral range (to 0) in DangerZone438!-----------------------------------439! The physical interpretation of this is that, when melt undercutting occurs440! in a region of high gradient, the unmelted parts of that column also shift441! with the melt.442! This is a minor limitation, but better than Free Surface Equation!443444DO i=1,FrontLineCount445IF(.NOT. DangerZone(i)) CYCLE446447col = FrontColumnList(i)448hits = COUNT(FNColumns == col)449450IF(hits == 0) CYCLE451452ALLOCATE(NodeNumbers(hits), &453ColumnNodes % x(hits),&454ColumnNodes % y(hits),&455ColumnNodes % z(hits))456457ColumnNodes % NumberOfNodes = hits458459!Gather nodenumbers in column460county = 0461DO j=1,Mesh % NumberOfNodes462IF(FNColumns(j) /= col) CYCLE463county = county + 1464465NodeNumbers(county) = j466ColumnNodes % x(county) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)467ColumnNodes % y(county) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)468ColumnNodes % z(county) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)469END DO470471!direction - which way is this part of the front pointing?472ShiftLeft = ColumnNormals(i,2) > 0473IF(ShiftLeft) THEN474ShiftTo = HUGE(ShiftTo)475ELSE476ShiftTo = -HUGE(ShiftTo)477END IF478479!Rotate points and find furthest left (or right)480DO j=1,ColumnNodes % NumberOfNodes481NodeHolder(1) = ColumnNodes % x(j)482NodeHolder(2) = ColumnNodes % y(j)483NodeHolder(3) = ColumnNodes % z(j)484NodeHolder = MATMUL(RotationMatrix, NodeHolder)485486IF(ShiftLeft) THEN487IF(NodeHolder(2) < ShiftTo) ShiftTo = NodeHolder(2)488ELSE489IF(NodeHolder(2) > ShiftTo) ShiftTo = NodeHolder(2)490END IF491END DO492493!Now, for each node in column, compute the displacement (in rotated y coordinate)494DO j=1,ColumnNodes % NumberOfNodes495NodeHolder(1) = ColumnNodes % x(j)496NodeHolder(2) = ColumnNodes % y(j)497NodeHolder(3) = ColumnNodes % z(j)498NodeHolder = MATMUL(RotationMatrix, NodeHolder)499500ShiftDist = ShiftTo - NodeHolder(2)501502Displace = 0.0_dp503Displace(2) = ShiftDist504505Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)506507!Adjust the variable values to shift nodes in line.508Advance((Perm(NodeNumbers(j))-1)*DOFs + 1) = &509Advance((Perm(NodeNumbers(j))-1)*DOFs + 1) + Displace(1)510Advance((Perm(NodeNumbers(j))-1)*DOFs + 2) = &511Advance((Perm(NodeNumbers(j))-1)*DOFs + 2) + Displace(2)512513IF(Debug) PRINT *,'node: ',NodeNumbers(j),' shifting: ',ShiftDist,' to ',ShiftTo,' point: ', &514ColumnNodes % x(j),ColumnNodes % y(j),ColumnNodes % z(j)515END DO516517DEALLOCATE(NodeNumbers, &518ColumnNodes % x, &519ColumnNodes % y, &520ColumnNodes % z)521END DO522523!-----------------------------------524! Limit longitudinal range everywhere525!-----------------------------------526! Two options for how to do this:527! Drag nodes back with melt, or effectively528! limit melt (keeping nodes forward)529! Opt for the latter, or else we're530! forcing melting to have an effect531!-----------------------------------532DO i=1,FrontLineCount533534col = FrontColumnList(i)535hits = COUNT(FNColumns == col)536537IF(hits == 0) CYCLE538539IF((Rot_z_coords(i,2) - Rot_z_coords(i,1)) > LongRangeLimit) THEN540541LimitZ = Rot_z_coords(i,2) - LongRangeLimit542Rot_z_coords(i,1) = LimitZ543544DO j=1,Mesh % NumberOfNodes545IF(FNColumns(j) /= col) CYCLE546547NodeHolder(1) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)548NodeHolder(2) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)549NodeHolder(3) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)550NodeHolder = MATMUL(RotationMatrix, NodeHolder)551552IF(NodeHolder(3) >= LimitZ) CYCLE553554Displace = 0.0_dp555Displace(3) = LimitZ - NodeHolder(3)556557Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)558559!Adjust the variable values to shift nodes in line.560Advance((Perm(j)-1)*DOFs + 1) = &561Advance((Perm(j)-1)*DOFs + 1) + Displace(1)562Advance((Perm(j)-1)*DOFs + 2) = &563Advance((Perm(j)-1)*DOFs + 2) + Displace(2)564IF(Debug) PRINT *,ParEnv % MyPE,' Debug, limiting node range: col ',&565i,' node ',j,' limit: ',LimitZ,' displace: ',Displace566END DO567568END IF569END DO570571572!-----------------------------------573! Now check projectability574!-----------------------------------575! If an unprojectable portion of the front is found,576! the inland nodes remain in place, while those further577! advanced shift sideways to maintain projectability578!579! Typical case is melt plume at end of recently calved580! straight edge.581!582! requires MPI comms583584!Gather rotated y coord of all columns585DO i=1,FrontLineCount586#ifdef ELMER_BROKEN_MPI_IN_PLACE587buffer = Rot_y_coords(i,1)588CALL MPI_AllReduce(buffer, &589#else590CALL MPI_AllReduce(MPI_IN_PLACE, &591#endif592Rot_y_coords(i,1), 1, MPI_DOUBLE_PRECISION, MPI_MIN, &593ELMER_COMM_WORLD, ierr)594#ifdef ELMER_BROKEN_MPI_IN_PLACE595buffer = Rot_y_coords(i,2)596CALL MPI_AllReduce(buffer, &597#else598CALL MPI_AllReduce(MPI_IN_PLACE, &599#endif600Rot_y_coords(i,2), 1, MPI_DOUBLE_PRECISION, MPI_MAX, &601ELMER_COMM_WORLD, ierr)602603#ifdef ELMER_BROKEN_MPI_IN_PLACE604buffer = Rot_z_coords(i,1)605CALL MPI_AllReduce(buffer, &606#else607CALL MPI_AllReduce(MPI_IN_PLACE, &608#endif609Rot_z_coords(i,1), 1, MPI_DOUBLE_PRECISION, MPI_MIN, &610ELMER_COMM_WORLD, ierr)611#ifdef ELMER_BROKEN_MPI_IN_PLACE612buffer = Rot_z_coords(i,2)613CALL MPI_AllReduce(buffer, &614#else615CALL MPI_AllReduce(MPI_IN_PLACE, &616#endif617Rot_z_coords(i,2), 1, MPI_DOUBLE_PRECISION, MPI_MAX, &618ELMER_COMM_WORLD,ierr)619620IF(Boss .AND. Debug) PRINT *,'Debug, rot_y_coords: ',i,rot_y_coords(i,:)621IF(Boss .AND. Debug) PRINT *,'Debug, rot_z_coords: ',i,rot_z_coords(i,:)622END DO623624ALLOCATE(UpdatedColumn(FrontLineCount), &625UpdatedDirection(FrontLineCount), &626Tangled(FrontLineCount),&627TangledGroup(FrontLineCount),&628TangledPivotIdx(FrontLineCount),&629TangledShiftTo(FrontLineCount),&630DontMove(FrontLineCount))631632UpdatedColumn = .FALSE.633UpdatedDirection = 0634Tangled = .FALSE.635TangledGroup = 0636TangledPivotIdx = 0637TangledShiftTo = 0.0_dp638DontMove = .FALSE.639640DontMove(1) = .TRUE.641DontMove(FrontLineCount) = .TRUE.642643!TODO: Deal with DontMove and tangling properly...644! Ensure that comms is not necessary for DontMove645646!Test for thin sliver at lateral margins - potential to get647!stuck like this, so impose an angle here648!We require that the 2nd node from the end not retreat beyond649!a 45 degree angle inland. PYTHAGORAS650!Shift the 2nd column in the lateral direction only,651!to the point where it makes a 45 degree angle w/652!the corner.653! *654! / |655! / |656! * <- *657658DO i=1,2659660IF(i==1) THEN661CornerIdx = 1662SecondIdx = 2663direction = 1.0_dp664ELSE665CornerIdx = FrontLineCount666SecondIdx = FrontLineCount-1667direction = -1.0_dp668END IF669670!Conveniently, first time round we want the left most coord (Rot_y_coords(:,1))671!whereas second time we want the right (Rot_y_cords(:,2))672CornerDirection(1) = Rot_y_coords(SecondIdx,i) - Rot_y_coords(CornerIdx,1)673CornerDirection(2) = Rot_z_coords(SecondIdx,i) - Rot_z_coords(CornerIdx,1)674!Unit vector675CornerDirection = CornerDirection / (SUM(CornerDirection ** 2.0)**0.5)676677IF(Debug) PRINT *,ParEnv % MyPE, 'CornerDirection ',i,': ',CornerDirection678679IF( CornerDirection(2) < (-1.0_dp / (2.0_dp ** 0.5)) ) THEN680!Shift 2nd node and mark DontMove681682ShiftToY = Rot_y_coords(CornerIdx,1) + &683direction * ((Rot_z_coords(CornerIdx,1) - Rot_z_coords(SecondIdx,1)))684685Rot_y_coords(SecondIdx,1) = ShiftToY686Rot_y_coords(SecondIdx,2) = ShiftToY687688DontMove(SecondIdx) = .TRUE.689IF(Debug) PRINT *,ParEnv % MyPE,' debug, side ',i,' of front is too inwards'690691DO k=1,Mesh % NumberOfNodes692IF(FNColumns(k) /= FrontColumnList(SecondIdx)) CYCLE693694NodeHolder(1) = Mesh % Nodes % x(k) + Advance((Perm(k)-1)*DOFs + 1)695NodeHolder(2) = Mesh % Nodes % y(k) + Advance((Perm(k)-1)*DOFs + 2)696NodeHolder(3) = Mesh % Nodes % z(k) + Advance((Perm(k)-1)*DOFs + 3)697698NodeHolder = MATMUL(RotationMatrix, NodeHolder)699700Displace = 0.0_dp701Displace(2) = ShiftToY - NodeHolder(2)702703Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)704705IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shifting next to corner node ',k,&706NodeHolder,' by ',Displace707708!Adjust the variable values to shift nodes in line.709Advance((Perm(k)-1)*DOFs + 1) = Advance((Perm(k)-1)*DOFs + 1) + Displace(1)710Advance((Perm(k)-1)*DOFs + 2) = Advance((Perm(k)-1)*DOFs + 2) + Displace(2)711END DO712713END IF714END DO715716!-----------------------------------------------717! Cycle the line, looking for unprojectable718! (but not tangled) regions.719! e.g. plume melting behind a vertical portion720!-----------------------------------------------721722!In case shifting nodes affects another partition, we loop so long as723!at least one partition has MovedOne724MovedOne = .TRUE.725county = 0726DO WHILE(MovedOne)727county = county + 1728IF(county > 100) CALL Fatal(SolverName, "Infinite loop!")729IF(Debug) PRINT *,ParEnv % MyPE, 'Debug, iterating projectability ', county730731MovedOne = .FALSE.732UpdatedColumn = .FALSE.733734DO i=2,FrontLineCount735736!If already detangled, skip737!IF(Tangled(i)) CYCLE738739!epsShift here740IF((Rot_y_coords(i,1) - Rot_y_coords(i-1,2)) < 0.9*epsShift) THEN741742IF(Debug) PRINT *, 'Debug diff ',i,i-1, &743(Rot_y_coords(i,1) - Rot_y_coords(i-1,2)), epsShift744745IF(DontMove(i) .AND. DontMove(i-1)) THEN746CALL Warn(SolverName,&747"Both nodes are marked Dont Move, stopping shifting.")748EXIT749END IF750!Work out which to shift751!If one is marked DontMove (corner node, or 2nd inland and weird shape)752! then move the other753!Otherwise, shift whichever is further forward754IF(DontMove(i)) THEN755ShiftSecond = .FALSE.756DontMove(i-1) = .TRUE.757IF(Debug) PRINT *,ParEnv % MyPE,'Debug, do not move second: ',i758ELSE IF(DontMove(i-1)) THEN759ShiftSecond = .TRUE.760DontMove(i) = .TRUE.761IF(Debug) PRINT *,ParEnv % MyPE,'Debug, do not move first: ',i-1762ELSE763IF(SUM(Rot_z_coords(i,:)) > SUM(Rot_z_coords(i-1,:))) THEN764ShiftSecond = .TRUE.765ELSE766ShiftSecond = .FALSE.767END IF768END IF769770IF(ShiftSecond) THEN771ShiftIdx = i772ShiftToIdx = i-1773ELSE774ShiftIdx = i-1775ShiftToIdx = i776END IF777778!Calculate ShiftTo, depends on ShiftSecond779!Also update shifted Rot_y_coords780IF(ShiftSecond) THEN781!If this node has already been moved the other way, leave it alone782IF((UpdatedDirection(ShiftIdx) < 0) .AND. .NOT. DontMove(ShiftToIdx)) CYCLE783784ShiftTo = Rot_y_coords(i-1,2) + epsShift785IF(Rot_y_coords(i,2) < ShiftTo) UpdatedDirection(ShiftIdx) = 1786787Rot_y_coords(i,1) = MAX(Rot_y_coords(i,1), ShiftTo)788Rot_y_coords(i,2) = MAX(Rot_y_coords(i,2), ShiftTo)789ELSE790!If this node has already been moved the other way, leave it alone791IF((UpdatedDirection(ShiftIdx) > 0) .AND. .NOT. DontMove(ShiftToIdx)) CYCLE792793ShiftTo = Rot_y_coords(i,1) - epsShift794IF(Rot_y_coords(i,1) > ShiftTo) UpdatedDirection(ShiftIdx) = -1795796Rot_y_coords(i-1,1) = MIN(Rot_y_coords(i-1,1), ShiftTo)797Rot_y_coords(i-1,2) = MIN(Rot_y_coords(i-1,2), ShiftTo)798END IF799800IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, updating col ',ShiftIdx,&801' rot_y_coords: ',Rot_y_coords(ShiftIdx,:)802803UpdatedColumn(ShiftIdx) = .TRUE.804805!Shift all the nodes in this column806DO j=1,Mesh % NumberOfNodes807IF(FNColumns(j) /= FrontColumnList(ShiftIdx)) CYCLE808809NodeHolder(1) = Mesh % Nodes % x(j) + Advance((Perm(j)-1)*DOFs + 1)810NodeHolder(2) = Mesh % Nodes % y(j) + Advance((Perm(j)-1)*DOFs + 2)811NodeHolder(3) = Mesh % Nodes % z(j) + Advance((Perm(j)-1)*DOFs + 3)812NodeHolder = MATMUL(RotationMatrix, NodeHolder)813814Displace = 0.0_dp815Displace(2) = ShiftTo - NodeHolder(2)816817!Check if node already projectable818IF(ShiftSecond) THEN819IF(Displace(2) < 0 ) CYCLE820ELSE821IF(Displace(2) > 0) CYCLE822END IF823824Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)825826IF(Debug) PRINT *,ParEnv % MyPE, 'Debug, shifting node ',j,' col ',ShiftIdx,'xyz: ',&827Mesh % Nodes % x(j)+Advance((Perm(j)-1)*DOFs + 1),&828Mesh % Nodes % y(j)+Advance((Perm(j)-1)*DOFs + 2),&829Mesh % Nodes % z(j)+Advance((Perm(j)-1)*DOFs + 3),&830' by ',Displace,' to ensure projectability. 1'831832833!Adjust the variable values to shift nodes in line.834Advance((Perm(j)-1)*DOFs + 1) = Advance((Perm(j)-1)*DOFs + 1) + Displace(1)835Advance((Perm(j)-1)*DOFs + 2) = Advance((Perm(j)-1)*DOFs + 2) + Displace(2)836837END DO838END IF839END DO840841IF(ANY(UpdatedColumn)) MovedOne = .TRUE.842843IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, MovedOne: ',MovedOne844IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, UpdatedColumn: ',UpdatedColumn845846END DO847848849!-----------------------------------------------850! Check for pinnacles or rifts which can't be made851! projectable. Set the columns in a line and mark them852! for removal by Remesh.F90853!-----------------------------------------------854!TODO: At least 3 separate issues here:855!856! *This one should be fixed now, at the end we check for groups disappearing*857! 1 : If there's a tangled group, then another, superset tangled858! group, this causes "programming error, tangled group has zero nodes"859! potential solution here is to just remove this error message, because860! its not really a problem *I DON'T THINK*. However, if so, need to861! check for it, and shift TangledGroups down one, so Remesh behaves862!863! *This one should now be fixed, because we do all the unprojectable shifting first:*864! 2 : Here we iteratively: check tangled, then check unprojectable865! If correcting unprojectability causes further tangling, this866! isn't caught. Potential solutions:867! Improve parallel unprojectability checker (dirty fix, won't catch every poss case)868! Better: recheck previously tangled regions869!870! 3 : Long straight sides which also happen to tangle with the end of their headlands871! are not well handled. All the nodes along these sides are shifted out the way872! in the new mesh873874875NoTangledGroups = 0876DO i=1,FrontLineCount-2877IF(Tangled(i)) CYCLE878j = i+2879880!If the column two columns away is less than 2*epsShift away, and there881!is a change of direction, problems...882IF((Rot_y_coords(j,1) - Rot_y_coords(i,2)) < 2*epsShift) THEN883884IF(((Rot_z_coords(i,1) < Rot_z_coords(i+1,1)) .NEQV. &885(Rot_z_coords(i+1,1) < Rot_z_coords(j,1))) .OR. &886((Rot_z_coords(i,2) < Rot_z_coords(i+1,2)) .NEQV. &887(Rot_z_coords(i+1,2) < Rot_z_coords(j,2)))) THEN888889!Either a pinnacle or a slit (i.e protrusion or rift)890Protrusion = SUM(Rot_z_coords(i+1,:)) > SUM(Rot_z_coords(i,:))891IF(Debug) PRINT *,'Debug, protrusion: ',Protrusion892893NoTangledGroups = NoTangledGroups + 1894PivotIdx = i+1895896DO k=2,PivotIdx897p1(1) = Rot_y_coords(k,2)898p1(2) = Rot_z_coords(k,2)899900p2(1) = Rot_y_coords(k-1,2)901p2(2) = Rot_z_coords(k-1,2)902903DO m=FrontLineCount-1,PivotIdx,-1904IF(k==m) CYCLE !first two will always intersect by definition, not what we want905q1(1) = Rot_y_coords(m,1)906q1(2) = Rot_z_coords(m,1)907908q2(1) = Rot_y_coords(m+1,1)909q2(2) = Rot_z_coords(m+1,1)910911CALL LineSegmentsIntersect ( p1, p2, q1, q2, intersect, intersect_flag )912913IF(intersect_flag) EXIT914END DO915IF(intersect_flag) EXIT916END DO917918IF(intersect_flag) THEN919920!Found an intersection point (intersect)921PRINT *,'Debug, found tangle intersection ',intersect,' leaving last tangled nodes: ',k,m922923Tangled(k:m) = .TRUE.924TangledPivotIdx(k:m) = PivotIdx925TangledShiftTo(k:m) = intersect(1)926927IF(Protrusion) THEN928TangledGroup(k:m) = NoTangledGroups929ELSE930TangledGroup(k:m) = -NoTangledGroups931END IF932933ELSE934PRINT *,'Debug, found no intersection, so nodes are not QUITE tangled: ',i,j935936Tangled(i:j) = .TRUE.937TangledPivotIdx(i:j) = PivotIdx938TangledShiftTo(i:j) = (SUM(rot_y_coords(i,:)) + SUM(rot_y_coords(j,:))) / 4.0_dp939940IF(Protrusion) THEN941TangledGroup(i:j) = NoTangledGroups942ELSE943TangledGroup(i:j) = -NoTangledGroups944END IF945946END IF947END IF948END IF949END DO950951!Check for a tangled group 'swallowing' another952county = 0953DO i=1,NoTangledGroups954IF(COUNT(TangledGroup == i) > 0) CYCLE955county = county + 1956!Shift group numbers down one957DO j=1,SIZE(TangledGroup)958IF(TangledGroup(j) > i) TangledGroup(j) = TangledGroup(j) - 1959END DO960END DO961NoTangledGroups = NoTangledGroups - county962963!Strategy:964! Shift all nodes to near (offset) the y-coordinate965! of the tangle pivot (i+1, above)966DO i=1,NoTangledGroups967n = COUNT(TangledGroup == i)968IF(n==0) THEN969n = COUNT(TangledGroup == -i)970Protrusion = .FALSE.971ELSE972Protrusion = .TRUE.973END IF974IF(n==0) CALL Fatal(SolverName, "Programming error: tangled group has 0 nodes?")975976!Get the pivot node index, ShiftToY, and tangled node range977FirstTangleIdx = 0978LastTangleIdx = 0979DO j=1,FrontLineCount980IF(TangledGroup(j) == i .OR. TangledGroup(j) == -i) THEN981IF(FirstTangleIdx == 0) FirstTangleIdx = j982LastTangleIdx = j983PivotIdx = TangledPivotIdx(j)984ShiftToY = TangledShiftTo(j)985!EXIT986END IF987END DO988989IF(LastTangleIdx - FirstTangleIdx /= n-1) CALL Fatal(SolverName, &990"Programming error: wrong number of nodes in TangledGroup")991IF(Debug) PRINT *,'Debug, tangled pivot index, ShiftToY: ', PivotIdx, ShiftToY992993LeftY = ShiftToY - (epsTangle * ((n-1) / 2.0_dp))994RightY = ShiftToY + (epsTangle * ((n-1) / 2.0_dp))995996!Potentially 'squeezed' on either side by neighbour columns997SqueezeLeft = .FALSE.998SqueezeRight = .FALSE.9991000IF(LeftY < Rot_y_coords(FirstTangleIdx-1,2) + epsTangle) THEN1001SqueezeLeft = .TRUE.1002IF( ( (Rot_y_coords(LastTangleIdx+1,1) - epsTangle) - (epsTangle * (n-1)) ) < &1003(Rot_y_coords(FirstTangleIdx-1,2) + epsTangle)) THEN1004SqueezeRight = .TRUE.1005END IF1006END IF1007IF(RightY > (Rot_y_coords(LastTangleIdx+1,1) - epsTangle)) THEN1008SqueezeRight = .TRUE.1009IF( ( (Rot_y_coords(LastTangleIdx+1,1) - epsTangle) - (epsTangle * (n-1)) ) < &1010(Rot_y_coords(FirstTangleIdx-1,2) + epsTangle)) THEN1011SqueezeLeft = .TRUE.1012END IF1013END IF10141015IF(Debug) PRINT *,'Debug, LeftY: ',LeftY,' RightY: ',RightY,' SqueezeL: ',&1016SqueezeLeft,' SqueezeR: ',SqueezeRight,' prev: ',Rot_y_coords(FirstTangleIdx-1,2),&1017' next: ',Rot_y_coords(LastTangleIdx+1,1)10181019!If squeezed, adjust the new y coord of the tangled nodes1020IF(SqueezeLeft .AND. SqueezeRight) THEN1021thisEps = (Rot_y_coords(LastTangleIdx+1,1) - Rot_y_coords(FirstTangleIdx-1,2)) / (n+1)1022LeftY = Rot_y_coords(FirstTangleIdx-1,2) + thisEps1023RightY = Rot_y_coords(LastTangleIdx+1,1) - thisEps1024ShiftToY = (RightY + LeftY) / 2.0_dp !midpoint1025ELSE IF(SqueezeLeft) THEN1026Shift = (Rot_y_coords(FirstTangleIdx-1,2) + epsTangle) - LeftY1027LeftY = LeftY + Shift1028RightY = RightY + Shift1029ShiftToY = ShiftToY + Shift1030ELSE IF(SqueezeRight) THEN1031Shift = (Rot_y_coords(LastTangleIdx+1,1) - epsTangle) - RightY1032LeftY = LeftY + Shift1033RightY = RightY + Shift1034ShiftToY = ShiftToY + Shift1035END IF10361037!Where tangling occurs, we shift the tangled columns to be1038!1m apart in a series. Then Remesh gets rid of them1039DO j=FirstTangleIdx,LastTangleIdx1040IF(.NOT. (TangledGroup(j) == i .OR. TangledGroup(j) == -i)) &1041CALL Fatal(SolverName, "Programming error: node in specified idx range not tangled?")10421043thisY = LeftY + ((REAL(j - FirstTangleIdx)/(n-1)) * (RightY - LeftY))1044IF(Debug) PRINT *,'Debug, thisY: ',thisY, j10451046DO k=1,Mesh % NumberOfNodes1047IF(FNColumns(k) /= FrontColumnList(j)) CYCLE10481049NodeHolder(1) = Mesh % Nodes % x(k) + Advance((Perm(k)-1)*DOFs + 1)1050NodeHolder(2) = Mesh % Nodes % y(k) + Advance((Perm(k)-1)*DOFs + 2)1051NodeHolder(3) = Mesh % Nodes % z(k) + Advance((Perm(k)-1)*DOFs + 3)1052IF(Debug) PRINT *, ParEnv % MyPE, ' Debug, pre shift tangled node ',k,': ',NodeHolder10531054NodeHolder = MATMUL(RotationMatrix, NodeHolder)10551056Displace = 0.0_dp1057Displace(2) = thisY - NodeHolder(2)10581059Displace = MATMUL(TRANSPOSE(RotationMatrix), Displace)10601061IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shifting node ',k, ' col ',j,&1062' by ',Displace,' to detangle.'10631064!Adjust the variable values to shift nodes in line.1065Advance((Perm(k)-1)*DOFs + 1) = Advance((Perm(k)-1)*DOFs + 1) + Displace(1)1066Advance((Perm(k)-1)*DOFs + 2) = Advance((Perm(k)-1)*DOFs + 2) + Displace(2)1067!Set this variable so that Remesh knows1068TangledVar % Values(TangledVar % Perm(k)) = 1.0_dp * ABS(TangledGroup(j))1069END DO1070END DO1071END DO107210731074!---------------------------------------1075!Done, just deallocations10761077FirstTime = .FALSE.10781079DEALLOCATE(FrontPerm, &1080TopPerm, &1081FrontNodeNums, &1082Rot_y_coords, &1083UpdatedColumn, &1084UpdatedDirection, &1085Tangled, &1086TangledGroup, &1087TangledPivotIdx, &1088TangledShiftTo, &1089FrontColumnList, &1090FrontLocalNodeNumbers)10911092END SUBROUTINE FrontAdvance3D109310941095