5.13.2-dev0
mHM
The mesoscale Hydrological Model
Loading...
Searching...
No Matches
mo_neutrons.f90
Go to the documentation of this file.
1!> \file mo_neutrons.f90
2!> \brief \copybrief mo_neutrons
3!> \details \copydetails mo_neutrons
4
5!> \brief Models to predict neutron intensities above soils
6!> \details The number of neutrons above the ground is directly related to
7!! the number soil water content in the ground, air, vegetation and/or snow.
8!! This module forward-models neutron abundance as a state variable for each cell.
9!> \authors Martin Schroen
10!> \date Mar 2015
11!> \warning THIS MODULE IS WORK IN PROGRESS, DO NOT USE FOR RESEARCH.
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_mhm
16
17 ! TODO make it faster with pre-calculated horizons and variables
18 ! TODO use global parameters as linear model
19
20 USE mo_kind, ONLY: i4, dp
21 IMPLICIT NONE
22
23 ! Neutron forward model using particle transport physics, see Shuttleworth et al. 2013
24 PUBLIC :: cosmic
25
26 ! inverse \theta(N) relation based on Desilets et al. 2010
27 PUBLIC :: desiletsn0
28
29 ! integration tabular for approximating the neutron flux integral
30 PUBLIC :: tabularintegralafast
31
32 PRIVATE
33
34CONTAINS
35
36 ! -----------------------------------------------------------------------------------
37 ! NAME
38 ! DesiletsN0
39 !
40 ! PURPOSE
41 !> \brief Calculate neutrons from soil moisture for effective soil layer
42 !> \details Using the N0-relation derived by Desilets, neutron
43 !> counts above the ground (one value per cell in mHM) can be
44 !> derived by a semi-empirical, semi-physical relation.
45 !> The result depends on N0, the neutron counts for 0% soil mositure.
46 !> This variable is site-specific and is a global parameter in mHM.
47 ! ------------------------------------------------------------------
48 ! N0 formula based on Desilets et al. 2010
49 ! ------------------------------------------------------------------
50 !
51 ! CALLING SEQUENCE
52 ! call DesiletsN0( Moisture(cells,layers), Depths(layers), &
53 ! N0-parameter, output(cells) )
54 !
55 ! INTENT(IN)
56 !> \param[in] "real(dp), dimension(:) :: SoilMoisture" Soil Moisture
57 !> \param[in] "real(dp), dimension(:) :: Horizon_depth" Horizon depths
58 !> \param[in] "real(dp), dimension(:) :: Bd" Bulk density
59 !> \param[in] "real(dp), dimension(:) :: LatWater" Lattice water
60 !> \param[in] "real(dp) :: N0" dry neutron counts
61 !
62 ! INTENT(INOUT)
63 ! None
64 !
65 ! INTENT(OUT)
66 !> \param[out] "real(dp), dimension(size(SoilMoisture,1)) :: neutrons" Neutron counts
67 !
68 ! INTENT(IN), OPTIONAL
69 ! None
70 !
71 ! INTENT(INOUT), OPTIONAL
72 ! None
73 !
74 ! INTENT(OUT), OPTIONAL
75 ! None
76 !
77 ! RETURN
78 ! None
79 !
80 ! RESTRICTIONS
81 ! Horizons(1) must not be zero.
82 !
83 ! EXAMPLE
84 ! N0=1500cph, SoilMoisture(1,1)=70mm, Horizons(1)=200mm
85 ! 1500*(0.372+0.0808/ (70mm/200mm + 0.115))
86 ! DesiletsN0 = 819cph
87 !
88 ! LITERATURE
89 ! Desilets, D., M. Zreda, and T. P. A. Ferre (2010),
90 ! Nature's neutron probe: Land surface hydrology at an elusive scale
91 ! with cosmic rays, WRR, 46, W11505, doi:10.1029/2009WR008726.
92 !
93 ! HISTORY
94 !> \author Martin Schroen
95 !> \date Mar 2015
96 !> Modified Rohini Kumar Oct 2021 - Vertical weighting approach
97 !> for the Neutron count module to mHM - develop branch
98
99 subroutine desiletsn0(SoilMoisture, Horizon_depth, Bd, latWater, N0, neutrons)
100
102 use mo_moment, only: average
103 implicit none
104
105 real(dp), dimension(:), intent(in) :: soilmoisture
106 real(dp), dimension(:), intent(in) :: horizon_depth
107 real(dp), dimension(:), intent(in) :: bd
108 real(dp), dimension(:), intent(in) :: latwater
109 real(dp), intent(in) :: n0 ! from global parameters
110 real(dp), intent(inout) :: neutrons
111 ! local variables
112 integer(i4) :: nlayers, ll, nn, nintervals
113 real(dp), dimension(:), allocatable :: layer_min, layer_max, layer_depth
114 real(dp) :: average_swc, average_bd
115 real(dp) :: d_86_in_cm, d_86_in_mm
116 real(dp), dimension(:), allocatable :: cummulative_layer_weight
117 real(dp) :: depth, weight_10mm_spacing, grav_swc
118
119 ! get the # of soil hoizons
120 nlayers = size(soilmoisture)
121 allocate( layer_min(nlayers) )
122 allocate( layer_max(nlayers) )
123 allocate( layer_depth(nlayers) )
124
125 ! assign layer-1
126 layer_min(1) = 0.0_dp
127 layer_max(1) = horizon_depth(1)
128 ! rest layers
129 do ll = 2, nlayers
130 layer_min(ll) = layer_max(ll-1)
131 layer_max(ll) = horizon_depth(ll)
132 end do
133
134 ! estimate layer depth [mm]
135 layer_depth(:) = layer_max(:) - layer_min(:)
136
137 ! average soil water content (volumetric ones) and Bulk density
138 average_swc = average( soilmoisture(:)/layer_depth(:) )
139 average_bd = average( bd(:) )
140
141 ! estimate D86 [in cm and mm ]
142 d_86_in_cm = ( 1.0_dp/average_bd ) * &
143 ( 8.321_dp + 0.14249_dp * (0.96655_dp + exp(-0.01_dp)) * (20.0_dp + average_swc)/(0.0429_dp + average_swc) )
144 d_86_in_mm = d_86_in_cm * 10.0_dp !# convert cm to mm
145
146
147 ! initalise cummulative layer specific weight
148 allocate( cummulative_layer_weight(nlayers) )
149 cummulative_layer_weight(:) = 0.0_dp
150
151 !! only once to calculate weight at equal space at every 10 mm spacing
152 nintervals = nint( maxval(horizon_depth(:))/10.0_dp )
153
154 ! calculate 10 mm spacing and on fly the cummulative weights
155 depth = 0.0_dp
156 do nn = 1, nintervals
157 weight_10mm_spacing = exp(-2.0_dp * depth / d_86_in_mm )
158
159 ! estimate the layer specific cummulative weights
160 do ll = 1, nlayers
161 if( (depth .GE. layer_min(ll)) .AND. (depth .LT. layer_max(ll)) ) then
162 cummulative_layer_weight(ll) = cummulative_layer_weight(ll) + weight_10mm_spacing
163 end if
164 end do
165 ! update depth
166 depth = depth + 10.0_dp
167 end do
168
169
170 ! estimate weightage SWC
171 ! here average_swc is gravemtric swc = vol.swc/Bulk density
172 average_swc = 0.0_dp
173 do ll = 1, nlayers
174 !! >> add here if organic water is coming !! >> SoilMoisture(LL) + latWater(LL) + organic_water(LL)...
175 grav_swc = ( soilmoisture(ll) + latwater(ll) )/layer_depth(ll)/bd(ll)
176 average_swc = average_swc + ( grav_swc * cummulative_layer_weight(ll) )
177 end do
178 average_swc = average_swc / sum( cummulative_layer_weight(:) )
179
180 ! calculate neutron count based on depth weighted SM of *D86*
181 neutrons = n0 * ( desilets_a1 + desilets_a0 / (average_swc + desilets_a2) )
182
183 !! deallocate variables
184 deallocate(layer_min, layer_max, layer_depth, cummulative_layer_weight)
185
186 end subroutine desiletsn0
187 ! -----------------------------------------------------------------------------------
188 ! NAME
189 ! COSMIC
190 !
191 ! PURPOSE
192 !> \brief Calculate neutrons from soil moisture in all layers.
193 !> \details Neutron counts above the ground (one value per cell in mHM)
194 !> can be derived by a simplified physical neutron transport simulation.
195 !> Fast cosmic-Ray neutrons are generated in the soil and attenuated
196 !> differently in water and soil. The remaining neutrons that reached
197 !> the surface relate to the profile of soil water content below.
198 !> Variables like N, alpha and L3 are site-specific and need to be calibrated.
199 ! ------------------------------------------------------------------
200 ! COSMIC model based on Shuttleworth et al. 2013
201 ! ------------------------------------------------------------------
202 !
203 ! CALLING SEQUENCE
204 ! call COSMIC( Moisture(cells,layers), Depths(layers), &
205 ! COSMIC-parameterset, neutron_integral_AFast, output(cells) )
206 !
207 ! INTENT(IN)
208 !> \param[in] "real(dp), dimension(:) :: SoilMoisture" Soil Moisture
209 !> \param[in] "real(dp), dimension(:) :: Horizons" Horizon depths
210 !> \param[in] "real(dp), dimension(:) :: neutron_integral_AFast" Tabular for Int Approx
211 !> \param[in] "real(dp) :: interc" interception
212 !> \param[in] "real(dp) :: snowpack" snowpack
213 !> \param[in] "real(dp) :: L1_No_count" L1_No_count
214 !> \param[in] "real(dp), dimension(:) :: L1_bulkDens" Bulk Density
215 !> \param[in] "real(dp), dimension(:) :: L1_latticeWater" Lattice Water
216 !> \param[in] "real(dp), dimension(:) :: L1_COSMICL3" L3 from the COSMIC module
217
218 !
219 ! INTENT(INOUT)
220 !> \param[inout] "real(dp) :: neutrons" Neutron counts
221 !
222 ! INTENT(OUT)
223 ! None
224 !
225 ! INTENT(IN), OPTIONAL
226 ! None
227 !
228 ! INTENT(INOUT), OPTIONAL
229 ! None
230 !
231 ! INTENT(OUT), OPTIONAL
232 ! None
233 !
234 ! RETURN
235 ! None
236 !
237 ! RESTRICTIONS
238 ! Horizons(:) must not be zero.
239 !
240 ! EXAMPLE
241 ! see supplementaries in literature
242 !
243 ! LITERATURE
244 ! J. Shuttleworth, R. Rosolem, M. Zreda, and T. Franz,
245 ! The COsmic-ray Soil Moisture Interaction Code (COSMIC) for use in data assimilation,
246 ! HESS, 17, 3205-3217, 2013, doi:10.5194/hess-17-3205-2013
247 ! Support and Code: http://cosmos.hwr.arizona.edu/Software/cosmic.html
248 !
249 ! HISTORY
250 !> \author Martin Schroen, originally written by Rafael Rosolem
251 !> \date Mar 2015
252 !> Rohini Kumar Oct 2021 - Neutron count module to mHM (5.11.2) - develop branch
253 subroutine cosmic(SoilMoisture, Horizons, neutron_integral_AFast, &
254 interc, snowpack, L1_N0, L1_bulkDens, L1_latticeWater, L1_COSMICL3, &
255 neutrons )
256
258 use mo_constants, only: pi_dp
259 implicit none
260
261 real(dp), dimension(:), intent(in) :: soilmoisture
262 real(dp), dimension(:), intent(in) :: horizons
263 real(dp), dimension(:), intent(in) :: neutron_integral_afast
264 real(dp), intent(in) :: interc
265 real(dp), intent(in) :: snowpack
266 real(dp), intent(in) :: l1_n0
267 real(dp), dimension(:), intent(in) :: l1_bulkdens
268 real(dp), dimension(:), intent(in) :: l1_latticewater
269 real(dp), dimension(:), intent(in) :: l1_cosmicl3
270 real(dp), intent(inout) :: neutrons
271
272 real(dp) :: lambdahigh
273 real(dp) :: lambdafast
274 real(dp) :: totflux
275 real(dp) :: sm ! SoilMoisture
276 real(dp) :: lw ! lattice water
277 real(dp) :: bd ! bulk density
278 real(dp) :: l3
279 integer(i4):: iflag_snowlayer_intecept ! 1 if snowlayer is active, 0 else
280
281 real(dp), dimension(size(Horizons)+1) :: zthick ! Soil layer thickness (cm)
282 real(dp), dimension(:), allocatable :: isoimass ! Integrated dry soil mass above layer (g)
283 real(dp), dimension(:), allocatable :: iwatmass ! Integrated water mass above layer (g)
284 real(dp), dimension(:), allocatable :: hiflux ! High energy neutron flux
285 real(dp), dimension(:), allocatable :: xeff ! Fast neutron source strength of layer
286 real(dp), dimension(:), allocatable :: h2oeffheight! "Effective" height of water in layer (g/cm3)
287 real(dp), dimension(:), allocatable :: h2oeffdens ! "Effective" density of water in layer (g/cm3)
288 real(dp), dimension(:), allocatable :: fastflux ! Contribution to above-ground neutron flux
289
290 integer(i4) :: layers ! Total number of soil layers
291 integer(i4) :: ll
292
293
294 ! # of layers
295 layers = size(soilmoisture) + 1 ! soil horizons + one additional snowpack and interception layer
296
297 ! allocate and initalize
298 allocate(hiflux(layers),xeff(layers),&
299 h2oeffdens(layers),h2oeffheight(layers),fastflux(layers),&
300 isoimass(layers),iwatmass(layers))
301
302 zthick(:) = 0.0_dp
303 isoimass(:) = 0.0_dp
304 iwatmass(:) = 0.0_dp
305 hiflux(:) = 0.0_dp
306 xeff(:) = 0.0_dp
307 h2oeffdens(:) = 0.0_dp
308 h2oeffheight(:)= 0.0_dp
309 fastflux(:) = 0.0_dp
310 totflux = 0.0_dp
311 lambdahigh = 0.0_dp
312 lambdafast = 0.0_dp
313 sm = 0.0_dp
314 lw = 0.0_dp
315 bd = 0.0_dp
316 l3 = 1.0_dp
317
318 ! switch of snowlayer+Intercept ON and off
319 ! ON = 1; OFF = 0
320 ! its hardocded here but can be later part of *.nml file
321 iflag_snowlayer_intecept = 0
322
323 ! layer 1 is the surface layer. layer 2 up to layers are the usual layers
324 do ll = 1,layers
325
326 ! High energy neutron downward flux
327 ! The integration is now performed at the node of each layer (i.e., center of the layer)
328
329 !ToDo: maybe put zthick into global constants, so it is an input paramter
330 ! Soil Layers and Thicknesses are constant in mHM, they could be defined outside of this function
331 ! except the top layer thickness, which is dependend on the snow for example
332 ! zthick will be in cm, as all heigths are in cm in this module
333 call layerthickness(ll,horizons,interc,snowpack,zthick)
334
335 if( ( zthick(ll) .GT. 0.0_dp ) .AND. ( (iflag_snowlayer_intecept .GT. 0 ) .OR. (ll.NE.1) ) ) then
336
337 call loopconstants(ll, soilmoisture(:), l1_bulkdens(:), &
338 l1_latticewater(:), l1_cosmicl3(:), sm, bd, lw, l3)
339
340 if (ll .EQ. 1) then
341 h2oeffdens(ll) = h2odens/1000.0_dp
342 else
343 ! calculate the effective height of water in each layer in cm
344 ! because neutron standard measurements are in cm
345 call layerwaterheight(ll,sm,h2oeffheight)
346 ! divided by the thickness of the layers,we get the effective density
347 h2oeffdens(ll) = (h2oeffheight(ll) + lw/10.0_dp)/zthick(ll)*h2odens/1000.0_dp
348 endif
349
350 ! Assuming an area of 1 cm2
351 ! we integrate the bulkdensity/h2oeffdens down to the middle of the layer ll:
352 isoimass(ll) = bd*(0.5_dp*zthick(ll))*1.0_dp
353 iwatmass(ll) = h2oeffdens(ll)*(0.5_dp*zthick(ll))*1.0_dp
354 if ( ll .gt. 1 ) then
355 isoimass(ll) = isoimass(ll)+isoimass(ll-1)+bd*(0.5_dp*zthick(ll-1))*1.0_dp
356 iwatmass(ll) = iwatmass(ll)+iwatmass(ll-1)+h2oeffdens(ll-1)*(0.5_dp*zthick(ll-1))*1.0_dp
357 endif
358
359 lambdahigh = isoimass(ll)/cosmic_l1 + iwatmass(ll)/cosmic_l2
360 lambdafast = isoimass(ll)/l3 + iwatmass(ll)/cosmic_l4
361
362 hiflux(ll) = exp(-lambdahigh)
363 xeff(ll) = zthick(ll)*(cosmic_alpha*bd + h2oeffdens(ll))
364
365 call lookupintegral(fastflux(ll),neutron_integral_afast,lambdafast)
366
367 ! After contribution from all directions are taken into account,
368 ! need to multiply fastflux by 2/pi
369 fastflux(ll) = (2.0_dp/pi_dp) * fastflux(ll)
370
371 ! Low energy (fast) neutron upward flux
372 totflux = totflux + hiflux(ll) * xeff(ll) * fastflux(ll)
373 endif
374
375 enddo
376
377 ! neutrons=COSMIC_N*totflux
378 !! >> now based on global parameter given in mhm_paramater.nml
379 neutrons = l1_n0*totflux
380
381 !! free space
382 deallocate( hiflux, xeff, h2oeffheight, h2oeffdens, fastflux, isoimass, iwatmass)
383
384 end subroutine cosmic
385
386
387
388
389 ! >>> Loop constants
390 subroutine loopconstants(ll, SoilMoisture,L1_bulkDens,L1_latticeWater,&
391 L1_COSMICL3,sm,bd,lw,L3 )
392 implicit none
393 integer(i4), intent(in) :: ll
394 real(dp), dimension(:), intent(in) :: SoilMoisture
395 real(dp), dimension(:), intent(in) :: L1_bulkDens
396 real(dp), dimension(:), intent(in) :: L1_latticeWater
397 real(dp), dimension(:), intent(in) :: L1_COSMICL3
398 real(dp), intent(out) :: sm ! SoilMoisture
399 real(dp), intent(out) :: bd ! Bulk density
400 real(dp), intent(out) :: lw ! Lattice water
401 real(dp), intent(out) :: L3 ! L3
402
403 if( ll .EQ. 1 ) then
404 !ToDo
405 sm = 0.0_dp
406 bd = 0.0_dp
407 lw = 0.0_dp
408 l3 = 1.0_dp
409 else
410 sm = soilmoisture(ll-1)
411 bd = l1_bulkdens(ll-1)
412 lw = l1_latticewater(ll-1)
413 l3 = l1_cosmicl3(ll-1)
414 endif
415 end subroutine loopconstants
416
417
418
419 ! >> layer thickness
420 subroutine layerthickness(ll,Horizons,interc,snowpack,zthick)
421 implicit none
422 integer(i4), intent(in) :: ll
423 real(dp),dimension(:), intent(in) :: Horizons
424 real(dp), intent(in) :: interc
425 real(dp), intent(in) :: snowpack
426 real(dp),dimension(:), intent(out) :: zthick
427
428 if (ll.eq.1) then
429 zthick(ll)=(snowpack+interc)/10.0_dp
430 else if (ll.eq.2) then
431 zthick(ll)=horizons(ll-1)/10.0_dp
432 else
433 zthick(ll)=(horizons(ll-1) - horizons(ll-2))/10.0_dp
434 endif
435 end subroutine
436
437
438 ! >> layer specific water height
439 subroutine layerwaterheight(ll,sm,h2oeffheight)
440 implicit none
441 integer(i4), intent(in) :: ll
442 real(dp), intent(in) :: sm
443 real(dp),dimension(:), intent(out) :: h2oeffheight
444 ! The effective water height in each layer in each profile:
445 ! ToDo:This should include in future: roots, soil organic matter
446 h2oeffheight(ll) = sm/10.0_dp
447 end subroutine
448
449 ! integrade a monotonuous function f, dependend on two parameters c and phi
450 ! xmin and xmax are the borders for the integration
451 ! if the values for f(xmin) or f(xmax) are undefinde (like exp(-1/0)), they
452 ! can be set with fxmin, fxmax.
453 ! eps is for the accuracy of the result. If the function f is monotonuous, the
454 ! error is at most eps.
455 ! steps is the maximum number of interpolation points. It is overriding the
456 ! error and is the maximum number of steps. A specification of the error
457 ! though still has an impact. If the function is interpolated well enough
458 ! in a specific flat region regarding the error it can be interpolated better
459 ! in a less flat region.
460 !
461 !For the specific given integral it is very precise with steps=1024
462 subroutine approx_mon_int(res,f,c,xmin,xmax,eps,steps,fxmin,fxmax)
463 implicit none
464 real(dp) :: res
465 real(dp), external :: f
466 real(dp), intent(in) :: c
467 real(dp), intent(in) :: xmax
468 real(dp), intent(in) :: xmin
469 real(dp), optional :: eps
470 integer(i4), optional :: steps
471 real(dp), optional :: fxmin
472 real(dp), optional :: fxmax
473
474 !locale variables
475 real(dp) :: epstemp
476 integer(i4) :: stepstemp
477 real(dp) :: fxmintemp
478 real(dp) :: fxmaxtemp
479
480 ! init
481 if (.not. present(eps)) then
482 epstemp=0.001_dp
483 else
484 epstemp=eps
485 endif
486
487 if (.not. present(steps)) then
488 stepstemp=0
489 else
490 stepstemp=steps
491 endif
492
493 if (.not. present(fxmin)) then
494 fxmintemp=f(c,xmin)
495 else
496 fxmintemp=fxmin
497 endif
498
499 if (.not. present(fxmax)) then
500 fxmaxtemp=f(c,xmax)
501 else
502 fxmaxtemp=fxmax
503 endif
504
505 res=0.0_dp
506
507 if (stepstemp .gt. 0) then
508 call approx_mon_int_steps(res,f,c,xmin,xmax,epstemp,stepstemp,fxmintemp,fxmaxtemp)
509 else
510 call approx_mon_int_eps(res,f,c,xmin,xmax,epstemp,fxmintemp,fxmaxtemp)
511 endif
512
513 end subroutine
514
515 recursive subroutine approx_mon_int_steps(res,f,c,xmin,xmax,eps,steps,fxmin,fxmax)
516 implicit none
517 real(dp) :: res
518 real(dp), external :: f
519 real(dp), intent(in) :: c
520 real(dp), intent(in) :: xmax
521 real(dp), intent(in) :: xmin
522 real(dp), intent(in) :: eps
523 integer(i4), intent(in) :: steps
524 real(dp), intent(in) :: fxmin
525 real(dp), intent(in) :: fxmax
526
527 !locale variables
528 real(dp) :: xm
529 real(dp) :: fxm
530 real(dp) :: err
531
532 xm = (xmax+xmin)/2.0_dp
533 fxm= f(c,xm)
534
535 err=abs((fxmax-fxm)*(xmax-xm))
536 if ((err .gt. eps).and.(steps .gt. 1)) then
537 call approx_mon_int_steps(res,f,c,xm,xmax,eps/2.0,steps-steps/2,fxm,fxmax)
538 else
539 res=res+(xmax-xm)*(fxmax+fxm)/2.0_dp
540 endif
541
542 err=abs((fxm-fxmin)*(xm-xmin))
543 if ((err .gt. eps).and.(steps .gt. 1)) then
544 call approx_mon_int_steps(res,f,c,xmin,xm,eps/2.0,steps/2,fxmin,fxm)
545 else
546 res=res+(xm-xmin)*(fxm+fxmin)/2.0_dp
547 endif
548 end subroutine
549
550 recursive subroutine approx_mon_int_eps(res,f,c,xmin,xmax,eps,fxmin,fxmax)
551 implicit none
552 real(dp) :: res
553 real(dp), external :: f
554 real(dp), intent(in) :: c
555 real(dp), intent(in) :: xmax
556 real(dp), intent(in) :: xmin
557 real(dp), intent(in) :: eps
558 real(dp), intent(in) :: fxmin
559 real(dp), intent(in) :: fxmax
560
561 !locale variables
562 real(dp) :: xm
563 real(dp) :: fxm
564 real(dp) :: err
565
566 xm = (xmax+xmin)/2.0_dp
567 fxm= f(c,xm)
568
569 err=abs((fxmax-fxm)*(xmax-xm))
570 if (err .gt. eps) then
571 call approx_mon_int_eps(res,f,c,xm,xmax,eps/2.0,fxm,fxmax)
572 else
573 res=res+(xmax-xm)*(fxmax+fxm)/2.0_dp
574 endif
575
576 err=abs((fxm-fxmin)*(xm-xmin))
577 if (err .gt. eps) then
578 call approx_mon_int_eps(res,f,c,xmin,xm,eps/2.0,fxmin,fxm)
579 else
580 res=res+(xm-xmin)*(fxm+fxmin)/2.0_dp
581 endif
582 end subroutine
583
584
585 ! -----------------------------------------------------------------------------------
586 ! NAME
587 ! TabularIntegralAFast
588 !
589 ! PURPOSE
590 !> \brief Save approximation data for A_fast
591 !> \details The COSMIC subroutine needs A_fast to be calculated.
592 !> A_fast=int_{0}^{pi/2} exp(-Lambda_fast(z)/cos(phi) dphi)
593 !> This subroutine stores data for intsize values for
594 !> c:=Lambda_fast(z) between 0 and maxC, and will be written
595 !> into the global array variable neutron_integral_AFast.
596 !> The calculation of the values is done with a very precise
597 !> recursive approximation subroutine. That recursive subroutine
598 !> should not be used inside the time, cells and layer loops, because
599 !> it is slow.
600 !> Inside the loops in the module COSMIC the tabular is used to
601 !> estimate A_fast, if 0<c<maxC, otherwise the recursive
602 !> approximation is used.
603 ! ------------------------------------------------------------------
604 ! TabularIntegralAFast: a tabular for calculations with splines
605 ! ------------------------------------------------------------------
606 !
607 ! CALLING SEQUENCE
608 ! call TabularIntegralAFast(neutron_integral_AFast,intsize,maxC)
609 !
610 ! INTENT(IN)
611 !> \param[in] "real(dp), dimension(:,:) :: SoilMoisture" Soil Moisture
612 !> \param[in] "real(dp), dimension(:) :: Horizons" Horizon depths
613 !> \param[in] "real(dp), dimension(:) :: params" ! N0, N1, N2, alpha0, alpha1, L30, L31
614 !> \param[in] "integer(i4) :: intsize" ! number of values for the approximation
615 !> \param[in] "real(dp) :: maxC" ! maximum value for A_fast
616 !
617 ! INTENT(INOUT)
618 !> \param[out] "real(dp), dimension(intsize) :: neutron_integral_AFast" approximation values
619 !
620 ! INTENT(OUT)
621 ! None
622 !
623 ! INTENT(IN), OPTIONAL
624 ! None
625 !
626 ! INTENT(INOUT), OPTIONAL
627 ! None
628 !
629 ! INTENT(OUT), OPTIONAL
630 ! None
631 !
632 ! RETURN
633 ! None
634 !
635 ! RESTRICTIONS
636 ! intsize and maxC must be positive
637 !
638 ! EXAMPLE
639 ! intsize=8000, maxC=20.0_dp
640 !
641 ! LITERATURE
642 ! see splines for example
643 !
644 ! HISTORY
645 !> \author Maren Kaluza
646 !> \date Nov 2017
647
648 subroutine tabularintegralafast(integral,maxC)
649 use mo_constants, only: pi_dp
650 implicit none
651 real(dp), dimension(:) :: integral
652 real(dp), intent(in) :: maxc
653
654 !local variables
655 integer(i4) :: i
656 real(dp) :: c
657 integer(i4) :: intsize
658
659 intsize=size(integral)-2
660
661 do i=1,intsize+1
662 c =real(i-1,dp)*maxc/real(intsize,dp)
663 call approx_mon_int(integral(i),&
664 intgrandfast,c,0.0_dp,pi_dp/2.0_dp,steps=1024,fxmax=0.0_dp)
665 enddo
666 integral(intsize+2)=maxc
667 end subroutine
668
669 ! if c>1.0, the function can be fitted very nice with gnuplot
670 ! pi/2*exp(a*x**b)
671 subroutine tabularintegralhermafast(integral,intsize,maxC)
672 use mo_constants, only: pi_dp
673 implicit none
674 real(dp), dimension(:) :: integral
675 integer(i4), intent(in) :: intsize
676 real(dp), intent(in) :: maxC
677
678 !local variables
679 integer(i4) :: i
680 real(dp) :: c
681
682 do i=1,intsize/2+1
683 c =real(i-1,dp)*maxc/real(intsize/2,dp)
684 call approx_mon_int(integral(2*i-1),&
685 intgrandfast,c,0.0_dp,pi_dp/2.0_dp,steps=1024,fxmax=0.0_dp)
686 call approx_mon_int(integral(2*i),&
687 intgrandderivfast,c,0.0_dp,pi_dp/2.0_dp,steps=1024,fxmax=0.0_dp)
688 integral(2*i)=integral(2*i)*maxc/real(intsize/2,dp)
689 enddo
690 end subroutine
691
692 ! if c>1.0, the function can be fitted very nice with gnuplot
693 ! pi/2*exp(a*x**b)
694 subroutine lookupintegral(res,integral,c)
695 use mo_constants, only: pi_dp
696 implicit none
697 real(dp), intent(out):: res
698 real(dp), dimension(:),intent(in) :: integral
699 real(dp), intent(in) :: c
700
701 !local variables
702 integer(i4) :: place
703 real(dp) :: mu
704 integer(i4) :: intsize
705 real(dp) :: maxC
706
707 intsize=size(integral)-2
708 maxc=integral(intsize+2)
709 mu=c*real(intsize,dp)/maxc
710 place=int(mu,i4)+1
711 if (place .gt. intsize) then
712 !call approx_mon_int(res,intgrandFast,c,0.0_dp,PI_dp/2.0_dp,steps=1024,fxmax=0.0_dp)
713 !write(*,*) 'Warning: Lambda_Fast is huge. Slow integration used.'
714 res=(pi_dp/2.0_dp)*exp(-1.57406_dp*c**0.815488_dp)
715 else
716 mu=mu-real(place-1,dp)
717 res=(1.0_dp-mu)*integral(place)+mu*integral(place+1)
718 res=res
719 end if
720 end subroutine
721
722 subroutine lookuphermiteintegral(res,integral,intsize,c,maxC)
723 use mo_constants, only: pi_dp
724 implicit none
725 real(dp) :: res
726 real(dp), dimension(:),intent(in):: integral
727 integer(i4), intent(in) :: intsize
728 real(dp), intent(in) :: c
729 real(dp), intent(in) :: maxC
730
731 !local variables
732 integer(i4) :: place
733 real(dp) :: mu
734
735 mu=c*real(intsize/2,dp)/maxc
736 place=int(mu,i4)+1
737 if (place .gt. intsize) then
738 !call approx_mon_int(res,intgrandFast,c,0.0_dp,PI_dp/2.0_dp,steps=1024,fxmax=0.0_dp)
739 !write(*,*) 'Warning: Lambda_Fast is huge. Slow integration used.'
740 res=(pi_dp/2.0_dp)*exp(-1.57406_dp*c**0.815488_dp)
741 else
742 mu=mu-real(place-1,dp)
743 res=h00(mu)*integral(2*place-1)+h01(mu)*integral(2*place+1)+&
744 h10(mu)*integral(2*place )+h11(mu)*integral(2*place+2)
745 end if
746 end subroutine
747
748 !very bad approximation for the integral from the COSMIC paper. Maybe there is
749 !some copying error? They claim, this function would have an error of less
750 !then 1/1000 (which can not be true anyway, because the integral goes to zero
751 !for c->\infty, and the last if case is a polynome with some coefficients
752 !unequal to zero and therefore tends to \pm \infty), but even in the first 5
753 !cases this approximation has sometimes an error of about 1/3 in case 4.
754 subroutine cosmiceffintegration(res,x)
755 implicit none
756 real(dp) :: res
757 real(dp), intent(in) :: x
758 !local variables
759 real(dp) :: a,b,c,d
760
761 ! write(*,*) x
762 if (x .le. 0.05_dp) then
763 a=-347.86105_dp
764 b=41.64233_dp
765 c=-4.018_dp
766 d=-0.00018_dp
767 res=exppolynomdeg3(x,a,b,c,d)
768 ! write(*,*) 'c1'
769 else if (x .le. 0.1_dp) then
770 a=-16.24066_dp
771 b=6.64468_dp
772 c=-2.82003_dp
773 d=-0.01389_dp
774 res=exppolynomdeg3(x,a,b,c,d)
775 ! write(*,*) 'c2'
776 else if (x .le. 0.5_dp) then
777 a=-0.95245_dp
778 b=1.44751_dp
779 c=-2.18933_dp
780 d=-0.04034_dp
781 res=exppolynomdeg3(x,a,b,c,d)
782 ! write(*,*) 'c3'
783 else if (x .le. 1.0_dp) then
784 a=-0.09781_dp
785 b=0.36907_dp
786 c=-1.72912_dp
787 d=-0.10761_dp
788 res=exppolynomdeg3(x,a,b,c,d)
789 ! write(*,*) 'c4'
790 else if (x .le. 5.0_dp) then
791 a=-0.00416_dp
792 b=0.05808_dp
793 c=-1.361482_dp
794 d=-0.25822_dp
795 res=exppolynomdeg3(x,a,b,c,d)
796 ! write(*,*) 'c5'
797 else
798 a=0.0_dp
799 b=0.00061_dp
800 c=-1.04847_dp
801 d=-0.96617_dp
802 res=exppolynomdeg3(x,a,b,c,d)
803 ! write(*,*) 'c6'
804 endif
805
806 end subroutine
807
808 subroutine oldintegration(res,c)
809 use mo_constants, only: pi_dp
810 implicit none
811 real(dp) :: res
812 real(dp), intent(in) :: c
813
814 ! local variables
815 real(dp) :: zdeg
816 real(dp) :: zrad
817 real(dp) :: costheta
818 real(dp) :: dtheta
819
820 integer(i4) :: angle ! loop indices for an integration interval
821 ! Angle distribution parameters (HARDWIRED)
822 ! rr: Using 0.5 deg angle intervals appears to be sufficient
823 ! rr: (smaller angles increase the computing time for COSMIC)
824
825 dtheta = 0.5_dp*(pi_dp/180.0_dp)
826
827 ! This second loop needs to be done for the distribution of angles for fast neutron release
828 ! the intent is to loop from 0 to 89.5 by 0.5 degrees - or similar.
829 ! Because Fortran loop indices are integers, we have to divide the indices by 10 - you get the idea.
830 res = 0.0_dp
831 do angle=0,179
832 zdeg = real(angle,dp)*0.5_dp
833 zrad = (zdeg*pi_dp)/180.0_dp
834 costheta = cos(zrad)
835 ! Angle-dependent low energy (fast) neutron upward flux
836 res = res + exp(-c/costheta)*dtheta
837 enddo
838 end subroutine
839
840 function intgrandfast(c,phi)
841 implicit none
842 real(dp) :: intgrandfast
843 real(dp), intent(in) :: c
844 real(dp), intent(in) :: phi
845 intgrandfast=exp(-c/cos(phi))
846 return
847 end function
848
849 function intgrandderivfast(c,phi)
850 implicit none
851 real(dp) :: intgrandderivfast
852 real(dp), intent(in) :: c
853 real(dp), intent(in) :: phi
854 intgrandderivfast=(-1.0_dp/cos(phi))*exp(-c/cos(phi))
855 return
856 end function
857
858 function exppolynomdeg3(x,a,b,c,d)
859 implicit none
860 real(dp) :: exppolynomdeg3
861 real(dp), intent(in) :: x
862 real(dp), intent(in) :: a,b,c,d
863
864 exppolynomdeg3=exp(a*x**3+b*x**2+c*x+d)
865 return
866 end function
867
868 ! hermite polynoms
869 function h00(t)
870 implicit none
871 real(dp) :: h00
872 real(dp), intent(in) :: t
873 h00=2.0_dp*t**3-3.0_dp*t**2+1.0_dp
874 return
875 end function
876
877 function h01(t)
878 implicit none
879 real(dp) :: h01
880 real(dp), intent(in) :: t
881 h01=-2.0_dp*t**3+3.0_dp*t**2
882 return
883 end function
884
885 function h10(t)
886 implicit none
887 real(dp) :: h10
888 real(dp), intent(in) :: t
889 h10=t**3-2.0_dp*t**2+t
890 return
891 end function
892
893 function h11(t)
894 implicit none
895 real(dp) :: h11
896 real(dp), intent(in) :: t
897 h11=t**3-t**2
898 return
899 end function
900
901END MODULE mo_neutrons
Provides mHM specific constants.
real(dp), parameter, public desilets_a0
Neutrons and moisture: N0 formula, Desilets et al.
real(dp), parameter, public desilets_a2
real(dp), parameter, public cosmic_l4
real(dp), parameter, public cosmic_l1
real(dp), parameter, public h2odens
real(dp), parameter, public cosmic_l2
real(dp), parameter, public desilets_a1
real(dp), parameter, public cosmic_alpha
Models to predict neutron intensities above soils.
subroutine loopconstants(ll, soilmoisture, l1_bulkdens, l1_latticewater, l1_cosmicl3, sm, bd, lw, l3)
real(dp) function h10(t)
subroutine oldintegration(res, c)
subroutine cosmiceffintegration(res, x)
subroutine, public desiletsn0(soilmoisture, horizon_depth, bd, latwater, n0, neutrons)
Calculate neutrons from soil moisture for effective soil layer.
subroutine, public cosmic(soilmoisture, horizons, neutron_integral_afast, interc, snowpack, l1_n0, l1_bulkdens, l1_latticewater, l1_cosmicl3, neutrons)
Calculate neutrons from soil moisture in all layers.
subroutine layerwaterheight(ll, sm, h2oeffheight)
real(dp) function h11(t)
subroutine approx_mon_int(res, f, c, xmin, xmax, eps, steps, fxmin, fxmax)
subroutine, public tabularintegralafast(integral, maxc)
Save approximation data for A_fast.
subroutine layerthickness(ll, horizons, interc, snowpack, zthick)
recursive subroutine approx_mon_int_steps(res, f, c, xmin, xmax, eps, steps, fxmin, fxmax)
real(dp) function intgrandfast(c, phi)
subroutine lookuphermiteintegral(res, integral, intsize, c, maxc)
recursive subroutine approx_mon_int_eps(res, f, c, xmin, xmax, eps, fxmin, fxmax)
subroutine tabularintegralhermafast(integral, intsize, maxc)
subroutine lookupintegral(res, integral, c)
real(dp) function h00(t)
real(dp) function intgrandderivfast(c, phi)
real(dp) function exppolynomdeg3(x, a, b, c, d)
real(dp) function h01(t)