Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/MainUtils.F90
3203 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 library is free software; you can redistribute it and/or
8
! * modify it under the terms of the GNU Lesser General Public
9
! * License as published by the Free Software Foundation; either
10
! * version 2.1 of the License, or (at your option) any later version.
11
! *
12
! * This library 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 GNU
15
! * Lesser General Public License for more details.
16
! *
17
! * You should have received a copy of the GNU Lesser General Public
18
! * License along with this library (in file ../LGPL-2.1); if not, write
19
! * to the Free Software Foundation, Inc., 51 Franklin Street,
20
! * Fifth Floor, Boston, MA 02110-1301 USA
21
! *
22
! *****************************************************************************/
23
!
24
!/******************************************************************************
25
! *
26
! * Authors: Juha Ruokolainen, Ville Savolainen
27
! * Email: [email protected]
28
! * Web: http://www.csc.fi/elmer
29
! * Address: CSC - IT Center for Science Ltd.
30
! * Keilaranta 14
31
! * 02101 Espoo, Finland
32
! *
33
! * Original Date: 08 Jun 1997
34
! *
35
! *****************************************************************************/
36
37
!> \ingroup ElmerLib
38
!> \}
39
40
#include "../config.h"
41
!------------------------------------------------------------------------------
42
!> Utility routines for the Elmer main program.
43
!------------------------------------------------------------------------------
44
MODULE MainUtils
45
!------------------------------------------------------------------------------
46
USE BlockSolve
47
USE IterSolve, ONLY : NumericalError
48
USE LoadMod, ONLY : ExecLocalAssembly, ExecSolver
49
USE ModelDescription, ONLY : GetProcAddr
50
USE MatrixAssembly, ONLY : CreateChildMatrix, GlueLocalSubMatrix, MoveRow, &
51
SetMatrixElement
52
USE ElementDescription, ONLY : SwapRefElemNodes
53
USE ElementUtils, ONLY : CreateOdeMatrix, CreateMatrix
54
55
USE MeshUtils, ONLY : BackCoordinateTransformation, Colouring_deallocate, &
56
CoordinateTransformation, CreateDiscontMesh, ElmerColouringToGraph, &
57
ElmerGraphColour, ElmerMeshToDualGraph, Graph_deallocate, LoadMesh2, &
58
MakePermUsingMask, MeshStabParams, ReleaseMesh, SetActivEelementsTable, &
59
SetCurrentMesh, SetMeshMaxDOFs, SplitMeshEqual, TransferCoordAndTime, &
60
UpdateSolverMesh
61
62
USE SolverUtils, ONLY : CalculateEntityWeights, &
63
CalculateNodalWeights, CheckStepSize, ComputeChange, &
64
ComputeNorm, CreateIpPerm, GenerateProjectors, GetPassiveBoundary, &
65
InitializeTimestep, InitializeToZero, InvalidateVariable, &
66
MatrixVectorMultiply, UpdateDependentObjects, UpdateExportedVariables, &
67
FinalizeLumpedMatrix
68
69
USE DefUtils, ONLY : GetString, GetCReal, GetElementNOFNodes, GetLogical, &
70
DefaultDirichletBCs, GetMesh, GetInteger, GetMatrix, GetElementNOFNodes, &
71
GetBodyForce, GetBC, Default2ndOrderTime, DefaultFinishBulkAssembly, &
72
DefaultUpdateMass, DefaultFinishBoundaryAssembly, DefaultInitialize, &
73
DefaultUpdateDamp, DefaultFinishAssembly, Default1stOrderTime
74
!------------------------------------------------------------------------------
75
IMPLICIT NONE
76
!------------------------------------------------------------------------------
77
78
LOGICAL, PRIVATE :: isParallel=.FALSE.
79
80
CONTAINS
81
82
!------------------------------------------------------------------------------
83
!> Routine checks the feasibility of solver options.
84
!------------------------------------------------------------------------------
85
SUBROUTINE CheckLinearSolverOptions( Solver )
86
!------------------------------------------------------------------------------
87
TYPE(Solver_t) :: Solver
88
!------------------------------------------------------------------------------
89
TYPE(ValueList_t), POINTER :: Params
90
LOGICAL :: Found, Parallel
91
CHARACTER(:), ALLOCATABLE :: str
92
!------------------------------------------------------------------------------
93
94
Params => ListGetSolverParams(Solver)
95
Parallel = Solver % Parallel
96
97
str = ListGetString( Params,'Linear System Solver', Found )
98
99
IF ( str == 'direct' ) THEN
100
str = ListGetString( Params,'Linear System Direct Method', Found )
101
102
IF( Found ) THEN
103
IF ( Parallel ) THEN
104
IF ( str /= 'mumps' .AND. str /= 'cpardiso' .AND. str /= 'permon' ) THEN
105
CALL Warn( 'CheckLinearSolverOptions', 'Only MUMPS and CPardiso direct solver' // &
106
' interface implemented in parallel, trying MUMPS!')
107
str = 'mumps'
108
CALL ListAddString( Params,'Linear System Direct Method', str)
109
END IF
110
END IF
111
112
#if !defined (HAVE_UMFPACK) && defined (HAVE_MUMPS)
113
IF ( str == 'umfpack' .OR. str == 'big umfpack' ) THEN
114
CALL Warn( 'CheckLinearSolverOptions', 'UMFPACK solver not installed, using MUMPS instead!' )
115
str = 'mumps'
116
CALL ListAddString( Params,'Linear System Direct Method', str)
117
END IF
118
#endif
119
120
SELECT CASE( str )
121
CASE('banded' )
122
CASE( 'umfpack', 'big umfpack' )
123
#ifndef HAVE_UMFPACK
124
CALL Fatal( 'CheckLinearSolverOptions', 'UMFPACK (and MUMPS) solver has not been installed.' )
125
#endif
126
CASE( 'mumps', 'mumpslocal' )
127
#ifndef HAVE_MUMPS
128
CALL Fatal( 'CheckLinearSolverOptions', 'MUMPS solver has not been installed.' )
129
#endif
130
CASE( 'superlu' )
131
#ifndef HAVE_SUPERLU
132
CALL Fatal( 'CheckLinearSolverOptions', 'SuperLU solver has not been installed.' )
133
#endif
134
CASE( 'pardiso' )
135
#if !defined(HAVE_PARDISO) && !defined(HAVE_MKL)
136
CALL Fatal( 'CheckLinearSolverOptions', 'Pardiso solver has not been installed.' )
137
#endif
138
CASE( 'cpardiso')
139
#if !defined(HAVE_CPARDISO) || !defined(HAVE_MKL)
140
CALL Fatal( 'CheckLinearSolverOptions', ' Cluster Pardiso solver has not been installed.' )
141
#endif
142
CASE( 'cholmod','spqr' )
143
#ifndef HAVE_CHOLMOD
144
CALL Fatal( 'CheckLinearSolverOptions', 'Cholmod solver has not been installed.' )
145
#endif
146
CASE( 'permon')
147
#ifndef HAVE_FETI4I
148
CALL Fatal( 'CheckLinearSolverOptions', 'FETI4I solver has not been installed.' )
149
#endif
150
CASE DEFAULT
151
CALL Fatal( 'CheckLinearSolverOptions', 'Unknown direct solver method: ' // TRIM(str) )
152
END SELECT
153
154
ELSE
155
IF ( .NOT. Parallel ) THEN
156
#ifdef HAVE_UMFPACK
157
str = 'umfpack'
158
#else
159
str = 'banded'
160
#endif
161
ELSE
162
#ifdef HAVE_MUMPS
163
str = 'mumps'
164
#else
165
CALL Fatal( 'CheckLinearSolverOptions', 'There is no direct parallel solver available (MUMPS)')
166
#endif
167
END IF
168
CALL Info('CheckLinearSolverOptions',&
169
'Setting > Linear System Direct Method < to:'//TRIM(str), Level=6)
170
CALL ListAddString( Params,'Linear System Direct Method', str )
171
END IF
172
173
ELSE IF ( str == 'feti' ) THEN
174
IF( ParEnv % PEs <= 1 ) THEN
175
CALL Fatal('CheckLinearSolverOptions','Feti not usable in serial!')
176
END IF
177
178
ELSE
179
IF (ListGetLogical( Params, &
180
'Linear System Use Hypre', Found )) THEN
181
#ifndef HAVE_HYPRE
182
CALL Fatal('CheckLinearSolverOptions','Hypre requested but not compiled with!')
183
#endif
184
END IF
185
186
IF (ListGetLogical( Params, &
187
'Linear System Use Trilinos', Found )) THEN
188
IF( .NOT. Parallel ) THEN
189
CALL Fatal('CheckLinearSolverOptions','Trilinos not usable in serial!')
190
END IF
191
#ifndef HAVE_TRILINOS
192
CALL Fatal('CheckLinearSolverOptions','Trilinos requested but not compiled with!')
193
#endif
194
END IF
195
196
! Naming replacement: 'cholesky' for 'symmetric ILU'
197
str = ListGetString(Params, 'Linear System Preconditioning', Found)
198
IF ( LEN_TRIM(str) >= 8 ) THEN
199
IF( str(1:8) == 'cholesky') THEN
200
CALL ListAddString(Params, 'Linear System Preconditioning', 'ilu'//TRIM(str(9:)) )
201
CALL ListAddLogical(Params, 'Linear System Symmetric', .TRUE.)
202
CALL ListAddLogical(Params, 'Linear System Symmetric ILU', .TRUE.)
203
END IF
204
END IF
205
206
END IF
207
208
!------------------------------------------------------------------------------
209
END SUBROUTINE CheckLinearSolverOptions
210
!------------------------------------------------------------------------------
211
212
213
!------------------------------------------------------------------------------
214
! This subroutine enables the scaling of keywords by geometric entities.
215
! If any Real type keyword has an associated keyword with suffix 'Normalize by Area'
216
! or 'Normalize by Volume', or the real valued keyword has the prefix -dist
217
! the keyword will be divided with the area/volume.
218
!------------------------------------------------------------------------------
219
SUBROUTINE SetNormalizedKeywords(Model, Mesh)
220
TYPE(Model_t) :: Model
221
TYPE(Mesh_t), POINTER :: Mesh
222
223
INTEGER :: cnt
224
LOGICAL :: Found
225
226
cnt = Model % NumberOfDistTags
227
228
! Initialize the count if not done before
229
IF( cnt == -1 ) THEN
230
cnt = ListTagCount(Model,.TRUE.)
231
Model % NumberOfDistTags = cnt
232
END IF
233
234
! If no tags nothing to do
235
IF( cnt == 0 ) RETURN
236
237
! If the weights have been already computed for linear cases no need to redo
238
IF( Mesh % EntityWeightsComputed ) THEN
239
IF( .NOT. ListGetLogical( Model % Simulation,&
240
'Update Keyword Normalization',Found ) ) RETURN
241
END IF
242
243
! Calculate entity weights
244
CALL CalculateEntityWeights( Model, Mesh )
245
246
! Tag count for entity normalization tags, 2nd and 3rd parameter neglected
247
! for this USE CASE!
248
CALL ListSetParameters( Model, 0, 0.0_dp, .FALSE., Found )
249
250
END SUBROUTINE SetNormalizedKeywords
251
!------------------------------------------------------------------------------
252
253
254
!------------------------------------------------------------------------------
255
! This subroutine enables the rotation of vector valued keywords.
256
!------------------------------------------------------------------------------
257
SUBROUTINE SetRotatedProperties(Model, Mesh)
258
TYPE(Model_t) :: Model
259
TYPE(Mesh_t), POINTER :: Mesh
260
261
LOGICAL :: AnyBC, AnyBodyForce, AnyMat, AnyBody
262
INTEGER :: list_ind
263
REAL(KIND=dp) :: RotMatrix(3,3), Angles(3,1)
264
REAL(KIND=dp), POINTER :: Pmatrix(:,:)
265
INTEGER, TARGET :: NodeIndex(1)
266
INTEGER, POINTER :: NodeIndexes(:)
267
REAL(KIND=dp) :: EntityWeight
268
LOGICAL :: Found
269
TYPE(ValueList_t), POINTER :: List
270
LOGICAL :: Visited = .FALSE.
271
CHARACTER(:), ALLOCATABLE :: Suffix
272
273
SAVE Visited, Suffix, AnyBC, AnyBodyForce, AnyMat, AnyBody
274
275
IF(.NOT. Visited ) THEN
276
Suffix = 'Property Rotate'
277
AnyBC = ListCheckSuffixAnyBC( Model, Suffix )
278
AnyBodyForce = ListCheckSuffixAnyBodyForce( Model, Suffix )
279
AnyMat = ListCheckSuffixAnyMaterial( Model, Suffix )
280
AnyBody = ListCheckSuffixAnyBody( Model, Suffix )
281
Visited = .TRUE.
282
END IF
283
284
IF( .NOT. ( AnyBC .OR. AnyBodyForce .OR. AnyMat .OR. AnyBody ) ) RETURN
285
286
NodeIndex(1) = 1
287
NodeIndexes => NodeIndex
288
289
IF( AnyBody ) THEN
290
DO list_ind = 1,Model % NumberOfBodies
291
List => Model % Bodies(list_ind) % Values
292
IF(.NOT. ListCheckSuffix( List, Suffix ) ) CYCLE
293
294
CALL ListGetRealVector( List,'Property Rotate',Angles,1,NodeIndexes,Found )
295
IF(.NOT. Found ) THEN
296
CALL Fatal('SetRotatedProperties','> Property Rotate < suffix requires angles!')
297
END IF
298
299
RotMatrix = AnglesToRotationMatrix( Angles(1:3,1) )
300
301
PMatrix => ListGetConstRealArray( List,'Property Rotation Matrix',Found )
302
IF( Found ) THEN
303
PMatrix = RotMatrix(3,3)
304
ELSE
305
CALL ListAddConstRealArray( List,'Property Rotation Matrix', 3, 3, RotMatrix )
306
END IF
307
END DO
308
END IF
309
310
311
IF( AnyBodyForce ) THEN
312
DO list_ind = 1,Model % NumberOfBodyForces
313
List => Model % BodyForces(list_ind) % Values
314
IF(.NOT. ListCheckSuffix( List, Suffix ) ) CYCLE
315
316
CALL ListGetRealVector( List,'Property Rotate',Angles,1,NodeIndexes,Found )
317
IF(.NOT. Found ) THEN
318
CALL Fatal('SetRotatedProperties','> Property Rotate < suffix requires angles!')
319
END IF
320
321
RotMatrix = AnglesToRotationMatrix( Angles(1:3,1) )
322
323
PMatrix => ListGetConstRealArray( List,'Property Rotation Matrix',Found )
324
IF( Found ) THEN
325
PMatrix = RotMatrix(3,3)
326
ELSE
327
CALL ListAddConstRealArray( List,'Property Rotation Matrix', 3, 3, RotMatrix )
328
END IF
329
END DO
330
END IF
331
332
333
IF( AnyMat ) THEN
334
DO list_ind = 1,Model % NumberOfMaterials
335
List => Model % Materials(list_ind) % Values
336
IF(.NOT. ListCheckSuffix( List, Suffix ) ) CYCLE
337
338
CALL ListGetRealVector( List,'Property Rotate',Angles,1,NodeIndexes,Found )
339
IF(.NOT. Found ) THEN
340
CALL Fatal('SetRotatedProperties','> Property Rotate < suffix requires angles!')
341
END IF
342
343
RotMatrix = AnglesToRotationMatrix( Angles(1:3,1) )
344
345
PMatrix => ListGetConstRealArray( List,'Property Rotation Matrix',Found )
346
IF( Found ) THEN
347
PMatrix = RotMatrix(3,3)
348
ELSE
349
CALL ListAddConstRealArray( List,'Property Rotation Matrix', 3, 3, RotMatrix )
350
END IF
351
END DO
352
END IF
353
354
355
IF( AnyBC ) THEN
356
DO list_ind = 1,Model % NumberOfBCs
357
List => Model % BCs(list_ind) % Values
358
IF(.NOT. ListCheckSuffix( List, Suffix ) ) CYCLE
359
360
CALL ListGetRealVector( List,'Property Rotate',Angles,1,NodeIndexes,Found )
361
IF(.NOT. Found ) THEN
362
CALL Fatal('SetRotatedProperties','> Property Rotate < suffix requires angles!')
363
END IF
364
365
RotMatrix = AnglesToRotationMatrix( Angles(1:3,1) )
366
367
PMatrix => ListGetConstRealArray( List,'Property Rotation Matrix',Found )
368
IF( Found ) THEN
369
PMatrix = RotMatrix(3,3)
370
ELSE
371
CALL ListAddConstRealArray( List,'Property Rotation Matrix', 3, 3, RotMatrix )
372
END IF
373
END DO
374
END IF
375
376
CONTAINS
377
378
FUNCTION AnglesToRotationMatrix( Angles, RotateOrder ) RESULT ( RotMatrix )
379
380
REAL(KIND=dp) :: Angles(3), RotMatrix(3,3)
381
INTEGER, OPTIONAL :: RotateOrder(3)
382
383
INTEGER :: i,j
384
REAL(KIND=dp) :: TrfMatrix(3,3), IdentityMatrix(3,3), Alpha
385
386
IdentityMatrix = 0.0_dp
387
DO i=1,3
388
IdentityMatrix(i,i) = 1.0_dp
389
END DO
390
391
RotMatrix = IdentityMatrix
392
393
DO i=1,3
394
j = i
395
IF( PRESENT( RotateOrder ) ) j = RotateOrder(i)
396
Alpha = Angles(j) * PI / 180.0_dp
397
398
IF( ABS(Alpha) < TINY(Alpha) ) CYCLE
399
TrfMatrix = IdentityMatrix
400
401
SELECT CASE(j)
402
CASE(1)
403
TrfMatrix(2,2) = COS(Alpha)
404
TrfMatrix(2,3) = -SIN(Alpha)
405
TrfMatrix(3,2) = SIN(Alpha)
406
TrfMatrix(3,3) = COS(Alpha)
407
CASE(2)
408
TrfMatrix(1,1) = COS(Alpha)
409
TrfMatrix(1,3) = -SIN(Alpha)
410
TrfMatrix(3,1) = SIN(Alpha)
411
TrfMatrix(3,3) = COS(Alpha)
412
CASE(3)
413
TrfMatrix(1,1) = COS(Alpha)
414
TrfMatrix(1,2) = -SIN(Alpha)
415
TrfMatrix(2,1) = SIN(Alpha)
416
TrfMatrix(2,2) = COS(Alpha)
417
END SELECT
418
419
RotMatrix = MATMUL( RotMatrix, TrfMatrix )
420
END DO
421
422
END FUNCTION AnglesToRotationMatrix
423
424
425
END SUBROUTINE SetRotatedProperties
426
!------------------------------------------------------------------------------
427
428
429
430
431
!------------------------------------------------------------------------------
432
!> Get calling address of the procedure and add it to the Solver structure.
433
!------------------------------------------------------------------------------
434
SUBROUTINE AddSolverProcedure( Solver,PROCEDURE )
435
!------------------------------------------------------------------------------
436
TYPE(Solver_t) :: Solver
437
EXTERNAL :: PROCEDURE
438
INTEGER :: PROCEDURE
439
!------------------------------------------------------------------------------
440
INTEGER(KIND=AddrInt) :: AddrFunc
441
EXTERNAL :: AddrFunc
442
!------------------------------------------------------------------------------
443
Solver % PROCEDURE = AddrFunc( PROCEDURE )
444
!------------------------------------------------------------------------------
445
END SUBROUTINE AddSolverProcedure
446
!------------------------------------------------------------------------------
447
448
449
450
!------------------------------------------------------------------------------
451
SUBROUTINE SwapMesh(Model,Mesh,Name,ExtMesh)
452
!------------------------------------------------------------------------------
453
TYPE(Model_t) :: Model
454
TYPE(Mesh_t), POINTER :: Mesh
455
CHARACTER(LEN=*), OPTIONAL :: Name
456
TYPE(Mesh_t), POINTER, OPTIONAL :: ExtMesh
457
!------------------------------------------------------------------------------
458
TYPE(Mesh_t), POINTER :: Newmesh, tmesh
459
INTEGER :: Def_Dofs(10,6), i,j,k
460
LOGICAL :: Found, Transient
461
TYPE(Solver_t), POINTER :: Solver
462
!------------------------------------------------------------------------------
463
464
INTERFACE
465
SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, &
466
NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes )
467
USE Lists
468
USE SParIterComm
469
USE Interpolation
470
USE CoordinateSystems
471
USE MeshUtils, ONLY: ReleaseMesh
472
TYPE(Mesh_t), TARGET :: OldMesh, NewMesh
473
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables
474
LOGICAL, OPTIONAL :: UseQuadrantTree
475
TYPE(Projector_t), POINTER, OPTIONAL :: Projector
476
CHARACTER(LEN=*),OPTIONAL :: MaskName
477
LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:)
478
END SUBROUTINE InterpolateMeshToMesh
479
END INTERFACE
480
481
482
Def_Dofs = -1;
483
DO i=1,Model % NumberOfSolvers
484
DO j=1,10
485
DO k=1,6
486
Def_Dofs(j,k) = MAX(Def_Dofs(j,k), MAXVAL(Model % Solvers(i) % Def_Dofs(j,:,k)))
487
END DO
488
END DO
489
END DO
490
491
IF( PRESENT( ExtMesh ) ) THEN
492
NewMesh => ExtMesh
493
ELSE
494
Newmesh => LoadMesh2( Model, Name, Name, &
495
.FALSE., Parenv % PEs, ParEnv % myPE, Def_Dofs )
496
NewMesh % Name = Name
497
END IF
498
499
IF(.NOT.ASSOCIATED(NewMesh)) RETURN
500
501
NewMesh % Next => Mesh % Next
502
IF(ASSOCIATED(Mesh, Model % Meshes)) THEN
503
Model % Meshes => Newmesh
504
ELSE
505
Tmesh => Model % Meshes
506
DO WHILE(ASSOCIATED(Tmesh % next))
507
IF(ASSOCIATED(Mesh, Tmesh % next)) THEN
508
Tmesh % Next => Newmesh
509
EXIT
510
END IF
511
END DO
512
END IF
513
514
CALL TransferCoordAndTime(Mesh,NewMesh)
515
516
DO i=1,Model % NumberOfSolvers
517
Solver => Model % Solvers(i)
518
IF(ASSOCIATED(Solver % Mesh, Mesh)) Solver % Mesh => Newmesh
519
END DO
520
521
IF(Mesh % DiscontMesh) CALL CreateDiscontMesh(Model,Newmesh,.TRUE.)
522
523
IF(ASSOCIATED(Model % Mesh, Mesh)) Model % Mesh => NewMesh
524
IF(ASSOCIATED(Model % Variables, Mesh % Variables)) Model % Variables => NewMesh % Variables
525
526
Mesh % Next => Null()
527
528
Transient = ListGetString( Model % Simulation, 'Simulation Type' ) == 'transient'
529
530
DO i=1,Model % NumberOfSolvers
531
Solver => Model % Solvers(i)
532
IF(ASSOCIATED(Solver % Mesh, NewMesh)) THEN
533
CALL FreeMatrix(Solver % Matrix)
534
Model % Solver => Solver
535
536
CALL AddEquationBasics( Solver, ListGetString(Solver % Values, &
537
'Variable', Found), Transient )
538
CALL AddEquationSolution( Solver, Transient )
539
IF ( Transient .AND. Solver % PROCEDURE /= 0 ) CALL InitializeTimestep(Solver)
540
END IF
541
END DO
542
543
IF( Transient ) THEN
544
IF(ParEnv % PEs > 1) CALL ParallelActive(.TRUE.)
545
!Free quadrant tree to ensure its rebuilt in InterpolateMeshToMesh (bug fix)
546
CALL FreeQuadrantTree(Mesh % RootQuadrant)
547
CALL InterpolateMeshToMesh( Mesh, NewMesh, Mesh % Variables, NewMesh % Variables )
548
END IF
549
550
551
CALL ReleaseMesh( Mesh )
552
553
CALL MeshStabParams( Newmesh )
554
NewMesh % Changed = .TRUE.
555
556
!------------------------------------------------------------------------------
557
END SUBROUTINE SwapMesh
558
!------------------------------------------------------------------------------
559
560
561
SUBROUTINE CheckAndCreateDGIndexes( Mesh, ActiveElem )
562
TYPE(Mesh_t), POINTER :: Mesh
563
LOGICAL, OPTIONAL :: ActiveElem(:)
564
565
TYPE(Element_t), POINTER :: Element
566
INTEGER :: i,n,t,DGIndex
567
LOGICAL :: Failed
568
569
Failed = .FALSE.
570
571
DO t=1,Mesh % NumberOfBulkElements
572
IF( PRESENT( ActiveElem ) ) THEN
573
IF( .NOT. ActiveElem(t) ) CYCLE
574
END IF
575
Element => Mesh % Elements(t)
576
n = Element % TYPE % NumberOfNodes
577
IF( .NOT. ASSOCIATED( Element % DGIndexes ) ) THEN
578
Failed = .TRUE.
579
ELSE IF( SIZE( Element % DGIndexes ) /= n ) THEN
580
Failed = .TRUE.
581
END IF
582
END DO
583
IF(.NOT. Failed) RETURN
584
585
! Number the bulk indexes such that each node gets a new index
586
CALL Info('CheckAndCreateDGIndexes','Creating DG indexes!',Level=12)
587
588
DGIndex = 0
589
DO t=1,Mesh % NumberOfBulkElements
590
Element => Mesh % Elements(t)
591
n = Element % TYPE % NumberOfNodes
592
IF( .NOT. ASSOCIATED( Element % DGIndexes ) ) THEN
593
ALLOCATE( Element % DGindexes( n ) )
594
ELSE IF( SIZE( Element % DGIndexes ) /= n ) THEN
595
DEALLOCATE( Element % DGindexes )
596
ALLOCATE( Element % DGindexes( n ) )
597
END IF
598
599
DO i=1,n
600
DGIndex = DGIndex + 1
601
Element % DGIndexes(i) = DGIndex
602
END DO
603
END DO
604
605
! Number the bulk indexes such that each node gets a new index
606
CALL Info('CheckAndCreateDGIndexes','Creating DG '//I2S(DgIndex)//' indexes',Level=6)
607
608
END SUBROUTINE CheckAndCreateDGIndexes
609
610
611
612
!> Create permutation for discontinuous galerking type of fields optionally
613
!> with a given mask.
614
!-----------------------------------------------------------------------------------
615
SUBROUTINE CreateDGPerm( Solver, DGPerm, DGCount, MaskName, SecName )
616
617
TYPE(Solver_t), POINTER :: Solver
618
INTEGER, POINTER :: DGPerm(:)
619
INTEGER :: DGCount
620
CHARACTER(LEN=*), OPTIONAL :: MaskName, SecName
621
622
TYPE(Mesh_t), POINTER :: Mesh
623
TYPE(Element_t), POINTER :: Element, Parent
624
INTEGER :: t, n, i, j, k, DGIndex
625
LOGICAL :: Found, ActiveElem, HaveSome
626
TYPE(ValueList_t), POINTER :: BF
627
CHARACTER(:), ALLOCATABLE :: EquationName
628
629
630
CALL Info('CreateDGPerm','Creating permutation for DG variable',Level=12)
631
632
EquationName = ListGetString( Solver % Values, 'Equation', Found)
633
IF( .NOT. Found ) THEN
634
CALL Fatal('CreateDGPerm','Equation not present!')
635
END IF
636
637
Mesh => Solver % Mesh
638
DGIndex = 0
639
640
! Check whether the DG indexes might already have been allocated
641
HaveSome = .FALSE.
642
DO t=1,Mesh % NumberOfBulkElements
643
Element => Mesh % Elements(t)
644
IF( ASSOCIATED( Element % DGIndexes ) ) THEN
645
HaveSome = .TRUE.
646
EXIT
647
END IF
648
END DO
649
650
! If they are allocated somewhere check that they are good for this variable as well
651
IF( HaveSome ) THEN
652
CALL Info('CreateDGPerm','There are at least some associated DG elements',Level=15)
653
DO t=1,Mesh % NumberOfBulkElements + Mesh % NumberOFBoundaryElements
654
Element => Mesh % Elements(t)
655
IF( Element % PartIndex == ParEnv % myPE ) THEN
656
IF ( CheckElementEquation( CurrentModel, Element, EquationName ) ) THEN
657
658
IF( PRESENT( MaskName ) ) THEN
659
BF => ListGetSection( Element, SecName )
660
ActiveElem = ListGetLogicalGen( BF, MaskName )
661
ELSE
662
ActiveElem = .TRUE.
663
END IF
664
665
IF( ActiveElem ) THEN
666
IF( .NOT. ASSOCIATED( Element % DGIndexes ) ) THEN
667
CALL Fatal('CreateDGPerm','Either all or none of DGIndexes should preexist!')
668
END IF
669
END IF
670
END IF
671
END IF
672
END DO
673
674
! Find out the maximum index for the size of the permutation
675
DO t=1,Mesh % NumberOfBulkElements
676
Element => Mesh % Elements(t)
677
DGIndex = MAX( DGIndex, MAXVAL( Element % DGIndexes ) )
678
END DO
679
END IF
680
681
682
! If they have not been allocated before the allocate DGIndexes for all bulk elements
683
IF(.NOT. HaveSome ) THEN
684
CALL Info('CreateDGPerm','Creating DG indexes for bulk elements',Level=15)
685
686
! Number the bulk indexes such that each node gets a new index
687
DGIndex = 0
688
DO t=1,Mesh % NumberOfBulkElements !+ Mesh % NumberOfBoundaryElements
689
Element => Mesh % Elements(t)
690
n = Element % TYPE % NumberOfNodes
691
ALLOCATE( Element % DGindexes( n ) )
692
DO i=1, n
693
DGIndex = DGIndex + 1
694
Element % DGIndexes(i) = DGIndex
695
END DO
696
END DO
697
698
! Make boundary elements to inherit the bulk indexes
699
! We neglect this as for now since it seems this causes problems in deallocation later on...
700
#if 1
701
DO t=Mesh % NumberOfBulkElements + 1, &
702
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
703
Element => Mesh % Elements(t)
704
n = Element % TYPE % NumberOfNodes
705
706
IF( .NOT. ASSOCIATED( Element % BoundaryInfo ) ) CYCLE
707
DO k=1,2
708
IF( k == 1 ) THEN
709
Parent => Element % BoundaryInfo % Left
710
ELSE
711
Parent => Element % BoundaryInfo % Right
712
END IF
713
IF(.NOT. ASSOCIATED( Parent ) ) CYCLE
714
IF( .NOT. ASSOCIATED( Parent % DGIndexes ) ) CYCLE
715
716
IF( .NOT. ASSOCIATED( Element % DGIndexes ) ) THEN
717
ALLOCATE( Element % DGIndexes(n) )
718
Element % DgIndexes = 0
719
END IF
720
DO i = 1, n
721
IF( Element % DGIndexes(i) > 0 ) CYCLE
722
DO j = 1, Parent % TYPE % NumberOfNodes
723
IF( Element % NodeIndexes(i) == Parent % NodeIndexes(j) ) THEN
724
Element % DGIndexes(i) = Parent % DGIndexes(j)
725
EXIT
726
END IF
727
END DO
728
END DO
729
IF( ANY( Element % DGIndexes == 0 ) ) PRINT *,'t dg:',t,n,Element % DGIndexes
730
EXIT
731
END DO
732
END DO
733
#endif
734
END IF
735
736
CALL Info('CreateDGPerm','Size of DgPerm table: '//I2S(DGIndex),Level=12)
737
738
ALLOCATE( DGPerm( DGIndex ) )
739
DGPerm = 0
740
741
! If we consider boundary element above do that also here
742
DO t=1,Mesh % NumberOfBulkElements + Mesh % NumberOFBoundaryElements
743
Element => Mesh % Elements(t)
744
745
IF( Element % PartIndex == ParEnv % myPE ) THEN
746
IF ( CheckElementEquation( CurrentModel, Element, EquationName ) ) THEN
747
748
IF( PRESENT( MaskName ) ) THEN
749
BF => ListGetSection( Element, SecName )
750
ActiveElem = ListGetLogicalGen( BF, MaskName )
751
ELSE
752
ActiveElem = .TRUE.
753
END IF
754
755
IF( ActiveElem ) THEN
756
n = Element % TYPE % NumberOfNodes
757
DGPerm( Element % DGIndexes ) = 1
758
END IF
759
760
END IF
761
END IF
762
END DO
763
764
DGCount = 0
765
DO i=1, DGIndex
766
IF( DGPerm(i) > 0 ) THEN
767
DGCount = DGCount + 1
768
DGPerm(i) = DGCount
769
END IF
770
END DO
771
772
CALL Info('CreateDGPerm','Created permutation for DG nodes: '//I2S(DgCount),Level=8)
773
774
END SUBROUTINE CreateDGPerm
775
776
777
!> Simple nodal permutation without any mask or other complications.
778
!> For the more complex version there is MakePermUsingMask.
779
!> This simple one could be used when primary variable is DG field and we want
780
!> to create a nodal exported variable.
781
!-----------------------------------------------------------------------------------
782
SUBROUTINE CreateNodalPerm( Solver, NodalPerm, nSize )
783
784
TYPE(Solver_t), POINTER :: Solver
785
INTEGER, POINTER :: NodalPerm(:)
786
INTEGER :: nSize
787
788
TYPE(Mesh_t), POINTER :: Mesh
789
TYPE(Element_t), POINTER :: Element
790
INTEGER :: t, n, i, j
791
LOGICAL :: Found
792
CHARACTER(:), ALLOCATABLE :: EquationName
793
794
CALL Info('CreateNodalPerm','Creating simple permutation for nodal variable',Level=12)
795
796
EquationName = ListGetString( Solver % Values, 'Equation', Found)
797
IF( .NOT. Found ) THEN
798
CALL Fatal('CreateNodalPerm','Equation not present!')
799
END IF
800
801
Mesh => Solver % Mesh
802
n = Mesh % NumberOfNodes
803
804
ALLOCATE( NodalPerm(n) )
805
NodalPerm = 0
806
807
DO t=1,Mesh % NumberOfBulkElements + Mesh % NumberOFBoundaryElements
808
Element => Mesh % Elements(t)
809
IF ( CheckElementEquation( CurrentModel, Element, EquationName ) ) THEN
810
NodalPerm( Element % NodeIndexes ) = 1
811
END IF
812
END DO
813
814
j = 0
815
DO i=1,n
816
IF( NodalPerm(i) > 0 ) THEN
817
j = j + 1
818
NodalPerm(i) = j
819
END IF
820
END DO
821
nSize = j
822
823
CALL Info('CreateNodalPerm','Number of active nodes in NodalPerm: '//I2S(nSize),Level=12)
824
825
END SUBROUTINE CreateNodalPerm
826
827
828
829
!> Create permutation for fields on elements, optional using mask
830
!-----------------------------------------------------------------
831
SUBROUTINE CreateElementsPerm( Solver, Perm, nsize, MaskName, SecName )
832
TYPE(Solver_t),POINTER :: Solver
833
INTEGER, POINTER :: Perm(:)
834
INTEGER :: nsize
835
CHARACTER(LEN=*), OPTIONAL :: MaskName, SecName
836
837
INTEGER :: t, n, m
838
TYPE(Element_t), POINTER :: Element
839
LOGICAL :: Found, ActiveElem
840
TYPE(Mesh_t), POINTER :: Mesh
841
TYPE(ValueList_t), POINTER :: BF
842
CHARACTER(:), ALLOCATABLE :: EquationName
843
844
CALL Info('CreateElementsPerm','Creating permutation for elemental fields',Level=8)
845
846
EquationName = ListGetString( Solver % Values, 'Equation', Found)
847
IF( .NOT. Found ) THEN
848
CALL Fatal('CreateElementsPerm','Equation not present!')
849
END IF
850
851
Mesh => Solver % Mesh
852
853
854
NULLIFY( Perm )
855
n = Mesh % NumberOfBulkElements + Mesh % NumberOFBoundaryElements
856
ALLOCATE( Perm(n) )
857
Perm = 0
858
859
860
m = 0
861
DO t=1,n
862
Element => Solver % Mesh % Elements(t)
863
IF( Element % PartIndex == ParEnv % myPE ) THEN
864
IF ( CheckElementEquation( CurrentModel, Element, EquationName ) ) THEN
865
IF( PRESENT( MaskName ) ) THEN
866
BF => ListGetSection( Element, SecName )
867
ActiveElem = ListGetLogicalGen( BF, MaskName )
868
ELSE
869
ActiveElem = .TRUE.
870
END IF
871
END IF
872
873
IF( ActiveElem ) THEN
874
m = m + 1
875
Perm(t) = m
876
END IF
877
END IF
878
END DO
879
880
CALL Info('CreateElementsPerm','Number of active elements in permutation: '//I2S(m),Level=8)
881
882
nsize = m
883
884
END SUBROUTINE CreateElementsPerm
885
886
887
888
!> Create permutation table that follows the degrees of freedom of the primary
889
!> permutation but is masked by a flag on the body force section.
890
!---------------------------------------------------------------------------------
891
SUBROUTINE CreateMaskedPerm( Solver, FullPerm, MaskName, MaskPerm, nsize, SecName )
892
893
TYPE(Solver_t), POINTER :: Solver
894
INTEGER, POINTER :: FullPerm(:)
895
CHARACTER(LEN=*) :: MaskName
896
INTEGER, POINTER :: MaskPerm(:)
897
INTEGER :: nsize
898
CHARACTER(LEN=*), OPTIONAL :: SecName
899
900
TYPE(Mesh_t), POINTER :: Mesh
901
TYPE(Element_t), POINTER :: Element
902
INTEGER :: t, n, m
903
TYPE(ValueList_t), POINTER :: BF
904
LOGICAL :: Found, ActiveElem
905
INTEGER, ALLOCATABLE :: Indexes(:)
906
CHARACTER(:), ALLOCATABLE :: EquationName
907
908
909
CALL Info('CreateMaskedPerm','Creating variable with mask: '//TRIM(MaskName),Level=8)
910
911
Mesh => Solver % Mesh
912
NULLIFY( MaskPerm )
913
914
n = SIZE( FullPerm )
915
ALLOCATE( MaskPerm( n ), Indexes(100) )
916
917
MaskPerm = 0
918
919
DO t=1,Mesh % NumberOfBulkElements + Mesh % NumberOFBoundaryElements
920
Element => Mesh % Elements(t)
921
922
IF( ParEnv % PEs > 1 ) THEN
923
IF( t <= Mesh % NumberOfBulkElements ) THEN
924
IF( Element % PartIndex /= ParEnv % myPE ) CYCLE
925
END IF
926
END IF
927
928
IF( PRESENT( SecName ) ) THEN
929
BF => ListGetSection( Element, SecName )
930
ELSE
931
IF( t <= Mesh % NumberOfBulkElements ) THEN
932
BF => ListGetSection( Element,'body force')
933
ELSE
934
BF => ListGetSection( Element,'boundary condition')
935
END IF
936
END IF
937
938
939
IF(.NOT. ASSOCIATED( BF ) ) CYCLE
940
941
ActiveElem = ListGetLogicalGen( BF, MaskName )
942
943
IF( ActiveElem ) THEN
944
MaskPerm( Element % NodeIndexes ) = FullPerm( Element % NodeIndexes )
945
END IF
946
947
END DO
948
949
m = 0
950
DO t=1,n
951
IF( MaskPerm( t ) > 0 ) THEN
952
m = m + 1
953
MaskPerm( t ) = m
954
END IF
955
END DO
956
957
nsize = m
958
959
CALL Info('CreateMaskedPerm','Created masked permutation for dofs: '//I2S(nsize),Level=8)
960
961
END SUBROUTINE CreateMaskedPerm
962
963
!------------------------------------------------------------------
964
! Check for special solvers, to be executed only
965
! at a certain instances during the simulation:
966
!------------------------------------------------------------------
967
SUBROUTINE AddExecWhenFlag(Solver)
968
TYPE(Solver_t), POINTER :: Solver
969
970
TYPE(ValueList_t), POINTER :: SolverParams
971
LOGICAL :: Found
972
CHARACTER(:), ALLOCATABLE :: str
973
974
SolverParams => ListGetSolverParams(Solver)
975
976
! Default value
977
Solver % SolverExecWhen = SOLVER_EXEC_ALWAYS
978
979
str = ListGetString( SolverParams, 'Exec Solver', Found )
980
981
IF( Found ) THEN
982
SELECT CASE( TRIM(str) )
983
CASE( 'never' )
984
Solver % SolverExecWhen = SOLVER_EXEC_NEVER
985
CASE( 'always' )
986
Solver % SolverExecWhen = SOLVER_EXEC_ALWAYS
987
CASE( 'after simulation', 'after all' )
988
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_ALL
989
CASE( 'before simulation', 'before all' )
990
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_ALL
991
CASE( 'before timestep' )
992
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_TIME
993
CASE( 'after timestep' )
994
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_TIME
995
CASE( 'before saving' )
996
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_SAVE
997
CASE( 'after saving' )
998
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_SAVE
999
CASE( 'predictor-corrector' )
1000
Solver % SolverExecWhen = SOLVER_EXEC_PREDCORR
1001
CASE( 'when created' )
1002
Solver % SolverExecWhen = SOLVER_EXEC_WHENCREATED
1003
CASE( 'after control' )
1004
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_CONTROL
1005
CASE DEFAULT
1006
CALL Fatal('AddExecWhenFlag','Unknown "exec solver" flag: '//TRIM(str))
1007
END SELECT
1008
ELSE
1009
IF ( ListGetLogical( SolverParams, 'Before All', Found ) ) THEN
1010
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_ALL
1011
ELSE IF ( ListGetLogical( SolverParams, 'Before Simulation', Found ) ) THEN
1012
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_ALL
1013
ELSE IF ( ListGetLogical( SolverParams, 'After All', Found ) ) THEN
1014
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_ALL
1015
ELSE IF ( ListGetLogical( SolverParams, 'After Simulation', Found ) ) THEN
1016
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_ALL
1017
ELSE IF ( ListGetLogical( SolverParams, 'Before Timestep', Found ) ) THEN
1018
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_TIME
1019
ELSE IF ( ListGetLogical( SolverParams, 'After Timestep', Found ) ) THEN
1020
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_TIME
1021
ELSE IF ( ListGetLogical( SolverParams, 'Before Saving', Found ) ) THEN
1022
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_SAVE
1023
ELSE IF ( ListGetLogical( SolverParams, 'After Saving', Found ) ) THEN
1024
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_SAVE
1025
ELSE IF ( ListGetLogical( SolverParams, 'Predictor-Corrector', Found ) ) THEN
1026
Solver % SolverExecWhen = SOLVER_EXEC_PREDCORR
1027
ELSE IF ( ListGetLogical( SolverParams, 'When Created', Found ) ) THEN
1028
Solver % SolverExecWhen = SOLVER_EXEC_WHENCREATED
1029
ELSE IF ( ListGetLogical( SolverParams, 'After Control', Found ) ) THEN
1030
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_CONTROL
1031
END IF
1032
END IF
1033
1034
1035
END SUBROUTINE AddExecWhenFlag
1036
1037
1038
1039
!------------------------------------------------------------------------------
1040
!> Add the generic stuff related to each Solver.
1041
!> A few solvers are for historical reasons given a special treatment.
1042
!------------------------------------------------------------------------------
1043
SUBROUTINE AddEquationBasics( Solver, Name, Transient )
1044
!------------------------------------------------------------------------------
1045
USE CoordinateSystems
1046
TYPE(Solver_t), POINTER :: Solver
1047
LOGICAL :: Transient
1048
CHARACTER(LEN=*) :: Name
1049
!------------------------------------------------------------------------------
1050
REAL(KIND=dp), POINTER :: Solution(:)
1051
1052
INTEGER, POINTER :: Perm(:)
1053
1054
INTEGER(KIND=AddrInt) :: InitProc, AssProc
1055
1056
INTEGER :: MaxDGDOFs, MaxNDOFs, MaxEDOFs, MaxFDOFs, MaxBDOFs, MaxDOFsPerNode
1057
INTEGER :: i,j,k,l,NDeg,Nrows,nSize,n,m,DOFs,dim,MatrixFormat,istat,Maxdim, AllocStat, &
1058
i1,i2,i3,iostat
1059
1060
LOGICAL :: Found, Stat, BandwidthOptimize, EigAnal, ComplexFlag, &
1061
MultigridActive, VariableOutput, GlobalBubbles, HarmonicAnal, MGAlgebraic, &
1062
VariableGlobal, VariableIP, VariableElem, VariableDG, VariableNodal, &
1063
DG, NoMatrix, IsAssemblySolver, IsCoupledSolver, IsBlockSolver, IsProcedure, &
1064
IsStepsSolver, LegacySolver, UseMask, TransientVar, InheritVarType, DoIt, &
1065
GotSecName, NoPerm
1066
1067
! CHARACTER(LEN=MAX_NAME_LEN) :: var_name
1068
CHARACTER(:), ALLOCATABLE :: proc_name, tmpname, mask_name,sec_name,eq,str,var_name
1069
1070
TYPE(ValueList_t), POINTER :: SolverParams
1071
TYPE(Mesh_t), POINTER :: NewMesh,OldMesh
1072
TYPE(Element_t), POINTER :: CurrentElement
1073
TYPE(Matrix_t), POINTER :: NewMatrix, Oldmatrix
1074
1075
TYPE(Variable_t), POINTER :: Var, PVar
1076
TYPE(Variable_t), POINTER :: NewVariable
1077
1078
REAL(KIND=dp) :: tt, InitValue
1079
REAL(KIND=dp), POINTER :: Component(:)
1080
1081
TYPE(Graph_t) :: DualGraph
1082
TYPE(GraphColour_t) :: GraphColouring, BoundaryGraphColouring
1083
LOGICAL :: ConsistentColours
1084
1085
LOGICAL :: ThreadedStartup, MultiColourSolver, HavePerm, Parallel
1086
INTEGER :: VariableType
1087
1088
CHARACTER(*), PARAMETER :: Caller="AddEquationBasics"
1089
1090
1091
! Set pointer to the list of solver parameters
1092
!------------------------------------------------------------------------------
1093
SolverParams => ListGetSolverParams(Solver)
1094
1095
!------------------------------------------------------------------------------
1096
! Check the historical solvers that may be built-in on some .sif files
1097
! Therefore some special strategies are used for them.
1098
!------------------------------------------------------------------------------
1099
IsProcedure = ListCheckPresent( SolverParams, 'Procedure' )
1100
Dim = CoordinateSystemDimension()
1101
Dofs = 1
1102
InitValue = 0.0_dp
1103
LegacySolver = .TRUE.
1104
1105
SELECT CASE( Name )
1106
!------------------------------------------------------------------------------
1107
1108
!------------------------------------------------------------------------------
1109
CASE('navier-stokes')
1110
!------------------------------------------------------------------------------
1111
dofs = dim
1112
IF ( CurrentCoordinateSystem() == CylindricSymmetric ) DOFs = DOFs + 1
1113
IF( dofs == 3 ) THEN
1114
var_name = 'Flow Solution[Velocity:3 Pressure:1]'
1115
ELSE
1116
var_name = 'Flow Solution[Velocity:2 Pressure:1]'
1117
END IF
1118
proc_name = 'FlowSolve FlowSolver'
1119
InitValue = 1.0d-6
1120
! We don't want to use automated setting of dofs later so set this back to one!
1121
dofs = 1
1122
1123
!------------------------------------------------------------------------------
1124
CASE('magnetic induction')
1125
!------------------------------------------------------------------------------
1126
var_name = 'Magnetic Field'
1127
proc_name = 'MagneticSolve MagneticSolver'
1128
dofs = 3
1129
CALL ListAddString( SolverParams,&
1130
NextFreeKeyword('Exported Variable',SolverParams),&
1131
'Electric Current[Electric Current:3]')
1132
1133
!------------------------------------------------------------------------------
1134
CASE('stress analysis')
1135
!------------------------------------------------------------------------------
1136
dofs = dim
1137
var_name = 'Displacement'
1138
proc_name = 'StressSolve StressSolver'
1139
1140
!------------------------------------------------------------------------------
1141
CASE('mesh update')
1142
!------------------------------------------------------------------------------
1143
dofs = dim
1144
var_name = 'Mesh Update'
1145
proc_name = 'MeshSolve MeshSolver'
1146
1147
IF( Transient ) THEN
1148
IF( Dofs == 2 ) THEN
1149
CALL ListAddString( SolverParams,&
1150
NextFreeKeyword('Exported Variable',SolverParams),&
1151
'-dofs 2 Mesh Velocity')
1152
ELSE
1153
CALL ListAddString( SolverParams,&
1154
NextFreeKeyword('Exported Variable',SolverParams),&
1155
'-dofs 3 Mesh Velocity')
1156
END IF
1157
END IF
1158
1159
!------------------------------------------------------------------------------
1160
CASE('heat equation')
1161
!------------------------------------------------------------------------------
1162
var_name = 'Temperature'
1163
proc_name = 'HeatSolve HeatSolver'
1164
1165
CALL ListAddNewLogical( SolverParams,'Radiation Solver',.TRUE.)
1166
!------------------------------------------------------------------------------
1167
1168
CASE DEFAULT
1169
LegacySolver = .FALSE.
1170
1171
END SELECT
1172
1173
IF( LegacySolver ) THEN
1174
CALL Info(Caller,'Setting up keywords internally for legacy solver: '&
1175
//TRIM(Name),Level=10)
1176
IF( .NOT. ListCheckPresent( SolverParams,'Variable') ) THEN
1177
CALL ListAddString( SolverParams,'Variable',var_name )
1178
IF( Dofs > 1 ) CALL ListAddInteger( SolverParams,'Variable Dofs',dofs )
1179
END IF
1180
IF( .NOT. IsProcedure ) THEN
1181
CALL ListAddString(SolverParams, 'Procedure', proc_name,.FALSE.)
1182
END IF
1183
END IF
1184
1185
! We should have the procedure
1186
proc_name = ListGetString( SolverParams, 'Procedure',IsProcedure)
1187
IF( IsProcedure ) THEN
1188
CALL Info(Caller,'Using procedure: '//TRIM(proc_name),Level=10)
1189
ELSE
1190
CALL Warn(Caller,'Solver '//I2S(Solver % SolverId)//' may require "Procedure" to operate!')
1191
END IF
1192
1193
1194
#if 0
1195
! Here we can for testing purposes change all solver named xxx to yyy
1196
! and then rerun all the tests, for example. Don't activate unless for this kind
1197
! of development work.
1198
i = INDEX( proc_name,'HeatSolver')
1199
IF( i /= 0 ) THEN
1200
proc_name = 'HeatSolveVec HeatSolver'
1201
CALL ListAddString(SolverParams, 'Procedure', proc_name,.FALSE.)
1202
CALL Info(Caller,'Using procedure: '//TRIM(proc_name),Level=6)
1203
END IF
1204
#endif
1205
1206
! Mark whether the solver will be treated as parallel in future
1207
! Mainly related to the linear system solution since assembly is trivially parallel.
1208
! There are some special cases where we have same mesh for multiple instances of almost
1209
! same independent problem.
1210
Parallel = ( ParEnv % PEs > 1 )
1211
IF( Parallel ) THEN
1212
IF(Solver % Mesh % SingleMesh ) THEN
1213
Parallel = ListGetLogical( SolverParams,'Enforce Parallel',Found )
1214
IF(.NOT. Found) THEN
1215
Parallel = ListGetLogical( CurrentModel % Simulation,'Enforce Parallel',Found )
1216
END IF
1217
END IF
1218
END IF
1219
Solver % Parallel = Parallel
1220
1221
1222
! If there is a matrix level Flux Corrected Transport and/or nonlinear timestepping
1223
! then you must use global matrices for time integration.
1224
!----------------------------------------------------------------------------------
1225
DoIt = .FALSE.
1226
IF( ListGetLogical( SolverParams,'Linear System FCT',Found ) ) DoIt = .TRUE.
1227
IF( ListGetLogical( SolverParams,'Nonlinear Timestepping',Found ) ) DoIt = .TRUE.
1228
IF( ListGetLogical( SolverParams,'Apply Conforming BCs',Found ) ) DoIt = .TRUE.
1229
1230
IF( DoIt ) THEN
1231
CALL Info(Caller,'Enforcing use of global mass matrix needed by other features!')
1232
CALL ListAddLogical( SolverParams,'Use Global Mass Matrix',.TRUE.)
1233
END IF
1234
1235
! Compute the mesh dimension for this solver
1236
!----------------------------------------------------------------------------
1237
eq = ListGetString( SolverParams, 'Equation', Found )
1238
IF( Found ) THEN
1239
CALL Info(Caller,'Setting up solver: '//TRIM(eq),Level=8)
1240
END IF
1241
1242
IF ( Found ) THEN
1243
MAXdim = 0
1244
DO i=1,Solver % Mesh % NumberOfBulkElements+Solver % Mesh % NumberOFBoundaryElements
1245
CurrentElement => Solver % Mesh % Elements(i)
1246
IF ( CheckElementEquation( CurrentModel, CurrentElement, eq ) ) THEN
1247
Maxdim = MAX( CurrentElement % TYPE % DIMENSION, Maxdim )
1248
END IF
1249
END DO
1250
CALL ListAddInteger( SolverParams, 'Active Mesh Dimension', Maxdim )
1251
END IF
1252
1253
! Check the solver for initialization
1254
! This is utilized only by some solvers.
1255
!-----------------------------------------------------------------
1256
IF( IsProcedure ) THEN
1257
InitProc = GetProcAddr( TRIM(proc_name)//'_Init', abort=.FALSE. )
1258
CALL Info(Caller,'Checking for _init solver',Level=12)
1259
IF ( InitProc /= 0 ) THEN
1260
CALL ExecSolver( InitProc, CurrentModel, Solver, &
1261
Solver % dt, Transient )
1262
END IF
1263
END IF
1264
1265
Solver % SolverMode = SOLVER_MODE_DEFAULT
1266
IF( ListGetLogical( SolverParams, 'Auxiliary Solver', Found ) ) &
1267
Solver % SolverMode = SOLVER_MODE_AUXILIARY
1268
IF( ListGetLogical( SolverParams, 'Coupled Solver', Found ) ) &
1269
Solver % SolverMode = SOLVER_MODE_COUPLED
1270
IF( ListGetLogical( SolverParams, 'Block Solver', Found ) ) &
1271
Solver % SolverMode = SOLVER_MODE_BLOCK
1272
IF( ListGetLogical( SolverParams, 'Assembly Solver', Found ) ) &
1273
Solver % SolverMode = SOLVER_MODE_ASSEMBLY
1274
1275
IF( Solver % SolverMode == SOLVER_MODE_DEFAULT ) THEN
1276
eq = ListGetString( Solver % Values, 'Equation', Found )
1277
IF(.NOT. Found ) THEN
1278
Solver % SolverMode = SOLVER_MODE_AUXILIARY
1279
ELSE
1280
AssProc = GetProcAddr( TRIM(proc_name)//'_bulk', abort=.FALSE. )
1281
CALL Info(Caller,'Checking for _bulk solver',Level=12)
1282
IF ( AssProc /= 0 ) THEN
1283
CALL Info(Caller,'Solver will be be performed in steps',Level=8)
1284
Solver % SolverMode = SOLVER_MODE_STEPS
1285
END IF
1286
END IF
1287
END IF
1288
1289
IsCoupledSolver = ( Solver % SolverMode == SOLVER_MODE_COUPLED )
1290
IsBlockSolver = ( Solver % SolverMode == SOLVER_MODE_BLOCK )
1291
IsAssemblySolver = ( Solver % SolverMode == SOLVER_MODE_ASSEMBLY )
1292
IsAssemblySolver = IsAssemblySolver .OR. &
1293
( IsCoupledSolver .AND. .NOT. IsProcedure ) .OR. &
1294
( IsBlockSolver .AND. .NOT. IsProcedure )
1295
IsStepsSolver = ( Solver % SolverMode == SOLVER_MODE_STEPS )
1296
1297
! Get the procudure that really runs the solver
1298
!------------------------------------------------------------------------------
1299
IF( IsProcedure ) THEN
1300
IF( IsStepsSolver ) THEN
1301
Solver % PROCEDURE = AssProc
1302
ELSE
1303
Solver % PROCEDURE = GetProcAddr(proc_name)
1304
END IF
1305
END IF
1306
1307
! Default order of equation
1308
!--------------------------
1309
Solver % Order = 1
1310
Solver % TimeOrder = 1
1311
1312
! Set up time-stepping strategies for transient problems
1313
!------------------------------------------------------------------------------
1314
IF ( Transient ) THEN
1315
str = ListGetString( SolverParams, 'Predictor Method',Found )
1316
IF ( .NOT. Found ) THEN
1317
str = ListGetString( CurrentModel % Simulation, 'Predictor Method',Found )
1318
IF ( Found ) THEN
1319
CALL ListAddString( SolverParams, 'Predictor Method', str )
1320
END IF
1321
END IF
1322
1323
str = ListGetString( SolverParams, 'Corrector Method',Found )
1324
IF ( .NOT. Found ) THEN
1325
str = ListGetString( CurrentModel % Simulation, 'Corrector Method',Found )
1326
IF ( Found ) THEN
1327
CALL ListAddString( SolverParams, 'Corrector Method', str )
1328
END IF
1329
END IF
1330
1331
IF ( Found ) THEN
1332
CALL ReadPredCorrParams( CurrentModel, SolverParams )
1333
CALL ListAddString( SolverParams, 'Timestepping Method', str )
1334
END IF
1335
1336
str = ListGetString( SolverParams, 'Timestepping Method',Found )
1337
IF ( .NOT. Found ) THEN
1338
str = ListGetString( CurrentModel % Simulation, 'Timestepping Method',Found )
1339
IF ( Found ) THEN
1340
CALL ListAddString( SolverParams, 'Timestepping Method', str )
1341
END IF
1342
END IF
1343
1344
IF ( Found ) THEN
1345
IF (str=='bdf') THEN
1346
Solver % Order = ListGetInteger( SolverParams, &
1347
'BDF Order', Found, minv=1, maxv=5 )
1348
IF ( .NOT. Found ) THEN
1349
Solver % Order = ListGetInteger( CurrentModel % &
1350
Simulation, 'BDF Order', Found, minv=1, maxv=5 )
1351
END IF
1352
IF ( .NOT.Found ) THEN
1353
Solver % Order = 2
1354
CALL Warn(Caller, 'BDF order defaulted to 2.' )
1355
END IF
1356
ELSE IF ( str=='runge-kutta') THEN
1357
Solver % Order = ListGetInteger( CurrentModel % &
1358
Simulation, 'Runge-Kutta Order', Found, minv=2, maxv=4 )
1359
IF ( .NOT.Found ) Solver % Order = 2
1360
ELSE IF( SEQL(str,'adams')) THEN
1361
Solver % Order = 2
1362
END IF
1363
CALL Info(Caller,'Time stepping method is: '//TRIM(str),Level=10)
1364
ELSE
1365
CALL Warn(Caller, '> Timestepping method < defaulted to > Implicit Euler <' )
1366
CALL ListAddString( SolverParams, 'Timestepping Method', 'Implicit Euler' )
1367
END IF
1368
1369
END IF
1370
1371
! Initialize and get the variable
1372
!------------------------------------------------------------------------
1373
Solver % TimeOrder = 0
1374
NULLIFY( Solver % Matrix )
1375
var_name = ListGetString( SolverParams, 'Variable', Found )
1376
1377
NoPerm = .FALSE.
1378
1379
IF(.NOT. Found ) THEN
1380
! Variable does not exist
1381
!------------------------------------------------------
1382
CALL Info(Caller,'Creating null variable with no name',Level=15)
1383
1384
ALLOCATE( Solver % Variable )
1385
Solver % Variable % Name = ''
1386
Solver % Variable % NameLen = 0
1387
Solver % Variable % Norm = 0.0d0
1388
NULLIFY( Solver % Variable % Perm )
1389
NULLIFY( Solver % Variable % Values )
1390
1391
1392
ELSE IF( IsCoupledSolver .AND. .NOT. IsProcedure ) THEN
1393
! Coupled solver may inherit the matrix only if procedure is given
1394
!-----------------------------------------------------------------
1395
1396
ELSE IF( IsBlockSolver .AND. .NOT. IsProcedure ) THEN
1397
! Block solver may inherit the matrix only if procedure is given
1398
!-----------------------------------------------------------------
1399
1400
ELSE
1401
CALL Info(Caller,'Treating variable string: '//TRIM(var_name),Level=15)
1402
1403
! It may be a normal field variable or a global (0D) variable
1404
!------------------------------------------------------------------------
1405
VariableGlobal = ListGetLogical( SolverParams, 'Variable Global', Found )
1406
1407
VariableOutput = ListGetLogical( SolverParams, 'Variable Output', Found )
1408
IF ( .NOT. Found ) VariableOutput = .TRUE.
1409
1410
VariableIp = ListGetLogical( SolverParams, 'Variable IP', Found )
1411
VariableElem = ListGetLogical( SolverParams, 'Variable Elemental', Found )
1412
1413
1414
DOFs = ListGetInteger( SolverParams, 'Variable DOFs', Found, minv=1 )
1415
IF ( .NOT. Found ) THEN
1416
j = 0
1417
DOFs = 0
1418
DO WHILE( .TRUE. )
1419
i = INDEX( var_name(j+1:), ':' ) + j
1420
IF ( i<=j ) EXIT
1421
READ( var_name(i+1:),'(i1)',IOSTAT=iostat) k
1422
IF(iostat /= 0) THEN
1423
CALL Fatal(Caller,'Could not read component count of variable!')
1424
END IF
1425
DOFs = DOFs + k
1426
j = i + 1
1427
END DO
1428
END IF
1429
1430
DO WHILE( var_name(1:1) == '-' )
1431
k = 0
1432
j = LEN_TRIM(var_name)
1433
1434
IF ( SEQL(var_name, '-noperm ') ) THEN
1435
NoPerm = .TRUE.
1436
k = 8
1437
1438
ELSE IF ( SEQL(var_name, '-nooutput ') ) THEN
1439
VariableOutput = .FALSE.
1440
k = 10
1441
1442
ELSE IF ( SEQL(var_name, '-global ') ) THEN
1443
VariableGlobal = .TRUE.
1444
k = 8
1445
1446
ELSE IF ( SEQL(var_name, '-ip ') ) THEN
1447
VariableIp = .TRUE.
1448
k = 4
1449
1450
ELSE IF ( SEQL(var_name, '-elem ') ) THEN
1451
VariableElem = .TRUE.
1452
k = 6
1453
1454
ELSE IF ( SEQL(var_name, '-dofs ') ) THEN
1455
READ( var_name(7:), *, IOSTAT=iostat) DOFs
1456
IF(iostat /= 0) THEN
1457
CALL Fatal(Caller,'Could not read number after -dofs of variable!')
1458
END IF
1459
i = 7
1460
DO WHILE( var_name(i:i) /= ' ' )
1461
i = i + 1
1462
IF ( i > j ) EXIT
1463
END DO
1464
k = i
1465
ELSE
1466
CALL Fatal(Caller,'Do not know how to parse: '//TRIM(var_name))
1467
END IF
1468
1469
IF(k>0) THEN
1470
var_name(1:j-k) = var_name(k+1:j)
1471
DO i=j-k+1,j
1472
var_name(i:i) = ' '
1473
END DO
1474
END IF
1475
1476
END DO
1477
IF ( DOFs == 0 ) DOFs = 1
1478
1479
n = LEN_TRIM(var_name)
1480
1481
! If the variable is 'global' it has nothing to do with the mesh and it may be simply
1482
! allocated.
1483
!------------------------------------------------------------------------------------
1484
IF( VariableGlobal ) THEN
1485
CALL Info(Caller,'Creating global variable: '//var_name(1:n),Level=8)
1486
1487
Solver % SolverMode = SOLVER_MODE_GLOBAL
1488
ALLOCATE( Solution( DOFs ) )
1489
Solution = 0.0_dp
1490
1491
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver, &
1492
var_name(1:n), DOFs, Solution )
1493
Solver % Variable => VariableGet( Solver % Mesh % Variables, var_name(1:n) )
1494
IF( DOFs > 1 ) THEN
1495
DO i=1,DOFs
1496
tmpname = ComponentName( var_name(1:n), i )
1497
Component => Solution( i:i )
1498
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
1499
tmpname, 1, Component )
1500
END DO
1501
END IF
1502
1503
IF( ListGetLogical( SolverParams,'Ode Matrix',Found ) ) THEN
1504
CALL Info(Caller,'Creating dense matrix for ODE: '&
1505
//I2S(Dofs),Level=8)
1506
ALLOCATE( Solver % Variable % Perm(1) )
1507
Solver % Variable % Perm(1) = 1
1508
Solver % Matrix => CreateOdeMatrix( CurrentModel, Solver, Dofs )
1509
END IF
1510
1511
ELSE
1512
CALL Info(Caller,'Creating standard variable: '//var_name(1:n),Level=8)
1513
1514
! If the variable is a field variable create a permutation and matrix related to it
1515
!----------------------------------------------------------------------------------
1516
eq = ListGetString( SolverParams, 'Equation', Found )
1517
IF(.NOT. Found) THEN
1518
CALL Fatal(Caller,'Variable exists but > Equation < is not defined in Solver ')
1519
END IF
1520
Found = .FALSE.
1521
DO i=1, CurrentModel % NumberOfEquations
1522
IF( ListGetLogical( CurrentModel % Equations(i) % Values, TRIM(eq), Stat)) THEN
1523
Found = .TRUE.
1524
EXIT
1525
END IF
1526
END DO
1527
IF(.NOT. Found ) THEN
1528
CALL Fatal(Caller,'Variable > '//var_name(1:n)//&
1529
' < exists but it is not associated to any equation')
1530
END IF
1531
1532
! Computate the size of the permutation vector
1533
!-----------------------------------------------------------------------------------------
1534
CALL Info(Caller,'Computing size of permutation vector',Level=12)
1535
Ndeg = 0
1536
1537
IF(.TRUE.) THEN
1538
eq = ListGetString( Solver % Values, 'Equation', Found )
1539
MaxNDOFs = 0
1540
MaxDOFsPerNode = 0
1541
MaxDGDOFs = 0
1542
MaxBDOFs = 0
1543
DO i=1,Solver % Mesh % NumberOFBulkElements
1544
CurrentElement => Solver % Mesh % Elements(i)
1545
MaxDGDOFs = MAX( MaxDGDOFs, CurrentElement % DGDOFs )
1546
MaxBDOFs = MAX( MaxBDOFs, CurrentElement % BDOFs )
1547
MaxNDOFs = MAX( MaxNDOFs, CurrentElement % NDOFs )
1548
MaxDOFsPerNode = MAX( MaxDOFsPerNode, CurrentElement % NDOFs / &
1549
CurrentElement % TYPE % NumberOfNodes )
1550
END DO
1551
1552
MaxEDOFs = 0
1553
DO i=1,Solver % Mesh % NumberOFEdges
1554
CurrentElement => Solver % Mesh % Edges(i)
1555
MaxEDOFs = MAX( MaxEDOFs, CurrentElement % BDOFs )
1556
END DO
1557
1558
MaxFDOFs = 0
1559
DO i=1,Solver % Mesh % NumberOFFaces
1560
CurrentElement => Solver % Mesh % Faces(i)
1561
MaxFDOFs = MAX( MaxFDOFs, CurrentElement % BDOFs )
1562
END DO
1563
1564
GlobalBubbles = ListGetLogical( SolverParams, 'Bubbles in Global System', Found )
1565
IF (.NOT.Found) GlobalBubbles = .TRUE.
1566
1567
Ndeg = Ndeg + MaxDOFsPerNode * Solver % Mesh % NumberOfNodes
1568
IF ( GlobalBubbles ) THEN
1569
Ndeg = Ndeg + MaxBDOFs * Solver % Mesh % NumberOfBulkElements
1570
1571
DO i=1,Solver % Mesh % NumberOfBoundaryElements
1572
j = i + Solver % Mesh % NumberOfBulkElements
1573
IF ( Solver % Mesh % Elements(j) % Type % ElementCode >= 300) THEN
1574
MaxFDOFs = MAX( maxFDOFs, Solver % Mesh % Elements(j) % BDOFs )
1575
ELSE
1576
MaxEDOFs = MAX( maxEDOFs, Solver % Mesh % Elements(j) % BDOFs )
1577
END IF
1578
END DO
1579
END IF
1580
1581
IF ( MaxEDOFs > 0 ) Ndeg = Ndeg + MaxEDOFs * Solver % Mesh % NumberOFEdges
1582
IF ( MaxFDOFs > 0 ) Ndeg = Ndeg + MaxFDOFs * Solver % Mesh % NumberOFFaces
1583
1584
DG = ListGetLogical( SolverParams, 'Discontinuous Galerkin', Found )
1585
IF( DG ) THEN
1586
Ndeg = MAX( NDeg, MaxDGDOFs * (Solver % Mesh % NumberOfBulkElements+ &
1587
Solver % Mesh % NumberOfBoundaryElements) )
1588
END IF
1589
END IF
1590
1591
IF( ListGetLogical( SolverParams,'Radiation Solver',Found ) ) THEN
1592
! We need to precompute view factors if they are included in CRS matrix
1593
! Benefit of doing it at later stage is that we may modify the geometry, for example.
1594
IF( .NOT. (ListGetLogical( SolverParams,'Radiosity Model',Found ) .OR. &
1595
ListGetLogical( SolverParams,'Spectral Model',Found ) .OR. &
1596
ListGetLogical( SolverParams,'Update Gebhart Factors',Found ) ) ) THEN
1597
! If we need to update the Gebhart factors we may not create the matrix topology at this
1598
! stage as we don't know the emissivities yet.
1599
CALL RadiationFactors( Solver, .TRUE., .FALSE.)
1600
END IF
1601
END IF
1602
1603
BandwidthOptimize = ListGetLogical( SolverParams, &
1604
'Optimize Bandwidth', Found )
1605
IF ( .NOT. Found ) BandwidthOptimize = .TRUE.
1606
CALL CheckLinearSolverOptions( Solver )
1607
1608
CALL Info(Caller,'Maximum size of permutation vector is: '//I2S(Ndeg),Level=12)
1609
ALLOCATE( Perm(Ndeg), STAT=AllocStat )
1610
IF( AllocStat /= 0 ) CALL Fatal(Caller,'Allocation error for Perm')
1611
Perm = 0
1612
MatrixFormat = MATRIX_CRS
1613
1614
ThreadedStartup = ListGetLogical( SolverParams,'Multithreaded Startup',Found )
1615
IF( ThreadedStartup ) THEN
1616
CALL Info(Caller,'Using multithreaded startup',Level=6)
1617
END IF
1618
1619
MultiColourSolver = ListGetLogical( SolverParams,'MultiColour Solver',Found )
1620
1621
IF( MultiColourSolver .OR. ThreadedStartup ) THEN
1622
CALL Info(Caller,'Creating structures for mesh colouring',Level=8)
1623
ConsistentColours = .FALSE.
1624
IF ( ListGetLogical(SolverParams,'MultiColour Consistent', Found) ) THEN
1625
CALL Info(Caller,'Creating consistent colouring',Level=8)
1626
ConsistentColours = .TRUE.
1627
END IF
1628
1629
IF (Solver % Mesh % NumberOfBulkElements > 0) THEN
1630
! Construct the dual graph from Elmer mesh
1631
CALL ElmerMeshToDualGraph(Solver % Mesh, DualGraph)
1632
! Colour the dual graph
1633
CALL ElmerGraphColour(DualGraph, GraphColouring, ConsistentColours)
1634
! Deallocate dual graph as it is no longer needed
1635
CALL Graph_Deallocate(DualGraph)
1636
1637
! Construct colour lists
1638
ALLOCATE( Solver % ColourIndexList, STAT=AllocStat )
1639
IF( AllocStat /= 0 ) CALL Fatal(Caller,'Allocation error for ColourIndexList')
1640
CALL ElmerColouringToGraph(GraphColouring, Solver % ColourIndexList)
1641
CALL Colouring_Deallocate(GraphColouring)
1642
END IF
1643
1644
IF (Solver % Mesh % NumberOfBoundaryElements > 0) THEN
1645
! Construct the dual graph from Elmer boundary mesh
1646
CALL ElmerMeshToDualGraph(Solver % Mesh, DualGraph, UseBoundaryMesh=.TRUE.)
1647
! Colour the dual graph of boundary mesh
1648
CALL ElmerGraphColour(DualGraph, BoundaryGraphColouring, ConsistentColours)
1649
! Deallocate dual graph as it is no longer needed
1650
CALL Graph_Deallocate(DualGraph)
1651
1652
ALLOCATE( Solver % BoundaryColourIndexList, STAT=AllocStat )
1653
IF( AllocStat /= 0 ) CALL Fatal(Caller,'Allocation error for BoundaryColourIndexList')
1654
CALL ElmerColouringToGraph(BoundaryGraphColouring, Solver % BoundaryColourIndexList)
1655
CALL Colouring_Deallocate(BoundaryGraphColouring)
1656
END IF
1657
1658
! DEBUG/TODO: Add a Solver keyword for enabling the functionality
1659
! CALL CheckColourings(Solver)
1660
END IF
1661
1662
CALL Info(Caller,'Creating solver matrix topology',Level=12)
1663
Solver % Matrix => CreateMatrix( CurrentModel, Solver, Solver % Mesh, &
1664
Perm, DOFs, MatrixFormat, BandwidthOptimize, eq(1:LEN_TRIM(eq)), DG, &
1665
GlobalBubbles=GlobalBubbles, ThreadedStartup=ThreadedStartup )
1666
Solver % GlobalBubbles = GlobalBubbles
1667
1668
Nrows = DOFs * Ndeg
1669
IF (ASSOCIATED(Solver % Matrix)) THEN
1670
Nrows = Solver % Matrix % NumberOfRows
1671
CALL Info(Caller,'Number of rows in CRS matrix: '//I2S(Nrows),Level=12)
1672
END IF
1673
1674
! Check if mesh colouring is needed by the solver
1675
IF( .NOT. MultiColourSolver .AND. ThreadedStartup ) THEN
1676
! Deallocate mesh colouring
1677
IF (ASSOCIATED(Solver % ColourIndexList)) THEN
1678
CALL Graph_Deallocate(Solver % ColourIndexList)
1679
DEALLOCATE(Solver % ColourIndexList)
1680
Solver % ColourIndexList => NULL()
1681
END IF
1682
1683
! Deallocate boundary mesh colouring
1684
IF (ASSOCIATED(Solver % BoundaryColourIndexList)) THEN
1685
CALL Graph_Deallocate(Solver % BoundaryColourIndexList)
1686
DEALLOCATE(Solver % BoundaryColourIndexList)
1687
Solver % BoundaryColourIndexList => NULL()
1688
END IF
1689
END IF
1690
1691
! Basically the solver could be matrix free but still the matrix
1692
! is used here temporarily since it is needed when making the
1693
! permutation vector
1694
!-----------------------------------------------------------------
1695
IF( ListGetLogical( SolverParams, 'No Matrix', Found ) ) THEN
1696
CALL Info(Caller,'No matrix needed any more, freeing structures!',Level=12)
1697
Solver % SolverMode = SOLVER_MODE_MATRIXFREE
1698
CALL FreeMatrix( Solver % Matrix )
1699
END IF
1700
1701
CALL Info(Caller,'Creating solver variable',Level=12)
1702
ALLOCATE(Solution(Nrows),STAT=AllocStat)
1703
IF( AllocStat /= 0 ) CALL Fatal(Caller,'Allocation error for Solution')
1704
1705
!$OMP PARALLEL DO
1706
DO i=1,Nrows
1707
Solution(i) = InitValue
1708
END DO
1709
!$OMP END PARALLEL DO
1710
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver, &
1711
var_name(1:n), DOFs, Solution, Perm, Output=VariableOutput )
1712
Solver % Variable => VariableGet( Solver % Mesh % Variables, var_name(1:n) )
1713
Solver % Variable % PeriodicFlipActive = Solver % PeriodicFlipActive
1714
1715
IF ( DOFs > 1 ) THEN
1716
DO i=1,DOFs
1717
tmpname = ComponentName( var_name(1:n), i )
1718
Component => Solution( i:Nrows-DOFs+i:DOFs )
1719
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
1720
tmpname, 1, Component, Perm, Output=VariableOutput )
1721
PVar => VariableGet( Solver % Mesh % Variables, tmpname )
1722
PVar % PeriodicFlipActive = Solver % PeriodicFlipActive
1723
END DO
1724
END IF
1725
1726
! Optionally save the limiters as a field. This is allocated here so that
1727
! if it used as a dependent variable it is allocated before being used.
1728
IF( ListGetLogical( SolverParams,'Apply Limiter', Found ) ) THEN
1729
IF( ListGetLogical( SolverParams,'Save Limiter',Found ) ) THEN
1730
CALL Info(Caller,'Adding "contact active" field for '//var_name(1:n))
1731
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver,&
1732
TRIM(GetVarName(Solver % Variable))//' Contact Active', &
1733
dofs = Solver % Variable % Dofs, Perm = Solver % Variable % Perm )
1734
END IF
1735
END IF
1736
1737
IF (ASSOCIATED(Solver % Matrix)) Solver % Matrix % Comm = ELMER_COMM_WORLD
1738
1739
IF ( DG ) THEN
1740
Solver % Variable % TYPE = Variable_on_nodes_on_elements
1741
END IF
1742
1743
IF( ListGetLogical(SolverParams,'Hcurl Basis',Found ) ) THEN
1744
Solver % Variable % Type = Variable_on_edges
1745
END IF
1746
1747
END IF
1748
!------------------------------------------------------------------------------
1749
END IF
1750
1751
IF( ListGetLogical( SolverParams,'Create Integration Points Table',Found ) ) THEN
1752
CALL CreateIpPerm( Solver )
1753
END IF
1754
1755
!------------------------------------------------------------------------------
1756
! Add the exported variables which are typically auxiliary variables derived
1757
! from the solution without their own matrix equation.
1758
!------------------------------------------------------------------------------
1759
l = 0
1760
DO WHILE( .TRUE. )
1761
l = l + 1
1762
str = ComponentName( 'exported variable', l )
1763
var_name = ListGetString( SolverParams, str, Found )
1764
1765
IF(.NOT. Found) EXIT
1766
1767
CALL Info(Caller,'Creating exported variable: '//TRIM(var_name),Level=12)
1768
1769
str = TRIM( ComponentName( 'exported variable', l ) ) // ' Output'
1770
VariableOutput = ListGetLogical( SolverParams, str, Found )
1771
IF ( .NOT. Found ) VariableOutput = .TRUE.
1772
1773
str = TRIM( ComponentName( 'exported variable', l ) ) // ' Mask'
1774
tmpname = ListGetString( SolverParams, str, UseMask )
1775
1776
GotSecName = .FALSE.
1777
IF( UseMask ) THEN
1778
i3 = LEN_TRIM(tmpname)
1779
i1 = INDEX(tmpname,':')
1780
IF( i1 > 1 ) THEN
1781
i2 = i1 + 1
1782
i1 = i1 - 1
1783
IF( i1 > 0 ) THEN
1784
DO WHILE( tmpname(i2:i2) == ' ')
1785
i2 = i2 + 1
1786
IF(i2>i3) EXIT
1787
END DO
1788
END IF
1789
sec_name = tmpname(1:i1)
1790
mask_name = tmpname(i2:i3)
1791
CALL Info(Caller,'masking with section: '//TRIM(sec_name),Level=12)
1792
CALL Info(Caller,'masking with keyword: '//TRIM(mask_name),Level=12)
1793
GotSecName = .TRUE.
1794
ELSE
1795
sec_name = 'body force'
1796
mask_name = tmpname(1:i3)
1797
END IF
1798
END IF
1799
1800
str = TRIM( ComponentName( 'exported variable', l ) ) // ' DOFs'
1801
DOFs = ListGetInteger( SolverParams, str, Found )
1802
IF ( .NOT. Found ) THEN
1803
j = 0
1804
DOFs = 0
1805
DO WHILE( .TRUE. )
1806
i = INDEX( var_name(j+1:), ':' ) + j
1807
IF ( i<=j ) EXIT
1808
READ( var_name(i+1:),'(i1)',IOSTAT=iostat) k
1809
IF(iostat /= 0) THEN
1810
CALL Fatal(Caller,'Could not read component dofs for exported variable '//I2S(l))
1811
END IF
1812
DOFs = DOFs + k
1813
j = i + 1
1814
END DO
1815
END IF
1816
1817
1818
VariableOutput = .TRUE.
1819
VariableGlobal = .FALSE.
1820
VariableIp = .FALSE.
1821
VariableElem = .FALSE.
1822
VariableDG = .FALSE.
1823
VariableNodal = .FALSE.
1824
VariableType = Solver % Variable % TYPE
1825
InheritVarType = .FALSE.
1826
1827
DO WHILE( var_name(1:1) == '-' )
1828
1829
k = 0
1830
j = LEN_TRIM( var_name )
1831
1832
IF ( SEQL(var_name, '-nooutput ') ) THEN
1833
VariableOutput = .FALSE.
1834
k = 10
1835
1836
! Different types of variables: global, ip, elem, dg
1837
ELSE IF ( SEQL(var_name, '-global ') ) THEN
1838
VariableGlobal = .TRUE.
1839
k = 8
1840
1841
ELSE IF ( SEQL(var_name, '-ip ') ) THEN
1842
VariableIp = .TRUE.
1843
k = 4
1844
1845
ELSE IF ( SEQL(var_name, '-elem ') ) THEN
1846
VariableElem = .TRUE.
1847
k = 6
1848
1849
ELSE IF ( SEQL(var_name, '-nodal ') ) THEN
1850
VariableNodal = .TRUE.
1851
k = 7
1852
1853
ELSE IF ( SEQL(var_name, '-dg ') ) THEN
1854
VariableDG = .TRUE.
1855
k = 4
1856
1857
ELSE IF ( SEQL(var_name, '-dofs ') ) THEN
1858
READ( var_name(7:), *, IOSTAT=iostat ) DOFs
1859
IF(iostat /= 0) THEN
1860
CALL Fatal(Caller,'Could not read -dofs parameter for exported variable '//I2S(l))
1861
END IF
1862
!j = LEN_TRIM( var_name )
1863
k = 7
1864
DO WHILE( var_name(k:k) /= ' ' )
1865
k = k + 1
1866
IF ( k > j ) EXIT
1867
END DO
1868
ELSE
1869
CALL Fatal(Caller,'Do not know how to parse: '//TRIM(var_name))
1870
END IF
1871
1872
! Remove the processed section.
1873
IF(k>0) THEN
1874
var_name(1:j-k) = var_name(k+1:j)
1875
DO i=j-k+1,j
1876
var_name(i:i) = ' '
1877
END DO
1878
END IF
1879
1880
END DO
1881
IF ( DOFs == 0 ) DOFs = 1
1882
1883
NewVariable => VariableGet( Solver % Mesh % Variables, Var_name )
1884
1885
IF ( .NOT. ASSOCIATED(NewVariable) ) THEN
1886
CALL Info(Caller,'Creating exported variable: '//TRIM(var_name),Level=12)
1887
1888
IF( NoPerm ) THEN
1889
IF(.NOT. (VariableNodal .OR. VariableElem .OR. VariableDG ) ) THEN
1890
CALL Fatal(Caller,'Invalid type for Noperm variable!')
1891
END IF
1892
END IF
1893
1894
1895
IF( VariableIp ) THEN
1896
VariableType = Variable_on_gauss_points
1897
1898
IF( UseMask ) THEN
1899
NULLIFY( Perm )
1900
CALL CreateIpPerm( Solver, Perm, mask_name, sec_name )
1901
nsize = MAXVAL( Perm )
1902
ELSE
1903
! Create a table showing the offset for IPs within elements
1904
CALL CreateIpPerm( Solver )
1905
nSize = Solver % IpTable % IpCount
1906
Perm => Solver % IpTable % IpOffset
1907
END IF
1908
nsize = nsize * DOFs
1909
1910
ELSE IF( VariableElem ) THEN
1911
VariableType = Variable_on_elements
1912
1913
! We need to call these earlier than otherwise
1914
IF( UseMask ) THEN
1915
CALL CreateElementsPerm( Solver, Perm, nsize, Mask_Name, sec_name )
1916
ELSE IF( NoPerm ) THEN
1917
nsize = Solver % Mesh % NumberOfBulkElements
1918
Perm => NULL()
1919
ELSE
1920
CALL SetActiveElementsTable( CurrentModel, Solver, k, CreateInv = .TRUE. )
1921
nSize = Solver % NumberOfActiveElements
1922
Perm => Solver % InvActiveElements
1923
END IF
1924
nSize = nSize * Dofs
1925
CALL ListAddInteger( Solver % Values, 'Active Mesh Dimension', k )
1926
1927
ELSE IF( VariableDG ) THEN
1928
VariableType = Variable_on_nodes_on_elements
1929
1930
NULLIFY( Perm )
1931
IF( UseMask ) THEN
1932
CALL CreateDGPerm( Solver, Perm, nsize, mask_name, sec_name )
1933
ELSE IF( NoPerm ) THEN
1934
nsize = 0
1935
DO j=1, Solver % Mesh % NumberOfBulkElements
1936
nsize = nsize + Solver % Mesh % Elements(j) % TYPE % NumberOfNodes
1937
END DO
1938
Perm => NULL()
1939
ELSE
1940
CALL CreateDGPerm( Solver, Perm, nsize )
1941
END IF
1942
nsize = nsize * DOFs
1943
1944
ELSE IF( VariableNodal ) THEN
1945
VariableType = Variable_on_nodes
1946
NULLIFY( Perm )
1947
1948
IF( UseMask ) THEN
1949
CALL MakePermUsingMask( CurrentModel, Solver, Solver % Mesh, Mask_Name, &
1950
.TRUE., Perm, nsize )
1951
nsize = DOFs * nsize
1952
ELSE IF( NoPerm ) THEN
1953
nsize = Solver % Mesh % NumberOfNodes
1954
Perm => NULL()
1955
ELSE
1956
! Just simple permutation
1957
CALL CreateNodalPerm( Solver, Perm, nSize )
1958
END IF
1959
1960
ELSE IF( VariableGlobal ) THEN
1961
VariableType = Variable_global
1962
nSize = DOFs
1963
NULLIFY( Perm )
1964
1965
ELSE ! Follow the primary type
1966
InheritVarType = .TRUE.
1967
IF( UseMask ) THEN
1968
NULLIFY( Perm )
1969
IF( GotSecName ) THEN
1970
CALL CreateMaskedPerm( Solver, Solver % Variable % Perm, Mask_Name, &
1971
Perm, nsize, sec_name )
1972
ELSE
1973
CALL CreateMaskedPerm( Solver, Solver % Variable % Perm, Mask_Name, &
1974
Perm, nsize )
1975
END IF
1976
nsize = DOFs * nsize
1977
ELSE
1978
nSize = DOFs * SIZE(Solver % Variable % Values) / Solver % Variable % DOFs
1979
Perm => Solver % Variable % Perm
1980
END IF
1981
END IF
1982
1983
ALLOCATE( Solution(nSize), STAT = AllocStat )
1984
IF( AllocStat /= 0 ) CALL Fatal(Caller,'Allocation error for Solution')
1985
1986
Solution = 0.0d0
1987
IF( ASSOCIATED(Perm) ) THEN
1988
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
1989
TRIM(var_name), DOFs, Solution, Perm, &
1990
Output=VariableOutput, TYPE=VariableType )
1991
ELSE
1992
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
1993
TRIM(var_name), DOFs, Solution, TYPE=VariableType )
1994
END IF
1995
NewVariable => VariableGet( Solver % Mesh % Variables, Var_name )
1996
IF(ASSOCIATED( NewVariable ) ) THEN
1997
CALL Info(Caller,'Succesfully created variable: '&
1998
//ComponentName(var_name),Level=12)
1999
ELSE
2000
CALL Warn(Caller,'Could not create variable: '//TRIM(var_name))
2001
END IF
2002
2003
IF( InheritVarType ) NewVariable % PeriodicFlipActive = Solver % PeriodicFlipActive
2004
2005
str = TRIM(ComponentName(Var_name ))//' Transient'
2006
TransientVar = ListGetLogical( SolverParams, str, Found )
2007
2008
IF(.NOT. Found ) THEN
2009
str = TRIM( ComponentName(Var_name) )//' Calculate Velocity'
2010
TransientVar = ListGetLogical( SolverParams, str, Found )
2011
END IF
2012
IF(.NOT. Found ) THEN
2013
str = TRIM( ComponentName( Var_name ) )//' Calculate Acceleration'
2014
TransientVar = ListGetLogical( SolverParams, str, Found )
2015
END IF
2016
2017
IF( TransientVar ) THEN
2018
n = 2
2019
CALL Info(Caller,'Allocating prevvalues of size 2 for exported variable',Level=12)
2020
ALLOCATE(NewVariable % PrevValues(nsize,n))
2021
NewVariable % PrevValues = 0.0_dp
2022
2023
! Create upon request the variables for the external fields
2024
CALL CreateTimeDerivativeVariables( Solver, NewVariable )
2025
END IF
2026
2027
IF ( DOFs > 1 ) THEN
2028
n = LEN_TRIM( var_name )
2029
DO j=1,DOFs
2030
tmpname = ComponentName( var_name(1:n), j )
2031
Component => Solution( j::DOFs )
2032
2033
IF( ASSOCIATED(Perm) ) THEN
2034
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2035
TRIM(tmpname), 1, Component, Perm, &
2036
Output=VariableOutput, TYPE = VariableType )
2037
ELSE
2038
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2039
TRIM(tmpname), 1, Component, TYPE = VariableType )
2040
END IF
2041
NewVariable => VariableGet( Solver % Mesh % Variables, tmpname )
2042
IF(ASSOCIATED( NewVariable ) ) THEN
2043
CALL Info(Caller,'Succesfully created variable: '//TRIM(tmpname),Level=12)
2044
ELSE
2045
CALL Warn(Caller,'Could not create variable: '//TRIM(tmpname))
2046
END IF
2047
2048
IF( InheritVarType ) NewVariable % PeriodicFlipActive = Solver % PeriodicFlipActive
2049
END DO
2050
END IF
2051
2052
END IF
2053
END DO
2054
2055
IF(Doit) THEN
2056
IF( ASSOCIATED( Solver % Matrix ) ) THEN
2057
ALLOCATE(Solver % Matrix % MassValues(SIZE(Solver % Matrix % Values)));
2058
Solver % Matrix % MassValues=0._dp
2059
END IF
2060
END IF
2061
2062
Solver % LinBeforeProc = 0
2063
str = ListGetString( Solver % Values, 'Before Linsolve', Found )
2064
IF ( Found ) Solver % LinBeforeProc = GetProcAddr( str )
2065
2066
Solver % LinAfterProc = 0
2067
str = ListGetString( Solver % Values, 'After Linsolve', Found )
2068
IF ( Found ) Solver % LinAfterProc = GetProcAddr( str )
2069
2070
IF( ASSOCIATED( Solver % Matrix ) ) THEN
2071
Solver % Matrix % MatVecSubr = 0
2072
str = ListGetString( Solver % Values, 'Matrix Vector Proc', Found )
2073
IF ( Found ) Solver % Matrix % MatVecSubr = GetProcAddr( str )
2074
END IF
2075
2076
END SUBROUTINE AddEquationBasics
2077
2078
2079
2080
! Create 1st and 2nd derirvatives of the solution either for postprocessing,
2081
! dependencies, or for higher order restarts. The optional argument is intended
2082
! for exported variables.
2083
!---------------------------------------------------------------------------
2084
SUBROUTINE CreateTimeDerivativeVariables( Solver, Var )
2085
TYPE(Solver_t), POINTER :: Solver
2086
TYPE(Variable_t), POINTER, OPTIONAL :: Var
2087
2088
TYPE(Variable_t), POINTER :: pVar
2089
REAL(KIND=dp), POINTER :: Component(:)
2090
INTEGER :: k
2091
LOGICAL :: Found, DoIt
2092
INTEGER :: TimeOrder
2093
CHARACTER(:), ALLOCATABLE :: str,kword
2094
2095
IF( PRESENT( Var ) ) THEN
2096
pVar => Var
2097
kword = TRIM(ComponentName(Var % Name))//' Calculate Velocity'
2098
ELSE
2099
pVar => Solver % Variable
2100
kword = 'Calculate Velocity'
2101
END IF
2102
DoIt = ListGetLogical( Solver % Values, kword, Found)
2103
IF(.NOT. DoIt) THEN
2104
IF( PRESENT( Var ) ) THEN
2105
kword = TRIM(ComponentName(Var % Name))//' Nonlinear Calculate Velocity'
2106
ELSE
2107
kword = 'Nonlinear Calculate Velocity'
2108
END IF
2109
END IF
2110
DoIt = ListGetLogical( Solver % Values, kword, Found)
2111
2112
IF( DoIt ) THEN
2113
! For exported variable we assume 1st order scheme
2114
IF( PRESENT( Var ) ) THEN
2115
TimeOrder = 1
2116
ELSE
2117
TimeOrder = Solver % TimeOrder
2118
END IF
2119
2120
k = INDEX(pVar % Name,'[')-1
2121
IF( k > 0 ) THEN
2122
str = pVar % Name(1:k)//' Velocity'
2123
ELSE
2124
str = TRIM(pVar % Name)//' Velocity'
2125
END IF
2126
2127
IF( TimeOrder < 1 ) THEN
2128
CALL Warn('CreateTimeDerivativeVariables',&
2129
'Velocity computation implemented only for time-dependent variables')
2130
ELSE IF ( TimeOrder == 1 ) THEN
2131
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver, &
2132
str, pVar % Dofs, Perm = pVar % Perm, VarType = pVar % TYPE )
2133
ELSE IF ( Solver % TimeOrder >= 2 ) THEN
2134
Component => pVar % PrevValues(:,1)
2135
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver, &
2136
str, pVar % Dofs, Component, pVar % Perm, Secondary = .TRUE., VarType = pVar % Type )
2137
END IF
2138
END IF
2139
2140
IF( PRESENT( Var ) ) THEN
2141
kword = TRIM(ComponentName(Var % Name))//' Calculate Acceleration'
2142
ELSE
2143
kword = 'Calculate Acceleration'
2144
END IF
2145
DoIt = ListGetLogical( Solver % Values, kword, Found)
2146
2147
IF( DoIt ) THEN
2148
IF( TimeOrder < 1 ) THEN
2149
CALL Warn('CreateTimeDerivativeVariables',&
2150
'Acceleration computation implemented only for time-dependent variables')
2151
ELSE IF ( TimeOrder == 1 ) THEN
2152
str = TRIM(ComponentName(pVar % Name))//' Acceleration'
2153
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver, &
2154
str, Solver % Variable % Dofs, Perm = Solver % Variable % Perm, VarType = pVar % Type )
2155
ELSE IF ( TimeOrder >= 2 ) THEN
2156
Component => Solver % Variable % PrevValues(:,2)
2157
str = TRIM( ComponentName( pVar % Name ) ) // ' Acceleration'
2158
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver, &
2159
str, pVar % Dofs, Component, pVar % Perm, Secondary = .TRUE., VarType = pVar % Type )
2160
END IF
2161
END IF
2162
2163
IF( PRESENT( Var ) ) THEN
2164
kword = TRIM(ComponentName(Var % Name))//' Transient Restart'
2165
ELSE
2166
kword = 'Transient Restart'
2167
END IF
2168
DoIt = ListGetLogical( Solver % Values, kword, Found)
2169
2170
IF( DoIt ) THEN
2171
IF( .NOT. ASSOCIATED( pVar % PrevValues ) ) THEN
2172
CALL Warn('CreateTimeDerivativeVariables',&
2173
'Transient restart requires PrevValues!')
2174
ELSE
2175
DO k = 1, SIZE( pVar % PrevValues, 2 )
2176
Component => pVar % PrevValues(:,k)
2177
str = TRIM( pVar % Name ) !//' PrevValues'//I2S(k)
2178
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver, &
2179
str, pVar % Dofs, Component, pVar % Perm, Secondary = .TRUE., &
2180
VarType = pvar % TYPE, VarSuffix = 'PrevValues'//I2S(k))
2181
END DO
2182
END IF
2183
END IF
2184
2185
END SUBROUTINE CreateTimeDerivativeVariables
2186
2187
2188
!------------------------------------------------------------------------------
2189
!> Add information that is typically only needed if there's a matrix equation
2190
!> to work with. This should be called only after both the solution vector and
2191
!> matrix have been created.
2192
!------------------------------------------------------------------------------
2193
SUBROUTINE AddEquationSolution(Solver, Transient )
2194
!------------------------------------------------------------------------------
2195
TYPE(Solver_t), POINTER :: Solver
2196
LOGICAL :: Transient
2197
!------------------------------------------------------------------------------
2198
TYPE(Variable_t), POINTER :: Var
2199
CHARACTER(LEN=MAX_NAME_LEN) :: str, var_name, tmpname
2200
INTEGER :: i,j,k,n,m,nrows,DOFs
2201
REAL(KIND=dp), POINTER :: Solution(:)
2202
INTEGER, POINTER :: Perm(:)
2203
LOGICAL :: Found, Stat, ComplexFlag, HarmonicAnal, EigAnal, &
2204
VariableOutput, MGAlgebraic,MultigridActive, HarmonicMode, DoIt
2205
INTEGER :: MgLevels, AllocStat
2206
REAL(KIND=dp), POINTER :: Component(:)
2207
REAL(KIND=dp), POINTER :: freqv(:,:)
2208
REAL(KIND=dp) :: Freq
2209
TYPE(Mesh_t), POINTER :: NewMesh,OldMesh
2210
TYPE(Element_t), POINTER :: CurrentElement
2211
TYPE(Matrix_t), POINTER :: NewMatrix, Oldmatrix, SaveMatrix
2212
2213
!------------------------------------------------------------------------------
2214
Solver % DoneTime = 0
2215
IF ( .NOT. ASSOCIATED( Solver % Variable ) ) RETURN
2216
IF ( .NOT. ASSOCIATED( Solver % Variable % Values ) ) RETURN
2217
IF (SIZE(Solver % Variable % Values)==0 ) THEN
2218
DEALLOCATE(Solver % Variable % Values)
2219
Solver % Variable % Values => Null(); RETURN
2220
END IF
2221
!------------------------------------------------------------------------------
2222
2223
!------------------------------------------------------------------------------
2224
! If soft limiters are applied then also loads must be computed
2225
!------------------------------------------------------------------------------
2226
IF( ListGetLogical( Solver % Values,'Calculate Boundary Fluxes',Found) ) THEN
2227
CALL ListAddLogical( Solver % Values,'Calculate Loads',.TRUE.)
2228
END IF
2229
2230
!------------------------------------------------------------------------------
2231
! Create the variable needed for the computation of nodal loads and
2232
! residual: r=b-Ax. The difference here is at what stage A and b are stored.
2233
!------------------------------------------------------------------------------
2234
DO k=1,2
2235
IF(k==1) THEN
2236
str = 'loads'
2237
ELSE
2238
str = 'residual'
2239
END IF
2240
2241
IF ( ListGetLogical( Solver % Values,'Calculate '//TRIM(str), Found ) ) THEN
2242
Var_name = GetVarName(Solver % Variable) // ' '//TRIM(str)
2243
Var => VariableGet( Solver % Mesh % Variables, var_name, .TRUE. )
2244
IF ( .NOT. ASSOCIATED(Var) ) THEN
2245
ALLOCATE( Solution(SIZE(Solver % Variable % Values)), STAT = AllocStat )
2246
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for '//TRIM(str))
2247
2248
DOFs = Solver % Variable % DOFs
2249
Solution = 0.0d0
2250
nrows = SIZE( Solution )
2251
Perm => Solver % Variable % Perm
2252
2253
VariableOutput = ListGetLogical( Solver % Values,'Save '//TRIM(str),Found )
2254
IF( .NOT. Found ) VariableOutput = Solver % Variable % Output
2255
2256
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2257
var_name, Solver % Variable % DOFs, Solution, &
2258
Solver % Variable % Perm, Output=VariableOutput, TYPE = Solver % Variable % TYPE )
2259
2260
IF ( DOFs > 1 ) THEN
2261
n = LEN_TRIM( Var_name )
2262
DO j=1,DOFs
2263
tmpname = ComponentName( var_name(1:n), j )
2264
Component => Solution( j:nRows-DOFs+j:DOFs )
2265
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2266
tmpname, 1, Component, Perm, Output=VariableOutput, TYPE = Solver % Variable % TYPE )
2267
END DO
2268
END IF
2269
NULLIFY( Solution )
2270
END IF
2271
END IF
2272
END DO
2273
2274
!------------------------------------------------------------------------------
2275
! Optionally create variable for saving permutation vector.
2276
! This is mainly for debugging purposes and is therefore commented out.
2277
!------------------------------------------------------------------------------
2278
#if 0
2279
DoIt = .FALSE.
2280
IF( ASSOCIATED( Solver % Variable ) ) THEN
2281
DoIt = ListGetLogical( Solver % Values,'Save Global Dofs', Found )
2282
IF( Solver % Variable % TYPE /= Variable_on_nodes_on_elements ) THEN
2283
CALL Info('AddEquationSolution','Saving of global dof indexes only for DG variable!')
2284
DoIt = .FALSE.
2285
END IF
2286
END IF
2287
2288
IF( DoIt ) THEN
2289
Var_name = GetVarName(Solver % Variable) // ' GDofs'
2290
Var => VariableGet( Solver % Mesh % Variables, var_name )
2291
2292
IF ( .NOT. ASSOCIATED(Var) ) THEN
2293
n = SIZE(Solver % Variable % Values) / Solver % Variable % Dofs
2294
ALLOCATE( Solution(n), STAT = AllocStat )
2295
Solution = 0.0_dp
2296
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error Gdofs')
2297
2298
Solution = 0.0d0
2299
Perm => Solver % Variable % Perm
2300
2301
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2302
var_name, 1, Solution, Solver % Variable % Perm, TYPE = Solver % Variable % TYPE )
2303
2304
IF( ParEnv % PEs == 1 ) THEN
2305
DO i = 1, SIZE( Solution )
2306
Solution(i) = 1.0_dp * i
2307
END DO
2308
END IF
2309
NULLIFY( Solution )
2310
END IF
2311
END IF
2312
#endif
2313
2314
! Create a additional variable for the limiters. For elasticity, for example
2315
! the variable will be the contact load.
2316
IF ( ListGetLogical( Solver % Values,'Apply Limiter', Found ) .OR. &
2317
ListGetLogical( Solver % Values,'Apply Contact BCs',Found ) ) THEN
2318
Var_name = GetVarName(Solver % Variable) // ' Contact Load'
2319
Var => VariableGet( Solver % Mesh % Variables, var_name )
2320
IF ( .NOT. ASSOCIATED(Var) ) THEN
2321
ALLOCATE( Solution(SIZE(Solver % Variable % Values)), STAT = AllocStat)
2322
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for Contact Loads')
2323
2324
DOFs = Solver % Variable % DOFs
2325
Solution = 0.0d0
2326
nrows = SIZE( Solution )
2327
Perm => Solver % Variable % Perm
2328
VariableOutput = Solver % Variable % Output
2329
2330
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2331
var_name, Solver % Variable % DOFs, Solution, &
2332
Solver % Variable % Perm, Output=VariableOutput, Type = Solver % Variable % Type )
2333
2334
IF ( DOFs > 1 ) THEN
2335
n = LEN_TRIM( Var_name )
2336
DO j=1,DOFs
2337
tmpname = ComponentName( var_name(1:n), j )
2338
Component => Solution( j:nRows-DOFs+j:DOFs )
2339
CALL VariableAdd( Solver % Mesh % Variables, Solver % Mesh, Solver,&
2340
tmpname, 1, Component, Perm, Output=VariableOutput, Type = Solver % Variable % Type )
2341
END DO
2342
END IF
2343
NULLIFY( Solution )
2344
END IF
2345
END IF
2346
2347
Solver % NOFEigenValues = 0
2348
Solver % MultiGridLevel = 1
2349
Solver % MultiGridTotal = 0
2350
Solver % MultiGridSolver = .FALSE.
2351
Solver % MultiGridEqualSplit = .FALSE.
2352
2353
2354
HarmonicAnal = ListGetLogical( Solver % Values, 'Harmonic Analysis', Found )
2355
2356
HarmonicMode = ListGetLogical( Solver % Values, 'Harmonic Mode', Found )
2357
2358
IF ( ASSOCIATED( Solver % Matrix ) ) THEN
2359
IF(.NOT. ASSOCIATED(Solver % Matrix % RHS)) THEN
2360
ALLOCATE( Solver % Matrix % RHS(Solver % Matrix % NumberOFRows), STAT=AllocStat )
2361
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for Rhs')
2362
Solver % Matrix % RHS = 0.0d0
2363
2364
Solver % Matrix % RHS_im => NULL()
2365
IF ( HarmonicAnal .OR. HarmonicMode ) THEN
2366
ALLOCATE( Solver % Matrix % RHS_im(Solver % Matrix % NumberOFRows), STAT=AllocStat)
2367
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for Rhs_im')
2368
Solver % Matrix % RHS_im = 0.0d0
2369
END IF
2370
END IF
2371
END IF
2372
!------------------------------------------------------------------------------
2373
2374
EigAnal = ListGetLogical( Solver % Values, 'Eigen Analysis', Found )
2375
2376
IF ( Transient .AND. .NOT. EigAnal .AND. .NOT. HarmonicAnal ) THEN
2377
k = ListGetInteger( Solver % Values, 'Time Derivative Order', Found, &
2378
minv=0, maxv=2 )
2379
Solver % TimeOrder = 1
2380
IF ( Found ) Solver % TimeOrder = MIN(MAX(1,k),2)
2381
2382
IF ( ASSOCIATED( Solver % Matrix ) ) THEN
2383
ALLOCATE( Solver % Matrix % Force(Solver % Matrix % NumberOFRows, &
2384
Solver % TimeOrder+1), STAT=AllocStat)
2385
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for Force')
2386
Solver % Matrix % Force = 0.0d0
2387
END IF
2388
2389
IF ( .NOT. ASSOCIATED( Solver % Variable % PrevValues ) ) THEN
2390
2391
n = SIZE(Solver % Variable % Values)
2392
IF ( Solver % TimeOrder == 2 ) THEN
2393
m = 7
2394
ELSE
2395
m = MAX( Solver % Order, Solver % TimeOrder )
2396
END IF
2397
2398
IF( m > 0 ) THEN
2399
ALLOCATE(Solver % Variable % PrevValues( n, m ), STAT=AllocStat )
2400
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for PrevValues')
2401
Solver % Variable % PrevValues = 0.0d0
2402
2403
IF ( Solver % Variable % DOFs > 1 ) THEN
2404
IF ( GetVarName( Solver % Variable ) == 'flow solution' ) THEN
2405
DO k=1,Solver % Variable % DOFs-1
2406
str = 'Velocity ' // CHAR(k+ICHAR('0'))
2407
Var => VariableGet( Solver % Mesh % Variables, str, .TRUE. )
2408
IF(.NOT. ASSOCIATED(Var)) THEN
2409
CALL Fatal("AddEquationSolution",&
2410
"Failed to get variable: "//TRIM(str)//". Try specifying Variable =&
2411
& Flow Solution[velocity:DIM pressure:1] in .sif")
2412
END IF
2413
Var % PrevValues => &
2414
Solver % Variable % PrevValues(k::Solver % Variable % DOFs,:)
2415
END DO
2416
Var => VariableGet( Solver % Mesh % Variables, 'Pressure', .TRUE. )
2417
IF(.NOT. ASSOCIATED(Var)) THEN
2418
CALL Fatal("AddEquationSolution","Failed to get variable: &
2419
&pressure. Try specifying Variable = Flow Solution &
2420
&[velocity:DIM pressure:1] in .SIF")
2421
END IF
2422
Var % PrevValues => &
2423
Solver % Variable % PrevValues(k::Solver % Variable % DOFs,:)
2424
ELSE
2425
DO k=1,Solver % Variable % DOFs
2426
str = ComponentName( Solver % Variable % Name, k )
2427
Var => VariableGet( Solver % Mesh % Variables, str, .TRUE. )
2428
IF( ASSOCIATED( Var ) ) THEN
2429
Var % PrevValues => &
2430
Solver % Variable % PrevValues(k::Solver % Variable % DOFs,:)
2431
END IF
2432
END DO
2433
END IF
2434
END IF
2435
END IF
2436
END IF
2437
2438
! Create 1st and 2nd derirvatives of the solution either for postprocessing,
2439
! dependencies, or for higher order restarts
2440
CALL CreateTimeDerivativeVariables( Solver )
2441
2442
ELSE
2443
Solver % TimeOrder = 0
2444
2445
DoIt = ListGetLogical( Solver % Values,'Calculate Derivative',Found)
2446
IF( .NOT. Found ) THEN
2447
DoIt = ListGetLogical( Solver % Values,'Nonlinear Calculate Derivative',Found)
2448
END IF
2449
IF( DoIt ) THEN
2450
str = GetVarname(Solver % Variable) // ' Derivative'
2451
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, Solver, &
2452
str, Solver % Variable % Dofs, Perm = Solver % Variable % Perm, &
2453
VarType = Solver % Variable % TYPE )
2454
Var => VariableGet( Solver % Mesh % Variables, str, .TRUE. )
2455
Var % Values = 0.0_dp
2456
END IF
2457
2458
IF ( EigAnal ) THEN
2459
n = ListGetInteger( Solver % Values, 'Eigen System Values', Found )
2460
IF ( Found .AND. n > 0 ) THEN
2461
IF (ListGetLogical(Solver % Values, 'Linear System Skip Complex', Found)) THEN
2462
ComplexFlag = .FALSE.
2463
ELSE
2464
ComplexFlag = ListGetLogical(Solver % Values, 'Linear System Complex', Found)
2465
END IF
2466
Solver % NOFEigenValues = n
2467
IF ( .NOT. ASSOCIATED( Solver % Variable % EigenValues ) ) THEN
2468
ALLOCATE( Solver % Variable % EigenValues(n) )
2469
IF (ComplexFlag) THEN
2470
ALLOCATE( Solver % Variable % EigenVectors(n, &
2471
SIZE( Solver % Variable % Values )/2 ), STAT=AllocStat)
2472
ELSE
2473
ALLOCATE( Solver % Variable % EigenVectors(n, &
2474
SIZE( Solver % Variable % Values ) ), STAT=AllocStat)
2475
END IF
2476
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for EigenValues')
2477
2478
Solver % Variable % EigenValues = 0.0d0
2479
Solver % Variable % EigenVectors = 0.0d0
2480
2481
IF( .NOT. ComplexFlag .AND. Solver % Variable % DOFs > 1 ) THEN
2482
CALL Info('AddEquationSolution','Repointing '//I2S(Solver % Variable % DOFs)//&
2483
' eigenvalue components for: '//TRIM(Solver % Variable % Name))
2484
2485
DO k=1,Solver % Variable % DOFs
2486
str = ComponentName( Solver % Variable % Name, k )
2487
Var => VariableGet( Solver % Mesh % Variables, str, .TRUE. )
2488
2489
IF( ASSOCIATED( Var ) ) THEN
2490
CALL Info('AddEquationSolution','Eigenvalue component '&
2491
//I2S(k)//': '//TRIM(str))
2492
Var % EigenValues => Solver % Variable % EigenValues
2493
Var % EigenVectors => &
2494
Solver % Variable % EigenVectors(:,k::Solver % Variable % DOFs )
2495
END IF
2496
END DO
2497
END IF
2498
END IF
2499
2500
ALLOCATE( Solver % Matrix % MassValues(SIZE(Solver % Matrix % Values)), STAT=AllocStat)
2501
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for MassValues')
2502
2503
Solver % Matrix % MassValues = 0.0d0
2504
END IF
2505
ELSE IF ( HarmonicAnal ) THEN
2506
2507
n = ListGetInteger( Solver % Values,'Harmonic System Values',Found )
2508
IF( n > 1 ) THEN
2509
freqv => ListGetConstRealArray( Solver % Values, 'Frequency', Found )
2510
IF(Found ) THEN
2511
IF( SIZE( Freqv,1) < n ) THEN
2512
CALL Fatal( 'AddEquationSolution', 'Frequency must be at least same size as > Harmonic System Values <')
2513
END IF
2514
ELSE
2515
CALL Fatal( 'AddEquationSolution', '> Frequency < must be given for harmonic analysis.' )
2516
END IF
2517
ELSE
2518
n = 1
2519
END IF
2520
2521
Solver % NOFEigenValues = n
2522
IF ( .NOT. ASSOCIATED( Solver % Variable % EigenValues ) ) THEN
2523
ALLOCATE( Solver % Variable % EigenValues(n) )
2524
ALLOCATE( Solver % Variable % EigenVectors(n, &
2525
SIZE( Solver % Variable % Values ) ), STAT=AllocStat)
2526
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for EigenValues')
2527
2528
Solver % Variable % EigenValues = 0.0d0
2529
Solver % Variable % EigenVectors = 0.0d0
2530
2531
DO k=1,Solver % Variable % DOFs
2532
str = ComponentName( Solver % Variable % Name, k )
2533
Var => VariableGet( Solver % Mesh % Variables, str, .TRUE. )
2534
IF ( ASSOCIATED( Var ) ) THEN
2535
Var % EigenValues => Solver % Variable % EigenValues
2536
Var % EigenVectors => &
2537
Solver % Variable % EigenVectors(:,k::Solver % Variable % DOFs)
2538
END IF
2539
END DO
2540
END IF
2541
2542
ALLOCATE( Solver % Matrix % MassValues(SIZE(Solver % Matrix % Values)), STAT=AllocStat)
2543
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for MassValues')
2544
Solver % Matrix % MassValues = 0.0d0
2545
2546
ELSE IF( HarmonicMode ) THEN
2547
2548
ALLOCATE( Solver % Matrix % MassValues(SIZE(Solver % Matrix % Values)), STAT=AllocStat)
2549
IF( AllocStat /= 0 ) CALL Fatal('AddEquationSolution','Allocation error for MassValues')
2550
Solver % Matrix % MassValues = 0.0d0
2551
2552
END IF
2553
END IF
2554
2555
2556
!------------------------------------------------------------------------------
2557
2558
IF ( ASSOCIATED( Solver % Matrix ) ) THEN
2559
Solver % Matrix % Symmetric = ListGetLogical( Solver % Values, &
2560
'Linear System Symmetric', Found )
2561
2562
Solver % Matrix % Lumped = ListGetLogical( Solver % Values, &
2563
'Lumped Mass Matrix', Found )
2564
2565
MultigridActive = &
2566
ListGetString( Solver % Values, 'Linear System Solver', Found ) == 'multigrid' .OR. &
2567
ListGetString( Solver % Values, 'Linear System Preconditioning', Found ) == 'multigrid'
2568
2569
2570
! Check for multigrid solver:
2571
! ---------------------------
2572
IF ( MultigridActive ) THEN
2573
2574
! Multigrid may be either solver or preconditioner, it it solver?
2575
Solver % MultiGridSolver = ListGetString(Solver % Values, &
2576
'Linear System Solver', Found ) == 'multigrid'
2577
2578
! There are four different methods: algrabraic, cluster, p and geometric
2579
str = ListGetString( Solver % Values,'MG Method',Found)
2580
IF( Found ) THEN
2581
MGAlgebraic = ( str == 'algebraic' ) .OR. ( str == 'cluster') .OR. ( str == 'p')
2582
ELSE
2583
MGAlgebraic = ListGetLogical( Solver % Values, 'MG Algebraic', Found ) &
2584
.OR. ListGetLogical( Solver % Values, 'MG Cluster', Found ) &
2585
.OR. ListGetLogical( Solver % Values, 'MG Pelement', Found )
2586
END IF
2587
2588
2589
MgLevels = ListGetInteger( Solver % Values, &
2590
'MG Levels', Found, minv=1 )
2591
IF ( .NOT. Found ) THEN
2592
MgLevels = ListGetInteger( Solver % Values, &
2593
'Multigrid Levels', Found, minv=1 )
2594
END IF
2595
IF ( .NOT. Found ) THEN
2596
IF( MGAlgebraic ) THEN
2597
MgLevels = 10
2598
ELSE
2599
CALL Fatal('AddEquationSolution','> MG Levels < must be defined for geometric multigrid!')
2600
END IF
2601
END IF
2602
Solver % MultiGridTotal = MgLevels
2603
2604
2605
! In case of geometric multigrid make the hierarchical meshes
2606
! -----------------------------------------------------------
2607
IF(.NOT. MGAlgebraic ) THEN
2608
2609
! Check if h/2 splitting of mesh requested:
2610
! ------------------------------------------
2611
Solver % MultiGridEqualSplit = ListGetLogical( &
2612
Solver % Values, 'MG Equal Split', Found )
2613
2614
IF ( Solver % MultiGridEqualSplit ) THEN
2615
CALL ParallelInitMatrix( Solver, Solver % Matrix )
2616
Solver % MultiGridLevel = 1
2617
2618
DO WHILE( Solver % MultiGridLevel < Solver % MultiGridTotal )
2619
IF ( ASSOCIATED( Solver % Mesh % Child ) ) THEN
2620
NewMesh => Solver % Mesh % Child
2621
2622
OldMesh => Solver % Mesh
2623
OldMatrix => Solver % Matrix
2624
2625
CALL UpdateSolverMesh( Solver, NewMesh )
2626
Solver % Mesh % Changed = .FALSE.
2627
ELSE
2628
NewMesh => SplitMeshEqual( Solver % Mesh )
2629
CALL SetMeshMaxDofs(NewMesh)
2630
NewMesh % Next => CurrentModel % Meshes
2631
CurrentModel % Meshes => NewMesh
2632
2633
OldMesh => Solver % Mesh
2634
OldMatrix => Solver % Matrix
2635
2636
CALL UpdateSolverMesh( Solver, NewMesh )
2637
Solver % Mesh % Changed = .FALSE.
2638
2639
NewMesh % Parent => OldMesh
2640
OldMesh % Child => NewMesh
2641
NewMesh % Name = OldMesh % Name
2642
END IF
2643
2644
Newmesh % OutputActive = .TRUE.
2645
OldMesh % OutputActive = .FALSE.
2646
2647
NewMatrix => Solver % Matrix
2648
NewMatrix % Parent => OldMatrix
2649
OldMatrix % Child => NewMatrix
2650
CALL ParallelInitMatrix( Solver, Solver % Matrix )
2651
Solver % MultiGridLevel = Solver % MultiGridLevel + 1
2652
END DO
2653
ELSE
2654
CALL ParallelInitMatrix( Solver, Solver % Matrix )
2655
OldMesh => Solver % Mesh
2656
Var => Solver % Variable
2657
SaveMatrix => Solver % Matrix
2658
2659
Solver % MultiGridLevel = 1
2660
DO WHILE( Solver % MultiGridLevel < Solver % MultigridTotal )
2661
IF ( ASSOCIATED(Solver % Mesh % Parent) ) THEN
2662
NewMesh => Solver % Mesh % Parent
2663
OldMatrix => Solver % Matrix
2664
CALL UpdateSolverMesh( Solver, NewMesh )
2665
Solver % Mesh % Changed = .FALSE.
2666
NewMatrix => Solver % Matrix
2667
NewMatrix % Child => OldMatrix
2668
OldMatrix % Parent => NewMatrix
2669
CALL ParallelInitMatrix(Solver, Solver % Matrix )
2670
END IF
2671
Solver % MultiGridLevel = Solver % MultiGridLevel+1
2672
END DO
2673
2674
Solver % Mesh => OldMesh
2675
Solver % Variable => Var
2676
Solver % Matrix => SaveMatrix
2677
CALL SetCurrentMesh(CurrentModel,Solver % Mesh)
2678
END IF
2679
2680
CALL MeshStabParams( Solver % Mesh )
2681
END IF
2682
2683
Solver % MultiGridLevel = Solver % MultigridTotal
2684
2685
END IF
2686
2687
! Set the default verbosity of the iterative solvers accordingly with the
2688
! global verbosity.
2689
!-----------------------------------------------------------------------------
2690
IF( .NOT. ListCheckPresent( Solver % Values,'Linear System Residual Output') ) THEN
2691
k = 1
2692
IF( .NOT. OutputLevelMask(4) ) THEN
2693
k = 0
2694
ELSE IF( .NOT. OutputLevelMask(5) ) THEN
2695
k = 10
2696
END IF
2697
IF( k /= 1 ) THEN
2698
CALL ListAddInteger( Solver % Values,'Linear System Residual Output',k)
2699
END IF
2700
END IF
2701
END IF
2702
2703
!------------------------------------------------------------------------------
2704
2705
!------------------------------------------------------------------------------
2706
END SUBROUTINE AddEquationSolution
2707
!------------------------------------------------------------------------------
2708
2709
2710
2711
2712
!------------------------------------------------------------------------------
2713
!> Generate a similar solver instance as for the parent solver.
2714
!> The number of dofs may vary but the basis functions and permutation is reused.
2715
!> If also the number of dofs is the same also matrix topology is reused.
2716
!------------------------------------------------------------------------------
2717
FUNCTION CreateChildSolver( ParentSolver, ChildVarName, ChildDofs, ChildPrefix, NoReuse) &
2718
RESULT ( ChildSolver )
2719
TYPE(Solver_t) :: ParentSolver
2720
CHARACTER(LEN=*) :: ChildVarName
2721
INTEGER, OPTIONAL :: ChildDofs
2722
CHARACTER(LEN=*), OPTIONAL :: ChildPrefix
2723
TYPE(Solver_t), POINTER :: ChildSolver
2724
LOGICAL, OPTIONAL :: NoReuse
2725
2726
INTEGER :: ParentDofs
2727
TYPE(Solver_t), POINTER :: Solver
2728
REAL(KIND=dp), POINTER :: ChildVarValues(:)
2729
INTEGER, POINTER :: ChildVarPerm(:)
2730
TYPE(Variable_t), POINTER :: ChildVar
2731
TYPE(Matrix_t), POINTER :: ChildMat, ParentMat
2732
INTEGER :: n,m,dofs, i,j,k,l,ii, jj, nn
2733
LOGICAL :: Found, Lvalue
2734
2735
ParentDofs = ParentSolver % Variable % Dofs
2736
IF( PRESENT( ChildDofs ) ) THEN
2737
Dofs = ChildDofs
2738
ELSE
2739
Dofs = ParentDofs
2740
END IF
2741
2742
CALL Info('CreateChildSolver','Creating solver of size '//I2S(Dofs)//' for variable: &
2743
'//TRIM(ChildVarName),Level=6)
2744
2745
NULLIFY( Solver )
2746
ALLOCATE( Solver )
2747
ChildSolver => Solver
2748
2749
Solver % Values => Null()
2750
CALL ListAddString(Solver % Values,'Equation',TRIM(ChildVarName)//' solver' )
2751
2752
IF( PRESENT( ChildPrefix ) ) THEN
2753
CALL Info('CreateChildSolver','Copying keywords with prefix: '//TRIM(ChildPrefix),Level=8)
2754
CALL ListCopyPrefixedKeywords( ParentSolver % Values, Solver % Values, &
2755
ChildPrefix )
2756
ELSE
2757
CALL Info('CreateChildSolver','Copying all keywords',Level=8)
2758
CALL ListCopyAllKeywords( ParentSolver % Values, Solver % Values )
2759
END IF
2760
2761
IF( .NOT. ASSOCIATED( ParentSolver % Mesh ) ) THEN
2762
CALL Fatal('CreateChildSolver','Parent solver is missing mesh!')
2763
END IF
2764
Solver % Mesh => ParentSolver % Mesh
2765
i = SIZE(ParentSolver % Def_Dofs,1)
2766
j = SIZE(ParentSolver % Def_Dofs,2)
2767
k = SIZE(ParentSolver % Def_Dofs,3)
2768
ALLOCATE(Solver % Def_Dofs(i,j,k))
2769
Solver % Def_Dofs = ParentSolver % Def_Dofs
2770
2771
IF( .NOT. ASSOCIATED( ParentSolver % Variable ) ) THEN
2772
CALL Fatal('CreateChildSolver','Parent solver is missing variable!')
2773
END IF
2774
2775
n = ( SIZE( ParentSolver % Variable % Values ) ) / ParentDofs
2776
2777
ALLOCATE( ChildVarValues( n * Dofs ) )
2778
ChildVarValues = 0.0_dp
2779
ChildVarPerm => ParentSolver % Variable % Perm
2780
2781
Lvalue = ListGetLogical( Solver % Values,'Variable Output', Found )
2782
IF(.NOT. Found ) Lvalue = ParentSolver % Variable % Output
2783
2784
CALL VariableAddVector( Solver % Mesh % Variables, Solver % Mesh, &
2785
Solver, ChildVarName, Dofs, ChildVarValues, ChildVarPerm, Lvalue )
2786
2787
2788
ChildVar => VariableGet( Solver % Mesh % Variables, ChildVarName )
2789
IF(.NOT. ASSOCIATED( ChildVar ) ) THEN
2790
CALL Fatal('CreateChildSolver','Could not generate child variable!')
2791
END IF
2792
2793
ChildVar % Solver => Solver
2794
2795
ChildVar % TYPE = ParentSolver % Variable % TYPE
2796
Solver % Variable => ChildVar
2797
2798
2799
CALL Info('CreateChildSolver','Creating matrix for solver variable',Level=8)
2800
Solver % Matrix => AllocateMatrix()
2801
ChildMat => Solver % Matrix
2802
2803
ParentMat => ParentSolver % Matrix
2804
IF( .NOT. ASSOCIATED( ParentMat ) ) THEN
2805
CALL Warn('CreateChildSolver','Parent matrix needed for child matrix!')
2806
Solver % Matrix => NULL()
2807
ELSE
2808
ChildMat => CreateChildMatrix( ParentMat, ParentDofs, Dofs, Dofs, .TRUE., NoReuse = NoReuse )
2809
ChildMat % Solver => Solver
2810
Solver % Matrix => ChildMat
2811
END IF
2812
2813
ChildMat % COMPLEX = ListGetLogical( Solver % Values,'Linear System Complex',Found )
2814
2815
IF(.NOT. Found ) THEN
2816
IF( MODULO( ChildDofs, 2 ) == 0 ) THEN
2817
ChildMat % COMPLEX = ParentMat % COMPLEX
2818
ELSE
2819
ChildMat % COMPLEX = .FALSE.
2820
END IF
2821
END IF
2822
2823
Lvalue = ListGetLogical( Solver % Values,'Bubbles in Global System',Found )
2824
IF( Found ) THEN
2825
CALL ListAddNewLogical( ChildSolver % Values,'Bubbles in Global System',Lvalue )
2826
END IF
2827
2828
IF( ASSOCIATED( ParentSolver % ActiveElements ) ) THEN
2829
Solver % ActiveElements => ParentSolver % ActiveElements
2830
Solver % NumberOfActiveElements = ParentSolver % NumberOfActiveElements
2831
END IF
2832
2833
IF( ASSOCIATED( ParentSolver % ColourIndexList ) ) THEN
2834
Solver % ColourIndexList => ParentSolver % ColourIndexList
2835
END IF
2836
2837
Solver % TimeOrder = ListGetInteger( Solver % values,'Time Derivative Order',Found )
2838
IF(.NOT. Found ) Solver % TimeOrder = ParentSolver % TimeOrder
2839
2840
Solver % MultigridTotal = 0
2841
Solver % SolverExecWhen = SOLVER_EXEC_NEVER
2842
Solver % LinBeforeProc = 0
2843
Solver % LinAfterProc = 0
2844
2845
IF ( Parenv % PEs >1 ) THEN
2846
CALL ParallelInitMatrix( Solver, Solver % Matrix )
2847
END IF
2848
2849
CALL Info('CreateChildSolver','All done for now!',Level=8)
2850
END FUNCTION CreateChildSolver
2851
!------------------------------------------------------------------------------
2852
2853
2854
2855
!------------------------------------------------------------------------------
2856
!> Solve the equations one-by-one.
2857
!------------------------------------------------------------------------------
2858
SUBROUTINE SolveEquations( Model, dt, TransientSimulation, &
2859
CoupledMinIter, CoupledMaxIter, SteadyStateReached, &
2860
RealTimestep, BeforeTime, AtTime, AfterTime )
2861
!------------------------------------------------------------------------------
2862
TYPE(Model_t) :: Model
2863
REAL(KIND=dp) :: dt
2864
INTEGER, OPTIONAL :: RealTimestep
2865
INTEGER :: CoupledMinIter, CoupledMaxIter
2866
LOGICAL :: TransientSimulation, SteadyStateReached, TransientSolver
2867
LOGICAL, OPTIONAL :: BeforeTime, AtTime, AfterTime
2868
!------------------------------------------------------------------------------
2869
REAL(KIND=dp) :: RelativeChange, Tolerance, PrevDT = 0.0d0, Relaxation, SSCond
2870
INTEGER :: i,j,k,l,n,ierr,istat,Visited=0, RKorder=0, nSolvers
2871
LOGICAL :: Found, Stat, AbsNorm, Scanning, Convergence, RungeKutta, MeActive, &
2872
NeedSol, CalculateDerivative, TestConvergence=.FALSE., TestDivergence, DivergenceExit, &
2873
ExecSlot, CoupledAbort
2874
LOGICAL, ALLOCATABLE :: DoneThis(:), AfterConverged(:)
2875
TYPE(Solver_t), POINTER :: Solver
2876
TYPE(Mesh_t), POINTER :: Mesh
2877
CHARACTER(LEN=max_name_len) :: When
2878
TYPE(Variable_t), POINTER :: IterV, TimestepV
2879
REAL(KIND=dp), POINTER :: steadyIt,nonlnIt
2880
REAL(KIND=dp), POINTER :: k1(:), k2(:), k3(:), k4(:), sTime
2881
2882
TYPE(Variable_t), POINTER :: TimeVar
2883
REAL(KIND=dp) :: RK2_err
2884
REAL(KIND=dp), ALLOCATABLE :: RK2_ErrorEstimate(:)
2885
TYPE RungeKutta_t
2886
REAL(KIND=dp), ALLOCATABLE :: k1(:),k2(:),k3(:),k4(:)
2887
END TYPE RungeKutta_t
2888
TYPE(RungeKutta_t), ALLOCATABLE, TARGET :: RKCoeff(:)
2889
2890
TYPE(ParEnv_t) :: ParEnv_Save
2891
!------------------------------------------------------------------------------
2892
2893
ParEnv_Save = ParEnv_Common
2894
2895
!------------------------------------------------------------------------------
2896
! Initialize equation solvers for new timestep
2897
!------------------------------------------------------------------------------
2898
nSolvers = Model % NumberOfSolvers
2899
2900
IF(TransientSimulation) THEN
2901
CoupledMinIter = ListGetInteger( Model % Simulation, &
2902
'Steady State Min Iterations', Found )
2903
2904
CoupledMaxIter = ListGetInteger( Model % Simulation, &
2905
'Steady State Max Iterations', Found, minv=1 )
2906
IF ( .NOT. Found ) CoupledMaxIter = 1
2907
END IF
2908
2909
Scanning = &
2910
ListGetString( CurrentModel % Simulation, 'Simulation Type', Found ) == 'scanning'
2911
2912
TestDivergence = .FALSE.
2913
DivergenceExit = .FALSE.
2914
2915
IF ( TransientSimulation ) THEN
2916
DO k=1,nSolvers
2917
Solver => Model % Solvers(k)
2918
IF ( Solver % PROCEDURE /= 0 ) THEN
2919
CALL InitializeTimestep(Solver)
2920
END IF
2921
END DO
2922
END IF
2923
2924
IF( TransientSimulation .OR. Scanning ) THEN
2925
IterV => VariableGet(Model % Solvers(1) % Mesh % Variables, 'coupled iter')
2926
steadyIt => IterV % Values(1)
2927
END IF
2928
!------------------------------------------------------------------------------
2929
2930
IF( PRESENT( BeforeTime ) ) THEN
2931
ExecSlot = BeforeTime
2932
ELSE
2933
ExecSlot = .TRUE.
2934
END IF
2935
2936
IF( ExecSlot ) THEN
2937
CALL Info('SolveEquations','Solvers before timestep',Level=12)
2938
DO k=1,nSolvers
2939
Solver => Model % Solvers(k)
2940
IF ( Solver % PROCEDURE==0 ) CYCLE
2941
IF ( Solver % SolverExecWhen == SOLVER_EXEC_AHEAD_TIME .OR. &
2942
Solver % SolverExecWhen == SOLVER_EXEC_PREDCORR ) THEN
2943
2944
IF( PRESENT( RealTimeStep ) ) THEN
2945
IF( RealTimeStep == 1 ) THEN
2946
IF( ListGetLogical( Solver % Values,'Skip First Timestep',Found ) ) CYCLE
2947
END IF
2948
END IF
2949
2950
! Use predictor method
2951
IF( Solver % SolverExecWhen == SOLVER_EXEC_PREDCORR ) THEN
2952
CALL Info('SolveEquations','Switching time-stepping method to predictor method',Level=7)
2953
CALL ListAddString( Solver % Values, 'Timestepping Method', &
2954
ListGetString( Solver % Values, 'Predictor Method',Found) )
2955
IF(.NOT. Found ) THEN
2956
CALL Fatal('SolveEquations','Predictor-corrector schemes require > Predictor Method <')
2957
END IF
2958
CALL ListAddLogical( Solver % Values,'Predictor Phase',.TRUE.)
2959
END IF
2960
2961
CALL SolverActivate( Model,Solver,dt,TransientSimulation )
2962
CALL ParallelBarrier
2963
2964
! Use Corrector method
2965
IF( Solver % SolverExecWhen == SOLVER_EXEC_PREDCORR ) THEN
2966
CALL Info('SolveEquations','Switching time-stepping method to corrector method',Level=7)
2967
CALL ListAddString( Solver % Values, 'Timestepping Method', &
2968
ListGetString( Solver % Values, 'Corrector Method',Found) )
2969
IF(.NOT. Found ) THEN
2970
CALL Fatal('SolveEquations','Predictor-corrector schemes require > Corrector Method <')
2971
END IF
2972
END IF
2973
END IF
2974
END DO
2975
END IF
2976
2977
!------------------------------------------------------------------------------
2978
IF( PRESENT( AtTime ) ) THEN
2979
ExecSlot = AtTime
2980
ELSE
2981
ExecSlot = .TRUE.
2982
END IF
2983
2984
IF( ExecSlot ) THEN
2985
TestDivergence = ListGetLogical( CurrentModel % Simulation, &
2986
'Convergence Control Within Iterations', Found )
2987
2988
ALLOCATE( DoneThis(nSolvers), AfterConverged(nSolvers) )
2989
2990
DO i=1,nSolvers
2991
Solver => Model % Solvers(i)
2992
AfterConverged(i) = ListGetLogical( Solver % Values, &
2993
'Coupled System After Others Converged', Found )
2994
END DO
2995
2996
!------------------------------------------------------------------------------
2997
CALL Info('SolveEquations','Solvers in main iteration loop',Level=12)
2998
2999
TimeVar => VariableGet( Model % Variables, 'Time')
3000
sTime => TimeVar % Values(1)
3001
3002
RungeKutta = ListGetString( Model % Simulation, &
3003
'Timestepping Method', Found ) == 'runge-kutta'
3004
3005
IF( .NOT. RungeKutta ) THEN
3006
! Without Runge-Kutta the cycling over equations is pretty easy
3007
CALL SolveCoupled()
3008
ELSE
3009
CALL Info('SolveEquations','Using Runge-Kutta time-stepping',Level=12)
3010
3011
CALL Info('SolveEquations','Runge-Kutta predictor step',Level=12)
3012
sTime = sTime - dt
3013
CALL SolveCoupled()
3014
3015
! Perform Runge-Kutta steps for ru
3016
!---------------------------------------------------------------
3017
3018
DO i=1,nSolvers
3019
Solver => Model % Solvers(i)
3020
3021
IF ( Solver % PROCEDURE==0 ) CYCLE
3022
IF ( .NOT. ASSOCIATED( Solver % Variable ) ) CYCLE
3023
3024
RungeKutta = .FALSE.
3025
IF ( TransientSimulation .AND. Solver % TimeOrder == 1 ) THEN
3026
RungeKutta = ListGetString( Solver % Values, &
3027
'Timestepping Method', Found ) == 'runge-kutta'
3028
END IF
3029
3030
IF ( .NOT. RungeKutta ) CYCLE
3031
3032
CALL Info('SolveEquations','Solver '//I2S(i)//' is Runge-Kutta Solver',Level=12)
3033
IF ( .NOT. ALLOCATED(RKCoeff) ) THEN
3034
ALLOCATE(RKCoeff(nSolvers), RK2_ErrorEstimate(nSolvers))
3035
END IF
3036
3037
n = SIZE(Solver % Variable % Values)
3038
ALLOCATE(RKCoeff(i) % k1(n), RKCoeff(i) % k2(n), RKCoeff(i) % k3(n), &
3039
RKCoeff(i) % k4(n) )
3040
3041
RKorder = Solver % Order
3042
k1 => RKCoeff(i) % k1
3043
k1 = Solver % Variable % Values-Solver % Variable % PrevValues(:,1)
3044
Solver % Variable % Values = Solver % Variable % PrevValues(:,1) + 2*k1/RKorder
3045
END DO
3046
3047
IF ( .NOT. ALLOCATED(RKCoeff) ) THEN
3048
CALL Fatal('SolveEquations','No Runge-Kutta after all in any Solver?')
3049
END IF
3050
3051
3052
CALL Info('SolveEquations','Using Runge-Kutta Order: '//I2S(RKOrder),Level=12)
3053
3054
IF(RKorder==4) THEN
3055
dt = dt / 2
3056
sTime = sTime + dt
3057
ELSE
3058
sTime = sTime + dt
3059
END IF
3060
CALL SolveCoupled()
3061
3062
RK2_ErrorEstimate = 0._dp
3063
RK2_err = 0.0_dp
3064
DO i=1,nSolvers
3065
Solver => Model % Solvers(i)
3066
IF(.NOT. ASSOCIATED(Solver % Variable)) CYCLE
3067
IF(.NOT. ASSOCIATED(Solver % Variable % PrevValues) ) CYCLE
3068
IF(Solver % Variable % Norm < 1.0d-20) CYCLE
3069
3070
IF ( .NOT. ALLOCATED(RKCoeff(i) % k1)) CYCLE
3071
3072
k1 => RKCoeff(i) % k1
3073
k2 => RKCoeff(i) % k2
3074
k2 = Solver % Variable % Values - Solver % Variable % PrevValues(:,1)
3075
SELECT CASE(RKorder)
3076
CASE(2)
3077
Solver % Variable % Values = Solver % Variable % PrevValues(:,1) + (k1+k2)/2
3078
RK2_Errorestimate(i) = SUM(((k2-k1)/2)**2)
3079
RK2_ErrorEstimate(i) = SQRT( ParallelReduction(RK2_ErrorEstimate(i)) ) / &
3080
Solver % Variable % Norm
3081
RK2_err = MAX(RK2_err, RK2_ErrorEstimate(i) )
3082
CASE(4)
3083
Solver % Variable % Values = Solver % Variable % PrevValues(:,1) + k2
3084
k2 = 2*k2
3085
END SELECT
3086
END DO
3087
3088
! Provide error measure for adaptive timestepping = ||RK2 (Heun) - Explicit Euler|| !
3089
IF ( RKorder == 2 ) THEN
3090
CALL ListAddConstReal( Model % Simulation, 'Adaptive Error Measure', RK2_err )
3091
END IF
3092
3093
3094
! For 4th order R-K we don't have error estimate but we have more steps to do
3095
IF( RKOrder == 4 ) THEN
3096
CALL SolveCoupled()
3097
3098
DO i=1,nSolvers
3099
Solver => Model % Solvers(i)
3100
IF ( .NOT. ALLOCATED(RKCoeff(i) % k1)) CYCLE
3101
3102
k3 => RKCoeff(i) % k3
3103
k3 = 2*(Solver % Variable % Values - Solver % Variable % PrevValues(:,1))
3104
Solver % Variable % Values = Solver % Variable % PrevValues(:,1) + k3
3105
END DO
3106
3107
sTime = sTime + dt
3108
dt = 2 * dt
3109
CALL SolveCoupled()
3110
3111
DO i=1,nSolvers
3112
Solver => Model % Solvers(i)
3113
IF ( .NOT. ALLOCATED(RKCoeff(i) % k1)) CYCLE
3114
3115
k1 => RKCoeff(i) % k1
3116
k2 => RKCoeff(i) % k2
3117
k3 => RKCoeff(i) % k3
3118
k4 => RKCoeff(i) % k4
3119
k4 = Solver % Variable % Values - Solver % Variable % PrevValues(:,1)
3120
3121
Solver % Variable % Values = Solver % Variable % PrevValues(:,1) + &
3122
( k1 + 2*k2 + 2*k3 + k4 ) / 6
3123
END DO
3124
END IF
3125
3126
DO i=1,nSolvers
3127
Solver => Model % Solvers(i)
3128
IF ( ALLOCATED(RKCoeff(i) % k1) ) THEN
3129
DEALLOCATE(RKCoeff(i) % k1, RKCoeff(i) % k2, RKCoeff(i) % k3, RKCoeff(i) % k4)
3130
END IF
3131
3132
IF( ASSOCIATED( Solver % Variable ) ) THEN
3133
IF(ASSOCIATED(Solver % Variable % Values)) THEN
3134
n = SIZE(Solver % Variable % Values)
3135
IF(n>0) &
3136
Solver % Variable % Norm = ComputeNorm( Solver, n, Solver % Variable % Values)
3137
END IF
3138
END IF
3139
END DO
3140
DEALLOCATE(RKCoeff)
3141
END IF
3142
3143
IF ( .NOT.TransientSimulation ) SteadyStateReached = ALL(DoneThis)
3144
DEALLOCATE( DoneThis, AfterConverged )
3145
END IF
3146
!------------------------------------------------------------------------------
3147
3148
3149
!------------------------------------------------------------------------------
3150
IF( PRESENT( AfterTime ) ) THEN
3151
ExecSlot = AfterTime
3152
ELSE
3153
ExecSlot = .TRUE.
3154
END IF
3155
3156
IF( ExecSlot ) THEN
3157
CALL Info('SolveEquations','Solvers after timestep',Level=12)
3158
DO k=1,nSolvers
3159
Solver => Model % Solvers(k)
3160
IF ( Solver % PROCEDURE==0 ) CYCLE
3161
IF ( Solver % SolverExecWhen == SOLVER_EXEC_AFTER_TIME .OR. &
3162
Solver % SolverExecWhen == SOLVER_EXEC_PREDCORR ) THEN
3163
3164
IF( PRESENT( RealTimeStep ) ) THEN
3165
IF( RealTimeStep == 1 ) THEN
3166
IF( ListGetLogical( Solver % Values,'Skip First Timestep',Found ) ) CYCLE
3167
END IF
3168
END IF
3169
3170
IF( Solver % SolverExecWhen == SOLVER_EXEC_PREDCORR ) THEN
3171
CALL InitializeTimestep(Solver)
3172
CALL ListAddLogical( Solver % Values,'Predictor Phase',.FALSE.)
3173
END IF
3174
3175
CALL SolverActivate( Model,Solver,dt,TransientSimulation )
3176
CALL ParallelBarrier
3177
END IF
3178
END DO
3179
END IF
3180
3181
ParEnv_Common = ParEnv_Save
3182
ParEnv => ParEnv_Common
3183
IF(ParEnv % PEs>1) THEN
3184
IF(.NOT.ASSOCIATED(ParEnv % Active)) ALLOCATE(ParEnv % Active(ParEnv % PEs))
3185
ParEnv % Active = .TRUE.
3186
ParEnv % ActiveComm = ELMER_COMM_WORLD
3187
END IF
3188
3189
CONTAINS
3190
3191
SUBROUTINE SolveCoupled()
3192
3193
TYPE(Mesh_t), POINTER, SAVE :: PrevMesh
3194
INTEGER, SAVE :: PrevMeshNoNodes
3195
LOGICAL, SAVE :: FirstTime=.TRUE.
3196
3197
IF(FirstTime) THEN
3198
PrevMesh => Model % Mesh
3199
PrevMeshNoNodes = Model % Mesh % NumberOfNodes
3200
FirstTime = .FALSE.
3201
END IF
3202
3203
!------------------------------------------------------------------------------
3204
3205
CALL Info('SolveEquations','Performing set of solvers in sequence',Level=12)
3206
3207
DO i=1,CoupledMaxIter
3208
IF ( TransientSimulation .OR. Scanning ) THEN
3209
IF( CoupledMaxIter > 1 ) THEN
3210
CALL Info( 'SolveEquations', '-------------------------------------', Level=3 )
3211
WRITE( Message, * ) 'Coupled system iteration: ', i
3212
CALL Info( 'SolveEquations', Message, Level=3 )
3213
CALL Info( 'SolveEquations', '-------------------------------------', Level=3 )
3214
END IF
3215
steadyIt = i
3216
END IF
3217
3218
IF( GetNamespaceCheck() ) CALL ListPushNamespace('coupled '//i2s(i)//': ')
3219
3220
DoneThis = .FALSE.
3221
3222
! Initialize the mesh output flag to FALSE here, reactivated
3223
! later for meshes connected to active solvers.
3224
! ----------------------------------------------------------
3225
Mesh => Model % Meshes
3226
DO WHILE( ASSOCIATED( Mesh ) )
3227
Mesh % OutputActive = .FALSE.
3228
Mesh => Mesh % Next
3229
END DO
3230
3231
!------------------------------------------------------------------------------
3232
! Go through number of solvers (heat,laminar or turbulent flow, etc...)
3233
!------------------------------------------------------------------------------
3234
DO k=1,Model % NumberOfSolvers
3235
!------------------------------------------------------------------------------
3236
Solver => Model % Solvers(k)
3237
3238
IF ( Solver % PROCEDURE == 0 ) THEN
3239
IF( .NOT. ( Solver % SolverMode == SOLVER_MODE_COUPLED .OR. &
3240
Solver % SolverMode == SOLVER_MODE_ASSEMBLY .OR. &
3241
Solver % SolverMode == SOLVER_MODE_BLOCK ) ) THEN
3242
3243
CALL Warn('SolveEquations','No routine related to solver!')
3244
DoneThis(k) = .TRUE.
3245
CYCLE
3246
END IF
3247
END IF
3248
3249
IF( PRESENT( RealTimeStep ) ) THEN
3250
IF( RealTimeStep == 1 ) THEN
3251
IF( ListGetLogical( Solver % Values,'Skip First Timestep',Found ) ) THEN
3252
DoneThis(k) = .TRUE.
3253
CYCLE
3254
END IF
3255
END IF
3256
END IF
3257
3258
IF ( Solver % SolverExecWhen /= SOLVER_EXEC_ALWAYS ) THEN
3259
DoneThis(k) = .TRUE.
3260
CYCLE
3261
END IF
3262
3263
IF ( AfterConverged(k) .AND. .NOT. ALL(AfterConverged .OR. DoneThis) ) CYCLE
3264
!------------------------------------------------------------------------------
3265
3266
n = 0
3267
IF ( ASSOCIATED(Solver % Variable) ) THEN
3268
IF ( ASSOCIATED(Solver % Variable % Values) ) &
3269
n = SIZE(Solver % Variable % Values)
3270
Solver % Variable % PrevNorm = Solver % Variable % Norm
3271
END IF
3272
3273
! There are some operations that require that the previous steady state values
3274
! are present. Check for these operations.
3275
!------------------------------------------------------------------------------
3276
CalculateDerivative = ListGetLogical( Solver % Values, &
3277
'Calculate Derivative', Stat )
3278
IF( CalculateDerivative .AND. .NOT. Scanning ) THEN
3279
CALL Fatal('SolveEquations','> Calculate Derivative < should be active only for scanning!')
3280
END IF
3281
3282
NeedSol = CalculateDerivative
3283
IF(.NOT. NeedSol ) THEN
3284
NeedSol = ( ListGetString( Solver % Values, &
3285
'Steady State Convergence Measure', Stat ) /= 'norm')
3286
NeedSol = NeedSol .AND. Stat
3287
END IF
3288
IF(.NOT. NeedSol ) THEN
3289
NeedSol = ListCheckPresent( Solver % Values, &
3290
'Steady State Relaxation Factor')
3291
END IF
3292
3293
IF ( NeedSol .AND. n > 0 ) THEN
3294
Stat = ASSOCIATED(Solver % Variable % SteadyValues)
3295
IF(Stat) THEN
3296
IF(SIZE(Solver % Variable % SteadyValues) /= n) THEN
3297
DEALLOCATE(Solver % Variable % SteadyValues)
3298
Stat = .FALSE.
3299
END IF
3300
END IF
3301
IF(.NOT. Stat) THEN
3302
ALLOCATE( Solver % Variable % SteadyValues(n), STAT=istat )
3303
IF ( istat /= 0 ) CALL Fatal( 'SolveEquations', 'Memory allocation error.' )
3304
END IF
3305
Solver % Variable % SteadyValues(1:n) = Solver % Variable % Values(1:n)
3306
END IF
3307
3308
! The solver may conditionally be turned into steady state
3309
! if the > Steady State Condition < is set positive.
3310
!------------------------------------------------------------------------
3311
TransientSolver = TransientSimulation
3312
IF( TransientSolver ) THEN
3313
SSCond = ListGetCReal( Solver % Values,'Steady State Condition',Found )
3314
IF( Found .AND. SSCond > 0.0_dp ) THEN
3315
TransientSolver = .FALSE.
3316
CALL Info('SolveEquations','Running solver in steady state',Level=6)
3317
END IF
3318
END IF
3319
3320
CALL SolverActivate(Model,Solver,dt,TransientSolver)
3321
3322
IF( TestDivergence ) THEN
3323
DivergenceExit = ( Solver % Variable % NonlinConverged > 1 )
3324
EXIT
3325
END IF
3326
3327
IF ( ASSOCIATED(Solver % Variable) ) THEN
3328
IF ( ASSOCIATED(Solver % Variable % Values) ) &
3329
n = SIZE(Solver % Variable % Values)
3330
END IF
3331
!------------------------------------------------------------------------------
3332
! check for coupled system convergence
3333
!------------------------------------------------------------------------------
3334
3335
IF ( Scanning .OR. TransientSimulation ) THEN
3336
IF( CoupledMaxIter == 1 ) THEN
3337
TestConvergence = .FALSE.
3338
! This means that the nonlinear system norm has not been computed
3339
IF( Solver % Variable % NonlinConverged < 0 ) THEN
3340
TestConvergence = ListCheckPresent( Solver % Values,'Reference Norm' )
3341
END IF
3342
IF(.NOT. TestConvergence) THEN
3343
TestConvergence = ListCheckPresent( Solver % Values,'Steady State Relaxation Factor')
3344
END IF
3345
ELSE
3346
TestConvergence = ( i >= CoupledMinIter )
3347
END IF
3348
ELSE ! Steady-state
3349
TestConvergence = .TRUE.
3350
END IF
3351
3352
IF( TestConvergence .OR. CalculateDerivative ) THEN
3353
IF ( ParEnv % PEs > 1 ) THEN
3354
IF ( ParEnv % Active(ParEnv % MyPE+1) ) THEN
3355
CALL ComputeChange(Solver,.TRUE., n)
3356
ELSE
3357
IF(.NOT.ASSOCIATED(Solver % Variable)) ALLOCATE(Solver % Variable)
3358
Solver % Variable % SteadyConverged = 1
3359
END IF
3360
ELSE
3361
CALL ComputeChange(Solver,.TRUE.)
3362
END IF
3363
END IF
3364
3365
! The ComputeChange subroutine sets a flag to zero if not yet
3366
! converged (otherwise -1/1)
3367
!------------------------------------------------------------
3368
IF( TestConvergence ) THEN
3369
DoneThis(k) = ( Solver % Variable % SteadyConverged /= 0 )
3370
END IF
3371
3372
IF( Solver % Mesh % AdaptiveFinished .AND. .NOT. DoneThis(k)) THEN
3373
CALL Info('SolveEquations','Overriding convergence due to Adaptive Meshing Finished!')
3374
DoneThis(k) = .TRUE.
3375
END IF
3376
3377
CALL ParallelAllReduceAnd( DoneThis(k) )
3378
IF( ALL(DoneThis) ) EXIT
3379
!------------------------------------------------------------------------------
3380
END DO
3381
!------------------------------------------------------------------------------
3382
CALL ListPopNamespace()
3383
3384
!Check if the mesh changed - should do this elsewhere too?
3385
IF(ASSOCIATED(Model % Mesh, PrevMesh) .AND. &
3386
Model % Mesh % NumberOfNodes == PrevMeshNoNodes) THEN
3387
Model % Mesh % Changed = .FALSE.
3388
ELSE
3389
PrevMesh => Model % Mesh
3390
PrevMeshNoNodes = Model % Mesh % NumberOfNodes
3391
Model % Mesh % Changed = .TRUE.
3392
END IF
3393
3394
IF( DivergenceExit ) EXIT
3395
3396
IF ( ALL(DoneThis) ) EXIT
3397
END DO
3398
3399
IF( TestConvergence .AND. CoupledMaxIter > 1 ) THEN
3400
IF ( TransientSimulation .AND. .NOT. ALL(DoneThis) ) THEN
3401
CALL Info( 'SolveEquations','Coupled system iteration: '//I2S(MIN(i,CoupledMaxIter)),Level=4)
3402
CoupledAbort = ListGetLogical( Model % Simulation, &
3403
'Coupled System Abort Not Converged', Found )
3404
CALL NumericalError('SolveEquations','Coupled system did not converge',CoupledAbort)
3405
END IF
3406
END IF
3407
3408
END SUBROUTINE SolveCoupled
3409
!------------------------------------------------------------------------------
3410
END SUBROUTINE SolveEquations
3411
!------------------------------------------------------------------------------
3412
3413
3414
!------------------------------------------------------------------------------
3415
!> This is a line of monolithic solvers where different physics and constraints
3416
!> are assemblied to the same matrix.
3417
!> Provide assembly loop and solution of linear and nonlinear systems
3418
!> This routine uses minimalistic assembly routines to create the
3419
!> matrices. Often the results to less labour in coding which may be
3420
!> comprimized by less flexibility.
3421
!
3422
! There are currently two modes
3423
! IsCoupledSolver: the variable consists of several pieces and the matrix equation
3424
! is created within this solver.
3425
! IsAssemblySolver: only one assembly routine is used and the matrix equation may
3426
! be formed externally using standard procedures.
3427
! TODO:
3428
! non-nodal elements (internal allocation & right elementtype)
3429
! solution (and perm) vectors could be reused by setting pointers to correct positions
3430
! check the time-dependency
3431
! check for eigenmode analysis
3432
! different lumped functionalities (energy,...)
3433
! bandwidth optimization for coupled system
3434
!------------------------------------------------------------------------------
3435
SUBROUTINE CoupledSolver( Model, Solver, dt, Transient )
3436
!------------------------------------------------------------------------------
3437
IMPLICIT NONE
3438
!------------------------------------------------------------------------------
3439
TYPE(Solver_t), TARGET :: Solver
3440
TYPE(Model_t) :: Model
3441
REAL(KIND=dp) :: dt
3442
LOGICAL :: Transient
3443
!------------------------------------------------------------------------------
3444
! Local variables
3445
!------------------------------------------------------------------------------
3446
TYPE(Solver_t), POINTER :: PSolver, PSolver2
3447
TYPE(Variable_t), POINTER :: Var
3448
TYPE(Element_t),POINTER :: Element
3449
INTEGER :: i,j,k,l,t,n,nd,NoIterations,iter,istat
3450
REAL(KIND=dp), ALLOCATABLE :: STIFF(:,:), DAMP(:,:), MASS(:,:), FORCE(:)
3451
LOGICAL :: GotIt, GotIt2, GotProc, BulkMode, ThisConstraint, &
3452
IsCoupledSolver, IsAssemblySolver, IsProcedure, IsListMatrix
3453
INTEGER :: Row, Col, ColDofs, RowDofs, ColInd0, RowInd0, MaxDofs, &
3454
ColVar, RowVar, Nrow, Ncol, NoVar, NoCons, TotSize, ConDofs, OffSet(20), &
3455
VarSizes(20),VarDofs(20)
3456
INTEGER :: ElementsFirst, ElementsLast
3457
INTEGER, POINTER :: VarPerm(:), ColPerm(:), RowPerm(:), ColInds(:), RowInds(:), DirPerm(:)
3458
REAL(KIND=dp), POINTER :: ConsValues(:)
3459
REAL(KIND=dp) :: NonlinearTol, Norm, PrevNorm, ConsValue, ConsCoeff, ConsVolume
3460
CHARACTER(LEN=max_name_len) :: str, VarName, ColName, RowName, ConsType
3461
LOGICAL :: Coupling, Equality, AssemblySymmetric, AssemblyAntisymmetric, &
3462
AllDirFlag, Robust, ReduceStep
3463
INTEGER, POINTER :: Rows(:),Cols(:),Indexes(:),AllPerm(:)
3464
TYPE(ListMatrix_t), POINTER :: Alist(:) => NULL()
3465
REAL(KIND=dp), POINTER :: AllValues(:)
3466
REAL(KIND=dp), POINTER CONTIG :: ForceVector(:)
3467
LOGICAL, POINTER :: AllDir(:)
3468
TYPE (Matrix_t), POINTER :: Amat
3469
REAL(KIND=dp), POINTER :: Component(:)
3470
INTEGER, POINTER :: VarInds(:)
3471
TYPE(Mesh_t), POINTER :: Mesh
3472
TYPE(ValueList_t), POINTER :: SolverParams
3473
3474
SolverParams => ListGetSolverParams(Solver)
3475
3476
IsCoupledSolver = .FALSE.
3477
IsAssemblySolver = .FALSE.
3478
3479
IsProcedure = ListCheckPresent( SolverParams,'Procedure')
3480
3481
CALL Info('CoupledSolver','---------------------------------------',Level=5)
3482
IF( IsProcedure ) THEN
3483
CALL Info('CoupledSolver','Inheriting matrix from procedure',Level=5)
3484
ELSE IF( Solver % SolverMode == SOLVER_MODE_COUPLED ) THEN
3485
CALL Info('CoupledSolver','Solving a system of equations',Level=5)
3486
IsCoupledSolver = .TRUE.
3487
ELSE IF( Solver % SolverMode == SOLVER_MODE_ASSEMBLY ) THEN
3488
CALL Info('CoupledSolver','Solving one equation',Level=5)
3489
IsAssemblySolver = .TRUE.
3490
ELSE
3491
CALL Fatal('CoupledSolver','You should maybe not be here?')
3492
END IF
3493
CALL Info('CoupledSolver','---------------------------------------',Level=5)
3494
3495
3496
Mesh => GetMesh()
3497
PSolver => Solver
3498
3499
!------------------------------------------------------------------------------
3500
! Check out which variables the coupled model includes
3501
! and compure size information related to the new coupled dof.
3502
!------------------------------------------------------------------------------
3503
Offset = 0
3504
VarSizes = 0
3505
NoVar = 0
3506
NoCons = 0
3507
VarDofs = 0
3508
3509
AllDirFlag = .FALSE.
3510
IF( IsProcedure .OR. IsAssemblySolver ) THEN
3511
CALL Info('CoupledSolver','Using existing variable and matrix',Level=8)
3512
IF(.NOT. ASSOCIATED(Solver % Matrix)) THEN
3513
CALL Fatal('CoupledSolver','In legacy solver mode the Matrix should exist!')
3514
END IF
3515
IF(.NOT. ASSOCIATED(Solver % Variable)) THEN
3516
CALL Fatal('CoupledSolver','In legacy solver mode the Variable should exist!')
3517
END IF
3518
NoVar = 1
3519
Var => Solver % Variable
3520
VarDofs(1) = Var % Dofs
3521
VarSizes(1) = SIZE( Var % Values )
3522
TotSize = VarSizes(1)
3523
MaxDofs = VarDofs(1)
3524
ELSE
3525
3526
DO i = 1,100
3527
WRITE (str,'(A,I0)') 'Variable ',i
3528
VarName = ListGetString( SolverParams, TRIM(str), GotIt )
3529
IF(.NOT. GotIt) EXIT
3530
Var => VariableGet( Mesh % Variables, TRIM(VarName) )
3531
3532
IF(.NOT. ASSOCIATED( Var )) THEN
3533
CALL Info('CoupledSolver','Variable '//TRIM(VarName)//' does not exist, creating',Level=10)
3534
PSolver => Solver
3535
Var => CreateBlockVariable(PSolver, i, VarName )
3536
END IF
3537
3538
NoVar = NoVar + 1
3539
VarDofs(NoVar) = Var % Dofs
3540
VarSizes(NoVar) = SIZE( Var % Values )
3541
Offset(NoVar+1) = Offset(NoVar) + VarSizes(NoVar)
3542
END DO
3543
3544
!------------------------------------------------------------------------------------
3545
! Here is a hack for taking constraints into account where dofs are created on-the-fly
3546
! might be better to create special solvers somewhere else.
3547
! The size of constraint is deduced directly from its type.
3548
!-------------------------------------------------------------------------------------
3549
DO i = 1,100
3550
WRITE (str,'(A,I0)') 'Constraint ',i
3551
VarName = ListGetString( SolverParams, TRIM(str), GotIt )
3552
IF(.NOT. GotIt) EXIT
3553
3554
NoCons = NoCons + 1
3555
3556
WRITE (str,'(A,I0,A)') 'Constraint ',i,' Type'
3557
ConsType = ListGetString( SolverParams,TRIM(str))
3558
3559
SELECT CASE( ConsType )
3560
3561
CASE('integral')
3562
ConDofs = 1
3563
3564
CASE('floating')
3565
ConDofs = 1
3566
AllDirFlag = .TRUE.
3567
3568
CASE('equality')
3569
ConDofs = 0
3570
AllDirFlag = .TRUE.
3571
3572
CASE DEFAULT
3573
CALL Warn('CoupledSolver','Coupled constraint does not really work yet')
3574
WRITE (str,'(A,I0,A)') 'Constraint ',i,' DOFs'
3575
ConDofs = ListGetInteger( SolverParams, str)
3576
3577
END SELECT
3578
3579
! Create the constrained variables for possible other use
3580
!-----------------------------------------------------------
3581
IF( ConDofs > 0 ) THEN
3582
Var => VariableGet( Mesh % Variables, VarName )
3583
IF( .NOT. ASSOCIATED(Var) ) THEN
3584
CALL Info('CoupledSolver','Constraint '//TRIM(VarName)//' does not exist, creating',Level=10)
3585
ALLOCATE(ConsValues(ConDofs))
3586
CALL VariableAdd( Mesh % Variables, Mesh, Solver, &
3587
VarName, ConDofs, ConsValues, Output = .FALSE. )
3588
Var => VariableGet( Mesh % Variables, VarName )
3589
END IF
3590
END IF
3591
3592
j = NoVar + NoCons
3593
VarDofs(j) = ConDofs
3594
VarSizes(j) = 1
3595
Offset(j+1) = Offset(j) + VarSizes(j)
3596
END DO
3597
3598
DO j=1,NoVar+NoCons
3599
WRITE(Message,'(A,I0,A,T35,I0)') 'Permutation offset ',j,': ',OffSet(j)
3600
CALL Info('CoupledSolver',Message,Level=8)
3601
END DO
3602
3603
TotSize = SUM( VarSizes )
3604
MaxDofs = MAXVAL( VarDofs )
3605
3606
WRITE(Message,'(A,T35,I0)') 'Number of coupled variables: ',NoVar
3607
CALL Info('CoupledSolver',Message,Level=8)
3608
3609
WRITE(Message,'(A,T35,I0)') 'Number of constraints: ',NoCons
3610
CALL Info('CoupledSolver',Message,Level=8)
3611
3612
WRITE(Message,'(A,T35,I0)') 'Size of coupled system: ',TotSize
3613
CALL Info('CoupledSolver',Message,Level=8)
3614
3615
! For the 1st time the matrix should be a list, later CRS
3616
!--------------------------------------------------------
3617
IF(.NOT. ASSOCIATED(Solver % Matrix)) THEN
3618
Amat => AllocateMatrix()
3619
Amat % ListMatrix => Alist
3620
Amat % FORMAT = MATRIX_LIST
3621
Solver % Matrix => Amat
3622
END IF
3623
3624
! This actual variable is not saved, so a dummy name suffices
3625
!------------------------------------------------------------
3626
VarName = ListGetString( SolverParams,'Variable', GotIt )
3627
IF(.NOT. GotIt) THEN
3628
CALL Info('CoupledSolver','New coupled variable added: Alldofs', Level=6)
3629
VarName = 'alldofs'
3630
END IF
3631
3632
! If the variable hasn't been created do it now
3633
!----------------------------------------------
3634
Var => VariableGet(Mesh % Variables, VarName )
3635
IF(.NOT. ASSOCIATED(Var) ) THEN
3636
3637
ALLOCATE(AllPerm(TotSize),AllValues(TotSize),ForceVector(TotSize))
3638
DO i=1,TotSize
3639
AllPerm(i) = i
3640
AllValues(i) = 0.0_dp
3641
END DO
3642
CALL VariableAdd( Mesh % Variables, Mesh, Solver, &
3643
VarName,1,AllValues,AllPerm,Output=.FALSE.)
3644
Amat % Rhs => ForceVector
3645
Solver % Variable => VariableGet(Mesh % Variables, VarName )
3646
Var => Solver % Variable
3647
3648
! Map the original vectors to the monolithic vector if requested
3649
!----------------------------------------------------------------------------------
3650
IF( ListGetLogical( SolverParams,'Coupled Initial Guess',GotIt)) THEN
3651
CALL SingleToCoupledVector()
3652
END IF
3653
END IF
3654
3655
END IF ! IsProcedure
3656
3657
3658
!------------------------------------------------------------------------------
3659
! Do some initial stuff common for both modes
3660
!------------------------------------------------------------------------------
3661
3662
N = Mesh % MaxElementDOFs
3663
3664
ALLOCATE( FORCE( MaxDofs*N ), &
3665
STIFF( MaxDofs*N, MaxDofs*N ), &
3666
DAMP( MaxDofs*N, MaxDofs*N ), &
3667
MASS( MaxDofs*N, MaxDofs*N ), &
3668
ColInds( N ), RowInds( N ), &
3669
Indexes( n ), STAT=istat )
3670
IF ( istat /= 0 ) CALL FATAL('CoupledSolver','Memory allocation error')
3671
3672
NoIterations = GetInteger( SolverParams,'Nonlinear System Max Iterations',GotIt)
3673
IF(.NOT. GotIt) NoIterations = 1
3674
NonlinearTol = GetCReal( SolverParams,'Nonlinear System Convergence Tolerance',gotIt)
3675
Robust = ListGetLogical(SolverParams,'Nonlinear System Linesearch',GotIt)
3676
Amat => GetMatrix()
3677
ForceVector => Amat % Rhs
3678
IF( AllDirFlag ) ALLOCATE( AllDir(TotSize) )
3679
3680
!------------------------------------------------------------------------------
3681
! Iterate over any nonlinearity of material or source
3682
!------------------------------------------------------------------------------
3683
CALL Info('CoupledSolver','-------------------------------------------------',Level=5)
3684
3685
IsListMatrix = ( Amat % FORMAT == MATRIX_LIST )
3686
3687
DO iter = 1,NoIterations
3688
3689
WRITE(Message,'(A,T35,I5)') 'Coupled iteration:',iter
3690
CALL Info('CoupledSolver',Message,Level=5)
3691
3692
100 IF( IsProcedure ) THEN
3693
3694
! Perform the normal solver procedure with just one iteration
3695
! linear system solver being disabled i.e. just do the assembly.
3696
! Also the possible internal linesearch is disabled.
3697
!---------------------------------------------------------------------
3698
CALL ListAddInteger( SolverParams,'Nonlinear System Max Iterations',1)
3699
CALL ListAddLogical( SolverParams,'Linear System Solver Disabled',.TRUE.)
3700
CALL ListAddLogical( SolverParams,'Skip Compute Nonlinear Change',.TRUE.)
3701
IF( Robust ) THEN
3702
CALL ListRemove( SolverParams,'Nonlinear System Linesearch')
3703
END IF
3704
3705
CALL SingleSolver( Model, PSolver, dt, Transient )
3706
3707
CALL ListAddInteger( SolverParams,'Nonlinear System Max Iterations',NoIterations)
3708
CALL ListRemove( SolverParams,'Linear System Solver Disabled')
3709
IF(Robust ) THEN
3710
CALL ListAddLogical(SolverParams,'Nonlinear System Linesearch',.TRUE.)
3711
ELSE
3712
CALL ListAddLogical(SolverParams,'Skip Compute Nonlinear Change',.FALSE.)
3713
END IF
3714
3715
ELSE
3716
IF(.NOT. IsListMatrix ) CALL DefaultInitialize()
3717
IF( AllDirFlag ) AllDir = .FALSE.
3718
3719
!----------------------------------------------------------------
3720
! Here we use the same assembly for coupled system and block system
3721
! approach.
3722
!----------------------------------------------------------------
3723
DO RowVar = 1,NoVar
3724
DO ColVar = 1,NoVar
3725
CALL BlockSystemAssembly(PSolver,dt,Transient,RowVar,ColVar,&
3726
Offset(RowVar),Offset(ColVar))
3727
END DO
3728
END DO
3729
3730
!-------------------------------------------------------------------------
3731
! Tailored assembly routines for setting the constraints to the same matrix
3732
!-------------------------------------------------------------------------
3733
3734
IF( IsCoupledSolver .AND. NoCons > 0 ) THEN
3735
CALL CoupledConstraintAssembly()
3736
END IF
3737
3738
!----------------------------------------------------------------------
3739
! The CRS matrix may be created only when the matrix structure is known
3740
! and some initialization may be done only when the matrix exists
3741
!----------------------------------------------------------------------
3742
IF( IsListMatrix ) THEN
3743
CALL List_ToCRSMatrix(Amat)
3744
IsListMatrix = .FALSE.
3745
CALL AddEquationSolution(PSolver, Transient )
3746
GOTO 100
3747
END IF
3748
CALL DefaultFinishAssembly()
3749
3750
!------------------------------------------------------------------------------
3751
! Do the Dirichlet conditions using offset
3752
!------------------------------------------------------------------------------
3753
IF( IsCoupledSolver ) THEN
3754
CALL CoupledSystemDirichlet()
3755
ELSE
3756
CALL DefaultDirichletBCs()
3757
END IF
3758
END IF
3759
3760
!------------------------------------------------------------------------------
3761
! Check the stepsize of nonlinear iteration using the Armijo-GoldStein
3762
! criterion for the stepsize.
3763
!------------------------------------------------------------------------------
3764
IF( Robust ) THEN
3765
IF( CheckStepSize( Solver, iter == 1) ) THEN
3766
IF( IsCoupledSolver ) CALL CoupledToSingleVector()
3767
GOTO 100
3768
ELSE
3769
IF(Solver % Variable % NonlinConverged > 0) EXIT
3770
END IF
3771
END IF
3772
3773
!------------------------------------------------------------------------------
3774
! Finally solve the system
3775
!------------------------------------------------------------------------------
3776
Norm = DefaultSolve()
3777
CALL Info('CoupledSolver','-------------------------------------------------',Level=5)
3778
3779
IF( .NOT. ( IsAssemblySolver .OR. IsProcedure ) ) THEN
3780
CALL CoupledToSingleVector()
3781
END IF
3782
3783
! The non-robust solver will use one assembly routine less
3784
IF(.NOT. Robust) THEN
3785
IF(Solver % Variable % NonlinChange < NonlinearTol) EXIT
3786
END IF
3787
END DO
3788
3789
3790
DEALLOCATE( FORCE, STIFF, DAMP, MASS, ColInds, RowInds, Indexes )
3791
IF( AllDirFlag ) DEALLOCATE( AllDir )
3792
3793
CALL Info('CoupledSolver','All done',Level=8)
3794
CALL Info('CoupledSolver','-------------------------------------------------',Level=5)
3795
3796
3797
CONTAINS
3798
3799
!------------------------------------------------------------------------------
3800
!> Integration routine for integral type of constraints i.e. body or boundary
3801
!> integral of some dof is known a priori.
3802
!------------------------------------------------------------------------------
3803
SUBROUTINE IntegralConstraint( Mass, Damp, Stiff, Force, Element, n )
3804
!------------------------------------------------------------------------------
3805
3806
IMPLICIT NONE
3807
!------------------------------------------------------------------------------
3808
REAL(KIND=dp) :: Stiff(:,:), Damp(:,:), Mass(:,:), Force(:)
3809
TYPE(Element_t), POINTER :: Element
3810
INTEGER :: n
3811
!------------------------------------------------------------------------------
3812
! Local variables
3813
!------------------------------------------------------------------------------
3814
TYPE(GaussIntegrationPoints_t) :: IntegStuff
3815
REAL(KIND=dp) :: Basis(n)
3816
REAL(KIND=dp) :: Weight, DetJ
3817
INTEGER :: i,j,t,p,q
3818
LOGICAL :: Found
3819
3820
TYPE(Nodes_t),SAVE :: Nodes
3821
3822
CALL GetElementNodes( Nodes )
3823
IntegStuff = GaussPoints( Element )
3824
3825
DO t=1,IntegStuff % n
3826
Found = ElementInfo( Element, Nodes, IntegStuff % u(t), &
3827
IntegStuff % v(t), IntegStuff % w(t), detJ, Basis )
3828
Weight = IntegStuff % s(t) * detJ
3829
DO p=1,n
3830
STIFF(1,p) = STIFF(1,p) + Weight * Basis(p)
3831
FORCE(p) = FORCE(p) + ConsValue * Weight * Basis(p)
3832
END DO
3833
ConsVolume = ConsVolume + Weight
3834
END DO
3835
3836
!------------------------------------------------------------------------------
3837
END SUBROUTINE IntegralConstraint
3838
!------------------------------------------------------------------------------
3839
3840
3841
!------------------------------------------------------------------------------
3842
!> Subroutine sets the Dirichlet conditions for the coupled system
3843
!> using the single system vector names and sizes.
3844
!----------------------------------------------------------------------------------
3845
SUBROUTINE CoupledSystemDirichlet()
3846
CALL Info( 'CoupledSolver', 'Setting coupled system Dirichlet conditions', Level=6 )
3847
3848
DO i = 1,NoVar
3849
WRITE (str,'(A,I0)') 'Variable ',i
3850
VarName = ListGetString( SolverParams, TRIM(str), GotIt )
3851
IF(.NOT. GotIt) EXIT
3852
3853
Var => VariableGet( Mesh % Variables, TRIM(VarName) )
3854
IF (.NOT. ASSOCIATED(Var)) EXIT
3855
3856
CALL DefaultDirichletBCs( Ux=Var, UOffset=Offset(i) )
3857
END DO
3858
3859
END SUBROUTINE CoupledSystemDirichlet
3860
3861
3862
!------------------------------------------------------------------------------
3863
! Subroutine copies results from the single system vectors to a coupled system
3864
! initial guess.
3865
!----------------------------------------------------------------------------------
3866
SUBROUTINE SingleToCoupledVector()
3867
CALL Info('CoupledSolver','Copying an initial guess',Level=8)
3868
3869
DO i = 1,NoVar + NoCons
3870
IF( VarDofs(i) == 0 ) CYCLE
3871
3872
IF( i <= NoVar ) THEN
3873
WRITE (str,'(A,I0)') 'Variable ',i
3874
ELSE
3875
WRITE (str,'(A,I0)') 'Constraint ',i-NoVar
3876
END IF
3877
VarName = ListGetString( SolverParams, TRIM(str), GotIt )
3878
Var => VariableGet( Mesh % Variables, TRIM(VarName) )
3879
3880
! CALL Info('CoupledSolver','Copying from variable: '//TRIM(VarName),Level=5)
3881
DO j=1,SIZE(Var % Values)
3882
Solver % Variable % Values(Offset(i)+j) = Var % Values(j)
3883
END DO
3884
END DO
3885
3886
END SUBROUTINE SingleToCoupledVector
3887
3888
3889
!------------------------------------------------------------------------------
3890
!> Subroutine copies results from the coupled system vector back to the
3891
!> original vectors.
3892
!----------------------------------------------------------------------------------
3893
SUBROUTINE CoupledToSingleVector()
3894
CALL Info('CoupledSolver','Copying results into original variables',Level=8)
3895
DO i = 1,NoVar + NoCons
3896
IF( VarDofs(i) == 0 ) CYCLE
3897
3898
IF( i <= NoVar ) THEN
3899
WRITE (str,'(A,I0)') 'Variable ',i
3900
ELSE
3901
WRITE (str,'(A,I0)') 'Constraint ',i-NoVar
3902
END IF
3903
VarName = ListGetString( SolverParams, TRIM(str), GotIt )
3904
Var => VariableGet( Mesh % Variables, TRIM(VarName) )
3905
3906
! CALL Info('CoupledSolver','Copying to variable: '//TRIM(VarName),Level=5)
3907
DO j=1,SIZE(Var % Values)
3908
Var % Values(j) = Solver % Variable % Values(Offset(i)+j)
3909
END DO
3910
3911
CALL InvalidateVariable( Model % Meshes, Solver % Mesh, VarName )
3912
END DO
3913
3914
END SUBROUTINE CoupledToSingleVector
3915
3916
3917
!-----------------------------------------------------------------------------------
3918
!> Perform constraints assembly
3919
!> Some constraints are assembled by integration while others are done by elimination.
3920
!> It is important that constraints are applied after the normal assembly process.
3921
!-----------------------------------------------------------------------------------
3922
SUBROUTINE CoupledConstraintAssembly()
3923
!-----------------------------------------------------------------------------------
3924
INTEGER :: TargetDof, TmpInds(4)
3925
3926
CALL Info('CoupledSolver','Starting constraint assembly',Level=8)
3927
3928
BulkMode = .TRUE.
3929
200 IF(BulkMode) THEN
3930
! CALL Info('CoupledSolver','Starting constraint bulk assembly',Level=5)
3931
ElementsFirst = 1
3932
ElementsLast = Mesh % NumberOfBulkElements
3933
ELSE
3934
! CALL Info('CoupledSolver','Starting boundary assembly',Level=5)
3935
ElementsFirst = Mesh % NumberOfBulkElements + 1
3936
ElementsLast = Mesh % NumberOfBulkElements + &
3937
Mesh % NumberOfBoundaryElements
3938
END IF
3939
3940
! Variables over rows
3941
!-------------------------------------------
3942
DO RowVar = 1,NoCons
3943
3944
RowDofs = VarDofs(NoVar + RowVar)
3945
RowInd0 = Offset(NoVar + RowVar)
3946
3947
AssemblySymmetric = .FALSE.
3948
AssemblyAntiSymmetric = .FALSE.
3949
3950
WRITE (str,'(A,I0)') 'Constraint ',RowVar
3951
RowName = ListGetString( Solver % Values, TRIM(str), GotIt )
3952
3953
WRITE (str,'(A,I0,A)') 'Constraint ',RowVar,' Variables'
3954
VarInds => ListGetIntegerArray( Solver % Values, TRIM(str) )
3955
3956
IF( ASSOCIATED(VarInds)) THEN
3957
ColVar = VarInds(1)
3958
ELSE
3959
CALL Fatal('CoupledSolver','Cannot continue without pointer to variables')
3960
END IF
3961
3962
WRITE (str,'(A,I0,A)') 'Constraint ',RowVar,' Components'
3963
TargetDof = ListGetInteger( Solver % Values, TRIM(str), GotIt )
3964
3965
WRITE (str,'(A,I0,A)') 'Constraint ',RowVar,' Value'
3966
ConsValue = GetCReal(Solver % Values,TRIM(str), GotIt)
3967
3968
WRITE (str,'(A,I0,A)') 'Constraint ',RowVar,' Coeff'
3969
ConsCoeff = GetCReal(Solver % Values,TRIM(str), GotIt)
3970
IF(.NOT. GotIt) ConsCoeff = 1.0_dp
3971
3972
3973
IF ( ConsType == 'equality' ) THEN
3974
WRITE (str,'(A,I0)') 'Variable ',VarInds(2)
3975
VarName = ListGetString( SolverParams, TRIM(str) )
3976
Var => VariableGet( Mesh % Variables, TRIM(VarName) )
3977
RowPerm => Var % Perm
3978
RowDofs = VarDofs(VarInds(2))
3979
RowInd0 = Offset(VarInds(2))
3980
ELSE
3981
Nrow = 1
3982
RowInds(1:Nrow) = 1
3983
Var => VariableGet( Mesh % Variables, TRIM(RowName) )
3984
3985
! Add the diagonal entry expected by some subroutines
3986
!-------------------------------------------------------
3987
DO i=1,Nrow
3988
DO j=1,RowDofs
3989
Row = RowInd0 + RowDofs * (RowInds(i)-1) + j
3990
CALL AddToMatrixElement( Amat, Row, Row, 0.0_dp )
3991
END DO
3992
END DO
3993
END IF
3994
3995
IF( ConsType == 'integral') THEN
3996
AssemblySymmetric = .TRUE.
3997
ConsVolume = 0.0_dp
3998
END IF
3999
4000
WRITE (str,'(A,I0)') 'Variable ',ColVar
4001
ColName = ListGetString( Solver % Values, TRIM(str), GotIt )
4002
Var => VariableGet( Mesh % Variables, TRIM(ColName) )
4003
4004
! Constrain only the target variable
4005
!-----------------------------------------------
4006
4007
ColPerm => Var % Perm
4008
ColDofs = Var % Dofs
4009
ColInd0 = Offset(ColVar)
4010
4011
! The assembly loop for a submatrix starts here
4012
!------------------------------------------------------------------------------
4013
DO t=ElementsFirst,ElementsLast
4014
4015
Element => Mesh % Elements(t)
4016
Model % CurrentElement => Element
4017
4018
! How to treat non-nodal elements must be rethought
4019
! nd = GetElementNOFDOFs( Element, Solver )
4020
!-----------------------------------------------------------------
4021
n = GetElementNOFNodes()
4022
nd = GetElementDOFs(Indexes)
4023
4024
! Set the permutations for equality constraint
4025
!----------------------------------------------------
4026
IF( ConsType == 'equality') THEN
4027
Nrow = nd
4028
RowInds(1:Nrow) = RowPerm(Indexes)
4029
IF(.NOT. ALL(RowInds(1:Nrow) > 0)) CYCLE
4030
END IF
4031
4032
Ncol = nd
4033
ColInds(1:n) = ColPerm(Indexes)
4034
IF(.NOT. ALL(ColInds(1:Ncol) > 0)) CYCLE
4035
4036
4037
! Check where constraints are active, both bodies and BCs
4038
!-------------------------------------------------------------------
4039
Coupling = .FALSE.
4040
IF( BulkMode ) THEN
4041
! Check coupling to bodies using Body/Equation section
4042
Coupling = GetLogical( GetBodyForce(), RowName, gotIt)
4043
ELSE
4044
Coupling = GetLogical( GetBC(), RowName, gotIt)
4045
END IF
4046
IF( .NOT. Coupling ) CYCLE
4047
4048
! These two constraints are based on moving already assembled information
4049
! rather than assembling new information. If the row is already treated cycle.
4050
! The constrainst have been tested only with one-component cases.
4051
!-----------------------------------------------------------------------------
4052
IF( ConsType == 'floating' .OR. ConsType == 'equality') THEN
4053
4054
DO i=1,Ncol
4055
DO k=0,ColDofs-1
4056
4057
! Note that for this type the column is rather also a row
4058
!--------------------------------------------------------
4059
Col = ColInd0 + ColDofs * ColInds(i) - k
4060
IF( AllDir(Col) ) CYCLE
4061
AllDir(Col) = .TRUE.
4062
4063
IF( ConsType == 'floating') THEN
4064
Row = RowInd0 + 1
4065
ELSE IF( ConsType == 'equality') THEN
4066
Row = RowInd0 + RowDofs * RowInds(i) - k
4067
END IF
4068
4069
IF( IsListMatrix ) THEN
4070
CALL MoveRow( Amat, Col, Row )
4071
CALL SetMatrixElement( Amat,Col,Col,0.0_dp )
4072
CALL SetMatrixElement( Amat,Col,Row,0.0_dp )
4073
CYCLE
4074
END IF
4075
4076
CALL MoveRow( Amat, Col, Row, ConsCoeff )
4077
ForceVector(Row) = ForceVector(Row) + ForceVector(Col)
4078
ForceVector(Col) = 0.0_dp
4079
4080
ForceVector(Row) = ForceVector(Row) + ConsValue
4081
CALL SetMatrixElement( Amat,Col,Col,1.0_dp )
4082
CALL SetMatrixElement( Amat,Col,Row,-ConsCoeff)
4083
END DO
4084
END DO
4085
CYCLE
4086
END IF
4087
4088
4089
! Do the assembly, now really (active only for some constraints)
4090
!----------------------------------------------------------------
4091
4092
STIFF = 0.0_dp
4093
DAMP = 0.0_dp
4094
MASS = 0.0_dp
4095
FORCE = 0.0_dp
4096
4097
CALL IntegralConstraint( MASS, DAMP, STIFF, FORCE, Element, Ncol )
4098
4099
IF ( Transient ) THEN
4100
IF( Solver % TimeOrder == 1 ) THEN
4101
CALL Default1stOrderTime( MASS,STIFF,FORCE)
4102
ELSE IF( Solver % TimeOrder == 2) THEN
4103
CALL Default2ndOrderTime( MASS,DAMP,STIFF,FORCE )
4104
END IF
4105
END IF
4106
4107
4108
! Assemble the matrix with offset
4109
! Because we want to have constraints component-wise
4110
! There is somewhat dirty hack for calling the gluematrix
4111
!---------------------------------------------------------
4112
DO k=1,ColDofs
4113
IF( TargetDof /= 0 .AND. TargetDof /= k) CYCLE
4114
TmpInds(1:Ncol) = ColDofs * (ColInds(1:Ncol)-1) + k
4115
4116
IF( IsListMatrix ) THEN
4117
CALL GlueLocalSubMatrix( Amat, &
4118
RowInd0,ColInd0,Nrow,Ncol,RowInds,TmpInds,&
4119
RowDofs,1,STIFF )
4120
IF( AssemblySymmetric .OR. AssemblyAntisymmetric ) THEN
4121
CALL GlueLocalSubMatrix( Amat, &
4122
ColInd0,RowInd0,Ncol,Nrow,TmpInds,RowInds,&
4123
1,RowDofs,STIFF )
4124
END IF
4125
CYCLE
4126
END IF
4127
4128
CALL GlueLocalSubMatrix( Amat, &
4129
RowInd0,ColInd0,Nrow,Ncol,RowInds,TmpInds,&
4130
RowDofs,1,STIFF )
4131
4132
! For some constraints assemble also the transpose
4133
!--------------------------------------------------
4134
IF( AssemblySymmetric ) THEN
4135
CALL GlueLocalSubMatrix( Amat, &
4136
ColInd0,RowInd0,Ncol,Nrow,TmpInds,RowInds,&
4137
1,RowDofs,TRANSPOSE(STIFF) )
4138
ELSE IF( AssemblyAntisymmetric ) THEN
4139
CALL GlueLocalSubMatrix( Amat, &
4140
ColInd0,RowInd0,Ncol,Nrow,TmpInds,RowInds,&
4141
1,RowDofs,-TRANSPOSE(STIFF) )
4142
END IF
4143
4144
! Assemble the r.h.s with offset
4145
!-----------------------------------------------
4146
DO i=1,Nrow
4147
DO j=1,RowDofs
4148
Row = RowInd0 + RowDofs * (RowInds(i)-1) + j
4149
ForceVector(Row) = ForceVector(Row) + &
4150
FORCE(RowDofs*(i-1)+j)
4151
END DO
4152
END DO
4153
4154
END DO
4155
END DO
4156
4157
! For constraints do some special setting
4158
!---------------------------------------------------
4159
IF( ConsType == 'integral' ) THEN
4160
! PRINT *,'Integral constraint sum of weights: ',ConsVolume
4161
DO i=1,Nrow
4162
DO j=1,RowDofs
4163
Row = RowInd0 + RowDofs * (RowInds(i)-1) + j
4164
ForceVector(Row) = ConsValue
4165
END DO
4166
END DO
4167
END IF
4168
END DO
4169
4170
IF(BulkMode) THEN
4171
! CALL Info( 'CoupledSolver', 'Bulk assembly done for constraints', Level=4 )
4172
BulkMode = .FALSE.
4173
GOTO 200
4174
ELSE
4175
! CALL Info( 'CoupledSolver', 'Boundary assembly done for constraints', Level=4 )
4176
END IF
4177
4178
END SUBROUTINE CoupledConstraintAssembly
4179
4180
!------------------------------------------------------------------------------
4181
END SUBROUTINE CoupledSolver
4182
!------------------------------------------------------------------------------
4183
4184
4185
4186
4187
!------------------------------------------------------------------------------
4188
!> This is a line of solvers where a matrix of matrices and a vector of vectors
4189
!> are created to allow different kinds of block strategies for the solvers.
4190
!> This strategy has optimal memory consumption even if block strategies are
4191
!> employed on the linear system level.
4192
!------------------------------------------------------------------------------
4193
SUBROUTINE BlockSolver( Model, Solver, dt, Transient )
4194
!------------------------------------------------------------------------------
4195
IMPLICIT NONE
4196
!------------------------------------------------------------------------------
4197
TYPE(Solver_t), TARGET :: Solver
4198
TYPE(Model_t) :: Model
4199
REAL(KIND=dp) :: dt
4200
LOGICAL :: Transient
4201
!------------------------------------------------------------------------------
4202
! Local variables
4203
!------------------------------------------------------------------------------
4204
4205
TYPE(Solver_t), POINTER :: PSolver
4206
TYPE(Variable_t), POINTER :: Var, SolverVar
4207
INTEGER :: i,j,k,l,n,nd,NonLinIter,tests,NoTests,iter
4208
LOGICAL :: GotIt, GotIt2, BlockPrec, BlockGS
4209
INTEGER :: ColVar, RowVar, NoVar, BlockDofs
4210
4211
REAL(KIND=dp) :: NonlinearTol, Norm, PrevNorm, Residual, PrevResidual, &
4212
TotNorm, MaxChange, alpha, beta, omega, rho, oldrho, s, r, PrevTotNorm, &
4213
Coeff
4214
CHARACTER(LEN=max_name_len) :: str, VarName, ColName, RowName
4215
LOGICAL :: Robust, LinearSearch, AcceptStep, IsProcedure, ScaleSystem,&
4216
ReuseMatrix, LS, InitDone
4217
INTEGER, POINTER :: VarPerm(:)
4218
4219
TYPE (Matrix_t), POINTER :: Amat, SolverMatrix
4220
TYPE(Mesh_t), POINTER :: Mesh
4221
TYPE(ValueList_t), POINTER :: SolverParams
4222
4223
CALL Info('BlockSolver','---------------------------------------',Level=5)
4224
IF( Solver % SolverMode /= SOLVER_MODE_BLOCK ) THEN
4225
CALL Fatal('BlockSolver','You should maybe not be here?')
4226
ELSE
4227
CALL Info('BlockSolver','Solving system of equations utilizing block strategies',Level=5)
4228
END IF
4229
CALL Info('BlockSolver','---------------------------------------',Level=5)
4230
4231
SolverParams => ListGetSolverParams(Solver)
4232
Mesh => Solver % Mesh
4233
PSolver => Solver
4234
4235
SolverRef => Solver
4236
4237
isParallel = ParEnv % PEs > 1
4238
4239
4240
! Determine some parameters related to the block strategy
4241
!------------------------------------------------------------------------------
4242
BlockPrec = GetLogical( SolverParams,'Block Preconditioner',GotIt)
4243
IF(.NOT. GotIt ) BlockPrec = .TRUE.
4244
BlockGS = GetLogical( SolverParams,'Block Gauss-Seidel',GotIt)
4245
IsProcedure = ListCheckPresent( SolverParams,'Procedure')
4246
4247
! Initialize the matrix
4248
! 1) Matrix is inherited from a monolithic matrix equation, or
4249
! 2) Matrix is assemblied using the compact assembly process
4250
!------------------------------------------------------------------------------
4251
IF( IsProcedure ) THEN
4252
BlockDofs = Solver % Variable % Dofs
4253
4254
CALL BlockInitMatrix( Solver, TotMatrix, BlockDofs )
4255
4256
NoVar = TotMatrix % NoVar
4257
4258
TotMatrix % Solver => Solver
4259
SolverMatrix => Solver % Matrix
4260
SolverVar => Solver % Variable
4261
4262
ELSE
4263
TotMatrix => Solver % BlockMatrix
4264
IF( .NOT.ASSOCIATED(TotMatrix)) THEN
4265
DO i = 1,9
4266
WRITE (str,'(A,I0)') 'Variable ',i
4267
IF(.NOT. ListCheckPresent( SolverParams, TRIM(str)) ) EXIT
4268
END DO
4269
NoVar = i-1
4270
4271
CALL BlockInitMatrix( Solver, TotMatrix, NoVar )
4272
TotMatrix % Solver => Solver
4273
4274
WRITE(Message,'(A,I0)') 'Number of coupled variables: ',NoVar
4275
CALL Info('BlockSolver',Message)
4276
IF( NoVar == 0 ) CALL Fatal('BlockSolver','No variables, nothing to do!')
4277
4278
! Create the matrix structures using the list structure as
4279
!------------------------------------------------------------------------------
4280
CALL Info('BlockSolver','Creating matrix structured using list matrices',Level=12)
4281
DO RowVar=1,NoVar
4282
Solver % Variable => TotMatrix % SubVector(RowVar) % Var
4283
4284
DO ColVar=1,NoVar
4285
Solver % Variable => TotMatrix % SubVector(ColVar) % Var
4286
Solver % Matrix => TotMatrix % Submatrix(RowVar,ColVar) % Mat
4287
4288
Amat => Solver % Matrix
4289
Amat % ListMatrix => NULL()
4290
Amat % FORMAT = MATRIX_LIST
4291
Amat % NumberOfRows = 0
4292
4293
CALL BlockSystemAssembly(PSolver,dt,Transient,RowVar,ColVar)
4294
4295
IF( .NOT. ASSOCIATED( Amat % ListMatrix ) ) THEN
4296
Amat % FORMAT = MATRIX_CRS
4297
ELSE
4298
CALL List_ToCRSMatrix(Amat)
4299
CALL AddEquationSolution(PSolver, Transient )
4300
END IF
4301
4302
END DO
4303
END DO
4304
END IF
4305
4306
! The variable and solver pointer should direct somewhere as they are used to
4307
! monitor the steady-state solution, for example. Therefore the user may
4308
! choose the component if not preferring one.
4309
!---------------------------------------------------------------------------
4310
RowVar = ListGetInteger( SolverParams,'Primary Variable',GotIt)
4311
IF(.NOT. GotIt) RowVar = 1
4312
SolverVar => TotMatrix % SubVector(RowVar) % Var
4313
SolverMatrix => TotMatrix % Submatrix(RowVar,RowVar) % Mat
4314
END IF
4315
4316
!------------------------------------------------------------------------------
4317
! This is the nonlinear loop that may be used either for true
4318
! nonlinearities.
4319
!------------------------------------------------------------------------------
4320
NonLinIter = GetInteger( SolverParams,'Nonlinear System Max Iterations',GotIt)
4321
IF(.NOT. GotIt) NonLinIter = 1
4322
NonlinearTol = GetCReal( SolverParams,'Nonlinear System Convergence Tolerance',gotIt)
4323
4324
Robust = ListGetLogical(SolverParams,'Nonlinear System Linesearch',GotIt)
4325
IF( Robust ) THEN
4326
LinearSearch = ListGetLogical( SolverParams,'Nonlinear System Linesearch Linear')
4327
NoTests = GetInteger( SolverParams,'Nonlinear System Linesearch Iterations',GotIt)
4328
IF(.NOT. GotIt) NoTests = NonLinIter
4329
CALL ListAddString(SolverParams,'Nonlinear System Convergence Measure','residual')
4330
CALL ListAddLogical(SolverParams,'Skip Compute Nonlinear Change',.TRUE.)
4331
END IF
4332
4333
CALL Info('BlockSolver','-------------------------------------------------',Level=6)
4334
Residual = -1.0_dp
4335
PrevResidual = -1.0_dp
4336
4337
DO iter = 1,NonLinIter
4338
4339
WRITE(Message,'(A,T35,I0)') 'Coupled iteration: ',iter
4340
CALL Info('BlockSolver',Message,Level=6)
4341
4342
tests = 0
4343
4344
! Assembly of matrices either using legacy or compact strategy
4345
!------------------------------------------------------------------
4346
100 IF( IsProcedure ) THEN
4347
4348
! Perform the normal solver procedure with just one iteration
4349
! linear system solver being disabled i.e. just do the assembly.
4350
!---------------------------------------------------------------------
4351
CALL ListAddInteger( SolverParams,'Nonlinear System Max Iterations',1)
4352
CALL ListAddLogical( SolverParams,'Linear System Solver Disabled',.TRUE.)
4353
4354
CALL SingleSolver( Model, PSolver, dt, Transient )
4355
4356
! Scaling of linear system. This could also be fused with the submatrix
4357
! picking procedure.
4358
!---------------------------------------------------------------------
4359
ScaleSystem = ListGetLogical( SolverParams,'Linear System Scaling',GotIt)
4360
IF(.NOT. GotIt) ScaleSystem = .TRUE.
4361
4362
IF( ScaleSystem ) THEN
4363
CALL Info('BlockSolver','Applying scaling',Level=8)
4364
CALL ScaleLinearSystem(Solver, SolverMatrix, &
4365
SolverMatrix % Rhs, Solver % Variable % Values )
4366
CALL ListAddLogical( Solver % Values,'Linear System Scaling',.FALSE.)
4367
END IF
4368
4369
CALL BlockPickMatrix( Solver, NoVar )
4370
4371
CALL ListAddInteger( SolverParams,'Nonlinear System Max Iterations',NonLinIter)
4372
CALL ListRemove( SolverParams,'Linear System Solver Disabled')
4373
ELSE
4374
4375
DO RowVar=1,NoVar
4376
Solver % Variable => TotMatrix % SubVector(RowVar) % Var
4377
DO ColVar=1,NoVar
4378
4379
Solver % Matrix => TotMatrix % Submatrix(RowVar,ColVar) % Mat
4380
IF( Solver % Matrix % NumberOfRows == 0 ) CYCLE
4381
Solver % Variable => TotMatrix % SubVector(ColVar) % Var
4382
CALL InitializeToZero(Solver % Matrix, Solver % Matrix % rhs)
4383
4384
CALL ListPushNameSpace('block:')
4385
CALL ListPushNameSpace('block '//i2s(RowVar)//i2s(ColVar)//':')
4386
CALL BlockSystemAssembly(PSolver,dt,Transient,RowVar,ColVar)
4387
4388
! Mainly sets the r.h.s. in transient case correctly
4389
CALL DefaultFinishAssembly()
4390
4391
CALL BlockSystemDirichlet(TotMatrix,RowVar,ColVar)
4392
CALL ListPopNameSpace(); CALL ListPopNameSpace()
4393
END DO
4394
END DO
4395
END IF
4396
4397
! The user may give a user defined preconditioner matrix
4398
!-----------------------------------------------------------
4399
CALL BlockPrecMatrix( Solver, NoVar )
4400
4401
IF (isParallel) THEN
4402
DO RowVar=1,NoVar
4403
DO ColVar=1,NoVar
4404
Amat => TotMatrix % SubMatrix(RowVar,ColVar) % Mat
4405
Amat % Comm = ELMER_COMM_WORLD
4406
Parenv % ActiveComm = Amat % Comm
4407
Solver % Variable => TotMatrix % SubVector(ColVar) % Var
4408
CALL ParallelInitMatrix(Solver,Amat)
4409
4410
Amat % ParMatrix % ParEnv % ActiveComm = Amat % Comm
4411
ParEnv => Amat % ParMatrix % ParEnv
4412
CALL ParallelActive( .TRUE.)
4413
END DO
4414
END DO
4415
END IF
4416
4417
4418
!------------------------------------------------------------------------------
4419
! Check the stepsize of nonlinear iteration using the Armijo-GoldStein
4420
! criterion for the stepsize, if requested
4421
!------------------------------------------------------------------------------
4422
IF( Robust ) THEN
4423
4424
200 AcceptStep = CheckStepSizeBlock(TotMatrix,tests==0,PrevResidual,Residual)
4425
tests = tests + 1
4426
4427
IF( tests > NoTests ) THEN
4428
CALL Fatal('BlockSolver','Maximum number of linesearch steps exceeded')
4429
END IF
4430
4431
! Update the reference residual only when new step is accepted
4432
IF( iter == 1 ) THEN
4433
PrevResidual = Residual
4434
ELSE
4435
IF( AcceptStep ) THEN
4436
PrevResidual = Residual
4437
IF(Solver % Variable % NonlinChange < NonlinearTol) EXIT
4438
ELSE
4439
IF( LinearSearch ) THEN
4440
GOTO 100
4441
ELSE
4442
GOTO 200
4443
END IF
4444
END IF
4445
END IF
4446
END IF
4447
4448
4449
!------------------------------------------------------------------------------
4450
! Finally solve the system using 'outer: ' as the optional namespace
4451
! for the linear system setting.
4452
!------------------------------------------------------------------------------
4453
4454
TotNorm = 0.0_dp
4455
MaxChange = 0.0_dp
4456
4457
CALL ListPushNameSpace('outer:')
4458
4459
! The case with one block is mainly for testing and developing features
4460
! related to nonlinearity and assembly.
4461
!----------------------------------------------------------------------
4462
IF( NoVar == 1 ) THEN
4463
CALL Info('BlockSolver','Solving in standard manner',Level=8)
4464
4465
Solver % Variable => TotMatrix % SubVector(1) % Var
4466
Solver % Matrix => TotMatrix % Submatrix(1,1) % Mat
4467
4468
TotNorm = DefaultSolve()
4469
MaxChange = Solver % Variable % NonlinChange
4470
4471
ELSE IF( BlockPrec ) THEN
4472
CALL Info('BlockSolver','Using block preconditioning strategy',Level=8)
4473
CALL BlockKrylovIter( Solver, MaxChange )
4474
4475
ELSE
4476
Solver % Variable => TotMatrix % SubVector(1) % Var
4477
Solver % Matrix => TotMatrix % Submatrix(1,1) % Mat
4478
4479
CALL Info('BlockSolver','Using block solution strategy',Level=8)
4480
CALL BlockStandardIter( Solver, MaxChange )
4481
END IF
4482
CALL ListPopNameSpace()
4483
4484
! For legacy matrices do the backmapping
4485
!------------------------------------------
4486
IF( IsProcedure ) THEN
4487
Solver % Matrix => SolverMatrix
4488
Solver % variable => SolverVar
4489
IF( ScaleSystem ) THEN
4490
CALL BackScaleLinearSystem(Solver, SolverMatrix, &
4491
SolverMatrix % Rhs, Solver % Variable % Values )
4492
CALL ListAddLogical( Solver % Values,'Linear System Scaling',.TRUE.)
4493
END IF
4494
END IF
4495
4496
IF(.NOT. Robust ) THEN
4497
IF( MaxChange < NonlinearTol) EXIT
4498
END IF
4499
END DO
4500
4501
! Before returning set the pointers appropriately as saved before
4502
!---------------------------------------------------------------------------
4503
Solver % Variable => SolverVar
4504
Solver % Matrix => SolverMatrix
4505
4506
CALL Info('BlockSolver','All done',Level=8)
4507
CALL Info('BlockSolver','-------------------------------------------------',Level=6)
4508
4509
4510
CONTAINS
4511
4512
4513
!------------------------------------------------------------------------------
4514
!> Subroutine sets the Dirichlet conditions for the block system
4515
!> using the single system vector names and sizes. For that aim there is an
4516
!> additional flag that is used to detect that the matrix cannot have the digonal
4517
!> entry that is by default set to unity and the r.h.s. to the target value.
4518
!> Now for off-diagonal matrices they will be both omitted.
4519
!> The routine assumes that the r.h.s. of the off diagonal matrices is zero.
4520
!----------------------------------------------------------------------------------
4521
SUBROUTINE BlockSystemDirichlet(BlockMatrix,NoRow,NoCol)
4522
4523
TYPE(BlockMatrix_t) :: BlockMatrix
4524
INTEGER :: NoRow, NoCol
4525
4526
LOGICAL :: OffDiagonal
4527
4528
CALL Info( 'BlockSolver', 'Setting block system Dirichlet conditions', Level=8 )
4529
4530
Solver % Matrix => BlockMatrix % SubMatrix( NoRow, NoCol ) % Mat
4531
IF( Solver % Matrix % NumberOfRows == 0 ) RETURN
4532
4533
WRITE (str,'(A,I0)') 'Variable ',NoRow
4534
VarName = ListGetString( SolverParams, TRIM(str), GotIt )
4535
IF(.NOT. GotIt) RETURN
4536
4537
Var => VariableGet( Mesh % Variables, TRIM(VarName) )
4538
IF (.NOT. ASSOCIATED(Var)) RETURN
4539
4540
Solver % Variable => Var
4541
OffDiagonal = ( NoRow /= NoCol )
4542
CALL DefaultDirichletBCs( Ux=Var, OffDiagonalMatrix = OffDiagonal )
4543
4544
END SUBROUTINE BlockSystemDirichlet
4545
4546
4547
!------------------------------------------------------------------------------
4548
!> Compute the block norm, optionally.
4549
!----------------------------------------------------------------------------------
4550
FUNCTION ComputeBlockNorm(BlockMatrix, DiagonalOnly, MatrixOnly ) RESULT ( Residual )
4551
4552
TYPE(BlockMatrix_t), TARGET :: BlockMatrix
4553
LOGICAL, OPTIONAL :: DiagonalOnly, MatrixOnly
4554
REAL(KIND=dp) :: Residual
4555
4556
REAL(KIND=dp) :: rnorm, xnorm, bnorm, TotXnorm, TotRnorm, TotBnorm, TotRelNorm
4557
REAL(KIND=dp), POINTER :: x(:),res(:),b(:),rtmp(:)
4558
INTEGER :: n, NoRow,NoCol, NoVar
4559
TYPE(Matrix_t), POINTER :: A
4560
4561
4562
CALL Info('BlockSolver','Computing block matrix norm',Level=8)
4563
4564
NoVar = BlockMatrix % NoVar
4565
ALLOCATE( rtmp( BlockMatrix % MaxSize ), res( BlockMatrix % MaxSize) )
4566
4567
DO NoRow = 1,NoVar
4568
Var => BlockMatrix % SubVector(NoRow) % Var
4569
n = SIZE( Var % Values )
4570
4571
x => Var % Values
4572
xNorm = ComputeNorm(Solver, n, x)
4573
4574
A => BlockMatrix % SubMatrix(NoRow,NoRow) % Mat
4575
Solver % Matrix => A
4576
b => A % rhs
4577
4578
xNorm = ComputeNorm(Solver, n, x)
4579
bNorm = ComputeNorm(Solver, n, b)
4580
4581
res = 0.0_dp
4582
rtmp = 0.0_dp
4583
4584
DO NoCol = 1,NoVar
4585
IF( PRESENT( DiagonalOnly ) ) THEN
4586
IF( DiagonalOnly .AND. NoCol /= NoRow ) CYCLE
4587
END IF
4588
4589
Var => BlockMatrix % SubVector(NoCol) % Var
4590
x => Var % Values
4591
4592
A => BlockMatrix % SubMatrix( NoRow, NoCol ) % Mat
4593
IF( A % NumberOfRows == 0 ) CYCLE
4594
b => A % rhs
4595
4596
CALL MatrixVectorMultiply( A, x, rtmp)
4597
res = res + rtmp
4598
END DO
4599
4600
IF( PRESENT( MatrixOnly ) ) THEN
4601
IF( .NOT. MatrixOnly ) THEN
4602
res = res - b
4603
END IF
4604
ELSE
4605
res = res - b
4606
END IF
4607
4608
rNorm = ComputeNorm(Solver, n, res)
4609
4610
! PRINT *,'comp norms:',NoRow,Rnorm,Xnorm
4611
4612
BlockMatrix % SubVector(NoRow) % rnorm = rnorm
4613
BlockMatrix % SubVector(NoRow) % xnorm = xnorm
4614
BlockMatrix % SubVector(NoRow) % bnorm = bnorm
4615
END DO
4616
4617
TotRnorm = SUM( BlockMatrix % SubVector(1:NoVar) % rnorm ** 2)
4618
TotXnorm = SUM( BlockMatrix % SubVector(1:NoVar) % xnorm ** 2)
4619
TotBnorm = SUM( BlockMatrix % SubVector(1:NoVar) % bnorm ** 2)
4620
4621
TotRnorm = SQRT( TotRnorm / NoVar )
4622
TotXnorm = SQRT( TotXnorm / NoVar )
4623
TotBnorm = SQRT( TotBnorm / NoVar )
4624
4625
BlockMatrix % Rnorm = TotRNorm
4626
BlockMatrix % Xnorm = TotXnorm
4627
BlockMatrix % Bnorm = TotBnorm
4628
4629
! PRINT *,'tot norms:',TotRnorm,TotXnorm
4630
DEALLOCATE( rtmp, res )
4631
4632
Residual = BlockMatrix % Rnorm
4633
4634
END FUNCTION ComputeBlockNorm
4635
4636
4637
4638
4639
!------------------------------------------------------------------------------
4640
FUNCTION CheckStepSizeBlock(BlockMatrix,FirstTrial,PrevResidual,Residual) &
4641
RESULT (Success)
4642
!------------------------------------------------------------------------------
4643
TYPE(BlockMatrix_t) :: BlockMatrix
4644
REAL(KIND=dp) :: PrevResidual, Residual
4645
LOGICAL :: FirstTrial,Success
4646
!------------------------------------------------------------------------------
4647
INTEGER :: i,n,niter
4648
TYPE(Matrix_t), POINTER :: A
4649
REAL(KIND=dp), POINTER :: b(:), x(:), x0(:), r(:)
4650
REAL(KIND=dp) :: Norm, PrevNorm, rNorm, bNorm, Relaxation, Alpha, Myy
4651
TYPE(Variable_t), POINTER :: iterV
4652
LOGICAL :: Stat
4653
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName
4654
4655
4656
SAVE Alpha, Relaxation, Myy
4657
4658
!--------------------------------------------------------------------------
4659
! This is the real residual r=b-Ax
4660
!--------------------------------------------------------------------------
4661
Residual = ComputeBlockNorm( BlockMatrix )
4662
4663
4664
! Negative (impossible) value may be used as indicator that's its the 1st step
4665
!-----------------------------------------------------------------------------
4666
IF( PrevResidual < 0.0 ) THEN
4667
Success = .FALSE.
4668
RETURN
4669
END IF
4670
4671
! At the first step set the relaxation
4672
!-----------------------------------------------------------------------------
4673
IF( FirstTrial ) THEN
4674
Alpha = 1.0_dp
4675
Relaxation = ListGetConstReal( Solver % Values, &
4676
'Nonlinear System Linesearch Factor', Stat )
4677
IF(.NOT. Stat) Relaxation = 0.5_dp
4678
Myy = ListGetConstReal( Solver % Values, &
4679
'Nonlinear System Linesearch Limit', Stat )
4680
IF(.NOT. Stat) Myy = 0.5_dp
4681
END IF
4682
4683
! Armijo GoldStein Criterion for accepting stepsize
4684
!-----------------------------------------------------------------
4685
Success = ( PrevResidual - Residual > Myy * Alpha * PrevResidual)
4686
4687
IF( Success ) THEN
4688
iterV => VariableGet( Solver % Mesh % Variables, 'nonlin iter' )
4689
niter = NINT(iterV % Values(1))
4690
4691
DO i=1,BlockMatrix % NoVar
4692
Var => BlockMatrix % SubVector(i) % Var
4693
b => BlockMatrix % SubMatrix(i,i) % Mat % rhs
4694
x => Var % Values
4695
n = SIZE( b )
4696
4697
Solver % Variable => Var
4698
bNorm = ComputeNorm(Solver, n, b)
4699
4700
Var % NonlinChange = Residual / bNorm
4701
4702
Norm = ComputeNorm(Solver, n, x)
4703
Solver % Variable % Norm = Norm
4704
4705
SolverName = ListGetString( Solver % Values, 'Equation',Stat)
4706
IF(.NOT. Stat) SolverName = Solver % Variable % Name
4707
4708
WRITE( Message, '(a,g15.8,g15.8,a,I2)') &
4709
'NS (ITER='//i2s(niter)//') (NRM,RELC): (',Norm, Residual / bNorm,&
4710
' ) :: '// TRIM(SolverName),i
4711
CALL Info( 'CheckStepSize', Message, Level=3 )
4712
END DO
4713
iterV % Values(1) = niter + 1
4714
ELSE
4715
4716
DO i=1,BlockMatrix % NoVar
4717
Var => BlockMatrix % SubVector(i) % Var
4718
IF(.NOT. ASSOCIATED(Var % NonlinValues)) &
4719
CALL Fatal('CheckStepSize','Previous nonlinear solution is needed')
4720
x0 => Var % NonlinValues
4721
4722
x => Var % Values
4723
4724
! PRINT *,'Before Range:',i,MINVAL(x),MAXVAL(x),MINVAL(x0),MAXVAL(x0)
4725
4726
x = (1-Relaxation) * x0 + Relaxation * x
4727
4728
! PRINT *,'After Range:',i,MINVAL(x),MAXVAL(x),MINVAL(x0),MAXVAL(x0)
4729
4730
END DO
4731
4732
Alpha = Alpha * Relaxation
4733
CALL Info( 'CheckStepSize','Step rejected, increasing relaxation', Level=5 )
4734
! PRINT *,'Residual',Residual,PrevResidual,Alpha
4735
4736
END IF
4737
4738
RowVar = ListGetInteger( SolverParams,'Primary Variable',GotIt)
4739
IF(.NOT. GotIt) RowVar = 1
4740
Solver % Matrix => TotMatrix % Submatrix(RowVar,RowVar) % Mat
4741
Solver % Variable => TotMatrix % SubVector(RowVar) % Var
4742
4743
4744
!------------------------------------------------------------------------------
4745
END FUNCTION CheckStepSizeBlock
4746
!------------------------------------------------------------------------------
4747
4748
!------------------------------------------------------------------------------
4749
END SUBROUTINE BlockSolver
4750
!------------------------------------------------------------------------------
4751
4752
!---------------------------------------------------
4753
!> Perform assembly for the block system linear
4754
!> system of equations.
4755
!---------------------------------------------------
4756
SUBROUTINE BlockSystemAssembly(Solver,dt,Transient,RowVar,ColVar,&
4757
RowIndOffset,ColIndOffset)
4758
!---------------------------------------------------
4759
TYPE(Solver_t), POINTER :: Solver
4760
REAL(KIND=dp) :: dt
4761
LOGICAL :: Transient
4762
INTEGER :: RowVar, ColVar
4763
INTEGER, OPTIONAL :: RowIndOffset,ColIndOffset
4764
4765
INTEGER :: RowInd0, ColInd0
4766
REAL(KIND=dp) :: SymmCoeff
4767
TYPE(Element_t), POINTER :: Element
4768
TYPE(Variable_t), POINTER :: Var
4769
TYPE(Matrix_t), POINTER :: Amat
4770
TYPE(Mesh_t), POINTER :: Mesh
4771
TYPE(Valuelist_t), POINTER :: SolverParams
4772
INTEGER :: i,j,t,n,nd,istat
4773
INTEGER :: ElementsFirst, ElementsLast, Row, Col, RowDofs, ColDofs, Ncol, Nrow
4774
INTEGER :: AllocCols, AllocRows
4775
INTEGER, POINTER :: ColPerm(:), RowPerm(:), Indexes(:),ColInds(:),RowInds(:)
4776
LOGICAL :: GotIt
4777
REAL(KIND=dp), ALLOCATABLE :: STIFF(:,:), DAMP(:,:), MASS(:,:), FORCE(:)
4778
REAL(KIND=dp), POINTER :: ForceVector(:)
4779
CHARACTER(LEN=MAX_NAME_LEN) :: ProcName, RowName, ColName, str
4780
INTEGER(KIND=AddrInt) :: ProcPntr
4781
LOGICAL :: BulkMode, AssemblySymmetric, AssemblyAntiSymmetric, IsListMatrix
4782
LOGICAL :: AllocationsDone = .FALSE., Diagonal
4783
4784
SAVE :: AllocationsDone, AllocCols, AllocRows, &
4785
FORCE, STIFF, DAMP, MASS, ColInds, RowInds, indexes
4786
4787
Mesh => Solver % Mesh
4788
SolverParams => Solver % Values
4789
Amat => Solver % Matrix
4790
ForceVector => Amat % rhs
4791
4792
RowInd0 = 0
4793
ColInd0 = 0
4794
IF( PRESENT( RowIndOffset ) ) RowInd0 = RowIndOffset
4795
IF( PRESENT( ColIndOffset ) ) ColInd0 = ColIndOffset
4796
4797
! Row Variable
4798
!-------------------------------------------
4799
WRITE (str,'(A,I0)') 'Variable ',RowVar
4800
RowName = ListGetString( SolverParams, TRIM(str), GotIt )
4801
Var => VariableGet( Mesh % Variables, TRIM(RowName) )
4802
IF(.NOT. ASSOCIATED( Var ) .AND. RowVar == 1 .AND. ColVar == 1 ) THEN
4803
Var => Solver % Variable
4804
END IF
4805
IF( .NOT. ASSOCIATED( Var ) ) THEN
4806
CALL Fatal('BlockSystemAssembly','Could not find variable: '//I2S(RowVar))
4807
END IF
4808
RowDofs = Var % Dofs
4809
RowPerm => Var % Perm
4810
IF( .NOT. ASSOCIATED( RowPerm ) ) THEN
4811
CALL Fatal('BlockSystemAssembly','Could not find permutation: '//I2S(RowVar))
4812
END IF
4813
4814
! Column variable
4815
!------------------------------------------
4816
IF( ColVar /= RowVar ) THEN
4817
WRITE (str,'(A,I0)') 'Variable ',ColVar
4818
ColName = ListGetString( SolverParams, TRIM(str), GotIt )
4819
Var => VariableGet( Mesh % Variables, TRIM(ColName) )
4820
END IF
4821
IF( .NOT. ASSOCIATED( Var ) ) THEN
4822
CALL Fatal('BlockSystemAssembly','Could not find variable: '//I2S(ColVar))
4823
END IF
4824
ColDofs = Var % Dofs
4825
ColPerm => Var % Perm
4826
IF( .NOT. ASSOCIATED( ColPerm ) ) THEN
4827
CALL Fatal('BlockSystemAssembly','Could not find permutation: '//I2S(ColVar))
4828
END IF
4829
4830
! These could be user provided for each block
4831
!-----------------------------------------
4832
AssemblySymmetric = ListGetLogical( SolverParams,'Symmetric Assembly',GotIt)
4833
AssemblyAntiSymmetric = ListGetLogical( SolverParams,'AntiSymmetric Assembly',GotIt)
4834
4835
IsListMatrix = ( Solver % Matrix % FORMAT == MATRIX_LIST )
4836
N = Mesh % MaxElementDOFs
4837
4838
IF( AllocationsDone ) THEN
4839
IF( RowDofs * n > AllocRows .OR. ColDofs * n > AllocCols ) THEN
4840
DEALLOCATE( FORCE, STIFF, DAMP, MASS, ColInds, RowInds )
4841
AllocationsDone = .FALSE.
4842
END IF
4843
END IF
4844
4845
IF( .NOT. AllocationsDone ) THEN
4846
AllocRows = RowDofs * n
4847
AllocCols = ColDofs * n
4848
ALLOCATE(Indexes(AllocRows))
4849
4850
ALLOCATE( FORCE( AllocRows ), &
4851
STIFF( AllocRows, AllocCols ), &
4852
DAMP( AllocRows, AllocCols ), &
4853
MASS( AllocRows, AllocCols ), &
4854
ColInds( N ), RowInds( N ), &
4855
STAT=istat )
4856
IF ( istat /= 0 ) CALL FATAL('BlockSystemAssembly','Memory allocation error')
4857
AllocationsDone = .TRUE.
4858
STIFF = 0.0_dp
4859
DAMP = 0.0_dp
4860
MASS = 0.0_dp
4861
FORCE = 0.0_dp
4862
ColInds = 0
4863
RowInds = 0
4864
END IF
4865
4866
CALL Info('BlockSystemAssembly','Starting block system assembly',Level=8)
4867
4868
BulkMode = .TRUE.
4869
4870
100 IF(BulkMode) THEN
4871
! CALL Info('BlockSystemAssembly','Starting bulk assembly',Level=5)
4872
ElementsFirst = 1
4873
ElementsLast = Mesh % NumberOFBulkElements
4874
ELSE
4875
! CALL Info('BlockSystemAssembly','Starting boundary assembly',Level=5)
4876
ElementsFirst = Mesh % NumberOFBulkElements+1
4877
ElementsLast = Mesh % NumberOfBulkElements + &
4878
Mesh % NumberOFBoundaryElements
4879
END IF
4880
4881
4882
! Load the assembly procudure
4883
!-----------------------------------------
4884
IF( BulkMode ) THEN
4885
WRITE (str,'(A,I0,I0)') 'Bulk Assembly Procedure ',RowVar,ColVar
4886
ELSE
4887
WRITE (str,'(A,I0,I0)') 'Boundary Assembly Procedure ',RowVar,ColVar
4888
END IF
4889
ProcName = ListGetString( SolverParams, TRIM(str), GotIt )
4890
4891
! Test if the 11 block is not given with 'ij' indexes
4892
!--------------------------------------------------------
4893
IF(.NOT. GotIt .AND. RowVar == 1 .AND. ColVar == 1) THEN
4894
IF( BulkMode ) THEN
4895
WRITE (str,'(A)') 'Bulk Assembly Procedure'
4896
ELSE
4897
WRITE (str,'(A)') 'Boundary Assembly Procedure'
4898
END IF
4899
ProcName = ListGetString( SolverParams, TRIM(str), GotIt )
4900
IF(.NOT. GotIt) THEN
4901
CALL Fatal('BlockSystemAssembly','Bulk Assembly Precedure not given!')
4902
END IF
4903
END IF
4904
4905
Diagonal = (RowVar == ColVar)
4906
4907
IF( .NOT. GotIt ) THEN
4908
IF( BulkMode .AND. Diagonal ) THEN
4909
CALL Warn('BlockSystemAssembly','Diagonal bulk entries should be assembled!')
4910
END IF
4911
RETURN
4912
END IF
4913
4914
ProcPntr = GetProcAddr( TRIM(ProcName), abort=.FALSE.)
4915
IF ( ProcPntr == 0 ) THEN
4916
CALL Fatal('BlockSystemAssembly','Assembly routine not found: '//TRIM(ProcName))
4917
ELSE
4918
CALL Info('BlockSystemAssembly','Using assembly routine: '//TRIM(ProcName),Level=8)
4919
END IF
4920
4921
! These may be fetched within the assembly routine, if needed
4922
CALL ListAddInteger( SolverParams,'Block Matrix Row',RowVar )
4923
CALL ListAddInteger( SolverParams,'Block Matrix Column',ColVar )
4924
4925
4926
! The assembly loop for a submatrix starts here
4927
!------------------------------------------------------------------------------
4928
DO t=ElementsFirst,ElementsLast
4929
4930
Element => Mesh % Elements(t)
4931
CurrentModel % CurrentElement => Element
4932
4933
!-----------------------------------------------------------------
4934
n = GetElementNOFNodes()
4935
nd = GetElementDOFs(Indexes)
4936
! Indexes => Element % NodeIndexes
4937
! n = Element % TYPE % NumberOfnodes
4938
! nd = n
4939
Nrow = nd
4940
4941
RowInds(1:Nrow) = RowPerm(Indexes(1:nd))
4942
IF(.NOT. ALL(RowInds(1:Nrow) > 0)) CYCLE
4943
4944
Ncol = nd
4945
ColInds(1:Ncol) = ColPerm(Indexes(1:nd))
4946
IF(.NOT. ALL(ColInds(1:Ncol) > 0)) CYCLE
4947
4948
! Here just the matrix structure is set, no values
4949
!---------------------------------------------------------------
4950
IF( IsListMatrix ) THEN
4951
CALL GlueLocalSubMatrix( Amat, &
4952
RowInd0,ColInd0,Nrow,Ncol,RowInds,ColInds,&
4953
RowDofs,ColDofs,STIFF )
4954
IF( AssemblySymmetric .OR. AssemblyAntiSymmetric ) THEN
4955
CALL GlueLocalSubMatrix( Amat, &
4956
ColInd0,RowInd0,Ncol,Nrow,ColInds,RowInds,&
4957
ColDofs,RowDofs,STIFF )
4958
END IF
4959
CYCLE
4960
END IF
4961
4962
! Do the assembly, now really
4963
!---------------------------------------------------------------
4964
STIFF = 0.0_dp
4965
DAMP = 0.0_dp
4966
MASS = 0.0_dp
4967
FORCE = 0.0_dp
4968
4969
CALL ExecLocalAssembly( ProcPntr, CurrentModel, Solver, &
4970
dt, Transient, MASS, DAMP, STIFF, FORCE, Element, &
4971
Nrow, Ncol )
4972
4973
IF ( Transient ) THEN
4974
IF( Solver % TimeOrder == 1 ) THEN
4975
CALL Default1stOrderTime( MASS,STIFF,FORCE)
4976
ELSE IF( Solver % TimeOrder == 2) THEN
4977
CALL Default2ndOrderTime( MASS,DAMP,STIFF,FORCE )
4978
END IF
4979
END IF
4980
4981
IF ( Solver % NOFEigenValues > 0 ) THEN
4982
IF( Solver % TimeOrder == 1 ) THEN
4983
CALL DefaultUpdateMass(MASS)
4984
ELSE IF( Solver % TimeOrder == 2) THEN
4985
CALL DefaultUpdateDamp(DAMP)
4986
CALL DefaultUpdateMass(MASS)
4987
END IF
4988
END IF
4989
4990
4991
! Assemble the matrix with offset
4992
!-----------------------------------------------
4993
IF( .TRUE. .OR. ColInd0 > 0 .OR. RowInd0 > 0 ) THEN
4994
CALL GlueLocalSubMatrix( Amat, &
4995
RowInd0,ColInd0,Nrow,Ncol,RowInds,ColInds,&
4996
RowDofs,ColDofs,STIFF )
4997
4998
! Assemble the r.h.s with offset
4999
!-----------------------------------------------
5000
DO i=1,Nrow
5001
DO j=1,RowDofs
5002
Row = RowInd0 + RowDofs * (RowInds(i)-1) + j
5003
ForceVector(Row) = ForceVector(Row) + &
5004
FORCE(RowDofs*(i-1)+j)
5005
END DO
5006
END DO
5007
5008
! For some constraints assemble also the transpose
5009
!--------------------------------------------------
5010
IF( AssemblySymmetric ) THEN
5011
CALL GlueLocalSubMatrix( Amat, &
5012
ColInd0,RowInd0,Ncol,Nrow,ColInds,RowInds,&
5013
ColDofs,RowDofs,TRANSPOSE(STIFF) )
5014
ELSE IF( AssemblyAntisymmetric ) THEN
5015
CALL GlueLocalSubMatrix( Amat, &
5016
ColInd0,RowInd0,Ncol,Nrow,ColInds,RowInds,&
5017
ColDofs,RowDofs,-TRANSPOSE(STIFF) )
5018
END IF
5019
ELSE
5020
CALL DefaultUpdateEquations( STIFF, FORCE )
5021
END IF
5022
5023
END DO
5024
5025
IF(BulkMode) THEN
5026
! CALL Info( 'BlockSystemAssembly', 'Bulk assembly done for blocks', Level=4 )
5027
BulkMode = .FALSE.
5028
! GOTO 100
5029
ELSE
5030
! CALL Info( 'BlockSystemAssembly', 'Boundary assembly done for blocks', Level=4 )
5031
END IF
5032
5033
END SUBROUTINE BlockSystemAssembly
5034
!-----------------------------------------------------------------------------------
5035
5036
5037
SUBROUTINE ExecSolverInSteps( Model, Solver, dt, TransientSimulation )
5038
!------------------------------------------------------------------------------
5039
TYPE(Model_t) :: Model
5040
TYPE(Solver_t),POINTER :: Solver
5041
LOGICAL :: TransientSimulation
5042
REAL(KIND=dp) :: dt
5043
!------------------------------------------------------------------------------
5044
INTEGER(KIND=AddrInt) :: SolverAddr
5045
CHARACTER(LEN=MAX_NAME_LEN) :: ProcName
5046
INTEGER :: iter, MaxIter
5047
LOGICAL :: Found
5048
!------------------------------------------------------------------------------
5049
INTEGER :: NColours, col
5050
REAL(KIND=dp) :: Norm
5051
5052
CALL Info('ExecSolverInSteps','Performing solution in steps',Level=6)
5053
ProcName = ListGetString( Solver % Values,'Procedure', Found )
5054
5055
MaxIter = ListGetInteger( Solver % Values,'Nonlinear System Max Iterations', Found )
5056
IF( .NOT. Found ) MaxIter = 1
5057
5058
DO iter = 1, MaxIter
5059
CALL DefaultInitialize( Solver )
5060
5061
IF( ASSOCIATED( Solver % ColourIndexList ) ) THEN
5062
ncolours = Solver % ColourIndexList % n
5063
ELSE
5064
ncolours = 1
5065
END IF
5066
Solver % CurrentColour = 0
5067
5068
SolverAddr = Solver % PROCEDURE
5069
DO col=1,ncolours
5070
! The > CurrentColour < is advanced by GetNOFActive() routine
5071
! to have similar interface as for non-steps solver
5072
CALL ExecSolver( SolverAddr, Model, Solver, dt, TransientSimulation)
5073
END DO
5074
CALL DefaultFinishBulkAssembly( Solver )
5075
5076
SolverAddr = GetProcAddr( TRIM(ProcName)//'_boundary', abort=.FALSE. )
5077
IF( SolverAddr /= 0 ) THEN
5078
CALL ExecSolver( SolverAddr, Model, Solver, dt, TransientSimulation)
5079
END IF
5080
5081
CALL DefaultFinishBoundaryAssembly( Solver )
5082
CALL DefaultFinishAssembly( Solver )
5083
CALL DefaultDirichletBCs( Solver )
5084
5085
Norm = DefaultSolve( Solver )
5086
5087
IF( Solver % Variable % NonlinConverged > 0 ) EXIT
5088
END DO
5089
5090
!SolverAddr = GetProcAddr( TRIM(ProcName)//'_post', abort=.FALSE. )
5091
!IF( SolverAddr /= 0 ) THEN
5092
! CALL ExecSolver( SolverAddr, Model, Solver, dt, TransientSimulation)
5093
!END IF
5094
5095
5096
END SUBROUTINE ExecSolverInSteps
5097
5098
5099
!------------------------------------------------------------------------------
5100
!> This executes the original line of solvers (legacy solvers) where each solver
5101
!> includes looping over elements and the convergence control. From generality
5102
!> point of view this misses some opportunities to have control of the nonlinear
5103
!> system.
5104
!------------------------------------------------------------------------------
5105
RECURSIVE SUBROUTINE SingleSolver( Model, Solver, dt, TransientSimulation )
5106
!------------------------------------------------------------------------------
5107
TYPE(Model_t) :: Model
5108
TYPE(Solver_t),POINTER :: Solver
5109
LOGICAL :: TransientSimulation
5110
REAL(KIND=dp) :: dt
5111
!------------------------------------------------------------------------------
5112
LOGICAL :: stat, Found, GB, MeActive, GotIt
5113
INTEGER :: i, j, k, l, col, row, n, BDOFs, maxdim, dsize, size0
5114
TYPE(Element_t), POINTER :: CurrentElement
5115
TYPE(ValueList_t), POINTER :: SolverParams
5116
INTEGER(KIND=AddrInt) :: SolverAddr
5117
CHARACTER(:), ALLOCATABLE :: EquationName
5118
5119
INTEGER, ALLOCATABLE :: memb(:)
5120
TYPE(Matrix_t), POINTER :: M
5121
INTEGER :: comm_active, group_active, group_world, ierr
5122
5123
LOGICAL :: ApplyMortar, FoundMortar, SlaveNotParallel, Parallel, UseOrigMesh
5124
TYPE(Matrix_t), POINTER :: CM, CM0, CM1, CMP
5125
TYPE(Mesh_t), POINTER :: Mesh
5126
5127
LOGICAL :: DoBC, DoBulk
5128
!------------------------------------------------------------------------------
5129
MeActive = ASSOCIATED(Solver % Matrix)
5130
IF ( MeActive ) MeActive = (Solver % Matrix % NumberOfRows > 0)
5131
5132
Parallel = Solver % Parallel
5133
!------------------------------------------------------------------------------
5134
5135
IF( Solver % Mesh % Changed .OR. Solver % NumberOfActiveElements <= 0 ) THEN
5136
Solver % NumberOFActiveElements = 0
5137
EquationName = ListGetString( Solver % Values, 'Equation', Found)
5138
5139
IF ( Found ) THEN
5140
5141
CALL SetActiveElementsTable( Model, Solver, MaxDim )
5142
CALL ListAddInteger( Solver % Values, 'Active Mesh Dimension', Maxdim )
5143
5144
! Calculate accumulated integration weights for bulk if requested
5145
DoBulk = ListGetLogical( Solver % Values,'Calculate Weights',Found )
5146
! Calculate weight for boundary
5147
DoBC = ListGetLogical( Solver % Values,'Calculate Boundary Weights',Found )
5148
5149
! In parallel we have to prepare the communicator already for the weights
5150
IF(DoBulk .OR. DoBC ) THEN
5151
IF ( Parallel .AND. MeActive ) THEN
5152
IF ( ASSOCIATED(Solver % Mesh % ParallelInfo % GInterface) ) THEN
5153
IF (.NOT. ASSOCIATED(Solver % Matrix % ParMatrix) ) &
5154
CALL ParallelInitMatrix(Solver, Solver % Matrix )
5155
ParEnv => Solver % Matrix % ParMatrix % ParEnv
5156
ParEnv % ActiveComm = Solver % Matrix % Comm
5157
END IF
5158
END IF
5159
END IF
5160
5161
IF(DoBulk) CALL CalculateNodalWeights(Solver,.FALSE.)
5162
IF(DoBC) CALL CalculateNodalWeights(Solver,.TRUE.)
5163
END IF
5164
END IF
5165
!------------------------------------------------------------------------------
5166
UseOrigMesh = ListGetLogical(Solver % Values,'Use Original Coordinates',Found )
5167
IF(UseOrigMesh ) THEN
5168
Mesh => Solver % Mesh
5169
IF(.NOT. ASSOCIATED(Mesh % NodesOrig)) THEN
5170
CALL Fatal('SingleSolver','Cannot toggle between meshes: NodesOrig not associated!')
5171
END IF
5172
IF(.NOT. ASSOCIATED(Mesh % NodesMapped)) THEN
5173
CALL Fatal('SingleSolver','Cannot toggle between meshes: NodesMapped not associated!')
5174
END IF
5175
Mesh % Nodes => Mesh % NodesOrig
5176
CALL Info('SingleSolver','Using stored original coordinate in solver')
5177
END IF
5178
5179
SlaveNotParallel = ListGetLogical( Solver % Values, 'Slave not parallel',Found )
5180
5181
IF ( Parallel .AND. .NOT. SlaveNotParallel ) THEN
5182
! Set the communicator and active info partitions.
5183
5184
BLOCK
5185
LOGICAL :: ChangedActiveParts
5186
5187
ChangedActiveParts = .FALSE.
5188
5189
!block partitions containing ONLY halos
5190
IF( MeActive ) THEN
5191
IF ( ListGetLogical( Solver % Values, 'Skip Halo Only Partitions', Found) ) THEN
5192
MeActive = .FALSE.
5193
DO i=1,Solver % NumberOfActiveElements
5194
IF(Solver % Mesh % Elements(Solver % ActiveElements(i)) % PartIndex==ParEnv % myPE) THEN
5195
MeActive = .TRUE.; EXIT
5196
END IF
5197
END DO
5198
IF(.NOT. MeActive) ChangedActiveParts = .TRUE.
5199
END IF
5200
END IF
5201
5202
IF ( ASSOCIATED(Solver % Matrix) ) THEN
5203
IF ( ASSOCIATED(Solver % Matrix % ParMatrix) ) THEN
5204
ParEnv => Solver % Matrix % ParMatrix % ParEnv
5205
END IF
5206
END IF
5207
5208
CALL ParallelActive( MeActive )
5209
n = COUNT(ParEnv % Active)
5210
5211
IF ( n>0 .AND. n<ParEnv % PEs ) THEN
5212
IF ( ASSOCIATED(Solver % Matrix) ) THEN
5213
IF ( Solver % Matrix % Comm /= ELMER_COMM_WORLD .AND. Solver % Matrix % Comm /= MPI_COMM_NULL ) &
5214
CALL MPI_Comm_Free( Solver % Matrix % Comm, ierr )
5215
END IF
5216
5217
CALL MPI_Comm_group( ELMER_COMM_WORLD, group_world, ierr )
5218
ALLOCATE(memb(n))
5219
n = 0
5220
DO i=1,ParEnv % PEs
5221
IF ( ParEnv % Active(i) ) THEN
5222
n=n+1
5223
memb(n)=i-1
5224
END IF
5225
END DO
5226
CALL MPI_Group_incl( group_world, n, memb, group_active, ierr)
5227
DEALLOCATE(memb)
5228
CALL MPI_Comm_create( ELMER_COMM_WORLD, group_active, &
5229
comm_active, ierr)
5230
5231
M => Solver % Matrix
5232
DO WHILE(ASSOCIATED(M))
5233
M % Comm = comm_active
5234
M => M % Parent
5235
END DO
5236
5237
IF( ANY( ParEnv % Active(MinOutputPE+1:MIN(MaxOutputPE+1,ParEnv % PEs)) ) ) THEN
5238
! If any of the active output partitions in active just use it.
5239
! Typically the 1st one. Others are passive.
5240
IF( ParEnv % MyPe >= MinOutputPE .AND. ParEnv % MyPe <= MaxOutputPE ) THEN
5241
OutputPE = ParEnv % MyPE
5242
ELSE
5243
OutputPE = -1
5244
END IF
5245
ELSE
5246
! Otherwise find the 1st active partition and if found use it.
5247
! Otherwise use the 0:th partition.
5248
DO i=1,ParEnv % PEs
5249
IF ( ParEnv % Active(i) ) EXIT
5250
END DO
5251
5252
OutputPE = -1
5253
IF ( i-1 == ParEnv % MyPE ) THEN
5254
OutputPE = i-1
5255
ELSE IF( i > ParEnv % PEs .AND. ParEnv % myPE == 0 ) THEN
5256
OutputPE = 0
5257
END IF
5258
END IF
5259
ELSE
5260
M => Solver % Matrix
5261
DO WHILE( ASSOCIATED(M) )
5262
M % Comm = ELMER_COMM_WORLD
5263
M => M % Parent
5264
END DO
5265
5266
IF(.NOT.ASSOCIATED(Solver % Matrix)) ParEnv % Active = .TRUE.
5267
5268
! Here set the default partitions active.
5269
IF( ParEnv % MyPe >= MinOutputPE .AND. &
5270
ParEnv % MyPe <= MaxOutputPE ) THEN
5271
OutputPE = ParEnv % MyPE
5272
ELSE
5273
OutputPE = -1
5274
END IF
5275
END IF
5276
5277
! POTENTIAL INCOMPATIBILITY: don't execute solvers for non-active partitions
5278
! (usually this is done within the solver by:
5279
! IF (.NOT. ASSOCIATED(Solver % Matrix) ) RETURN
5280
! or some such .... )
5281
5282
IF(.NOT. MeActive .AND. n>0) RETURN
5283
END BLOCK
5284
END IF
5285
5286
IF( ListGetLogical( Solver % Values,'CutFEM',Found ) ) GOTO 1
5287
5288
IF ( ASSOCIATED(Solver % Matrix) ) THEN
5289
IF ( Parallel .AND. MeActive ) THEN
5290
IF ( ASSOCIATED(Solver % Mesh % ParallelInfo % GInterface) ) THEN
5291
ParEnv % ActiveComm = Solver % Matrix % Comm
5292
5293
IF (.NOT. ASSOCIATED(Solver % Matrix % ParMatrix) ) &
5294
CALL ParallelInitMatrix(Solver, Solver % Matrix )
5295
5296
ParEnv => Solver % Matrix % ParMatrix % ParEnv
5297
ParEnv % ActiveComm = Solver % Matrix % Comm
5298
5299
#if 0
5300
! This one is mainly for debugging of parallel problems
5301
BLOCK
5302
TYPE(Variable_t), POINTER :: Gvar
5303
5304
GVar => VariableGet( Solver % Mesh % Variables,&
5305
Solver % Variable % Name(Solver % Variable NameLen)//' Gdofs')
5306
IF ( ASSOCIATED(GVar) ) THEN
5307
DO i = 1, SIZE( GVar % Perm )
5308
j = GVar % Perm(i)
5309
IF( j == 0 ) CYCLE
5310
k = Solver % Matrix % ParallelInfo % GlobalDOFs(j)
5311
GVar % Values(j) = 1.0_dp * k
5312
END DO
5313
END IF
5314
END BLOCK
5315
#endif
5316
5317
END IF
5318
END IF
5319
ELSE IF (.NOT.SlaveNotParallel) THEN
5320
Parenv % ActiveComm = ELMER_COMM_WORLD
5321
END IF
5322
5323
5324
! This is more featured version than the original one with just one flag.
5325
! This way different solvers can detect when their mesh has been updated.
5326
1 Solver % MeshChanged = Solver % Mesh % Changed
5327
IF( Solver % MeshTag /= Solver % Mesh % MeshTag ) THEN
5328
Solver % MeshChanged = .TRUE.
5329
Solver % MeshTag = Solver % Mesh % MeshTag
5330
END IF
5331
5332
! Linear constraints from mortar BCs:
5333
! -----------------------------------
5334
CALL GenerateProjectors(Model,Solver,Nonlinear = .FALSE. )
5335
5336
CALL Info("SingleSolver", "Attempting to call solver: "//I2S(Solver % SolverId), level=8)
5337
SolverParams => ListGetSolverParams(Solver)
5338
EquationName = GetString(SolverParams, 'Equation', GotIt)
5339
IF (GotIt) THEN
5340
Message = 'Solver Equation string is: '//TRIM(EquationName)
5341
CALL Info("SingleSolver", Message, level=8)
5342
END IF
5343
5344
IF( Solver % SolverMode == SOLVER_MODE_STEPS ) THEN
5345
CALL ExecSolverinSteps( Model, Solver, dt, TransientSimulation)
5346
ELSE
5347
SolverAddr = Solver % PROCEDURE
5348
CALL ExecSolver( SolverAddr, Model, Solver, dt, TransientSimulation)
5349
END IF
5350
5351
5352
! Special slot for post-processing solvers
5353
! This makes it convenient to separate the solution and postprocessing.
5354
! This solver must use the same structures as the primary solver.
5355
! If the postprocessing solver uses different element basis this is
5356
! not a good idea.
5357
!-----------------------------------------------------------------------
5358
BLOCK
5359
CHARACTER(LEN=MAX_NAME_LEN) :: ProcName
5360
LOGICAL :: PostActive
5361
5362
PostActive = ListGetLogical( Solver % Values,'PostSolver Active',Found )
5363
5364
IF( PostActive ) THEN
5365
ProcName = ListGetString( Solver % Values,'Procedure', Found )
5366
SolverAddr = GetProcAddr( TRIM(ProcName)//'_post', abort=.FALSE. )
5367
IF( SolverAddr /= 0 ) THEN
5368
CALL Info("SingleSolver",'Calling solver for postprocessing',Level=10)
5369
CALL ExecSolver( SolverAddr, Model, Solver, dt, TransientSimulation)
5370
END IF
5371
END IF
5372
END BLOCK
5373
5374
IF( ListGetLogical( Solver % Values,'Library Adaptivity', Found ) ) THEN
5375
#ifdef LIBRARY_ADAPTIVITY
5376
! Do adaptive meshing, whether to do this before or after "_post" is a matter of taste i guess
5377
BLOCK
5378
USE, INTRINSIC :: ISO_C_BINDING
5379
5380
CHARACTER(LEN=MAX_NAME_LEN) :: ProcName
5381
LOGICAL :: AdaptiveActive
5382
TYPE(Variable_t), POINTER :: Var
5383
INTEGER(KIND=AddrInt) :: IResidual, EResidual, BResidual
5384
5385
INTERFACE
5386
SUBROUTINE RefineMeshExt(Model,Solver,Quant,Perm,InsideResidual,EdgeResidual,BoundaryResidual)
5387
USE Types
5388
5389
TYPE( Model_t ) :: Model
5390
TYPE(Solver_t), TARGET :: Solver
5391
REAL(KIND=dp) :: Quant(:)
5392
INTEGER :: Perm(:)
5393
5394
INTERFACE
5395
SUBROUTINE BoundaryResidual( Model,Edge,Mesh,Quant,Perm,Gnorm,Indicator )
5396
USE Types
5397
TYPE(Element_t), POINTER :: Edge
5398
TYPE(Model_t) :: Model
5399
TYPE(Mesh_t), POINTER :: Mesh
5400
REAL(KIND=dp) :: Quant(:), Indicator(2), Gnorm
5401
INTEGER :: Perm(:)
5402
END SUBROUTINE BoundaryResidual
5403
5404
SUBROUTINE EdgeResidual( Model,Edge,Mesh,Quant,Perm, Indicator)
5405
USE Types
5406
TYPE(Element_t), POINTER :: Edge
5407
TYPE(Model_t) :: Model
5408
TYPE(Mesh_t), POINTER :: Mesh
5409
REAL(KIND=dp) :: Quant(:), Indicator(2)
5410
INTEGER :: Perm(:)
5411
END SUBROUTINE EdgeResidual
5412
5413
5414
SUBROUTINE InsideResidual( Model,Element,Mesh,Quant,Perm,Fnorm, Indicator)
5415
USE Types
5416
TYPE(Element_t), POINTER :: Element
5417
TYPE(Model_t) :: Model
5418
TYPE(Mesh_t), POINTER :: Mesh
5419
REAL(KIND=dp) :: Quant(:), Indicator(2), Fnorm
5420
INTEGER :: Perm(:)
5421
END SUBROUTINE InsideResidual
5422
END INTERFACE
5423
END SUBROUTINE RefineMeshExt
5424
5425
SUBROUTINE BoundaryResidual( Model,Edge,Mesh,Quant,Perm,Gnorm,Indicator)
5426
USE Types
5427
TYPE(Element_t), POINTER :: Edge
5428
TYPE(Model_t) :: Model
5429
TYPE(Mesh_t), POINTER :: Mesh
5430
REAL(KIND=dp) :: Quant(:), Indicator(2), Gnorm
5431
INTEGER :: Perm(:)
5432
END SUBROUTINE BoundaryResidual
5433
5434
SUBROUTINE EdgeResidual( Model,Edge,Mesh,Quant,Perm,Indicator)
5435
USE Types
5436
TYPE(Element_t), POINTER :: Edge
5437
TYPE(Model_t) :: Model
5438
TYPE(Mesh_t), POINTER :: Mesh
5439
REAL(KIND=dp) :: Quant(:), Indicator(2)
5440
INTEGER :: Perm(:)
5441
END SUBROUTINE EdgeResidual
5442
5443
SUBROUTINE InsideResidual( Model,Element,Mesh,Quant,Perm,Fnorm,Indicator)
5444
USE Types
5445
TYPE(Element_t), POINTER :: Element
5446
TYPE(Model_t) :: Model
5447
TYPE(Mesh_t), POINTER :: Mesh
5448
REAL(KIND=dp) :: Quant(:), Indicator(2), Fnorm
5449
INTEGER :: Perm(:)
5450
END SUBROUTINE InsideResidual
5451
END INTERFACE
5452
5453
PROCEDURE(InsideResidual), POINTER :: InsidePtr
5454
PROCEDURE(EdgeResidual), POINTER :: EdgePtr
5455
PROCEDURE(BoundaryResidual), POINTER :: BoundaryPtr
5456
5457
TYPE(C_FUNPTR) :: IResFunC, EResFunC, BresFunC
5458
5459
AdaptiveActive = ListGetLogical(Solver % Values, 'Adaptive Mesh Refinement', Found)
5460
5461
IF (AdaptiveActive) THEN
5462
ProcName = ListGetString( Solver % Values,'Procedure', Found )
5463
5464
IResidual = GetProcAddr( TRIM(ProcName)//'_inside_residual', abort=.FALSE. )
5465
EResidual = GetProcAddr( TRIM(ProcName)//'_edge_residual', abort=.FALSE. )
5466
BResidual = GetProcAddr( TRIM(ProcName)//'_boundary_residual', abort=.FALSE. )
5467
5468
IF( IResidual/=0 .AND. EResidual /= 0 .AND. BResidual /= 0 ) THEN
5469
IResFunC = TRANSFER( Iresidual, IresFunC )
5470
EResFunC = TRANSFER( Eresidual, EresFunC )
5471
BResFunC = TRANSFER( Bresidual, BresFunC )
5472
5473
CALL C_F_PROCPOINTER(IresFunC, InsidePtr)
5474
CALL C_F_PROCPOINTER(EResFunC, EdgePtr )
5475
CALL C_F_PROCPOINTER(BResFunC, BoundaryPtr )
5476
5477
Var => Solver % Variable
5478
CALL RefineMeshExt( Model, Solver, Var % Values, Var % Perm, InsidePtr, EdgePtr, BoundaryPtr )
5479
END IF
5480
END IF
5481
END BLOCK
5482
#else
5483
CALL Fatal('SingleSolver','Library version of adaptivity residuals not compiled with!')
5484
#endif
5485
END IF
5486
5487
! Compute all dependent fields, components and derivatives related to the primary solver.
5488
!-----------------------------------------------------------------------
5489
CALL UpdateDependentObjects( Solver, .TRUE. )
5490
5491
IF( UseOrigMesh ) THEN
5492
CALL Info('SingleSolver','Reverting back to current coordinates',Level=12)
5493
Mesh % Nodes => Mesh % NodesMapped
5494
END IF
5495
!------------------------------------------------------------------------------
5496
END SUBROUTINE SingleSolver
5497
!------------------------------------------------------------------------------
5498
5499
5500
!------------------------------------------------------------------------------
5501
!> Runs a solver as defined in the command file. There are several
5502
!> ways how to skip the execution. The are also three different ways
5503
!> how the matrices may be assembled and solved: standard (single), coupled and
5504
!> block.
5505
!------------------------------------------------------------------------------
5506
RECURSIVE SUBROUTINE SolverActivate( Model, Solver, dt, TransientSimulation )
5507
!------------------------------------------------------------------------------
5508
TYPE(Model_t) :: Model
5509
TYPE(Solver_t),POINTER :: Solver
5510
LOGICAL :: TransientSimulation
5511
REAL(KIND=dp) :: dt
5512
!------------------------------------------------------------------------------
5513
REAL(KIND=dp) :: OrigDT, DTScal
5514
LOGICAL :: stat, Found, TimeDerivativeActive, Timing, IsPassiveBC, &
5515
GotCoordTransform, NamespaceFound
5516
INTEGER :: i, j, k, n, BDOFs, timestep, timei, timei0, PassiveBcId, Execi
5517
INTEGER, POINTER :: ExecIntervals(:),ExecIntervalsOffset(:)
5518
REAL(KIND=dp) :: tcond, t0, rt0, st, rst, ct
5519
TYPE(Variable_t), POINTER :: TimeVar, IterV
5520
TYPE(ValueList_t), POINTER :: Params
5521
INTEGER, POINTER :: UpdateComponents(:)
5522
5523
INTEGER :: ScanningLoops, scan, sOutputPE
5524
LOGICAL :: GotLoops
5525
TYPE(Variable_t), POINTER :: ScanVar, Var
5526
CHARACTER(:), ALLOCATABLE :: str, CoordTransform
5527
TYPE(Mesh_t), POINTER :: Mesh, pMesh
5528
5529
SAVE TimeVar
5530
!------------------------------------------------------------------------------
5531
sOutputPE = OutputPE
5532
5533
5534
Mesh => Solver % Mesh
5535
5536
IF( ASSOCIATED( Mesh % Child ) .AND. .NOT. Mesh % OutputActive ) THEN
5537
i = 0
5538
pMesh => Mesh
5539
DO WHILE( ASSOCIATED( pMesh % Child ) )
5540
pMesh => pMesh % Child
5541
i = i+1
5542
IF(pMesh % OutputActive) EXIT
5543
END DO
5544
IF( .NOT. ASSOCIATED(pMesh,Mesh) .AND. ASSOCIATED(pMesh) ) THEN
5545
IF( .NOT. ListCheckPresent( Solver % Values,'Relative Mesh Level') ) THEN
5546
CALL Info('SolverActivate','By some logic the mesh is switched here to child mesh!!!')
5547
CALL Info('SolverActivate','Changing Solver '//I2S(Solver % SolverId)//&
5548
' mesh to be the '//TRIM(I2S(i))//'th Child mesh: '&
5549
//TRIM(pMesh % Name),Level=7)
5550
Solver % Mesh => pMesh
5551
END IF
5552
END IF
5553
END IF
5554
5555
CALL SetCurrentMesh( Model, Solver % Mesh )
5556
5557
Model % Solver => Solver
5558
Params => ListGetSolverParams(Solver)
5559
5560
CoordTransform = ListGetString(Params,'Coordinate Transformation',&
5561
GotCoordTransform )
5562
IF( GotCoordTransform ) THEN
5563
CALL CoordinateTransformation( Solver % Mesh, CoordTransform, &
5564
Params, .FALSE. )
5565
END IF
5566
5567
! Some properties might be rotated by "Property Rotate"
5568
!--------------------------------------------------------------
5569
CALL SetRotatedProperties(Model, Solver % Mesh)
5570
5571
! Some keywords may be normalized by area or volume
5572
!--------------------------------------------------------------
5573
CALL SetNormalizedKeywords(Model, Solver % Mesh)
5574
5575
!------------------------------------------------------------------------------
5576
! The solver may be skipped if the exec condition is negative.
5577
! This allows for complicated conditions, and the earlier simple ones
5578
! have therefore been replaced by this keyword.
5579
!------------------------------------------------------------------------------
5580
IF( ListCheckPresent( Params,'Start Time') ) &
5581
CALL Fatal('SolverActivate','Use > Exec Condition = Real < instead of > Start Time <')
5582
5583
IF( ListCheckPresent( Params,'Stop Time') ) &
5584
CALL Fatal('SolverActivate','Use > Exec Condition = Real < instead of > Stop Time <')
5585
5586
st = ListGetCReal( Params,'Exec Condition',Found)
5587
IF( Found .AND. st < 0.0 ) RETURN
5588
5589
!---------------------------------------------------------------------------------
5590
! There may also be predefined discrete intervals for the execution of the solver.
5591
!---------------------------------------------------------------------------------
5592
execi = 1
5593
ExecIntervals => ListGetIntegerArray( Params,'Exec Intervals', Found )
5594
IF( .NOT. Found ) THEN
5595
ExecIntervals => ListGetIntegerArray( Params,'Exec Interval', Found )
5596
END IF
5597
IF ( Found ) THEN
5598
TimeVar => VariableGet( Model % Variables, 'Timestep Interval' )
5599
timei = NINT(Timevar % Values(1))
5600
5601
IF( ExecIntervals(timei) == 0 ) RETURN
5602
5603
TimeVar => VariableGet( Model % Variables, 'Timestep' )
5604
timestep = NINT(TimeVar % Values(1))
5605
5606
ExecIntervalsOffset => ListGetIntegerArray( Params,&
5607
'Exec Intervals Offset', Found )
5608
IF( Found ) THEN
5609
timei0 = ExecIntervalsOffset(timei)
5610
ELSE
5611
timei0 = 0
5612
END IF
5613
5614
execi = ExecIntervals(timei)
5615
IF( MOD( timestep-1-timei0, execi) /= 0 ) RETURN
5616
END IF
5617
5618
!-------------------------------------------------------------------------------
5619
! Set solver parameters to avoid list operations during assembly
5620
!-------------------------------------------------------------------------------
5621
Solver % DG = ListGetLogical(Params, 'Discontinuous Galerkin', Found)
5622
Solver % GlobalBubbles = ListGetLogical(Params, 'Bubbles in Global System', Found)
5623
IF(.NOT. Found) Solver % GlobalBubbles = .TRUE.
5624
IF(GetString(Params, 'Linear System Direct Method', Found) == 'permon') THEN
5625
Solver % DirectMethod = DIRECT_PERMON
5626
END IF
5627
5628
str = ListGetString( Params, 'Boundary Element Procedure', Found)
5629
IF(Found) THEN
5630
Solver % BoundaryElementProcedure = GetProcAddr( Str, abort=.FALSE., quiet=.TRUE. )
5631
ELSE
5632
Solver % BoundaryElementProcedure = 0
5633
END IF
5634
5635
str = ListGetString( Params, 'Bulk Element Procedure', Found)
5636
IF(Found) THEN
5637
Solver % BulkElementProcedure = GetProcAddr( Str, abort=.FALSE., quiet=.TRUE. )
5638
ELSE
5639
Solver % BulkElementProcedure = 0
5640
END IF
5641
5642
!------------------------------------------------------------------------------
5643
! If solver timing is requested start the watches
5644
!------------------------------------------------------------------------------
5645
Timing = ListCheckPrefix(Params,'Solver Timing')
5646
IF( Timing ) THEN
5647
t0 = CPUTime()
5648
rt0 = RealTime()
5649
END IF
5650
5651
IF (.NOT. Solver % Mesh % AdaptiveDepth > 0) Solver % Mesh % OutputActive = .TRUE.
5652
TimeDerivativeActive = TransientSimulation
5653
5654
!----------------------------------------------------------------------
5655
! This is to avoid resetting of certain info that could be interesting
5656
! when saving data i.e. using an auxiliary solver.
5657
!----------------------------------------------------------------------
5658
IF(.NOT. ListGetLogical( Params,'Auxiliary Solver',Found)) THEN
5659
DTScal = ListGetConstReal( Params, 'Timestep Scale', Found )
5660
IF ( .NOT. Found ) DTScal = 1.0_dp
5661
5662
IF( ListGetLogical( Params,'Timestep Over Intervals',Found) ) THEN
5663
DTScal = 1.0_dp * Execi
5664
END IF
5665
5666
Solver % dt = DtScal * dt
5667
5668
IF ( TransientSimulation ) THEN
5669
TimeDerivativeActive = &
5670
ListGetLogical( Params, 'Time Derivative Active', Found )
5671
5672
IF ( .NOT. Found ) THEN
5673
TimeDerivativeActive = .TRUE.
5674
tcond = ListGetCReal(Params,'Time Derivative Condition',Found)
5675
IF ( Found ) TimeDerivativeActive = TimeDerivativeActive .AND. tcond>0
5676
END IF
5677
END IF
5678
5679
str = ListGetString( Params, 'Namespace', NamespaceFound )
5680
IF (NamespaceFound) CALL ListPushNamespace(TRIM(str))
5681
END IF
5682
5683
!------------------------------------------------------------------------------
5684
! Check for passive-active boundaries
5685
!------------------------------------------------------------------------------
5686
PassiveBcId = 0
5687
IsPassiveBC = .FALSE.
5688
DO j=1,Model % NumberOfBCs
5689
IsPassiveBC = ListGetLogical( Model % BCs(j) % Values, &
5690
'Passive Target',Found)
5691
IF (IsPassiveBC) THEN
5692
PassiveBcId = j
5693
EXIT
5694
END IF
5695
END DO
5696
IF ( IsPassiveBC ) THEN
5697
CALL GetPassiveBoundary( Model, Model % Mesh, PassiveBcId )
5698
Message = 'Passive element BC no. '//I2S(j)//' assigned to BC-ID no. '// &
5699
I2S(PassiveBcId)
5700
CALL Info('MainUtils',Message,Level=6)
5701
END IF
5702
5703
ScanningLoops = ListGetInteger( Params,'Scanning Loops',GotLoops)
5704
IF( GotLoops ) THEN
5705
ScanVar => VariableGet( Solver % Mesh % Variables,'scan')
5706
IF(.NOT. ASSOCIATED( ScanVar ) ) THEN
5707
CALL Fatal('SolverActivate','For scanning we should have scanning variable!')
5708
END IF
5709
ELSE
5710
ScanningLoops = 1
5711
END IF
5712
5713
CALL SwapRefElemNodes( ANY(Solver % Def_Dofs(:,:,6)>0) )
5714
5715
DO scan = 1, ScanningLoops
5716
!----------------------------------------------------------------------
5717
! This is to avoid resetting iteration that may be interesting and we
5718
! don't want to override it.
5719
!----------------------------------------------------------------------
5720
IF(.NOT. ListGetLogical( Params,'Auxiliary Solver',Found)) THEN
5721
iterV => VariableGet( Solver % Mesh % Variables, 'nonlin iter' )
5722
IF(ASSOCIATED(iterV)) iterV % Values(1) = 1
5723
END IF
5724
5725
IF( GotLoops ) THEN
5726
ScanVar % Values(1) = scan
5727
END IF
5728
5729
!---------------------------------------------------------------------
5730
! Call the correct type of solver: standard (single), coupled or block
5731
! This is where everything really happens!
5732
!---------------------------------------------------------------------
5733
IF( Solver % SolverMode == SOLVER_MODE_COUPLED .OR. &
5734
Solver % SolverMode == SOLVER_MODE_ASSEMBLY ) THEN
5735
CALL CoupledSolver( Model, Solver, DTScal * dt, TimeDerivativeActive )
5736
ELSE IF( Solver % SolverMode == SOLVER_MODE_BLOCK ) THEN
5737
CALL BlockSolver( Model, Solver, DTScal * dt, TimeDerivativeActive )
5738
ELSE
5739
CALL SingleSolver( Model, Solver, DTScal * dt, TimeDerivativeActive )
5740
END IF
5741
5742
IF( GotLoops ) THEN
5743
IF( ListGetLogical( Params,'Save Scanning Modes',Found ) ) THEN
5744
n = SIZE( Solver % Variable % Values )
5745
IF ( .NOT. ASSOCIATED( Solver % Variable % EigenValues ) ) THEN
5746
CALL Info('MainUtils','Creating modes over scanned fields',Level=8)
5747
ALLOCATE( Solver % Variable % EigenValues(ScanningLoops) )
5748
ALLOCATE( Solver % Variable % EigenVectors(ScanningLoops,n) )
5749
5750
IF( Solver % Variable % Dofs > 1 ) THEN
5751
DO k=1,Solver % Variable % DOFs
5752
str = ComponentName( Solver % Variable % Name, k )
5753
Var => VariableGet( Solver % Mesh % Variables, str, .TRUE. )
5754
IF ( ASSOCIATED( Var ) ) THEN
5755
Var % EigenValues => Solver % Variable % EigenValues
5756
Var % EigenVectors => &
5757
Solver % Variable % EigenVectors(:,k::Solver % Variable % DOFs )
5758
END IF
5759
END DO
5760
END IF
5761
END IF
5762
Solver % Variable % EigenValues(scan) = 1.0_dp * scan
5763
Solver % Variable % EigenVectors(scan,:) = Solver % Variable % Values
5764
END IF
5765
END IF
5766
5767
Solver % TimesVisited = Solver % TimesVisited + 1
5768
END DO
5769
5770
IF(.NOT. ListGetLogical( Params,'Auxiliary Solver',Found)) THEN
5771
IF(NamespaceFound) CALL ListPopNamespace()
5772
END IF
5773
Solver % dt = dt
5774
5775
IF( GotCoordTransform ) THEN
5776
CALL BackCoordinateTransformation( Solver % Mesh )
5777
END IF
5778
5779
!------------------------------------------------------------------------------
5780
! After solution register the timing, if requested
5781
!------------------------------------------------------------------------------
5782
IF( Timing ) THEN
5783
st = CPUTime() - t0
5784
rst = RealTime() - rt0
5785
5786
str = ListGetString( Params,'Equation',Found)
5787
WRITE(Message,'(a,f8.2,f8.2,a)') 'Solver time (CPU,REAL) for '&
5788
//TRIM(str)//': ',st,rst,' (s)'
5789
CALL Info('SolverActivate',Message,Level=4)
5790
5791
IF( ListGetLogical(Params,'Solver Timing',Found)) THEN
5792
CALL ListAddConstReal(CurrentModel % Simulation,'res: solver cpu time '&
5793
//TRIM(str),st)
5794
CALL ListAddConstReal(CurrentModel % Simulation,'res: solver real time '&
5795
//TRIM(str),rst)
5796
END IF
5797
5798
IF( ListGetLogical(Params,'Solver Timing Cumulative',Found)) THEN
5799
ct = ListGetConstReal(CurrentModel % Simulation,'res: cum solver cpu time '&
5800
//TRIM(str),Found)
5801
st = st + ct
5802
ct = ListGetConstReal(CurrentModel % Simulation,'res: cum solver real time '&
5803
//TRIM(str),Found)
5804
rst = rst + ct
5805
CALL ListAddConstReal(CurrentModel % Simulation,'res: cum solver cpu time '&
5806
//TRIM(str),st)
5807
CALL ListAddConstReal(CurrentModel % Simulation,'res: cum solver real time '&
5808
//TRIM(str),rst)
5809
END IF
5810
END IF
5811
5812
OutputPE = sOutputPE
5813
!------------------------------------------------------------------------------
5814
END SUBROUTINE SolverActivate
5815
!------------------------------------------------------------------------------
5816
5817
!------------------------------------------------------------------------------
5818
! The predictor-corrector scheme to change dt
5819
!------------------------------------------------------------------------------
5820
!------------------------------------------------------------------------------
5821
SUBROUTINE PredictorCorrectorControl( Model, dt, RealTimestep )
5822
!------------------------------------------------------------------------------
5823
5824
TYPE(Model_t), INTENT(IN) :: Model
5825
REAL(KIND=dp), INTENT(INOUT) :: dt
5826
INTEGER, OPTIONAL :: RealTimestep
5827
!------------------------------------------------------------------------------
5828
TYPE(Solver_t), POINTER :: Solver
5829
TYPE(ValueList_t), POINTER :: SolverParams
5830
INTEGER :: PredCorrOrder, i, predcorrIndex = 0
5831
REAL(KIND=dp) :: epsilon, beta1, beta2
5832
LOGICAL :: Found, OutputFlag = .FALSE.
5833
5834
REAL(KIND=dp) :: timeError, timeErrorMax, timeError2Norm, eta
5835
5836
REAL(KIND=dp), SAVE:: dtOld, etaOld, zeta
5837
5838
5839
DO i=1,Model % NumberOFSolvers
5840
Solver => Model % Solvers(i)
5841
!> Find the Solver for adaptive predictor, there should be only one solver as predictor
5842
IF (Solver % SolverExecWhen == SOLVER_EXEC_PREDCORR) THEN
5843
predcorrIndex = i
5844
END IF
5845
END DO
5846
5847
IF ( predcorrIndex == 0) THEN
5848
CALL Fatal('Predictor-Corrector Control','Predictor-Corrector Solver is not found!')
5849
ELSE
5850
! Do Predictor-Corrector
5851
Solver => Model % Solvers(predcorrIndex)
5852
SolverParams => ListGetSolverParams(Solver)
5853
5854
IF (RealTimestep == 1) THEN
5855
! Do nothing on the first step
5856
dtOld = dt
5857
dt = 0.0_dp
5858
zeta = 1.0_dp
5859
ELSE IF (RealTimestep == 2) THEN
5860
! Use the initial time step, force to use first order time schemes
5861
dt = dtOld
5862
zeta = 1.0_dp
5863
ELSE IF (RealTimestep > 2) THEN
5864
! Use local error estimate and PI control
5865
5866
! Read in the settings
5867
CALL ReadPredCorrParams( Model, SolverParams, PredCorrOrder, epsilon, beta1, beta2 )
5868
5869
! Compute the error |H-\tilde{H}|_inf
5870
5871
timeErrorMax = MAXVAL( ABS(Solver % Variable % Values(:) - Solver % Variable % PrevValues(:,1)))
5872
timeErrorMax = ParallelReduction(timeErrorMax,2)
5873
5874
timeError = timeErrorMax
5875
5876
! 1st order error estimate for the first control step
5877
IF (RealTimestep == 3 ) THEN
5878
PredCorrOrder = 1
5879
END IF
5880
5881
! Estimate local truncation error use old zeta
5882
CALL PredCorrErrorEstimate( eta, dtOld, PredCorrOrder, timeError, zeta )
5883
IF (RealTimestep == 3 ) THEN
5884
etaOld =eta
5885
END IF
5886
! PI controller
5887
CALL TimeStepController( dt, dtOld, eta, etaOld, epsilon, beta1, beta2 )
5888
! Compute new zeta and for predictor time scheme
5889
zeta = dt / dtOld
5890
CALL ListAddConstReal(Solver % Values, 'Adams Zeta', zeta)
5891
! Save old eta
5892
etaOld = eta
5893
5894
5895
!> Save the time errors!
5896
OutputFlag = ListGetLogical(SolverParams, 'Predictor-Corrector Save Error', Found)
5897
IF ( OutputFlag ) THEN
5898
OPEN (unit=135, file="ErrorPredictorCorrector.dat", POSITION='APPEND')
5899
WRITE(135, *) dtOld, eta, timeError
5900
CLOSE(135)
5901
END IF
5902
5903
!> Output
5904
WRITE (Message,*) "---------------- Predictor-Corrector Control ----------------------"
5905
CALL Info('Predictor-Corrector', Message, Level=4)
5906
WRITE (Message,*) "current dt=", dtOld, "next dt=", dt
5907
CALL Info('Predictor-Corrector', Message, Level=4)
5908
WRITE (Message,*) "zeta=", zeta, "eta=", eta, "terr=", timeError
5909
CALL Info('Predictor-Corrector', Message, Level=6)
5910
dtOld = dt
5911
END IF
5912
END IF
5913
5914
!------------------------------------------------------------------------------
5915
END SUBROUTINE PredictorCorrectorControl
5916
!------------------------------------------------------------------------------
5917
5918
5919
!------------------------------------------------------------------------------
5920
! The adaptive controller for the predictor-corrector scheme
5921
!------------------------------------------------------------------------------
5922
!------------------------------------------------------------------------------
5923
SUBROUTINE TimeStepController( dt, dtOld, eta, etaOld, epsilon, beta1, beta2 )
5924
!------------------------------------------------------------------------------
5925
5926
REAL(KIND=dp), INTENT(INOUT):: dt
5927
REAL(KIND=dp), INTENT(IN):: dtOld, eta, etaOld, epsilon, beta1, beta2
5928
5929
REAL(KIND=dp) :: gfactor
5930
5931
IF ((eta /= 0.0_dp) .AND. (etaOld /= 0.0_dp)) THEN
5932
gfactor = ((epsilon/eta)**beta1) * ((epsilon/etaOld)**beta2)
5933
CALL TimeStepLimiter(dtOld, dt, gfactor)
5934
ELSE
5935
dt = dtOld
5936
END IF
5937
5938
!------------------------------------------------------------------------------
5939
END SUBROUTINE TimeStepController
5940
!------------------------------------------------------------------------------
5941
5942
!------------------------------------------------------------------------------
5943
! Time step limiter for Adaptive TimeStepping
5944
!------------------------------------------------------------------------------
5945
!------------------------------------------------------------------------------
5946
SUBROUTINE TimeStepLimiter(dtOld, dt, gfactor, k)
5947
!------------------------------------------------------------------------------
5948
REAL(KIND=dp), INTENT(IN) :: dtOld, gfactor
5949
REAL(KIND=dp), INTENT(OUT) :: dt
5950
REAL(KIND=dp) :: xfactor
5951
INTEGER, OPTIONAL :: k
5952
5953
IF( PRESENT(k) ) THEN
5954
xfactor = 1.0 + k * ATAN((gfactor-1)/k)
5955
ELSE
5956
xfactor = 1.0 + 2.0 * ATAN((gfactor-1)*0.5)
5957
END IF
5958
5959
dt = dtOld * xfactor
5960
!------------------------------------------------------------------------------
5961
END SUBROUTINE TimeStepLimiter
5962
!------------------------------------------------------------------------------
5963
5964
5965
!------------------------------------------------------------------------------
5966
! Local truncation error for AB-AM 1st and 2nd order methods
5967
!------------------------------------------------------------------------------
5968
!------------------------------------------------------------------------------
5969
SUBROUTINE PredCorrErrorEstimate(eta, dt, PredCorrOrder, timeError, zeta)
5970
!------------------------------------------------------------------------------
5971
REAL(KIND=dp), INTENT(IN) :: dt, timeError, zeta
5972
INTEGER, INTENT(IN) :: PredCorrOrder
5973
REAL(KIND=dp), INTENT(OUT) :: eta
5974
5975
IF (dt > 0.0_dp) THEN
5976
IF (PredCorrOrder == 2) THEN
5977
eta = timeError * zeta / dt / (zeta + 1.0_dp) / 3.0_dp
5978
ELSE
5979
eta = timeError / dt / 2.0_dp
5980
END IF
5981
ELSE
5982
CALL WARN('Predictor-Corrector','Time Step is 0 in Local error estimate!')
5983
eta =0.0_dp
5984
END IF
5985
5986
!------------------------------------------------------------------------------
5987
END SUBROUTINE PredCorrErrorEstimate
5988
!------------------------------------------------------------------------------
5989
5990
5991
5992
!------------------------------------------------------------------------------
5993
! Set Default / Read in the settings for Predictor-Corrector scheme
5994
!------------------------------------------------------------------------------
5995
!------------------------------------------------------------------------------
5996
SUBROUTINE ReadPredCorrParams(Model, SolverParams, outOrder, outEps, outB1, outB2)
5997
5998
TYPE(Model_t), INTENT(IN) :: Model
5999
TYPE(ValueList_t), POINTER, INTENT(INOUT):: SolverParams
6000
INTEGER, OPTIONAL, INTENT(OUT) :: outOrder
6001
REAL(KIND=dp), OPTIONAL, INTENT(OUT) :: outEps, outB1, outB2
6002
6003
INTEGER :: PredCorrOrder
6004
REAL(KIND=dp) :: epsilon, beta1, beta2
6005
6006
LOGICAL :: Found
6007
6008
6009
PredCorrOrder = ListGetInteger( SolverParams, &
6010
'Predictor-Corrector Scheme Order', Found)
6011
IF ( .NOT. Found ) THEN
6012
PredCorrOrder = ListGetInteger( Model % Simulation, &
6013
'Predictor-Corrector Scheme Order', Found)
6014
IF ( .NOT. Found ) THEN
6015
PredCorrOrder = 2
6016
END IF
6017
CALL ListAddInteger( SolverParams, &
6018
'Predictor-Corrector Scheme Order', PredCorrOrder )
6019
END IF
6020
6021
epsilon = ListGetCReal( SolverParams, &
6022
'Predictor-Corrector Control Tolerance', Found )
6023
IF ( .NOT. Found ) THEN
6024
epsilon = ListGetCReal( Model % Simulation, &
6025
'Predictor-Corrector Control Tolerance', Found )
6026
IF ( .NOT. Found ) THEN
6027
epsilon = 1.0e-6
6028
END IF
6029
CALL ListAddConstReal( SolverParams, &
6030
'Predictor-Corrector Control Tolerance', epsilon )
6031
END IF
6032
6033
beta1 = ListGetCReal( SolverParams, &
6034
'Predictor-Corrector Control Beta 1', Found )
6035
IF ( .NOT. Found ) THEN
6036
beta1 = ListGetCReal( Model % Simulation, &
6037
'Predictor-Corrector Control Beta 1', Found )
6038
IF ( .NOT. Found ) THEN
6039
beta1 = 0.6_dp / ( PredCorrOrder + 1.0_dp )
6040
END IF
6041
CALL ListAddConstReal( SolverParams, &
6042
'Predictor-Corrector Control Beta 1', beta1 )
6043
END IF
6044
6045
beta2 = ListGetCReal( SolverParams, &
6046
'Predictor-Corrector Control Beta 2', Found )
6047
IF ( .NOT. Found ) THEN
6048
beta2 = ListGetCReal( Model % Simulation, &
6049
'Predictor-Corrector Control Beta 2', Found )
6050
IF ( .NOT. Found ) THEN
6051
beta2 = -0.2_dp / ( PredCorrOrder + 1.0_dp )
6052
END IF
6053
CALL ListAddConstReal( SolverParams, &
6054
'Predictor-Corrector Control Beta 2', beta2 )
6055
END IF
6056
6057
! Output if required
6058
IF ( PRESENT( outOrder ) ) outOrder = PredCorrOrder
6059
IF ( PRESENT( outEps ) ) outEps = epsilon
6060
IF ( PRESENT( outB1 ) ) outB1 = beta1
6061
IF ( PRESENT( outB2 ) ) outB2 = beta2
6062
!------------------------------------------------------------------------------
6063
END SUBROUTINE ReadPredCorrParams
6064
!------------------------------------------------------------------------------
6065
6066
END MODULE MainUtils
6067
6068
!> \} ElmerLib
6069
6070