Skip to content

Commit f4548ab

Browse files
author
brasmus
committed
Further work on WG.fwmu.day.precip
1 parent 6074cf4 commit f4548ab

File tree

4 files changed

+128
-126
lines changed

4 files changed

+128
-126
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: esd
2-
Version: 1.10.84
3-
Date: 2024-06-14
2+
Version: 1.10.85
3+
Date: 2024-06-17
44
Title: Climate analysis and empirical-statistical downscaling (ESD) package for monthly and daily data
55
Author: Rasmus E. Benestad, Abdelkader Mezghani, Kajsa M. Parding, Helene B. Erlandsen, Ketil Tunheim, and Cristian Lussana
66
Maintainer: Rasmus E. Benestad <[email protected]>

R/WG.R

Lines changed: 94 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@
100100
#' @param n.spells.year = c('fw','spell') if 'fw' then estimate number of spells according to 365.25 else estimate number of events from \code{\link{spell}}.
101101
#' @param alpha.scaling TRUE scale the low-probability events according to alpha in DOI:10.1088/1748-9326/ab2bb2
102102
#' @param alpha values for alpha-scaling
103-
#' @param ensure.fw TRUE then WG tries to ensure that fw of simulations match those of observations or prescribed by adding or substracting wet days.
103+
#' @param ensure.fw TRUE then WG tries to ensure that fw of simulations match those of observations or prescribed by adding or subtracting wet days.
104+
#' @param w.fw.ac weighting to balance how the wet day occurrences follows seasonal cycle or randomness. 0 - no seasonal cycle; 1000 - mainly determined by climatology (default=30).
105+
#' @param w.mu.ac same as above, but for wet-day mean precipitation (default=10).
104106
#' @param \dots additional arguments
105107
#' @author R.E. Benestad
106108
#' @keywords manip
@@ -128,7 +130,25 @@
128130
#' lines(c(0, max(sy,sz,na.rm=TRUE)), c(0,max(sy,sz,na.rm=TRUE)), lty=2, col='red')
129131
#' points(sy, sz2, col='blue', cex=0.7)
130132
#'
131-
#' ## Test the WG
133+
#'
134+
#' ## Simple simulation of contnued trends in wet-day mean precipitation and frequency
135+
#' mu <- annual(bjornholt,FUN='wetmean',nmin=270) # Avoid missing values (NA)
136+
#' fw <- annual(bjornholt,FUN='wetfreq',nmin=270) # Avoid missing values (NA)
137+
#' mu.trend <- trend(mu)
138+
#' fw.trend <- trend(fw)
139+
#' ## Construct precipitation statistics for input to WG
140+
#' mu2 <- c(mu,zoo(coredata(mu)+coredata(max(mu.trend)-min(mu.trend)),order.by=max(index(mu))+1:length(mu)))
141+
#' fw2 <- c(fw,zoo(coredata(fw)+coredata(max(fw.trend)-min(fw.trend)),order.by=max(index(fw))+1:length(fw)))
142+
#' z <- WG(bjornholt,mu=mu2,fw=fw2,verbose=TRUE)
143+
#' plot(z)
144+
#'
145+
#' #' ## Test the WG
146+
#' z2 <- WG(bjornholt,w.mu.ac=1000,plot=TRUE,verbose=TRUE)
147+
#' plot(aggregate(z2,by=month,FUN='wetmean')); lines(aggregate(bjornholt,by=month,FUN='wetmean'))
148+
#' z3 <- WG(bjornholt,w.fw.ac=1000,plot=TRUE,verbose=TRUE)
149+
#' plot(aggregate(z3,by=month,FUN='wetfreq')); lines(aggregate(bjornholt,by=month,FUN='wetfreq'))
150+
#'
151+
#' ## Test-routine for WG
132152
#' test.WG.fwmu.day.precip()
133153
#' @export WG
134154
WG <- function(x,...) UseMethod("WG")
@@ -162,15 +182,15 @@ WG.FT.day.t2m <- function(x=NULL,...,amean=NULL,asd=NULL,t=NULL,ip=1:4,
162182
x <- ferder
163183
rm('ferder')
164184
}
165-
185+
166186
## Get the daily anomalies and the climatology
167187
xa <- anomaly(x); clim <- x - xa
168-
188+
169189
## KMP 2024-05-31: If amean or asd are NULL, the function fails! Is amean and asd supposed to be the
170190
## annual mean and standard deviation of x? Adding a check to see if amean and asd exist and if not calculate them based on x.
171191
if(is.null(amean)) amean <- as.annual(x, FUN="mean", na.rm=TRUE)
172192
if(is.null(asd)) asd <- as.annual(x, FUN="sd", na.rm=TRUE)
173-
193+
174194
## Define time axis for projection based on the annual mean data either from station or
175195
## downscaled projections
176196
if (is.null(t)) {
@@ -239,15 +259,15 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
239259
plot <- args$plot; if (is.null(plot)) plot <- FALSE
240260
verbose <- args$verbose; if (is.null(verbose)) verbose <- FALSE
241261
mu=args$mu
242-
fw=args$fr
262+
fw=args$fw
243263
t=args$t
244264
threshold <- args$threshold; if (is.null(threshold)) threshold <- 1
245265
alpha.scaling <-args$alpha.scaling
246266
if (is.null(alpha.scaling)) alpha.scaling <- TRUE
247267
## Use alpha scaling estimates from DOI:10.1088/1748-9326/abd4ab - same as in ERL::IDF()
248268
alpha <-args$alpha; if (is.null(alpha)) alpha=c(1.256,0.064)
249269
## Weighting function to determine the degree which the mean seasonal cycle determines the results
250-
w.fw.ac <- args$w.fw.ac; if (is.null(w.fw.ac)) w.fw.ac <- 100
270+
w.fw.ac <- args$w.fw.ac; if (is.null(w.fw.ac)) w.fw.ac <- 30
251271
w.mu.ac <- args$w.mu.ac; if (is.null(w.mu.ac)) w.mu.ac <- 10
252272

253273
if (verbose) print('WG.fwmu.day.precip')
@@ -263,13 +283,10 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
263283
## Estimate climatology for mean seasonal cycle in total precipitation. Use this information
264284
## as a guide for which months to add wet days to ensure correct wet-day frequency fw -
265285
## this is important for locations with a rainy season
266-
# pt.ac <- aggregate(x,month,FUN='mean',na.rm=TRUE)
267-
# pt.ac <- 3 * pt.ac/sum(coredata(pt.ac)) ## Normal distr.: N(1,1) ~[-3,3]
268-
# pt.ac <- approx(1:12,pt.ac,xout = seq(1,12,length=366))$y
269-
## Also find the climatology for the wet-day frequency fw
270286
fw.ac <- aggregate(x,month,FUN='wetfreq',threshold=1,na.rm=TRUE)
271287
fw.ac <- w.fw.ac * fw.ac/sum(coredata(fw.ac)) ## Normal distr.: N(1,1) ~[-3,3]
272288
fw.ac <- approx(1:12,fw.ac,xout = seq(1,12,length=366))$y
289+
273290
## Also find the climatology for the wet-day mean precipitation mu
274291
mu.ac <- aggregate(x,month,FUN='wetmean',threshold=1,na.rm=TRUE)
275292
mu.ac <- w.mu.ac * mu.ac/sum(coredata(mu.ac)) ## Normal distr.: N(1,1) ~[-3,3]
@@ -284,52 +301,22 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
284301
# Number of consecutive wet/dry days
285302
ncd <- subset(spell(x,threshold=threshold),is=1)
286303
## Annual mean number of consecutive wet days
287-
288304
amncwd <- subset(annual(ncd, nmin=30), is=1)
289305
# extract the time interval between the start of each dry spell
290306
dt1 <- diff(julian(as.Date(index(ncd[is.finite(ncd[,1]),1]))))
291307

292-
if (plot) {
293-
## Timing between each precipitation event
294-
dev.new()
295-
par(mfrow=c(2,2),cex.main=0.7)
296-
# f.k <- dgeom(0:max(dt1), prob=1/(mean(dt1)+1))
297-
# hist(dt1,freq=FALSE,col="grey",xlab="days",
298-
# main="The time between the start of each precipitation event",
299-
# sub="Test: Red curve is the fitted geometric distribution")
300-
# lines(0:max(dt1),f.k,lwd=5,col="red")
301-
# grid()
308+
if (plot) {
309+
## Timing between each precipitation event
310+
dev.new()
311+
par(mfrow=c(2,2),cex.main=0.7)
312+
f.k <- dgeom(0:max(dt1), prob=1/(mean(dt1)+1))
313+
hist(dt1,freq=FALSE,col="grey",xlab="days",
314+
main="The time between the start of each precipitation event",
315+
sub="Test: Red curve is the fitted geometric distribution")
316+
lines(0:max(dt1),f.k,lwd=5,col="red")
317+
grid()
302318
}
303319

304-
## Annual mean number of days between start of each rain event
305-
## Remove first and last elements to avoid cut-off problems at start and
306-
## end of the time series
307-
#amndse <- annual(zoo(x=dt1,order.by=index(dt1)))[-c(1,length(dt1))]
308-
309-
## Wet-day spell duration statistics:
310-
#wetsd <- subset(ncd,is=1)
311-
## Remove the first and last estimate to avoid cut-off problems
312-
#wetsd <- subset(wetsd,it=c(FALSE,rep(TRUE,length(wetsd)-2),FALSE))
313-
#amwetsd <- annual(wetsd,FUN='mean',nmin=1)
314-
## Annual number of wet events
315-
#nwes <- aggregate(wetsd,by=year(wetsd),FUN="nv")
316-
# if (plot) {
317-
# ## Number of events per year
318-
# hist(coredata(nwes),breaks=seq(0,100,by=5),freq=FALSE,col="grey",
319-
# main="Number of wet events per year",xlab="days",
320-
# sub="Test: Red curve is the fitted Poisson distribution")
321-
# lines(seq(0,100,by=1),dpois(seq(0,100,by=1),lambda=mean(coredata(nwes))),
322-
# col="red",lwd=3)
323-
# grid()
324-
# }
325-
326-
## Estimate climatology for mean seasonal cycle in total precipitation. Use this information
327-
## as a guide for which months to add wet days to ensure correct wet-day frequency fw
328-
# KMP 2024-05-31: y has not been defined yet and is not an input to the function!
329-
# Is it supposed to be x or was this moved up here from after line 409 where a y is defined?
330-
# pt.ac isn't used anywhere so I am am commenting it out for now.
331-
#pt.ac <- aggregate(y, month, FUN='mean', na.rm=TRUE)
332-
333320
# Wet-day mean: from DS or from observations
334321
if (verbose) print('wet-day mean')
335322
if (is.null(mu))
@@ -342,35 +329,35 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
342329

343330
# Wet-day frequency: from DS or from observations
344331
if (verbose) print('wet-day frequency')
345-
if (is.null(fw)) fw <- zoo(FTscramble(x.fw),order.by=index(x.fw)) else
332+
if (is.null(fw))
333+
fw <- zoo(FTscramble(x.fw),order.by=index(x.fw)) else
346334
## fw is introduced as a change factor
347335
if (length(fw)==1) {
348336
fw <- fw + zoo(FTscramble(x.mu),order.by=index(x.mu))
349337
}
350338
rm('x.fw')
351339

352-
# if (plot) {
353-
# ## Number of events per year
354-
# hist(coredata(ncd),breaks=seq(0,40,by=2),freq=FALSE,col="grey",
355-
# main="Duration of wet spells",xlab="days",
356-
# sub="Test: Red curve is the fitted geometric distribution")
357-
# lines(seq(0,40,by=1),dgeom(seq(0,40,by=1),prob=1/mean(coredata(amncwd))),
358-
# col="red",lwd=3)
359-
# grid()
360-
# }
340+
if (plot) {
341+
## Number of events per year
342+
hist(coredata(ncd),breaks=seq(0,40,by=2),freq=FALSE,col="grey",
343+
main="Duration of wet spells",xlab="days",
344+
sub="Test: Red curve is the fitted geometric distribution")
345+
lines(seq(0,40,by=1),dgeom(seq(0,40,by=1),prob=1/mean(coredata(amncwd))),
346+
col="red",lwd=3)
347+
grid()
348+
}
361349

362350
## Time axis for projection:
363351
if (verbose) print('Time axis for projection')
364352
if (is.null(t)) {
365-
ly <- max(year(mu))
353+
nxy <- range(year(mu))
354+
t <- seq(as.Date(paste0(nxy[1],'-01-01')),as.Date(paste0(nxy[2],'-12-31')),by=1)
355+
## Number of years
366356
ny <- length(rownames(table(year(mu))))
367-
interval <- c(ly-ny+1,ny)
368-
t <- index(x)
369-
t <- t - julian(as.Date(t[1])) +
370-
julian(as.Date(paste(interval[1],month(x)[1],day(x)[1],sep='-')))
371-
if (verbose) print(interval)
357+
if (verbose) print(range(t))
372358
}
373-
n <- length(t)
359+
## Number of days
360+
nd <- length(t)
374361
yrs <- as.numeric(rownames(table(year(t))))
375362

376363
# Estimate the annual number of rainy days:
@@ -383,29 +370,32 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
383370
mu.err <- mu/sqrt(anwd - 1)
384371

385372
# set up a record with no rain:
386-
z <- zoo(rep(0,length(t)),order.by=t)
373+
z <- zoo(rep(0,nd),order.by=t)
387374

388375
# add rain events:
389-
if (verbose) print(paste('loop over year:',1,'-',ny))
376+
if (verbose) print(paste('loop over year:',1,'-',ny,'number of days=',nd,length(z),
377+
'length(mu)=',length(mu),'length(fw)=',length(fw),length(anwd)))
390378
for ( i in 1:ny ) {
391-
## Julian day
392-
jd <- 1:366
393379

394380
## Duration of wet events
395-
ncwd <- rgeom(366,prob=1/(amncwd[i])) + 1
381+
if (i <= length(amncwd)) ncwd <- rgeom(366,prob=1/amncwd[i]) + 1 else
382+
ncwd <- rgeom(366,prob=1/mean(amncwd,na.rm=TRUE)) + 1
396383

397384
## White noise to introduce stochastic weather
398385
wn <- rnorm(366)
399386
## Find most suitable times of the year with stochastic influence
400-
ij <- order(fw.ac + wn,decreasing=FALSE)
401-
## Use jd as index for timing wet events
402-
jd <- jd[ij]
387+
fw.ac.wn <- fw.ac + wn
388+
## Use ij as index for timing wet events
389+
ij <- order(fw.ac.wn,decreasing=TRUE)
403390

404391
## Repeat for the procedure using climatology and stochastic weather for mu,
405392
## but with 1/3 less weight on climatology and more on random order
406-
## kl is the julian day ordered by intensity of rainfall
407-
kl <- order(mu.ac + rnorm(366),decreasing=FALSE)
408-
kd <- (1:366)[kl]
393+
## kl is the julian day ordered by seasonal mean intensity of rainfall plus random noise
394+
## The first indices tend to represent higher intensities
395+
mu.ac.wn <- mu.ac + rnorm(366)
396+
## Use kl as index for timing amounts
397+
kl <- order(mu.ac.wn,decreasing=TRUE)
398+
409399
if ( (plot) & (i==1) ) {
410400
plot(ij,main='fw/mu sorting',xlab='index',ylab='day',type='b')
411401
points(kl,col='blue',pch=19,type='b')
@@ -417,59 +407,48 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
417407
while ( (length(wet) < anwd[i]) & (nes <= 366) ) {
418408
## Check whether the selected days are available: start with the first julian day in the year
419409
idy1 <- 1
410+
## TRUE if found available sequence of wet days
420411
d.available <- FALSE
421412
## We search the days in the year for sequences that include the wet spell duration
422413
## padded by dry days
423414
while( (!d.available) & (idy1 <= 366) ) {
424415
## sequence of days: wet spell padded by dry days
425-
iseq <- jd[idy1] + seq(0,ncwd[1]+1,by=1)
426-
if (length(intersect(iseq,jd))==length(iseq)) d.available <- TRUE else
416+
iseq <- ij[idy1] + seq(-1,ncwd[1]+1,by=1)
417+
if (length(intersect(iseq,ij))==length(iseq)) d.available <- TRUE else
427418
idy1 <- idy1 + 1
428419
}
429420
## If no available sequence of days was found, then pick just random individual days available
430421
## from the pool of remaining days
431422
if (!d.available) {
432-
iseq <- jd[seq(1,length(ncwd[1])+2,by=1)]
423+
iseq <- ij[seq(1,length(ncwd[1])+2,by=1)]
433424
}
434-
## Check that dry and wet contain valid julian days from jd also, if there are elements
435-
## out of sample, then add new random elements from jd
425+
## Check that dry and wet contain valid julian days from ij also, if there are elements
426+
## out of sample, then add new random elements from ij
436427
nseq <- length(iseq)
437-
iseq <- intersect(iseq,jd)
438-
dseq <- setdiff(iseq,jd)
428+
iseq <- intersect(iseq,ij)
429+
dseq <- setdiff(iseq,ij)
439430
diffseq <- nseq - length(iseq)
440431
if (diffseq > 0) iseq <- c(iseq,dseq[sort(rnorm(length(dseq)))][1:diffseq])
441-
if (nseq != length(iseq)) browser()
442432
## Once a suitable sequence of days have been located, use it to define wet spell padded
443433
## with dry days
444-
dry <- c( dry, jd[iseq[c(1,length(iseq))]] )
445-
wet <- c( wet, jd[iseq[-c(1,length(iseq))]] )
434+
435+
dry <- c( dry, iseq[c(1,length(iseq))] )
436+
wet <- c( wet, iseq[2:(length(iseq)-1)] )
446437

447438
## Remove duplicates - for some reason, there are some of them...
448439
ndupl <- sum(duplicated(c(dry,wet)))
449440
wet <- wet[!duplicated(wet)]
450441
dry <- dry[!duplicated(dry)]
451442

452443
## Remove used indices and used wet-spell duration
453-
jd <- jd[!is.element(jd,intersect(c(dry,wet),jd))]; ncwd <- ncwd[-1]
454-
# if (verbose) print(paste(length(dry),'dry days,',length(wet),'wet days =',anwd[i],': ',
455-
# length(jd),'remaining days,',length(ncwd),'spell lengths,',
456-
# length(iseq),'length of day sequence, #event=',nes,', duplicated',
457-
# ndupl,idy1,d.available))
458-
459-
# inboth <- intersect(wet,dry)
460-
## If cases are classified as both wet and dry, then set them to dry and
461-
## add new wet days
462-
# if (length(inboth)>0) {
463-
# wet <- wet[!is.element(wet,inboth)]
464-
# wet <- c(wet,jd[1:length(inboth)])
465-
# jd <- jd[-c(1:length(inboth))]
466-
# }
444+
ij <- ij[!is.element(ij,intersect(c(dry,wet),ij))]; ncwd <- ncwd[-1]
445+
467446
## Increment number of events
468447
nes <- nes + 1
469448
}
470449

471450
## Finish dividing all the 366 days into wet and dry
472-
dry <- sort(c(dry,jd)); wet <- sort(wet); rm('jd')
451+
dry <- sort(c(dry,ij)); wet <- sort(wet)
473452
## This should not happen, but ...
474453
dry <- dry[!duplicated(dry)]; wet <- wet[!duplicated(wet)]
475454
## deal with cases where days are classified as both dry and wet
@@ -484,12 +463,11 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
484463
swap <- order(rnorm(length(wet)))[1:nwdd]
485464
dry <- sort(c(dry,wet[swap])); wet <- sort(wet[-swap])
486465
}
487-
# if (verbose) print(paste(length(dry),'dry days,',length(wet),'wet days =',anwd[i],': ',
488-
# length(dry)+length(wet),'assigned days, #event=',
489-
# nes,', duplicated',sum(duplicated(wet))))
490466

467+
if (i > length(mu)) browser()
491468
## The wet-day mean precipitation amount
492469
if (!is.finite(mu[i])) mu[i] <- mean(mu,na.rm=TRUE)
470+
493471
## The daily amounts for wet days - first sort the data according to magnitude
494472
## then shuffle them according to a mix of chance and mu climatology
495473
y <- sort(round(rexp(366,rate=1/coredata(mu[i])),1),decreasing = TRUE) + threshold
@@ -518,19 +496,17 @@ WG.fwmu.day.precip <- function(x=NULL,...) {
518496
}
519497
# add rain to the appropriate year:
520498
ii <- is.element(year(t),yrs[i])
521-
rain <- rep(0,sum(ii)); iii <- 0
522-
#if (verbose) print(wet)
523-
rain[wet] <- y[kl[wet]]
524-
525-
## Make it a zoo object to assign months
526-
#if (verbose) print(range(as.Date(paste0(year(fw[i])-1,'-12-31'))+1:length(rain)))
527-
#rain <- zoo(rain,order.by=as.Date(paste0(year(fw[i])-1,'-12-31'))+1:length(rain))
499+
rain <- rep(0,sum(ii))
500+
## the amounts in y are sorted from high to low values - make sure y has a seasonality that
501+
## reflects climatology. Insert the wet days of y into rain
502+
rain[kl] <- y
503+
rain[dry] <- 0
528504

529-
if (verbose) print(paste(yrs[i],'tot rain',round(sum(rain,na.rm=TRUE)),
505+
if (verbose) print(paste(yrs[i],i,'tot rain',round(sum(rain,na.rm=TRUE)),
530506
'mm/year, #wet days=',length(wet),'=',sum(rain >= 1),'n*fw[i]=',anwd[i],
531-
'mu[i]=',round(mu[i],1),'#events=',nes,'ii:',sum(ii),
532-
' [',min((1:n)[ii]),',',max((1:n)[ii]),']'))
533-
z[ii] <- rain
507+
'mu[i]=',round(mu[i],1),'#events=',nes,'ii:',sum(ii),length(rain),
508+
' [',min((1:nd)[ii]),',',max((1:nd)[ii]),']'))
509+
z[ii] <- rain[1:sum(ii)]
534510
}
535511
z <- zoo(z,order.by=t)
536512
class(z) <- class(x)

0 commit comments

Comments
 (0)