Skip to content

Commit 756dcf2

Browse files
authored
Merge pull request #28 from ecmwf/feature/cpf-dev
Enhancements to CPF
2 parents 1e3c54a + 2c67c46 commit 756dcf2

File tree

2 files changed

+394
-28
lines changed

2 files changed

+394
-28
lines changed

src/earthkit/meteo/extreme/array/cpf.py

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,13 @@
1010
import numpy as np
1111

1212

13-
def cpf(clim, ens, sort_clim=True, sort_ens=True):
14-
"""Compute Crossing Point Forecast (CPF)
15-
16-
WARNING: this code is experimental, use at your own risk!
17-
18-
Parameters
19-
----------
20-
clim: numpy array (nclim, npoints)
21-
Per-point climatology
22-
ens: numpy array (nens, npoints)
23-
Ensemble forecast
24-
sort_clim: bool
25-
If True, sort the climatology first
26-
sort_ens: bool
27-
If True, sort the ensemble first
28-
29-
Returns
30-
-------
31-
numpy array (npoints)
32-
CPF values
33-
"""
13+
def _cpf(clim, ens, epsilon=None):
3414
nclim, npoints = clim.shape
35-
nens, npoints_ens = ens.shape
36-
assert npoints == npoints_ens
15+
nens, _ = ens.shape
3716

3817
cpf = np.ones(npoints, dtype=np.float32)
3918
mask = np.zeros(npoints, dtype=np.bool_)
4019

41-
if sort_clim:
42-
clim = np.sort(clim, axis=0)
43-
if sort_ens:
44-
ens = np.sort(ens, axis=0)
45-
4620
for icl in range(1, nclim - 1):
4721
# quantile level of climatology
4822
tau_c = icl / (nclim - 1.0)
@@ -94,4 +68,58 @@ def cpf(clim, ens, sort_clim=True, sort_ens=True):
9468
# speed up process
9569
break
9670

71+
if epsilon is not None:
72+
# ens is assumed to be sorted at this point
73+
mask = ens[-1, :] < epsilon
74+
cpf[mask] = 0.0
75+
9776
return cpf
77+
78+
79+
def cpf(clim, ens, sort_clim=True, sort_ens=True, epsilon=None, symmetric=False):
80+
"""Compute Crossing Point Forecast (CPF)
81+
82+
WARNING: this code is experimental, use at your own risk!
83+
84+
Parameters
85+
----------
86+
clim: numpy array (nclim, npoints)
87+
Per-point climatology
88+
ens: numpy array (nens, npoints)
89+
Ensemble forecast
90+
sort_clim: bool
91+
If True, sort the climatology first
92+
sort_ens: bool
93+
If True, sort the ensemble first
94+
epsilon: float or None
95+
If set, use this as a threshold for low-signal regions. Ignored if
96+
`symmetric` is True
97+
symmetric: bool
98+
If True, make CPF values below 0.5 use a symmetric computation (CPF of
99+
opposite values)
100+
101+
Returns
102+
-------
103+
numpy array (npoints)
104+
CPF values
105+
"""
106+
_, npoints = clim.shape
107+
_, npoints_ens = ens.shape
108+
assert npoints == npoints_ens
109+
110+
if sort_clim:
111+
clim = np.sort(clim, axis=0)
112+
if sort_ens:
113+
ens = np.sort(ens, axis=0)
114+
115+
if symmetric:
116+
epsilon = None
117+
118+
cpf_direct = _cpf(clim, ens, epsilon)
119+
120+
if symmetric:
121+
cpf_reverse = _cpf(-clim[::-1, :], -ens[::-1, :])
122+
mask = cpf_direct < 0.5
123+
cpf_direct[mask] = 1 - cpf_reverse[mask]
124+
125+
return cpf_direct

0 commit comments

Comments
 (0)