Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Solvers/Calving.F90
3204 views
1
!*****************************************************************************/
2
! *
3
! * Elmer, A Finite Element Software for Multiphysical Problems
4
! *
5
! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland
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
! *
26
! * Solver to predict calving events in 2D based on crevasse depth calving
27
! * criterion (Benn et al. 2007). Description of algorithm in
28
! * Todd & Christoffersen (2014) [doi:10.5194/tc-8-2353-2014]
29
! *
30
! * Relies on TwoMeshes.F90 for mesh migration and interpolation
31
! * following calving, and FrontDisplacement.F90 for mesh update computation
32
! *
33
! ******************************************************************************
34
! *
35
! * Authors: Joe Todd
36
! * Email: [email protected]
37
! * Web: http://www.csc.fi/elmer
38
! * Address: CSC - IT Center for Science Ltd.
39
! * Keilaranta 14
40
! * 02101 Espoo, Finland
41
! *
42
! * Original Date: 10.11.2014
43
! *
44
! ****************************************************************************/
45
SUBROUTINE Find_Calving (Model, Solver, dt, TransientSimulation )
46
47
USE types
48
USE CoordinateSystems
49
USE SolverUtils
50
USE ElementDescription
51
USE DefUtils
52
53
IMPLICIT NONE
54
55
TYPE CrevasseGroups_t
56
LOGICAL, ALLOCATABLE :: NotEmpty(:),Valid(:)
57
INTEGER, ALLOCATABLE :: NodeIndexes(:,:), NoNodes(:)
58
INTEGER :: CurrentGroup
59
!This derived type is for storing groups of connected nodes which
60
!have surface or basal crevasses. I can't think of a sensible strategy
61
!to determine the number of groups required, nor the number of nodes in
62
!each group. However, given that they only contain integers and logicals,
63
!declaring them massive probably isn't an issue.
64
END TYPE CrevasseGroups_t
65
66
TYPE(Model_t) :: Model
67
TYPE(Solver_t) :: Solver
68
Type(Nodes_t) :: CornerElementNodes, CurrentElementNodes, &
69
TargetNodes, CalvedNodes
70
Type(Nodes_t), POINTER :: Nodes0
71
Type(Nodes_t), TARGET :: EvalNodes
72
TYPE(Matrix_t), POINTER :: Vel1Matrix !For finding neighbours
73
TYPE(ValueList_t), POINTER :: Material, SolverParams
74
TYPE(Element_t), POINTER :: CurrentElement, CornerElement
75
TYPE(CrevasseGroups_t) :: SurfaceCrevasseGroups, BasalCrevasseGroups
76
TYPE(Mesh_t), POINTER :: Mesh, EvalMesh, Mesh0 => Null()
77
TYPE(Variable_t), POINTER :: DepthSol, StressSol, Stress1Sol, Stress4Sol, &
78
Vel1Sol, DistanceSol, FlowSol, &
79
WPressureSol, TimeVar, CSurfIndexSol, CBasalIndexSol, Var, &
80
CrevasseGroupVar
81
82
REAL (KIND=dp) :: t, dt, xcoord, ycoord, NodeDepth, NodeStress, NodeStress1, NodeStress4, &
83
NodeWPressure, CalvingSurfIndex, CalvingBasalIndex, CalvingCoordHolder = 1.0E20, &
84
FreeConnect, FreeConnected, Length, WaterDepth, Dw, NodeLength, RhoWF, RhoWS, Rho, &
85
g, OverlapCalvingCoordinate, SeaLevel, BasalCalvingCoordinate, EvalResolution, &
86
STuningParam, BTuningParam, DwStart, DwStop, season, Te, sign, Normal(3), &
87
CornerNormal(3), BedSecond, BedSecondDiff, beddiff, BedToler, dx, dy, &
88
LocalDist, LocalDistNode, PropDistNode, normalcond, ForceCalveSize, &
89
localM(2,2), EigValues(2), EI(2), dumy, dumy2, work(8),YieldStress, &
90
rt0, rt
91
REAL (KIND=DP), POINTER :: DepthValues(:), StressValues(:), Stress1Values(:), Stress4Values(:), &
92
Calving1Values(:), DistanceValues(:), WPressureValues(:), FrontValues(:), &
93
CSurfIndexValues(:), CBasalIndexValues(:), CIndexValues(:), CrevasseGroupValues(:), &
94
Calving2Values(:), EvalPoints(:,:), Field(:), WorkReal(:)
95
REAL (KIND=dp), ALLOCATABLE :: CumDist(:), PropCumDist(:), TargetCumDist(:),TargetPropDist(:)
96
INTEGER :: DIM, i, j, NoNodes, MaxN, FrontNodes, BotNodes, TopNodes, &
97
NofEvalPoints, NextSlot, BotCornerIndex, &
98
BotSecondIndex, county, GoToNode, PrevNode, NextNode, &
99
TimeSinceLast, NoNeighbours, MaxNeighbours, DOFs, ierr, StressDOFs
100
101
INTEGER, POINTER :: DepthPerm(:), StressPerm(:), Stress1Perm(:), Stress4Perm(:),&
102
Vel1Perm(:), Vel1InvPerm(:), DistancePerm(:), OrderPerm(:),&
103
WPressurePerm(:), FrontPerm(:)=>NULL(), InvFrontPerm(:), &
104
TopPerm(:)=>NULL(), BotPerm(:)=>NULL(), Permutation(:), &
105
CSurfIndexPerm(:), CBasalIndexPerm(:), CIndexPerm(:), FieldPerm(:), &
106
NodeNeighbours(:,:), NumNeighbours(:), CrevasseGroupPerm(:)
107
108
INTEGER, ALLOCATABLE :: ThisNodeNeighbours(:)
109
LOGICAL :: FirstTime = .TRUE., CalvingOccurs, RemeshOccurs, &
110
BasalCalvingOccurs = .FALSE., BasalCrevasseModel, OverlapOccurs, DwSeason, &
111
CornerCalving, KeepLooking, TransientSimulation, Found, Debug = .FALSE., &
112
ForceCalving = .FALSE., PlaneStressOnly, Cauchy, OldWay, BasalFS, CornerBadBed, &
113
CornerBadSlope
114
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, FrontMaskName, BotMaskName, TopMaskName, &
115
DwMode, CalvingModel, FlowVarName,BasalFSVarName
116
117
SAVE :: FirstTime, DIM, Permutation, Calving1Values, Calving2Values, &
118
NoNodes, Mesh, FrontMaskName, FreeConnect, WaterDepth, Rho, RhoWS, RhoWF, g, &
119
FrontPerm, FrontValues, FrontNodes, BotPerm, BotNodes, TopPerm, &
120
TopNodes, CSurfIndexSol, &
121
CBasalIndexSol, CSurfIndexPerm, CBasalIndexPerm, CSurfIndexValues, CBasalIndexValues, &
122
EvalMesh, Material, EvalNodes, NodeNeighbours, NumNeighbours, Mesh0, Nodes0, &
123
TimeSinceLast, BasalCrevasseModel, PlaneStressOnly, Cauchy, SolverParams, OldWay, &
124
BasalFS, BasalFSVarName
125
126
INTERFACE
127
SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, &
128
NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes )
129
!------------------------------------------------------------------------------
130
USE Lists
131
USE SParIterComm
132
USE Interpolation
133
USE CoordinateSystems
134
!-------------------------------------------------------------------------------
135
TYPE(Mesh_t), TARGET :: OldMesh, NewMesh
136
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables
137
LOGICAL, OPTIONAL :: UseQuadrantTree
138
LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:)
139
TYPE(Projector_t), POINTER, OPTIONAL :: Projector
140
CHARACTER(LEN=*),OPTIONAL :: MaskName
141
END SUBROUTINE InterpolateMeshToMesh
142
END INTERFACE
143
144
rt0 = RealTime() !time it!
145
146
SolverName = "Calving"
147
148
Debug = .FALSE.
149
150
IF(ParEnv % Pes > 1) CALL Fatal(SolverName, "Solver does not work in parallel!")
151
152
Timevar => VariableGet( Model % Variables,'Time', UnfoundFatal=.TRUE.)
153
t = TimeVar % Values(1)
154
dt = Model % Solver % dt
155
season = t - floor(t)
156
157
RemeshOccurs = .FALSE. !For undercutting
158
159
!*****************************Get Variables*******************************
160
161
!This is the main solver variable, which only has a value on the calving
162
!face of the glacier, and corresponds to the magnitude of retreat.
163
IF(.NOT. ASSOCIATED(Solver % Variable)) CALL Fatal('Calving', 'Variable not associated!')
164
165
!This is the Calving Solver's Surface Crevasse exported variable, which has
166
!a positive value where a crevasse is predicted to exist, and a negative value elsewhere.
167
CSurfIndexSol => VariableGet( Solver % Mesh % Variables, 'Calving Surface Index', &
168
UnfoundFatal=.TRUE.)
169
CSurfIndexPerm => CSurfIndexSol % Perm
170
CSurfIndexValues => CSurfIndexSol % Values
171
172
!This is the Calving Solver's Basal crevasse exported variable.
173
!Positive value where basal crevasse
174
!is predicted to exist, negative otherwise, and zero at the tip.
175
176
!From Faezeh: C_basal = LongitudinalDevStress(Rxx)-Rho*g*(IceThickness-
177
!HeightAboveGlacierBase)+RhoOcean*g*(PiezometricHeight-HeightAboveGlacierBase)
178
179
CBasalIndexSol => VariableGet( Solver % Mesh % Variables, 'Calving Basal Index', &
180
UnfoundFatal=.TRUE.)
181
CBasalIndexPerm => CBasalIndexSol % Perm
182
CBasalIndexValues => CBasalIndexSol % Values
183
184
CrevasseGroupVar => VariableGet( Solver % Mesh % Variables, 'Crevasse Group ID', &
185
UnfoundFatal=.TRUE.)
186
CrevasseGroupPerm => CrevasseGroupVar % Perm
187
CrevasseGroupValues => CrevasseGroupVar % Values
188
189
DepthSol => VariableGet( Model % Variables, 'Depth', UnfoundFatal=.TRUE.)
190
DepthPerm => DepthSol % Perm
191
DepthValues => DepthSol % Values
192
193
StressSol => VariableGet( Model % Variables, "Stress", UnfoundFatal=.TRUE.)
194
StressPerm => StressSol % Perm
195
StressValues => StressSol % Values
196
StressDOFs = StressSol % DOFs
197
198
Stress1Sol => VariableGet( Model % Variables, "Stress 1", &
199
UnfoundFatal=.TRUE.)
200
Stress1Perm => Stress1Sol % Perm
201
Stress1Values => Stress1Sol % Values
202
203
Stress4Sol => VariableGet( Model % Variables, "Stress 4", &
204
UnfoundFatal=.TRUE.)
205
Stress4Perm => Stress4Sol % Perm
206
Stress4Values => Stress4Sol % Values
207
208
DistanceSol => VariableGet( Model % Variables, "Distance", &
209
UnfoundFatal=.TRUE.)
210
DistancePerm => DistanceSol % Perm
211
DistanceValues => DistanceSol % Values
212
213
WPressureSol => VariableGet( Model % Variables, "Water Pressure", &
214
UnfoundFatal=.TRUE.)
215
WPressurePerm => WPressureSol % Perm
216
WPressureValues => WPressureSol % Values
217
218
IF(FirstTime) THEN
219
220
DIM = CoordinateSystemDimension()
221
IF(DIM /= 2) CALL Fatal('Calving','Solver only works in 2D!')
222
MaxN = Model % Mesh % MaxElementNodes
223
!To track time since last calving event. Probably not needed.
224
TimeSinceLast = 0
225
226
Mesh => Solver % Mesh
227
NoNodes = Mesh % NumberOfNodes
228
229
FrontMaskName = 'Calving Front Mask'
230
TopMaskName = 'Top Surface Mask'
231
BotMaskName = 'Bottom Surface Mask'
232
ALLOCATE( FrontPerm(NoNodes), TopPerm(NoNodes), BotPerm(NoNodes))
233
234
CALL MakePermUsingMask( Model,Solver,Mesh,FrontMaskName, &
235
.FALSE., FrontPerm, FrontNodes )
236
CALL MakePermUsingMask( Model,Solver,Mesh,TopMaskName, &
237
.FALSE., TopPerm, TopNodes )
238
CALL MakePermUsingMask( Model,Solver,Mesh,BotMaskName, &
239
.FALSE., BotPerm, BotNodes )
240
241
!Holds the variable values
242
ALLOCATE( FrontValues(FrontNodes * 2) )
243
244
Mesh0 => AllocateMesh()
245
Mesh0 = Mesh
246
Mesh0 % Name = TRIM(Mesh % Name)//'_initial'
247
CALL Info('Calving','Created initial reference mesh to remap front, maintaining quality')
248
ALLOCATE( Nodes0 )
249
ALLOCATE( Nodes0 % x(NoNodes), Nodes0 % y(NoNodes), Nodes0 % z(NoNodes) )
250
Nodes0 % x = Mesh % Nodes % x
251
Nodes0 % y = Mesh % Nodes % y
252
Nodes0 % z = Mesh % Nodes % z
253
Mesh0 % Nodes => Nodes0
254
255
Permutation => FrontPerm
256
Calving1Values => FrontValues(1::2)
257
Calving2Values => FrontValues(2::2)
258
259
!Initialize
260
Calving1Values = 0.0_dp
261
Calving2Values = 0.0_dp
262
263
!Potential to force an initial calving event
264
!Specified by LOGICAL and REAL in SIF
265
SolverParams => GetSolverParams()
266
ForceCalving = GetLogical(SolverParams, 'Force Calving', Found)
267
!TODO: This doesn't work - Calving1Values isn't associated with Solver % Variable % Values yet...
268
IF(ForceCalving) THEN
269
ForceCalveSize = GetConstReal (SolverParams, 'Force Calving Size', Found)
270
IF(.NOT.Found) CALL Fatal(SolverName, "Force Calving Size not found.")
271
Calving1Values = -ForceCalveSize
272
CalvingOccurs =.TRUE.
273
CALL ListAddLogical( Model % Simulation, 'CalvingOccurs', CalvingOccurs )
274
RETURN
275
END IF
276
ENDIF
277
278
Solver % Variable % Values => FrontValues
279
Solver % Variable % Perm => FrontPerm
280
281
Var => VariableGet(Solver % Mesh % Variables, ComponentName(Solver % Variable % Name, 1), .TRUE.)
282
Var % Values => Calving1Values
283
Var % Perm => Permutation
284
Var => VariableGet(Solver % Mesh % Variables, ComponentName(Solver % Variable % Name, 2), .TRUE.)
285
Var % Values => Calving2Values
286
Var % Perm => Permutation
287
288
IF(FirstTime .OR. Solver % Mesh % Changed) THEN
289
FirstTime = .FALSE.
290
291
!STRATEGY: Finding neighbours on the fly works fine UNLESS you are in a recursive subroutine
292
!Then it messes up, because at each level, ThisNodeNeighbours is deallocate and reallocated,
293
!meaning that when you jump back up, info is already overwritten. SO:
294
!Keep the structure as it was with CalvingNeighbours, cycle as below to fill it, and ALSO
295
!create an array to hold the number of neighbours for each node
296
297
!Get the Matrix of the N-S Solver
298
Vel1Sol => VariableGet( Solver % Mesh % Variables, 'Velocity 1', UnfoundFatal=.TRUE.)
299
Vel1Perm => Vel1Sol % Perm
300
Vel1Matrix => Vel1Sol % Solver % Matrix
301
302
!Vel * DIM + Pressure...
303
DOFs = DIM + 1
304
MaxNeighbours = DIM * 10 !totally arbitrary...
305
306
!Create inverse perm to lookup Matrix later
307
ALLOCATE(Vel1InvPerm(MAXVAL(Vel1Perm)*DOFs)) !TODO DEALLOCATE
308
!2D array to hold each nodes neighbours
309
ALLOCATE(NodeNeighbours(NoNodes,MaxNeighbours))
310
!1D array to hold number of neighbours for each node
311
ALLOCATE(NumNeighbours(NoNodes))
312
NodeNeighbours = 0
313
NumNeighbours = 0
314
Vel1InvPerm = 0
315
316
j = 0
317
DO i=1,SIZE(Vel1Perm)
318
IF(Vel1Perm(i) == 0) CYCLE
319
j = j + 1
320
Vel1InvPerm( (Vel1Perm(i)*DOFs-2) : (Vel1Perm(i)*DOFs) ) = j !The 2 here is suspect...
321
END DO
322
323
DO i = 1,NoNodes
324
CALL FindNodeNeighbours(i) !Updates the allocatable array 'ThisNodeNeighbours'
325
NumNeighbours(i) = SIZE(ThisNodeNeighbours)
326
NodeNeighbours(i,1:NumNeighbours(i)) = ThisNodeNeighbours
327
END DO
328
END IF
329
330
PRINT *, '****Calving Timestep : ',t
331
332
rt = RealTime() - rt0
333
IF(ParEnv % MyPE == 0) &
334
PRINT *, 'Calving, time taken for variable loading etc:', rt
335
rt0 = RealTime()
336
337
MaxN = Model % Mesh % MaxElementNodes
338
OldWay = ListGetLogical(SolverParams, "Old Way", Found)
339
IF(.NOT. Found) OldWay = .FALSE.
340
341
!Identify the basal freesurface variable
342
BasalFS = ListGetLogical(SolverParams, "Basal FreeSurface", Found)
343
IF(.NOT. Found) BasalFS = .TRUE.
344
IF(BasalFS) THEN
345
BasalFSVarName = ListGetString(SolverParams, "Basal FreeSurface Variable Name",Found)
346
IF(.NOT. Found) CALL Fatal(SolverName, &
347
"Basal FreeSurface = True but no Basal FreeSurface Variable Name found!")
348
END IF
349
350
Material => GetMaterial(Model % Mesh % Elements(Solver % ActiveElements(1)))
351
Cauchy = ListGetLogical( Material , 'Cauchy', Found )
352
IF(.NOT. Found) THEN
353
Cauchy = .FALSE.
354
CALL Warn(SolverName, "Couldn't find 'cauchy' logical in material, &
355
&assuming deviatoric stress.")
356
END IF
357
358
FlowVarName = ListGetString(SolverParams, "Flow Solution Variable Name",Found)
359
IF(.NOT. Found) FlowVarName = "Flow Solution"
360
361
FlowSol => VariableGet(Model % Variables, FlowVarName, .TRUE., UnfoundFatal=.TRUE.)
362
363
!! FreeConnect: distance from calving front where free connection to proglacial
364
!!water body exists. Declare in SIF Constants!
365
FreeConnect = GetConstReal( Model % Constants, "FreeConnect", Found )
366
IF(.NOT.Found) CALL Fatal(SolverName, "Unable to find FreeConnect")
367
Dw = GetConstReal( Model % Constants, "Water Depth", Found )
368
IF(.NOT.Found) THEN
369
CALL Warn(SolverName, "Unable to find Water Depth, assuming zero.")
370
Dw = 0.0_dp
371
END IF
372
373
Rho = GetConstReal( Model % Constants, "Rho", Found )
374
IF(.NOT.Found) CALL Fatal(SolverName, "Unable to find Rho.")
375
RhoWS = GetConstReal( Model % Constants, "RhoWS", Found )
376
IF(.NOT.Found) CALL Fatal(SolverName, "Unable to find RhoW.")
377
RhoWF = GetConstReal( Model % Constants, "RhoWF", Found )
378
IF(.NOT.Found) CALL Fatal(SolverName, "Unable to find RhoWF.")
379
g = GetConstReal( Model % Constants, "g", Found )
380
IF(.NOT.Found) CALL Fatal(SolverName, "Unable to find Gravity.")
381
IF(g < 0.0_dp) g = -1.0_dp * g
382
383
SeaLevel = GetConstReal( Model % Constants, "Sea Level", Found )
384
IF(.NOT.Found) THEN
385
WRITE(Message,'(A)') 'Variable Sea level not found. &
386
&Setting to 0.0'
387
CALL Info('Calving', Message, level=2)
388
SeaLevel = 0.0_dp
389
END IF
390
391
!This next bit stolen from MaterialModels.src
392
!Only needed if calvingmodel=planestrain, could put logical?
393
STuningParam = GetConstReal( Model % Constants, "Surface Crevasse Tuning Parameter", Found )
394
IF(.NOT.Found) THEN
395
WRITE(Message,'(A)') 'Surface Crevasse Tuning Parameter not found. &
396
Setting to 1.0'
397
CALL Info('Calving', Message, level=2)
398
STuningParam = 1.0_dp
399
END IF
400
401
BTuningParam = GetConstReal( Model % Constants, "Basal Crevasse Tuning Parameter", Found )
402
IF(.NOT.Found) THEN
403
WRITE(Message,'(A)') 'Basal Crevasse Tuning Parameter not found. &
404
Setting to 1.0'
405
CALL Info('Calving', Message, level=2)
406
BTuningParam = 1.0_dp
407
END IF
408
409
!Get stuff for variable water depth in crevasses.
410
!Don't think this is the logical place for this, but whatever.
411
SolverParams => GetSolverParams()
412
413
DwMode = GetString (SolverParams, 'Dw Mode', Found)
414
IF(.NOT.Found) THEN
415
CALL Warn(SolverName, "Dw Mode not found, assuming off.")
416
DwMode = "off"
417
END IF
418
DwMode = TRIM(DwMode)
419
IF(DwMode /= "off") THEN
420
DwStart = GetConstReal (SolverParams, 'Dw Start', Found)
421
IF(.NOT.Found) CALL Fatal(SolverName, "Dw Start not found.")
422
423
DwStop = GetConstReal (SolverParams, 'Dw Stop', Found)
424
IF(.NOT.Found) CALL Fatal(SolverName, "Dw Stop not found.")
425
END IF
426
427
YieldStress = ListGetConstReal(SolverParams, "Yield Stress", Found)
428
IF(.NOT. Found) THEN
429
YieldStress = 0.0_dp
430
CALL Warn(SolverName, "No yield stress found, assuming 0.0")
431
ELSE
432
WRITE(Message,'(A,f8.2)') 'Yield Stress = ',YieldStress
433
CALL Info(SolverName, Message)
434
END IF
435
436
BasalCrevasseModel = ListGetLogical(SolverParams, "Basal Crevasse Model", Found)
437
IF(.NOT. Found) THEN
438
BasalCrevasseModel = .TRUE.
439
CALL Warn(SolverName,"No 'Basal Crevasse Model' found, assuming True")
440
END IF
441
IF(BasalCrevasseModel) THEN
442
CALL Info(SolverName,"Using Basal Crevasse Model, &
443
&calving occurs when surface and basal crevasses meet", Level=2)
444
ELSE
445
CALL Info(SolverName,"Using Surface Crevasse Model, &
446
&calving occurs when surface crevasses meet sea level",Level=2)
447
END IF
448
449
PlaneStressOnly = ListGetLogical(SolverParams, "Plane Stress Only", Found)
450
IF(.NOT. Found) PlaneStressOnly = .FALSE.
451
452
SELECT CASE(DwMode)
453
CASE("off")
454
WaterDepth = 0.0
455
CASE ("constant")
456
457
WaterDepth = Dw
458
459
CASE("binary")
460
461
DwSeason = .FALSE.
462
IF(DwStart .LT. DwStop) THEN !normal case, dw season doesn't pass winter
463
IF((season .GT. DwStart) .AND. (season .LT. DwStop)) DwSeason = .TRUE.
464
ELSE !this is unlikely to be needed
465
IF((season .GT. DwStart ) .OR. (season .LT. DwStop )) DwSeason = .TRUE.
466
END IF
467
IF(DwSeason) THEN
468
WaterDepth = Dw
469
ELSE
470
WaterDepth = 0.0
471
ENDIF
472
473
CASE DEFAULT
474
CALL Fatal(SolverName,"Invalid Dw mode selection")
475
END SELECT
476
477
!Finds the maximum value of Coordinate 1 (i.e. the calving face) and
478
!FreeConnected (the minimum Coordinate 1 which should be checked for calving.
479
480
!First find the length of the glacier
481
Length = 0.0
482
DO i = 1, NoNodes
483
IF(Permutation(i) == 0) CYCLE
484
NodeLength = Model % Nodes % x(i)
485
IF(NodeLength > Length) Length = NodeLength
486
END DO
487
PRINT *, '**** Calving Glacier Length = ',Length
488
CalvingCoordHolder = Length
489
FreeConnected = Length - FreeConnect
490
491
!---------------------------------------
492
!
493
! Evaluate the crevasse criteria for basal and surface crevasses
494
!
495
!---------------------------------------
496
497
DO i = 1, NoNodes
498
xcoord = Mesh % Nodes % x(i)
499
ycoord = Mesh % Nodes % y(i)
500
501
!TODO - Tuning Parameters aren't used except in specific PlaneStressOnly case.
502
!SORT THIS OUT - but what sense does the tuning parameter have in the cauchy case?
503
IF(.NOT. OldWay) THEN
504
505
!Compute maximum extensive principal stress
506
localM=0.0_dp
507
!xx,yy,zz,xy,yz,zx
508
localM(1,1)=StressValues( StressDOFs * (StressPerm(i) - 1 ) + 1) !xx
509
localM(2,2)=StressValues( StressDOFs * (StressPerm(i) - 1 ) + 2) !yy
510
localM(1,2)=StressValues( StressDOFs * (StressPerm(i) - 1 ) + 4) !xy
511
localM(2,1)=localM(1,2)
512
513
IF(.NOT. Cauchy) THEN
514
DO j=1,2 !dim+1, only runs in 3D
515
localM(j,j)=localM(j,j) - FlowSol % Values(FlowSol % Perm(i)*FlowSol % DOFs)
516
END DO
517
END IF
518
519
CALL DGEEV('N','N',2,localM,2,EigValues,EI,Dumy,1,Dumy2,1,Work,8,ierr )
520
IF (ierr/=0) THEN
521
WRITE(Message,'(A,i0)') 'Failed to compute EigenValues, error code: ',ierr
522
CALL FATAL(SolverName, Message)
523
END IF
524
525
NodeStress = MAXVAL(EigValues) !Maximum principalstress
526
527
NodeWPressure = -WPressureValues(WPressurePerm(i))
528
IF(NodeWPressure < 0.0_dp) NodeWPressure = 0.0_dp
529
530
CalvingSurfIndex = NodeStress + (WaterDepth * RhoWF * g) - YieldStress
531
532
CalvingBasalIndex = NodeStress + NodeWPressure - YieldStress
533
ELSE
534
CALL Info('Calving', 'Calculating calving criterion the old way', level=2)
535
NodeDepth = DepthValues(DepthPerm(i))
536
537
NodeStress1 = Stress1Values(Stress1Perm(i))
538
IF(Cauchy) &
539
NodeStress1 = NodeStress1 + FlowSol % Values(FlowSol % Perm(i)*FlowSol % DOFs)
540
541
542
NodeWPressure = -WPressureValues(WPressurePerm(i))
543
IF(NodeWPressure < 0.0_dp) NodeWPressure = 0.0_dp
544
545
!NodeWPressure is the water pressure in a basal crevasse (piezometric - height) basically
546
!From Faezeh:
547
!C_surface = LongitudinalDevStress(Rxx) - Rho*g*NodeDepth + WaterDepth*RhoW*g
548
549
!C_basal = LongitudinalDevStress(Rxx)-Rho*g*(IceThickness-HeightAboveGlacierBase)+RhoOcean*g*(PiezometricHeight-HeightAboveGlacierBase)
550
!Water pressure (Rho_w * g * Piezo) is PressureSol,PressureValues,PressurePerm
551
552
IF(PlaneStressOnly) THEN
553
554
CalvingSurfIndex = ( STuningParam * 2.0 * NodeStress1 ) - &
555
(NodeDepth * g * Rho) + (WaterDepth * RhoWF * g) - YieldStress
556
CalvingBasalIndex = ( BTuningParam * 2.0 * NodeStress1 ) - &
557
(NodeDepth * g * Rho) + NodeWPressure - YieldStress
558
!Last term appears if you split the brackets on the last term in C_Basal from Faezeh
559
ELSE
560
!Shear stress for Te calc
561
NodeStress4 = Stress4Values(Stress4Perm(i))
562
!2(Te^2) = Txx^2 + Tyy^2 + 2*Txy^2
563
!Txx = Tyy thus:
564
!Te^2 = (Txx^2) + (Txy^2)
565
!Te = ((Txx^2) + (Txy^2)) ^ 0.5
566
Te = ((NodeStress1**2) + (NodeStress4**2)) ** 0.5
567
sign = NodeStress1/abs(NodeStress1)
568
569
CalvingSurfIndex = ( STuningParam * 2.0 * sign * Te ) - &
570
(NodeDepth * g * Rho) + (WaterDepth * RhoWF * g) - YieldStress
571
CalvingBasalIndex = ( BTuningParam * 2.0 * sign * Te ) - &
572
(NodeDepth * g * Rho) + NodeWPressure - YieldStress
573
END IF
574
575
END IF
576
577
CSurfIndexValues(CSurfIndexPerm(i)) = CalvingSurfIndex
578
CBasalIndexValues(CBasalIndexPerm(i)) = CalvingBasalIndex
579
END DO
580
581
rt = RealTime() - rt0
582
IF(ParEnv % MyPE == 0) &
583
PRINT *, 'Calving, time taken for evaluating CIndex:', rt
584
rt0 = RealTime()
585
586
!---------------------------------------
587
!
588
! Find connected crevassed regions and check for calving
589
!
590
!---------------------------------------
591
592
!TODO, allow both
593
IF(BasalCrevasseModel) THEN
594
CrevasseGroupValues = 0
595
596
!Find groups of nodes which have surface crevasses
597
CIndexValues => CSurfIndexValues
598
CIndexPerm => CSurfIndexPerm
599
CALL FindCrevasseGroups(SurfaceCrevasseGroups,.TRUE., TopPerm)
600
601
!As above, basal crevasses
602
CIndexValues => CBasalIndexValues
603
CIndexPerm => CBasalIndexPerm
604
CALL FindCrevasseGroups(BasalCrevasseGroups,.TRUE., BotPerm)
605
606
!Look for touching/almost touching crevasse groups
607
CALL FindCalvingBasal(SurfaceCrevasseGroups, BasalCrevasseGroups, BasalCalvingCoordinate)
608
609
ELSE !Surface Crevasse Model
610
611
!This dictates the resolution of the mesh for interpolating calving index
612
!Only relevant for surface crevasse mode, as opposed to surf and basal
613
!TODO: Unhardcode this
614
EvalResolution = 1.0_dp
615
616
!Create the points (at the waterline) at which Calving Index will be evaluated
617
!and assign them to a new mesh for interpolation
618
619
NofEvalPoints = FLOOR(FreeConnect/EvalResolution)
620
EvalMesh => AllocateMesh()
621
EvalMesh % Name = "Eval_Mesh"
622
EvalMesh % NumberOfNodes = NofEvalPoints
623
EvalMesh % Nodes => EvalNodes
624
ALLOCATE(Field(NofEvalPoints), &
625
FieldPerm(NofEvalPoints), &
626
EvalPoints(NofEvalPoints,2), &
627
EvalNodes % x(NofEvalPoints), &
628
EvalNodes % y(NofEvalPoints), &
629
EvalNodes % z(NofEvalPoints))
630
631
Field = -1.0_dp
632
EvalPoints(:,2) = SeaLevel
633
DO i=1,NofEvalPoints
634
EvalPoints(i,1) = Length - i*EvalResolution
635
FieldPerm(i) = i
636
END DO
637
EvalMesh % Nodes % x = EvalPoints(:,1)
638
EvalMesh % Nodes % y = SeaLevel
639
EvalMesh % Nodes % z = 0.0_dp
640
641
CALL VariableAdd( EvalMesh % Variables, EvalMesh, Solver, "Calving Surface Index", 1, Field, FieldPerm )
642
!TEST - should be true
643
CALL InterpolateMeshToMesh( Mesh, EvalMesh, Mesh % Variables, EvalMesh % Variables, .FALSE. )
644
645
CalvingOccurs = .FALSE.
646
Var => VariableGet( EvalMesh % Variables, 'Calving Surface Index', UnfoundFatal=.TRUE.)
647
DO i=1,NofEvalPoints
648
IF(Var % Values(Var % Perm(i)) < 0.0_dp ) CYCLE
649
CalvingOccurs = .TRUE.
650
PRINT *,'Surface Calving Event'
651
IF(EvalMesh % Nodes % x(i) < CalvingCoordHolder) &
652
CalvingCoordHolder = EvalMesh % Nodes % x(i)
653
END DO
654
655
END IF !Surface crevasse model
656
657
IF(BasalCrevasseModel) THEN
658
CalvingOccurs = .FALSE.
659
IF(BasalCalvingOccurs) THEN
660
CalvingOccurs = .TRUE.
661
DO j = 1, NoNodes
662
IF(Permutation(j) == 0) CYCLE
663
Calving1Values(Permutation(j)) = BasalCalvingCoordinate - &
664
Mesh % Nodes % x(j)
665
IF(Calving1Values(Permutation(j)) .GE. 0.0_dp) &
666
Calving1Values(Permutation(j)) = 0.0_dp
667
END DO
668
PRINT *, 'Basal Calving Event at timestep ',t
669
PRINT *, 'Calving Coordinate :',BasalCalvingCoordinate
670
ELSE
671
Calving1Values = 0.0_dp
672
END IF
673
ELSE
674
!Surface-only calving model
675
IF(CalvingOccurs) THEN
676
DO j = 1, NoNodes
677
IF(Permutation(j) == 0) CYCLE
678
Calving1Values(Permutation(j)) = CalvingCoordHolder - &
679
Mesh % Nodes % x(j)
680
IF(Calving1Values(Permutation(j)) .GE. 0.0_dp) &
681
Calving1Values(Permutation(j)) = 0.0_dp
682
END DO
683
PRINT *, 'Calving Event at timestep ',t
684
PRINT *, 'Calving Coordinate :',CalvingCoordHolder
685
ELSE
686
Calving1Values = 0.0_dp
687
END IF
688
END IF
689
690
rt = RealTime() - rt0
691
IF(ParEnv % MyPE == 0) &
692
PRINT *, 'Calving, time taken for calving connectivity:', rt
693
rt0 = RealTime()
694
695
!---------------------------------------
696
! CALVING DONE
697
!---------------------------------------
698
699
!At this point, the 'calving' solution is done. However...
700
!Here we solve a problem to do with undercutting:
701
!Progressive undercutting by melting of the calving front
702
!can lead to a situation where 'Front' nodes start to look like
703
!basal nodes. However, they are 'officially' front nodes and so
704
!don't have a friction law, grounding dynamics OR (most importantly
705
!I think), a bed constraint. Thus, it is necessary to check for this
706
!occurring, and shift the bed nodes appropriately.
707
!
708
!Strategy:
709
!-Identify the corner node by BotPerm and FrontPerm
710
!
711
!-Use BCelement connections to find the second to bottom
712
! node on the calving front
713
!
714
!-Check a condition: either
715
! second node is 'near' bed OR
716
! BCelement slope is below some critical level
717
!
718
!-If condition is met (i.e. we need to take action)
719
! Calving1Values @CornerNode = (X@2nd - X@corner)
720
! CalvingOccurs = .TRUE. <-- will this have any unforeseen consequences?
721
!
722
!NOTE: This works in tandem with a section of TwoMeshes.f90 which does the actual
723
!deformation
724
725
!Get the node index of the bottom corner
726
!NOTE: this could be 'FirstTime'd if it was also 'SAVE'd
727
DO i=1,NoNodes
728
IF(BotPerm(i) > 0 .AND. FrontPerm(i) > 0) THEN
729
BotCornerIndex = i
730
END IF
731
END DO
732
733
!Loop boundary elements, we're looking for the BCelement
734
!containing BotCornerIndex and ANOTHER FrontPerm node...
735
DO i=Mesh % NumberOfBulkElements+1,&
736
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
737
CurrentElement => Mesh % Elements(i)
738
IF(.NOT.(ANY(CurrentElement % nodeindexes == BotCornerIndex))) CYCLE
739
IF(ALL(FrontPerm(CurrentElement % nodeindexes) .GT. 0)) THEN
740
!We have a winner
741
CornerElement => Mesh % Elements(i)
742
DO j=1,2
743
IF(CurrentElement % NodeIndexes(j) .NE. BotCornerIndex) THEN
744
BotSecondIndex = CurrentElement % NodeIndexes(j)
745
END IF
746
END DO
747
END IF
748
END DO
749
750
!Check corner node isn't already calving
751
IF(Calving1Values(Permutation(BotCornerIndex)) .LT. 0.0_dp) THEN
752
CornerCalving = .TRUE.
753
ELSE
754
CornerCalving = .FALSE.
755
END IF
756
757
!Get normal vector:
758
CALL GetElementNodes(CornerElementNodes, CornerElement)
759
CornerNormal = NormalVector(CornerElement, CornerElementNodes)
760
761
IF(Debug) PRINT *, 'Debug Calving, corner normal is: ' , &
762
CornerNormal(1), CornerNormal(2), CornerNormal(3)
763
764
IF(BasalFS) THEN
765
BedSecond = ListGetRealAtNode( Material, "Min "//BasalFSVarName, &
766
BotSecondIndex, UnfoundFatal=.TRUE. )
767
768
IF(Debug) PRINT *, 'Debug Calving, second node bed is: ',&
769
BedSecond,' and y coord is: ', Model % Nodes % y(BotSecondIndex)
770
771
PRINT *, 'Debug Calving, second node bed is: ',&
772
BedSecond,' and y coord is: ', Model % Nodes % y(BotSecondIndex)
773
774
BedSecondDiff = Model % Nodes % y(BotSecondIndex) - BedSecond
775
END IF
776
777
!TODO - unhardcode these
778
BedToler = 2.0_dp
779
normalcond = 0.95_dp
780
781
CornerBadBed = BasalFS .AND. (BedSecondDiff < BedToler)
782
CornerBadSlope = ABS(CornerNormal(2)) > normalcond
783
784
!If the slope normal is above threshold, or the second node is too close to the bed,
785
!move the corner node to the second, via 'calving'
786
IF((CornerBadSlope .OR. CornerBadBed) .AND. (.NOT. CornerCalving)) THEN
787
788
IF(Debug) PRINT *,'Debug Calving, migrating mesh'
789
county = 1
790
GoToNode = BotSecondIndex
791
PrevNode = BotCornerIndex
792
KeepLooking = .TRUE.
793
DO WHILE (KeepLooking)
794
!Check if we should shift more than one node forward...
795
IF(Debug) PRINT *, 'Debug calving: looking!'
796
KeepLooking = .FALSE.
797
DO i=Mesh % NumberOfBulkElements+1,&
798
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
799
CurrentElement => Mesh % Elements(i)
800
IF(.NOT.(ALL(FrontPerm(CurrentElement % nodeindexes) .GT. 0))) CYCLE
801
!This point reached by Front BC elements, stupid way of doing it, but whatever
802
IF(.NOT.(ANY(CurrentElement % nodeindexes == GoToNode))) CYCLE
803
IF(ANY(CurrentElement % nodeindexes == PrevNode)) CYCLE
804
!We only get here if element is the next one along from previous
805
CALL GetElementNodes(CurrentElementNodes, Currentelement)
806
Normal = NormalVector(CurrentElement, CurrentElementNodes)
807
DO j=1,2
808
IF(CurrentElement % NodeIndexes(j) .NE. GoToNode) &
809
NextNode = CurrentElement % NodeIndexes(j)
810
END DO
811
812
IF(BasalFS) THEN
813
beddiff = Model % Nodes % y(NextNode) - ListGetRealAtNode( Material, &
814
"Min "//BasalFSVarName, NextNode, UnfoundFatal=.TRUE. )
815
END IF
816
817
CornerBadBed = BasalFS .AND. (beddiff < BedToler)
818
CornerBadSlope = ABS(CornerNormal(2)) > normalcond
819
820
IF(CornerBadBed .OR. CornerBadSlope) THEN
821
PrevNode = GoToNode
822
GoToNode = NextNode
823
county = county + 1
824
IF(Debug) PRINT *, 'Debug calving: Found another shift'
825
KeepLooking = .TRUE.
826
EXIT
827
END IF
828
END DO
829
END DO
830
831
RemeshOccurs = .TRUE.
832
ELSE
833
county = 0
834
END IF
835
836
rt = RealTime() - rt0
837
IF(ParEnv % MyPE == 0) &
838
PRINT *, 'Calving, time taken for corner problems:', rt
839
rt0 = RealTime()
840
841
IF(CalvingOccurs .OR. RemeshOccurs) THEN
842
843
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
844
!Find the FrontDisplacement (= Calving 2 <-sif) for each frontal node
845
!resulting from the shift in the corner node
846
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
847
848
!Use FrontPerm to construct ordered node list
849
!I think MakeMaskUsingPerm already ordered the nodes, from the comments:
850
!! The bandwidth optimization for lines results to perfectly ordered
851
!! permutations. If there is only one line the 1st node should be the
852
!! lower left corner.
853
ALLOCATE(InvFrontPerm(FrontNodes),&
854
CumDist(FrontNodes),&
855
PropCumDist(FrontNodes),&
856
TargetCumDist(FrontNodes),&
857
TargetPropDist(FrontNodes),&
858
TargetNodes % x(FrontNodes),&
859
TargetNodes % y(FrontNodes),&
860
TargetNodes % z(FrontNodes),&
861
CalvedNodes % x(FrontNodes))
862
863
!InvFrontPerm(FrontNodes) points to nodeindexes in order they appear...
864
!InvFrontPerm(1) points to 'lower left' corner, according to MakePermUsingMask
865
DO i=1,NoNodes
866
IF(FrontPerm(i) .GT. 0) THEN
867
InvFrontPerm(FrontPerm(i)) = i
868
END IF
869
END DO
870
871
ALLOCATE(OrderPerm(FrontNodes), WorkReal(FrontNodes))
872
OrderPerm = [(i,i=1,FrontNodes)]
873
DO i=1,FrontNodes
874
WorkReal(i) = Mesh0 % Nodes % y(InvFrontPerm(i))
875
END DO
876
CALL SortD( FrontNodes, WorkReal, OrderPerm )
877
DEALLOCATE(WorkReal)
878
879
DO i=1,FrontNodes
880
j = InvFrontPerm(OrderPerm(i))
881
CalvedNodes % x(i) = Mesh % Nodes % x(j) + Calving1Values(Permutation(j))
882
IF(Debug) PRINT *,'Debug Calving, CalvedNodes node: ',j,' is ',&
883
CalvedNodes % x(i),' init coord: ',Mesh % Nodes % x(j)
884
END DO
885
886
!cycle through in order
887
!First get target distribution from Mesh0
888
TargetCumDist(1) = 0.0_dp
889
DO i=2,FrontNodes
890
dx = Mesh0 % Nodes % x(InvFrontPerm(OrderPerm(i))) - Mesh0 % Nodes % x(InvFrontPerm(OrderPerm(i-1)))
891
dy = Mesh0 % Nodes % y(InvFrontPerm(OrderPerm(i))) - Mesh0 % Nodes % y(InvFrontPerm(OrderPerm(i-1)))
892
893
TargetCumDist(i) = TargetCumDist(i-1) + (((dx**2) + (dy**2)) ** 0.5_dp)
894
END DO
895
TargetPropDist = TargetCumDist / MAXVAL(TargetCumDist)
896
897
!Now find the length segments of our current line
898
!If RemeshOccurs (because of bad corner node), county dictates
899
!the offset from the previous bottom node of the new calving front
900
CumDist(1:county+1) = 0.0_dp
901
DO i=county+2,FrontNodes
902
!sum coord magnitude from base upwards to give front 'length'
903
!keep cumulative total
904
!allocate proporitional y (and x) distances (i.e. out of 1)
905
dx = CalvedNodes % x(i) - CalvedNodes % x(i-1)
906
dy = Mesh % Nodes % y(InvFrontPerm(OrderPerm(i))) - Mesh % Nodes % y(InvFrontPerm(OrderPerm(i-1)))
907
908
CumDist(i) = CumDist(i-1) + (((dx**2) + (dy**2)) ** 0.5_dp)
909
!Remember first one is corner node...
910
IF(Debug) PRINT *, 'Debug Calving: CumDist at node: ',&
911
InvFrontPerm(OrderPerm(i)),' is ',CumDist(i)
912
IF(Debug) PRINT *, 'Debug Calving: TargetDist at node: ',&
913
InvFrontPerm(OrderPerm(i)),' is ',TargetCumDist(i)
914
END DO
915
PropCumDist = CumDist / MAXVAL(CumDist)
916
917
!Loop each front node
918
TargetNodes % x(1) = CalvedNodes % x(county+1)
919
TargetNodes % y(1) = Mesh % Nodes % y(InvFrontPerm(OrderPerm(county+1)))
920
TargetNodes % x(FrontNodes) = CalvedNodes % x(FrontNodes)
921
TargetNodes % y(FrontNodes) = Mesh % Nodes % y(InvFrontPerm(OrderPerm(FrontNodes)))
922
923
DO i=2,FrontNodes-1
924
!and find nearest two nodes to interpolate
925
DO j=county+2,FrontNodes
926
IF(PropCumDist(j) .GT. TargetPropDist(i)) THEN
927
!lin interp between j and j-1
928
LocalDist = PropCumDist(j) - PropCumDist(j-1)
929
LocalDistNode = TargetPropDist(i) - PropCumDist(j-1)
930
931
PropDistNode = LocalDistNode / LocalDist
932
IF(Debug) PRINT *, 'Debug Calving: PropDist at node: ',&
933
InvFrontPerm(OrderPerm(i)),' is ',PropDistNode
934
935
TargetNodes % x(i) = ((1 - PropDistNode) * CalvedNodes % x(j-1)) + &
936
(PropDistNode * CalvedNodes % x(j))
937
TargetNodes % y(i) = ((1 - PropDistNode) * Mesh % Nodes % y(InvFrontPerm(OrderPerm(j-1)))) + &
938
(PropDistNode * Mesh % Nodes % y(InvFrontPerm(OrderPerm(j))))
939
EXIT
940
END IF
941
END DO
942
END DO
943
944
!At this point, we have obtained, for each FrontNode, a TargetNode % x and y
945
!Thus, it simply remains to compute the two components of the displacement
946
947
!Calving 1 = Diff X (New % x - Old % x)
948
!Calving 2 = Diff Y (New % y - Old % y)
949
950
DO i=1,FrontNodes
951
Calving1Values(Permutation(InvFrontPerm(OrderPerm(i)))) = TargetNodes % x(i) &
952
- Mesh % Nodes % x(InvFrontPerm(OrderPerm(i)))
953
954
Calving2Values(Permutation(InvFrontPerm(OrderPerm(i)))) = TargetNodes % y(i) &
955
- Mesh % Nodes % y(InvFrontPerm(OrderPerm(i)))
956
957
IF(Debug) THEN
958
PRINT *,'Debug Calving: Node: ',InvFrontPerm(OrderPerm(i)),' pos x: ',&
959
Mesh % nodes % x(InvFrontPerm(OrderPerm(i))),&
960
' pos y: ',Mesh % nodes % y(InvFrontPerm(OrderPerm(i)))
961
PRINT *,'Moving to: x: ',TargetNodes % x(i),' y: ',TargetNodes % y(i)
962
PRINT *,'Displacement 1: ',Calving1Values(Permutation(InvFrontPerm(OrderPerm(i)))),&
963
'Displacement 2: ',Calving2Values(Permutation(InvFrontPerm(OrderPerm(i))))
964
END IF
965
END DO
966
END IF
967
968
CALL ListAddLogical( Model % Simulation, 'CalvingOccurs', CalvingOccurs )
969
CALL ListAddLogical( Model % Simulation, 'RemeshOccurs', RemeshOccurs )
970
971
rt = RealTime() - rt0
972
IF(ParEnv % MyPE == 0) &
973
PRINT *, 'Calving, time taken for calculating displacements (end):', rt
974
rt0 = RealTime()
975
976
CONTAINS
977
978
SUBROUTINE FindNodeNeighbours(NodeNumber)
979
INTEGER :: NodeNumber, i, count
980
981
NoNeighbours = Vel1Matrix % Rows((Vel1Perm(NodeNumber)*DOFs)+1) - Vel1Matrix % Rows(Vel1Perm(NodeNumber)*DOFs)
982
IF(MOD(NoNeighbours, DOFs).NE. 0) CALL Fatal(SolverName,"This shouldn't have happened...")
983
!Each neighbour appears once per DOF, and there's also the current node thus: (x/DOFS) - 1...
984
NoNeighbours = (NoNeighbours / DOFs) - 1
985
IF(NoNeighbours .GT. MaxNeighbours) CALL Fatal(SolverName,"Need more neighbour slots!")
986
987
IF(ALLOCATED(ThisNodeNeighbours)) DEALLOCATE(ThisNodeNeighbours)
988
ALLOCATE(ThisNodeNeighbours(NoNeighbours))
989
ThisNodeNeighbours = 0
990
991
count = 0
992
DO i=Vel1Matrix % Rows(Vel1Perm(NodeNumber)*DOFs),&
993
(Vel1Matrix % Rows((Vel1Perm(NodeNumber)*DOFs)+1)-1)
994
IF(MOD(i,DOFs) .NE. 0) CYCLE !Stored DOF1, DOF2, DOF3, only need every 3rd
995
IF(Vel1InvPerm(Vel1Matrix % Cols(i)) == NodeNumber) CYCLE !Not our own neighbour
996
count = count + 1
997
ThisNodeNeighbours(count) = &
998
Vel1InvPerm(Vel1Matrix % Cols(i))
999
END DO
1000
1001
END SUBROUTINE FindNodeNeighbours
1002
1003
!After finding groups, pass them between partitions and join...
1004
!Can just be done in one partition and then passed back?
1005
SUBROUTINE FindCrevasseGroups(CrevasseGroups, CheckValidity, MaskPerm)
1006
1007
TYPE(CrevasseGroups_t), INTENT(INOUT) :: CrevasseGroups
1008
INTEGER :: MaxGroups = 100, MaxNodesPerGroup = 10000, locali
1009
LOGICAL :: CheckValidity, FoundIt
1010
INTEGER, POINTER, OPTIONAL, INTENT(IN) :: MaskPerm(:)
1011
1012
ALLOCATE( &
1013
CrevasseGroups % NodeIndexes(MaxGroups,MaxNodesPerGroup), &
1014
CrevasseGroups % NotEmpty(MaxGroups), &
1015
CrevasseGroups % NoNodes(MaxGroups), &
1016
CrevasseGroups % Valid(MaxGroups))
1017
1018
CrevasseGroups % NodeIndexes = 0
1019
CrevasseGroups % NoNodes = 0
1020
CrevasseGroups % NotEmpty = .FALSE.
1021
CrevasseGroups % Valid = .FALSE.
1022
CrevasseGroups % CurrentGroup = 0
1023
1024
DO i=1,NoNodes
1025
IF(DistanceValues(DistancePerm(i))>FreeConnect) CYCLE
1026
IF(CIndexValues(CIndexPerm(i))<0) CYCLE
1027
IF(FrontPerm(i) > 0) CYCLE
1028
1029
!Check if node is already in a group
1030
!This aint ideal, but I put it in to prevent a bug whereby, when surface and
1031
!basal groups join, all surface group nodes are ALSO basal group nodes, and so
1032
!'calving' is detected everywhere...
1033
FoundIt = .FALSE.
1034
DO locali = 1, SurfaceCrevasseGroups % CurrentGroup
1035
IF(ANY(SurfaceCrevasseGroups % NodeIndexes( &
1036
locali,1:SurfaceCrevasseGroups % NoNodes(locali)) .EQ. i)) THEN
1037
FoundIt = .TRUE.
1038
EXIT
1039
END IF
1040
END DO
1041
!Only check basal groups if they exist already...
1042
IF(ALLOCATED(BasalCrevasseGroups % NodeIndexes)) THEN
1043
DO locali = 1, BasalCrevasseGroups % CurrentGroup
1044
IF(ANY(BasalCrevasseGroups % NodeIndexes( &
1045
locali,1:BasalCrevasseGroups % NoNodes(locali)) .EQ. i)) THEN
1046
FoundIt = .TRUE.
1047
EXIT
1048
END IF
1049
END DO
1050
END IF
1051
IF(FoundIt) CYCLE
1052
1053
!This point is only reached by nodes which ARE crevassing
1054
!and not already in a group, so it takes the first slot of
1055
!active group
1056
CrevasseGroups % CurrentGroup = CrevasseGroups % CurrentGroup + 1
1057
CrevasseGroups % NodeIndexes(CrevasseGroups % CurrentGroup,1) = i
1058
CrevasseGroups % NotEmpty(CrevasseGroups % CurrentGroup) = .TRUE.
1059
CrevasseGroups % NoNodes(CrevasseGroups % CurrentGroup) = &
1060
CrevasseGroups % NoNodes(CrevasseGroups % CurrentGroup) + 1
1061
CrevasseGroupValues(CrevasseGroupPerm(i)) = CrevasseGroups % CurrentGroup
1062
NextSlot = 2
1063
CALL SearchNeighbours(i, CrevasseGroups)
1064
NextSlot = 1
1065
END DO
1066
1067
!Now we have all the groups. If requested, check they are valid.
1068
IF(CheckValidity) THEN
1069
DO i=1,CrevasseGroups % CurrentGroup !Cycle all groups
1070
DO j=1,SIZE(CrevasseGroups % NodeIndexes,2) !Cycle all nodes in group
1071
IF(CrevasseGroups % NodeIndexes(i,j)==0) EXIT !End of group
1072
IF(MaskPerm(CrevasseGroups % NodeIndexes(i,j))>0) THEN
1073
!At least 1 node in group is on relevant surface
1074
CrevasseGroups % Valid(i) = .TRUE.
1075
EXIT
1076
END IF
1077
END DO
1078
END DO
1079
END IF
1080
1081
END SUBROUTINE FindCrevasseGroups
1082
1083
!Recursively looks in neighbouring nodes to construct contiguous groups of
1084
!crevassing nodes.
1085
RECURSIVE SUBROUTINE SearchNeighbours(nodenum, CrevasseGroups)
1086
INTEGER :: nodenum, neighbourindex
1087
INTEGER :: localj,locali
1088
TYPE(CrevasseGroups_t) :: CrevasseGroups
1089
LOGICAL :: FoundIt
1090
1091
IF(Debug) PRINT *,'Debug, nextslot, nodenum: ', nextslot, nodenum
1092
DO localj = 1,NumNeighbours(nodenum)
1093
neighbourindex = NodeNeighbours(nodenum,localj)
1094
IF(DistanceValues(DistancePerm(neighbourindex))>FreeConnect) CYCLE
1095
IF(CIndexValues(CIndexPerm(neighbourindex))<0) CYCLE
1096
IF(FrontPerm(localj) > 0) CYCLE
1097
1098
!Check if node is already in a group
1099
!NOTE: is this actually possible?
1100
FoundIt = .FALSE.
1101
DO locali = 1, SurfaceCrevasseGroups % CurrentGroup
1102
IF(ANY(SurfaceCrevasseGroups % NodeIndexes(&
1103
locali,1:SurfaceCrevasseGroups % NoNodes(locali)) &
1104
.EQ. neighbourindex)) THEN
1105
FoundIt = .TRUE.
1106
EXIT
1107
END IF
1108
END DO
1109
IF(ALLOCATED(BasalCrevasseGroups % NodeIndexes)) THEN
1110
DO locali = 1, BasalCrevasseGroups % CurrentGroup
1111
IF(ANY(BasalCrevasseGroups % NodeIndexes(&
1112
locali,1:BasalCrevasseGroups % NoNodes(locali)) &
1113
.EQ. neighbourindex)) THEN
1114
FoundIt = .TRUE.
1115
EXIT
1116
END IF
1117
END DO
1118
END IF
1119
IF(FoundIt) CYCLE
1120
1121
!Only get here if node is valid new calving node, not already in a group
1122
CrevasseGroups % NodeIndexes(CrevasseGroups % CurrentGroup, NextSlot) = neighbourindex
1123
CrevasseGroups % NoNodes(CrevasseGroups % CurrentGroup) = &
1124
CrevasseGroups % NoNodes(CrevasseGroups % CurrentGroup) + 1
1125
CrevasseGroupValues(CrevasseGroupPerm(neighbourindex)) = CrevasseGroups % CurrentGroup
1126
NextSlot = NextSlot + 1
1127
IF(NextSlot > SIZE(CrevasseGroups % NodeIndexes, 2)) CALL Fatal(SolverName, &
1128
"More than 10000 nodes in a crevasse group? Almost certainly an error in setup")
1129
1130
CALL SearchNeighbours(neighbourindex,CrevasseGroups)
1131
!Add a check for group validity (i.e. at least one node is on the relevant boundary
1132
END DO
1133
END SUBROUTINE SearchNeighbours
1134
1135
SUBROUTINE FindCalvingBasal(SurfaceCrevasseGroups, BasalCrevasseGroups, BasalCalvingCoordinate)
1136
TYPE(CrevasseGroups_t), INTENT(IN) :: SurfaceCrevasseGroups, BasalCrevasseGroups
1137
REAL (KIND=dp), INTENT(OUT) :: BasalCalvingCoordinate
1138
INTEGER :: i,j,k,m, BasalNode, SurfaceNode
1139
1140
BasalCalvingOccurs = .FALSE.
1141
BasalCalvingCoordinate = HUGE(BasalCalvingCoordinate)
1142
!Cycle surface crevasse groups
1143
DO i = 1, COUNT(SurfaceCrevasseGroups % NotEmpty)
1144
IF(.NOT.(SurfaceCrevasseGroups % Valid(i))) CYCLE
1145
1146
!Cycle basal crevasse groups
1147
DO j = 1, COUNT(BasalCrevasseGroups % NotEmpty)
1148
IF(.NOT.(BasalCrevasseGroups % Valid(j))) CYCLE
1149
1150
!Cycle Nodes in Surface Crevasse Group
1151
DO k = 1,SIZE(SurfaceCrevasseGroups % NodeIndexes, 2)
1152
IF(SurfaceCrevasseGroups % NodeIndexes(i,k)==0) EXIT
1153
SurfaceNode = SurfaceCrevasseGroups % NodeIndexes(i,k)
1154
1155
!Cycle Nodes in Basal Crevasse Group
1156
DO m = 1,SIZE(BasalCrevasseGroups % NodeIndexes, 2)
1157
IF(BasalCrevasseGroups % NodeIndexes(j,m)==0) EXIT
1158
BasalNode = BasalCrevasseGroups % NodeIndexes(j,m)
1159
!Is it the same node? i.e. do the groups touch?
1160
IF(SurfaceNode == BasalNode) THEN
1161
!Yes, so calving coord
1162
IF(Mesh % Nodes % x(SurfaceNode) .LT. BasalCalvingCoordinate) THEN
1163
BasalCalvingCoordinate = Mesh % Nodes % x(SurfaceNode)
1164
BasalCalvingOccurs = .TRUE.
1165
!TEST
1166
IF(Debug) THEN
1167
PRINT *, "Debugging Calving, Surface node x: ", &
1168
Mesh % Nodes % x(SurfaceNode)," y: ",&
1169
Mesh % Nodes % y(SurfaceNode)
1170
PRINT *, "Debugging Calving, Basal node x: ", &
1171
Mesh % Nodes % x(BasalNode)," y: ",&
1172
Mesh % Nodes % y(BasalNode)
1173
END IF
1174
END IF
1175
ELSE
1176
!No, but are they neighbours?
1177
IF(ANY(NodeNeighbours(BasalNode,:)==SurfaceNode)) THEN
1178
!yes, neighbours
1179
CALL CheckCIndexOverlap(SurfaceNode, BasalNode, OverlapCalvingCoordinate, OverlapOccurs)
1180
IF(OverlapOccurs .AND. (OverlapCalvingCoordinate .LT. BasalCalvingCoordinate)) THEN
1181
BasalCalvingCoordinate = OverlapCalvingCoordinate
1182
BasalCalvingOccurs = .TRUE.
1183
END IF
1184
END IF
1185
!not neighbours, cycle
1186
END IF
1187
1188
END DO
1189
END DO
1190
END DO
1191
END DO
1192
END SUBROUTINE FindCalvingBasal
1193
1194
SUBROUTINE CheckCIndexOverlap(SurfaceNode, BasalNode, OverlapCoord, OverlapOccurs)
1195
1196
IMPLICIT NONE
1197
REAL(KIND=dp) :: CSurfSurf, CSurfBasal, CBasalSurf, &
1198
CBasalBasal, XSurf, YSurf, XBasal, YBasal, dx, dy,dxdy, dCSurf, &
1199
dCBasal, xzerobasal, yzerobasal, xzerosurf, yzerosurf, &
1200
dbindexdy,dsindexdy
1201
REAL(KIND=dp), INTENT(OUT) :: OverlapCoord
1202
INTEGER :: SurfaceNode, BasalNode
1203
LOGICAL, INTENT(OUT) :: OverlapOccurs
1204
1205
OverlapOccurs = .FALSE.
1206
1207
1208
!4 values at 2 nodes
1209
!Format is CIndexLocation. So CSurfBasal is the value of
1210
!CSurfIndex at the Basal node.
1211
1212
!This used to be simple linear interpolation between two nodes, but this was problematic:
1213
!For surface crevassing nodes, basal crevasse index is also positive, because they are
1214
!governed by the same equations. This lead to situations where both CBasalSurf and CBasalBasal
1215
!would be small positive, with an OBVIOUS massive gap inbetween, but because both were positive,
1216
!linear interp didn't see the gap.
1217
1218
!Strategy to overcome the previous:
1219
!No problem with CSurf* because it decreases with depth as expected.
1220
!Need to address CBasal*: at the lower node (C*Basal), assuming negligible upward changes in
1221
!stress_xx (any better way?), then the y-gradient of CBasal* is predictable, as the remaining
1222
!components decrease linearly with depth:
1223
! rho_w * g * dy - BTuningParam * rho_i * g * dy
1224
1225
!So, we replace the part of this subroutine which used to calculate the zero level of basal
1226
!crevassing. We need:
1227
!g, rho_i, rho_w, BTuningParam
1228
1229
!the various indices
1230
CSurfSurf = CSurfIndexValues(CSurfIndexPerm(SurfaceNode))
1231
CBasalSurf = CBasalIndexValues(CBasalIndexPerm(SurfaceNode))
1232
CSurfBasal = CSurfIndexValues(CSurfIndexPerm(BasalNode))
1233
CBasalBasal = CBasalIndexValues(CBasalIndexPerm(BasalNode))
1234
1235
!the coords
1236
XSurf = Mesh % Nodes % x(SurfaceNode)
1237
YSurf = Mesh % Nodes % y(SurfaceNode)
1238
XBasal = Mesh % Nodes % x(BasalNode)
1239
YBasal = Mesh % Nodes % y(BasalNode)
1240
1241
!The rest of the maths assumes that the surface node is ABOVE the basal node, so check:
1242
!If the node in the basal crevasse field is *above* the node in the surface crevasse field
1243
!we have calving and we assume its x-coord is between the two. else check...
1244
1245
IF(YSurf .LE. YBasal) THEN
1246
IF(Debug) PRINT *, 'DEBUG Calving: Basal Node above Surf Node, calving...'
1247
OverlapOccurs = .TRUE.
1248
OverlapCoord = (XSurf + XBasal)/2.0
1249
ELSE
1250
!Gradients
1251
dx = XSurf - XBasal
1252
dy = YSurf - YBasal !+ve
1253
dxdy = dx/dy
1254
1255
dCSurf = CSurfSurf - CSurfBasal !+ve
1256
dCBasal = CBasalSurf - CBasalBasal !-ve
1257
1258
dsindexdy = dCSurf/dy !+ve
1259
dbindexdy = -g*(RhoWF - Rho) !-ve
1260
1261
yzerobasal = YBasal - (CBasalBasal/dbindexdy)
1262
yzerosurf = YSurf - (CSurfSurf/dsindexdy)
1263
1264
IF(yzerosurf .LT. yzerobasal) THEN
1265
OverlapOccurs = .TRUE.
1266
1267
xzerobasal = xbasal + ((yzerobasal - YBasal)*dxdy)
1268
xzerosurf = xsurf + ((yzerosurf - YSurf)*dxdy)
1269
1270
OverlapCoord = MIN(xzerosurf, xzerobasal)
1271
OverlapCoord = MAX(OverlapCoord,MIN(XSurf, XBasal))
1272
ELSE
1273
OverlapOccurs = .FALSE.
1274
ENDIF
1275
END IF
1276
1277
IF(OverlapOccurs .AND. Debug) THEN
1278
PRINT *, "Overlap occurs!, OverlapCoord: ",OverlapCoord
1279
PRINT *, "Surf: ",SurfaceNode," x: ",Mesh % Nodes % x(SurfaceNode),&
1280
"y: ",Mesh % Nodes % y(SurfaceNode)
1281
PRINT *, "Base: ",BasalNode," x: ",Mesh % Nodes % x(BasalNode),&
1282
"y: ",Mesh % Nodes % y(BasalNode)
1283
1284
END IF
1285
1286
END SUBROUTINE CheckCIndexOverlap
1287
END SUBROUTINE Find_Calving
1288
1289