5.13.3-dev0
mHM
The mesoscale Hydrological Model
Loading...
Searching...
No Matches
mo_soil_database.f90
Go to the documentation of this file.
1!> \file mo_soil_database.f90
2!> \brief \copybrief mo_soil_database
3!> \details \copydetails mo_soil_database
4
5!> \brief Generating soil database from input file.
6!> \details This module provides the routines for generating the soil database for mHM from an ASCII input file.
7!! One routine \e read_soil_LUT reads a soil LookUpTable, performs some consistency checks and returns an initial
8!! soil database.
9!! The second routine \e generate_soil_database calculates based on the initial one the proper soil database.
10!> \authors Juliane Mai
11!> \date Dec 2012
12!> \copyright Copyright 2005-\today, the mHM Developers, Luis Samaniego, Sabine Attinger: All rights reserved.
13!! mHM is released under the LGPLv3+ license \license_note
14!> \ingroup f_mpr
16
17 ! This module to provide a soil database for mHM.
18
19 ! Written Juliane Mai, Dec 2012
20
21 use mo_kind, only : i4, dp
22 use mo_message, only: message, error_message
23 use mo_string_utils, only : num2str
24 use mo_os, only : check_path_isfile
25
26 IMPLICIT NONE
27
28 PRIVATE
29
30 PUBLIC :: read_soil_lut ! Reads the soil LUT file
31 PUBLIC :: generate_soil_database ! Generates the soil database
32
33 ! ------------------------------------------------------------------
34
35CONTAINS
36
37 ! ------------------------------------------------------------------
38
39 ! NAME
40 ! read_soil_LUT
41
42 ! PURPOSE
43 !> \brief Reads the soil LUT file.
44
45 !> \details Reads the soil LookUpTable file and checks for consistency.
46
47 ! INTENT(IN)
48 !> \param[in] "character(len = *) :: filename" filename of the soil LUT
49
50 ! HISTORY
51 !> \authors Juliane Mai
52
53 !> \date Dec 2012
54
55 ! Modifications:
56 ! Luis Samaniego Nov 2013 - transform relation op. == -> .eq. etc
57 ! Rohini Kumar Mar 2016 - new variables for handling different soil databases
58 ! Robert Schweppe Jun 2018 - refactoring and reformatting
59
60 subroutine read_soil_lut(filename)
61
64 use mo_mpr_file, only : usoil_database
67
68 implicit none
69
70 ! filename of the soil LUT
71 character(len = *), intent(in) :: filename
72
73 character(len = 10) :: dummy
74
75 integer(i4) :: ios
76
77 integer(i4) :: ii, jj, kk
78
79 integer(i4) :: nr, nh, max_i
80
81 real(dp) :: up, down
82
83 real(dp) :: cly, snd, bd
84
85 real(dp) :: dmin
86
87 integer(i4) :: maxnumberhorizons
88
89 integer(i4) :: minnumbertillhorizons, maxnumbertillhorizons
90
91
92 SELECT CASE (iflag_soildb)
93 ! classical mHM soil database
94 CASE(0)
95 ios = 0_i4
96 !checking whether the file exists
97 call check_path_isfile(path = filename, raise=.true.)
98 open(usoil_database, file = filename, status = 'old', iostat = ios)
99 read(usoil_database, *) dummy, nsoiltypes
100 dummy = dummy // '' ! only to avoid warning
101
102 ! allocate space
103 allocate(soildb%Id(nsoiltypes))
104 allocate(soildb%nHorizons(nsoiltypes))
105 allocate(soildb%nTillHorizons(nsoiltypes))
106 allocate(soildb%RZdepth(nsoiltypes))
107 ! initialize
108 soildb%Id(:) = nodata_i4
109 soildb%nHorizons(:) = nodata_i4
110 soildb%nTillHorizons(:) = nodata_i4
111 soildb%RZdepth(:) = nodata_dp
112
113 ! check if max soil_type id is equal to nSoilTypes
114 max_i = 0_i4
115 ! initalise total rows to read
116 nr = 0_i4
117 read(usoil_database, *) dummy
118 do while (.NOT. (ios .ne. 0))
119 read(usoil_database, *, iostat = ios) ii, jj, up, down, cly, snd, bd
120
121 ! Checks
122 if(up .ge. down) then
123 call error_message('read_soil_LUT: ERROR occurred: Mixed horizon depths in soil type', &
124 num2str(ii, '(I3)'), ' and horizon no.', num2str(jj, '(I3)'))
125 end if
126 if(cly .lt. 0.0_dp .OR. cly .gt. 100.0_dp .OR. &
127 snd .lt. 0.0_dp .OR. snd .gt. 100.0_dp .OR. &
128 bd .lt. 0.0_dp .OR. bd .gt. 5.0_dp) then
129 call error_message('read_soil_LUT: ERROR occurred: Inappropriate soil properties in soil type', &
130 num2str(ii, '(I3)'), ' and horizon no.', num2str(jj, '(I3)'))
131 end if
132
133 ! initalise soil id
134 if ( ii > size(soildb%Id) ) call error_message( &
135 "ERROR: nSoil_Types (", num2str(size(soildb%Id)), &
136 ") in soil_classdefinition.txt seems to be to low! Tried to read: ", num2str(ii) &
137 )
138 soildb%Id(ii) = ii
139 soildb%nHorizons(ii) = jj
140 if( anint(down, dp) .gt. soildb%RZdepth(ii) ) soildb%RZdepth(ii) = anint(down, dp)
141
142 nr = nr + 1_i4
143 max_i = max(max_i, ii)
144
145 end do
146
147 if ( max_i < nsoiltypes ) call error_message( &
148 "ERROR: nSoil_Types (", num2str(size(soildb%Id)), &
149 ") in soil_classdefinition.txt seems to be to high! Highest read value: ", num2str(max_i) &
150 )
151
152 ! initalise minimum root zone depth among all soil types
153 dmin = minval(soildb%RZdepth(:), soildb%RZdepth(:) .gt. 0.0_dp)
154
155 ! check the tillage depth...(if possible adjust it..)
156 if(tillagedepth .gt. dmin) then
157 call error_message('read_soil_LUT: ERROR occurred: ', raise=.false.)
158 call error_message(' Tillage depth is greater than overall minimum total soil depth ', raise=.false.)
159 call error_message(' So tillage depth should be at least', num2str(dmin, '(F7.2)'), raise=.false.)
160 call error_message(' Please adjust!')
161 end if
162
163 ! insert a new tillage soil layer, only in those soil types, in which it is not present
164 rewind(usoil_database)
165 read(usoil_database, *) dummy
166 read(usoil_database, *) dummy
167
168 ! Last row is read twice so, read only upto (nR - 1)
169 do ii = 1, nr - 1
170 read(usoil_database, *) jj, nh, up, down, cly, snd, bd
171 if(tillagedepth .gt. anint(up, dp) .and. tillagedepth .lt. anint(down, dp)) then
172 soildb%nHorizons(jj) = soildb%nHorizons(jj) + 1_i4
173 end if
174
175 ! identify upto which soil horizon does the tillage depth goes in
176 if(tillagedepth .gt. anint(up, dp) .and. tillagedepth .le. anint(down, dp)) then
177 soildb%nTillHorizons(jj) = nh
178 end if
179 end do
180
181 maxnumberhorizons = maxval(soildb%nHorizons(:))
182
183 ! the variables of soilType are all allocated with maximal number of Horizons although not for each of the
184 ! nSoilTypes the array will be used
185 ! loops for array(i,j) should be: j=1, nHorizons(i), otherwise a nodata_dp will appear
186 allocate(soildb%UD(nsoiltypes, maxnumberhorizons))
187 allocate(soildb%LD(nsoiltypes, maxnumberhorizons))
188 allocate(soildb%depth(nsoiltypes, maxnumberhorizons))
189 allocate(soildb%clay(nsoiltypes, maxnumberhorizons))
190 allocate(soildb%sand(nsoiltypes, maxnumberhorizons))
191 allocate(soildb%dbM(nsoiltypes, maxnumberhorizons))
192
193 soildb%UD(:, :) = nodata_dp
194 soildb%LD(:, :) = nodata_dp
195 soildb%depth(:, :) = nodata_dp
196 soildb%clay(:, :) = nodata_dp
197 soildb%sand(:, :) = nodata_dp
198 soildb%dbM(:, :) = nodata_dp
199
200 ! allocate space for other derived soil hydraulic properties ...
201 minnumbertillhorizons = minval(soildb%nTillHorizons(:))
202 maxnumbertillhorizons = maxval(soildb%nTillHorizons(:))
203
204 allocate(soildb%thetaS_till(nsoiltypes, 1 : maxnumbertillhorizons, nlcover_class))
205 allocate(soildb%thetaS(nsoiltypes, minnumbertillhorizons + 1 : maxnumberhorizons))
206
207 allocate(soildb%thetaFC_till(nsoiltypes, 1 : maxnumbertillhorizons, nlcover_class))
208 allocate(soildb%thetaFC(nsoiltypes, minnumbertillhorizons + 1 : maxnumberhorizons))
209
210 allocate(soildb%thetaPW_till(nsoiltypes, 1 : maxnumbertillhorizons, nlcover_class))
211 allocate(soildb%thetaPW(nsoiltypes, minnumbertillhorizons + 1 : maxnumberhorizons))
212
213 allocate(soildb%Db(nsoiltypes, 1 : maxnumbertillhorizons, nlcover_class))
214
215 allocate(soildb%Ks(nsoiltypes, 1 : maxnumberhorizons, nlcover_class))
216
217 ! Initialize with default nodata_dp
218 soildb%thetaS_till(:, :, :) = nodata_dp
219 soildb%thetaS(:, :) = nodata_dp
220
221 soildb%thetaFC_till(:, :, :) = nodata_dp
222 soildb%thetaFC(:, :) = nodata_dp
223
224 soildb%thetaPW_till(:, :, :) = nodata_dp
225 soildb%thetaPW(:, :) = nodata_dp
226
227 soildb%Db(:, :, :) = nodata_dp
228 soildb%Ks(:, :, :) = nodata_dp
229
230 ! Read again soil properties from the data base
231 rewind(usoil_database)
232 read(usoil_database, *) dummy
233 read(usoil_database, *) dummy
234
235 ! Last row is read twice so, read only upto (nR - 1)
236 kk = 0_i4
237 do ii = 1, nr - 1_i4
238 read(usoil_database, *) jj, nh, up, down, cly, snd, bd
239
240 ! to avoid numerical errors in PTF
241 if(cly .lt. 1.0_dp) cly = 1.0_dp
242 if(snd .lt. 1.0_dp) snd = 1.0_dp
243
244 ! Physical consistency
245 if((cly + snd) .gt. 100.0_dp) then
246 cly = cly / (cly + snd)
247 snd = snd / (cly + snd)
248 end if
249
250 ! check for an extra tillage horizon (if not exists create a layer)
251 if(tillagedepth .gt. anint(up, dp) .and. tillagedepth .lt. anint(down, dp)) then
252
253 soildb%UD(jj, nh) = anint(up, dp)
254 soildb%LD(jj, nh) = tillagedepth
255 soildb%depth(jj, nh) = soildb%LD(jj, nh) - soildb%UD(jj, nh)
256 soildb%clay(jj, nh) = cly
257 soildb%sand(jj, nh) = snd
258 soildb%dbM(jj, nh) = bd
259
260 ! initalise the upper depth to new value... and the increment counter
261 up = tillagedepth
262 kk = 1_i4
263 end if
264
265 ! increment nH by one once it encounter the upper loop..
266 if(kk .eq. 1_i4) nh = nh + 1_i4
267
268 soildb%UD(jj, nh) = anint(up, dp)
269 soildb%LD(jj, nh) = anint(down, dp)
270 soildb%depth(jj, nh) = soildb%LD(jj, nh) - soildb%UD(jj, nh)
271 soildb%clay(jj, nh) = cly
272 soildb%sand(jj, nh) = snd
273 soildb%dbM(jj, nh) = bd
274
275 ! check for number of soil horizons...
276 if(nh .gt. soildb%nHorizons(jj)) then
277 call error_message('read_soil_LUT: ERROR occurred: ', raise=.false.)
278 call error_message(' There is something wrong in allocating horizons in soil data base.', raise=.false.)
279 call error_message(' Please check in code !')
280 end if
281
282 ! initalise the increment counter to zero
283 if(nh .eq. soildb%nHorizons(jj)) kk = 0_i4
284
285 end do
286 close(usoil_database)
287
288 ! soil database for the horizon specific case
289 CASE(1)
290
291 allocate(soildb%RZdepth(1))
292 allocate(soildb%UD(1, 1))
293 allocate(soildb%LD(1, 1))
294 allocate(soildb%depth(1, 1))
295 allocate(soildb%thetaS_till(1, 1, 1))
296 allocate(soildb%thetaS(1, 1))
297 allocate(soildb%thetaFC_till(1, 1, 1))
298 allocate(soildb%thetaFC(1, 1))
299 allocate(soildb%thetaPW_till(1, 1, 1))
300 allocate(soildb%thetaPW(1, 1))
301 allocate(soildb%Db(1, 1, 1))
302 allocate(soildb%Ks(1, 1, 1))
303
304
305
306 !checking whether the file exists
307 call check_path_isfile(path = filename, raise=.true.)
308 open(usoil_database, file = filename, status = 'old', action = 'read')
309 read(usoil_database, *) dummy, nsoiltypes
310 dummy = dummy // '' ! only to avoid warning
311 allocate(soildb%Id (nsoiltypes))
312 allocate(soildb%clay(nsoiltypes, 1))
313 allocate(soildb%sand(nsoiltypes, 1))
314 allocate(soildb%dbM (nsoiltypes, 1))
315 soildb%clay(:, :) = nodata_dp
316 soildb%sand(:, :) = nodata_dp
317 soildb%dbM (:, :) = nodata_dp
318 read(usoil_database, *) dummy
319 do kk = 1, nsoiltypes
320 read(usoil_database, *, iostat=ios) jj, cly, snd, bd
321 if ( ios /= 0 ) call error_message( &
322 "ERROR: nSoil_Types (", num2str(nsoiltypes), ") in soil_classdefinition_iFlag_soilDB_1.txt ", &
323 "seems to be higher than available soil types!" &
324 )
325 ! to avoid numerical errors in PTF
326 if(cly .lt. 1.0_dp) cly = 1.0_dp
327 if(snd .lt. 1.0_dp) snd = 1.0_dp
328 soildb%Id(kk) = jj
329 soildb%clay(kk, 1) = cly
330 soildb%sand(kk, 1) = snd
331 soildb%dbM (kk, 1) = bd
332 end do
333 close(usoil_database)
334
335 ! assign up to which horizon layer a soil is treated as tillage layer
336 ! since our horizon information is well defined for modeling too
337 ! this information is uniform across all soils/modeling cells
338 ! for compatibility with iFlag_soilDB == 0, assign
339 ! both nHorizons & tillage horizons
340 allocate(soildb%nHorizons(1))
341 allocate(soildb%nTillHorizons(1))
342 soildb%nHorizons(:) = nsoilhorizons_mhm
343 soildb%nTillHorizons(:) = -9
344 do kk = 1, nsoilhorizons_mhm
345 if(abs(horizondepth_mhm(kk) - tillagedepth) .lt. eps_dp) then
346 soildb%nTillHorizons(1) = kk
347 end if
348 end do
349
350 ! check
351 if(soildb%nTillHorizons(1) .eq. -9) then
352 ! rarely could happen *** since this is checked in reading of horizons depths only
353 ! but is checked here for double confirmation
354 call error_message('***ERROR: specification of tillage depths is not confirming', raise=.false.)
355 call error_message(' with given depths of soil horizons to be modeled.')
356 else
357 call message()
358 call message('Tillage layers: the tillage horizons are modelled ')
359 call message(' upto mHM layers: ', trim(num2str(soildb%nTillHorizons(1))))
360 end if
361
362 CASE DEFAULT
363 call error_message('***ERROR: iFlag_soilDB option given does not exist. Only 0 and 1 is taken at the moment.')
364
365 END SELECT
366
367 end subroutine read_soil_lut
368
369 ! ------------------------------------------------------------------
370
371 ! NAME
372 ! generate_soil_database
373
374 ! PURPOSE
375 !> \brief Generates soil database.
376
377 !> \details Calculates the proper soil database using the initialized soil database from read_soil_LUT.
378
379 ! HISTORY
380 !> \authors Juliane Mai
381
382 !> \date Dec 2012
383
384 ! Modifications:
385 ! Robert Schweppe Jun 2018 - refactoring and reformatting
386
388
391
392 implicit none
393
394 integer(i4) :: ii, jj, kk
395
396 real(dp) :: dmin
397
398 real(dp) :: dpth_f, dpth_t
399
400 integer(i4) :: layer_f, layer_t
401
402 real(dp), parameter :: small = 0.000001_dp
403
404 ! [mm] soil depth accuracy
405 real(dp), parameter :: soil_daccuracy = 0.5_dp
406
407
408 SELECT CASE (iflag_soildb)
409 ! classical mHM soil database
410 CASE(0)
411 ! initalise minimum root zone depth among all soil types
412 dmin = minval(soildb%RZdepth(:), soildb%RZdepth(:) > 0.0_dp)
413
414 ! check
415 if (horizondepth_mhm(nsoilhorizons_mhm - 1) .ge. dmin) then
416 call error_message('generate_soil_database: ERROR occurred: ', raise=.false.)
417 call error_message(' The depth of soil Horizons provided for modelling is not appropriate', raise=.false.)
418 call error_message(' The global minimum of total soil horizon depth among all soil type is ', num2str(dmin, '(F7.2)'), raise=.false.)
419 call error_message(' Adjust your modeling soil horizon depth in this range', raise=.false.)
420 call error_message(' OR Increase the soil depth in data base for only those soil types', raise=.false.)
421 call error_message(' whose total depth is smaller than your given modeling depth.')
422 end if
423
424 ! allocate and initalise depth weight
425 allocate(soildb%Wd(nsoiltypes, nsoilhorizons_mhm, maxval(soildb%nHorizons(:))))
426 soildb%Wd(:, :, :) = 0.0_dp
427
428 ! Process further to estimate weight of each horizons
429 ! weightage according to soil depths
430 do ii = 1, nsoiltypes
431 soildb%Wd(ii, :, soildb%nHorizons(ii) + 1_i4 : maxval(soildb%nHorizons(:))) = nodata_dp
432
433 ! initalise last horizon depth to model w.r.t to surface
434 ! NOTE:: it is different for each soil
436
437 ! Estimate soil properties for each modeling layers
438 do jj = 1, nsoilhorizons_mhm
439
440 ! modeling depth ( **from --> to ** )
441 ! take into account the depth accuracy [0.5mm, defined in module..]
442 dpth_f = 0.0_dp
443 if(jj .ne. 1_i4) dpth_f = horizondepth_mhm(jj - 1)
444 dpth_t = horizondepth_mhm(jj) - soil_daccuracy
445
446 ! identify to which layer of batabase this mHM horizon lies
447 layer_f = nodata_i4
448 layer_t = nodata_i4
449 do kk = 1, soildb%nHorizons(ii)
450 if(dpth_f .ge. soildb%UD(ii, kk) .and. dpth_f .le. (soildb%LD(ii, kk) - soil_daccuracy)) layer_f = kk
451 if(dpth_t .ge. soildb%UD(ii, kk) .and. dpth_t .le. (soildb%LD(ii, kk) - soil_daccuracy)) layer_t = kk
452 end do
453
454 ! Check
455 if(layer_f .le. 0_i4 .or. layer_t .le. 0_i4) then
456 call error_message('generate_soil_database: ERROR occurred: ', raise=.false.)
457 call error_message(' Horizon depths to model do not lie in database for soil type', num2str(ii, '(I3)'), raise=.false.)
458 call error_message(' Please check!')
459 end if
460 if(layer_f .gt. layer_t) then
461 call error_message('generate_soil_database: ERROR occurred: ', raise=.false.)
462 call error_message(' Something is wrong in assignment of modeling soil horizons or', raise=.false.)
463 call error_message(' database of soil type ', num2str(ii, '(I3)'), raise=.false.)
464 call error_message(' Please check!')
465 end if
466
467 ! iF modeling depth of a given horizon falls in a same soil layer
468 if(layer_f .eq. layer_t) then
469 soildb%Wd(ii, jj, layer_f) = 1.0_dp
470
471 ! else estimate depth weightage...
472 else
473
474 ! for starting layer...
475 soildb%Wd(ii, jj, layer_f) = soildb%LD(ii, layer_f) - dpth_f
476
477 ! for ending layer
478 soildb%Wd(ii, jj, layer_t) = (dpth_t + soil_daccuracy) - soildb%UD(ii, layer_t)
479
480 ! other intermediate layers weight, if exit
481 if(layer_t - layer_f .gt. 1_i4) then
482 do kk = layer_f + 1, layer_t - 1
483 soildb%Wd(ii, jj, kk) = soildb%LD(ii, kk) - soildb%UD(ii, kk)
484 end do
485 end if
486
487 ! Estimate depth weightage
488 if(jj .ne. 1_i4) then
489 soildb%Wd(ii, jj, 1 : soildb%nHorizons(ii)) = soildb%Wd(ii, jj, 1 : soildb%nHorizons(ii)) / &
490 (horizondepth_mhm(jj) - horizondepth_mhm(jj - 1_i4))
491 else
492 soildb%Wd(ii, jj, 1 : soildb%nHorizons(ii)) = soildb%Wd(ii, jj, 1 : soildb%nHorizons(ii)) / &
494 end if
495
496 ! Check (small margin for numerical errors)
497 if(sum(soildb%Wd(ii, jj, :), soildb%Wd(ii, jj, :) .gt. 0.0_dp) .le. 1.0_dp - small .or. &
498 sum(soildb%Wd(ii, jj, :), soildb%Wd(ii, jj, :) .gt. 0.0_dp) .ge. 1.0_dp + small) then
499 call error_message('generate_soil_database: ERROR occurred: ', raise=.false.)
500 call error_message(' Weight assigned for each soil horizons are not correct.', raise=.false.)
501 call error_message(' Please check!')
502 end if
503
504 end if
505
506 end do
507 end do
508 ! soil database for the horizon specific case
509 CASE(1)
510 ! right now nothing is done here
511 ! *** reserved for future changes
512 allocate(soildb%Wd(1,1,1))
513
514 CASE DEFAULT
515 call error_message('***ERROR: iFlag_soilDB option given does not exist. Only 0 and 1 is taken at the moment.')
516 END SELECT
517
518 end subroutine generate_soil_database
519
520END MODULE mo_soil_database
Provides constants commonly used by mHM, mRM and MPR.
real(dp), parameter, public eps_dp
epsilon(1.0) in double precision
real(dp), parameter, public nodata_dp
integer(i4), parameter, public nodata_i4
Provides MPR specific constants.
integer(i4), parameter, public nlcover_class
Provides file names and units for mRM.
integer, parameter usoil_database
Unit for soil data base.
Global variables for mpr only.
integer(i4), public nsoilhorizons_mhm
real(dp), dimension(:), allocatable, public horizondepth_mhm
Generating soil database from input file.
subroutine, public generate_soil_database
Generates soil database.
subroutine, public read_soil_lut(filename)
Reads the soil LUT file.