forked from ESCOMP/CLUBB_CESM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
input_reader.F90
865 lines (615 loc) · 23.9 KB
/
input_reader.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
!-----------------------------------------------------------------------
!$Id$
!===============================================================================
module input_reader
! Description:
! This module is respondsible for the procedures and structures necessary to
! read in "SAM-Like" case specific files. Currently only the
! <casename>_sounding.in file is formatted to be used by this module.
!
! References:
! None
!---------------------------------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
private
public :: one_dim_read_var, &
read_one_dim_file, &
two_dim_read_var, &
read_two_dim_file, &
fill_blanks_one_dim_vars, &
fill_blanks_two_dim_vars, &
deallocate_one_dim_vars, &
deallocate_two_dim_vars, &
read_x_table, &
read_x_profile, &
get_target_index, &
count_columns
! Derived type for representing a rank 1 variable that has been read in by one
! of the procedures.
type one_dim_read_var
character(len=30) :: name ! Name of the variable
character(len=30) :: dim_name ! Name of the dimension that the
! variable varies along
real( kind = core_rknd ), dimension(:), allocatable :: values ! Values of that variable
end type one_dim_read_var
! Derived type for representing a rank 2 variable that has been read in by one
! of the procedures.
type two_dim_read_var
character(len=30) :: name ! Name of the variable
character(len=30) :: dim1_name ! Name of one of the dimensions
! that the variable varies along
character(len=30) :: dim2_name ! Name of the other variable that
! the variable varies along
real( kind = core_rknd ), dimension(:,:), allocatable :: values ! Values of that variable
end type two_dim_read_var
! Constant Parameter(s)
real( kind = core_rknd ), parameter, private :: &
blank_value = -999.9_core_rknd ! Used to denote if a value is missing from the file
contains
!-------------------------------------------------------------------------------------------------
subroutine read_two_dim_file( iunit, nCol, filename, read_vars, other_dim )
!
! Description: This subroutine reads from a file containing data that varies
! in two dimensions. These are dimensions are typically height
! and time.
!
!-----------------------------------------------------------------------------------------------
use constants_clubb, only: &
fstderr ! Constant(s)
use input_names, only: &
time_name ! Constant(s)
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: trim, index
! Input Variable(s)
integer, intent(in) :: iunit ! File I/O unit
integer, intent(in) :: nCol ! Number of columns expected in the data file
character(len=*), intent(in) :: filename ! Name of the file being read from
! Output Variable(s)
type (two_dim_read_var), dimension(nCol),intent(out) :: read_vars ! Structured information
! from the file
type (one_dim_read_var), intent(out) :: other_dim ! Structured information
! on the dimesion not stored in read_vars
! Local Variables
character(len=30),dimension(nCol) :: names ! Names of variables
integer nRowI ! Inner row
integer nRowO ! Outer row
integer :: k, j, i
logical :: isComment
character(len=200) :: tmpline
real( kind = core_rknd ), dimension(nCol) :: tmp
integer :: input_status ! The status of a read statement
! ---- Begin Code ----
! First run through, take names and determine how large the data file is.
open(unit=iunit, file=trim( filename ), status = 'old', action='read' )
isComment = .true.
! Skip all the comments at the top of the file
do while ( isComment )
read(iunit,fmt='(A)') tmpline
k = index( tmpline, "!" )
isComment = .false.
if ( k > 0 ) then
isComment = .true.
end if
end do
! Go back to the line that wasn't a comment.
backspace(iunit)
read(iunit, fmt=*) names
nRowO = 0
do while(.true.)
read(iunit, *, iostat=input_status) tmp(1), nRowI
! If input_status shows an end of data, then exit the loop
if( input_status < 0 ) then
exit
else if ( input_status > 0 ) then
write(fstderr,*) "Error reading data from file: " //trim( filename )
stop "Fatal error input_reader"
end if
if( nRowI < 1 ) then
stop "Number of elements must be an integer and greater than zero in two-dim input file."
end if
do k =1, nRowI
read(iunit, *) tmp
end do
nRowO = nRowO + 1
end do
do i=1, nRowO
backspace(iunit)
do j=1, nRowI
backspace(iunit)
end do
end do
backspace(iunit)
! Store the names into the structure and allocate accordingly
do k =1, nCol
read_vars(k)%name = names(k)
read_vars(k)%dim1_name = time_name
read_vars(k)%dim2_name = names(1)
allocate( read_vars(k)%values(nRowI, nRowO) )
end do
other_dim%name = time_name
other_dim%dim_name = time_name
allocate( other_dim%values(nRowO) )
! Read in the data again to the newly allocated arrays
do k=1, nRowO
read(iunit,*) other_dim%values(k)
do j=1, nRowI
read(iunit,*) ( read_vars(i)%values(j,k), i=1, nCol)
end do
end do
close(iunit)
! Eliminate a compiler warning
if ( .false. ) print *, tmp
return
end subroutine read_two_dim_file
!------------------------------------------------------------------------------------------------
subroutine read_one_dim_file( iunit, nCol, filename, read_vars )
!
! Description:
! This subroutine reads from a file containing data that varies
! in one dimension. The dimension is typically time.
!
! References:
! None
!----------------------------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: trim, index
! Input Variable(s)
integer, intent(in) :: iunit ! I/O unit
integer, intent(in) :: nCol ! Number of columns expected in the data file
character(len=*), intent(in) :: filename ! Name of the file being read from
! Output Variable(s)
type (one_dim_read_var), dimension(nCol),intent(out) :: &
read_vars ! Structured information from the file
! Local Variable(s)
character(len=30),dimension(nCol) :: names
character(len=200) :: tmpline
integer nRow
integer :: k, j
real( kind = core_rknd ), dimension(nCol) :: tmp
logical :: isComment
integer :: input_status ! The status of a read statement
! ---- Begin Code ----
isComment = .true.
! First run through, take names and determine how large the data file is.
open(unit=iunit, file=trim( filename ), status = 'old' )
! Skip all the comments at the top of the file
do while(isComment)
read(iunit,fmt='(A)') tmpline
k = index( tmpline, "!" )
isComment = .false.
if(k > 0) then
isComment = .true.
end if
end do
! Go back to the line that wasn't a comment.
backspace(iunit)
read(iunit, fmt=*) names
! Count up the number of rows
nRow = 0
do while(.true.)
read(iunit, *, iostat=input_status) tmp
! If input_status shows an end of file, exit the loop
if( input_status < 0 ) then
exit
end if
nRow = nRow+1
end do
! Rewind that many rows
do k = 0, nRow
backspace(iunit)
end do
! Store the names into the structure and allocate accordingly
do k = 1, nCol
read_vars(k)%name = names(k)
read_vars(k)%dim_name = names(1)
allocate( read_vars(k)%values(nRow) )
end do
! Read in the data again to the newly allocated arrays
do k=1, nRow
read(iunit,*) ( read_vars(j)%values(k), j=1, nCol)
end do
close(iunit)
! Avoiding compiler warning
if ( .false. ) print *, tmp
return
end subroutine read_one_dim_file
!------------------------------------------------------------------------------------------------
subroutine fill_blanks_one_dim_vars( num_vars, one_dim_vars )
!
! Description:
! This subroutine fills in the blank spots (signified by constant blank_value)
! with values linearly interpolated using the first element of the array as a
! guide.
!
! References:
! None
!----------------------------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: size
! Input Variable(s)
integer, intent(in) :: num_vars ! Number of elements in one_dim_vars
! Input/Output Variable(s)
type(one_dim_read_var), dimension(num_vars), intent(inout) :: &
one_dim_vars ! Read data that may have gaps.
! Local variable(s)
integer :: i
! ---- Begin Code ----
do i=1, num_vars
one_dim_vars(i)%values = linear_fill_blanks( size( one_dim_vars(i)%values ), &
one_dim_vars(1)%values, one_dim_vars(i)%values, &
0.0_core_rknd )
end do
return
end subroutine fill_blanks_one_dim_vars
!------------------------------------------------------------------------------------------------
subroutine fill_blanks_two_dim_vars( num_vars, other_dim, two_dim_vars )
!
! Description:
! This subroutine fills in the blank spots (signified by the
! constant blank_value with values linearly interpolated using the first
! element of the array and the values in the other_dim argument as a guide.
!
! This is a two step process. First we assume that the other_dim values
! have no holes, but there are blanks for that variable across that
! dimension. Then we fill holes across the dimension whose values are first
! in the array of two_dim_vars.
!
! Ex. Time is the 'other_dim' and Height in meters is the first element in
! two_dim_vars.
!
! References:
! None
!----------------------------------------------------------------------------------------------
implicit none
! External
intrinsic :: size
! Input Variable(s)
integer, intent(in) :: num_vars ! Number of elements in one_dim_vars
! Input/Output Variable(s)
type(one_dim_read_var), intent(in) :: other_dim ! Read data
type(two_dim_read_var), dimension(num_vars), intent(inout) :: &
two_dim_vars ! Read data that may have gaps.
! Local variables
integer :: i,j ! Loop iterators
integer :: &
dim_size, & ! 1st dimension size
other_dim_size ! 2nd dimension size
! ---- Begin Code ----
dim_size = size( two_dim_vars(1)%values, 1 )
other_dim_size = size( other_dim%values )
do i=2, num_vars
! Interpolate along main dim
do j=1, other_dim_size
two_dim_vars(i)%values(:,j) = linear_fill_blanks( dim_size, &
two_dim_vars(1)%values(:,j), &
two_dim_vars(i)%values(:,j), blank_value )
end do ! j = 1 .. other_dim_size
! Interpolate along other dim
do j=1, dim_size
two_dim_vars(i)%values(j,:) = linear_fill_blanks( other_dim_size, &
other_dim%values, &
two_dim_vars(i)%values(j,:), blank_value )
end do ! j = 1 .. dim_size
end do ! i = 2 .. num_vars
return
end subroutine fill_blanks_two_dim_vars
!------------------------------------------------------------------------------------------------
function linear_fill_blanks( dim_grid, grid, var, default_value ) &
result( var_out )
!
! Description:
! This function fills blanks in array var using the grid
! as a guide. Blank values in var are signified by being
! less than or equal to the constant blank_value.
!
! References:
! None
!-----------------------------------------------------------------------------------------------
use interpolation, only: zlinterp_fnc
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variable(s)
integer, intent(in) :: dim_grid ! Size of grid
real( kind = core_rknd ), dimension(dim_grid), intent(in) :: &
grid ! Array that var is being interpolated to.
real( kind = core_rknd ), dimension(dim_grid), intent(in) :: &
var ! Array that may contain gaps.
real( kind = core_rknd ), intent(in) :: &
default_value ! Default value if entire profile == blank_value
! Output Variable(s)
real( kind = core_rknd ), dimension(dim_grid) :: &
var_out ! Return variable
! Local Variables
real( kind = core_rknd ), dimension(dim_grid) :: temp_grid
real( kind = core_rknd ), dimension(dim_grid) :: temp_var
integer :: i
integer :: amt
logical :: reversed
! ---- Begin Code ----
reversed = .false.
! Essentially this code leverages the previously written zlinterp function.
! A smaller temporary grid and var variable are being created to pass to
! zlinterp. zlinterp then performs the work of taking the temporary var
! array and interpolating it to the actual grid array.
amt = 0
do i=1, dim_grid
if ( var(i) > blank_value ) then
amt = amt + 1
temp_var(amt) = var(i)
temp_grid(amt) = grid(i)
end if
if ( i > 1 ) then
if ( grid(i) < grid(i-1) ) then
reversed = .true.
end if
end if
end do
if ( amt == 0 ) then
var_out = default_value
else if (amt < dim_grid) then
if ( reversed ) then
var_out = zlinterp_fnc( dim_grid, amt, -grid, -temp_grid(1:amt), temp_var(1:amt) )
else
var_out = zlinterp_fnc( dim_grid, amt, grid, temp_grid(1:amt), temp_var(1:amt) )
end if
else
var_out = var
end if
return
end function linear_fill_blanks
!----------------------------------------------------------------------------
subroutine deallocate_one_dim_vars( num_vars, one_dim_vars )
!
! Description:
! This subroutine deallocates the pointer stored in
! one_dim_vars%value for the whole array.
!
!------------------------------------------------------------------------------
implicit none
! External functions
intrinsic :: allocated
! Input Variable(s)
integer, intent(in) :: num_vars ! Number of elements in one_dim_vars
type(one_dim_read_var), dimension(num_vars), intent(inout) :: &
one_dim_vars ! Read data that may have gaps.
! Local Variable(s)
integer :: i
! Begin Code
do i=1, num_vars
if ( allocated( one_dim_vars(i)%values ) ) then
deallocate( one_dim_vars(i)%values )
end if
end do ! 1 .. num_vars
return
end subroutine deallocate_one_dim_vars
!------------------------------------------------------------------------------------------------
subroutine deallocate_two_dim_vars( num_vars, two_dim_vars, other_dim )
!
! Description:
! This subroutine deallocates the pointer stored in
! two_dim_vars%value for the whole array
!
! References:
! None
!----------------------------------------------------------------------------------------------
implicit none
! External Functions
intrinsic :: allocated
! Input Variable(s)
integer, intent(in) :: num_vars ! Number of elements in one_dim_vars
! Input/Output Variables
type(one_dim_read_var), intent(inout) :: other_dim
type(two_dim_read_var), dimension(num_vars), intent(inout) :: &
two_dim_vars ! Read data that may have gaps.
! Local Variable(s)
integer :: i
! ---- Begin Code ----
do i=1, num_vars
if ( allocated( two_dim_vars(i)%values ) ) then
deallocate(two_dim_vars(i)%values)
end if
end do
if ( allocated( other_dim%values ) ) then
deallocate(other_dim%values)
end if
return
end subroutine deallocate_two_dim_vars
!------------------------------------------------------------------------------------------------
function read_x_table( nvar, xdim, ydim, target_name, retVars ) result( x )
!
! Description:
! Searches for the variable specified by target_name in the
! collection of retVars. If the function finds the variable then it returns
! it. If it does not the program using this function will exit gracefully
! with a warning message.
!
! References:
! None
!-----------------------------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
use constants_clubb, only: &
fstderr ! Constant(s)
implicit none
! External Functions
intrinsic :: trim
! Input Variable(s)
integer, intent(in) :: nvar ! Number of variables in retVars
integer, intent(in) :: xdim, ydim
character(len=*), intent(in) :: &
target_name ! Name of the variable that is being searched for
type(two_dim_read_var), dimension(nvar), intent(in) :: &
retVars ! Collection of data being searched through
! Output Variable(s)
real( kind = core_rknd ), dimension( xdim, ydim ) :: x
! Local Variables
integer :: i ! Loop iterator
logical :: l_found
! ---- Begin Code ----
l_found = .false.
i = 1
do while( i <= nvar .and. .not. l_found)
if( retVars(i)%name == target_name ) then
l_found = .true.
x = retVars(i)%values
end if
i=i+1
end do ! i <= nvar .and. not l_found
if ( .not. l_found ) then
write(fstderr,*) trim( target_name )//" could not be found."
stop "Fatal error in function read_x_table"
end if
return
end function read_x_table
!------------------------------------------------------------------------------------------------
function read_x_profile( nvar, dim_size, target_name, retVars, &
input_file ) result( x )
!
! Description:
! Searches for the variable specified by target_name in the
! collection of retVars. If the function finds the variable then it returns
! it. If it does not the program using this function will exit gracefully
! with a warning message.
!
! Modified by Cavyn, June 2010
!----------------------------------------------------------------------------------------------
use constants_clubb, only: &
fstderr ! Variable for writing to error stream
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External Functions
intrinsic :: present, size, trim
! Input Variable(s)
integer, intent(in) :: &
nvar, & ! Number of variables in retVars
dim_size ! Size of the array returned
character(len=*), intent(in) :: &
target_name ! Name of the variable that is being searched for
type(one_dim_read_var), dimension(nvar), intent(in) :: &
retVars ! Collection being searched
character(len=*), optional, intent(in) :: &
input_file ! Name of the input file containing the variables
! Output Variable(s)
real( kind = core_rknd ), dimension(dim_size) :: x
! Local Variables
integer :: i
! ---- Begin Code ----
i = get_target_index( nvar, target_name, retVars )
if ( i > 0 ) then
x(1:size(retVars(i)%values)) = retVars(i)%values
else
if( present( input_file ) ) then
write(fstderr,*) trim( target_name ), ' could not be found. Check the file ', input_file
else
write(fstderr,*) trim( target_name ), ' could not be found. Check your sounding.in file.'
end if ! present( input_file )
stop "Fatal error in read_x_profile"
end if ! target_exists_in_array
return
end function read_x_profile
!------------------------------------------------------------------------------
function get_target_index( nvar, target_name, retVars) result( i )
!
! Description:
! Returns the index of the variable specified by target_name in the
! collection of retVars. Returns -1 if variable does not exist in retVars
!
! References:
! None
!
! Created by Cavyn, July 2010
!----------------------------------------------------------------------------------------------
implicit none
! Input Variable(s)
integer, intent(in) :: nvar ! Number of variables in retVars
character(len=*), intent(in) :: target_name ! Variable being searched for
type(one_dim_read_var), dimension(nvar), intent(in) :: retVars ! Collection being searched
! Output Variable
integer :: i
! Local Variable(s)
logical :: l_found
!----------------BEGIN CODE------------------
l_found = .false.
i = 0
do while ( i < nvar .and. .not. l_found )
i = i+1
if( retVars(i)%name == target_name ) then
l_found = .true.
end if
end do
if( .not. l_found ) then
i = -1
end if
return
end function get_target_index
!=============================================================================
function count_columns( iunit, filename ) result( nCols )
! Description:
! This function counts the number of columns in a file, assuming that the
! first line of the file contains only column headers. (Comments are OK)
! References:
! None
! Created by Cavyn, July 2010
!-----------------------------------------------------------------------------
implicit none
! External
intrinsic :: index, trim, size
! Input Variables
integer, intent(in) :: iunit ! I/O unit
character(len=*), intent(in) :: filename ! Name of the file being read from
! Output Variable
integer :: nCols ! The number of data columns in the selected file
! Local Variables
integer :: i, k ! Loop Counter
character(len=200) :: tmp ! Temporary char buffer
character(len=200), dimension(50) :: colArray ! Max of 50 columns
logical :: isComment
integer :: status_var ! IO status for read statement
! -------------------------BEGIN CODE-------------------------------------
isComment = .true.
open(unit=iunit, file=trim( filename ), status = 'old' )
! Skip all the comments at the top of the file
do while(isComment)
read(iunit,fmt='(A)') tmp
k = index( tmp, "!" )
isComment = .false.
if(k > 0) then
isComment = .true.
end if
end do
! Go back to the line that wasn't a comment.
backspace(iunit)
! Count the number of columns
nCols = 0
colArray = ""
read(iunit,fmt='(A)',iostat=status_var) tmp
! Only continue if there was no IO error or end of data
if( status_var == 0 ) then
! Move all words into an array
read(tmp,*,iostat=status_var) (colArray(i), i=1,size( colArray ))
else if ( status_var > 0 ) then
! Handle the case where we have an error before the EOF marker is found
stop "Fatal error reading data in time_dependent_input function count_columns"
end if
do i=1,size( colArray )
if( colArray(i) /= "" ) then ! Increment number of columns until array is blank
nCols = nCols+1
end if
end do
close(iunit)
end function count_columns
!------------------------------------------------------------------------------
end module input_reader