Skip to content

Commit 99b0924

Browse files
Merge branch 'main' into qceff_dartlab
2 parents bd2a8cc + 7784ed0 commit 99b0924

39 files changed

+5080
-682
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ test_normal_dist
207207
test_beta_dist
208208
test_kde_dist
209209
test_window
210+
test_force_bounds
210211

211212
# Directories to NOT IGNORE ... same as executable names
212213
# as far as I know, these must be listed after the executables

CHANGELOG.rst

+21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,27 @@ individual files.
2222

2323
The changes are now listed with the most recent at the top.
2424

25+
**January 23 2025 :: Pangu-DART. Tag v11.9.0**
26+
27+
- Pangu-Weather ML model DART interface
28+
29+
*contributed by Nuo Chen, University of Oklahoma*
30+
31+
**January 22 2025 :: Bug-fix: Gamma and Beta Distributions. Tag v11.8.9**
32+
33+
Bug fixes:
34+
35+
- Beta distribution only supporting standard Beta, bounded 0-1.
36+
- Gamma distribution only supporting standard, lower bound 0.
37+
- Beta and Gamma bounds are forced in the QCEFF table.
38+
39+
Updates:
40+
41+
- Explicitly setting distribution type, now have UNSET.
42+
- Message about failing to converge changed to E_ALLMSG to be visible
43+
on all mpi ranks.
44+
- remove unused test_obs directory
45+
2546
**January 14 2025 :: Bug-fix MOM6 potential temperature. Tag v11.8.8**
2647

2748
- MOM6 model_interpolate for potential temperature

assimilation_code/modules/assimilation/algorithm_info_mod.f90

+30
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,19 @@ subroutine read_qceff_table(qceff_table_filename)
217217
case ('BOUNDED_NORMAL_RH_DISTRIBUTION')
218218
qceff_table_data(row)%probit_inflation%dist_type = BOUNDED_NORMAL_RH_DISTRIBUTION
219219
case ('GAMMA_DISTRIBUTION')
220+
! Force standard Gamma distribution
220221
qceff_table_data(row)%probit_inflation%dist_type = GAMMA_DISTRIBUTION
222+
qceff_table_data(row)%probit_inflation%bounded_above = .false.
223+
qceff_table_data(row)%probit_inflation%bounded_below = .true.
224+
qceff_table_data(row)%probit_inflation%upper_bound = MISSING_R8
225+
qceff_table_data(row)%probit_inflation%lower_bound = 0.0_r8
221226
case ('BETA_DISTRIBUTION')
227+
! Force standard Beta distribution
222228
qceff_table_data(row)%probit_inflation%dist_type = BETA_DISTRIBUTION
229+
qceff_table_data(row)%probit_inflation%bounded_above = .true.
230+
qceff_table_data(row)%probit_inflation%bounded_below = .true.
231+
qceff_table_data(row)%probit_inflation%upper_bound = 1.0_r8
232+
qceff_table_data(row)%probit_inflation%lower_bound = 0.0_r8
223233
case ('LOG_NORMAL_DISTRIBUTION')
224234
qceff_table_data(row)%probit_inflation%dist_type = LOG_NORMAL_DISTRIBUTION
225235
case ('UNIFORM_DISTRIBUTION')
@@ -242,9 +252,19 @@ subroutine read_qceff_table(qceff_table_filename)
242252
case ('BOUNDED_NORMAL_RH_DISTRIBUTION')
243253
qceff_table_data(row)%probit_state%dist_type = BOUNDED_NORMAL_RH_DISTRIBUTION
244254
case ('GAMMA_DISTRIBUTION')
255+
! Force standard Gamma distribution
245256
qceff_table_data(row)%probit_state%dist_type = GAMMA_DISTRIBUTION
257+
qceff_table_data(row)%probit_state%bounded_above = .false.
258+
qceff_table_data(row)%probit_state%bounded_below = .true.
259+
qceff_table_data(row)%probit_state%upper_bound = MISSING_R8
260+
qceff_table_data(row)%probit_state%lower_bound = 0.0_r8
246261
case ('BETA_DISTRIBUTION')
262+
! Force standard Beta distribution
247263
qceff_table_data(row)%probit_state%dist_type = BETA_DISTRIBUTION
264+
qceff_table_data(row)%probit_state%bounded_above = .true.
265+
qceff_table_data(row)%probit_state%bounded_below = .true.
266+
qceff_table_data(row)%probit_state%upper_bound = 1.0_r8
267+
qceff_table_data(row)%probit_state%lower_bound = 0.0_r8
248268
case ('LOG_NORMAL_DISTRIBUTION')
249269
qceff_table_data(row)%probit_state%dist_type = LOG_NORMAL_DISTRIBUTION
250270
case ('UNIFORM_DISTRIBUTION')
@@ -266,9 +286,19 @@ subroutine read_qceff_table(qceff_table_filename)
266286
case ('BOUNDED_NORMAL_RH_DISTRIBUTION')
267287
qceff_table_data(row)%probit_extended_state%dist_type = BOUNDED_NORMAL_RH_DISTRIBUTION
268288
case ('GAMMA_DISTRIBUTION')
289+
! Force standard Gamma distribution
269290
qceff_table_data(row)%probit_extended_state%dist_type = GAMMA_DISTRIBUTION
291+
qceff_table_data(row)%probit_extended_state%bounded_above = .false.
292+
qceff_table_data(row)%probit_extended_state%bounded_below = .true.
293+
qceff_table_data(row)%probit_extended_state%upper_bound = MISSING_R8
294+
qceff_table_data(row)%probit_extended_state%lower_bound = 0.0_r8
270295
case ('BETA_DISTRIBUTION')
296+
! Force standard Beta distribution
271297
qceff_table_data(row)%probit_extended_state%dist_type = BETA_DISTRIBUTION
298+
qceff_table_data(row)%probit_extended_state%bounded_above = .true.
299+
qceff_table_data(row)%probit_extended_state%bounded_below = .true.
300+
qceff_table_data(row)%probit_extended_state%upper_bound = 1.0_r8
301+
qceff_table_data(row)%probit_extended_state%lower_bound = 0.0_r8
272302
case ('LOG_NORMAL_DISTRIBUTION')
273303
qceff_table_data(row)%probit_extended_state%dist_type = LOG_NORMAL_DISTRIBUTION
274304
case ('UNIFORM_DISTRIBUTION')

assimilation_code/modules/assimilation/assim_tools_mod.f90

+2-2
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,7 @@ subroutine obs_increment_gamma(ens, ens_size, prior_mean, prior_var, obs, obs_va
10651065
! Compute the prior quantiles of each ensemble member in the prior gamma distribution
10661066
call gamma_mn_var_to_shape_scale(prior_mean, prior_var, prior_shape, prior_scale)
10671067
do i = 1, ens_size
1068-
q(i) = gamma_cdf(ens(i), prior_shape, prior_scale, .true., .false., 0.0_r8, missing_r8)
1068+
q(i) = gamma_cdf(ens(i), prior_shape, prior_scale)
10691069
end do
10701070

10711071
! Compute the statistics of the continous posterior distribution
@@ -1082,7 +1082,7 @@ subroutine obs_increment_gamma(ens, ens_size, prior_mean, prior_var, obs, obs_va
10821082

10831083
! Now invert the quantiles with the posterior distribution
10841084
do i = 1, ens_size
1085-
post(i) = inv_gamma_cdf(q(i), post_shape, post_scale, .true., .false., 0.0_r8, missing_r8)
1085+
post(i) = inv_gamma_cdf(q(i), post_shape, post_scale)
10861086
end do
10871087

10881088
obs_inc = post - ens

assimilation_code/modules/assimilation/beta_distribution_mod.f90

+53-38
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
! Thanks to Chris Riedel who developed the methods in this module.
66

7+
! This module only supports standard beta distributions for which the
8+
! lower bound is 0 and the upper bound is 1.
9+
710
module beta_distribution_mod
811

912
use types_mod, only : r8, PI, missing_r8
@@ -12,7 +15,7 @@ module beta_distribution_mod
1215

1316
use random_seq_mod, only : random_seq_type, random_uniform
1417

15-
use distribution_params_mod, only : distribution_params_type
18+
use distribution_params_mod, only : distribution_params_type, BETA_DISTRIBUTION
1619

1720
use normal_distribution_mod, only : inv_cdf
1821

@@ -36,11 +39,11 @@ subroutine test_beta
3639

3740
! This routine provides limited tests of the numerics in this module. It begins
3841
! by comparing a handful of cases of the pdf and cdf to results from Matlab. It
39-
! then tests the quality of the inverse cdf for a single shape/scale pair. Failing
42+
! then tests the quality of the inverse cdf for a single alpha/beta pair. Failing
4043
! these tests suggests a serious problem. Passing them does not indicate that
4144
! there are acceptable results for all possible inputs.
4245

43-
real(r8) :: x, y, p, inv
46+
real(r8) :: x, y, inv
4447
real(r8) :: alpha, beta, max_diff
4548
integer :: i
4649

@@ -61,41 +64,37 @@ subroutine test_beta
6164
! Compare to matlab
6265
write(*, *) 'Absolute value of differences should be less than 1e-15'
6366
do i = 1, 7
64-
pdf_diff(i) = beta_pdf(mx(i), malpha(i), mbeta(i)) - mpdf(i)
65-
cdf_diff(i) = beta_cdf(mx(i), malpha(i), mbeta(i), 0.0_r8, 1.0_r8) - mcdf(i)
67+
pdf_diff(i) = abs(beta_pdf(mx(i), malpha(i), mbeta(i)) - mpdf(i))
68+
cdf_diff(i) = abs(beta_cdf(mx(i), malpha(i), mbeta(i)) - mcdf(i))
6669
write(*, *) i, pdf_diff(i), cdf_diff(i)
6770
end do
68-
if(abs(maxval(pdf_diff)) < 1e-15_r8 .and. abs(maxval(cdf_diff)) < 1e-15_r8) then
71+
if(maxval(pdf_diff) < 1e-15_r8 .and. maxval(cdf_diff) < 1e-15_r8) then
6972
write(*, *) 'Matlab Comparison Tests: PASS'
7073
else
7174
write(*, *) 'Matlab Comparison Tests: FAIL'
7275
endif
7376

74-
7577
! Test many x values for cdf and inverse cdf for a single set of alpha and beta
7678
alpha = 5.0_r8
7779
beta = 2.0_r8
7880

7981
max_diff = -1.0_r8
8082
do i = 0, 1000
8183
x = i / 1000.0_r8
82-
p = beta_pdf(x, alpha, beta)
83-
y = beta_cdf(x, alpha, beta, 0.0_r8, 1.0_r8)
84-
inv = inv_beta_cdf(y, alpha, beta, 0.0_r8, 1.0_r8)
84+
y = beta_cdf(x, alpha, beta)
85+
inv = inv_beta_cdf(y, alpha, beta)
8586
max_diff = max(abs(x - inv), max_diff)
8687
end do
8788

8889
write(*, *) '----------------------------'
8990
write(*, *) 'max difference in inversion is ', max_diff
9091
write(*, *) 'max difference should be less than 1e-14'
91-
9292
if(max_diff < 1e-14_r8) then
9393
write(*, *) 'Inversion Tests: PASS'
9494
else
9595
write(*, *) 'Inversion Tests: FAIL'
9696
endif
9797

98-
9998
end subroutine test_beta
10099

101100
!-----------------------------------------------------------------------
@@ -106,20 +105,25 @@ function inv_beta_cdf_params(quantile, p) result(x)
106105
real(r8), intent(in) :: quantile
107106
type(distribution_params_type), intent(in) :: p
108107

108+
! Only standard beta is currently supported. Fail if bounds are not 0 and 1
109+
if(p%lower_bound /= 0.0_r8 .or. p%upper_bound /= 1.0_r8) then
110+
errstring = 'Only standard beta distribution with bounds of 0 and 1 is supported'
111+
call error_handler(E_ERR, 'inv_beta_cdf_params', errstring, source)
112+
endif
113+
109114
x = inv_cdf(quantile, beta_cdf_params, inv_beta_first_guess_params, p)
110115

111116
end function inv_beta_cdf_params
112117

113118
!-----------------------------------------------------------------------
114119

115-
function inv_beta_cdf(quantile, alpha, beta, lower_bound, upper_bound) result(x)
120+
function inv_beta_cdf(quantile, alpha, beta) result(x)
116121

117122
real(r8) :: x
118123
real(r8), intent(in) :: quantile
119124
real(r8), intent(in) :: alpha, beta
120-
real(r8), intent(in) :: lower_bound, upper_bound
121125

122-
! Given a quantile, finds the value of x for which the scaled beta cdf
126+
! Given a quantile, finds the value of x for which the beta cdf
123127
! with alpha and beta has approximately this quantile
124128

125129
type(distribution_params_type) :: p
@@ -129,15 +133,13 @@ function inv_beta_cdf(quantile, alpha, beta, lower_bound, upper_bound) result(x)
129133
call error_handler(E_ERR, 'inv_beta_cdf', errstring, source)
130134
endif
131135

136+
! Load the p type for the generic cdf calls
132137
p%params(1) = alpha; p%params(2) = beta
133-
! Beta must be bounded on both sides
134-
p%lower_bound = lower_bound; p%upper_bound = upper_bound
138+
p%bounded_below = .true.; p%bounded_above = .true.
139+
p%lower_bound = 0.0_r8; p%upper_bound = 1.0_r8
135140

136141
x = inv_beta_cdf_params(quantile, p)
137142

138-
! Undo the scaling
139-
x = x * (upper_bound - lower_bound) + lower_bound
140-
141143
end function inv_beta_cdf
142144

143145
!---------------------------------------------------------------------------
@@ -184,25 +186,34 @@ function beta_cdf_params(x, p)
184186
real(r8), intent(in) :: x
185187
type(distribution_params_type), intent(in) :: p
186188

189+
! A translation routine that is required to use the generic cdf optimization routine
190+
! Extracts the appropriate information from the distribution_params_type that is needed
191+
! for a call to the function beta_cdf below.
192+
187193
real(r8) :: alpha, beta
188194

195+
! Only standard beta is currently supported. Fail if bounds are not 0 and 1
196+
if(p%lower_bound /= 0.0_r8 .or. p%upper_bound /= 1.0_r8) then
197+
errstring = 'Only standard beta distribution with bounds of 0 and 1 is supported'
198+
call error_handler(E_ERR, 'beta_cdf_params', errstring, source)
199+
endif
200+
189201
alpha = p%params(1); beta = p%params(2)
190-
beta_cdf_params = beta_cdf(x, alpha, beta, p%lower_bound, p%upper_bound)
202+
beta_cdf_params = beta_cdf(x, alpha, beta)
191203

192204
end function beta_cdf_params
193205

194206
!---------------------------------------------------------------------------
195207

196-
function beta_cdf(x, alpha, beta, lower_bound, upper_bound)
208+
function beta_cdf(x, alpha, beta)
197209

198210
! Returns the cumulative distribution of a beta function with alpha and beta
199211
! at the value x
200212

201-
! Returns a large negative value if called with illegal values
213+
! Returns a failed_value if called with illegal values
202214

203215
real(r8) :: beta_cdf
204216
real(r8), intent(in) :: x, alpha, beta
205-
real(r8), intent(in) :: lower_bound, upper_bound
206217

207218
! Parameters must be positive
208219
if(alpha <= 0.0_r8 .or. beta <= 0.0_r8) then
@@ -251,7 +262,7 @@ function random_beta(r, alpha, beta)
251262
! Draw from U(0, 1) to get a quantile
252263
quantile = random_uniform(r)
253264
! Invert cdf to get a draw from beta
254-
random_beta = inv_beta_cdf(quantile, alpha, beta, 0.0_r8, 1.0_r8)
265+
random_beta = inv_beta_cdf(quantile, alpha, beta)
255266

256267
end function random_beta
257268

@@ -339,24 +350,25 @@ function inv_beta_first_guess_params(quantile, p)
339350
real(r8), intent(in) :: quantile
340351
type(distribution_params_type), intent(in) :: p
341352

353+
! A translation routine that is required to use the generic first_guess for
354+
! the cdf optimization routine.
355+
! Extracts the appropriate information from the distribution_params_type that is needed
356+
! for a call to the function inv_beta_first_guess below.
357+
342358
real(r8) :: alpha, beta
343359

344360
alpha = p%params(1); beta = p%params(2)
345-
inv_beta_first_guess_params = inv_beta_first_guess(quantile, alpha, beta, &
346-
p%bounded_below, p%bounded_above, p%lower_bound, p%upper_bound)
361+
inv_beta_first_guess_params = inv_beta_first_guess(quantile, alpha, beta)
347362

348363
end function inv_beta_first_guess_params
349364

350365
!---------------------------------------------------------------------------
351366

352-
function inv_beta_first_guess(x, alpha, beta, &
353-
bounded_below, bounded_above, lower_bound, upper_bound)
367+
function inv_beta_first_guess(x, alpha, beta)
354368

355369
real(r8) :: inv_beta_first_guess
356370
real(r8), intent(in) :: x
357371
real(r8), intent(in) :: alpha, beta
358-
logical, intent(in) :: bounded_below, bounded_above
359-
real(r8), intent(in) :: lower_bound, upper_bound
360372

361373
! Need some sort of first guess, should be smarter here
362374
! For starters, take the mean for this alpha and beta
@@ -389,19 +401,22 @@ end subroutine beta_alpha_beta
389401

390402
!---------------------------------------------------------------------------
391403

392-
subroutine set_beta_params_from_ens(ens, num, lower_bound, upper_bound, p)
404+
subroutine set_beta_params_from_ens(ens, num, p)
393405

394-
integer, intent(in) :: num
395-
real(r8), intent(in) :: ens(num)
396-
real(r8), intent(in) :: lower_bound, upper_bound
397-
type(distribution_params_type), intent(inout) :: p
406+
integer, intent(in) :: num
407+
real(r8), intent(in) :: ens(num)
408+
type(distribution_params_type), intent(out) :: p
398409

399410
real(r8) :: alpha, beta
400411

412+
! Set up the description of the beta distribution defined by the ensemble
413+
p%distribution_type = BETA_DISTRIBUTION
414+
401415
! Set the bounds info
402-
p%lower_bound = lower_bound; p%upper_bound = upper_bound
416+
p%bounded_below = .true.; p%bounded_above = .true.
417+
p%lower_bound = 0.0_r8; p%upper_bound = 1.0_r8
403418

404-
! Get alpha and beta for the scaled ensemble
419+
! Get alpha and beta for the ensemble
405420
call beta_alpha_beta(ens, num, alpha, beta)
406421
p%params(1) = alpha
407422
p%params(2) = beta

assimilation_code/modules/assimilation/distribution_params_mod.f90

+11-10
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,8 @@ module distribution_params_mod
77
implicit none
88
private
99

10-
type distribution_params_type
11-
integer :: distribution_type
12-
logical :: bounded_below, bounded_above
13-
real(r8) :: lower_bound, upper_bound
14-
real(r8) :: params(2)
15-
integer :: ens_size
16-
real(r8), allocatable :: ens(:)
17-
real(r8), allocatable :: more_params(:)
18-
end type
19-
2010
! Defining parameter strings for different prior distributions that can be used for probit transform
11+
integer, parameter :: UNSET = -1
2112
integer, parameter :: NORMAL_DISTRIBUTION = 1
2213
integer, parameter :: BOUNDED_NORMAL_RH_DISTRIBUTION = 2
2314
integer, parameter :: GAMMA_DISTRIBUTION = 3
@@ -27,6 +18,16 @@ module distribution_params_mod
2718
integer, parameter :: PARTICLE_FILTER_DISTRIBUTION = 7
2819
integer, parameter :: KDE_DISTRIBUTION = 8
2920

21+
type distribution_params_type
22+
integer :: distribution_type = UNSET
23+
logical :: bounded_below, bounded_above
24+
real(r8) :: lower_bound, upper_bound
25+
real(r8) :: params(2)
26+
integer :: ens_size
27+
real(r8), allocatable :: ens(:)
28+
real(r8), allocatable :: more_params(:)
29+
end type
30+
3031
public :: distribution_params_type, deallocate_distribution_params, &
3132
NORMAL_DISTRIBUTION, BOUNDED_NORMAL_RH_DISTRIBUTION, GAMMA_DISTRIBUTION, BETA_DISTRIBUTION, &
3233
LOG_NORMAL_DISTRIBUTION, UNIFORM_DISTRIBUTION, PARTICLE_FILTER_DISTRIBUTION, &

0 commit comments

Comments
 (0)