SUEWS API Site
Documentation of SUEWS source code
suews_phys_ohm.f95
Go to the documentation of this file.
1! MODULE OHM_module
2! ! USE allocateArray
3! ! USE data_in
4! ! USE defaultNotUsed
5! ! USE gis_data
6! ! USE sues_data
7! ! USE time
8!
9! IMPLICIT NONE
10! CONTAINS
11!========================================================================================
12SUBROUTINE ohm(qn1, qn_av_prev, dqndt_prev, qn_av_next, dqndt_next, &
13 qn1_S, qn_s_av_prev, dqnsdt_prev, qn_s_av_next, dqnsdt_next, &
14 tstep, dt_since_start, &
15 sfr_surf, nsurf, &
16 Tair_mav_5d, &
17 OHM_coef, &
18 OHM_threshSW, OHM_threshWD, &
19 soilstore_id, SoilStoreCap, state_id, &
20 BldgSurf, WaterSurf, &
21 SnowUse, SnowFrac, &
22 DiagQS, &
23 a1, a2, a3, qs, deltaQi)
24 ! Made by HCW Jan 2015 to replace OHMnew (no longer needed).
25 ! Calculates net storage heat flux (QS) from Eq 4, Grimmond et al. 1991, Atm Env.
26 ! Accounts for variable timesteps in dQ*/dt term.
27 ! BareSoilSurfFraction removed so bare soil is now handled like other surfaces.
28 ! Snow part changed from summer wet to winter wet coefficients.
29 ! Changed -333 checks to -999 checks and added error handling
30 ! Gradient now calculated for t-1 (was previously calculated for t-2).
31 ! TS 28 Jun 2018:
32 ! improved and tested the phase-in method for calculating dqndt
33 ! TS & SG 30 Apr 2018:
34 ! a new calculation scheme of dqndt by using a phase-in approach that releases
35 ! the requirement for storeing multiple qn values for adapting SUEWS into WRF
36 ! TS 07 Aug 2017:
37 ! 1. interface changed to account for explict passing
38 ! 2. calculation refactorization.
39 ! HCW 25 Feb 2015:
40 ! Adapted q1,q2,q3 & r1,r2,r3 for multiple grids
41 ! HCW 14 Dec 2016:
42 ! Thresholds for Summer/Winter and Wet/Dry now provided in input files
43 ! Calculation of dqndt now uses hourly running mean rather than instantaneous values
44 ! To Do:
45 ! - No canyons implemented at the moment [OHM_coef(nsurf+1,,)]
46 !========================================================================================
47
48 IMPLICIT NONE
49 INTEGER, INTENT(in) :: tstep ! time step [s]
50 INTEGER, INTENT(in) :: dt_since_start ! time since simulation starts [s]
51
52 REAL(KIND(1D0)), INTENT(in) :: qn1 ! net all-wave radiation
53 REAL(KIND(1D0)), INTENT(in) :: qn1_S ! net all-wave radiation over snow
54 REAL(KIND(1D0)), INTENT(in) :: sfr_surf(nsurf) ! surface fractions
55 REAL(KIND(1D0)), INTENT(in) :: SnowFrac(nsurf) ! snow fractions of each surface
56 REAL(KIND(1D0)), INTENT(in) :: Tair_mav_5d ! Tair_mav_5d=HDD(id-1,4) HDD at the begining of today (id-1)
57 REAL(KIND(1D0)), INTENT(in) :: OHM_coef(nsurf + 1, 4, 3) ! OHM coefficients
58 REAL(KIND(1D0)), INTENT(in) :: OHM_threshSW(nsurf + 1), OHM_threshWD(nsurf + 1) ! OHM thresholds
59 REAL(KIND(1D0)), INTENT(in) :: soilstore_id(nsurf) ! soil moisture
60 REAL(KIND(1D0)), INTENT(in) :: SoilStoreCap(nsurf) ! capacity of soil store
61 REAL(KIND(1D0)), INTENT(in) :: state_id(nsurf) ! wetness status
62
63 INTEGER, INTENT(in) :: nsurf ! number of surfaces
64 ! INTEGER,INTENT(in)::nsh ! number of timesteps in one hour
65 ! integer,intent(in) :: dt ! current timestep [second]
66 ! integer,intent(INOUT) :: dt0 ! period length for qn1 memory
67 INTEGER, INTENT(in) :: BldgSurf ! code for specific surfaces
68 INTEGER, INTENT(in) :: WaterSurf ! code for specific surfaces
69 INTEGER, INTENT(in) :: SnowUse ! option for snow related calculations
70 INTEGER, INTENT(in) :: DiagQS ! diagnostic option
71
72 REAL(KIND(1D0)), INTENT(in) :: qn_av_prev
73 REAL(KIND(1D0)), INTENT(out) :: qn_av_next
74 REAL(KIND(1D0)), INTENT(in) :: dqndt_prev ! Rate of change of net radiation [W m-2 h-1] at t-1
75 REAL(KIND(1D0)), INTENT(out) :: dqndt_next ! Rate of change of net radiation [W m-2 h-1] at t-1
76 REAL(KIND(1D0)), INTENT(in) :: qn_s_av_prev
77 REAL(KIND(1D0)), INTENT(out) :: qn_s_av_next
78 REAL(KIND(1D0)), INTENT(in) :: dqnsdt_prev ! Rate of change of net radiation [W m-2 h-1] at t-1
79 REAL(KIND(1D0)), INTENT(out) :: dqnsdt_next ! Rate of change of net radiation [W m-2 h-1] at t-1
80
81 REAL(KIND(1D0)), INTENT(out) :: qs ! storage heat flux
82 ! REAL(KIND(1d0)),INTENT(out)::deltaQi(nsurf+1) ! storage heat flux of snow surfaces
83 REAL(KIND(1D0)), INTENT(out) :: deltaQi(nsurf) ! storage heat flux of snow surfaces
84
85 REAL(KIND(1D0)), INTENT(out) :: a1, a2, a3 ! OHM coefficients of grid
86
87 ! REAL(KIND(1d0)):: nsh_nna ! number of timesteps per hour with non -999 values (used for spinup)
88
89 ! REAL(KIND(1d0)):: dqndt !Rate of change of net radiation [W m-2 h-1] at t-1
90 ! REAL(KIND(1d0)):: surfrac !Surface fraction accounting for SnowFrac if appropriate
91
92 REAL(KIND(1D0)) :: deltaQi0 ! temporarily store
93
94 ! REAL(KIND(1d0)):: qn1_store_grid0(nsh), qn1_av_store_grid0(2*nsh+1) ! temporarily store
95
96 !These are now provided in SiteInfo (OHMthresh for Summer/Winter and Wet/Dry)
97 !!real(kind(1d0)):: OHM_TForSummer = 5 !Use summer coefficients if 5-day Tair >= 5 degC
98 !real(kind(1d0)):: OHM_TForSummer = 10 !Use summer coefficients if 5-day Tair >= 10 degC - modified for UK HCW 14 Dec 2015
99 !real(kind(1d0)):: OHM_SMForWet = 0.9 !Use wet coefficients if SM close to soil capacity
100
101 CALL ohm_coef_cal(sfr_surf, nsurf, &
102 tair_mav_5d, ohm_coef, ohm_threshsw, ohm_threshwd, &
103 soilstore_id, soilstorecap, state_id, &
104 bldgsurf, watersurf, &
105 snowuse, snowfrac, &
106 a1, a2, a3)
107 ! WRITE(*,*) '----- OHM coeffs new-----'
108 ! WRITE(*,*) a1,a2,a3
109
110 ! Old OHM calculations (up to v2016a)
111 !! Calculate radiation part ------------------------------------------------------------
112 !qs=NAN !qs = Net storage heat flux [W m-2]
113 !if(qn1>-999) then !qn1 = Net all-wave radiation [W m-2]
114 ! !if(q1>-999.and.q3>-999) then
115 ! !dqndt = 0.5*(q3-q1)*nsh_real !gradient at t-2
116 ! dqndt = 0.5*(qn1-q2_grids(Gridiv))*nsh_real !gradient at t-1
117 !
118 ! !Calculate net storage heat flux
119 ! qs = qn1*a1 + dqndt*a2 + a3 !Eq 4, Grimmond et al. 1991
120 ! !endif
121 ! !q1=q2 !q1 = net radiation at t-2 (at t-3 when q1 used in next timestep)
122 ! !q2=q3 !q2 = net radiation at t-1
123 ! !q3=qn1 !q3 = net radiation at t (at t-1 when q3 used in next timestep)
124 ! q1_grids(Gridiv) = q2_grids(Gridiv) !q1 = net radiation at t-2 (at t-3 when q1 used in next timestep)
125 ! q2_grids(Gridiv) = q3_grids(Gridiv) !q2 = net radiation at t-1
126 ! q3_grids(Gridiv) = qn1 !q3 = net radiation at t (at t-1 when q3 used in next timestep)
127 !else
128 ! call ErrorHint(21,'Bad value for qn1 found during OHM calculation',qn1,NotUsed,notUsedI)
129 !endif
130
131 ! New OHM calculations (v2017a onwards) using running mean (HCW Dec 2016)
132 ! Calculate radiation part ------------------------------------------------------------
133 qs = -999 !qs = Net storage heat flux [W m-2]
134 IF (qn1 > -999) THEN !qn1 = Net all-wave radiation [W m-2]
135 ! Store instantaneous qn1 values for previous hour (qn1_store_grid) and average (qn1_av)
136 ! print*,''
137 ! CALL OHM_dqndt_cal(nsh,qn1,qn1_store_grid,qn1_av_store_grid,dqndt)
138 ! print*, 'old dqndt',dqndt
139 CALL ohm_dqndt_cal_x(tstep, dt_since_start, qn_av_prev, qn1, dqndt_prev, &
140 qn_av_next, dqndt_next)
141 ! print*, 'new dqndt',dqndt
142
143 ! Calculate net storage heat flux
144 CALL ohm_qs_cal(qn1, dqndt_next, a1, a2, a3, qs)
145 IF (diagqs == 1) WRITE (*, *) 'qs: ', qs, 'qn1:', qn1, 'dqndt: ', dqndt_next
146
147 ELSE
148 CALL errorhint(21, 'In SUEWS_OHM.f95: bad value for qn1 found during qs calculation.', qn1, -55.55d0, -55)
149 END IF
150
151 !write(*,*) qs
152 !write(*,*) '--------------------'
153
154 ! Do snow calculations separately -----
155 ! Added by LJ in August 2013
156 IF (snowuse == 1) THEN
157 deltaqi = -999
158 IF (qn1_s > -999) THEN
159 ! Old OHM calculations (commented out HCW Dec 2016)
160 !!if(r1>-999.and.r3>-999) then
161 ! !dqndt = 0.5*(r3-r1)*nsh_real !gradient at t-2
162 ! dqndt = 0.5*(qn1_S-r2_grids(Gridiv))*nsh_real !gradient at t-1
163 ! ! Calculate net storage heat flux for snow surface (winter wet conditions HCW 15/01/2015)
164 ! deltaQi = qn1_S*OHM_coef(nsurf+1,3,1) + dqndt*OHM_coef(nsurf+1,3,2) + OHM_coef(nsurf+1,3,3)
165 !!endif
166 !r1_grids(Gridiv)=r2_grids(Gridiv)
167 !r2_grids(Gridiv)=r3_grids(Gridiv)
168 !r3_grids(Gridiv)=qn1_S
169 ! New OHM calculations
170 ! Store instantaneous qn1 values for previous hour (qn1_store_grid) and average (qn1_av)
171 ! CALL OHM_dqndt_cal(nsh,qn1_S,qn1_S_store_grid,qn1_S_av_store_grid,dqndt)
172
173 CALL ohm_dqndt_cal_x(tstep, dt_since_start, qn_s_av_prev, qn1_s, dqnsdt_prev, &
174 qn_s_av_next, dqnsdt_next)
175
176 ! Calculate net storage heat flux for snow surface (winter wet conditions)
177 CALL ohm_qs_cal(qn1_s, dqnsdt_next, &
178 ohm_coef(nsurf + 1, 3, 1), ohm_coef(nsurf + 1, 3, 2), ohm_coef(nsurf + 1, 3, 3), &
179 deltaqi0)
180 deltaqi = deltaqi0
181
182 ELSE
183 CALL errorhint(21, 'In SUEWS_OHM.f95: bad value for qn1(snow) found during qs calculation.', qn1_s, -55.55d0, -55)
184 END IF
185
186 END IF
187
188 RETURN
189END SUBROUTINE ohm
190!========================================================================================
191
192SUBROUTINE ohm_coef_cal(sfr_surf, nsurf, &
193 Tair_mav_5d, OHM_coef, OHM_threshSW, OHM_threshWD, &
194 soilstore_id, SoilStoreCap, state_id, &
195 BldgSurf, WaterSurf, &
196 SnowUse, SnowFrac, &
197 a1, a2, a3)
198 IMPLICIT NONE
199 INTEGER, INTENT(in) :: &
200 nsurf, & ! number of surfaces
201 SnowUse, & ! option for snow related calculations
202 BldgSurf, WaterSurf ! code for specific surfaces
203 REAL(KIND(1D0)), INTENT(in) :: &
204 sfr_surf(nsurf), & ! surface cover fractions
205 SnowFrac(nsurf), & ! snow fractions of each surface
206 Tair_mav_5d, & ! Tair_mav_5d=HDD(id-1,4) HDD at the begining of today (id-1)
207 OHM_coef(nsurf + 1, 4, 3), &
208 OHM_threshSW(nsurf + 1), OHM_threshWD(nsurf + 1), & ! OHM thresholds
209 soilstore_id(nsurf), & ! soil moisture
210 SoilStoreCap(nsurf), & ! capacity of soil store
211 state_id(nsurf) ! wetness status
212 REAL(KIND(1D0)), INTENT(out) :: a1, a2, a3
213
214 REAL(KIND(1D0)) :: surfrac
215 INTEGER :: i, ii, is
216
217 ! OHM coefficients --------
218 ! Set to zero initially
219 a1 = 0 ![-]
220 a2 = 0 ![h]
221 a3 = 0 ![W m-2]
222 ! -------------------------
223
224 ! Loop through surface types ----------------------------------------------------------
225 DO is = 1, nsurf
226 surfrac = sfr_surf(is)
227
228 ! Use 5-day running mean Tair to decide whether it is summer or winter ----------------
229 IF (tair_mav_5d >= ohm_threshsw(is)) THEN !Summer
230 ii = 0
231 ELSE !Winter
232 ii = 2
233 END IF
234
235 IF (state_id(is) > 0) THEN !Wet surface
236 i = ii + 1
237 ELSE !Dry surface
238 i = ii + 2
239 ! If the surface is dry but SM is close to capacity, use coefficients for wet surfaces
240 IF (is > bldgsurf .AND. is /= watersurf) THEN !Wet soil (i.e. EveTr, DecTr, Grass, BSoil surfaces)
241 IF (soilstore_id(is)/soilstorecap(is) > ohm_threshwd(is)) THEN
242 i = ii + 1
243 END IF
244 END IF
245 END IF
246
247 ! If snow, adjust surface fractions accordingly
248 IF (snowuse == 1 .AND. is /= bldgsurf .AND. is /= watersurf) THEN ! QUESTION: Why is BldgSurf excluded here?
249 surfrac = surfrac*(1 - snowfrac(is))
250 END IF
251
252 ! Calculate the areally-weighted OHM coefficients
253 a1 = a1 + surfrac*ohm_coef(is, i, 1)
254 a2 = a2 + surfrac*ohm_coef(is, i, 2)
255 a3 = a3 + surfrac*ohm_coef(is, i, 3)
256
257 END DO !end of loop over surface types ------------------------------------------------
258END SUBROUTINE ohm_coef_cal
259
260! Updated OHM calculations for WRF-SUEWS coupling (v2018b onwards) weighted mean (TS Apr 2018)
261SUBROUTINE ohm_dqndt_cal_x(dt, dt_since_start, qn1_av_prev, qn1, dqndt_prev, qn1_av_next, dqndt_next)
262 IMPLICIT NONE
263 INTEGER, INTENT(in) :: dt ! time step [s]
264 INTEGER, INTENT(in) :: dt_since_start ! time since simulation starts [s]
265 REAL(KIND(1D0)), INTENT(in) :: qn1 ! new qn1 value [W m-2]
266 REAL(KIND(1D0)), INTENT(in) :: qn1_av_prev ! weighted average of qn1 [W m-2]
267 REAL(KIND(1D0)), INTENT(in) :: dqndt_prev ! dQ* per dt for 60 min [W m-2 h-1]
268 REAL(KIND(1D0)), INTENT(out) :: qn1_av_next ! weighted average of qn1 [W m-2]
269 REAL(KIND(1D0)), INTENT(out) :: dqndt_next ! dQ* per dt for 60 min [W m-2 h-1]
270 REAL(KIND(1D0)), PARAMETER :: dt0_thresh = 3600 ! threshold for period of dqndt0 [s]
271 REAL(KIND(1D0)), PARAMETER :: window_hr = 2 ! window size for Difference calculation [hr]
272
273 INTEGER :: dt0 ! period of dqndt0 [s]
274
275 REAL(KIND(1D0)) :: qn1_av_0 !, qn1_av_start,qn1_av_end
276
277 ! if previous period shorter than dt0_thresh, expand the storage/memory period
278 IF (dt_since_start < dt0_thresh) THEN ! spinup period
279 dt0 = dt_since_start + dt
280
281 ELSE ! effective period
282 dt0 = dt0_thresh
283 END IF
284
285 ! get weighted average at a previous time specified by `window_hr`
286 qn1_av_0 = qn1_av_prev - dqndt_prev*(window_hr - dt/3600.)
287
288 ! averaged qn1 for previous period = dt0_thresh
289 qn1_av_next = (qn1_av_prev*(dt0 - dt) + qn1*dt)/(dt0)
290
291 ! do weighted average to calculate the difference by using the memory value and new forcing value
292 ! NB: keep the output dqndt in [W m-2 h-1]
293 dqndt_next = (qn1_av_next - qn1_av_0)/window_hr
294
295END SUBROUTINE ohm_dqndt_cal_x
296
297! New OHM calculations (v2017a-v2018a) using running mean (HCW Dec 2016)
298SUBROUTINE ohm_dqndt_cal(nsh, qn, qn_store_grid, qn_av_store_grid, dqndt)
299 IMPLICIT NONE
300 INTEGER, INTENT(in) :: nsh ! number of timesteps in one hour
301 REAL(KIND(1D0)), INTENT(in) :: qn
302 REAL(KIND(1D0)), INTENT(inout) :: qn_store_grid(nsh) ! instantaneous qn1 values for previous hour
303 REAL(KIND(1D0)), INTENT(inout) :: qn_av_store_grid(2*nsh + 1) ! average qn1 values for previous hour
304 REAL(KIND(1D0)), INTENT(out) :: dqndt !dQ* per dt for 60 min
305
306 REAL(KIND(1D0)) :: qn_av
307 INTEGER :: nsh_nna
308
309 dqndt = -999 ! initialise as -999
310
311 ! Store instantaneous qn1 values for previous hour (qn1_store_grid) and average (qn1_av)
312 IF (nsh > 1) THEN
313 qn_store_grid = cshift(qn_store_grid, 1) ! shift to left with one place
314 qn_store_grid(nsh) = qn
315 nsh_nna = count(qn_store_grid /= -999, dim=1) !Find how many are not -999s !bug fixed HCW 08 Feb 2017
316 qn_av = sum(qn_store_grid, mask=qn_store_grid /= -999)/nsh_nna
317 ELSEIF (nsh == 1) THEN
318 qn_store_grid(:) = qn
319 qn_av = qn
320 END IF
321 ! Store hourly average values (calculated every timestep) for previous 2 hours
322 IF (nsh > 1) THEN
323 qn_av_store_grid = cshift(qn_av_store_grid, 1)
324 qn_av_store_grid(2*nsh + 1) = qn_av
325 ELSEIF (nsh == 1) THEN
326 qn_av_store_grid(:) = qn_av
327 END IF
328 ! Calculate dQ* per dt for 60 min (using running mean Q* at t hours and (t-2) hours)
329 IF (any(qn_av_store_grid == -999)) THEN
330 dqndt = 0 ! Set dqndt term to zero for spinup
331 ELSE
332 dqndt = 0.5*(qn_av_store_grid((2*nsh + 1)) - qn_av_store_grid(1))
333 END IF
334
335END SUBROUTINE ohm_dqndt_cal
336
337SUBROUTINE ohm_qs_cal(qn1, dqndt, a1, a2, a3, qs)
338 IMPLICIT NONE
339 REAL(KIND(1D0)), INTENT(in) :: qn1, dqndt, a1, a2, a3
340 REAL(KIND(1D0)), INTENT(out) :: qs
341 qs = -999 ! initialise as -999
342 qs = qn1*a1 + dqndt*a2 + a3 !Eq 4, Grimmond et al. 1991
343
344END SUBROUTINE ohm_qs_cal
345
346! END MODULE OHM_module
subroutine errorhint(errh, problemfile, value, value2, valuei)
subroutine ohm_qs_cal(qn1, dqndt, a1, a2, a3, qs)
subroutine ohm_dqndt_cal(nsh, qn, qn_store_grid, qn_av_store_grid, dqndt)
subroutine ohm(qn1, qn_av_prev, dqndt_prev, qn_av_next, dqndt_next, qn1_s, qn_s_av_prev, dqnsdt_prev, qn_s_av_next, dqnsdt_next, tstep, dt_since_start, sfr_surf, nsurf, tair_mav_5d, ohm_coef, ohm_threshsw, ohm_threshwd, soilstore_id, soilstorecap, state_id, bldgsurf, watersurf, snowuse, snowfrac, diagqs, a1, a2, a3, qs, deltaqi)
subroutine ohm_coef_cal(sfr_surf, nsurf, tair_mav_5d, ohm_coef, ohm_threshsw, ohm_threshwd, soilstore_id, soilstorecap, state_id, bldgsurf, watersurf, snowuse, snowfrac, a1, a2, a3)
subroutine ohm_dqndt_cal_x(dt, dt_since_start, qn1_av_prev, qn1, dqndt_prev, qn1_av_next, dqndt_next)