source: palm/trunk/UTIL/inifor/src/inifor_io.f90 @ 4675

Last change on this file since 4675 was 4675, checked in by eckhard, 4 years ago

Support for homogeneous (domain-averaged) boundary conditions and soil profile initialization

  • Property svn:keywords set to Id
File size: 65.9 KB
RevLine 
[3447]1!> @file src/inifor_io.f90
[2696]2!------------------------------------------------------------------------------!
[2718]3! This file is part of the PALM model system.
[2696]4!
[2718]5! PALM is free software: you can redistribute it and/or modify it under the
6! terms of the GNU General Public License as published by the Free Software
7! Foundation, either version 3 of the License, or (at your option) any later
[2696]8! version.
9!
[2718]10! PALM is distributed in the hope that it will be useful, but WITHOUT ANY
11! WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12! A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
[2696]13!
14! You should have received a copy of the GNU General Public License along with
15! PALM. If not, see <http://www.gnu.org/licenses/>.
16!
[4481]17! Copyright 2017-2020 Leibniz Universitaet Hannover
18! Copyright 2017-2020 Deutscher Wetterdienst Offenbach
[2696]19!------------------------------------------------------------------------------!
20!
21! Current revisions:
22! -----------------
[4659]23!
24!
[3183]25! Former revisions:
26! -----------------
27! $Id: inifor_io.f90 4675 2020-09-11 10:00:26Z eckhard $
[4675]28! New command-line options for soil profile initialization and homogeneous
29!    boundary conditions
30!
31!
32! 4659 2020-08-31 11:21:17Z eckhard
[4659]33! New command-line option '--precipitation' enables output of precipitation
34!    surface forcing
35! Only define netCDF variables in enabled IO groups
36! Improved code formatting
37!
38!
39! 4569 2020-06-19 12:46:17Z eckhard
[4569]40! Removed unused variable
41!
42!
43! 4568 2020-06-19 11:56:30Z eckhard
[4568]44! Handle COSMO soil data with and without additional surface temperature
45!
46!
47! 4553 2020-06-03 16:34:15Z eckhard
[4553]48! Option --help now points the user to INIFOR's wiki page
49! Remove deprecated command-line options -clon and -clat
50!
51!
52! 4538 2020-05-18 13:45:35Z eckhard
[4538]53! Make setting the vertical PALM origin mandatory
54!
55!
56! 4523 2020-05-07 15:58:16Z eckhard
[4523]57! respect integer working precision (iwp) specified in inifor_defs.f90
58!
59!
60! 4481 2020-03-31 18:55:54Z maronga
[4074]61! Pass hhl_file directly instead of entire INIFOR configuration
62!
63!
64! 3997 2019-05-23 12:35:57Z eckhard
[3997]65! Added boolean indicator for --elevation option invocation
66! Stop INIFOR if no command-line options given
67!
68!
69! 3866 2019-04-05 14:25:01Z eckhard
[3866]70! Use PALM's working precision
71! Improved coding style
72!
73!
74! 3801 2019-03-15 17:14:25Z eckhard
[3801]75! Added routine get_cosmo_grid() to read in COSMO rotated pole from COSMO domain
76! Moved get_soil_layer_thickness() here from inifor_grid
77!
78! 3785 2019-03-06 10:41:14Z eckhard
[3779]79! Temporariliy disabled height-based geostrophic wind averaging
80! Improved variable naming
81!
82!
83! 3764 2019-02-26 13:42:09Z eckhard
[3764]84! Removed dependency on radiation input files
85!
86!
87! 3716 2019-02-05 17:02:38Z eckhard
[3716]88! Removed dependency on soilmoisture input files
89!
90!
91! 3680 2019-01-18 14:54:12Z knoop
[3678]92! Moved get_input_file_list() here from grid module, added check for presence of
93!    input files
94!
95!
96! 3618 2018-12-10 13:25:22Z eckhard
[3618]97! Prefixed all INIFOR modules with inifor_
98!
99!
100! 3615 2018-12-10 07:21:03Z raasch
[3615]101! bugfix: abort replaced by inifor_abort
102!
103! 3557 2018-11-22 16:01:22Z eckhard
[3557]104! Updated documentation, removed unused subroutine write_netcdf_variable_2d()
105!
106!
107! 3537 2018-11-20 10:53:14Z eckhard
[3537]108! New routine get_netcdf_dim_vector()
109!
110!
111! 3534 2018-11-19 15:35:16Z raasch
[3534]112! bugfix: INTENT attribute changed
113!
114! 3456 2018-10-30 14:29:54Z eckhard
[3456]115! NetCDf output of internal arrays only with --debug option
116!
117!
118! 3447 2018-10-29 15:52:54Z eckhard
[3447]119! Removed INCLUDE statement for get_netcdf_variable()
120! Renamed source files for compatibilty with PALM build system
121!
122!
123! 3395 2018-10-22 17:32:49Z eckhard
[3395]124! Added command-line options for configuring the computation of geostrophic
125!     winds (--averagin-mode, --averaging-angle)
126! Added command-line option --input-prefix for setting input file prefixes all
127!     at once
128! Added --debug option for more verbose terminal output
129! Added option-specific *_is_set LOGICALs to indicate invocation from the
130!     command-line
131! Improved error messages in case of empty file-name strings
132! Improved routine naming
[3262]133!
134! 3183 2018-07-27 14:25:55Z suehring
[3182]135! Introduced new PALM grid stretching
136! Updated variable names and metadata for PIDS v1.9 compatibility
137! Improved handling of the start date string
138! Better compatibility with older Intel compilers:
139! - avoiding implicit array allocation with new get_netcdf_variable()
140!   subroutine instead of function
141! Improved command line interface:
142! - Added configuration validation
143! - New options to configure input file prefixes
144! - GNU-style short and long option names
145! - Added version and copyright output
[2696]146!
[3182]147!
[3183]148! 3182 2018-07-27 13:36:03Z suehring
[2696]149! Initial revision
150!
151!
152!
153! Authors:
154! --------
[3557]155!> @author Eckhard Kadasch (Deutscher Wetterdienst, Offenbach)
[2696]156!
157! Description:
158! ------------
159!> The io module contains the functions needed to read and write netCDF data in
160!> INIFOR.
161!------------------------------------------------------------------------------!
[3680]162#if defined ( __netcdf )
[3618]163 MODULE inifor_io
[2696]164
[3618]165    USE inifor_control
166    USE inifor_defs,                                                           &
[4675]167        ONLY:  CFG_INIT_PROFILE, CFG_INIT_VOLUME,                              &
168               CFG_INIT_SOIL_PROFILE, CFG_INIT_SOIL_VOLUME,                    &
169               CFG_FORCING_HETERO, CFG_FORCING_HOMO, CFG_FORCING_NUDGING,      &
170               DATE, SNAME, PATH, PI, TO_RADIANS, TO_DEGREES, VERSION,         &
[4568]171               NC_DEPTH_DIM_IDX, NC_DEPTH_NAME, NC_HHL_NAME, NC_RLAT_NAME,     &
172               NC_RLON_NAME, NC_ROTATED_POLE_NAME, NC_POLE_LATITUDE_NAME,      &
[4538]173               NC_POLE_LONGITUDE_NAME, RHO_L, iwp, wp,                         &
174               PIDS_ORIGIN_LON, PIDS_ORIGIN_LAT, PIDS_ORIGIN_Z
[3618]175    USE inifor_types
176    USE inifor_util,                                                           &
[4568]177        ONLY:  add_hours_to, nearly_equal, reverse, str, real_to_str
[2696]178    USE netcdf
179
180    IMPLICIT NONE
181
[3557]182!------------------------------------------------------------------------------!
183! Description:
184! ------------
185!> get_netcdf_variable() reads the netCDF data and metadate for the netCDF
[3866]186!> variable 'in_var%name' from the file 'in_file'. The netCDF data array is
[3557]187!> stored in the 'buffer' array and metadata added to the respective members of
188!> the given 'in_var'.
189!------------------------------------------------------------------------------!
[3182]190    INTERFACE get_netcdf_variable
[3997]191       MODULE PROCEDURE get_netcdf_variable_int
192       MODULE PROCEDURE get_netcdf_variable_real
[3182]193    END INTERFACE get_netcdf_variable
194
195    PRIVATE ::  get_netcdf_variable_int, get_netcdf_variable_real
196
[2696]197 CONTAINS
198
[3557]199!------------------------------------------------------------------------------!
200! Description:
201! ------------
202!> get_netcdf_variable_int() implements the integer variant for the
203!> get_netcdf_variable interface.
204!------------------------------------------------------------------------------!
[3866]205 SUBROUTINE get_netcdf_variable_int(in_file, in_var, buffer)
[3182]206
[3866]207    CHARACTER(LEN=PATH), INTENT(IN)          ::  in_file
208    TYPE(nc_var), INTENT(INOUT)              ::  in_var
209    INTEGER(iwp), ALLOCATABLE, INTENT(INOUT) ::  buffer(:,:,:)
[3182]210
[3866]211    INTEGER               ::  ncid
212    INTEGER, DIMENSION(3) ::  start, count
[3182]213
[3866]214    IF ( nf90_open( TRIM(in_file), NF90_NOWRITE, ncid ) .EQ. NF90_NOERR .AND. &
215         nf90_inq_varid( ncid, in_var%name, in_var%varid ) .EQ. NF90_NOERR )  THEN
[3447]216
[3866]217       CALL get_input_dimensions(in_var, ncid)
[3447]218
[3866]219       CALL get_netcdf_start_and_count(in_var, start, count)
220       CALL log_runtime('time', 'read')
[3447]221
[3866]222       ALLOCATE( buffer( count(1), count(2), count(3) ) )
223       CALL log_runtime('time', 'alloc')
[3447]224
[3866]225       CALL check(nf90_get_var( ncid, in_var%varid, buffer,                  &
226                                start = start,                                 &
227                                count = count ))
[3447]228
[3866]229    ELSE
[3447]230
[3866]231       message = "Failed to read '" // TRIM(in_var%name) // &
232          "' from file '" // TRIM(in_file) // "'."
233       CALL inifor_abort('get_netcdf_variable', message)
[3447]234
[3866]235    ENDIF
[3447]236
[3866]237    CALL check(nf90_close(ncid))
238    CALL log_runtime('time', 'read')
[3447]239
[3866]240 END SUBROUTINE get_netcdf_variable_int
[3182]241
242
[3557]243!------------------------------------------------------------------------------!
244! Description:
245! ------------
246!> get_netcdf_variable_real() implements the real variant for the
247!> get_netcdf_variable interface.
248!------------------------------------------------------------------------------!
[3866]249 SUBROUTINE get_netcdf_variable_real(in_file, in_var, buffer)
[3182]250
[3866]251    CHARACTER(LEN=PATH), INTENT(IN)      ::  in_file
252    TYPE(nc_var), INTENT(INOUT)          ::  in_var
253    REAL(wp), ALLOCATABLE, INTENT(INOUT) ::  buffer(:,:,:)
[3182]254
[3866]255    INTEGER               ::  ncid
256    INTEGER, DIMENSION(3) ::  start, count
[3182]257
[3866]258    IF ( nf90_open( TRIM(in_file), NF90_NOWRITE, ncid ) .EQ. NF90_NOERR .AND. &
259         nf90_inq_varid( ncid, in_var%name, in_var%varid ) .EQ. NF90_NOERR )  THEN
[3447]260
[3866]261       CALL get_input_dimensions(in_var, ncid)
[3447]262
[3866]263       CALL get_netcdf_start_and_count(in_var, start, count)
264       CALL log_runtime('time', 'read')
[3447]265
[3866]266       ALLOCATE( buffer( count(1), count(2), count(3) ) )
267       CALL log_runtime('time', 'alloc')
[3447]268
[3866]269       CALL check(nf90_get_var( ncid, in_var%varid, buffer,                  &
270                                start = start,                                 &
271                                count = count ))
[3447]272
[3866]273    ELSE
[3447]274
[3866]275       message = "Failed to read '" // TRIM(in_var%name) // &
276          "' from file '" // TRIM(in_file) // "'."
277       CALL inifor_abort('get_netcdf_variable', message)
[3447]278
[3866]279    ENDIF
[3447]280
[3866]281    CALL check(nf90_close(ncid))
282    CALL log_runtime('time', 'read')
[3447]283
[3866]284 END SUBROUTINE get_netcdf_variable_real
[3182]285
286
[3557]287!------------------------------------------------------------------------------!
288! Description:
289! ------------
290!> get_netcdf_dim_vector() reads the coordinate array 'coordname' from the
291!> netCDF file 'filename'.
292!------------------------------------------------------------------------------!
[3866]293 SUBROUTINE get_netcdf_dim_vector(filename, coordname, coords)
[3537]294
[3866]295    CHARACTER(LEN=*), INTENT(IN)         ::  filename
296    CHARACTER(LEN=*), INTENT(IN)         ::  coordname
297    REAL(wp), ALLOCATABLE, INTENT(INOUT) ::  coords(:)
[3537]298
[3866]299    INTEGER ::  ncid, varid, dimlen
300    INTEGER ::  dimids(NF90_MAX_VAR_DIMS)
[3537]301
[3866]302    IF ( nf90_open( TRIM(filename), NF90_NOWRITE, ncid ) .EQ. NF90_NOERR .AND. &
303         nf90_inq_varid( ncid, coordname, varid ) .EQ. NF90_NOERR )  THEN
[3537]304
[3866]305       CALL check(nf90_inquire_variable( ncid, varid, dimids = dimids ))
306       CALL check(nf90_inquire_dimension( ncid, dimids(1), len = dimlen ))
[3537]307
[3866]308       ALLOCATE(coords(dimlen))
309       CALL check(nf90_get_var( ncid, varid, coords))
[3537]310
[3866]311    ELSE
[3537]312
[3866]313       message = "Failed to read '" // TRIM(coordname) // &
314          "' from file '" // TRIM(filename) // "'."
315       CALL inifor_abort('get_netcdf_dim_vector', message)
[3537]316
[3866]317    ENDIF
[3537]318
[3866]319 END SUBROUTINE get_netcdf_dim_vector
[3537]320
321
[3557]322!------------------------------------------------------------------------------!
323! Description:
324! ------------
325!> get_input_dimensions() reads dimensions metadata of the netCDF variable given
[3866]326!> by 'in_var%name' into 'in_var' data structure.
[3557]327!------------------------------------------------------------------------------!
[3866]328 SUBROUTINE get_input_dimensions(in_var, ncid)
[3447]329
[3866]330    TYPE(nc_var), INTENT(INOUT) ::  in_var
331    INTEGER, INTENT(IN)         ::  ncid
[3447]332
[3866]333    INTEGER ::  i
[3447]334
[3866]335    CALL check(nf90_get_att( ncid, in_var%varid, "long_name",             &
336                             in_var%long_name))
[3447]337
[3866]338    CALL check(nf90_get_att( ncid, in_var%varid, "units",                 &
339                             in_var%units ))
[3447]340
[3866]341    CALL check(nf90_inquire_variable( ncid, in_var%varid,                 &
342                                      ndims  = in_var%ndim,               &
343                                      dimids = in_var%dimids ))
[3447]344
[3866]345    DO  i = 1, in_var%ndim
346       CALL check(nf90_inquire_dimension( ncid, in_var%dimids(i),         &
347                                          name = in_var%dimname(i),       &
348                                          len  = in_var%dimlen(i) ))
349    ENDDO
[3447]350
[3866]351 END SUBROUTINE get_input_dimensions
[3447]352
353
[3557]354!------------------------------------------------------------------------------!
355! Description:
356! ------------
357!> get_netcdf_start_and_count() gets the start position and element counts for
358!> the given netCDF file. This information is used in get_netcdf_variable_int()
359!> and _real() for reading input variables..
360!------------------------------------------------------------------------------!
[3866]361 SUBROUTINE get_netcdf_start_and_count(in_var, start, count)
[3447]362
[3866]363    TYPE(nc_var), INTENT(INOUT)        ::  in_var
364    INTEGER, DIMENSION(3), INTENT(OUT) ::  start, count
[3447]365
[3866]366    INTEGER ::  ndim
[3447]367
[3866]368    IF ( in_var%ndim .LT. 2  .OR.  in_var%ndim .GT. 4 )  THEN
[3447]369
[3866]370       message = "Failed reading NetCDF variable " //                       &
[4523]371          TRIM(in_var%name) // " with " //                                  &
372          TRIM(str(INT(in_var%ndim, kind=iwp))) //                          &
[3866]373          " dimensions because only two- and and three-dimensional" //      &
374          " variables are supported."
375       CALL inifor_abort('get_netcdf_start_and_count', message)
[3447]376
[3866]377    ENDIF
[3447]378
[3866]379    start = (/ 1, 1, 1 /)
[4568]380    IF ( TRIM(in_var%name) .EQ. 'T_SO' .AND.                                &
381         in_var%has_redundant_first_level )  THEN
382       
[3557]383!
[3866]384!--    Skip depth = 0.0 for T_SO and reduce number of depths from 9 to 8
385       in_var%dimlen(3) = in_var%dimlen(3) - 1
[3447]386
[3557]387!
[3866]388!--    Start reading from second level, e.g. depth = 0.005 instead of 0.0
389       start(3) = 2
390    ENDIF
[3447]391
[3866]392    IF (in_var%ndim .EQ. 2)  THEN
393       in_var%dimlen(3) = 1
394    ENDIF
[3447]395
[3866]396    ndim = MIN(in_var%ndim, 3)
397    count = (/ 1, 1, 1 /)
398    count(1:ndim) = in_var%dimlen(1:ndim)
[3447]399
[3866]400 END SUBROUTINE get_netcdf_start_and_count
[3447]401
402
[3557]403!------------------------------------------------------------------------------!
404! Description:
405! ------------
406!> Routine for defining netCDF variables in the dynamic driver, INIFOR's netCDF
407!> output.
408!------------------------------------------------------------------------------!
[3866]409 SUBROUTINE netcdf_define_variable(var, ncid)
[2696]410
[3866]411     TYPE(nc_var), INTENT(INOUT) ::  var
412     INTEGER, INTENT(IN)         ::  ncid
[2696]413
[3866]414     CALL check(nf90_def_var(ncid, var%name, NF90_FLOAT,       var%dimids(1:var%ndim), var%varid))
415     CALL check(nf90_put_att(ncid, var%varid, "long_name",     var%long_name))
416     CALL check(nf90_put_att(ncid, var%varid, "units",         var%units))
417     IF ( var%lod .GE. 0 )  THEN
418        CALL check(nf90_put_att(ncid, var%varid, "lod",           var%lod))
419     ENDIF
420     CALL check(nf90_put_att(ncid, var%varid, "source",        var%source))
421     CALL check(nf90_put_att(ncid, var%varid, "_FillValue",    NF90_FILL_REAL))
[2696]422
[3866]423 END SUBROUTINE netcdf_define_variable
[2696]424   
425
[3557]426!------------------------------------------------------------------------------!
427! Description:
428! ------------
429!> netcdf_get_dimensions() reads in all dimensions and their lengths and stores
430!> them in the given the 'var' data structure. This information is used later
431!> for writing output variables in update_output().
432!------------------------------------------------------------------------------!
[3866]433 SUBROUTINE netcdf_get_dimensions(var, ncid)
[2696]434
[3866]435     TYPE(nc_var), INTENT(INOUT) ::  var
436     INTEGER, INTENT(IN)         ::  ncid
437     INTEGER                     ::  i
438     CHARACTER(SNAME)            ::  null
[2696]439
[3866]440     DO  i = 1, var%ndim
441        CALL check(nf90_inquire_dimension(ncid, var%dimids(i), &
442                                          name = null, &
443                                          len  = var%dimlen(i)  ) )
444     ENDDO
[2696]445
[3866]446 END SUBROUTINE netcdf_get_dimensions
[2696]447
448
449!------------------------------------------------------------------------------!
450! Description:
451! ------------
[3557]452!> This routine parses and interpretes the command-line options and stores the
453!> resulting settings in the 'cfg' data structure.
[2696]454!------------------------------------------------------------------------------!
[3866]455 SUBROUTINE parse_command_line_arguments( cfg )
[2696]456
[3866]457    TYPE(inifor_config), INTENT(INOUT) ::  cfg
[2696]458
[3866]459    CHARACTER(LEN=PATH)                ::  option, arg
460    INTEGER                            ::  arg_count, i
[2696]461
[3866]462    cfg%flow_prefix_is_set = .FALSE.
463    cfg%input_prefix_is_set = .FALSE.
[3997]464    cfg%p0_is_set = .FALSE.
[3866]465    cfg%radiation_prefix_is_set = .FALSE.
466    cfg%soil_prefix_is_set = .FALSE.
[4659]467    cfg%precipitation_prefix_is_set = .FALSE.
468    cfg%process_precipitation = .FALSE.
[4538]469    cfg%static_driver_is_set = .FALSE.
[3997]470    cfg%ug_defined_by_user = .FALSE.
471    cfg%vg_defined_by_user = .FALSE.
472    cfg%z0_is_set = .FALSE.
[3395]473
[3866]474    arg_count = COMMAND_ARGUMENT_COUNT()
475    IF (arg_count .GT. 0)  THEN
[2696]476
[3866]477       i = 1
478       DO  WHILE (i .LE. arg_count)
[2696]479
[3866]480          CALL GET_COMMAND_ARGUMENT( i, option )
[2696]481
[3866]482          SELECT CASE( TRIM(option) )
[2696]483
[3395]484             CASE( '--averaging-mode' )
485                CALL get_option_argument( i, arg )
[3866]486                cfg%averaging_mode = TRIM(arg)
[3395]487
[3182]488             CASE( '-date', '-d', '--date' )
489                CALL get_option_argument( i, arg )
[3866]490                cfg%start_date = TRIM(arg)
[2696]491
[3395]492             CASE( '--debug' )
[3866]493                cfg%debug = .TRUE.
[3395]494
[3182]495             CASE( '-z0', '-z', '--elevation' )
[3997]496                cfg%z0_is_set = .TRUE.
[3182]497                CALL get_option_argument( i, arg )
[3866]498                READ(arg, *) cfg%z0
[2696]499
[3182]500             CASE( '-p0', '-r', '--surface-pressure' )
[3866]501                cfg%p0_is_set = .TRUE.
[3182]502                CALL get_option_argument( i, arg )
[3866]503                READ(arg, *) cfg%p0
[2696]504
[3182]505             CASE( '-ug', '-u', '--geostrophic-u' )
[3866]506                cfg%ug_defined_by_user = .TRUE.
[3182]507                CALL get_option_argument( i, arg )
[3866]508                READ(arg, *) cfg%ug
[2696]509
[3182]510             CASE( '-vg', '-v', '--geostrophic-v' )
[3866]511                cfg%vg_defined_by_user = .TRUE.
[3182]512                CALL get_option_argument( i, arg )
[3866]513                READ(arg, *) cfg%vg
[2696]514
[3182]515             CASE( '-path', '-p', '--path' )
516                CALL get_option_argument( i, arg )
[3866]517                 cfg%input_path = TRIM(arg)
[2696]518
[3182]519             CASE( '-hhl', '-l', '--hhl-file' )
520                CALL get_option_argument( i, arg )
[3866]521                cfg%hhl_file = TRIM(arg)
[2696]522
[3395]523             CASE( '--input-prefix')
524                CALL get_option_argument( i, arg )
[3866]525                cfg%input_prefix = TRIM(arg)
526                cfg%input_prefix_is_set = .TRUE.
[3395]527   
528             CASE( '-a', '--averaging-angle' )
529                CALL get_option_argument( i, arg )
[3866]530                READ(arg, *) cfg%averaging_angle
[3395]531
[3182]532             CASE( '-static', '-t', '--static-driver' )
[4538]533                cfg%static_driver_is_set = .TRUE.
[3182]534                CALL get_option_argument( i, arg )
[3866]535                cfg%static_driver_file = TRIM(arg)
[2696]536
[3182]537             CASE( '-soil', '-s', '--soil-file')
538                CALL get_option_argument( i, arg )
[3866]539                cfg%soiltyp_file = TRIM(arg)
[2696]540
[3182]541             CASE( '--flow-prefix')
542                CALL get_option_argument( i, arg )
[3866]543                cfg%flow_prefix = TRIM(arg)
544                cfg%flow_prefix_is_set = .TRUE.
[3395]545   
[3182]546             CASE( '--radiation-prefix')
547                CALL get_option_argument( i, arg )
[3866]548                cfg%radiation_prefix = TRIM(arg)
549                cfg%radiation_prefix_is_set = .TRUE.
[3395]550   
[3182]551             CASE( '--soil-prefix')
552                CALL get_option_argument( i, arg )
[3866]553                cfg%soil_prefix = TRIM(arg)
554                cfg%soil_prefix_is_set = .TRUE.
[3395]555   
[4659]556             CASE( '--precipitation-prefix')
[3182]557                CALL get_option_argument( i, arg )
[4659]558                cfg%precipitation_prefix = TRIM(arg)
559                cfg%precipitation_prefix_is_set = .TRUE.
[2696]560
[4659]561             CASE( '--precipitation')
562                cfg%process_precipitation = .TRUE.
563
[3182]564             CASE( '-o', '--output' )
565                CALL get_option_argument( i, arg )
[3866]566                cfg%output_file = TRIM(arg)
[2696]567
[3182]568             CASE( '-n', '--namelist' )
569                CALL get_option_argument( i, arg )
[3866]570                cfg%namelist_file = TRIM(arg)
[2696]571
[4675]572             CASE( '-i', '--init-mode' )
[3182]573                CALL get_option_argument( i, arg )
[3866]574                cfg%ic_mode = TRIM(arg)
[3182]575
[4675]576             CASE( '--soil-init-mode' )
577                CALL get_option_argument( i, arg )
578                cfg%isc_mode = TRIM(arg)
579
[3182]580             CASE( '-f', '--forcing-mode' )
581                CALL get_option_argument( i, arg )
[3866]582                cfg%bc_mode = TRIM(arg)
[3182]583
584             CASE( '--version' )
[3866]585                CALL print_version
[3182]586                STOP
587
588             CASE( '--help' )
[3866]589                CALL print_version
[3182]590                PRINT *, ""
[4553]591                PRINT *, &
[4659]592                   "For documentation and a list of available command-line options " // NEW_LINE( " " ) // &
[4553]593                   " please visit https://palm.muk.uni-hannover.de/trac/wiki/doc/app/iofiles/inifor."
[3182]594                STOP
595
[2696]596             CASE DEFAULT
[3182]597                message = "unknown option '" // TRIM(option) // "'."
[3615]598                CALL inifor_abort('parse_command_line_arguments', message)
[2696]599
[3866]600          END SELECT
[2696]601
[3866]602          i = i + 1
[3182]603
[3866]604       ENDDO
[2696]605
[3866]606    ELSE
607         
[3997]608       CALL print_version
609       CALL report( 'parse_command_line_arguments', 'No arguments present, exiting.' )
610       STOP
[2696]611
[3866]612    ENDIF
[2696]613
[3866]614 END SUBROUTINE parse_command_line_arguments
[2696]615
[3678]616
[3997]617
[3866]618 SUBROUTINE get_datetime_file_list( start_date_string, start_hour, end_hour, &
619                                    step_hour, input_path, prefix, suffix,   &
620                                    file_list )
[3678]621
[3866]622    CHARACTER (LEN=DATE), INTENT(IN) ::  start_date_string
623    CHARACTER (LEN=*),    INTENT(IN) ::  prefix, suffix, input_path
[4523]624    INTEGER(iwp),         INTENT(IN) ::  start_hour, end_hour, step_hour
[3866]625    CHARACTER(LEN=*), ALLOCATABLE, INTENT(INOUT) ::  file_list(:)
[3678]626
[4523]627    INTEGER(iwp)        ::  number_of_intervals, hour, i
[3866]628    CHARACTER(LEN=DATE) ::  date_string
[3678]629
[3866]630    number_of_intervals = CEILING( REAL(end_hour - start_hour) / step_hour )
631    ALLOCATE( file_list(number_of_intervals + 1) )
[3678]632
[3866]633    DO  i = 0, number_of_intervals
[3678]634
[3866]635       hour = start_hour + i * step_hour
636       date_string = add_hours_to(start_date_string, hour)
[3678]637
[3866]638       file_list(i+1) = TRIM(input_path) // TRIM(prefix) //                  &
639                        TRIM(date_string) // TRIM(suffix) // '.nc'
[3678]640
[3866]641    ENDDO
[3678]642
[3866]643 END SUBROUTINE get_datetime_file_list
[3678]644
[3557]645!------------------------------------------------------------------------------!
646! Description:
647! ------------
[3678]648!> Establish a list of files based on the given start and end times and file
649!> prefixes and suffixes.
650!------------------------------------------------------------------------------!
[3866]651 SUBROUTINE get_input_file_list( start_date_string, start_hour, end_hour,    &
652                                 step_hour, input_path, prefix, suffix,      &
[4659]653                                 file_list )
[3678]654
[3866]655    CHARACTER (LEN=DATE), INTENT(IN) ::  start_date_string
656    CHARACTER (LEN=*),    INTENT(IN) ::  prefix, suffix, input_path
[4523]657    INTEGER(iwp),         INTENT(IN) ::  start_hour, end_hour, step_hour
[3866]658    CHARACTER(LEN=*), ALLOCATABLE, INTENT(INOUT) ::  file_list(:)
[3678]659
[3866]660    CALL get_datetime_file_list( start_date_string, start_hour, end_hour,    &
661                                 step_hour, input_path, prefix, suffix,      &
662                                 file_list )
[3678]663
[4659]664 END SUBROUTINE get_input_file_list
[3678]665
666
[4659]667!------------------------------------------------------------------------------!
668! Description:
669! ------------
670!> Abort INIFOR if the given file is not present.
671!------------------------------------------------------------------------------!
672LOGICAL FUNCTION file_is_present( filename, file_kind, message )
[3716]673
[4659]674    CHARACTER(LEN=*), INTENT(IN)            ::  filename, file_kind
675    CHARACTER(LEN=*), INTENT(OUT)           ::  message
[3716]676
[4659]677    file_is_present = file_present(filename)
678
679    IF (.NOT. file_is_present)  THEN
680
681       IF (LEN( TRIM( filename ) ) == 0)  THEN
682          message = "No name was given for the " // TRIM( file_kind ) // " file."
683       ELSE
684          message = "The " // TRIM( file_kind ) // " file '" //                  &
685                    TRIM( filename ) // "' was not found."
686       ENDIF
687
[3866]688    ENDIF
[3716]689
[4659]690END FUNCTION file_is_present
[3678]691
692
693!------------------------------------------------------------------------------!
694! Description:
695! ------------
696!> Abort INIFOR if the given file is not present.
697!------------------------------------------------------------------------------!
[4659]698 SUBROUTINE verify_file( file_name, file_kind )
[3678]699
[3866]700    CHARACTER(LEN=*), INTENT(IN)           ::  file_name, file_kind
[3678]701
[4659]702    IF (.NOT. file_is_present( file_name, file_kind, message ))  THEN
[3678]703
[4659]704       CALL inifor_abort( 'verify_file', message )
[3678]705
[3866]706    ENDIF
[3678]707
[4659]708    message = "Set up input file name '" // TRIM( file_name ) // "'"
709    CALL report( 'verify_file', message )
[3764]710
[3866]711 END SUBROUTINE verify_file
[3678]712!------------------------------------------------------------------------------!
713! Description:
714! ------------
[3557]715!> Get the argument of the i'th command line option, which is at the location
716!> i+1 of the argument list.
717!------------------------------------------------------------------------------!
[4659]718 SUBROUTINE get_option_argument( i, arg )
[3866]719    CHARACTER(LEN=PATH), INTENT(INOUT) ::  arg
720    INTEGER, INTENT(INOUT)             ::  i
[2696]721
[3866]722    i = i + 1
[4659]723    CALL GET_COMMAND_ARGUMENT( i, arg )
[3182]724
[3866]725 END SUBROUTINE
[3182]726
727
[3557]728!------------------------------------------------------------------------------!
729! Description:
730! ------------
731!> Checks the INIFOR configuration 'cfg' for plausibility.
732!------------------------------------------------------------------------------!
[4659]733 SUBROUTINE validate_config( cfg )
[3866]734    TYPE(inifor_config), INTENT(IN) ::  cfg
[3182]735
[4659]736    CALL verify_file( cfg%hhl_file, 'HHL' )
737    CALL verify_file( cfg%namelist_file, 'NAMELIST' )
738    CALL verify_file( cfg%soiltyp_file, 'SOILTYP' )
[3182]739
[3557]740!
[3866]741!-- Only check optional static driver file name, if it has been given.
[4659]742    IF (TRIM( cfg%static_driver_file ) .NE. '')  THEN
743       CALL verify_file( cfg%static_driver_file, 'static driver' )
[3866]744    ENDIF
[3182]745
[4659]746    SELECT CASE( TRIM( cfg%ic_mode ) )
[4675]747       CASE( CFG_INIT_PROFILE, CFG_INIT_VOLUME )
[3866]748       CASE DEFAULT
[4659]749          message = "Initialization mode '" // TRIM( cfg%ic_mode ) //&
[3866]750                    "' is not supported. " //&
[4675]751                    "Please select either '" // CFG_INIT_PROFILE //"' or '" // &
752                    CFG_INIT_VOLUME //"', " //&
753                    "or omit the -i/--init-mode option entirely, which corresponds "//&
[3866]754                    "to the latter."
755          CALL inifor_abort( 'validate_config', message )
756    END SELECT
[3182]757
[4675]758    SELECT CASE( TRIM( cfg%isc_mode ) )
759       CASE( CFG_INIT_SOIL_PROFILE, CFG_INIT_SOIL_VOLUME )
760       CASE DEFAULT
761          message = "Soil initialization mode '" // TRIM( cfg%isc_mode ) //&
762                    "' is not supported. " //&
763                    "Please select either '" // CFG_INIT_SOIL_PROFILE //"' or '" // &
764                    CFG_INIT_SOIL_VOLUME //"', " //&
765                    "or omit the --soil-init-mode option entirely, which corresponds "//&
766                    "to the latter."
767          CALL inifor_abort( 'validate_config', message )
768    END SELECT
769
[3866]770    SELECT CASE( TRIM(cfg%bc_mode) )
[4675]771       CASE( CFG_FORCING_HOMO, CFG_FORCING_HETERO, CFG_FORCING_NUDGING )
[3866]772       CASE DEFAULT
[4659]773          message = "Forcing mode '" // TRIM( cfg%bc_mode ) //& 
[3866]774                    "' is not supported. " //&
[4675]775                    "Please select either '" // CFG_FORCING_NUDGING //          &
776                    "', '" // CFG_FORCING_HOMO // "', or '" //                 &
777                    CFG_FORCING_HETERO // "' " //&
[3866]778                    "or omit the -f/--forcing-mode option entirely, which corresponds "//&
779                    "to the latter."
780          CALL inifor_abort( 'validate_config', message )
781    END SELECT
[3182]782
[4659]783    SELECT CASE( TRIM( cfg%averaging_mode ) )
[3866]784       CASE( 'level' )
785       CASE( 'height' )
[4659]786          message = "Averaging mode '" // TRIM( cfg%averaging_mode ) //&
[3866]787                    "' is currently not supported. " //&
788                    "Please use level-based averaging by selecting 'level', " //&
789                    "or by omitting the --averaging-mode option entirely."
790          CALL inifor_abort( 'validate_config', message )
791       CASE DEFAULT
[4659]792          message = "Averaging mode '" // TRIM( cfg%averaging_mode ) //&
[3866]793                    "' is not supported. " //&
794          !          "Please select either 'height' or 'level', " //&
795          !          "or omit the --averaging-mode option entirely, which corresponds "//&
796          !          "to the latter."
797                    "Please use level-based averaging by selecting 'level', " //&
798                    "or by omitting the --averaging-mode option entirely."
799          CALL inifor_abort( 'validate_config', message )
800    END SELECT
[3182]801
[3866]802    IF ( cfg%ug_defined_by_user .NEQV. cfg%vg_defined_by_user )  THEN
803       message = "You specified only one component of the geostrophic " // &
804                 "wind. Please specify either both or none."
805       CALL inifor_abort( 'validate_config', message )
806    ENDIF
[3182]807
[4538]808    IF ( .NOT. cfg%static_driver_is_set .AND. .NOT. cfg%z0_is_set )  THEN
809       message =                                                               &
[4659]810          "The vertical origin of the PALM grid has not been defined. " // NEW_LINE( " " ) // &
811          "Please specify the right value for your setup by either " // NEW_LINE( " " ) // &
812          "  - using the command-line option --elevation <height above sea level>, or by" // NEW_LINE( " " ) // &
813          "  - specifying a static driver file using --static <filename> in order to use " // NEW_LINE( " " ) // &
[4538]814          "    use the value of origin_z (and origin_lon and origin_lat) specifed therein."
815       CALL inifor_abort( 'validate_config', message )
816    ENDIF
817
[3866]818 END SUBROUTINE validate_config
[3395]819
[3182]820
[4074]821 SUBROUTINE get_cosmo_grid( hhl_file, soil_file, rlon, rlat, hhl, hfl, depths, &
[3866]822                            d_depth, d_depth_rho_inv, phi_n, lambda_n,       &
823                            phi_equat,                                       &
824                            lonmin_cosmo, lonmax_cosmo,                      &
825                            latmin_cosmo, latmax_cosmo,                      &
826                            nlon, nlat, nlev, ndepths )
[3182]827
[4074]828    CHARACTER(LEN=PATH), INTENT(IN)                      ::  hhl_file  !< path to file containing the HHL variable (height of half layers)
829    CHARACTER(LEN=PATH), INTENT(IN)                      ::  soil_file !< path to one of the soil input files for reading soil layer depths
[3866]830    REAL(wp), DIMENSION(:), ALLOCATABLE, INTENT(OUT)     ::  rlon      !< longitudes of COSMO-DE's rotated-pole grid
831    REAL(wp), DIMENSION(:), ALLOCATABLE, INTENT(OUT)     ::  rlat      !< latitudes of COSMO-DE's rotated-pole grid
832    REAL(wp), DIMENSION(:,:,:), ALLOCATABLE, INTENT(OUT) ::  hhl       !< heights of half layers (cell faces) above sea level in COSMO-DE, read in from external file
833    REAL(wp), DIMENSION(:,:,:), ALLOCATABLE, INTENT(OUT) ::  hfl       !< heights of full layers (cell centres) above sea level in COSMO-DE, computed as arithmetic average of hhl
834    REAL(wp), DIMENSION(:), ALLOCATABLE, INTENT(OUT)     ::  depths    !< COSMO-DE's TERRA-ML soil layer depths
835    REAL(wp), DIMENSION(:), ALLOCATABLE, INTENT(OUT)     ::  d_depth
836    REAL(wp), DIMENSION(:), ALLOCATABLE, INTENT(OUT)     ::  d_depth_rho_inv
837    REAL(wp), INTENT(OUT)                                ::  phi_n
838    REAL(wp), INTENT(OUT)                                ::  phi_equat
839    REAL(wp), INTENT(OUT)                                ::  lambda_n
840    REAL(wp), INTENT(OUT)                                ::  lonmin_cosmo !< Minimunm longitude of COSMO-DE's rotated-pole grid [COSMO rotated-pole rad]
841    REAL(wp), INTENT(OUT)                                ::  lonmax_cosmo !< Maximum longitude of COSMO-DE's rotated-pole grid [COSMO rotated-pole rad]
842    REAL(wp), INTENT(OUT)                                ::  latmin_cosmo !< Minimunm latitude of COSMO-DE's rotated-pole grid [COSMO rotated-pole rad]
843    REAL(wp), INTENT(OUT)                                ::  latmax_cosmo !< Maximum latitude of COSMO-DE's rotated-pole grid [COSMO rotated-pole rad]
[4523]844    INTEGER(iwp), INTENT(OUT)                            ::  nlon, nlat, nlev, ndepths
[3801]845
[3866]846    TYPE(nc_var) ::  cosmo_var !< COSMO dummy variable, used for reading HHL, rlon, rlat
[4523]847    INTEGER(iwp) ::  k
[3801]848
849!
[3866]850!-- Read in COSMO's heights of half layers (vertical cell faces)
851    cosmo_var%name = NC_HHL_NAME
[4074]852    CALL get_netcdf_variable( hhl_file, cosmo_var, hhl )
853    CALL get_netcdf_dim_vector( hhl_file, NC_RLON_NAME, rlon )
854    CALL get_netcdf_dim_vector( hhl_file, NC_RLAT_NAME, rlat )
[3866]855    CALL get_netcdf_dim_vector( soil_file, NC_DEPTH_NAME, depths)
856    CALL log_runtime( 'time', 'read' )
[3801]857
[3866]858    CALL reverse( hhl )
859    nlon = SIZE( hhl, 1 )
860    nlat = SIZE( hhl, 2 )
861    nlev = SIZE( hhl, 3 )
862    ndepths = SIZE( depths )
[3801]863
[3866]864    CALL log_runtime( 'time', 'comp' )
[3801]865
[3866]866    ALLOCATE( hfl( nlon, nlat, nlev-1 ) )
867    ALLOCATE( d_depth( ndepths ), d_depth_rho_inv( ndepths ) )
868    CALL log_runtime('time', 'alloc')
[3801]869
[3866]870    CALL get_soil_layer_thickness( depths, d_depth )
871    d_depth_rho_inv = 1.0_wp / ( d_depth * RHO_L )
[3801]872
873!
[3866]874!-- Compute COSMO's heights of full layers (cell centres)
875    DO  k = 1, nlev-1
876       hfl(:,:,k) = 0.5_wp * ( hhl(:,:,k) +                                  &
877                               hhl(:,:,k+1) )
878    ENDDO
[3801]879!
[3866]880!-- COSMO rotated pole coordinates
881    phi_n = TO_RADIANS                                                       &
[4074]882          * get_netcdf_variable_attribute( hhl_file,                         &
[3866]883                                           NC_ROTATED_POLE_NAME,             &
884                                           NC_POLE_LATITUDE_NAME )
[3801]885
[3866]886    lambda_n = TO_RADIANS                                                    &
[4074]887             * get_netcdf_variable_attribute( hhl_file,                      &
[3866]888                                              NC_ROTATED_POLE_NAME,          &
889                                              NC_POLE_LONGITUDE_NAME )
[3801]890
[3866]891    phi_equat = 90.0_wp * TO_RADIANS - phi_n
[3801]892
[3866]893    lonmin_cosmo = MINVAL( rlon ) * TO_RADIANS
894    lonmax_cosmo = MAXVAL( rlon ) * TO_RADIANS
895    latmin_cosmo = MINVAL( rlat ) * TO_RADIANS
896    latmax_cosmo = MAXVAL( rlat ) * TO_RADIANS
897    CALL log_runtime('time', 'comp')
[3801]898
[3866]899 END SUBROUTINE get_cosmo_grid
[3801]900
901
[3557]902!------------------------------------------------------------------------------!
903! Description:
904! ------------
[3801]905!> Fills the thickness array of the COSMO soil layers. Since COSMO's (i.e.
906!> TERRA_ML's [1]) soil layer boundaries follow the rule
907!>
908!>    depth(0) = 0.0, and
909!>    depth(k) = 0.01 * 3**(k-1), k in [1,2,3,...,7]
910!>
911!> and full levels are defined as the midpoints between two layer boundaries,
912!> all except the first layer thicknesses equal the depth of the midpoint.
913!>
914!> [1] A Description of the Nonhydrostatic Regional COSMO Model Part II :
915!>     Physical Parameterization*, Sect. 11 TERRA_ML.
916!>     http://www.cosmo-model.org/content/model/documentation/core/cosmoPhysParamtr.pdf)
917!>
918!> Input parameters:
919!> -----------------
920!>
921!> depths: array of full soil layer depths (cell midpoints)
922!>
923!>
924!> Output parameters:
925!> ------------------
926!>
927!> d_depth: array of soil layer thicknesses
928!>
929!------------------------------------------------------------------------------!
[4659]930 SUBROUTINE get_soil_layer_thickness( depths, d_depth )
[3801]931
[3866]932    REAL(wp), INTENT(IN)  ::  depths(:)
933    REAL(wp), INTENT(OUT) ::  d_depth(:)
[3801]934
[3866]935    d_depth(:) = depths(:)
936    d_depth(1) = 2.0_wp * depths(1)
[3801]937
[3866]938 END SUBROUTINE get_soil_layer_thickness
[3801]939!------------------------------------------------------------------------------!
940! Description:
941! ------------
[3557]942!> Check whether the given file is present on the filesystem.
943!------------------------------------------------------------------------------!
[4659]944 LOGICAL FUNCTION file_present( filename )
[3866]945    CHARACTER(LEN=PATH), INTENT(IN) ::  filename
[3182]946
[4659]947    INQUIRE( FILE=filename, EXIST=file_present )
[3182]948
[3866]949 END FUNCTION file_present
[3182]950
951
[2696]952!------------------------------------------------------------------------------!
953! Description:
954! ------------
[3557]955!> This routine initializes the dynamic driver file, i.e. INIFOR's netCDF output
956!> file.
[2696]957!>
958!> Besides writing metadata, such as global attributes, coordinates, variables,
[3557]959!> in the netCDF file, various netCDF IDs are saved for later, when INIFOR
[2696]960!> writes the actual data.
961!------------------------------------------------------------------------------!
[3866]962 SUBROUTINE setup_netcdf_dimensions( output_file, palm_grid,                  &
963                                     start_date_string, origin_lon, origin_lat )
[2696]964
[3866]965    TYPE(nc_file), INTENT(INOUT)      ::  output_file
966    TYPE(grid_definition), INTENT(IN) ::  palm_grid
967    CHARACTER (LEN=DATE), INTENT(IN)  ::  start_date_string
968    REAL(wp), INTENT(IN)              ::  origin_lon, origin_lat
[2696]969
[3866]970    CHARACTER (LEN=8)     ::  date_string
971    CHARACTER (LEN=10)    ::  time_string
972    CHARACTER (LEN=5)     ::  zone_string
973    CHARACTER (LEN=SNAME) ::  history_string
[4523]974    INTEGER               ::  nx, ny, nz, nt
975    INTEGER               ::  ncid, dimids(3), dimvarids(3)
[3866]976    REAL(wp)              ::  z0
[2696]977
[3866]978    message = "Initializing PALM-4U dynamic driver file '" //               &
979              TRIM(output_file%name) // "' and setting up dimensions."
980    CALL report('setup_netcdf_dimensions', message)
[3182]981
[3557]982!
[3866]983!-- Create the netCDF file as in netCDF-4/HDF5 format if __netcdf4 preprocessor flag is given
[3182]984#if defined( __netcdf4 )
[3866]985    CALL check(nf90_create(TRIM(output_file%name), OR(NF90_CLOBBER, NF90_HDF5), ncid))
[3182]986#else
[3866]987    CALL check(nf90_create(TRIM(output_file%name), NF90_CLOBBER, ncid))
[3182]988#endif
[2696]989
[3395]990!------------------------------------------------------------------------------
991!- Section 1: Define NetCDF dimensions and coordinates
992!------------------------------------------------------------------------------
[3866]993    nt = SIZE(output_file%time)
994    nx = palm_grid%nx
995    ny = palm_grid%ny
996    nz = palm_grid%nz
997    z0 = palm_grid%z0
[3395]998
999
[2696]1000!
1001!------------------------------------------------------------------------------
[3395]1002!- Section 2: Write global NetCDF attributes
[2696]1003!------------------------------------------------------------------------------
[3866]1004    CALL date_and_time(DATE=date_string, TIME=time_string, ZONE=zone_string)
1005    history_string =                                                        &
1006        'Created on '// date_string      //                                 &
1007        ' at '       // time_string(1:2) // ':' // time_string(3:4) //      &
1008        ' (UTC'      // zone_string // ')'
[3182]1009
[3866]1010    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'title',          'PALM input file for scenario ...'))
1011    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'institution',    'Deutscher Wetterdienst, Offenbach'))
1012    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'author',         'Eckhard Kadasch, eckhard.kadasch@dwd.de'))
1013    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'history',        TRIM(history_string)))
1014    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'references',     '--'))
1015    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'comment',        '--'))
1016    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'origin_lat',     TRIM(real_to_str(origin_lat*TO_DEGREES, '(F18.13)'))))
1017    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'origin_lon',     TRIM(real_to_str(origin_lon*TO_DEGREES, '(F18.13)'))))
[3557]1018!
[3866]1019!-- FIXME: This is the elevation relative to COSMO-DE/D2 sea level and does
1020!-- FIXME: not necessarily comply with DHHN2016 (c.f. PALM Input Data
1021!-- FIXME: Standard v1.9., origin_z)
1022    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'origin_z',       TRIM(real_to_str(z0, '(F18.13)'))))
1023    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'inifor_version', TRIM(VERSION)))
1024    CALL check(nf90_put_att(ncid, NF90_GLOBAL, 'palm_version',   '--'))
[2696]1025
1026!
1027!
1028!------------------------------------------------------------------------------
1029!- Section 2a: Define dimensions for cell centers (scalars in soil and atmosph.)
1030!------------------------------------------------------------------------------
[3557]1031!
[3866]1032!-- reset dimids first
1033    dimids = (/0, 0, 0/)
1034    CALL check( nf90_def_dim(ncid, "x", nx+1, dimids(1)) )
1035    CALL check( nf90_def_dim(ncid, "y", ny+1, dimids(2)) )
1036    CALL check( nf90_def_dim(ncid, "z", nz, dimids(3)) )
[3557]1037!
[3866]1038!-- save dimids for later
1039    output_file%dimids_scl = dimids 
[2696]1040
[3557]1041!
[3866]1042!-- reset dimvarids first
1043    dimvarids = (/0, 0, 0/)
1044    CALL check(nf90_def_var(ncid, "x", NF90_FLOAT, dimids(1), dimvarids(1)))
1045    CALL check(nf90_put_att(ncid, dimvarids(1), "standard_name", "x coordinate of cell centers"))
1046    CALL check(nf90_put_att(ncid, dimvarids(1), "units", "m"))
[2696]1047
[3866]1048    CALL check(nf90_def_var(ncid, "y", NF90_FLOAT, dimids(2), dimvarids(2)))
1049    CALL check(nf90_put_att(ncid, dimvarids(2), "standard_name", "y coordinate of cell centers"))
1050    CALL check(nf90_put_att(ncid, dimvarids(2), "units", "m"))
[2696]1051
[3866]1052    CALL check(nf90_def_var(ncid, "z", NF90_FLOAT, dimids(3), dimvarids(3)))
1053    CALL check(nf90_put_att(ncid, dimvarids(3), "standard_name", "z coordinate of cell centers"))
1054    CALL check(nf90_put_att(ncid, dimvarids(3), "units", "m"))
[3557]1055!
[3866]1056!-- save dimvarids for later
1057    output_file%dimvarids_scl = dimvarids
[2696]1058
[3557]1059!
[3866]1060!-- overwrite third dimid with the one of depth
1061    CALL check(nf90_def_dim(ncid, "zsoil", SIZE(palm_grid%depths), dimids(3)) )
[3557]1062!
[3866]1063!-- save dimids for later
1064    output_file%dimids_soil = dimids
[2696]1065
[3557]1066!
[3866]1067!-- overwrite third dimvarid with the one of depth
1068    CALL check(nf90_def_var(ncid, "zsoil", NF90_FLOAT, output_file%dimids_soil(3), dimvarids(3)))
1069    CALL check(nf90_put_att(ncid, dimvarids(3), "standard_name", "depth_below_land"))
1070    CALL check(nf90_put_att(ncid, dimvarids(3), "positive", "down"))
1071    CALL check(nf90_put_att(ncid, dimvarids(3), "units", "m"))
[2696]1072!
[3866]1073!-- save dimvarids for later
1074    output_file%dimvarids_soil = dimvarids
[3557]1075!
[2696]1076!------------------------------------------------------------------------------
1077!- Section 2b: Define dimensions for cell faces/velocities
1078!------------------------------------------------------------------------------
[3557]1079!
[3866]1080!-- reset dimids first
1081    dimids = (/0, 0, 0/)
1082    CALL check(nf90_def_dim(ncid, "xu", nx, dimids(1)) )
1083    CALL check(nf90_def_dim(ncid, "yv", ny, dimids(2)) )
1084    CALL check(nf90_def_dim(ncid, "zw", nz-1, dimids(3)) )
[3557]1085!
[3866]1086!-- save dimids for later
1087    output_file%dimids_vel = dimids
[2696]1088
[3557]1089!
[3866]1090!-- reset dimvarids first
1091    dimvarids = (/0, 0, 0/)
1092    CALL check(nf90_def_var(ncid, "xu", NF90_FLOAT, dimids(1), dimvarids(1)))
1093    CALL check(nf90_put_att(ncid, dimvarids(1), "standard_name", "x coordinate of cell faces"))
1094    CALL check(nf90_put_att(ncid, dimvarids(1), "units", "m"))
[2696]1095
[3866]1096    CALL check(nf90_def_var(ncid, "yv", NF90_FLOAT, dimids(2), dimvarids(2)))
1097    CALL check(nf90_put_att(ncid, dimvarids(2), "standard_name", "y coordinate of cell faces"))
1098    CALL check(nf90_put_att(ncid, dimvarids(2), "units", "m"))
[2696]1099
[3866]1100    CALL check(nf90_def_var(ncid, "zw", NF90_FLOAT, dimids(3), dimvarids(3)))
1101    CALL check(nf90_put_att(ncid, dimvarids(3), "standard_name", "z coordinate of cell faces"))
1102    CALL check(nf90_put_att(ncid, dimvarids(3), "units", "m"))
[3557]1103!
[3866]1104!-- save dimvarids for later
1105    output_file%dimvarids_vel = dimvarids
[2696]1106
1107!
1108!------------------------------------------------------------------------------
1109!- Section 2c: Define time dimension
1110!------------------------------------------------------------------------------
[3866]1111    CALL check(nf90_def_dim(ncid, "time", nt, output_file%dimid_time) )
1112    CALL check(nf90_def_var(ncid, "time", NF90_FLOAT, &
1113                                          output_file%dimid_time, &
1114                                          output_file%dimvarid_time))
1115    CALL check(nf90_put_att(ncid, output_file%dimvarid_time, "standard_name", "time"))
1116    CALL check(nf90_put_att(ncid, output_file%dimvarid_time, "long_name", "time"))
1117    CALL check(nf90_put_att(ncid, output_file%dimvarid_time, "units",     &
1118                            "seconds since " // start_date_string // " UTC"))
[2696]1119
[3866]1120    CALL check(nf90_enddef(ncid))
[2696]1121
1122!
1123!------------------------------------------------------------------------------
1124!- Section 3: Write grid coordinates
1125!------------------------------------------------------------------------------
[3866]1126    CALL check(nf90_put_var(ncid, output_file%dimvarids_scl(1), palm_grid%x))
1127    CALL check(nf90_put_var(ncid, output_file%dimvarids_scl(2), palm_grid%y))
1128    CALL check(nf90_put_var(ncid, output_file%dimvarids_scl(3), palm_grid%z))
[2696]1129
[3866]1130    CALL check(nf90_put_var(ncid, output_file%dimvarids_vel(1), palm_grid%xu))
1131    CALL check(nf90_put_var(ncid, output_file%dimvarids_vel(2), palm_grid%yv))
1132    CALL check(nf90_put_var(ncid, output_file%dimvarids_vel(3), palm_grid%zw))
[2696]1133
[3557]1134!
[3866]1135!-- TODO Read in soil depths from input file before this.
1136    CALL check(nf90_put_var(ncid, output_file%dimvarids_soil(3), palm_grid%depths))
[2696]1137
[3557]1138!
[3866]1139!-- Write time vector
1140    CALL check(nf90_put_var(ncid, output_file%dimvarid_time, output_file%time))
[2696]1141
[3557]1142!
[3866]1143!-- Close the file
1144    CALL check(nf90_close(ncid))
[2696]1145
[3866]1146 END SUBROUTINE setup_netcdf_dimensions
[2696]1147
1148
[3557]1149!------------------------------------------------------------------------------!
1150! Description:
1151! ------------
1152!> Defines the netCDF variables to be written to the dynamic driver file
1153!------------------------------------------------------------------------------!
[4659]1154 SUBROUTINE setup_netcdf_variables(filename, io_group_list)
[2696]1155
[4659]1156    CHARACTER (LEN=*), INTENT(IN)       ::  filename
1157    TYPE(io_group), INTENT(IN), TARGET  ::  io_group_list(:)
[3456]1158
[4659]1159    TYPE(io_group), POINTER             ::  group
1160    TYPE(nc_var), POINTER               ::  var
1161    INTEGER(iwp)                        ::  group_idx, var_idx, n_var
1162    INTEGER                             ::  ncid
1163    LOGICAL                             ::  to_be_written
[2696]1164
[3866]1165    message = "Defining variables in dynamic driver '" // TRIM(filename) // "'."
1166    CALL report('setup_netcdf_variables', message)
[2696]1167
[3866]1168    CALL check(nf90_open(TRIM(filename), NF90_WRITE, ncid))
1169    CALL check(nf90_redef(ncid))
[2696]1170
[4659]1171    n_var = 0
1172    DO  group_idx = 1, SIZE( io_group_list )
[2696]1173
[4659]1174       group => io_group_list(group_idx)
1175       DO var_idx = 1, SIZE( group%out_vars )
[2696]1176
[4659]1177          to_be_written = .FALSE.
[3456]1178
[4659]1179          IF (ALLOCATED( group%out_vars ))  THEN
1180             var => group%out_vars(var_idx)
1181             n_var = n_var + 1
[2696]1182
[4659]1183             to_be_written = (                                                 &
1184                group%to_be_processed  .AND.  var%to_be_processed              &
1185                .AND. .NOT. var%is_internal                                    &
1186             )
1187          ENDIF
1188
1189          IF ( to_be_written )  THEN
1190             message = "  variable #" // TRIM(str(n_var)) // " '" // TRIM(var%name) // "'."
1191             CALL report('setup_netcdf_variables', message)
1192
1193             CALL netcdf_define_variable(var, ncid)
1194             CALL netcdf_get_dimensions(var, ncid)
1195          ENDIF
1196
1197       ENDDO
[3866]1198       
1199    ENDDO
[2696]1200
[3866]1201    CALL check(nf90_enddef(ncid))
1202    CALL check(nf90_close(ncid))
[2696]1203
[3866]1204    message = "Dynamic driver '" // TRIM(filename) // "' initialized successfully."
1205    CALL report('setup_netcdf_variables', message)
[2696]1206
[3866]1207 END SUBROUTINE setup_netcdf_variables
[2696]1208
1209
1210!------------------------------------------------------------------------------!
1211! Description:
1212! ------------
1213!> This routine reads and returns all input variables of the given IO group
1214!> It accomodates the data by allocating a container variable that represents a
1215!> list of arrays of the same length as the groups variable list. (This list
1216!> will typically contain one or two items.) After the container, its members
1217!> are allocated one by one with the appropriate, possibly different,
1218!> dimensions.
1219!>
1220!> The 'group' is an INTENT(INOUT) variable so that 'get_netcdf_variable()' can
1221!> record netCDF IDs in the 'in_var_list()' member variable.
1222!------------------------------------------------------------------------------!
[3866]1223 SUBROUTINE read_input_variables(group, iter, buffer)
1224    TYPE(io_group), INTENT(INOUT), TARGET       ::  group
[4523]1225    INTEGER(iwp), INTENT(IN)                    ::  iter
[3866]1226    TYPE(container), ALLOCATABLE, INTENT(INOUT) ::  buffer(:)
[4523]1227    INTEGER(iwp)                                ::  hour, buf_id
[3866]1228    TYPE(nc_var), POINTER                       ::  input_var
1229    CHARACTER(LEN=PATH), POINTER                ::  input_file
[4523]1230    INTEGER(iwp)                                ::  ivar, nbuffers
[2696]1231
[3866]1232    message = "Reading data for I/O group '" // TRIM(group%in_var_list(1)%name) // "'."
1233    CALL report('read_input_variables', message)
[2696]1234
[3866]1235    input_file => group%in_files(iter)
[2696]1236
1237!
1238!------------------------------------------------------------------------------
1239!- Section 1: Load input buffers for accumulated variables
1240!------------------------------------------------------------------------------
[3557]1241!
[3866]1242!-- radiation budgets, precipitation
[4659]1243    IF ( group%kind == 'running average' .OR.                                  &
1244         group%kind == 'accumulated' )  THEN
[2696]1245
[4659]1246       IF ( SIZE( group%in_var_list ) .GT. 1 ) THEN
[3866]1247          message = "I/O groups may not contain more than one " // & 
1248                    "accumulated variable. Group '" // TRIM(group%kind) //&
[4659]1249                    "' contains " //                                           &
1250                    TRIM( str( SIZE( group%in_var_list, kind=iwp ) ) ) // "."
1251          CALL inifor_abort( 'read_input_variables | accumulation', message )
[3866]1252       ENDIF
[2696]1253
[3557]1254!
[3866]1255!--    use two buffer arrays
1256       nbuffers = 2
1257       IF ( .NOT. ALLOCATED( buffer ) )  ALLOCATE( buffer(nbuffers) )
[2696]1258
[3557]1259!
[3866]1260!--    hour of the day
1261       hour = iter - 1
[3557]1262!
[3866]1263!--    chose correct buffer array
1264       buf_id = select_buffer(hour)
[2696]1265
[3866]1266       CALL log_runtime('time', 'read')
1267       IF ( ALLOCATED(buffer(buf_id)%array) )  DEALLOCATE(buffer(buf_id)%array)
1268       CALL log_runtime('time', 'alloc')
[2696]1269
[3866]1270       input_var => group%in_var_list(1)
1271       CALL get_netcdf_variable(input_file, input_var, buffer(buf_id)%array)
1272       CALL report('read_input_variables', "Read accumulated " // TRIM(group%in_var_list(1)%name)) 
[2696]1273
[3866]1274       IF ( input_var%is_upside_down )  CALL reverse(buffer(buf_id)%array)
1275       CALL log_runtime('time', 'comp')
[2696]1276         
1277!------------------------------------------------------------------------------
1278!- Section 2: Load input buffers for normal I/O groups
1279!------------------------------------------------------------------------------
[3866]1280    ELSE
[2696]1281
[3557]1282!
[4659]1283!--    Allocate one input buffer per output quantity. If more quantities
[3866]1284!--    have to be computed than input variables exist in this group,
1285!--    allocate more buffers. For instance, in the thermodynamics group,
1286!--    there are three input variabels (PP, T, Qv) and four quantities
1287!--    necessart (P, Theta, Rho, qv) for the corresponding output fields
1288!--    (p0, Theta, qv, ug, and vg)
[4659]1289       ALLOCATE( buffer(group%n_output_quantities) )
[3866]1290       CALL log_runtime('time', 'alloc')
1291       
[3557]1292!
[3866]1293!--    Read in all input variables, leave extra buffers-if any-untouched.
1294       DO  ivar = 1, group%n_inputs
[2696]1295
[3866]1296          input_var => group%in_var_list(ivar)
[2696]1297
[4659]1298          IF ( input_var%to_be_processed )  THEN
1299!            Check wheather P or PP is present in input file
1300             IF (input_var%name == 'P')  THEN
1301                input_var%name = TRIM( get_pressure_varname(input_file) )
1302             CALL log_runtime('time', 'read')
1303             ENDIF
[2696]1304
[4659]1305             CALL get_netcdf_variable(input_file, input_var, buffer(ivar)%array)
[2696]1306
[4659]1307             IF ( input_var%is_upside_down )  CALL reverse(buffer(ivar)%array)
1308             CALL log_runtime('time', 'comp')
1309          ENDIF
[2696]1310
[3866]1311       ENDDO
1312    ENDIF
[2696]1313
[3866]1314 END SUBROUTINE read_input_variables
[2696]1315
1316
[3557]1317!------------------------------------------------------------------------------!
1318! Description:
1319! ------------
1320!> Select the appropriate buffer ID for accumulated COSMO input variables
1321!> depending on the current hour.
1322!------------------------------------------------------------------------------!
[4523]1323 INTEGER(iwp) FUNCTION select_buffer(hour)
1324    INTEGER(iwp), INTENT(IN) ::  hour
1325    INTEGER(iwp)             ::  step
[2696]1326
[4523]1327    select_buffer = 0_iwp
1328    step = MODULO(hour, 3_iwp) + 1_iwp
[2696]1329
[3866]1330    SELECT CASE(step)
[2696]1331       CASE(1, 3)
[4523]1332           select_buffer = 1_iwp
[2696]1333       CASE(2)
[4523]1334           select_buffer = 2_iwp
[2696]1335       CASE DEFAULT
1336           message = "Invalid step '" // TRIM(str(step))
[3615]1337           CALL inifor_abort('select_buffer', message)
[3866]1338    END SELECT
1339 END FUNCTION select_buffer
[2696]1340
1341
1342!------------------------------------------------------------------------------!
1343! Description:
1344! ------------
1345!> Checks if the input_file contains the absolute pressure, 'P', or the pressure
1346!> perturbation, 'PP', and returns the appropriate string.
1347!------------------------------------------------------------------------------!
[3866]1348 CHARACTER(LEN=2) FUNCTION get_pressure_varname(input_file) RESULT(var)
1349    CHARACTER(LEN=*) ::  input_file
1350    INTEGER          ::  ncid, varid
[2696]1351
[3866]1352    CALL check(nf90_open( TRIM(input_file), NF90_NOWRITE, ncid ))
1353    IF ( nf90_inq_varid( ncid, 'P', varid ) .EQ. NF90_NOERR )  THEN
[2696]1354
[3866]1355       var = 'P'
[2696]1356
[3866]1357    ELSE IF ( nf90_inq_varid( ncid, 'PP', varid ) .EQ. NF90_NOERR )  THEN
[2696]1358
[3866]1359       var = 'PP'
1360       CALL report('get_pressure_var', 'Using PP instead of P')
[2696]1361
[3866]1362    ELSE
[2696]1363
[3866]1364       message = "Failed to read '" // TRIM(var) // &
1365                 "' from file '" // TRIM(input_file) // "'."
1366       CALL inifor_abort('get_pressure_var', message)
[2696]1367
[3866]1368    ENDIF
[2696]1369
[3866]1370    CALL check(nf90_close(ncid))
[2696]1371
[3866]1372 END FUNCTION get_pressure_varname
[2696]1373
1374
[3557]1375!------------------------------------------------------------------------------!
1376! Description:
1377! ------------
1378!> Read the given global attribute form the given netCDF file.
1379!------------------------------------------------------------------------------!
[3866]1380 FUNCTION get_netcdf_attribute(filename, attribute) RESULT(attribute_value)
[2696]1381
[3866]1382    CHARACTER(LEN=*), INTENT(IN) ::  filename, attribute
1383    REAL(wp)                     ::  attribute_value
[2696]1384
[3866]1385    INTEGER                      ::  ncid
[2696]1386
[3866]1387    IF ( nf90_open( TRIM(filename), NF90_NOWRITE, ncid ) == NF90_NOERR )  THEN
[2696]1388
[3866]1389       CALL check(nf90_get_att(ncid, NF90_GLOBAL, TRIM(attribute), attribute_value))
1390       CALL check(nf90_close(ncid))
[2696]1391
[3866]1392    ELSE
[2696]1393
[3866]1394       message = "Failed to read '" // TRIM(attribute) // &
1395                 "' from file '" // TRIM(filename) // "'."
1396       CALL inifor_abort('get_netcdf_attribute', message)
[2696]1397
[3866]1398    ENDIF
[2696]1399
[3866]1400 END FUNCTION get_netcdf_attribute
[2696]1401
1402
[3801]1403!------------------------------------------------------------------------------!
1404! Description:
1405! ------------
1406!> Read the attribute of the given variable form the given netCDF file.
1407!------------------------------------------------------------------------------!
[3866]1408 FUNCTION get_netcdf_variable_attribute(filename, varname, attribute)       &
1409    RESULT(attribute_value)
[3557]1410
[3866]1411    CHARACTER(LEN=*), INTENT(IN) ::  filename, varname, attribute
1412    REAL(wp)                     ::  attribute_value
[3801]1413
[3866]1414    INTEGER                      ::  ncid, varid
[3801]1415
[3866]1416    IF ( nf90_open( TRIM(filename), NF90_NOWRITE, ncid ) == NF90_NOERR )  THEN
[3801]1417
[3866]1418       CALL check( nf90_inq_varid( ncid, TRIM( varname ), varid ) )
1419       CALL check( nf90_get_att( ncid, varid, TRIM( attribute ),            &
1420                   attribute_value ) )
1421       CALL check( nf90_close( ncid ) )
[3801]1422
[3866]1423    ELSE
[3801]1424
[3866]1425       message = "Failed to read '" // TRIM( varname ) // ":" //            &
1426                 TRIM( attribute ) // "' from file '" // TRIM(filename) // "'."
1427       CALL inifor_abort('get_netcdf_variable_attribute', message)
[3801]1428
[3866]1429    ENDIF
[3801]1430
[3866]1431 END FUNCTION get_netcdf_variable_attribute
[3801]1432
[3557]1433!------------------------------------------------------------------------------!
1434! Description:
1435! ------------
1436!> Updates the dynamic driver with the interpolated field of the current
1437!> variable at the current time step.
1438!------------------------------------------------------------------------------!
[3866]1439 SUBROUTINE update_output(var, array, iter, output_file, cfg)
1440    TYPE(nc_var), INTENT(IN)  ::  var
1441    REAL(wp), INTENT(IN)      ::  array(:,:,:)
[4523]1442    INTEGER(iwp), INTENT(IN)  ::  iter
[3866]1443    TYPE(nc_file), INTENT(IN) ::  output_file
1444    TYPE(inifor_config)       ::  cfg
[2696]1445
[4523]1446    INTEGER      ::  ncid, ndim, start(4), count(4)
1447    LOGICAL      ::  var_is_time_dependent
[2696]1448
[4675]1449    var_is_time_dependent = (                                                  &
1450       var%dimids( var%ndim ) == output_file%dimid_time                        &
[3866]1451    )
[2696]1452
[3557]1453!
[3866]1454!-- Skip time dimension for output
1455    ndim = var%ndim
1456    IF ( var_is_time_dependent )  ndim = var%ndim - 1
[2696]1457
[3866]1458    start(:)      = (/1,1,1,1/)
1459    start(ndim+1) = iter
1460    count(1:ndim) = var%dimlen(1:ndim)
[2696]1461
[3866]1462    CALL check(nf90_open(output_file%name, NF90_WRITE, ncid))
[2696]1463
[3557]1464!
[3866]1465!-- Reduce dimension of output array according to variable kind
1466    SELECT CASE (TRIM(var%kind))
[2696]1467       
[4675]1468       CASE( 'init scalar profile', 'init u profile', 'init v profile',        &
1469             'init w profile', 'init soil profile' )
[2696]1470
1471          CALL check(nf90_put_var( ncid, var%varid, array(1,1,:) ) )
1472
[4675]1473       CASE( 'init soil', 'init scalar', 'init u', 'init v', 'init w' )
[2696]1474
1475          CALL check(nf90_put_var( ncid, var%varid, array(:,:,:) ) )
1476
1477       CASE( 'left scalar', 'right scalar', 'left w', 'right w' ) 
1478
1479          CALL check(nf90_put_var( ncid, var%varid, array(1,:,:),              &
1480                                   start=start(1:ndim+1),                      &
1481                                   count=count(1:ndim) ) )
1482         
1483
[3866]1484          IF (.NOT. SIZE(array, 2) .EQ. var%dimlen(1))  THEN
[2696]1485             PRINT *, "inifor: update_output: Dimension ", 1, " of variable ", &
[4675]1486                 TRIM(var%name), " (", var%dimlen(1),                          &
[2696]1487                 ") does not match the dimension of the output array (",       &
1488                 SIZE(array, 2), ")."
1489             STOP
[3785]1490          ENDIF
[2696]1491         
1492
1493       CASE( 'north scalar', 'south scalar', 'north w', 'south w' )
1494
1495          CALL check(nf90_put_var( ncid, var%varid, array(:,1,:),              &
1496                                   start=start(1:ndim+1),                      &
1497                                   count=count(1:ndim) ) )
1498         
1499
1500       CASE( 'surface forcing', 'top scalar', 'top w' )
1501
1502          CALL check(nf90_put_var( ncid, var%varid, array(:,:,1),              &
1503                                   start=start(1:ndim+1),                      &
1504                                   count=count(1:ndim) ) )
1505         
[4675]1506       CASE( 'left u', 'right u', 'left v', 'right v' )
[2696]1507
1508          CALL check(nf90_put_var( ncid, var%varid, array(1,:,:),              &
1509                                   start=start(1:ndim+1),                      &
1510                                   count=count(1:ndim) ) )
1511
[4675]1512       CASE( 'north u', 'south u', 'north v', 'south v' )
[2696]1513
1514          CALL check(nf90_put_var( ncid, var%varid, array(:,1,:),              &
1515                                   start=start(1:ndim+1),                      &
1516                                   count=count(1:ndim) ) )
1517
[4675]1518       CASE( 'top u', 'top v' )
[2696]1519
1520          CALL check(nf90_put_var( ncid, var%varid, array(:,:,1),              &
1521                                   start=start(1:ndim+1),                      &
1522                                   count=count(1:ndim) ) )
1523
[4675]1524       CASE( 'time series' )
[2696]1525
1526          CALL check(nf90_put_var( ncid, var%varid, array(1,1,1),              &
1527                                   start=start(1:ndim+1) ) )
1528
[4675]1529       CASE( 'constant scalar profile', 'geostrophic',                         &
1530             'left scalar profile', 'right scalar profile',                    &
1531             'north scalar profile', 'south scalar profile',                   &
1532             'left u profile', 'right u profile',                              &
1533             'north u profile', 'south u profile',                             &
1534             'left v profile', 'right v profile',                              &
1535             'north v profile', 'south v profile',                             &
1536             'top scalar profile', 'top u profile', 'top v profile',           &
1537             'top w profile',                                                  &
1538             'left w profile', 'right w profile',                              &
1539             'north w profile', 'south w profile' )
[2696]1540
1541          CALL check(nf90_put_var( ncid, var%varid, array(1,1,:),              &
1542                                   start=start(1:ndim+1),                      &
1543                                   count=count(1:ndim) ) )
1544
[3456]1545       CASE ( 'internal profile' )
1546
[3866]1547          IF ( cfg%debug )  THEN
[3456]1548             CALL check(nf90_put_var( ncid, var%varid, array(1,1,:),           &
1549                                      start=start(1:ndim+1),                   &
1550                                      count=count(1:ndim) ) )
[3785]1551          ENDIF
[3456]1552
[3182]1553       CASE ( 'large-scale scalar forcing', 'large-scale w forcing' )
1554
1555           message = "Doing nothing in terms of writing large-scale forings."
1556           CALL report('update_output', message)
1557
[2696]1558       CASE DEFAULT
1559
[3866]1560           message = "Variable kind '" // TRIM(var%kind) //                  &
[2696]1561                    "' not recognized."
[3615]1562           CALL inifor_abort('update_output', message)
[2696]1563
[3866]1564    END SELECT
[2696]1565
[3866]1566    CALL check(nf90_close(ncid))
[2696]1567
[3866]1568 END SUBROUTINE update_output
[2696]1569
1570
[3557]1571!------------------------------------------------------------------------------!
1572! Description:
1573! ------------
1574!> Checks the status of a netCDF API call and aborts if an error occured
1575!------------------------------------------------------------------------------!
[3866]1576 SUBROUTINE check(status)
[2696]1577
[3866]1578    INTEGER, INTENT(IN) ::  status
[2696]1579
[3866]1580    IF (status /= nf90_noerr)  THEN
1581       message = "NetCDF API call failed with error: " //                     &
1582                 TRIM( nf90_strerror(status) )
1583       CALL inifor_abort('io.check', message)
1584    ENDIF
[2696]1585
[3866]1586 END SUBROUTINE check
[2696]1587
[4538]1588
1589!------------------------------------------------------------------------------!
1590! Description:
1591! ------------
1592!> Setup the origin of the PALM coordinate system based on what is given in the
1593!> INIFOR namelist file and via an optional static driver.
1594!------------------------------------------------------------------------------!
1595 SUBROUTINE set_palm_origin( cfg, namelist_longitude, namelist_latitude,       &
1596                             origin_lon, origin_lat, z0 )
1597
1598    TYPE(inifor_config), INTENT(IN) ::  cfg
1599    REAL(wp), INTENT(IN)            ::  namelist_longitude, namelist_latitude
1600    REAL(wp), INTENT(OUT)           ::  origin_lon, origin_lat, z0
1601
1602    message = 'Reading PALM-4U origin from'
1603    IF ( TRIM( cfg%static_driver_file ) .NE. '' )  THEN
1604
1605       origin_lon = get_netcdf_attribute( cfg%static_driver_file, TRIM( PIDS_ORIGIN_LON ) )
1606       origin_lat = get_netcdf_attribute( cfg%static_driver_file, TRIM( PIDS_ORIGIN_LAT ) )
1607       z0         = get_netcdf_attribute( cfg%static_driver_file, TRIM( PIDS_ORIGIN_Z ) )
1608
1609       message = TRIM(message) // " static driver file '"                      &
1610                               // TRIM( cfg%static_driver_file ) // "'"
1611
1612
1613    ELSE
1614
1615       origin_lon = namelist_longitude
1616       origin_lat = namelist_latitude
1617
1618       message = TRIM( message ) // " namlist file '"                          &
1619                                 // TRIM( cfg%namelist_file ) // "'"
1620
1621    ENDIF
1622    origin_lon = origin_lon * TO_RADIANS
1623    origin_lat = origin_lat * TO_RADIANS
1624
1625    CALL report('set_palm_origin', message)
1626
1627    IF ( cfg%z0_is_set )  THEN
1628       z0 = cfg%z0
1629       IF ( TRIM( cfg%static_driver_file ) .NE. '' )  THEN
1630          message = 'You specified both --static-driver/-t and --elevation/-z0. ' // &
1631                    'Using the command line value (' // TRIM( real_to_str_f( cfg%z0 ) ) // &
1632                    ') instead of static driver value (' // TRIM( real_to_str_f( z0 ) ) // ').'
1633          CALL warn( 'set_palm_origin', message )
1634       ENDIF
1635    ENDIF
1636
1637 END SUBROUTINE set_palm_origin
1638
[4568]1639
1640!------------------------------------------------------------------------------!
1641! Description:
1642! ------------
1643! This function is meant to check weather a COSMO soil variable has an
1644! additional and redunant surface value at depth = 0.0. For instance operational
1645! DWD COSMO output contains the surface temperature in T_SO as a copy of the
1646! values in the first soil layer.
1647!------------------------------------------------------------------------------!
1648 LOGICAL FUNCTION has_surface_value( soil_var, filename ) 
1649
1650    TYPE(nc_var), INTENT(IN)     ::  soil_var
1651    CHARACTER(LEN=*), INTENT(IN) ::  filename
1652   
1653    REAL(wp), ALLOCATABLE        ::  depths(:)
1654
1655    CALL get_dimension_vector_of_variable(                                     &
1656       soil_var%name,                                                          &
1657       dim_idx = NC_DEPTH_DIM_IDX,                                             &
1658       filename = filename,                                                    &
1659       dim_vector = depths                                                     &
1660    )
1661
1662    has_surface_value = nearly_equal( depths(1), 0.0_wp, 10 * EPSILON(1.0_wp) )
1663
1664 END FUNCTION has_surface_value 
1665
1666
1667!------------------------------------------------------------------------------!
1668! Description:
1669! ------------
1670! This routine reads the dim_idx-th dimension vector of the variable varname
1671! from netCDF file filename. It is used for finding the depth coordinate vector
1672! of COSMO soil variables without knowing its name.
1673!------------------------------------------------------------------------------!
1674 SUBROUTINE get_dimension_vector_of_variable( varname, dim_idx, filename, dim_vector )
1675    CHARACTER(LEN=*), INTENT(IN)              ::  varname, filename
1676    INTEGER, INTENT(IN)                       ::  dim_idx
1677
1678    REAL(wp), INTENT(OUT), ALLOCATABLE ::  dim_vector(:)
1679
1680    INTEGER                            ::  dimids(NF90_MAX_VAR_DIMS)
1681    INTEGER                            ::  varid
1682    CHARACTER(LEN=NF90_MAX_NAME)       ::  dimname
1683
1684    INTEGER ::  ncid
1685
1686    IF ( nf90_open( TRIM( filename ), NF90_NOWRITE, ncid ) .EQ. NF90_NOERR )  THEN
1687
1688!
1689!--    get id of variable varname
1690       CALL check( nf90_inq_varid( ncid, TRIM( varname ), varid ) )
1691       
1692!
1693!--    get dimension ids of variable with varid
1694       CALL check( nf90_inquire_variable( ncid, varid, dimids = dimids ) )
1695
1696!
1697!--    get name of dim_idx-th dimension variable
1698       CALL check( nf90_inquire_dimension( ncid, dimids(dim_idx), name = dimname ) )
1699       CALL check( nf90_close( ncid ) )
1700
1701    ELSE
1702
1703       message = "Failed to open file '" // TRIM(filename) // "'."
1704       CALL inifor_abort('get_netcdf_variable', message)
1705
1706    ENDIF
1707
1708    ! get dimension vector with dimname
1709    CALL get_netcdf_dim_vector( filename, dimname, dim_vector )
1710
1711 END SUBROUTINE get_dimension_vector_of_variable
1712
1713
[4659]1714 LOGICAL FUNCTION netcdf_variable_present_in_file( varname, filename )
1715    CHARACTER(LEN=SNAME), INTENT(IN) ::  varname
1716    CHARACTER(LEN=PATH), INTENT(IN)  ::  filename
1717
1718    INTEGER ::  ncid, varid
1719   
1720    netcdf_variable_present_in_file = (                                        &
1721       nf90_open( TRIM( filename ), NF90_NOWRITE, ncid ) .EQ. NF90_NOERR .AND. &
1722       nf90_inq_varid( ncid, varname, varid ) .EQ. NF90_NOERR .AND.            &
1723       nf90_close( ncid ) .EQ. NF90_NOERR                                      &
1724    )
1725 
1726 END FUNCTION netcdf_variable_present_in_file
1727
1728
[3618]1729 END MODULE inifor_io
[3680]1730#endif
Note: See TracBrowser for help on using the repository browser.