source: trunk/LMDZ.COMMON/libf/evolution/math_toolkit.F90 @ 4003

Last change on this file since 4003 was 3991, checked in by jbclement, 6 weeks ago

PEM:
Apply documentation template everywhere: standardized headers format with short description, separators between functions/subroutines, normalized code sections, aligned dependencies/arguments/variables declaration.
JBC

File size: 9.8 KB
RevLine 
[3989]1module math_toolkit
[3991]2!-----------------------------------------------------------------------
3! NAME
4!     math_toolkit
5!
6! DESCRIPTION
7!     The module contains all the mathematical subroutines used in the PEM.
8!
9! AUTHORS & DATE
10!     L. Lange
11!     JB Clement, 2023-2025
12!
13! NOTES
14!     Adapted from Schorghofer MSIM (N.S, Icarus 2010)
15!-----------------------------------------------------------------------
[3526]16
[3991]17! DECLARATION
18! -----------
[3526]19implicit none
20
[3991]21contains
22!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23
[3526]24!=======================================================================
25SUBROUTINE deriv1(z,nz,y,y0,ybot,dzY)
[3991]26!-----------------------------------------------------------------------
27! NAME
28!     deriv1
29!
30! DESCRIPTION
31!     Compute the first derivative of a function y(z) on an irregular grid.
32!
33! AUTHORS & DATE
34!     N.S (Icarus 2010)
35!     L. Lange
36!     JB Clement, 2023-2025
37!
38! NOTES
39!     Upper boundary conditions: y(0) = y0
40!     Lower boundary condition: yp = ybottom
41!-----------------------------------------------------------------------
[3526]42
[3991]43! DECLARATION
44! -----------
[3526]45implicit none
46
[3991]47! ARGUMENTS
48! ---------
49integer,             intent(in)  :: nz       ! number of layer
50real, dimension(nz), intent(in)  :: z        ! depth layer
51real, dimension(nz), intent(in)  :: y        ! function which needs to be derived
52real,                intent(in)  :: y0, ybot ! boundary conditions
53real, dimension(nz), intent(out) :: dzY      ! derivative of y w.r.t depth
54
55! LOCAL VARIABLES
56! ---------------
[3526]57integer :: j
58real    :: hm, hp, c1, c2, c3
[2961]59
[3991]60! CODE
61! ----
[3526]62hp = z(2) - z(1)
63c1 = z(1)/(hp*z(2))
64c2 = 1/z(1) - 1/(z(2) - z(1))
65c3 = -hp/(z(1)*z(2))
66dzY(1) = c1*y(2) + c2*y(1) + c3*y0
67do j = 2,nz - 1
68    hp = z(j + 1) - z(j)
69    hm = z(j) - z(j - 1)
70    c1 = +hm/(hp*(z(j + 1) - z(j - 1)))
71    c2 = 1/hm - 1/hp
72    c3 = -hp/(hm*(z(j + 1) - z(j - 1)))
73    dzY(j) = c1*y(j + 1) + c2*y(j) + c3*y(j - 1)
74enddo
75dzY(nz) = (ybot - y(nz - 1))/(2.*(z(nz) - z(nz - 1)))
[2961]76
[3526]77END SUBROUTINE deriv1
[3991]78!=======================================================================
[2961]79
[3526]80!=======================================================================
81SUBROUTINE deriv2_simple(z,nz,y,y0,yNp1,yp2)
[3991]82!-----------------------------------------------------------------------
83! NAME
84!     deriv2_simple
85!
86! DESCRIPTION
87!     Compute the second derivative of a function y(z) on an irregular grid.
88!
89! AUTHORS & DATE
90!     N.S (Icarus 2010)
91!     JB Clement, 2023-2025
92!
93! NOTES
94!     Boundary conditions: y(0) = y0, y(nz + 1) = yNp1
95!-----------------------------------------------------------------------
[2961]96
[3991]97! DECLARATION
98! -----------
[3526]99implicit none
[2961]100
[3991]101! ARGUMENTS
102! ---------
103integer,             intent(in)  :: nz
104real, dimension(nz), intent(in)  :: z, y
105real,                intent(in)  :: y0, yNp1
[3553]106real, dimension(nz), intent(out) :: yp2
[3991]107
108! LOCAL VARIABLES
109! ---------------
[3526]110integer :: j
111real    :: hm, hp, c1, c2, c3
[2961]112
[3991]113! CODE
114! ----
[3526]115c1 = +2./((z(2) - z(1))*z(2))
116c2 = -2./((z(2) - z(1))*z(1))
117c3 = +2./(z(1)*z(2))
118yp2(1) = c1*y(2) + c2*y(1) + c3*y0
119do j = 2,nz - 1
120    hp = z(j + 1) - z(j)
121    hm = z(j) - z(j - 1)
122    c1 = +2./(hp*(z(j + 1) - z(j - 1)))
123    c2 = -2./(hp*hm)
124    c3 = +2./(hm*(z(j + 1) - z(j-1)))
125    yp2(j) = c1*y(j + 1) + c2*y(j) + c3*y(j - 1)
126enddo
127yp2(nz) = (yNp1 - 2*y(nz) + y(nz - 1))/(z(nz) - z(nz - 1))**2
[2961]128
[3526]129END SUBROUTINE deriv2_simple
[3991]130!=======================================================================
[2961]131
[3526]132!=======================================================================
133SUBROUTINE  deriv1_onesided(j,z,nz,y,dy_zj)
[3991]134!-----------------------------------------------------------------------
135! NAME
136!     deriv1_onesided
137!
138! DESCRIPTION
139!     First derivative of function y(z) at z(j) one-sided derivative
140!     on irregular grid.
141!
142! AUTHORS & DATE
143!     N.S (Icarus 2010)
144!     JB Clement, 2023-2025
145!
146! NOTES
147!
148!-----------------------------------------------------------------------
[2961]149
[3991]150! DECLARATION
151! -----------
[3553]152implicit none
[2961]153
[3991]154! ARGUMENTS
155! ---------
156integer,             intent(in)  :: nz, j
157real, dimension(nz), intent(in)  :: z, y
158real,                intent(out) :: dy_zj
159
160! LOCAL VARIABLES
161! ---------------
[3526]162real :: h1, h2, c1, c2, c3
[2961]163
[3991]164! CODE
165! ----
[3526]166if (j < 1 .or. j > nz - 2) then
167    dy_zj = -1.
168else
169    h1 = z(j + 1) - z(j)
170    h2 = z(j + 2)- z(j + 1)
171    c1 = -(2*h1 + h2)/(h1*(h1 + h2))
172    c2 = (h1 + h2)/(h1*h2)
173    c3 = -h1/(h2*(h1 + h2))
174    dy_zj = c1*y(j) + c2*y(j + 1) + c3*y(j + 2)
175endif
176
177END SUBROUTINE deriv1_onesided
[3991]178!=======================================================================
[3526]179
180!=======================================================================
181PURE SUBROUTINE colint(y,z,nz,i1,i2,integral)
[3991]182!-----------------------------------------------------------------------
183! NAME
184!     colint
185!
186! DESCRIPTION
187!     Column integrates y on irregular grid.
188!
189! AUTHORS & DATE
190!     N.S (Icarus 2010)
191!     JB Clement, 2023-2025
192!
193! NOTES
194!
195!-----------------------------------------------------------------------
[3532]196
[3991]197! DECLARATION
198! -----------
[3526]199implicit none
[3532]200
[3991]201! ARGUMENTS
202! ---------
203integer,             intent(in)  :: nz, i1, i2
204real, dimension(nz), intent(in)  :: y, z
205real,                intent(out) :: integral
206
207! LOCAL VARIABLES
208! ---------------
[3553]209integer             :: i
210real, dimension(nz) :: dz
[2961]211
[3991]212! CODE
213! ----
[3526]214dz(1) = (z(2) - 0.)/2
215do i = 2,nz - 1
216    dz(i) = (z(i + 1) - z(i - 1))/2.
217enddo
218dz(nz) = z(nz) - z(nz - 1)
219integral = sum(y(i1:i2)*dz(i1:i2))
[2961]220
[3526]221END SUBROUTINE colint
[3991]222!=======================================================================
[2961]223
[3526]224!=======================================================================
[2961]225SUBROUTINE findroot(y1,y2,z1,z2,zr)
[3991]226!-----------------------------------------------------------------------
227! NAME
228!     findroot
229!
230! DESCRIPTION
231!     Compute the root zr, between two values y1 and y2 at depth z1,z2.
232!
233! AUTHORS & DATE
234!     L. Lange
235!     JB Clement, 2023-2025
236!
237! NOTES
238!
239!-----------------------------------------------------------------------
[2961]240
[3991]241! DECLARATION
242! -----------
[3526]243implicit none
[2961]244
[3991]245! ARGUMENTS
246! ---------
247real, intent(in)  :: y1, y2
248real, intent(in)  :: z1, z2
249real, intent(out) :: zr
[3526]250
[3991]251! CODE
252! ----
[3526]253zr = (y1*z2 - y2*z1)/(y1 - y2)
254
255END SUBROUTINE findroot
[3991]256!=======================================================================
[3526]257
[3553]258!=======================================================================
259SUBROUTINE solve_tridiag(a,b,c,d,n,x,error)
[3991]260!-----------------------------------------------------------------------
261! NAME
262!     solve_tridiag
263!
264! DESCRIPTION
265!     Solve a tridiagonal system Ax = d using the Thomas' algorithm
266!     a: sub-diagonal
267!     b: main diagonal
268!     c: super-diagonal
269!     d: right-hand side
270!     x: solution
271!
272! AUTHORS & DATE
273!     JB Clement, 2025
274!
275! NOTES
276!
277!-----------------------------------------------------------------------
[3553]278
[3991]279! DECLARATION
280! -----------
[3553]281implicit none
282
[3991]283! ARGUMENTS
284! ---------
285integer,                intent(in)  :: n
286real, dimension(n),     intent(in)  :: b, d
287real, dimension(n - 1), intent(in)  :: a, c
288real, dimension(n),     intent(out) :: x
289integer,                intent(out) :: error
290
291! LOCAL VARIABLES
292! ---------------
[3553]293integer            :: i
294real               :: m
295real, dimension(n) :: cp, dp
296
[3991]297! CODE
298! ----
[3553]299! Check stability: diagonally dominant condition
300error = 0
301if (abs(b(1)) < abs(c(1))) then
302    error = 1
303    return
304endif
305do i = 2,n - 1
306    if (abs(b(i)) < abs(a(i - 1)) + abs(c(i))) then
307        error = 1
308        return
309    endif
310enddo
311if (abs(b(n)) < abs(a(n - 1))) then
312    error = 1
313    return
314endif
315
316! Initialization
317cp(1) = c(1)/b(1)
318dp(1) = d(1)/b(1)
319
320! Forward sweep
321do i = 2,n - 1
322    m = b(i) - a(i - 1)*cp(i - 1)
323    cp(i) = c(i)/m
324    dp(i) = (d(i) - a(i - 1)*dp(i - 1))/m
325enddo
326m = b(n) - a(n - 1)*cp(n - 1)
327dp(n) = (d(n) - a(n - 1)*dp(n - 1))/m
328
329! Backward substitution
330x(n) = dp(n)
331do i = n - 1,1,-1
332    x(i) = dp(i) - cp(i)*x(i + 1)
333enddo
334
335END SUBROUTINE solve_tridiag
[3991]336!=======================================================================
[3553]337
338!=======================================================================
339SUBROUTINE solve_steady_heat(n,z,mz,kappa,mkappa,T_left,q_right,T)
[3991]340!-----------------------------------------------------------------------
341! NAME
342!     solve_steady_heat
343!
344! DESCRIPTION
345!     Solve 1D steady-state heat equation with space-dependent thermal diffusivity.
346!     Uses Thomas algorithm to solve tridiagonal system.
347!     Left boundary: prescribed temperature (Dirichlet).
348!     Right boundary: prescribed thermal flux (Neumann).
349!
350! AUTHORS & DATE
351!     JB Clement, 2025
352!
353! NOTES
354!     Grid points at z, mid-grid points at mz.
355!     Thermal diffusivity specified at both grids (kappa, mkappa).
356!-----------------------------------------------------------------------
[3553]357
[3991]358! DEPENDENCIES
359! ------------
[3989]360use stop_pem, only: stop_clean
[3553]361
[3991]362! DECLARATION
363! -----------
[3553]364implicit none
365
[3991]366! ARGUMENTS
367! ---------
368integer,                intent(in)  :: n
369real, dimension(n),     intent(in)  :: z, kappa
370real, dimension(n - 1), intent(in)  :: mz, mkappa
371real,                   intent(in)  :: T_left, q_right
372real, dimension(n),     intent(out) :: T
373
374! LOCAL VARIABLES
375! ---------------
[3553]376integer                :: i, error
377real, dimension(n)     :: b, d
378real, dimension(n - 1) :: a, c
379
[3991]380! CODE
381! ----
[3553]382! Initialization
383a = 0.; b = 0.; c = 0.; d = 0.
384
385! Left boundary condition (Dirichlet: prescribed temperature)
386b(1) = 1.
387d(1) = T_left
388
389! Internal points
390do i = 2,n - 1
391    a(i - 1) = -mkappa(i - 1)/((mz(i) - mz(i - 1))*(z(i) - z(i - 1)))
392    c(i) = -mkappa(i)/((mz(i) - mz(i - 1))*(z(i + 1) - z(i)))
393    b(i) = -(a(i - 1) + c(i))
394enddo
395
396! Right boundary condition (Neumann: prescribed temperature)
397a(n - 1) = kappa(n - 1)/(z(n) - z(n - 1))
398b(n) = -kappa(n)/(z(n) - z(n - 1))
399d(n) = q_right
400
401! Solve the tridiagonal linear system with the Thomas' algorithm
402call solve_tridiag(a,b,c,d,n,T,error)
[3989]403if (error /= 0) call stop_clean("solve_steady_heat","Unstable solving!",1)
[3553]404
405END SUBROUTINE solve_steady_heat
[3991]406!=======================================================================
[3553]407
[3989]408end module math_toolkit
Note: See TracBrowser for help on using the repository browser.