LCOV - code coverage report
Current view: top level - common - mo_common_restart.f90 (source / functions) Hit Total Coverage
Test: mHM coverage Lines: 134 149 89.9 %
Date: 2024-04-15 17:48:09 Functions: 6 6 100.0 %

          Line data    Source code
       1             : !> \file mo_common_restart.f90
       2             : !> \brief   \copybrief mo_common_read_data
       3             : !> \details \copydetails mo_common_read_data
       4             : 
       5             : !> \brief common restart tools
       6             : !> \details Routines to deal with grid infos for restart files
       7             : !> \authors Robert Schweppe
       8             : !> \date Jun 2018
       9             : !> \copyright Copyright 2005-\today, the mHM Developers, Luis Samaniego, Sabine Attinger: All rights reserved.
      10             : !! mHM is released under the LGPLv3+ license \license_note
      11             : !> \ingroup f_common
      12             : module mo_common_restart
      13             : 
      14             :   use mo_kind, only : i4, dp
      15             :   use mo_message, only: message, error_message
      16             : 
      17             :   IMPLICIT NONE
      18             : 
      19             :   PRIVATE
      20             : 
      21             :   PUBLIC :: write_grid_info
      22             :   PUBLIC :: read_grid_info     ! read restart files for configuration from a given path
      23             :   PUBLIC :: read_nLAI_and_check_dims
      24             : 
      25             : 
      26             :   !> \brief check consistency of two given items
      27             :   INTERFACE check_consistency_element
      28             :     MODULE PROCEDURE check_consistency_element_i4, check_consistency_element_dp
      29             :   end interface check_consistency_element
      30             : 
      31             : 
      32             : CONTAINS
      33             : 
      34             : 
      35             :   !> \brief write restart files for each domain
      36             :   !> \details write restart files for each domain. For each domain
      37             :   !! three restart files are written. These are xxx_states.nc,
      38             :   !! xxx_L11_config.nc, and xxx_config.nc (xxx being the three digit
      39             :   !! domain index). If a variable is added here, it should also be added
      40             :   !! in the read restart routines below.
      41             :   !> \changelog
      42             :   !! - Stephan Thober     Aug  2015
      43             :   !!   - moved write of routing states to mRM
      44             :   !! - David Schaefer     Nov  2015
      45             :   !!   - mo_netcdf
      46             :   !! - Stephan Thober     Nov  2016
      47             :   !!   - moved processMatrix to common variables
      48             :   !! - Zink M. Demirel C. Mar 2017
      49             :   !!   - Added Jarvis soil water stress function at SM process(3)
      50             :   !! - Robert Schweppe    Feb 2018
      51             :   !!   - Removed all L0 references
      52             :   !! - Robert Schweppe    Jun 2018
      53             :   !!   - refactoring and reformatting
      54             :   !! - Stephan Thober     May 2019
      55             :   !!   - where statement for gnu73 to translate level0 mask
      56             :   !> \authors Stephan Thober
      57             :   !> \date Jun 2014
      58          92 :   subroutine write_grid_info(grid_in, level_name, nc)
      59             : 
      60             :     use mo_common_constants, only : nodata_dp, nodata_i4
      61             :     use mo_common_types, only: Grid
      62             :     use mo_netcdf, only : NcDataset, NcDimension, NcVariable
      63             : 
      64             :     implicit none
      65             : 
      66             :     !> level to be written
      67             :     type(Grid), intent(in) :: grid_in
      68             : 
      69             :     !> level_id
      70             :     character(*), intent(in) :: level_name
      71             : 
      72             :     !> NcDataset to write information to
      73             :     type(NcDataset), intent(inout) :: nc
      74             : 
      75             :     ! dummy for gnu73
      76          92 :     integer(i4), allocatable :: dummy(:, :)
      77             : 
      78             :     type(NcDimension) :: rows, cols
      79             : 
      80             :     type(NcVariable) :: var
      81             : 
      82             : 
      83          92 :     rows = nc%setDimension("nrows" // trim(level_name), grid_in%nrows)
      84          92 :     cols = nc%setDimension("ncols" // trim(level_name), grid_in%ncols)
      85             : 
      86             :     ! now set everything related to the grid
      87         276 :     var = nc%setVariable("L" // trim(level_name) // "_domain_mask", "i32", (/rows, cols/))
      88          92 :     call var%setFillValue(nodata_i4)
      89             :     ! transform from logical to i32
      90             :     ! ST: where statement is used because gnu73 does not properly translate with merge
      91         368 :     allocate(dummy(size(grid_in%mask, 1), size(grid_in%mask, 2)))
      92     4231207 :     dummy = 0_i4
      93     4231115 :     where(grid_in%mask) dummy = 1_i4
      94          92 :     call var%setData(dummy)
      95          92 :     deallocate(dummy)
      96          92 :     call var%setAttribute("long_name", "Mask at level " // trim(level_name))
      97             : 
      98         276 :     var = nc%setVariable("L" // trim(level_name) // "_domain_lat", "f64", (/rows, cols/))
      99          92 :     call var%setFillValue(nodata_dp)
     100          92 :     call var%setData(grid_in%y)
     101          92 :     call var%setAttribute("long_name", "Latitude at level " // trim(level_name))
     102             : 
     103         276 :     var = nc%setVariable("L" // trim(level_name) // "_domain_lon", "f64", (/rows, cols/))
     104          92 :     call var%setFillValue(nodata_dp)
     105          92 :     call var%setData(grid_in%x)
     106          92 :     call var%setAttribute("long_name", "Longitude at level " // trim(level_name))
     107             : 
     108         276 :     var = nc%setVariable("L" // trim(level_name) // "_domain_cellarea", "f64", (/rows, cols/))
     109          92 :     call var%setFillValue(nodata_dp)
     110     1575971 :     call var%setData(unpack(grid_in%CellArea * 1.0E-6_dp, grid_in%mask, nodata_dp))
     111          92 :     call var%setAttribute("long_name", "Cell area at level " // trim(level_name))
     112             : 
     113          92 :     call nc%setAttribute("xllcorner_L" // trim(level_name), grid_in%xllcorner)
     114          92 :     call nc%setAttribute("yllcorner_L" // trim(level_name), grid_in%yllcorner)
     115          92 :     call nc%setAttribute("cellsize_L" // trim(level_name), grid_in%cellsize)
     116          92 :     call nc%setAttribute("nrows_L" // trim(level_name), grid_in%nrows)
     117          92 :     call nc%setAttribute("ncols_L" // trim(level_name), grid_in%ncols)
     118          92 :     call nc%setAttribute("nCells_L" // trim(level_name), grid_in%nCells)
     119             : 
     120          92 :   end subroutine write_grid_info
     121             : 
     122             : 
     123             :   !> \brief reads configuration apart from Level 11 configuration from a restart directory
     124             :   !> \details read configuration variables from a given restart
     125             :   !> directory and initializes all configuration variables,
     126             :   !> that are initialized in the subroutine initialise,
     127             :   !> contained in module mo_startup.
     128             :   !> \changelog
     129             :   !! - David Schaefer     Nov 2015
     130             :   !!   - mo_netcdf
     131             :   !! - Zink M. Demirel C. Mar 2017
     132             :   !!   - Added Jarvis soil water stress function at SM process(3)
     133             :   !! - Robert Schweppe    Feb 2018
     134             :   !!   - Removed all L0 references
     135             :   !! - Robert Schweppe    Jun 2018
     136             :   !!   - refactoring and reformatting
     137             :   !! - Stephan Thober     May 2019
     138             :   !!   - added allocation check for mask and cellArea because cellArea needs to be read by mRM, but mask is created before by mHM
     139             :   !> \authors Stephan Thober
     140             :   !> \date Apr 2013
     141           4 :   subroutine read_grid_info(InFile, level_name, new_grid)
     142             : 
     143          92 :     use mo_common_types, only: Grid
     144             :     use mo_netcdf, only : NcDataset, NcVariable
     145             : 
     146             :     implicit none
     147             : 
     148             :     !> Input Path including trailing slash
     149             :     character(256), intent(in) :: InFile
     150             : 
     151             :     !> level_name (id)
     152             :     character(*), intent(in) :: level_name
     153             : 
     154             :     !> grid to save information to
     155             :     type(Grid), intent(inout) :: new_grid
     156             : 
     157             :     ! dummy, 2 dimension I4
     158           4 :     integer(i4), dimension(:, :), allocatable :: dummyI2
     159             : 
     160             :     ! dummy, 2 dimension DP
     161           4 :     real(dp), dimension(:, :), allocatable :: dummyD2
     162             : 
     163             :     character(256) :: Fname
     164             : 
     165             :     type(NcDataset) :: nc
     166             : 
     167             :     type(NcVariable) :: var
     168             : 
     169             :     integer(i4) :: k
     170             : 
     171             : 
     172             :     ! read config
     173           4 :     fname = trim(InFile)
     174           4 :     call message('    Reading config from     ', trim(adjustl(Fname)), ' ...')
     175             : 
     176           4 :     nc = NcDataset(fname, "r")
     177             : 
     178             :     ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
     179             :     ! Read L1 variables <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
     180             :     ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
     181             :     ! read the grid properties
     182           4 :     call nc%getAttribute("xllcorner_L" // trim(level_name), new_grid%xllcorner)
     183           4 :     call nc%getAttribute("yllcorner_L" // trim(level_name), new_grid%yllcorner)
     184           4 :     call nc%getAttribute("nrows_L" // trim(level_name), new_grid%nrows)
     185           4 :     call nc%getAttribute("ncols_L" // trim(level_name), new_grid%ncols)
     186           4 :     call nc%getAttribute("cellsize_L" // trim(level_name), new_grid%cellsize)
     187           4 :     call nc%getAttribute("nCells_L" // trim(level_name), new_grid%nCells)
     188             : 
     189          13 :     if (.not. allocated(new_grid%mask)) allocate(new_grid%mask(new_grid%nrows, new_grid%ncols))
     190          13 :     if (.not. allocated(new_grid%x)) allocate(new_grid%x(new_grid%nrows, new_grid%ncols))
     191          13 :     if (.not. allocated(new_grid%y)) allocate(new_grid%y(new_grid%nrows, new_grid%ncols))
     192             :     ! read L1 mask
     193           4 :     var = nc%getVariable("L" // trim(level_name) // "_domain_mask")
     194             :     ! read integer
     195           4 :     call var%getData(dummyI2)
     196             :     ! transform to logical
     197      249830 :     new_grid%mask = (dummyI2 .eq. 1_i4)
     198             : 
     199           4 :     var = nc%getVariable("L" // trim(level_name) // "_domain_lat")
     200           4 :     call var%getData(new_grid%y)
     201             : 
     202           4 :     var = nc%getVariable("L" // trim(level_name) // "_domain_lon")
     203           4 :     call var%getData(new_grid%x)
     204             : 
     205           4 :     var = nc%getVariable("L" // trim(level_name) // "_domain_cellarea")
     206           4 :     call var%getData(dummyD2)
     207      171594 :     if (.not. allocated(new_grid%CellArea)) new_grid%CellArea = pack(dummyD2 / 1.0E-6_dp, new_grid%mask)
     208             :     ! new_grid%CellArea = pack(dummyD2 / 1.0E-6_dp, new_grid%mask)
     209             : 
     210           4 :     call nc%close()
     211             : 
     212      279482 :     new_grid%Id = (/ (k, k = 1, new_grid%nCells) /)
     213             : 
     214           4 :   end subroutine read_grid_info
     215             : 
     216             : 
     217             :   !> \brief read nubmer of LAI time steps and check dimension configurations read from restart file
     218             :   !> \author Robert Schweppe
     219             :   !> \date Aug 2019
     220             :   !> \author Sebastian Mueller
     221             :   !> \date Feb 2023
     222           1 :   subroutine read_nLAI_and_check_dims(iDomain, InFile)
     223             : 
     224           4 :     use mo_mpr_global_variables, only: nLAI, LAIBoundaries ! may read from restart
     225             :     use mo_netcdf, only : NcDataset, NcVariable, NcDimension
     226             :     use mo_common_constants, only : soilHorizonsVarName, landCoverPeriodsVarName, LAIVarName
     227             :     use mo_common_mHM_mRM_variables, only: read_old_style_restart_bounds
     228             : 
     229             :     implicit none
     230             : 
     231             :     !> domain counter (not ID)
     232             :     integer(i4), intent(in) :: iDomain
     233             :     !> Input Path including trailing slash
     234             :     character(256), intent(in) :: InFile
     235             : 
     236             :     character(256) :: fname
     237             :     type(NcDataset) :: nc
     238             :     type(NcVariable) :: var
     239             :     type(NcDimension) :: nc_dim
     240             : 
     241             :     integer(i4) :: nSoilHorizons_temp, nLAIs_temp, nLandCoverPeriods_temp
     242           1 :     real(dp), dimension(:), allocatable :: landCoverPeriodBoundaries_temp, soilHorizonBoundaries_temp, &
     243           1 :             LAIBoundaries_temp
     244             : 
     245             :     ! dummy, 2 dimension
     246           1 :     real(dp), dimension(:, :), allocatable :: dummyD2, dummyD2_tmp
     247             : 
     248             :     integer(i4) :: ii
     249             : 
     250             :     ! read config
     251           1 :     fname = trim(InFile)
     252           1 :     call message('    Reading and checking LAI, land-cover and soil-horizons from     ', trim(adjustl(Fname)), ' ...')
     253             : 
     254           1 :     nc = NcDataset(fname, "r")
     255             : 
     256             :     ! get the dimensions
     257           1 :     var = nc%getVariable(trim(soilHorizonsVarName)//'_bnds')
     258           1 :     call var%getData(dummyD2_tmp)
     259             :     if (allocated(dummyD2)) deallocate(dummyD2)
     260           1 :     if ( read_old_style_restart_bounds ) then
     261           0 :       allocate(dummyD2(size(dummyD2_tmp,2), size(dummyD2_tmp,1)))
     262           0 :       dummyD2 = transpose(dummyD2_tmp)
     263             :     else
     264           4 :       allocate(dummyD2(size(dummyD2_tmp,1), size(dummyD2_tmp,2)))
     265           8 :       dummyD2 = dummyD2_tmp
     266             :     end if
     267           1 :     deallocate(dummyD2_tmp)
     268           1 :     nSoilHorizons_temp = size(dummyD2, 2)
     269           3 :     allocate(soilHorizonBoundaries_temp(nSoilHorizons_temp+1))
     270           3 :     soilHorizonBoundaries_temp(1:nSoilHorizons_temp) = dummyD2(1, :)
     271           1 :     soilHorizonBoundaries_temp(nSoilHorizons_temp+1) = dummyD2(2, nSoilHorizons_temp)
     272             : 
     273             :     ! get the landcover dimension
     274           1 :     var = nc%getVariable(trim(landCoverPeriodsVarName)//'_bnds')
     275           1 :     call var%getData(dummyD2_tmp)
     276           1 :     if (allocated(dummyD2)) deallocate(dummyD2)
     277           1 :     if ( read_old_style_restart_bounds ) then
     278           0 :       allocate(dummyD2(size(dummyD2_tmp,2), size(dummyD2_tmp,1)))
     279           0 :       dummyD2 = transpose(dummyD2_tmp)
     280             :     else
     281           4 :       allocate(dummyD2(size(dummyD2_tmp,1), size(dummyD2_tmp,2)))
     282           8 :       dummyD2 = dummyD2_tmp
     283             :     end if
     284           1 :     deallocate(dummyD2_tmp)
     285           1 :     nLandCoverPeriods_temp = size(dummyD2, 2)
     286           3 :     allocate(landCoverPeriodBoundaries_temp(nLandCoverPeriods_temp+1))
     287           3 :     landCoverPeriodBoundaries_temp(1:nLandCoverPeriods_temp) = dummyD2(1, :)
     288           1 :     landCoverPeriodBoundaries_temp(nLandCoverPeriods_temp+1) = dummyD2(2, nLandCoverPeriods_temp)
     289             : 
     290             :     ! get the LAI dimension
     291           1 :     if (nc%hasVariable(trim(LAIVarName)//'_bnds')) then
     292           1 :       var = nc%getVariable(trim(LAIVarName)//'_bnds')
     293           1 :       call var%getData(dummyD2_tmp)
     294           1 :       if (allocated(dummyD2)) deallocate(dummyD2)
     295           1 :       if ( read_old_style_restart_bounds ) then
     296           0 :         allocate(dummyD2(size(dummyD2_tmp,2), size(dummyD2_tmp,1)))
     297           0 :         dummyD2 = transpose(dummyD2_tmp)
     298             :       else
     299           4 :         allocate(dummyD2(size(dummyD2_tmp,1), size(dummyD2_tmp,2)))
     300          38 :         dummyD2 = dummyD2_tmp
     301             :       end if
     302           1 :       deallocate(dummyD2_tmp)
     303           1 :       nLAIs_temp = size(dummyD2, 2)
     304           3 :       allocate(LAIBoundaries_temp(nLAIs_temp+1))
     305          13 :       LAIBoundaries_temp(1:nLAIs_temp) = dummyD2(1, :)
     306           1 :       LAIBoundaries_temp(nLAIs_temp+1) = dummyD2(2, nLAIs_temp)
     307           0 :     else if (nc%hasDimension('L1_LAITimesteps')) then
     308           0 :       nc_dim = nc%getDimension('L1_LAITimesteps')
     309           0 :       nLAIs_temp = nc_dim%getLength()
     310           0 :       allocate(LAIBoundaries_temp(nLAIs_temp+1))
     311           0 :       LAIBoundaries_temp = [(ii, ii=1, nLAIs_temp+1)]
     312             :     else
     313           0 :       call error_message('***ERROR: no LAI information in restart file for reading')
     314             :     end if
     315             : 
     316             :     ! check LAI for consistency on all domains (-1 indicates first reading)
     317           1 :     if (nLAI == -1_i4) then
     318           1 :       nLAI = nLAIs_temp
     319           3 :       allocate(LAIBoundaries(nLAI + 1_i4))
     320          15 :       LAIBoundaries = LAIBoundaries_temp
     321             :     end if
     322             : 
     323             :     call check_dimension_consistency(iDomain, nSoilHorizons_temp, soilHorizonBoundaries_temp, &
     324           1 :           nLAIs_temp, LAIBoundaries_temp, nLandCoverPeriods_temp, landCoverPeriodBoundaries_temp)
     325             : 
     326           1 :     call nc%close()
     327             : 
     328           1 :   end subroutine read_nLAI_and_check_dims
     329             : 
     330             :   !> \brief checks dimension configurations read from restart file
     331             :   !> \authors Robert Schweppe
     332             :   !> \date Aug 2019
     333           1 :   subroutine check_dimension_consistency(iDomain, nSoilHorizons_temp, soilHorizonBoundaries_temp, &
     334           1 :       nLAIs_temp, LAIBoundaries_temp, nLandCoverPeriods_temp, landCoverPeriodBoundaries_temp)
     335             : 
     336           1 :     use mo_mpr_global_variables, only: nSoilHorizons_mHM, HorizonDepth_mHM, nLAI, LAIBoundaries ! may read from restart
     337             :     use mo_common_variables, only: nLCoverScene, LC_year_start, LC_year_end ! read from nml
     338             :     use mo_string_utils, only: compress, num2str
     339             : 
     340             :     integer(i4), intent(in) :: iDomain
     341             : 
     342             :     integer(i4), intent(in) :: nSoilHorizons_temp, nLAIs_temp, nLandCoverPeriods_temp
     343             :     real(dp), dimension(:), intent(inout) :: landCoverPeriodBoundaries_temp, soilHorizonBoundaries_temp, &
     344             :           LAIBoundaries_temp
     345             :     character(256) :: errorString
     346             : 
     347             :     integer(i4) :: k
     348             : 
     349             :     ! compare local to global
     350           1 :     call check_consistency_element(nLCoverScene, nLandCoverPeriods_temp, 'number of land cover periods', iDomain)
     351           1 :     call check_consistency_element(nSoilHorizons_mHM, nSoilHorizons_temp, 'number of soil horizons', iDomain)
     352           1 :     call check_consistency_element(nLAI, nLAIs_temp, 'number of LAI timesteps', iDomain)
     353             : 
     354             :     ! now check the boundaries
     355           3 :     do k=1, nLCoverScene
     356           2 :       errorString = compress(trim(num2str(k)))//'th land cover boundary'
     357           3 :       call check_consistency_element(real(LC_year_start(k), dp), landCoverPeriodBoundaries_temp(k), errorString, iDomain)
     358             :     end do
     359           1 :     errorString = 'last land cover boundary (with 1 year added due to real/int conversion) '
     360           0 :     call check_consistency_element(real(LC_year_end(nLCoverScene) + 1_i4, dp), &
     361           1 :           landCoverPeriodBoundaries_temp(nLCoverScene+1), errorString, iDomain)
     362             : 
     363             :     ! last soil horizon is spatially variable, so this is not checked yet
     364             :     ! first soil horizon 0 and not contained in HorizonDepth_mHM, so skip that, too
     365           2 :     do k=2, nSoilHorizons_mHM
     366           1 :       errorString = compress(trim(num2str(k)))//'th soil horizon boundary'
     367           2 :       call check_consistency_element(HorizonDepth_mHM(k-1), soilHorizonBoundaries_temp(k), errorString, iDomain)
     368             :     end do
     369             : 
     370          14 :     do k=1, nLAI+1
     371          13 :       errorString = compress(trim(num2str(k)))//'th LAI period boundary'
     372          14 :       call check_consistency_element(LAIBoundaries(k), LAIBoundaries_temp(k), errorString, iDomain)
     373             :     end do
     374             : 
     375           1 :   end subroutine check_dimension_consistency
     376             : 
     377             :   !> \copydoc check_consistency_element
     378          17 :   subroutine check_consistency_element_dp(item1, item2, name, iDomain)
     379           1 :     use mo_utils, only: ne
     380             :     use mo_string_utils, only: compress, num2str
     381             : 
     382             :     real(dp), intent(in) :: item1, item2
     383             :     character(*), intent(in) :: name
     384             :     integer(i4), intent(in) :: iDomain
     385             : 
     386          17 :     if (ne(item1, item2)) then
     387             :     call error_message( &
     388             :       'The ', trim(name),&
     389             :       ' as set in the configuration file (', &
     390             :       compress(trim(num2str(item1))), &
     391             :       ') does not conform with domain ', &
     392           0 :       compress(trim(num2str(iDomain))), ' (', compress(trim(num2str(item2))), ').')
     393             :     end if
     394          17 :   end subroutine check_consistency_element_dp
     395             : 
     396             :   !> \copydoc check_consistency_element
     397           3 :   subroutine check_consistency_element_i4(item1, item2, name, iDomain)
     398          17 :     use mo_string_utils, only: compress, num2str
     399             : 
     400             :     integer(i4), intent(in) :: item1, item2
     401             :     character(*), intent(in) :: name
     402             :     integer(i4), intent(in) :: iDomain
     403             : 
     404           3 :     if (item1 /= item2) then
     405             :     call error_message( &
     406             :       'The ', trim(name),&
     407             :       ' as set in the configuration file (', &
     408             :       compress(trim(num2str(item1))), &
     409             :       ') does not conform with domain ', &
     410           0 :       compress(trim(num2str(iDomain))), ' (', compress(trim(num2str(item2))), ').')
     411             :     end if
     412           3 :   end subroutine check_consistency_element_i4
     413             : 
     414             : end module mo_common_restart

Generated by: LCOV version 1.16