-
Notifications
You must be signed in to change notification settings - Fork 0
/
SparseMatrix.f90
943 lines (876 loc) · 34.5 KB
/
SparseMatrix.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
module mSMat
use mParallel, only: StartOpenMP, ReturnOldThreadNum
implicit none
! logical :: UseDenseAlgebra,VoxelInversion
integer,private :: FInitNoElements=1000000 !Initial allocation size of the matrices
logical :: FCheckDoubleEntries=.false. !Debug var. When .true. the code checks for double entries
logical :: FShowMessages=.false. !when adding new values to the matrix and checks that the column indices of the
logical,private :: ColIdxCorrected=.false. !Internal variable ensuring that column index correction is only applied once. VERY IMPORTANT
!TO TOGGLE THIS VALUE TRUE IN THE BEGINNING OF ROUTINES THAT ALTER THE INDEX AND FALSE IN THE END!!!!!
logical,private :: UseMKLSparseBLAS=.true. !If true the intel math kernel library is used for high performance sparse BLAS. If false, full source routines from SparseKit are used.
logical :: SMatLogParameters=.true.
!$OMP THREADPRIVATE(ColIdxCorrected)
type TSparseMat
!Public variables(General matrix information)
character*(256) :: Label
integer :: NoElements !Number of elements in the matrix
integer :: NoRows !Number of rows of the matrix
integer :: NoCols !Number of columns of the matrix
logical :: IsComplex=.false. !True if the matrix is complex
real*8 , dimension(:),pointer :: Vals=>null() !Values of the matrix elements if Real
Complex*16, dimension(:),pointer :: cVals=>null() !Values of the matrix elements if Complex
integer, dimension(:),pointer :: RowIdxs =>null() !Row indices of the elements
integer, dimension(:),pointer :: ColIdxs=>null() !Column indices of the elements
end type TSparseMat
contains
subroutine SMatCreate(ioSparseMat,iNoRows,iNoCols,iLabel,iIsComplex,iAllocSize)
!Casper Kirkegaard, November 2009.
!This routine creates a TSparseMat structure, ie. initializes
!parameters allocates memory etc.
!
!Updated 2014 Tue, Now it takes complex numbers as well
!
! CK, october 2015.
! Removed initialization of value and column index vectors. It takes a long time when creating small matrices and is not necessary.
! KRA, Dec 2016. Added optional allocation size. Has to be >= 1
!IO
! oSparseMat -> Sparse matrix to create
! iNoRows -> Number of rows in the matrix
! iNoCols -> Number of cols in the matrix
! iLabel -> Label of the matrix. Setting this to eg. 'G' or 'B' is very useful
! when debugging or optimizing
! iIsComplex -> Default=.false., if true it means that the matrix is complex
! iAllocSize -> Default=FInitNoElements, is the allocation size. The default value is a
! model parameter. Has to >=1
implicit none
type (TSparseMat),intent(inout) :: ioSparseMat
integer,intent(in) :: iNoRows
integer,intent(in) :: iNoCols
character*(*),intent(in) :: iLabel
logical,intent(in),optional :: iIsComplex
integer,intent(in),optional :: iAllocSize
integer :: FInitNoElements=1000000 !Initial allocation size of the matrices
! var
integer :: NoAlloc
!Implementation
!If the matrix already exists, destroy it and recreate.
if (associated(ioSparseMat%Vals)) then
call SMatDestroy(ioSparseMat)
end if
!First initialize the vars of the input matrix
ioSparseMat%IsComplex=.false.
NoAlloc = FInitNoElements
if (present(iIsComplex)) ioSparseMat%IsComplex=iIsComplex
if (present(iAllocSize)) then
if(iAllocSize>0) NoAlloc = iAllocSize
endif
ioSparseMat%NoRows=iNoRows
ioSparseMat%NoCols=iNoCols
ioSparseMat%Label=iLabel
ioSparseMat%NoElements=0
!Now allocate an initial size for the arrays holding the matrix
allocate(ioSparseMat%ColIdxs(NoAlloc))
if (ioSparseMat%IsComplex) then
allocate(ioSparseMat%cVals(NoAlloc))
else
allocate(ioSparseMat%Vals(NoAlloc))
end if
allocate(ioSparseMat%RowIdxs(iNoRows+1))
ioSparseMat%RowIdxs(:)=0
end subroutine SMatCreate
subroutine SMatWriteSparse(iA,iV,iNDigits,iName,iLabel)
!Gianluca Fiandaca, April 2013, AU
!This routine writes the sparse matrix iA in 3 different files, one containing
!the matrix row indices, one containing the matrix column indices and one containing
!the matrix values. The names of the output files are built concatenating the file name
!iName and the words "RowIdxs.txt", "ColIdxs.txt" & "Values.txt"
!IO
! iA -> Sparse matrix to be written
! iV -> RHS vector
! iName -> the name of the 3 output files
! iLabel -> Label to be written before the matrix
! iNDigits -> Number of digits after comma in the output format (in scientific notation)
implicit none
type (TSparseMat),intent(inout) :: iA
real*8,dimension(:) :: iV
integer ,intent(in), optional :: iNDigits
character*(*) ,intent(in), optional :: iName
character*(*) ,intent(in), optional :: iLabel
integer :: i,j,k,Error
integer :: NIntegerDigits,NScientificDigits
integer,parameter :: F=99
real*8 :: Val
character*256 :: RowName,ColName,ValName,S,DefName,RHSName
integer(kind=1) :: CSR
!digits to be used in the formats
if(present(iNDigits)) then
NScientificDigits=iNDigits
else
NScientificDigits=4
end if
NIntegerDigits=nint(log10(1.d0*iA%NoElements))+1
!names and legend
if(present(iName)) then
DefName=trim(iName)//'_Definition.txt'
RHSName=trim(iName)//'_RHS.txt'
RowName=trim(iName)//'_RowIdxs.txt'
ColName=trim(iName)//'_ColIdxs.txt'
ValName=trim(iName)//'_Values.txt'
else
DefName='Definition.txt'
RHSName='RHS.txt'
RowName='RowIdxs.txt'
ColName='ColIdxs.txt'
ValName='Values.txt'
end if
!Definition file
open(unit=F,file=DefName,status='unknown')
if(present(iLabel)) then
write(F,'(A)') trim(iLabel)
else
write(F,'(A)')
end if
write(F,30) iA%NoRows,iA%NoCols,iA%NoElements,CSR
close(F)
!RHS
open(unit=F,file=RhsName,status='unknown')
if(present(iLabel)) then
write(F,'(A)') trim(iLabel)
else
write(F,'(A)')
end if
do i=1,iA%NoCols
write(F,10) iV(i)
end do
close(F)
!Row indices
open(unit=F,file=RowName,status='unknown')
if(present(iLabel)) then
write(F,'(A)') trim(iLabel)
else
write(F,'(A)')
end if
do i=1,iA%NoRows+1
write(F,20) iA%RowIdxs(i)
end do
close(F)
!Col indices
open(unit=F,file=ColName,status='unknown')
if(present(iLabel)) then
write(F,'(A)') trim(iLabel)
else
write(F,'(A)')
end if
do i=1,iA%NoElements
write(F,20) iA%ColIdxs(i)
end do
close(F)
!Values
open(unit=F,file=ValName,status='unknown')
if(present(iLabel)) then
write(F,'(A)') trim(iLabel)
else
write(F,'(A)')
end if
do i=1,iA%NoElements
write(F,10) iA%Vals(i)
end do
close(F)
return
30 format(4(1xi<NIntegerDigits>))
20 format(3(1xi<NIntegerDigits>))
10 format(1x1pe<7+NScientificDigits>.<NScientificDigits>)
end subroutine SMatWriteSparse
subroutine SMatReadSparse(oA,oV, iFileName,BlockSize)
!Gianluca Fiandaca, April 2013, AU
!This routine reads a sparse matrix iA in 3 different files, one containing
!the matrix row indices, one containing the matrix column indices and one containing
!the matrix values. The names of the output files are built concatenating the file name
!iName and the words "RowIdxs.txt", "ColIdxs.txt" & "Values.txt"
!IO
! iA -> Sparse matrix to be written
! iName -> the name of the 3 output files
! iLabel -> Label to be written before the matrix
! BlockSize -> The size of each block in the matrix (Used for blocksplitting)
!------------------------------------
!Updated Tue & Casper, September 2014.
!The reader can now read both CSR and dense format
!Furthermore the reader now assumes the file starts with a label line at the very top
!------------------------------------
use mArrays
implicit none
type (TSparseMat),intent(out) :: oA
real*8,dimension(:),pointer :: oV
character*(*) ,intent(in), optional :: iFileName
integer, intent(out) :: Blocksize
integer i,j,k,Error
integer,parameter :: F=99
real*8 :: Val
character*256 :: RowFileName,ColFileName,ValFileName,S,DefFileName,RHSFileName
integer :: NoCols,NoRows,NoElements,IsCSR,Pos
logical :: ErrorCode
character*256 :: str
!names and legend
if(present(iFileName)) then
DefFileName=trim(iFileName)//'_definition.txt'
RHSFileName=trim(iFileName)//'_RHS.txt'
RowFileName=trim(iFileName)//'_RowIdxs.txt'
ColFileName=trim(iFileName)//'_ColIdxs.txt'
ValFileName=trim(iFileName)//'_Values.txt'
else
DefFileName='Definition.txt'
RHSFileName='RHS.txt'
RowFileName='RowIdxs.txt'
ColFileName='ColIdxs.txt'
ValFileName='Values.txt'
end if
open(unit=F,file=DefFileName,status='old')
do i=1,4
read(F,'(A)') str ! read line
str = adjustl(str) ! remove left blanks
pos = scan(str,'!')
if(pos.gt.0) then
str = str(1:pos-1) ! extract the parameter value
end if
select case(i)
case(1)
read(str,*) NoRows
case(2)
read(str,*) NoCols
case(3)
read(str,*) NoElements
case(4)
read(str,*) Blocksize
end select
end do
close(F)
call SMatCreate(oA,NoRows,NoCols,'from file')
call mArraysReallocateReals(oA%Vals,NoElements)
call mArraysReallocateInts(oA%ColIdxs,NoElements)
oA%NoRows=NoRows
oA%NoCols=NoCols
oA%NoElements=NoElements
!Row indices
open(unit=F,file=RowFileName,status='unknown')
read(F,'(A)') S
do i=1,NoRows+1
read(F,'(A)') S
read(S,*) oA%RowIdxs(i)
end do
close(F)
!Col indices
open(unit=F,file=ColFileName,status='unknown')
read(F,'(A)') S
do i=1,NoElements
read(F,'(A)') S
read(S,*) oA%ColIdxs(i)
end do
close(F)
!Vals indices
open(unit=F,file=ValFileName,status='unknown')
read(F,'(A)') S
do i=1,NoElements
read(F,'(A)') S
read(S,*) oA%Vals(i)
end do
close(F)
!RHS
allocate(oV(NoCols))
open(unit=F,file=RHSFileName,status='unknown')
read(F,'(A)') S
do i=1,NoCols
read(F,'(A)') S
read(S,*) oV(i)
end do
close(F)
return
end subroutine SMatReadSparse
subroutine SMatDestroy(ioSparseMat)
!Casper Kirkegaard, November 2009.
!This routine destroys a TSparseMat structures, ie.
!deallocates memory etc.
!
!Updated 2014 Tue, Now it takes complex numbers as well
!
!Updated Dec 2016 Kristoffer Andersen. Now also resets IsSorted and IsNativeCSR
!
!IO
! ioSparseMat -> Sparse matrix to destroy
implicit none
type (TSparseMat),intent(inout) :: ioSparseMat
!Implementation
!Deallocate the coordinate form arrays(Always allocated)
if (associated(ioSparseMat%Vals)) then
deallocate(ioSparseMat%Vals)
deallocate(ioSparseMat%ColIdxs)
deallocate(ioSparseMat%RowIdxs)
end if
if (associated(ioSparseMat%cVals)) then
deallocate(ioSparseMat%cVals)
deallocate(ioSparseMat%ColIdxs)
deallocate(ioSparseMat%RowIdxs)
end if
nullify(ioSparseMat%Vals)
nullify(ioSparseMat%CVals)
nullify(ioSparseMat%ColIdxs)
nullify(ioSparseMat%RowIdxs)
!Reset vars
ioSparseMat%NoRows=0
ioSparseMat%NoCols=0
ioSparseMat%NoElements=0
ioSparseMat%IsComplex=.false.
end subroutine SMatDestroy
subroutine SMatGetDiag(iA,oDiag,iEnsurePositiveDefinite)
!Tue June 2015
!This function should be called through its interface SMatGetDiag
!This function returns the value of the diagonal elements in a matrix saved in CSR format
!IO
!iA -> input sparse matrix in CSR format
!oDiag -> output oDiag(i) contains the inverse value of the i'th diagonal element in the CSR matrix given in iA
!iEnsurePositiveDefinite -> (default = false) can be used to check if the diagonal is positive - NOT FULL POSITIVE DEFINITENESS
!
!CK, october 2015
!Changed oDiag to no longer be allocatable - it not possible to have an output array alloctable.
!
! KRA, Dec 2016
! Added Positive diagonal check and check of the size of oDiag compared to iA%NoRows
!
implicit none
type (TSparseMat),intent(inout) :: iA
real*8,intent(out) :: oDiag(:)
logical,intent(in),optional :: iEnsurePositiveDefinite
integer :: i,k
integer :: oError
real*8 :: tmp
character*256 :: S
logical :: Error = .False.
integer :: OldThreads
logical :: CheckPositiveDefinite
!allocate(oDiag(iA%NoRows))
if(size(oDiag,1).lt.iA%NoRows) goto 903
if(present(iEnsurePositiveDefinite)) then
CheckPositiveDefinite=iEnsurePositiveDefinite
else
CheckPositiveDefinite=.False.
end if
if(CheckPositiveDefinite) then
!$OMP PARALLEL IF(StartOpenMP(2,1)) DEFAULT(SHARED) PRIVATE(i,tmp,oError)
!$OMP DO SCHEDULE(STATIC)
do i=1,iA%NoRows
tmp=SMatGetValue(iA,i,i,oError)
if (tmp.gt.0d0) then
oDiag(i)=tmp
else
Error=.true.
k=i
end if
end do
!$OMP END DO
!$OMP END PARALLEL
else
!$OMP PARALLEL IF(StartOpenMP(2,1)) DEFAULT(SHARED) PRIVATE(i,tmp,oError)
!$OMP DO SCHEDULE(STATIC)
do i=1,iA%NoRows
oDiag(i)=SMatGetValue(iA,i,i,oError)
end do
!$OMP END DO
!$OMP END PARALLEL
end if
call ReturnOldThreadNum()
if (Error) goto 901
return
901 continue
print*, 'Critical failure in SMatGetDiag_real, matrix-diagonal is ',SMatGetValue(iA,k,k,oError),' at ',k
903 continue
print*, 'Output array allocation too small in SMatGetDiag_real'
End subroutine SMatGetDiag
real*8 function SMatGetValue(iA,iRowNo,iColNo,oError)
!Casper Kirkegaard, November 2009.
!This routine locates and returns the value of a given row,col entry.
!NOTE THAT THE MATRIX IS FINALIZED WHEN CALLING THIS PROCEDURE!!!
!IO
! iA -> Sparse matrix to search for value
! iRowNo -> Index of the row to search for
! iColNo -> Index of the column to search for
! oError -> -1 if value does not exist, ie. entry equal to zero
! Positive value if the value entry exists.
!IO parameters
implicit none
type (TSparseMat),intent(inout) :: iA
integer,intent(in) :: iColNo
integer,intent(in) :: iRowNo
integer,intent(out) :: oError
!Variable declarations
integer :: CorColNo
!Implementation
!Apply column index correction if necessary. Also make sure to switch back the ColIdxCorrected status flag
!at the end of this routine if this is the routine applying the correction.
CorColNo=iColNo
!Return the value
call SMatLocateCSRIdx(iA,iRowNo,CorColNo,oError)
if (oError.eq.-1) then
SMatGetValue=0d0
else
SMatGetValue=iA%Vals(oError)
end if
!Switch back the col index correction lock if necessary.
end function SMatGetValue
subroutine SMatDAD(A,iD)
type (TSparseMat),intent(inout) :: A
real*8,dimension(:),intent(in) :: iD
integer :: i,j
do i=1,A%NoRows
do j=A%RowIdxs(i),A%RowIdxs(i+1)-1
A%Vals(j)=A%Vals(j)*iD(i)*iD(A%colIdxs(j))
end do
end do
end subroutine SMatDAD
subroutine SMatLocateCSRIdx(iA,iRowNo,iColNo,oIndex,iFindNearest)
!Casper Kirkegaard, November 2009.
!This routine locates and returns the CSR index of a given row,col entry.
!-------------------------------------
!Updated September 2014, by Tue
!Added the optional parameter iFindNearest.
!------------------------------------
!IO
! iA -> Sparse matrix to search
! iRowNo -> Index of the row to search for
! iColNo -> Index of the column to search for
! oIndex -> Index in iA%Vals of the entry iRow,iColNo
! Set to -1 if the entry does not exits in the matrix
! iFindNearest -> optional parameter, which tells whether to return the nearest match if iIndex does not exist.
! default is 0, which returns -1 if index is not found
! if set to 1, it returns the nearest index searching forward
! if set to -1, it returns the nearest index searching backwards
!IO parameters
implicit none
type (TSparseMat),intent(inout) :: iA
integer,intent(in) :: iColNo
integer,intent(in) :: iRowNo
integer,intent(out) :: oIndex
integer,intent(in),optional :: iFindNearest
!Variable declarations
integer :: CorColNo
integer :: StartIndex,EndIndex
integer :: FindNearest
!Implementation
if (present(iFindNearest)) then
FindNearest = iFindNearest
else
FindNearest = 0
end if
!Apply column index correction if necessary. Also make to switch back the ColIdxCorrected status flag
!at the end of this routine if this is the routine applying the correction.
CorColNo=iColNo
!Do an n*log(n) search by exploiting that the column indices of each
!row are sorted by increasing value. Chop up the entire interval in halfs
!and determine in which of the intervals the index lies. Continue until the value
!is found or the width of the search interval is 1 and the values has not been found.
StartIndex=iA%RowIdxs(iRowNo)
EndIndex=iA%RowIdxs(iRowNo+1)-1
call SMatLocateIdx(iA%ColIdxs,StartIndex,EndIndex,CorColNo,oIndex,FindNearest)
!Switch back the col index correction lock if necessary.
end subroutine SMatLocateCSRIdx
subroutine SMatLocateIdx(iArray,iBegin,iEnd,iIndex,oIndex,iFindNearest)
!Casper Kirkegaard, November 2009.
!This routine locates the position of a given value in a sorted integer array.
!-------------------------------------
!Updated September 2014, by Tue
!Added the optional parameter iFindNearest.
!------------------------------------
!IO
! iArray -> Array to search through
! iBegin -> Index in iArray to start from
! iEnd -> Index in iArray to end at
! iIndex -> Integer to locate index in iArray for
! oIndex -> index in iarray of the value iIndex
! Set to -1 if the entry does not exits in the
! iFindNearest -> optional parameter, which tells whether to return the nearest match if iIndex does not exist.
! default is 0, which returns -1 if index is not found
! if set to 1, it returns the nearest index searching forward
! if set to -1, it returns the nearest index searching backwards
!IO parameters
implicit none
integer, dimension(:),intent(in) :: iArray
integer,intent(in) :: iBegin
integer,intent(in) :: iEnd
integer,intent(in) :: iIndex
integer,intent(out) :: oIndex
integer,intent(in),optional :: iFindNearest
!Variable declarations
integer :: i,j
integer :: StartIndex
integer :: MidIndex
integer :: EndIndex
real*8 :: Dummy
integer :: BruteIndex
logical :: Searching
integer :: Count
integer :: SearchForNearest
!Implementation
!BruteIndex=-1
!do i=iA%RowIdxs(iRowNo),iA%RowIdxs(iRowNo+1)-1
! if (iA%ColIdxs(i).eq.iColNo) then
! BruteIndex=i
! end if
!end do
!Do a log(n) search by exploiting that the column indices of each
!row are sorted by increasing value. Chop up the entire interval in halfs
!and determine in which of the intervals the index lies. Continue until the value
!is found or the width of the search interval is 1 and the values has not been found.
if (present(iFindNearest)) then
SearchForNearest = iFindNearest
else
SearchForNearest = 0
end if
Searching=.true.
oIndex=-1
StartIndex=iBegin
EndIndex=iEnd
!Don't search if the row is empty ...
if (EndIndex.lt.StartIndex) Searching=.False.
Count=1
do while(Searching)
Count=Count+1
if (EndIndex-StartIndex.lt.2) then
Searching=.false.
do i=StartIndex,EndIndex
if (iArray(i).eq.iIndex) then
oIndex=i
exit
end if
end do
end if
Dummy=(StartIndex+EndIndex)/2
MidIndex=AnInt(Dummy)
if (iArray(MidIndex).eq.iIndex) then
Searching=.false.
oIndex=MidIndex
else
if (iArray(MidIndex).gt.iIndex) then
EndIndex=MidIndex
else
StartIndex=MidIndex
end if
end if
end do
!If the exact index was not found, do we look for nearest index?
if ((oIndex.eq.-1).and.(SearchForNearest.ne.0)) then
if (SearchForNearest.eq.1) then
if (iArray(StartIndex).gt.iIndex) then
oIndex=StartIndex
else
oIndex=EndIndex
end if
else if(SearchForNearest.eq.-1) then
if (iArray(EndIndex).lt.iIndex) then
oIndex=EndIndex
else
oIndex=StartIndex
end if
end if
end if
end subroutine SMatLocateIdx
subroutine SMatGetDiagonalDominant(iA,oDiagDom)
!Tue Dec, 2014
!Determines the
!
!IO
!iA -> matrix in CSR format
!oDiagDom -> on output the diagonal dominance of the matrix, if DiagDom<1 it is strictly diagonal dominant
implicit none
type (TSparseMat),intent(inout) :: iA
real*8,intent(out) :: oDiagDom
!Local vars
integer :: i, j,k
integer,allocatable,dimension(:) :: DiagIdxs
real*8 :: Diag,RowVal,MinVal,Maxval
character*256 :: S
logical :: Error =.False.
integer :: OldThreads
allocate(DiagIdxs(iA%NoRows))
Call SMatGetDiagIdx(iA,DiagIdxs)
oDiagDom=0
!$OMP PARALLEL IF(StartOpenMP(2,1)) PRIVATE(I,J,Diag,RowVal) SHARED(DiagIdxs,iA,k,error) DEFAULT(NONE) REDUCTION(+:oDiagDom)
!$OMP DO SCHEDULE(STATIC)
do i=1,iA%NoRows
Diag=(abs(iA%Vals(DiagIdxs(i))))
if(Diag.eq.0) then
Error=.true.
k=i
end if
RowVal=0d0
do j=iA%RowIdxs(i),iA%RowIdxs(i+1)-1
RowVal=RowVal+abs(iA%Vals(j))
end do
if (Error) then
!Set oDiagDom to something very large if the diagelement is 0.
oDiagDom=50d0
else
oDiagDom=oDiagDom+(RowVal-Diag)/Diag
end if
end do
!$OMP END DO
!$OMP END PARALLEL
oDiagDom=oDiagDom/((iA%NoRows-2)*0.5d0)
call ReturnOldThreadNum()
deallocate(DiagIdxs)
return
end subroutine SMatGetDiagonalDominant
subroutine SMatAmultV(A,iV,oV)
!Casper Kirkegaard, November 2009.
!This routine calculates the product of the input matrix A with a vector iV
!IO
! A -> Sparse A matrix
! iV -> Dense vector to multiply with
! oV -> Product A*iV
!IO parameters
implicit none
type (TSparseMat),intent(inout) :: A
real*8,dimension(:),intent(in) :: iV
real*8,dimension(:),intent(out) :: oV
!Variable declarations
integer,Dimension(:),allocatable :: TempVals
integer :: i,j
integer :: AFirst,ALast
integer :: RowStart
integer :: RowEnd
real*8 :: Sum
integer :: OldThreads
logical :: Dummy
!Implementation
dummy = StartOpenMP(2,1)
call amux (A%NoRows, iV, oV, A%Vals,A%ColIdxs,A%RowIdxs)
call ReturnOldThreadNum()
end subroutine SMatAmultV
subroutine amux (n, x, y, a,ja,ia)
real*8 x(*), y(*), a(*)
integer n, ja(*), ia(*)
!c-----------------------------------------------------------------------
!c A times a vector
!c-----------------------------------------------------------------------
!c multiplies a matrix by a vector using the dot product form
!c Matrix A is stored in compressed sparse row storage.
!c
!c on entry:
!c----------
!c n = row dimension of A
!c x = real array of length equal to the column dimension of
!c the A matrix.
!c a, ja,
!c ia = input matrix in compressed sparse row format.
!c
!c on return:
!c-----------
!c y = real array of length n, containing the product y=Ax
!c
!c-----------------------------------------------------------------------
!c local variables
!c
real*8 t
integer i, k
!c-----------------------------------------------------------------------
!call mkl_dcsrgemv('n', n, a, ia, ja, x, y)
!return
!$OMP PARALLEL SHARED(n, x, y, a,ja,ia) PRIVATE(t,i,k)
!$OMP DO
do i = 1,n
t = 0
do k=ia(i), ia(i+1)-1
t = t + a(k)*x(ja(k))
end do
y(i) = t
end do
!$OMP END DO
!$OMP END PARALLEL
!c---------end-of-amux---------------------------------------------------
!c-----------------------------------------------------------------------
end
subroutine smatgetUpperTriangular (ioA)
! Kristoffer Andersen, Dec 2016
! extracts the upper trangular part of ioA.
type(tsparsemat),intent(inout) :: ioA
!real*8 a(*), ao(*), t
!integer ia(*), ja(*), iao(*), jao(*)
!-----------------------------------------------------------------------
! On entry
!-----------
! nrow = dimension of the matrix a.
! a, ja,
! ia = matrix stored in compressed row sparse format
!
! nzmax = length of arrays ao, and jao.
!
! On return:
!-----------
! ao, jao,
! iao = lower part of input matrix (a,ja,ia) stored in compressed sparse
! row format format.
!
! ierr = integer error indicator.
! ierr .eq. 0 means normal return
! ierr .eq. i means that the code has stopped when processing
! row number i, because there is not enough space in ao, jao
! (according to the value of nzmax)
!
!-----------------------------------------------------------------------
!var
integer :: ierr,ko,i,kold,kdiag,k,RowIDold
!prog
ko = 0
do i=1, ioA%NoRows
RowIdOld = ioA%RowIdxs(i)
ioA%RowIdxs(i) = ko+1
do k = RowIdOld, ioA%RowIdxs(i+1) -1
if (ioA%ColIdxs(k) .ge. i) then ! upper trangle
ko = ko+1 ! next new index
ioA%Vals(ko) = ioA%Vals(k) ! store value
ioA%ColIdxs(ko) = ioA%ColIdxs(k)
endif
enddo
enddo
ioA%RowIDxs(ioA%NoRows+1) = ko+1
ioA%NoElements = ko
return
end subroutine smatgetUpperTriangular
subroutine SMatGetInvDiag(iA,oInvDiag)
!Tue September 2014
!This function should be called through its interface SMatGetInvDiag
!This function returns the inverse value of the diagonal elements in a matrix saved in CSR format
!IO
!iA -> input sparse matrix in CSR format
!oInvDiag -> output oInvDiag(i) contains the inverse value of the i'th diagonal element in the CSR matrix given in iA
!
!CK, october 2015
!Removed allocatable property on oInvDiag. Cannot allocate and pass an allocatable outside a subroutine.
implicit none
type (TSparseMat),intent(inout) :: iA
real*8,intent(out) :: oInvDiag(:)
!var
integer :: i,k
integer :: oError
real*8 :: tmp
character*256 :: S
logical :: Error
integer :: OldThreads
Error = .False.
!allocate(oInvDiag(iA%NoRows))
!$OMP PARALLEL IF(StartOpenMP(2,1)) DEFAULT(SHARED) PRIVATE(i,tmp,oError)
!$OMP DO SCHEDULE(STATIC)
do i=1,iA%NoRows
tmp=SMatGetValue(iA,i,i,oError)
if (abs(tmp).gt.0d0) then
oInvDiag(i)=1/tmp
else
!!$OMP CRITICAL (Errorfound) ! might be neccesary
Error=.true.
k=i
!!$OMP END CRITICAL (Errorfound)
end if
end do
!$OMP END DO
!$OMP END PARALLEL
call ReturnOldThreadNum()
return
End subroutine SMatGetInvDiag
double precision function ddot(n,dx,incx,dy,incy)
double precision dx(*),dy(*),dtemp
integer i,incx,incy,ix,iy,m,mp1,n
ddot = 0.0d0
dtemp = 0.0d0
if(n<=0)return
if(incx==1.AND.incy==1)go to 20
ix = 1
iy = 1
if(incx<0)ix = (-n+1)*incx + 1
if(incy<0)iy = (-n+1)*incy + 1
do 10 i = 1,n
dtemp = dtemp + dx(ix)*dy(iy)
ix = ix + incx
iy = iy + incy
10 continue
ddot = dtemp
return
20 m = mod(n,5)
if( m == 0 ) go to 40
do 30 i = 1,m
dtemp = dtemp + dx(i)*dy(i)
30 continue
if( n < 5 ) go to 60
40 mp1 = m + 1
do 50 i = mp1,n,5
dtemp = dtemp + dx(i)*dy(i) + dx(i + 1)*dy(i + 1) + dx(i + 2)*dy(i + 2) + dx(i + 3)*dy(i + 3) + dx(i + 4)*dy(i + 4)
50 continue
60 ddot = dtemp
return
end
subroutine SMatGetDiagIdx(iA,oDiagIdxs)
!Tue September 2014
!This function should be called through its interface SMatGetDiagIdx
!This function returns the indices for the diagonal elements in a matrix saved in CSR format
!CK, october 2015
!Changed oDiagIdxs to no longer be allocatable - it not possible to have an output array alloctable.
!IO
!iA -> input sparse matrix in CSR format
!SMatGetDiagIdx_type -> output SMatGetDiagIdx_type(i) contains the indices for the i'th diagonal element in the CSR matrix given in iA
implicit none
type (TSparseMat),intent(inout) :: iA
integer,intent(out) :: oDiagIdxs(:)
integer :: i
integer :: OldThreads
!$OMP PARALLEL IF(StartOpenMP(2,1)) DEFAULT(SHARED) PRIVATE(i)
!$OMP DO SCHEDULE(STATIC)
do i=1,iA%NoRows
call SMatLocateCSRIdx(iA,i,i,oDiagIdxs(i))
end do
!$OMP END DO
!$OMP END PARALLEL
call ReturnOldThreadNum()
End subroutine SMatGetDiagIdx
subroutine SMatGetBlockRowIdx(iA,iBlocks,oBRowIdxs)
!Tue September 2014
!This routine finds the first and last element in each row which are within the blocks given in iBlocks.
!In other words, this routine is used to sort out the elements which are not used in a blocksplitting of the matrix.
!CK, October 2015
!Removed allocation of browidxs - the allocation hasto be performed from outside not to cause data corruption.
!IO
!iA -> input sparse matrix in CSR format
!iBlocks -> The blocksplitting we wish to use on iA.
!oBRowIdxs -> oBRowIdxs(i,1) is the first indices to first element in row i within the block, oBRowIdxs(i,2) is the last element.
implicit none
type (TSparseMat),intent(inout) :: iA
integer,dimension(:,:),intent(in):: iBlocks
integer, intent(out) :: oBRowIdxs(:,:)
integer :: i,k
integer :: threads
integer :: OldThreads
!allocate(oBRowIdxs(iA%NoRows,2))
threads = size(iBlocks,1)
do k=1,threads
!$OMP PARALLEL IF(StartOpenMP(2,1)) DEFAULT(SHARED) PRIVATE(i)
!$OMP DO SCHEDULE(STATIC)
do i = iBlocks(k,1),iBlocks(k,2)
call SMatLocateCSRIdx(iA,i,iBlocks(k,1),oBRowIdxs(i,1),1)
call SMatLocateCSRIdx(iA,i,iBlocks(k,2),oBRowIdxs(i,2),-1)
end do
!$OMP END DO
!$OMP END PARALLEL
end do
call ReturnOldThreadNum()
End subroutine SMatGetBlockRowIdx
function SMatFindMaxColDif(iA)
!Tue September 2014
!This function finds the maximum distance two elements can be apart in the same row in the matrix
!IO
!iA -> input sparse matrix in CSR format
!FindMaxColDif -> output FindMaxColDif contains the maximum difference between the first and last element in each row across all rows.
implicit none
type (TSparseMat),intent(in) :: iA
integer :: SMatFindMaxColDif
integer :: i,j
SMatFindMaxColDif=0
do i=1,iA%NoRows
SMatFindMaxColDif=max(SMatFindMaxColDif,(iA%ColIdxs(iA%RowIdxs(i+1)-1)-iA%ColIdxs(iA%RowIdxs(i))+1))
end do
end function SMatFindMaxColDif
end module mSMat