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! * Mesh adaptation & variable interpolation for 2D calving model. Original 27! * notes from Peter below: 28! * 29! * This solver may be used to map the field variables of a stretched mesh 30! * to the unstretched initial configuration of the same mesh. In addition 31! * the vertical size of the mesh is accommodated so that the height of 32! * the mesh stays fixed. All-in-all two different mapping stages and 33! * one solution of Laplace equation is needed for the mapping. 34! * The user may also optionally nullify some field variables such as the 35! * Mesh Update which should freshly start from the new configuration. 36! * 37! * Notes: 38! * ---------- 39! * This solver is currently only implemented for the 2D flowline case. 40! * For 3D calving, see the Calving3D test case. 41! * In places, the code would appear to permit 3D, but these haven't been 42! * properly implemented or tested. 43! * 44! * Currently InterpolateMeshToMesh will not report points for which 45! * interpolation failed (although InterpolateVarToVarReduced *will*). 46! * When the new variable is created, it's values are set to -HUGE() 47! * in the hope that it will be obvious to the user that a problem has 48! * occurred, though of course the long term solution should be to report 49! * failure... This is probably only a minor issue, as no points should 50! * ever be missing, as this would indicate a larger problem with 51! * this routine. 52! * 53! * In addition to dealing with the mesh manipulation required for a calving 54! * event, this subroutine (in conjunction with Calving.F90), can also modify 55! * the mesh when a frontal melting overhang becomes so severe as to force 56! * 'calving front' nodes below 'bed' nodes. This is done via the 'RemeshOccurs' 57! * switch. 58! ****************************************************************************** 59! * 60! * Authors: Peter R�back, Joe Todd 61! * Email: Peter.Raback@csc.fi 62! * Web: http://www.csc.fi/elmer 63! * Address: CSC - IT Center for Science Ltd. 64! * Keilaranta 14 65! * 02101 Espoo, Finland 66! * 67! * Original Date: 19.10.2010 68! * 69! ****************************************************************************/ 70 71SUBROUTINE TwoMeshes( Model,Solver,dt,TransientSimulation ) 72!------------------------------------------------------------------------------ 73 USE DefUtils 74 USE CRSMatrix 75 USE GeneralUtils 76 USE ElementDescription 77 USE MeshUtils 78 USE InterpVarToVar 79 80 IMPLICIT NONE 81!------------------------------------------------------------------------------ 82 TYPE(Solver_t) :: Solver 83 TYPE(Model_t) :: Model 84 REAL(KIND=dp) :: dt 85 LOGICAL :: TransientSimulation 86!------------------------------------------------------------------------------ 87! Local variables 88!------------------------------------------------------------------------------ 89 90 TYPE(Mesh_t), POINTER :: MeshA => NULL(), MeshB => NULL(), Mesh0 => Null(),& 91 MeshTop => Null(), MeshBot => Null() 92 TYPE(Variable_t), POINTER :: Var, Var2, VarNew, VarTop, VarBot, VarDisplace1, VarDisplace2, & 93 TopVar, BotVar, FrontVar, FirstBCVar, LastBodyVar 94 TYPE(Element_t), POINTER :: Element 95 TYPE(Nodes_t), POINTER :: NodesA, NodesB, Nodes0, NodesTop, NodesBot, PreNodes, PostNodes 96 CHARACTER(LEN=MAX_NAME_LEN) :: Name, VarName, TopMaskName, BotMaskName, FrontMaskName, SolverName 97 REAL(KIND=dp) :: BoundingBox(6),eps1,eps2, Norm, & 98 TopDisplacement, BotDisplacement, H, B, T, y, p, & 99 PreArea, PostArea, TotalLoss, BergLength, BergHeight 100 INTEGER :: i,j,n,dim,istat,HeightDim, NoNodes, TopNodes, BotNodes, & 101 OldBotNodes, FrontNodes, active, TopCornerIndex, BotCornerIndex, County 102 INTEGER, POINTER :: TopPerm(:), BotPerm(:), FrontPerm(:), HeightPerm(:), & 103 FieldPerm(:), WorkInt(:) 104 REAL(KIND=dp), POINTER :: TopValues(:), BotValues(:), FrontValues(:), Height(:), ForceVector(:), Field(:),AuxReal(:) 105 TYPE(Matrix_t), POINTER :: StiffMatrix 106 REAL(KIND=dp), ALLOCATABLE :: STIFF(:,:), FORCE(:) 107 LOGICAL :: Found,RebuiltQuadrantTree,FSTop,FSBot,NeedInit=.FALSE., & 108 MapCondition, RemeshCondition, Parallel, Debug = .FALSE., FirstTime = .TRUE. 109 LOGICAL, POINTER :: UnfoundNodes(:) 110 111 SAVE MeshA, MeshB, Mesh0, MeshTop, MeshBot, NodesA, NodesB, Nodes0, & 112 NodesTop, NodesBot, dim, HeightDim, TopCornerIndex, BotCornerIndex, & 113 TopPerm, BotPerm, FrontPerm, TopValues, BotValues, FrontValues, NoNodes,& 114 TopNodes, BotNodes, TopMaskName, BotMaskName, Height, HeightPerm, & 115 STIFF, FORCE, FirstTime, FSTop, FSBot, TotalLoss, PreNodes, PostNodes, & 116 FirstBCVar, LastBodyVar 117 118 INTERFACE 119 SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, & 120 NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes ) 121 !------------------------------------------------------------------------------ 122 USE Lists 123 USE SParIterComm 124 USE Interpolation 125 USE CoordinateSystems 126 !------------------------------------------------------------------------------- 127 TYPE(Mesh_t), TARGET :: OldMesh, NewMesh 128 TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables 129 LOGICAL, OPTIONAL :: UseQuadrantTree 130 LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:) 131 TYPE(Projector_t), POINTER, OPTIONAL :: Projector 132 CHARACTER(LEN=*),OPTIONAL :: MaskName 133 END SUBROUTINE InterpolateMeshToMesh 134 END INTERFACE 135 136!------------------------------------------------------------------------------ 137 138 Debug = .FALSE. 139 SolverName = "TwoMeshes" 140 141 CALL Info( SolverName, '-----------------------------------' ) 142 CALL Info( SolverName, ' Adapting the calving mesh...' ) 143 CALL Info( SolverName, '-----------------------------------' ) 144 145 dim = CoordinateSystemDimension() 146 IF(dim /= 2) THEN 147 CALL Fatal(SolverName, "This solver only works in 2D, sorry!") 148 END IF 149 !This is somewhat redundant but it permits the possibility of future generalisation 150 HeightDim = dim 151 152 153 Parallel = ParEnv % Pes > 1 154 IF(Parallel) CALL Fatal(SolverName, "Solver does not work in parallel yet!") 155 156 IF ( FirstTime ) THEN 157 FirstTime = .FALSE. 158 159 !variable to store total mass lost through calving 160 TotalLoss = 0.0_dp 161 162 !If both top and bottom surfaces are free, we simply interpolate 163 !them from the previous (t-1) timestep. However, repeated interpolation 164 !on a fixed surface will lead to artificial smoothing, so we must store 165 !the t=0 surface and interpolate from that each time. 166 FSTop = ListGetLogical( Solver % Values, 'FS Top', Found) 167 IF(.NOT. Found) THEN 168 Call Warn(SolverName,'No value "FS Top" found, assuming Free Surface at surface.') 169 FSTop = .TRUE. 170 END IF 171 172 FSBot = ListGetLogical( Solver % Values, 'FS Bottom', Found) 173 IF(.NOT. Found) THEN 174 Call Warn(SolverName,'No value "FS Bottom" found, assuming no Free Surface at bed.') 175 FSBot = .FALSE. 176 END IF 177 178 IF((.NOT. FSBot).OR.(.NOT. FSTop))THEN 179 NeedInit = .TRUE. 180 END IF 181 182 MeshA => Solver % Mesh 183 NodesA => MeshA % Nodes 184 185 MeshB => AllocateMesh() 186 MeshB = MeshA 187 MeshB % Name = TRIM(MeshA % Name)//'_copy' 188 189 CALL Info(SolverName,'Allocated a new copy of the mesh') 190 191 IF(NeedInit)THEN 192 Mesh0 => AllocateMesh() 193 Mesh0 = MeshA 194 Mesh0 % Name = TRIM(Solver % Mesh % Name)//'_initial' 195 CALL Info(SolverName,'Created initial reference mesh because at least one surface is fixed') 196 END IF 197 198 NoNodes = SIZE( MeshA % Nodes % x ) 199 NULLIFY( MeshB % Nodes ) 200 ALLOCATE( NodesB ) 201 ALLOCATE( NodesB % x(NoNodes), NodesB % y(NoNodes), NodesB % z(NoNodes) ) 202 NodesB % x = NodesA % x 203 NodesB % y = NodesA % y 204 NodesB % z = NodesA % z 205 MeshB % Nodes => NodesB 206 207 ALLOCATE( Nodes0 ) 208 ALLOCATE( Nodes0 % x(NoNodes), Nodes0 % y(NoNodes), Nodes0 % z(NoNodes) ) 209 Nodes0 % x = NodesA % x 210 Nodes0 % y = NodesA % y 211 Nodes0 % z = NodesA % z 212 IF(NeedInit) Mesh0 % Nodes => Nodes0 213 214 !Get the height variable for geometry interpolation 215 HeightPerm => Solver % Variable % Perm 216 Height => Solver % Variable % Values 217 218 !The convenience variables MeshTop/Bot and NodesTop/Bot remove the 219 !need for multiple IF statements later on. 220 !------------------------------------------------------------ 221 IF(FSTop) THEN 222 MeshTop => MeshA 223 ELSE 224 MeshTop => Mesh0 225 END IF 226 227 IF(FSBot) THEN 228 MeshBot => MeshA 229 ELSE 230 MeshBot => Mesh0 231 END IF 232 233 NodesTop => MeshTop % Nodes 234 NodesBot => MeshBot % Nodes 235 236 ! Nullify the list of variables to take use of the automatic 237 ! features of the mapping 238 NULLIFY( MeshB % Variables ) 239 240 ! Create the variables in MeshB 241 ! These need to be there for successful mapping 242 !-------------------------------------------------------------- 243 DO i=1,99 244 245 WRITE( Name, '(A,I0)') 'Variable ',i 246 VarName = ListGetString( Solver % Values,TRIM(Name),Found) 247 IF(.NOT. Found ) EXIT 248 249 Var => VariableGet( MeshA % Variables, TRIM( VarName), ThisOnly = .TRUE.) 250 IF( ASSOCIATED( Var) ) THEN 251 NULLIFY( Field, FieldPerm ) 252 ALLOCATE( Field(SIZE(Var % Values)), FieldPerm(SIZE(Var % Perm)) ) 253 254 !TODO InterpolateMeshToMesh should be made to report missing points in 255 !interpolation, as InterpVarToVar currently does. 256 Field = -HUGE(Field) 257 FieldPerm = Var % Perm 258 259 CALL VariableAdd( MeshB % Variables, MeshB, Solver, TRIM( VarName), 1, Field, FieldPerm ) 260 261 IF(ASSOCIATED(Var % PrevValues)) THEN 262 VarNew => VariableGet( MeshB % Variables, TRIM( VarName), ThisOnly = .TRUE. ) 263 ALLOCATE( VarNew % PrevValues ( SIZE( Var % PrevValues,1), & 264 SIZE( Var % PrevValues,2) ) ) 265 END IF 266 CALL Info(SolverName,'Created variable: '//TRIM( VarName ) ) 267 ELSE 268 WRITE(Message,'(a,a,a)') "Requested variable: ", TRIM(VarName), " but it wasn't found!" 269 CALL Warn(SolverName, Message) 270 END IF 271 END DO 272 273 DO i=1,99 274 !Variables created here (listed in the sif Solver section) are those 275 !which are only defined on the upper and lower surface 276 !They are interpolated using InterpolateVarToVarReduced 277 278 WRITE( Name, '(A,I0)') 'Surface Variable ',i 279 VarName = ListGetString( Solver % Values,TRIM(Name),Found) 280 IF(.NOT. Found ) EXIT 281 282 Var => VariableGet( MeshA % Variables, TRIM( VarName), ThisOnly = .TRUE. ) 283 284 IF( ASSOCIATED( Var) ) THEN 285 NULLIFY( Field, FieldPerm) 286 ALLOCATE( Field(SIZE(Var % Values)), FieldPerm(SIZE(Var % Perm)) ) 287 Field = Var % Values 288 !Field = 0.0_dp 289 FieldPerm = Var % Perm 290 291 CALL VariableAdd( MeshB % Variables, MeshB, Solver, TRIM( VarName), 1, Field, FieldPerm ) 292 293 IF(i==1) THEN 294 !Keep a handle on this so we can split variable list when interpolating 295 FirstBCVar => VariableGet( MeshB % Variables, TRIM( VarName), ThisOnly = .TRUE. ) 296 END IF 297 298 IF(ASSOCIATED(Var % PrevValues)) THEN 299 VarNew => VariableGet( MeshB % Variables, TRIM( VarName), ThisOnly = .TRUE. ) 300 ALLOCATE( VarNew % PrevValues ( SIZE( Var % PrevValues,1),& 301 SIZE( Var % PrevValues,2) ) ) 302 END IF 303 CALL Info(SolverName,'Created surface variable: '//TRIM( VarName ) ) 304 ELSE 305 WRITE(Message,'(a,a,a)') "Requested surface variable: ", & 306 TRIM(VarName), " but it wasn't found!" 307 CALL Warn(SolverName, Message) 308 END IF 309 END DO 310 311 !Handle to last body variable, same as above 312 Var => MeshB % Variables 313 DO WHILE(ASSOCIATED(Var)) 314 IF(ASSOCIATED(Var % Next, FirstBCVar)) THEN 315 LastBodyVar => Var 316 EXIT 317 END IF 318 Var => Var % Next 319 END DO 320 321 ! Create variable for the top surface 322 !--------------------------------------------------- 323 TopMaskName = 'Top Surface Mask' 324 TopVar => VariableGet(MeshTop % Variables, TopMaskName, ThisOnly=.TRUE.) 325 326 IF(ASSOCIATED(TopVar)) THEN 327 TopValues => TopVar % Values 328 TopPerm => TopVar % Perm 329 TopNodes = COUNT(TopPerm > 0) 330 ELSE 331 ALLOCATE( TopPerm(NoNodes) ) 332 CALL MakePermUsingMask( Model,Solver,MeshTop,TopMaskName, & 333 .FALSE., TopPerm, TopNodes ) 334 335 ALLOCATE( TopValues(TopNodes) ) 336 CALL VariableAdd( MeshTop % Variables, MeshTop, Solver, & 337 TopMaskName, 1, TopValues, TopPerm ) 338 END IF 339 340 ! Create variable for the bottom surface 341 !--------------------------------------------------- 342 BotMaskName = 'Bottom Surface Mask' 343 BotVar => VariableGet(MeshBot % Variables, BotMaskName, ThisOnly=.TRUE.) 344 345 IF(ASSOCIATED(BotVar)) THEN 346 BotValues => BotVar % Values 347 BotPerm => BotVar % Perm 348 BotNodes = COUNT(BotPerm > 0) 349 ELSE 350 ALLOCATE( BotPerm(NoNodes) ) 351 CALL MakePermUsingMask( Model,Solver,MeshBot,BotMaskName, & 352 .FALSE., BotPerm, BotNodes ) 353 354 ALLOCATE( BotValues(BotNodes) ) 355 CALL VariableAdd( MeshBot % Variables, MeshBot, Solver, & 356 BotMaskName, 1, BotValues, BotPerm ) 357 END IF 358 359 ! Create variable for the calving front 360 !--------------------------------------------------- 361 FrontMaskName = 'Calving Front Mask' 362 FrontVar => VariableGet(MeshBot % Variables, FrontMaskName, ThisOnly=.TRUE.) 363 364 IF(ASSOCIATED(FrontVar)) THEN 365 FrontValues => FrontVar % Values 366 FrontPerm => FrontVar % Perm 367 FrontNodes = COUNT(FrontPerm > 0) 368 ELSE 369 ALLOCATE( FrontPerm(NoNodes) ) 370 CALL MakePermUsingMask( Model,Solver,MeshBot,FrontMaskName, & 371 .FALSE., FrontPerm, FrontNodes ) 372 373 ALLOCATE( FrontValues(FrontNodes) ) 374 CALL VariableAdd( MeshA % Variables, MeshA, Solver, & 375 FrontMaskName, 1, FrontValues, FrontPerm ) 376 END IF 377 378 379 ! Find two corner nodes at calving front 380 !--------------------------------------------------- 381 DO i=1,NoNodes 382 IF(TopPerm(i) > 0 .AND. FrontPerm(i) > 0) THEN 383 TopCornerIndex = i 384 END IF 385 IF(BotPerm(i) > 0 .AND. FrontPerm(i) > 0) THEN 386 BotCornerIndex = i 387 END IF 388 END DO 389 390 n = MeshA % MaxElementNodes 391 ALLOCATE( FORCE(n), STIFF(n,n), STAT=istat ) 392 393 !Allocate node holders for calculating calving event size later 394 ALLOCATE(PreNodes,PostNodes) 395 ALLOCATE(PreNodes % x(2),& 396 PreNodes % y(2),& 397 PostNodes % x(2),& 398 PostNodes % y(2)) 399 400 END IF !FirstTime 401 402 403 ! Add a suitable condition here for calving 404 !--------------------------------------------- 405 ! This has been linked to the Calving.f90 solver by means of the "CalvingOccurs" 406 ! logical in the list. 407 MapCondition = ListGetLogical( Model % Simulation, 'CalvingOccurs', Found) 408 IF(.NOT. Found) THEN 409 CALL Warn("Two Meshes","Can't find CalvingOccurs Logical, exiting... ") 410 RETURN 411 END IF 412 413 RemeshCondition = ListGetLogical( Model % Simulation, 'RemeshOccurs', Found) 414 IF(.NOT. Found) THEN 415 CALL Warn("Two Meshes","Can't find RemeshCondition Logical, assuming false!") 416 RemeshCondition = .FALSE. 417 END IF 418 419 IF( .NOT. (MapCondition .OR. RemeshCondition)) RETURN 420 421 PreArea = ModelArea(MeshA) 422 423 !An array of 2 nodes, 1 being top, 2 bottom. 424 !This is stored to check if berg is tabular or not... 425 PreNodes % x(1) = MeshA % Nodes % x(TopCornerIndex) 426 PreNodes % y(1) = MeshA % Nodes % y(TopCornerIndex) 427 PreNodes % x(2) = MeshA % Nodes % x(BotCornerIndex) 428 PreNodes % y(2) = MeshA % Nodes % y(BotCornerIndex) 429 430 PRINT *,'Initial ranges' 431 PRINT *,'X0:',MINVAL( Nodes0 % x), MAXVAL( Nodes0 % x) 432 PRINT *,'XA:',MINVAL( NodesA % x), MAXVAL( NodesA % x) 433 PRINT *,'XB:',MINVAL( NodesB % x), MAXVAL( NodesB % x) 434 435 ! First copy the top and bottom (and front) height to variables 436 !---------------------------------------------------------- 437 DO i=1,NoNodes 438 j = TopPerm(i) 439 IF( j > 0 ) THEN 440 IF( HeightDim == 1 ) THEN 441 TopValues(j) = NodesTop % x(i) 442 ELSE IF( HeightDim == 2 ) THEN 443 TopValues(j) = NodesTop % y(i) 444 ELSE 445 TopValues(j) = NodesTop % z(i) 446 END IF 447 END IF 448 449 j = BotPerm(i) 450 IF( j > 0 ) THEN 451 IF( HeightDim == 1 ) THEN 452 BotValues(j) = NodesBot % x(i) 453 ELSE IF( HeightDim == 2 ) THEN 454 BotValues(j) = NodesBot % y(i) 455 ELSE 456 BotValues(j) = NodesBot % z(i) 457 END IF 458 END IF 459 460 j = FrontPerm(i) 461 IF( j > 0 ) THEN 462 IF( HeightDim == 1 ) THEN 463 FrontValues(j) = NodesA % x(i) 464 ELSE IF( HeightDim == 2 ) THEN 465 FrontValues(j) = NodesA % y(i) 466 ELSE 467 FrontValues(j) = NodesA % z(i) 468 END IF 469 END IF 470 END DO 471 472 ! Map the top and bottom variables to the reference mesh 473 ! For that purpose set the coordinates of MeshB to initial ones 474 ! Stretching may be accounted for here. 475 !-------------------------------------------------------------- 476 NodesB % x = NodesA % x 477 NodesB % y = NodesA % y 478 NodesB % z = NodesA % z 479 480 !At this point, our nodes are displaced due to calving. If we were dealing only 481 !with vertical calving faces and uniform calving retreat (i.e. a perfect rectangle 482 !falls off) then we can simply say NodesB % x = NodesB % x - CalvingEventSize 483 !However, non-vertical calving faces require a more complex treatment. 484 485 !FrontDisplacement returns the translation between current pre-calving node 486 !positions and the post-calving mesh whose node positions are the result of 487 !mesh displacement on the *initial* reference mesh. This preserves mesh 488 !quality after repeated calving/advance cycles. 489 VarDisplace1 => VariableGet(MeshA % Variables, 'Front Displacement 1', ThisOnly=.TRUE.) 490 VarDisplace2 => VariableGet(MeshA % Variables, 'Front Displacement 2', ThisOnly=.TRUE.) 491 492 NodesB % x = NodesA % x + VarDisplace1 % Values(VarDisplace1 % Perm) 493 NodesB % y = NodesA % y + VarDisplace2 % Values(VarDisplace2 % Perm) 494 495 !In the case where remeshing is required to deal with a severe melt overhang 496 !the bottom corner node may actually 'advance' (though no physical advance of 497 !the front actually takes place). 498 !In this case, its height must be interpolated from some severely tilted front 499 !nodes, and so we temporarily change their BotPerm here. 500 IF(RemeshCondition) THEN 501 IF(Debug) PRINT *, 'Debug Calving: RemeshCondition TRUE' 502 !Save BotNodes so it can be restored... 503 OldBotNodes = BotNodes 504 505 !Adding all frontnodes, except the bottom corner which is already in 506 BotNodes = BotNodes + FrontNodes - 1 507 508 ALLOCATE(AuxReal(BotNodes)) 509 AuxReal = BotValues !(BotNodes - OldBotNodes) slots left empty 510 511 County = 0 512 DO i=1,NoNodes 513 IF(FrontPerm(i) == 0) CYCLE !if it's not a front node 514 IF(BotPerm(i) /= 0) CYCLE !if it already exists in bottom variable 515 516 County = County + 1 517 BotPerm(i) = OldBotNodes + County 518 519 AuxReal(BotPerm(i)) = MeshA % Nodes % y(i) 520 IF(Debug) THEN 521 PRINT *, 'Debug ',SolverName,': Added NEW AuxReal nodenum: ',i,& 522 ' botperm: ',BotPerm(i),' and AuxReal: ',AuxReal(BotPerm(i)) 523 END IF 524 END DO 525 526 BotVar => VariableGet(MeshBot % Variables, BotMaskName, ThisOnly=.TRUE.) 527 BotVar % Values => AuxReal 528 529 END IF 530 531 !this is somewhat obtuse, but it allows InterpolateVarToVarReduced to 532 !reduce either 1 or 2 dimensions. 533 ALLOCATE(WorkInt(1)); WorkInt = HeightDim; 534 UnfoundNodes => NULL() 535 CALL InterpolateVarToVarReduced(MeshTop, MeshB, TopMaskName, WorkInt, & 536 UnfoundNodes, Variables=MeshA % Variables) 537 538 IF(ANY(UnfoundNodes)) CALL Fatal(SolverName,& 539 "Failed to find all nodes in top surface linesearch") 540 CALL Info(SolverName,'Interpolated the top values to the original mesh') 541 542 CALL InterpolateVarToVarReduced(MeshBot, MeshB, BotMaskName, WorkInt, & 543 UnfoundNodes, Variables=MeshA % Variables) 544 545 IF(ANY(UnfoundNodes)) CALL Fatal(SolverName,& 546 "Failed to find all nodes in bottom surface linesearch") 547 CALL Info(SolverName,'Interpolated the bottom values to the original mesh') 548 549 IF(RemeshCondition) THEN 550 VarBot => VariableGet(MeshB % Variables, BotMaskName, ThisOnly = .TRUE.) 551 !Reset surplus BotPerm values 552 DO i=1,NoNodes 553 IF(BotPerm(i) > OldBotNodes) THEN 554 BotPerm(i) = 0 555 VarBot % Perm(i) = 0 556 END IF 557 END DO 558 BotVar % Values => BotValues !Reset variable values from AuxReal 559 560 IF(Debug) THEN 561 PRINT *, 'Debug ',SolverName,', BotVar % Values: ', BotVar % Values 562 PRINT *, 'Debug ',SolverName,', BotVar % Perm: ', BotVar % Perm 563 END IF 564 END IF 565 566 ! Copy interpolated height variables back to TopValues and BotValues 567 !---------------------------------------------------------- 568 569 VarTop => VariableGet(MeshB % Variables, TopMaskName, ThisOnly = .TRUE.) 570 VarBot => VariableGet(MeshB % Variables, BotMaskName, ThisOnly = .TRUE.) 571 DO i=1,NoNodes 572 j = TopPerm(i) 573 IF( j > 0 ) THEN 574 TopValues(j) = VarTop % Values(VarTop % Perm(i)) 575 END IF 576 j = BotPerm(i) 577 IF( j > 0 ) THEN 578 BotValues(j) = VarBot % Values(VarBot % Perm(i)) 579 END IF 580 END DO 581 582 ! Find the y-shift in the calving front nodes 583 ! using the two end values from MeshA and B to guide the deformation 584 ! This is necessary for situations where the calving front is not 585 ! vertical, as the nodes will tend towards the outwardmost end otherwise 586 !---------------------------------------------------------- 587 588 H = Nodes0 % y(TopCornerIndex) - Nodes0 % y(BotCornerIndex) 589 T = Nodes0 % y(TopCornerIndex) 590 B = Nodes0 % y(BotCornerIndex) 591 592 TopDisplacement = VarTop % Values(VarTop % Perm(TopCornerIndex)) - NodesB % y(TopCornerIndex) 593 BotDisplacement = VarBot % Values(VarBot % Perm(BotCornerIndex)) - NodesB % y(BotCornerIndex) 594 595 DO i=1,NoNodes 596 j = FrontPerm(i) 597 IF( j > 0 ) THEN 598 y = Nodes0 % y(i) 599 !p is 1 at the top, 0 at the bottom 600 p = ((y-B)/H) 601 FrontValues(j) = NodesB % y(i) + (p*TopDisplacement) + ((1-p)*BotDisplacement) 602 END IF 603 END DO 604 605 606 ! Then map the mesh using Laplace equation in height direction 607 !---------------------------------------------------------- 608 CALL Info(SolverName,'Solving the height from poisson equation') 609 Solver % Mesh % Nodes => NodesB 610 611 CALL DefaultInitialize() 612 613 active = GetNOFActive() 614 DO i=1,active 615 Element => GetActiveElement(i) 616 n = GetElementNOFNodes() 617 CALL LocalMatrix( STIFF, FORCE, Element, n ) 618 CALL DefaultUpdateEquations( STIFF, FORCE ) 619 END DO 620 621 CALL DefaultFinishAssembly() 622 623 624 ! Set top and bottom Dirichlet BCs using the ones mapped from the 625 ! deformed mesh. This eliminates the need for external BCs. 626 !----------------------------------------------------------------- 627 StiffMatrix => Solver % Matrix 628 ForceVector => StiffMatrix % RHS 629 630 DO i=1, NoNodes 631 j = TopPerm(i) 632 IF( j > 0 ) THEN 633 CALL SetDirichtletPoint( StiffMatrix, ForceVector,1,1, & 634 HeightPerm, i, TopValues(j) ) 635 END IF 636 j = BotPerm(i) 637 IF( j > 0 ) THEN 638 CALL SetDirichtletPoint( StiffMatrix, ForceVector,1,1, & 639 HeightPerm, i, BotValues(j) ) 640 END IF 641 j = FrontPerm(i) 642 IF( j > 0 ) THEN 643 CALL SetDirichtletPoint( StiffMatrix, ForceVector,1,1, & 644 HeightPerm, i, FrontValues(j) ) 645 END IF 646 END DO 647 648 Norm = DefaultSolve() 649 650 ! Return the nodes to the original ones in which all other variables 651 ! have been computed in order not to case problems later on... 652 Solver % Mesh % Nodes => NodesA 653 654 ! Copy the height now to the original mesh deformed to comply 655 ! with the deformed top and bottom surfaces 656 !------------------------------------------------------------ 657 DO i=1,NoNodes 658 j = HeightPerm(i) 659 IF( j == 0 ) CYCLE 660 IF( HeightDim == 1 ) THEN 661 NodesB % x(i) = Height(j) 662 ELSE IF( HeightDim == 2 ) THEN 663 NodesB % y(i) = Height(j) 664 ELSE 665 NodesB % z(i) = Height(j) 666 END IF 667 END DO 668 669 670 ! Map the mesh to the real positions 671 ! Always built a new tree as MeshA has changed 672 ! This would ideally be built inside the interpolation... 673 !-------------------------------------------------------------- 674 RebuiltQuadrantTree = .TRUE. 675 IF( RebuiltQuadrantTree ) THEN 676 BoundingBox(1) = MINVAL( NodesA % x ) 677 BoundingBox(2) = MINVAL( NodesA % y ) 678 BoundingBox(3) = MINVAL( NodesA % z ) 679 BoundingBox(4) = MAXVAL( NodesA % x ) 680 BoundingBox(5) = MAXVAL( NodesA % y ) 681 BoundingBox(6) = MAXVAL( NodesA % z ) 682 683 eps1 = 0.1_dp 684 eps2 = eps1 * MAXVAL( BoundingBox(4:6) - BoundingBox(1:3) ) 685 BoundingBox(1:3) = BoundingBox(1:3) - eps2 686 BoundingBox(4:6) = BoundingBox(4:6) + eps2 687 688 IF(ASSOCIATED(MeshA % RootQuadrant)) CALL FreeQuadrantTree( MeshA % RootQuadrant ) 689 CALL BuildQuadrantTree( MeshA,BoundingBox,MeshA % RootQuadrant) 690 END IF 691 692 !Manipulate variable list to prevent BC variables being interpolated, 693 !they're already interpolated by InterpolateVarToVarReduced 694 LastBodyVar % Next => NULL() 695 696 ! This one uses the standard interpolation routines with two meshes 697 ! Note that the 4th argument (the new meshes variable) is never actually used. 698 CALL InterpolateMeshToMesh( MeshA, MeshB, MeshA % Variables,MeshB % Variables, .TRUE. ) 699 CALL Info('TwoMesh','Interpolation done') 700 701 !Restore variable list 702 LastBodyVar % Next => FirstBCVar 703 704 ! Now we still have the problem that the interpolated values sit on MeshB while 705 ! we would like to work with MeshA. It seems easiest to copy all the relevant 706 ! stuff back to MeshA. 707 !------------------------------------------------------------------------------ 708 709 NodesA % x = NodesB % x 710 NodesA % y = NodesB % y 711 NodesA % z = NodesB % z 712 713 714 Var => MeshB % Variables 715 DO WHILE(ASSOCIATED(Var)) 716 Var2 => VariableGet(MeshA % Variables, TRIM(Var % Name), ThisOnly = .TRUE.) 717 IF(.NOT. ASSOCIATED(Var2)) CALL Fatal(SolverName, "Error while copying back variables") 718 719 CALL Info(SolverName,'Copying back variable: '//TRIM( Var % Name ) ) 720 Var2 % Values = Var % Values 721 PRINT *,'Range',MINVAL( Var2 % Values), MAXVAL( Var2 % Values ) 722 723 Var => Var % Next 724 END DO 725 726 DO i=1,99 727 WRITE( Name, '(A,I0)') 'Nullify ',i 728 VarName = ListGetString( Solver % Values,TRIM(Name),Found) 729 IF(.NOT. Found ) EXIT 730 Var2 => VariableGet( MeshA % Variables, TRIM( VarName), ThisOnly = .TRUE. ) 731 IF( ASSOCIATED( Var2) ) THEN 732 CALL Info(SolverName,'Zeroing variable: '//TRIM( VarName ) ) 733 Var2 % Values = 0.0_dp 734 ELSE 735 WRITE(Message,'(a,a,a)') "Requested nullified variable: ", & 736 TRIM(VarName), " but it wasn't found!" 737 CALL Warn(SolverName, Message) 738 END IF 739 END DO 740 741 MeshA % Changed = .TRUE. 742 743 PRINT *,'Final ranges' 744 PRINT *,'X0:',MINVAL( Nodes0 % x), MAXVAL( Nodes0 % x) 745 PRINT *,'XA:',MINVAL( NodesA % x), MAXVAL( NodesA % x) 746 PRINT *,'XB:',MINVAL( NodesB % x), MAXVAL( NodesB % x) 747 748 !Calculate and print berg size 749 PostArea = ModelArea(MeshA) 750 TotalLoss = TotalLoss + (PreArea-Postarea) 751 752 WRITE(Message, '(a,E10.4,a)') 'Calving event size: ',PreArea-PostArea,' m2' 753 CALL Info(SolverName, Message) 754 WRITE(Message, '(a,E10.4,a)') 'Total mass lost through calving: ',TotalLoss,' m2' 755 CALL Info(SolverName, Message) 756 757 !Check if tabular berg or not. 758 PostNodes % x(1) = MeshA % Nodes % x(TopCornerIndex) 759 PostNodes % y(1) = MeshA % Nodes % y(TopCornerIndex) 760 PostNodes % x(2) = MeshA % Nodes % x(BotCornerIndex) 761 PostNodes % y(2) = MeshA % Nodes % y(BotCornerIndex) 762 763 BergLength = ((PreNodes % x(1) - PostNodes % x(1)) + (PreNodes % x(2) - PostNodes % x(2))) * 0.5 764 BergHeight = ((PreNodes % y(1) - PreNodes % y(2)) + ( PostNodes % y(1) - PostNodes % y(2))) * 0.5 765 766 IF(BergLength >= BergHeight) THEN 767 WRITE(Message,'(a)') 'Calving event type: tabular' 768 ELSE 769 WRITE(Message,'(a)') 'Calving event type: normal' 770 END IF 771 CALL Info(SolverName, Message) 772 773 DEALLOCATE(WorkInt, UnfoundNodes) 774 IF(RemeshCondition) DEALLOCATE(AuxReal) 775 776 CALL Info(SolverName,'All done') 777 778CONTAINS 779 780!------------------------------------------------------------------------------ 781 SUBROUTINE LocalMatrix( STIFF, FORCE, Element, n ) 782!------------------------------------------------------------------------------ 783 REAL(KIND=dp) :: STIFF(:,:), FORCE(:) 784 INTEGER :: n 785 TYPE(Element_t), POINTER :: Element 786!------------------------------------------------------------------------------ 787 REAL(KIND=dp) :: Basis(n),dBasisdx(n,3),DetJ 788 LOGICAL :: Stat 789 INTEGER :: t 790 TYPE(GaussIntegrationPoints_t) :: IP 791 792 TYPE(Nodes_t) :: Nodes 793 SAVE Nodes 794!------------------------------------------------------------------------------ 795 CALL GetElementNodes( Nodes ) 796 797 FORCE = 0.0_dp 798 STIFF = 0.0_dp 799 800 !Numerical integration: 801 !---------------------- 802 IP = GaussPoints( Element ) 803 DO t=1,IP % n 804 stat = ElementInfo( Element, Nodes, IP % U(t), IP % V(t), & 805 IP % W(t), detJ, Basis, dBasisdx ) 806 STIFF(1:n,1:n) = STIFF(1:n,1:n) + IP % s(t) * DetJ * & 807 MATMUL( dBasisdx, TRANSPOSE( dBasisdx ) ) 808 END DO 809!------------------------------------------------------------------------------ 810 END SUBROUTINE LocalMatrix 811 812 813!------------------------------------------------------------------------------ 814 SUBROUTINE SetDirichtletPoint( StiffMatrix, ForceVector,DOF, NDOFs, & 815 Perm, NodeIndex, NodeValue) 816!------------------------------------------------------------------------------ 817 818 IMPLICIT NONE 819 820 TYPE(Matrix_t), POINTER :: StiffMatrix 821 REAL(KIND=dp) :: ForceVector(:), NodeValue 822 INTEGER :: DOF, NDOFs, Perm(:), NodeIndex 823!------------------------------------------------------------------------------ 824 825 INTEGER :: PermIndex 826 REAL(KIND=dp) :: s 827 828!------------------------------------------------------------------------------ 829 830 PermIndex = Perm(NodeIndex) 831 832 IF ( PermIndex > 0 ) THEN 833 PermIndex = NDOFs * (PermIndex-1) + DOF 834 835 IF ( StiffMatrix % FORMAT == MATRIX_SBAND ) THEN 836 CALL SBand_SetDirichlet( StiffMatrix,ForceVector,PermIndex,NodeValue ) 837 ELSE IF ( StiffMatrix % FORMAT == MATRIX_CRS .AND. & 838 StiffMatrix % Symmetric ) THEN 839 CALL CRS_SetSymmDirichlet(StiffMatrix,ForceVector,PermIndex,NodeValue) 840 ELSE 841 s = StiffMatrix % Values(StiffMatrix % Diag(PermIndex)) 842 ForceVector(PermIndex) = NodeValue * s 843 CALL ZeroRow( StiffMatrix,PermIndex ) 844 CALL SetMatrixElement( StiffMatrix,PermIndex,PermIndex,1.0d0*s ) 845 END IF 846 END IF 847 848!------------------------------------------------------------------------------ 849 END SUBROUTINE SetDirichtletPoint 850!------------------------------------------------------------------------------ 851 852 FUNCTION ModelArea(Mesh) RESULT(A) 853 TYPE(Mesh_t), POINTER :: Mesh 854 INTEGER :: i 855 REAL(KIND=dp) :: A 856 857 A = 0.0_dp 858 859 DO i=1,Mesh % NumberOfBulkElements 860 861 A = A + ElementArea(Mesh, Mesh % Elements(i), & 862 Mesh % Elements(i) % Type % NumberOfNodes) 863 END DO 864 865 END FUNCTION ModelArea 866!------------------------------------------------------------------------------ 867END SUBROUTINE TwoMeshes 868