Notes on the implementation in PALM
The following code is from PALM
❯ svn info
Path: .
Working Copy Root Path: /home/avmo/src/snek5000/misc/palm
URL: https://palm.muk.uni-hannover.de/svn/palm/trunk
Relative URL: ^/palm/trunk
Repository Root: https://palm.muk.uni-hannover.de/svn
Repository UUID: 5fb83930-4629-0410-b46d-57b98ee623a8
Revision: 4901
Node Kind: directory
Schedule: normal
Last Changed Author: banzhafs
Last Changed Rev: 4901
Last Changed Date: 2021-03-04 22:24:08 +0100 (Thu, 04 Mar 2021)
This excerpt from subroutine calc_ob
is responsible for computing the Obukhov Length.
!> @file surface_layer_fluxes_mod.f90
!--------------------------------------------------------------------------------------------------!
! This file is part of the PALM model system.
!
! PALM is free software: you can redistribute it and/or modify it under the terms of the GNU General
! Public License as published by the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! PALM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
! implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
! Public License for more details.
!
! You should have received a copy of the GNU General Public License along with PALM. If not, see
! <http://www.gnu.org/licenses/>.
!
! Copyright 1997-2021 Leibniz Universitaet Hannover
!--------------------------------------------------------------------------------------------------!
!-- Calculate the Obukhov length using Newton iteration
!$OMP PARALLEL DO PRIVATE(i, j, z_mo, ol_old, iter, ol_m, ol_l, ol_u, f, f_d_ol)
!$ACC PARALLEL LOOP PRIVATE(i, j, z_mo) &
!$ACC PRIVATE(ol_old, ol_m, ol_l, ol_u, f, f_d_ol) &
!$ACC PRESENT(surf)
DO m = 1, surf%ns
i = surf%i(m)
j = surf%j(m)
z_mo = surf%z_mo(m)
!
!-- Store current value in case the Newton iteration fails
ol_old = surf%ol(m)
!
!-- Ensure that the bulk Richardson number and the Obukhov length have the same sign
IF ( surf%rib(m) * surf%ol(m) < 0.0_wp .OR. ABS( surf%ol(m) ) == ol_max ) THEN
IF ( surf%rib(m) > 1.0_wp ) surf%ol(m) = 0.01_wp
IF ( surf%rib(m) < 0.0_wp ) surf%ol(m) = -0.01_wp
ENDIF
!
!-- Iteration to find Obukhov length
iter = 0
DO
iter = iter + 1
!
!-- In case of divergence, use the value of the previous time step
IF ( iter > 1000 ) THEN
surf%ol(m) = ol_old
EXIT
ENDIF
ol_m = surf%ol(m)
ol_l = ol_m - 0.001_wp * ol_m
ol_u = ol_m + 0.001_wp * ol_m
IF ( ibc_pt_b /= 1 ) THEN
!
!-- Calculate f = Ri - [...]/[...]^2 = 0
f = surf%rib(m) - ( z_mo / ol_m ) * ( surf%ln_z_z0h(m) &
- psi_h( z_mo / ol_m ) &
+ psi_h( surf%z0h(m) / ol_m ) ) / &
( surf%ln_z_z0(m) - psi_m( z_mo / ol_m ) &
+ psi_m( surf%z0(m) / ol_m ) )**2
!
!-- Calculate df/dL
f_d_ol = ( - ( z_mo / ol_u ) * ( surf%ln_z_z0h(m) &
- psi_h( z_mo / ol_u ) &
+ psi_h( surf%z0h(m) / ol_u ) ) / &
( surf%ln_z_z0(m) - psi_m( z_mo / ol_u ) &
+ psi_m( surf%z0(m) / ol_u ) )**2 &
+ ( z_mo / ol_l ) * ( surf%ln_z_z0h(m) - psi_h( z_mo / ol_l ) &
+ psi_h( surf%z0h(m) / ol_l ) ) /&
( surf%ln_z_z0(m) - psi_m( z_mo / ol_l ) &
+ psi_m( surf%z0(m) / ol_l ) )**2 ) / ( ol_u - ol_l )
ELSE
!
!-- Calculate f = Ri - 1 /[...]^3 = 0
f = surf%rib(m) - ( z_mo / ol_m ) / &
( surf%ln_z_z0(m) - psi_m( z_mo / ol_m ) + psi_m( surf%z0(m) / ol_m ) )**3
!
!-- Calculate df/dL
f_d_ol = ( - ( z_mo / ol_u ) / ( surf%ln_z_z0(m) &
- psi_m( z_mo / ol_u ) &
+ psi_m( surf%z0(m) / ol_u ) &
)**3 &
+ ( z_mo / ol_l ) / ( surf%ln_z_z0(m) &
- psi_m( z_mo / ol_l ) &
+ psi_m( surf%z0(m) / ol_l ) &
)**3 &
) / ( ol_u - ol_l )
ENDIF
!
!-- Calculate new L
surf%ol(m) = ol_m - f / f_d_ol
!
!-- Ensure that the bulk Richardson number and the Obukhov length have the same sign and
!-- ensure convergence.
IF ( surf%ol(m) * ol_m < 0.0_wp ) surf%ol(m) = ol_m * 0.5_wp
!
!-- If unrealistic value occurs, set L to the maximum value that is allowed
IF ( ABS( surf%ol(m) ) > ol_max ) THEN
surf%ol(m) = ol_max
EXIT
ENDIF
!
!-- Assure that Obukhov length does not become zero. If the limit is reached, exit the loop.
IF ( ABS( surf%ol(m) ) < 1E-5_wp ) THEN
surf%ol(m) = SIGN( 1E-5_wp, surf%ol(m) )
EXIT
ENDIF
!
!-- Check for convergence
IF ( ABS( ( surf%ol(m) - ol_m ) / surf%ol(m) ) < 1.0E-4_wp ) EXIT
ENDDO
ENDDO
What is interesting here is that f_d_ol
is not an exact derivative but, uses approximates it using Central difference. Does not seem like Secant Method.