source: codes/icosagcm/devel/src/dynamics/compute_geopot.F90

Last change on this file was 1027, checked in by dubos, 4 years ago

devel : towards conformity to F2008 standard

File size: 11.7 KB
Line 
1MODULE compute_geopot_mod
2  USE prec, ONLY : rstd
3  USE grid_param
4  USE earth_const
5  USE disvert_mod
6  USE omp_para
7  USE trace
8  IMPLICIT NONE
9  PRIVATE
10  SAVE
11
12#include "../unstructured/unstructured.h90"
13
14  PUBLIC :: compute_geopot_unst, compute_geopot_hex, compute_geopot_manual
15
16CONTAINS
17
18#ifdef BEGIN_DYSL
19
20{# ---------------- macro to generate code computing pressure top-down ---------------
21   formula = formula to compute 'gravitational' mass
22   = rhodz (dry) rhodz*theta (boussinesq) rhodz*(1+qv) (moist)         #}
23
24#define BALANCE(formula) {% call(thecell) balance() %} formula {% endcall %}
25{% macro balance() %} {% set formula=caller %}
26SEQUENCE_C1
27  PROLOGUE(llm)
28    pk(CELL) = ptop + .5*g*{{ formula('CELL') }}
29  END_BLOCK
30  BODY('llm-1,1,-1') 
31    pk(CELL) = pk(UP(CELL)) + (.5*g)*({{ formula('CELL') }}+{{ formula('UP(CELL)') }})
32  END_BLOCK
33  IF(caldyn_eta == eta_lag) THEN
34    EPILOGUE(1)
35      ps(HIDX(CELL)) = pk(CELL) + .5*g*{{ formula('CELL') }}
36    END_BLOCK
37  END IF
38END_BLOCK
39{%- endmacro %}
40
41{# ------------ macro to generate code computing geopotential bottom-up --------------
42   var = variable to be stored in pk(CELL)
43   caller() computes gv = g*v where v = specific volume
44   details depend on caldyn_thermo #}
45
46#define GEOPOT(var) {% call geopot(var) %}
47{% macro geopot(var) %} {% set formula=caller %}
48  SEQUENCE_C1
49    BODY('1,llm')
50      p_ik = pk(CELL)
51      {{ formula() }}
52      pk(CELL) = {{ var }}
53      geopot(UP(CELL)) = geopot(CELL) + gv*rhodz(CELL)
54    END_BLOCK
55  END_BLOCK
56{%- endmacro %}
57
58#define END_GEOPOT {% endcall %}
59
60#define THECELL {{ thecell }}
61
62KERNEL(compute_hydrostatic_pressure)
63  SELECT CASE(caldyn_thermo)
64  CASE(thermo_boussinesq)
65    ! use hydrostatic balance with theta*rhodz to find pk (=Lagrange multiplier=pressure)
66    BALANCE( theta_rhodz(THECELL,1) )
67  CASE(thermo_theta, thermo_entropy, thermo_variable_Cp)
68    BALANCE( rhodz(THECELL) )
69  CASE(thermo_moist)
70    BALANCE( (rhodz(THECELL)+theta_rhodz(THECELL,2)) )
71  END SELECT
72END_BLOCK
73
74KERNEL(compute_geopot)
75  SELECT CASE(caldyn_thermo)
76  CASE(thermo_boussinesq)
77    ! use hydrostatic balance with theta*rhodz to find pk (=Lagrange multiplier=pressure)
78    BALANCE( theta(THECELL,1)*rhodz(THECELL) )
79    ! now pk contains the Lagrange multiplier (pressure)
80    ! specific volume 1 = dphi/g/rhodz
81    SEQUENCE_C1
82      BODY('1,llm')
83        geopot(UP(CELL)) = geopot(CELL) + g*rhodz(CELL)
84      END_BLOCK
85    END_BLOCK
86  CASE(thermo_theta)
87    BALANCE( rhodz(THECELL) )
88    GEOPOT('exner_ik')
89      exner_ik = cpp * (p_ik/preff) ** kappa
90      gv = (g*kappa)*theta(CELL,1)*exner_ik/p_ik
91    END_GEOPOT
92  CASE(thermo_entropy)
93    BALANCE( rhodz(THECELL) )
94    GEOPOT('temp_ik')
95      temp_ik = Treff*exp((theta(CELL,1) + Rd*log(p_ik/preff))/cpp)
96      gv = (g*Rd)*temp_ik/p_ik   ! specific volume v = Rd*T/p
97    END_GEOPOT
98  CASE(thermo_variable_Cp)
99    ! thermodynamics with variable Cp
100    !      Cp.dT = dh = Tds + vdp
101    !              pv = RT
102    ! =>           ds = (dh+v.dp)/T = Cp.dT/T - R dp/p
103    !           Cp(T) = Cp0 * (T/T0)^nu
104    ! =>       s(p,T) = Cp(T)/nu - R log(p/preff)
105    !               h = Cp(T).T/(nu+1)
106    BALANCE( rhodz(THECELL) )
107    GEOPOT('temp_ik')
108      Cp_ik = nu*( theta(CELL,1) + Rd*log(p_ik/preff) )
109      temp_ik = Treff* (Cp_ik/cpp)**(1./nu)
110      gv = (g*Rd)*temp_ik/p_ik   ! specific volume v = Rd*T/p
111    END_GEOPOT
112  CASE(thermo_moist)
113    BALANCE( rhodz(THECELL)*(1.+theta(THECELL,2)) )
114    GEOPOT('temp_ik')
115      qv = theta(CELL,2) ! water vaper mixing ratio = mv/md
116      Rmix = Rd+qv*Rv
117      chi = ( theta(CELL,1) + Rmix*log(p_ik/preff) ) / (cpp + qv*cppv) ! log(T/Treff)
118      temp_ik = Treff*exp(chi)
119      ! specific volume v = R*T/p
120      ! R = (Rd + qv.Rv)/(1+qv)
121      gv = g*Rmix*temp_ik/(p_ik*(1+qv))
122    END_GEOPOT
123  END SELECT
124END_BLOCK
125
126#endif END_DYSL
127
128  !-------------- Wrappers for F2008 conformity -----------------
129  !--------------------------------------------------------------
130
131  SUBROUTINE compute_geopot_hex(rhodz,theta, ps,pk,geopot) 
132    REAL(rstd),INTENT(IN)    :: rhodz(:,:), theta(:,:,:) ! active scalars : theta/entropy, moisture, ...
133    REAL(rstd),INTENT(INOUT) :: ps(:), geopot(:,:)       ! geopotential
134    REAL(rstd),INTENT(OUT)   :: pk(:,:)       ! Exner function (compressible) /Lagrange multiplier (Boussinesq)
135    CALL compute_geopot_hex_(rhodz,theta, ps,pk,geopot)
136  END SUBROUTINE compute_geopot_hex
137
138  SUBROUTINE compute_geopot_unst(rhodz,theta, ps,pk,geopot) 
139    REAL(rstd),INTENT(IN)    :: rhodz(:,:), theta(:,:,:) ! active scalars : theta/entropy, moisture, ...
140    REAL(rstd),INTENT(INOUT) :: ps(:), geopot(:,:)       ! geopotential
141    REAL(rstd),INTENT(OUT)   :: pk(:,:)       ! Exner function (compressible) /Lagrange multiplier (Boussinesq)
142    CALL compute_geopot_unst_(rhodz,theta, ps,pk,geopot)
143  END SUBROUTINE compute_geopot_unst
144
145  !**************************** Geopotential *****************************
146
147  SUBROUTINE compute_geopot_unst_(rhodz,theta,ps,pk,geopot)
148        USE ISO_C_BINDING, only : C_DOUBLE, C_FLOAT
149    USE data_unstructured_mod, ONLY : enter_trace, exit_trace, &
150         id_geopot
151
152    FIELD_MASS  :: rhodz,pk   ! IN, OUT
153    FIELD_THETA :: theta      ! IN
154    FIELD_GEOPOT :: geopot    ! IN(l=1)/OUT(l>1)
155    FIELD_PS     :: ps        ! OUT
156    DECLARE_INDICES
157    NUM :: chi, gv, exner_ik, temp_ik, p_ik, qv, Rmix, Cp_ik
158    START_TRACE(id_geopot, 3,0,3)
159#include "../kernels_unst/compute_geopot.k90"
160    STOP_TRACE
161  END SUBROUTINE compute_geopot_unst_
162 
163  SUBROUTINE compute_geopot_hex_(rhodz,theta, ps,pk,geopot) 
164    REAL(rstd),INTENT(IN)    :: rhodz(iim*jjm,llm)
165    REAL(rstd),INTENT(IN)    :: theta(iim*jjm,llm,nqdyn) ! active scalars : theta/entropy, moisture, ...
166    REAL(rstd),INTENT(INOUT) :: ps(iim*jjm)
167    REAL(rstd),INTENT(OUT)   :: pk(iim*jjm,llm)       ! Exner function (compressible) /Lagrange multiplier (Boussinesq)
168    REAL(rstd),INTENT(INOUT) :: geopot(iim*jjm,llm+1) ! geopotential
169
170    INTEGER :: i,j,ij,l
171    REAL(rstd) :: p_ik, exner_ik, Cp_ik, temp_ik, qv, chi, Rmix, gv
172    INTEGER    :: ij_omp_begin_ext, ij_omp_end_ext
173
174    CALL trace_start("compute_geopot")
175    !$OMP BARRIER
176    CALL distrib_level(ij_begin_ext,ij_end_ext, ij_omp_begin_ext,ij_omp_end_ext)
177#include "../kernels_hex/compute_geopot.k90"
178    !$OMP BARRIER
179
180    CALL trace_end("compute_geopot")
181  END SUBROUTINE compute_geopot_hex_
182
183  SUBROUTINE compute_geopot_manual(rhodz,theta, ps,pk,geopot) 
184    REAL(rstd),INTENT(IN)    :: rhodz(iim*jjm,llm)
185    REAL(rstd),INTENT(IN)    :: theta(iim*jjm,llm,nqdyn) ! active scalars : theta/entropy, moisture, ...
186    REAL(rstd),INTENT(INOUT) :: ps(iim*jjm)
187    REAL(rstd),INTENT(OUT)   :: pk(iim*jjm,llm)       ! Exner function (compressible) /Lagrange multiplier (Boussinesq)
188    REAL(rstd),INTENT(INOUT) :: geopot(iim*jjm,llm+1) ! geopotential
189
190    INTEGER :: i,j,ij,l
191    REAL(rstd) :: p_ik, exner_ik, Cp_ik, temp_ik, qv, chi, Rmix, gv
192    INTEGER    :: ij_omp_begin_ext, ij_omp_end_ext
193
194    CALL trace_start("compute_geopot")
195
196!$OMP BARRIER
197
198    CALL distrib_level(ij_begin_ext,ij_end_ext, ij_omp_begin_ext,ij_omp_end_ext)
199
200    ! Pressure is computed first top-down (temporarily stored in pk)
201    ! Then Exner pressure and geopotential are computed bottom-up
202    ! Works also when caldyn_eta=eta_mass         
203
204    IF(boussinesq) THEN ! compute geopotential and pk=Lagrange multiplier
205       ! specific volume 1 = dphi/g/rhodz
206       !         IF (is_omp_level_master) THEN ! no openMP on vertical due to dependency
207       DO l = 1,llm
208          !DIR$ SIMD
209          DO ij=ij_omp_begin_ext,ij_omp_end_ext         
210             geopot(ij,l+1) = geopot(ij,l) + g*rhodz(ij,l)
211          ENDDO
212       ENDDO
213       ! use hydrostatic balance with theta*rhodz to find pk (Lagrange multiplier=pressure)
214       ! uppermost layer
215       !DIR$ SIMD
216       DO ij=ij_begin_ext,ij_end_ext         
217          pk(ij,llm) = ptop + (.5*g)*theta(ij,llm,1)*rhodz(ij,llm)
218       END DO
219       ! other layers
220       DO l = llm-1, 1, -1
221          !          !$OMP DO SCHEDULE(STATIC)
222          !DIR$ SIMD
223          DO ij=ij_begin_ext,ij_end_ext         
224             pk(ij,l) = pk(ij,l+1) + (.5*g)*(theta(ij,l,1)*rhodz(ij,l)+theta(ij,l+1,1)*rhodz(ij,l+1))
225          END DO
226       END DO
227       ! now pk contains the Lagrange multiplier (pressure)
228    ELSE ! non-Boussinesq, compute pressure, Exner pressure or temperature, then geopotential
229       ! uppermost layer
230       
231       SELECT CASE(caldyn_thermo)
232          CASE(thermo_theta, thermo_entropy)
233             !DIR$ SIMD
234             DO ij=ij_omp_begin_ext,ij_omp_end_ext
235                pk(ij,llm) = ptop + (.5*g)*rhodz(ij,llm)
236             END DO
237             ! other layers
238             DO l = llm-1, 1, -1
239                !DIR$ SIMD
240                DO ij=ij_omp_begin_ext,ij_omp_end_ext         
241                   pk(ij,l) = pk(ij,l+1) + (.5*g)*(rhodz(ij,l)+rhodz(ij,l+1))
242                END DO
243             END DO
244             ! surface pressure (for diagnostics)
245             IF(caldyn_eta==eta_lag) THEN
246                DO ij=ij_omp_begin_ext,ij_omp_end_ext         
247                   ps(ij) = pk(ij,1) + (.5*g)*rhodz(ij,1)
248                END DO
249             END IF
250          CASE(thermo_moist) ! theta(ij,l,2) = qv = mv/md
251             !DIR$ SIMD
252             DO ij=ij_omp_begin_ext,ij_omp_end_ext
253                pk(ij,llm) = ptop + (.5*g)*rhodz(ij,llm)*(1.+theta(ij,l,2))
254             END DO
255             ! other layers
256             DO l = llm-1, 1, -1
257                !DIR$ SIMD
258                DO ij=ij_omp_begin_ext,ij_omp_end_ext         
259                   pk(ij,l) = pk(ij,l+1) + (.5*g)*(          &
260                        rhodz(ij,l)  *(1.+theta(ij,l,2)) +   &
261                        rhodz(ij,l+1)*(1.+theta(ij,l+1,2)) )
262                END DO
263             END DO
264             ! surface pressure (for diagnostics)
265             IF(caldyn_eta==eta_lag) THEN
266                DO ij=ij_omp_begin_ext,ij_omp_end_ext         
267                   ps(ij) = pk(ij,1) + (.5*g)*rhodz(ij,1)*(1.+theta(ij,l,2))
268                END DO
269             END IF
270          END SELECT
271
272       DO l = 1,llm
273          SELECT CASE(caldyn_thermo)
274          CASE(thermo_theta)
275             !DIR$ SIMD
276             DO ij=ij_omp_begin_ext,ij_omp_end_ext
277                p_ik = pk(ij,l)
278                exner_ik = cpp * (p_ik/preff) ** kappa
279                pk(ij,l) = exner_ik
280                ! specific volume v = kappa*theta*pi/p = dphi/g/rhodz
281                geopot(ij,l+1) = geopot(ij,l) + (g*kappa)*rhodz(ij,l)*theta(ij,l,1)*exner_ik/p_ik
282             ENDDO
283          CASE(thermo_entropy) ! theta is in fact entropy = cpp*log(theta/Treff) = cpp*log(T/Treff) - Rd*log(p/preff)
284             !DIR$ SIMD
285             DO ij=ij_omp_begin_ext,ij_omp_end_ext
286                p_ik = pk(ij,l)
287                temp_ik = Treff*exp((theta(ij,l,1) + Rd*log(p_ik/preff))/cpp)
288                pk(ij,l) = temp_ik
289                ! specific volume v = Rd*T/p = dphi/g/rhodz
290                geopot(ij,l+1) = geopot(ij,l) + (g*Rd)*rhodz(ij,l)*temp_ik/p_ik
291             ENDDO
292          CASE(thermo_moist) ! theta is moist pseudo-entropy per dry air mass
293             DO ij=ij_omp_begin_ext,ij_omp_end_ext
294                p_ik = pk(ij,l)
295                qv = theta(ij,l,2) ! water vaper mixing ratio = mv/md
296                Rmix = Rd+qv*Rv
297                chi = ( theta(ij,l,1) + Rmix*log(p_ik/preff) ) / (cpp + qv*cppv) ! log(T/Treff)
298                temp_ik = Treff*exp(chi)
299                pk(ij,l) = temp_ik
300                ! specific volume v = R*T/p = dphi/g/rhodz
301                ! R = (Rd + qv.Rv)/(1+qv)
302                geopot(ij,l+1) = geopot(ij,l) + g*Rmix*rhodz(ij,l)*temp_ik/(p_ik*(1+qv))
303             ENDDO
304          CASE DEFAULT
305             STOP
306          END SELECT
307       ENDDO
308    END IF
309
310    !ym flush geopot
311    !$OMP BARRIER
312
313    CALL trace_end("compute_geopot")
314
315  END SUBROUTINE compute_geopot_manual
316
317END MODULE compute_geopot_mod
Note: See TracBrowser for help on using the repository browser.