From 23b03783b18ca7f7ea978c1c7e50df87e7785182 Mon Sep 17 00:00:00 2001 From: aho Date: Fri, 9 Aug 2019 16:36:10 +0200 Subject: [PATCH 1/3] Update --- .../{ => test-compare/Composite}/Composite.R | 14 +- .../test-compare/Composite/test-Composite.R | 36 + s2dv_review/test-compare/Eno/Eno_new.R | 66 + s2dv_review/test-compare/Eno/Eno_old.R | 66 + s2dv_review/test-compare/Eno/test-compare.R | 23 + s2dv_review/test-compare/Season/Season_new.R | 169 + s2dv_review/test-compare/Season/Season_old.R | 58 + .../test-compare/Season/test-compare.R | 45 + startR/.Start_test.R.swp | Bin 0 -> 212992 bytes startR/Start_test.R | 3533 +++++++++++++++++ 10 files changed, 4003 insertions(+), 7 deletions(-) rename s2dv_review/{ => test-compare/Composite}/Composite.R (78%) create mode 100644 s2dv_review/test-compare/Composite/test-Composite.R create mode 100644 s2dv_review/test-compare/Eno/Eno_new.R create mode 100644 s2dv_review/test-compare/Eno/Eno_old.R create mode 100644 s2dv_review/test-compare/Eno/test-compare.R create mode 100644 s2dv_review/test-compare/Season/Season_new.R create mode 100644 s2dv_review/test-compare/Season/Season_old.R create mode 100644 s2dv_review/test-compare/Season/test-compare.R create mode 100644 startR/.Start_test.R.swp create mode 100644 startR/Start_test.R diff --git a/s2dv_review/Composite.R b/s2dv_review/test-compare/Composite/Composite.R similarity index 78% rename from s2dv_review/Composite.R rename to s2dv_review/test-compare/Composite/Composite.R index 9c263bd..564f1a2 100644 --- a/s2dv_review/Composite.R +++ b/s2dv_review/test-compare/Composite/Composite.R @@ -1,7 +1,7 @@ Composite <- function(var, occ, lag = 0, eno = FALSE, fileout = NULL) { if ( dim(var)[3] != length(occ) ) { - stop("temporal dimension of var is not equal to length of occ.") + stop("Temporal dimension of var is not equal to length of occ.") } K <- max(occ) composite <- array(dim = c(dim(var)[1 : 2], K)) @@ -23,7 +23,7 @@ Composite <- function(var, occ, lag = 0, eno = FALSE, fileout = NULL) { toberemoved = which(0 > indices | indices > dim(var)[3]) if (length(toberemoved) > 0) { - indices=indices[-toberemoved] + indices = indices[-toberemoved] } if (eno == TRUE) { n_k <- Eno(var[,, indices], posdim = 3) @@ -31,19 +31,19 @@ Composite <- function(var, occ, lag = 0, eno = FALSE, fileout = NULL) { n_k <- length(indices) } if (length(indices) == 1) { - composite[,, k] <- var[,, indices] + composite[, , k] <- var[, , indices] warning(paste("Composite", k, "has length 1 and pvalue is NA.")) } else { - composite[,,k] <- Mean1Dim(var[,, indices], posdim = 3, narm = TRUE) + composite[, , k] <- Mean1Dim(var[, , indices], posdim = 3, narm = TRUE) } - stdv_k <- apply(var[,, indices], c(1, 2), sd, na.rm = TRUE) + stdv_k <- apply(var[, , indices], c(1, 2), sd, na.rm = TRUE) - tvalue <- (mean_tot - composite[,, k]) / + tvalue <- (mean_tot - composite[, , k]) / sqrt(stdv_tot ^ 2 / n_tot + stdv_k ^ 2 / n_k) dof <- (stdv_tot ^ 2 / n_tot + stdv_k ^ 2 / n_k) ^ 2 / ((stdv_tot ^ 2 / n_tot) ^ 2 / (n_tot - 1) + (stdv_k ^ 2 / n_k) ^ 2 / (n_k - 1)) - pvalue[,, k] <- 2 * pt(-abs(tvalue), df = dof) + pvalue[, , k] <- 2 * pt(-abs(tvalue), df = dof) } if ( is.null(fileout) == FALSE ) { diff --git a/s2dv_review/test-compare/Composite/test-Composite.R b/s2dv_review/test-compare/Composite/test-Composite.R new file mode 100644 index 0000000..c0ed868 --- /dev/null +++ b/s2dv_review/test-compare/Composite/test-Composite.R @@ -0,0 +1,36 @@ +library(s2dverification) +source('/home/Earth/aho/aho-testtest/s2dv_review/Composite.R') + +context("Generic tests") +test_that("Sanity checks", { + + expect_error( + Composite(var = array(1:20, dim = c(2, 5, 2)), c(1, 1, 0)), + "temporal dimension of var is not equal to length of occ.") + + expect_warning( + Composite(var = array(1:40, dim = c(2, 5, 4)), c(1, 2, 2, 2)), + "Composite 1 has length 1 and pvalue is NA.") + + var <- array(rep(c(1, 3, 2, 1, 2), 8), dim = c(x = 2, y = 4, time = 5)) + occ <- c(1, 2, 2, 2, 1) + output <- c(x = 2, y = 4, 2) #dim(asd$composite) + expect_equal( + dim(Composite(var, occ)$composite), + output + ) + output <- c(1.5, 2.0, 2.5, 2.0) + expect_equal( + Composite(var, occ)$composite[1, , 1], + output + ) + + var <- array(rep(c(1, 3, 2, 1, 2), 8), dim = c(x = 2, y = 4, time = 5)) + occ <- c(1, 2, 2, 3, 3) + output <- array(as.numeric(rep(NA, 8)), dim = c(2, 4)) + expect_equal( + Composite(var, occ)$pvalue[, , 1], + output + ) + +}) diff --git a/s2dv_review/test-compare/Eno/Eno_new.R b/s2dv_review/test-compare/Eno/Eno_new.R new file mode 100644 index 0000000..02f30ff --- /dev/null +++ b/s2dv_review/test-compare/Eno/Eno_new.R @@ -0,0 +1,66 @@ +Eno_new <- function(obs, posdim, na.action = na.pass, ncores = NULL) { + + #Check params: + + if (is.null(obs)) { + stop("Parameter 'obs' cannot be NULL.") + } + if (!is.numeric(obs)) { + stop("Parameter 'obs' must be a numeric array.") + } + + if (is.null(posdim)) { + stop("Parameter 'posdim' cannot be NULL.") + } + + if (posdim %% 1 != 0 && !is.character(posdim)) { +# if (is.null(dim(obs))) { #a vector +# posdim <- 1 +# } else if (length(dim(obs)) == 1) { +# posdim <- 1 +# } else { + stop("Parameter 'posdim' must be an integer or character.") + } + + if (is.integer(posdim) && posdim > length(dim(obs))) { + stop("Parameter 'posdim' excesses the dimension length of parameter 'obs'.") + } + if (is.character(posdim) && !any(posdim %in% names(dim(obs)))) { + stop("Parameter 'posdim' does not match any dimension name of parameter 'obs'.") + } + + +# if (!is.null(dim(obs))) { + effnumobs <- Apply(data = list(obs), + target_dims = posdim, + fun = .Eno, + na.action = na.action, + ncores = ncores)$output1 + #parallel = parallel) + #effnumobs <- adrop(eno, 1) #dont drop length(dim) = 1. Keep the dim same as input. +# } else { +# effnumobs <- .Eno(obs) +# } + return(effnumobs) +} + +.Eno <- function(x, na.action) { + + if (length(sort(x)) > 1) { + n <- length(sort(x)) + if (n == 0) { + n <- 1 + } + a <- acf(x, lag.max = n - 1, plot = FALSE, + na.action = na.action)$acf[2:n, 1, 1] + s <- 0 + for (k in 1:(n - 1)) { + s <- s + (((n - k) / n) * a[k]) + } + eno <- min(n / (1 + (2 * s)), n) + } else { + eno <- NA + } + eno +} + diff --git a/s2dv_review/test-compare/Eno/Eno_old.R b/s2dv_review/test-compare/Eno/Eno_old.R new file mode 100644 index 0000000..a5f89c8 --- /dev/null +++ b/s2dv_review/test-compare/Eno/Eno_old.R @@ -0,0 +1,66 @@ +Eno_old <- function(obs, posdim) { + dimsvar <- dim(obs) + if (is.null(dimsvar)) { + dimsvar <- length(obs) + } + enlobs <- Enlarge(obs, 10) + outdim <- c(dimsvar, array(1, dim = (10 - length(dimsvar)))) + posaperm <- 1:10 + posaperm[posdim] <- 1 + posaperm[1] <- posdim + enlobs <- aperm(enlobs, posaperm) + dimsaperm <- outdim[posaperm] + # + # Loop on all dimensions to compute effective number of observations + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + enleno <- array(dim = c(1, dimsaperm[2:10])) + for (j2 in 1:dimsaperm[2]) { + for (j3 in 1:dimsaperm[3]) { + for (j4 in 1:dimsaperm[4]) { + for (j5 in 1:dimsaperm[5]) { + for (j6 in 1:dimsaperm[6]) { + for (j7 in 1:dimsaperm[7]) { + for (j8 in 1:dimsaperm[8]) { + for (j9 in 1:dimsaperm[9]) { + for (j10 in 1:dimsaperm[10]) { + tmp <- enlobs[, j2, j3, j4, j5, j6, j7, j8, j9, j10] + if (length(sort(tmp)) > 1 ) { + n <- length(sort(tmp)) + a <- acf(tmp, lag.max = n - 1, plot = FALSE, + na.action = na.pass)$acf[2:n, 1, 1] + s <- 0 + for (k in 1:(n - 1)) { + s <- s + (((n - k) / n) * a[k]) + } + enleno[1, j2, j3, j4, j5, j6, j7, j8, j9, + j10] <- min(n / (1 + (2 * s)), n) + } + } + } + } + } + } + } + } + } + } + # + # Back to the original dimensions + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + #dimsvar <- dimsvar[-posdim] + if (length(dimsvar) == 1) { + dimsvar <- 1 + } else { + dimsvar <- dimsvar[-posdim] + } + effnumobs <- array(dim = dimsvar) + effnumobs[] <- aperm(enleno, posaperm) + # + # Outputs + # ~~~~~~~~~ + # + effnumobs +} + diff --git a/s2dv_review/test-compare/Eno/test-compare.R b/s2dv_review/test-compare/Eno/test-compare.R new file mode 100644 index 0000000..f6e5bf6 --- /dev/null +++ b/s2dv_review/test-compare/Eno/test-compare.R @@ -0,0 +1,23 @@ +library(s2dverification) +library(multiApply) +source('Eno_old.R') +source('Eno_new.R') + +#test1 + var <- array(1:20, dim = c(2, 4, 5)) + var[1,1,1] <- NA + res1 <- Eno_new(obs = var, posdim = 2, na.action = na.pass) + res2 <- Eno_old(obs = var, posdim = 2) + + print("Test 1: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +#test2 + var <- array(rnorm(80), dim = c(1, 2, 4, 10)) + res1 <- Eno_new(obs = var, posdim = 1) + res2 <- Eno_old(obs = var, posdim = 1) + + print("Test 2: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +print('All tests pass!') diff --git a/s2dv_review/test-compare/Season/Season_new.R b/s2dv_review/test-compare/Season/Season_new.R new file mode 100644 index 0000000..7a0ae33 --- /dev/null +++ b/s2dv_review/test-compare/Season/Season_new.R @@ -0,0 +1,169 @@ +#'Computes Seasonal Means +#' +#'Computes seasonal means (or other operations) on monthly timeseries from n-dimensional arrays with named dimensions +#' +#'@param var a numeric n-dimensional array with named dimensions on monthy frequency. +#'@param posdim a character indicating the name of the dimension or a integer numeric indicating the position along which to compute seasonal means (or other operations). By default, 'time' dimension is expected. +#'@param monini an integer indicating the first month of the time series: 1 to 12. +#'@param moninf an integer indicating the month when to start the seasonal means: 1 to 12. +#'@param monsup an integer indicating the month when to stop the seasonal means: 1 to 12. +#'@param operation a character or function indicating the name of a function to be applied in seasonal basins. By default, means are computed. Other operations can be 'sum' for total precipitation. +#' +#'@return Array with the same dimensions as var except along the posdim dimension whose length corresponds to the number of seasons. Partial seasons are not accounted for. +#' +#'@import multiApply +#'@examples +#'dat <- 1 : (12 * 5 * 2 * 3 * 2) +#'dim(dat) <- c(dat = 1, memb = 3, time = 12 * 5, lon = 2, lat = 2) +#'res <- Season(var = dat, monini = 1, moninf = 1, monsup = 2) +#'dat <- 1 : (12 * 2 * 3) +#'dim(dat) <- c(dat = 2, time = 12, memb = 3) +#'res <- Season(var = dat, monini = 1, moninf =1, monsup = 2) +#'@export +Season_new <- function(var, posdim = 'time', monini, moninf, monsup, + operation = mean) { + # Check var + if (is.null(var)) { + stop("Parameter 'var' cannot be NULL.") + } + if (!is.numeric(var)) { + stop("Parameter 'var' must be a numeric array.") + } + if (is.null(dim(var))) { + dim(var) <- c(length(var)) + if (is.character(posdim)) { + names(dim(var)) <- posdim + } else { + names(dim(var)) <- 'time' + posdim <- 'time' + } + time_dim <- 1 + } else { + if (is.null(names(dim(var)))) { + if (is.numeric(posdim)) { + names(dim(var)) <- paste0("D", 1 : length(dim(var))) + names(dim(var))[posdim] <- 'time' + time_dim <- posdim + posdim <- 'time' + } else { + stop("Parameter 'var' must contain dimesnion names.") + } + } else { + if (is.numeric(posdim)) { + time_dim <- posdim + posdim <- names(dim(var))[posdim] + } else if (is.character(posdim)) { + time_dim <- which(names(dim(var)) == posdim) + } else { + stop("Parameter 'posdim' must be a integer or character", + "indicating the 'time' dimension.") + } + } + } + + dim_names <- names(dim(var)) +# series <- apply(var, margins, .Season, +# monini = monini, moninf = moninf, monsup = monsup, +# operation = operation) + series <- Apply(list(var), + target_dims = posdim, + fun = .Season, + monini = monini, moninf = moninf, monsup = monsup, + operation = operation)$output1 + if (is.null(dim(series))) { + dim(series) <- c(time = length(series)) + } else if (names(dim(series))[1] != "") { #& length(dim(series)) > 1) { + dim(series) <- c(1, dim(series)) + names(dim(series))[1] <- posdim + } else { + names(dim(series))[1] <- posdim + } + if (any(dim_names != names(dim(series)))) { + pos <- match(dim_names, names(dim(series))) + series <- aperm(series, pos) + names(dim(series)) <- dim_names + } + return(series) +} + +.Season <- function(x, monini, moninf, monsup, operation = mean) { + # Checks: + if (!is.numeric(x)) { + stop("Parameter 'x' must be a numeric vector.") + } + if (!is.numeric(monini)) { + stop("Parameter 'monini' must be numeric.") + } + if (length(monini) > 1) { + monini <- monini[1] + warning("Parameter 'monini' has length > 1 and only the first ", + "element will be used.") + } + if (monini %% 1 != 0) { + stop("Parameter 'monini' must be an integer.") + } + if (!is.numeric(moninf)) { + stop("Parameter 'moninf' must be numeric.") + } + if (length(moninf) > 1) { + moninf <- moninf[1] + warning("Parameter 'moninf' has length > 1 and only the first ", + "element will be used.") + } + if (moninf %% 1 != 0) { + stop("Parameter 'moninf' must be an integer.") + } + if (!is.numeric(monsup)) { + stop("Parameter 'monsup' must be numeric.") + } + if (length(monsup) > 1) { + monsup <- monsup[1] + warning("Parameter 'monsup' has length > 1 and only the first ", + "element will be used.") + } + if (monsup %% 1 != 0) { + stop("Parameter 'monsup' must be an integer.") + } + # Check fun operation + if (is.character(operation)) { + fun_name <- operation + err <- try({operation <- get(operation)}, silent = TRUE) + if (!is.function(operation)) { + stop("Could not find the function '", fun_name, "'.") + } + } + if (!is.function(operation)) { + stop("Parameter 'operation' must be a function or a character string ", + "with the name of a function.") + } + + # Correction e.g. 'winter': + while (monsup < moninf) { + monsup <- monsup + 12 + } + # Correction need if monini is not January: + moninf <- moninf - monini + 1 + monsup <- monsup - monini + 1 + moninf <- ifelse(moninf <= 0, moninf + 12, moninf) + monsup <- ifelse(monsup <= 0, monsup + 12, monsup) + + #### Create position index: + # Basic index: + pos <- moninf : monsup + # Extended index for all period: + if (length(x) > pos[length(pos)]) { + pos2 <- lapply(pos, function(y) {seq(y, length(x), 12)}) + } else { + pos2 <- pos + } + # Correct if the final season is not complete: + maxyear <- min(unlist(lapply(pos2, length))) + pos2 <- lapply(pos2, function(y) {y[1 : maxyear]}) + # Convert to array: + pos2 <- unlist(pos2) + dim(pos2) <- c(year = maxyear, month = length(pos2)/maxyear) + + timeseries <- apply(pos2, 1, function(y) {operation(x[y])}) + return(timeseries) +} + diff --git a/s2dv_review/test-compare/Season/Season_old.R b/s2dv_review/test-compare/Season/Season_old.R new file mode 100644 index 0000000..52e1861 --- /dev/null +++ b/s2dv_review/test-compare/Season/Season_old.R @@ -0,0 +1,58 @@ +Season_old <- function(var, posdim = 4, monini, moninf, monsup) { + while (monsup < moninf) { + monsup <- monsup + 12 + } + # + # Enlarge the size of var to 10 + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + dimsvar <- dim(var) + if (is.null(dimsvar)) { + dimsvar <- length(var) + } + ntime <- dimsvar[posdim] + enlvar <- Enlarge(var, 10) + outdim <- c(dimsvar, array(1, dim = (10 - length(dimsvar)))) + u <- IniListDims(outdim, 10) + v <- IniListDims(outdim, 10) + # + # Compute the seasonal means + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + # + ind <- 1:ntime + months <- ((ind - 1) + monini - 1) %% 12 + 1 + years <- ((ind - 1) + monini - 1) %/% 12 + + for (jmon in moninf:monsup) { + u[[posdim]] <- ind[which(months == ((jmon - 1) %% 12 + 1))] + ind0 <- u[[posdim]][1] + indf <- u[[posdim]][length(u[[posdim]])] + if (indf > (ntime - (monsup - jmon))) { + u[[posdim]] <- u[[posdim]][-which(u[[posdim]] == indf)] + } + if (ind0 < (jmon - moninf + 1)) { + u[[posdim]] <- u[[posdim]][-which(u[[posdim]] == ind0)] + } + if (jmon == moninf) { + nseas <- length(u[[posdim]]) + dimsvar[posdim] <- nseas + outdim[posdim] <- nseas + enlvarout <- array(0, dim = outdim) + } + v[[posdim]] <- 1:nseas + enlvarout[v[[1]], v[[2]], v[[3]], v[[4]], v[[5]], v[[6]], v[[7]], v[[8]], + v[[9]], v[[10]]] <- enlvarout[v[[1]], v[[2]], v[[3]], v[[4]], + v[[5]], v[[6]], v[[7]], v[[8]], + v[[9]], v[[10]]] + enlvar[u[[1]], + u[[2]], u[[3]], u[[4]], u[[5]], u[[6]], + u[[7]], u[[8]], u[[9]], u[[10]]] + } + varout <- array(dim = dimsvar) + varout[] <- enlvarout + varout <- varout / (monsup - moninf + 1) + # + # Outputs + # ~~~~~~~~~ + # + varout +} diff --git a/s2dv_review/test-compare/Season/test-compare.R b/s2dv_review/test-compare/Season/test-compare.R new file mode 100644 index 0000000..c089a65 --- /dev/null +++ b/s2dv_review/test-compare/Season/test-compare.R @@ -0,0 +1,45 @@ +library(s2dverification) +library(multiApply) +source('Season_old.R') +source('Season_new.R') + +#test1 + var <- array(1 : 20, dim = c(2, 4, 5)) + res1 <- Season_new(var, posdim = 3, monini = 1, moninf = 1, monsup = 2) + res2 <- Season_old(var, posdim = 3, monini = 1, moninf = 1, monsup = 2) + + print("Test 1: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +#test2 + names(dim(var)) <- c('x', 'y', 'time') + res1 <- Season_new(var, monini = 1, moninf = 1, monsup = 2) + res2 <- Season_old(var, posdim = 3, monini = 1, moninf = 1, monsup = 2) + + print("Test 2: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +#test3 + res1 <- Season_new(var, posdim = 1, monini = 1, moninf = 1, monsup = 2) + res2 <- Season_old(var, posdim = 1, monini = 1, moninf = 1, monsup = 2) + + print("Test 3: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +#test4 + var <- 1 : (12 * 2 * 3) + dim(var) <- c(dat = 2, time = 12, sdate = 3) + res1 <- Season_new(var, posdim = 2, monini = 1, moninf = 1, monsup = 2) + res2 <- Season_old(var, posdim = 2, monini = 1, moninf = 1, monsup = 2) + + print("Test 4: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +#test5 + res1 <- Season_new(var, posdim = 2, monini = 10, moninf = 2, monsup = 4) + res2<- Season_old(var, posdim = 2, monini = 10, moninf = 2, monsup = 4) + + print("Test 5: ") + print(all.equal(res1, res2, check.attributes = FALSE)) + +print('All tests pass!') diff --git a/startR/.Start_test.R.swp b/startR/.Start_test.R.swp new file mode 100644 index 0000000000000000000000000000000000000000..bf2d7b6ab150dea7448db0ae293b610bb4dca5b6 GIT binary patch literal 212992 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 OHj|Q=h)fli8vh4{0&a}} literal 0 HcmV?d00001 diff --git a/startR/Start_test.R b/startR/Start_test.R new file mode 100644 index 0000000..6fc5c1e --- /dev/null +++ b/startR/Start_test.R @@ -0,0 +1,3533 @@ + library(startR) +.ReplaceVariablesInString <- function(string, replace_values, allow_undefined_key_vars = FALSE) { + # This function replaces all the occurrences of a variable in a string by + # their corresponding string stored in the replace_values. + if (length(strsplit(string, "\\$")[[1]]) > 1) { + parts <- strsplit(string, "\\$")[[1]] + output <- "" + i <- 0 + for (part in parts) { + if (i %% 2 == 0) { + output <- paste(output, part, sep = "") + } else { + if (part %in% names(replace_values)) { + output <- paste(output, .ReplaceVariablesInString(replace_values[[part]], replace_values, allow_undefined_key_vars), sep = "") + } else if (allow_undefined_key_vars) { + output <- paste0(output, "$", part, "$") + } else { + stop(paste('Error: The variable $', part, '$ was not defined in the configuration file.', sep = '')) + } + } + i <- i + 1 + } + output + } else { + string + } +} + +.FindTagValue <- function(path_with_globs_and_tag, actual_path, tag) { + if (!all(sapply(c(path_with_globs_and_tag, actual_path, tag), is.character))) { + stop("All 'path_with_globs_and_tag', 'actual_path' and 'tag' must be character strings.") + } + + if (grepl('$', tag, fixed = TRUE)) { + stop("The provided 'tag' must not contain '$' symbols.") + } + full_tag <- paste0('$', tag, '$') + + if (!grepl(full_tag, path_with_globs_and_tag, fixed = TRUE)) { + stop("The provided 'path_with_globs_and_tag' must contain the tag in 'tag' surrounded by '$' symbols.") + } + + parts <- strsplit(path_with_globs_and_tag, full_tag, fixed = TRUE)[[1]] + if (length(parts) == 1) { + parts <- c(parts, '') + } + parts[1] <- paste0('^', parts[1]) + parts[length(parts)] <- paste0(parts[length(parts)], '$') + + # Group the parts in 2 groups, in a way that both groups have a number + # of characters as similar as possible. + part_lengths <- sapply(parts, nchar) + group_len_diffs <- sapply(1:(length(parts) - 1), + function(x) { + sum(part_lengths[(x + 1):length(parts)]) - sum(part_lengths[1:x]) + } + ) + clp <- chosen_left_part <- which.min(group_len_diffs)[1] + + left_expr <- paste(parts[1:clp], collapse = full_tag) + left_expr <- gsub('?', '.', left_expr, fixed = TRUE) + # The .*? will force lazy evaluation (find the shortest match from the + # beginning of the actual_path). + left_expr <- gsub('*', '.*?', left_expr, fixed = TRUE) + left_expr <- gsub(full_tag, '.*?', left_expr, fixed = TRUE) + left_match <- regexec(left_expr, actual_path)[[1]] + if (left_match < 0) { + stop("Unexpected error in .FindTagValue.") + } + + right_expr <- paste(parts[(clp + 1):(length(parts))], collapse = full_tag) + right_expr <- gsub('?', '.', right_expr, fixed = TRUE) + # For lazy evaulation to work, pattern and string have to be reversed. + right_expr <- gsub('*', '.*?', right_expr, fixed = TRUE) + right_expr <- gsub(full_tag, '.*?', right_expr, fixed = TRUE) + right_expr <- gsub('$', '^', right_expr, fixed = TRUE) + rev_str <- function(s) { + paste(rev(strsplit(s, NULL)[[1]]), collapse = '') + } + right_expr <- rev_str(right_expr) + right_expr <- gsub('?*.', '.*?', right_expr, fixed = TRUE) + right_match <- regexec(right_expr, rev_str(actual_path))[[1]] + if (right_match < 0) { + stop("Unexpected error in .FindTagValue.") + } + right_match[] <- nchar(actual_path) - + (right_match[] + attr(right_match, 'match.length') - 1) + 1 + + if ((left_match + attr(left_match, 'match.length')) > + (right_match - 1)) { + NULL + } else { + substr(actual_path, left_match + attr(left_match, 'match.length'), + right_match - 1) + } +} +.ReplaceGlobExpressions <- function(path_with_globs, actual_path, + replace_values, tags_to_keep, + dataset_name, permissive) { + # The goal of this function is to replace the shell globbing expressions in + # a path pattern (that may contain shell globbing expressions and Load() + # tags) by the corresponding part of the real existing path. + # What is done actually is to replace all the values of the tags in the + # actual path by the corresponding $TAG$ + # + # It takes mainly two inputs. The path with expressions and tags, e.g.: + # /data/experiments/*/$EXP_NAME$/$VAR_NAME$/$VAR_NAME$_*$START_DATE$*.nc + # and a complete known path to one of the matching files, e.g.: + # /data/experiments/ecearth/i00k/tos/tos_fc0-1_19901101_199011-199110.nc + # and it returns the path pattern but without shell globbing expressions: + # /data/experiments/ecearth/$EXP_NAME$/$VAR_NAME$/$VAR_NAME$_fc0-1_$START_DATE$_199011-199110.nc + # + # To do that, it needs also as inputs the list of replace values (the + # association of each tag to their value). + # + # All the tags not present in the parameter tags_to_keep will be repalced. + # + # Not all cases can be resolved with the implemented algorithm. In an + # unsolvable case a warning is given and one possible guess is returned. + # + # In some cases it is interesting to replace only the expressions in the + # path to the file, but not the ones in the file name itself. To keep the + # expressions in the file name, the parameter permissive can be set to + # TRUE. To replace all the expressions it can be set to FALSE. + + # Tests + #a <- "/esarchive/exp/ecearth/a13c/3hourly/$var$_*/$var$_*-LR_historical_r1i1p1f1_gr_$chunk$.nc" + #b <- "/esarchive/exp/ecearth/a13c/3hourly/psl_f6h/psl_E3hrPt_EC-Earth3-LR_historical_r1i1p1f1_gr_195001010000-195001312100.nc" + #c <- list(dat = 'dat1', var = 'psl', chunk = '195001010000-195001312100') + #d <- c('dat', 'var', 'chunk') + #e <- 'dat1' + #f <- FALSE #TRUE/0/1/2/3 + #r <- .ReplaceGlobExpressions(a, b, c, d, e, f) + + clean <- function(x) { + if (nchar(x) > 0) { + x <- gsub('\\\\', '', x) + x <- gsub('\\^', '', x) + x <- gsub('\\$', '', x) + x <- unname(sapply(strsplit(x, '[',fixed = TRUE)[[1]], function(y) gsub('.*]', '.', y))) + do.call(paste0, as.list(x)) + } else { + x + } + } + + strReverse <- function(x) sapply(lapply(strsplit(x, NULL), rev), paste, collapse = "") + + if (permissive == 0) { + permissive <- FALSE + } else { + if (permissive == TRUE) { + permissive_levels <- 1 + } else { + permissive_levels <- round(permissive[1]) + permissive <- TRUE + } + } + + if (permissive) { + actual_path_chunks <- strsplit(actual_path, '/')[[1]] + if (permissive_levels >= length(actual_path_chunks)) { + stop("Error: Provided levels out of scope in parameter 'permissive'.") + } + permissive_levels <- 1:permissive_levels + permissive_levels <- length(actual_path_chunks) - (rev(permissive_levels) - 1) + actual_path <- paste(actual_path_chunks[-permissive_levels], collapse = '/') + file_name <- paste(actual_path_chunks[permissive_levels], collapse = '/') + if (length(actual_path_chunks) > 1) { + file_name <- paste0('/', file_name) + } + path_with_globs_chunks <- strsplit(path_with_globs, '/')[[1]] + path_with_globs <- paste(path_with_globs_chunks[-permissive_levels], + collapse = '/') + path_with_globs_no_tags <- .ReplaceVariablesInString(path_with_globs, replace_values) + file_name_with_globs <- paste(path_with_globs_chunks[permissive_levels], collapse = '/') + if (length(path_with_globs_chunks) > 1) { + file_name_with_globs <- paste0('/', file_name_with_globs) + } + right_known <- head(strsplit(file_name_with_globs, '*', fixed = TRUE)[[1]], 1) + right_known_no_tags <- .ReplaceVariablesInString(right_known, replace_values) + path_with_globs_no_tags_rx <- utils::glob2rx(paste0(path_with_globs_no_tags, right_known_no_tags)) + match <- regexpr(gsub('$', '', path_with_globs_no_tags_rx, fixed = TRUE), paste0(actual_path, file_name)) + if (match != 1) { + stop("Incorrect parameters to replace glob expressions. The path with expressions does not match the actual path.") + } + #if (attr(match, 'match.length') - nchar(right_known_no_tags) < nchar(actual_path)) { + # path_with_globs_no_tags <- paste0(path_with_globs_no_tags, right_known_no_tags, '*') + # file_name_with_globs <- sub(right_known, '/*', file_name_with_globs) + #} + } + path_with_globs_rx <- utils::glob2rx(path_with_globs) + values_to_replace <- c() + tags_to_replace_starts <- c() + tags_to_replace_ends <- c() + give_warning <- FALSE + for (tag in tags_to_keep) { + matches <- gregexpr(paste0('$', tag, '$'), path_with_globs_rx, fixed = TRUE)[[1]] + lengths <- attr(matches, 'match.length') + if (!(length(matches) == 1 && matches[1] == -1)) { + for (i in 1:length(matches)) { + left <- NULL + if (matches[i] > 1) { + left <- .ReplaceVariablesInString(substr(path_with_globs_rx, 1, matches[i] - 1), replace_values) + left_known <- strReverse(head(strsplit(strReverse(left), strReverse('.*'), fixed = TRUE)[[1]], 1)) + } + right <- NULL + if ((matches[i] + lengths[i] - 1) < nchar(path_with_globs_rx)) { + right <- .ReplaceVariablesInString(substr(path_with_globs_rx, matches[i] + lengths[i], nchar(path_with_globs_rx)), replace_values) + right_known <- head(strsplit(right, '.*', fixed = TRUE)[[1]], 1) + } + final_match <- NULL + match_limits <- NULL + if (!is.null(left)) { + left_match <- regexpr(paste0(left, replace_values[[tag]], right_known), actual_path) + match_len <- attr(left_match, 'match.length') + left_match_limits <- c(left_match + match_len - 1 - nchar(clean(right_known)) - nchar(replace_values[[tag]]) + 1, + left_match + match_len - 1 - nchar(clean(right_known))) + if (!(left_match < 1)) { + match_limits <- left_match_limits + } + } + right_match <- NULL + if (!is.null(right)) { + right_match <- regexpr(paste0(left_known, replace_values[[tag]], right), actual_path) + match_len <- attr(right_match, 'match.length') + right_match_limits <- c(right_match + nchar(clean(left_known)), + right_match + nchar(clean(left_known)) + nchar(replace_values[[tag]]) - 1) + if (is.null(match_limits) && !(right_match < 1)) { + match_limits <- right_match_limits + } + } + if (!is.null(right_match) && !is.null(left_match)) { + if (!identical(right_match_limits, left_match_limits)) { + give_warning <- TRUE + } + } + if (is.null(match_limits)) { + stop("Too complex path pattern specified for ", dataset_name, + ". Specify a simpler path pattern for this dataset.") + } + values_to_replace <- c(values_to_replace, tag) + tags_to_replace_starts <- c(tags_to_replace_starts, match_limits[1]) + tags_to_replace_ends <- c(tags_to_replace_ends, match_limits[2]) + } + } + } + + actual_path_with_tags <- actual_path + if (length(tags_to_replace_starts) > 0) { + reorder <- sort(tags_to_replace_starts, index.return = TRUE) + tags_to_replace_starts <- reorder$x + values_to_replace <- values_to_replace[reorder$ix] + tags_to_replace_ends <- tags_to_replace_ends[reorder$ix] + while (length(values_to_replace) > 0) { + actual_path_with_tags <- paste0(substr(actual_path_with_tags, 1, head(tags_to_replace_starts, 1) - 1), + '$', head(values_to_replace, 1), '$', + substr(actual_path_with_tags, head(tags_to_replace_ends, 1) + 1, nchar(actual_path_with_tags))) + extra_chars <- nchar(head(values_to_replace, 1)) + 2 - (head(tags_to_replace_ends, 1) - head(tags_to_replace_starts, 1) + 1) + values_to_replace <- values_to_replace[-1] + tags_to_replace_starts <- tags_to_replace_starts[-1] + tags_to_replace_ends <- tags_to_replace_ends[-1] + tags_to_replace_starts <- tags_to_replace_starts + extra_chars + tags_to_replace_ends <- tags_to_replace_ends + extra_chars + } + } + + if (give_warning) { + .warning(paste0("Too complex path pattern specified for ", dataset_name, + ". Double check carefully the '$Files' fetched for this dataset or specify a simpler path pattern.")) + } + + if (permissive) { + paste0(actual_path_with_tags, file_name_with_globs) + } else { + actual_path_with_tags + } +} +.MergeArrayDims <- function(dims1, dims2) { + new_dims1 <- c() + new_dims2 <- c() + while (length(dims1) > 0) { + if (names(dims1)[1] %in% names(dims2)) { + pos <- which(names(dims2) == names(dims1)[1]) + dims_to_add <- rep(1, pos - 1) + if (length(dims_to_add) > 0) { + names(dims_to_add) <- names(dims2[1:(pos - 1)]) + } + new_dims1 <- c(new_dims1, dims_to_add, dims1[1]) + new_dims2 <- c(new_dims2, dims2[1:pos]) + dims1 <- dims1[-1] + dims2 <- dims2[-c(1:pos)] + } else { + new_dims1 <- c(new_dims1, dims1[1]) + new_dims2 <- c(new_dims2, 1) + names(new_dims2)[length(new_dims2)] <- names(dims1)[1] + dims1 <- dims1[-1] + } + } + if (length(dims2) > 0) { + dims_to_add <- rep(1, length(dims2)) + names(dims_to_add) <- names(dims2) + new_dims1 <- c(new_dims1, dims_to_add) + new_dims2 <- c(new_dims2, dims2) + } + list(new_dims1, new_dims2, pmax(new_dims1, new_dims2)) +} + +.MergeArrays <- function(array1, array2, along) { + if (!(is.null(array1) || is.null(array2))) { + if (!(identical(names(dim(array1)), names(dim(array2))) && + identical(dim(array1)[-which(names(dim(array1)) == along)], + dim(array2)[-which(names(dim(array2)) == along)]))) { + new_dims <- .MergeArrayDims(dim(array1), dim(array2)) + dim(array1) <- new_dims[[1]] + dim(array2) <- new_dims[[2]] + for (j in 1:length(dim(array1))) { + if (names(dim(array1))[j] != along) { + if (dim(array1)[j] != dim(array2)[j]) { + if (which.max(c(dim(array1)[j], dim(array2)[j])) == 1) { + na_array_dims <- dim(array2) + na_array_dims[j] <- dim(array1)[j] - dim(array2)[j] + na_array <- array(dim = na_array_dims) + array2 <- abind(array2, na_array, along = j) + names(dim(array2)) <- names(na_array_dims) + } else { + na_array_dims <- dim(array1) + na_array_dims[j] <- dim(array2)[j] - dim(array1)[j] + na_array <- array(dim = na_array_dims) + array1 <- abind(array1, na_array, along = j) + names(dim(array1)) <- names(na_array_dims) + } + } + } + } + } + if (!(along %in% names(dim(array2)))) { + stop("The dimension specified in 'along' is not present in the ", + "provided arrays.") + } + array1 <- abind(array1, array2, along = which(names(dim(array1)) == along)) + names(dim(array1)) <- names(dim(array2)) + } else if (is.null(array1)) { + array1 <- array2 + } + array1 +} + +# Takes as input a list of arrays. The list must have named dimensions. +.MergeArrayOfArrays <- function(array_of_arrays) { + MergeArrays <- .MergeArrays + array_dims <- (dim(array_of_arrays)) + dim_names <- names(array_dims) + + # Merge the chunks. + for (dim_index in 1:length(dim_names)) { + dim_sub_array_of_chunks <- dim_sub_array_of_chunk_indices <- NULL + if (dim_index < length(dim_names)) { + dim_sub_array_of_chunks <- array_dims[(dim_index + 1):length(dim_names)] + names(dim_sub_array_of_chunks) <- dim_names[(dim_index + 1):length(dim_names)] + dim_sub_array_of_chunk_indices <- dim_sub_array_of_chunks + sub_array_of_chunk_indices <- array(1:prod(dim_sub_array_of_chunk_indices), + dim_sub_array_of_chunk_indices) + } else { + sub_array_of_chunk_indices <- NULL + } + sub_array_of_chunks <- vector('list', prod(dim_sub_array_of_chunks)) + dim(sub_array_of_chunks) <- dim_sub_array_of_chunks + for (i in 1:prod(dim_sub_array_of_chunks)) { + if (!is.null(sub_array_of_chunk_indices)) { + chunk_sub_indices <- which(sub_array_of_chunk_indices == i, arr.ind = TRUE)[1, ] + } else { + chunk_sub_indices <- NULL + } + for (j in 1:(array_dims[dim_index])) { + new_chunk <- do.call('[[', c(list(x = array_of_arrays), + as.list(c(j, chunk_sub_indices)))) + if (is.null(new_chunk)) { + stop("Chunks missing.") + } + if (is.null(sub_array_of_chunks[[i]])) { + sub_array_of_chunks[[i]] <- new_chunk + } else { + sub_array_of_chunks[[i]] <- MergeArrays(sub_array_of_chunks[[i]], + new_chunk, + dim_names[dim_index]) + } + } + } + array_of_arrays <- sub_array_of_chunks + rm(sub_array_of_chunks) + gc() + } + + array_of_arrays[[1]] +} +.message <- function(...) { + # Function to use the 'message' R function with our custom settings + # Default: new line at end of message, indent to 0, exdent to 3, + # collapse to \n* + args <- list(...) + + ## In case we need to specify message arguments + if (!is.null(args[["appendLF"]])) { + appendLF <- args[["appendLF"]] + } else { + ## Default value in message function + appendLF <- TRUE + } + if (!is.null(args[["domain"]])) { + domain <- args[["domain"]] + } else { + ## Default value in message function + domain <- NULL + } + args[["appendLF"]] <- NULL + args[["domain"]] <- NULL + + ## To modify strwrap indent and exdent arguments + if (!is.null(args[["indent"]])) { + indent <- args[["indent"]] + } else { + indent <- 0 + } + if (!is.null(args[["exdent"]])) { + exdent <- args[["exdent"]] + } else { + exdent <- 3 + } + args[["indent"]] <- NULL + args[["exdent"]] <- NULL + + ## To modify paste collapse argument + if (!is.null(args[["collapse"]])) { + collapse <- args[["collapse"]] + } else { + collapse <- "\n*" + } + args[["collapse"]] <- NULL + + ## Message tag + if (!is.null(args[["tag"]])) { + tag <- args[["tag"]] + } else { + tag <- "* " + } + args[["tag"]] <- NULL + + message(paste0(tag, paste(strwrap( + args, indent = indent, exdent = exdent + ), collapse = collapse)), appendLF = appendLF, domain = domain) +} + +.warning <- function(...) { + # Function to use the 'warning' R function with our custom settings + # Default: no call information, indent to 0, exdent to 3, + # collapse to \n + args <- list(...) + + ## In case we need to specify warning arguments + if (!is.null(args[["call."]])) { + call <- args[["call."]] + } else { + ## Default: don't show info about the call where the warning came up + call <- FALSE + } + if (!is.null(args[["immediate."]])) { + immediate <- args[["immediate."]] + } else { + ## Default value in warning function + immediate <- FALSE + } + if (!is.null(args[["noBreaks."]])) { + noBreaks <- args[["noBreaks."]] + } else { + ## Default value warning function + noBreaks <- FALSE + } + if (!is.null(args[["domain"]])) { + domain <- args[["domain"]] + } else { + ## Default value warning function + domain <- NULL + } + args[["call."]] <- NULL + args[["immediate."]] <- NULL + args[["noBreaks."]] <- NULL + args[["domain"]] <- NULL + + ## To modify strwrap indent and exdent arguments + if (!is.null(args[["indent"]])) { + indent <- args[["indent"]] + } else { + indent <- 0 + } + if (!is.null(args[["exdent"]])) { + exdent <- args[["exdent"]] + } else { + exdent <- 3 + } + args[["indent"]] <- NULL + args[["exdent"]] <- NULL + + ## To modify paste collapse argument + if (!is.null(args[["collapse"]])) { + collapse <- args[["collapse"]] + } else { + collapse <- "\n!" + } + args[["collapse"]] <- NULL + + ## Warning tag + if (!is.null(args[["tag"]])) { + tag <- args[["tag"]] + } else { + tag <- "! Warning: " + } + args[["tag"]] <- NULL + + warning(paste0(tag, paste(strwrap( + args, indent = indent, exdent = exdent + ), collapse = collapse)), call. = call, immediate. = immediate, + noBreaks. = noBreaks, domain = domain) +} + +.LoadDataFile <- function(work_piece, shared_matrix_pointer, + file_data_reader, synonims, + transform, transform_params, + silent = FALSE, debug = FALSE) { +# suppressPackageStartupMessages({library(bigmemory)}) +### TODO: Specify dependencies as parameter +# suppressPackageStartupMessages({library(ncdf4)}) + +#print("1") + store_indices <- as.list(work_piece[['store_position']]) + first_round_indices <- work_piece[['first_round_indices']] + second_round_indices <- work_piece[['second_round_indices']] +#print("2") + file_to_open <- work_piece[['file_path']] + sub_array <- file_data_reader(file_to_open, NULL, + work_piece[['file_selectors']], + first_round_indices, synonims) +print('sub_array') +print(dim(sub_array)) +if (debug) { +if (all(unlist(store_indices[1:6]) == 1)) { +print("-> LOADING A WORK PIECE") +print("-> STRUCTURE OF READ UNTRANSFORMED DATA:") +print(str(sub_array)) +print("-> STRUCTURE OF VARIABLES TO TRANSFORM:") +print(str(work_piece[['vars_to_transform']])) +print("-> COMMON ARRAY DIMENSIONS:") +print(str(work_piece[['store_dims']])) +} +} + if (!is.null(sub_array)) { + # Apply data transformation once we have the data arrays. + if (!is.null(transform)) { +if (debug) { +if (all(unlist(store_indices[1:6]) == 1)) { +print("-> PROCEEDING TO TRANSFORM ARRAY") +print("-> DIMENSIONS OF ARRAY RIGHT BEFORE TRANSFORMING:") +print(dim(sub_array)) +} +} + sub_array <- do.call(transform, c(list(data_array = sub_array, + variables = work_piece[['vars_to_transform']], + file_selectors = work_piece[['file_selectors']]), + transform_params)) +if (debug) { +if (all(unlist(store_indices[1:6]) == 1)) { +print("-> STRUCTURE OF ARRAY AND VARIABLES RIGHT AFTER TRANSFORMING:") +print(str(sub_array)) +print("-> DIMENSIONS OF ARRAY RIGHT AFTER TRANSFORMING:") +print(dim(sub_array$data_array)) +} +} + sub_array <- sub_array$data_array + # Subset with second round of indices + dims_to_crop <- which(!sapply(second_round_indices, is.null)) + if (length(dims_to_crop) > 0) { + dimnames_to_crop <- names(second_round_indices)[dims_to_crop] + sub_array <- Subset(sub_array, dimnames_to_crop, + second_round_indices[dimnames_to_crop]) + } +if (debug) { +if (all(unlist(store_indices[1:6]) == 1)) { +print("-> STRUCTURE OF ARRAY AND VARIABLES RIGHT AFTER SUBSETTING WITH 2nd ROUND INDICES:") +print(str(sub_array)) +} +} + } + + metadata <- attr(sub_array, 'variables') + + names_bk <- names(store_indices) +print('names(store_indices)') +print(names(store_indices)) +print('names(first_round_indices)') +print(names(first_round_indices)) +print('second_round_indices') +print(second_round_indices) +#print(str(attr(sub_array, 'variables'))) + store_indices <- lapply(names(store_indices), + function (x) { + if (!(x %in% names(first_round_indices))) { + store_indices[[x]] + } else if (is.null(second_round_indices[[x]])) { +# 1:dim(sub_array)[x] +###modified!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1 + 1:attr(sub_array, 'variables')[[1]]$dim[[3]]$len + } else { + if (is.numeric(second_round_indices[[x]])) { + ## TODO: Review carefully this line. Inner indices are all + ## aligned to the left-most positions. If dataset A has longitudes + ## 1, 2, 3, 4 but dataset B has only longitudes 3 and 4, then + ## they will be stored as follows: + ## 1, 2, 3, 4 + ## 3, 4, NA, NA + ##x - min(x) + 1 + 1:length(second_round_indices[[x]]) + } else { + 1:length(second_round_indices[[x]]) + } + } + }) + names(store_indices) <- names_bk +print('store_indices$time=') +print(store_indices$time) + +if (debug) { +if (all(unlist(store_indices) == 1)) { +print("-> STRUCTURE OF FIRST ROUND INDICES FOR THIS WORK PIECE:") +print(str(first_round_indices)) +print("-> STRUCTURE OF SECOND ROUND INDICES FOR THIS WORK PIECE:") +print(str(second_round_indices)) +print("-> STRUCTURE OF STORE INDICES FOR THIS WORK PIECE:") +print(str(store_indices)) +} +} + + store_indices <- lapply(store_indices, as.integer) + store_dims <- work_piece[['store_dims']] + + # split the storage work of the loaded subset in parts + largest_dim_name <- names(dim(sub_array))[which.max(dim(sub_array))] + max_parts <- length(store_indices[[largest_dim_name]]) + + # Indexing a data file of N MB with expand.grid takes 30*N MB + # The peak ram of Start is, minimum, 2 * total data to load from all files + # due to inefficiencies in other regions of the code + # The more parts we split the indexing done below in, the lower + # the memory footprint of the indexing and the fast. + # But more than 10 indexing iterations (parts) for each MB processed + # makes the iteration slower (tested empirically on BSC workstations). + subset_size_in_mb <- prod(dim(sub_array)) * 8 / 1024 / 1024 + best_n_parts <- ceiling(subset_size_in_mb * 10) + # We want to set n_parts to a greater value than the one that would + # result in a memory footprint (of the subset indexing code below) equal + # to 2 * total data to load from all files. + # s = subset size in MB + # p = number of parts to break it in + # T = total size of data to load + # then, s / p * 30 = 2 * T + # then, p = s * 15 / T + min_n_parts <- ceiling(prod(dim(sub_array)) * 15 / prod(store_dims)) + # Make sure we pick n_parts much greater than the minimum calculated + n_parts <- min_n_parts * 10 + if (n_parts > best_n_parts) { + n_parts <- best_n_parts + } + # Boundary checks + if (n_parts < 1) { + n_parts <- 1 + } + if (n_parts > max_parts) { + n_parts <- max_parts + } + + if (n_parts > 1) { + make_parts <- function(length, n) { + clusters <- cut(1:length, n, labels = FALSE) + lapply(1:n, function(y) which(clusters == y)) + } + part_indices <- make_parts(max_parts, n_parts) + parts <- lapply(part_indices, + function(x) { + store_indices[[largest_dim_name]][x] + }) + } else { + part_indices <- list(1:max_parts) + parts <- store_indices[largest_dim_name] + } + +print('store_dims') +print(store_dims) + # do the storage work + weights <- sapply(1:length(store_dims), + function(i) prod(c(1, store_dims)[1:i])) + part_indices_in_sub_array <- as.list(rep(TRUE, length(dim(sub_array)))) + names(part_indices_in_sub_array) <- names(dim(sub_array)) + data_array <- bigmemory::attach.big.matrix(shared_matrix_pointer) + for (i in 1:n_parts) { + store_indices[[largest_dim_name]] <- parts[[i]] + # Converting array indices to vector indices + matrix_indices <- do.call("expand.grid", store_indices) + # Given a matrix where each row is a set of array indices of an element + # the vector indices are computed + matrix_indices <- 1 + colSums(t(matrix_indices - 1) * weights) + part_indices_in_sub_array[[largest_dim_name]] <- part_indices[[i]] +print('1.5') + data_array[matrix_indices] <- as.vector(do.call('[', + c(list(x = sub_array), + part_indices_in_sub_array))) +print(data_array[matrix_indices]) + } + rm(data_array) + gc() + + if (!is.null(work_piece[['save_metadata_in']])) { + saveRDS(metadata, file = work_piece[['save_metadata_in']]) + } + } + if (!is.null(work_piece[['progress_amount']]) && !silent) { + message(work_piece[['progress_amount']], appendLF = FALSE) + } + is.null(sub_array) +} + +#---------------------------------------------------------------- +# repos <- '/esarchive/exp/ecearth/a1ua/cmorfiles/DCPP/EC-Earth-Consortium/EC-Earth3/dcppA-hindcast/$memb$/Omon/$var$/gr/v20190508/$var$_Omon_EC-Earth3_dcppA-hindcast_s$sdate$-$memb$_gr_$chunk$.nc' +# +# lons.min <- 0 +# lons.max <- 360 +# lats.min <- -90 +# lats.max <- 90 +# +# dat = repos +# var = 'tos' +# memb = c('r1i1p1f1') +# sdate = paste(seq(1961,1962)) +# time = c(3:21) +# chunk = 'all' +# lat = values(list(lats.min, lats.max)) #'all', CANNOT USE 'all' +# lon = values(list(lons.min, lons.max)) #'all', CANNOT USE 'all' +# chunk_depends = 'sdate' +# time_across = 'chunk' +# +# merge_across_dims = FALSE +# transform = CDORemapper +# transform_params = list(grid='/esarchive/obs/ukmo/hadcrut_v4.6/monthly_mean/tasanomaly/tasanomaly_201712.nc', method='con') +# transform_vars = c('lat', 'lon') +# return_vars = list(lat = 'dat', lon = 'dat') +# retrieve = TRUE +# +# synonims = NULL +# file_opener = NcOpener +# file_var_reader = NcVarReader +# file_dim_reader = NcDimReader +# file_data_reader = NcDataReader +# file_closer = NcCloser +# transform_extra_cells = 0 +# apply_indices_after_transform = FALSE +# pattern_dims = NULL +# metadata_dims = NULL +# selector_checker = SelectorChecker +# split_multiselected_dims = FALSE +# path_glob_permissive = FALSE +# num_procs = 1 +# silent = FALSE +# debug = FALSE + +#---------------------------------------------------------- + + repos <- '/home/Earth/aho/$var$_Amon_EC-Earth3_historical_r20i1p1f1_gr_$chunk$.nc' + + lons.min <- 0 + lons.max <- 360 + lats.min <- -90 + lats.max <- 90 + + dat = repos + var = 'clivi' + time = c(5:21) + chunk = 'all' + lat = values(list(lats.min, lats.max)) #'all', CANNOT USE 'all' + lon = values(list(lons.min, lons.max)) #'all', CANNOT USE 'all' + #chunk_depends = 'sdate' + time_across = 'chunk' + + merge_across_dims = FALSE + transform = NULL + transform_params = NULL + transform_vars = NULL + return_vars = NULL #list(lat = 'dat', lon = 'dat') + retrieve = TRUE + + synonims = NULL + file_opener = NcOpener + file_var_reader = NcVarReader + file_dim_reader = NcDimReader + file_data_reader = NcDataReader + file_closer = NcCloser + transform_extra_cells = 0 + apply_indices_after_transform = FALSE + pattern_dims = NULL + metadata_dims = NULL + selector_checker = SelectorChecker + split_multiselected_dims = FALSE + path_glob_permissive = FALSE + num_procs = 1 + silent = FALSE + debug = FALSE + +#---------------------------------------------------------- +# dim_params <- list(dat = dat, var = var, memb = memb, sdate = sdate, time = time, chunk = chunk, lat = lat, lon = lon, chunk_depends = chunk_depends, time_across = time_across) + + dim_params <- list(dat = dat, var = var, time = time, chunk = chunk, lat = lat, lon = lon, time_across = time_across) + + var_params_ind <- grep("_var$", names(dim_params)) + var_params <- dim_params[var_params_ind] + i <- 1 + for (var_param in var_params) { + if (!is.character(var_param)) { + stop("All '*_var' parameters must be character strings.") + } + else if (!any(grepl(paste0("^", strsplit(names(var_params)[i], + "_var$")[[1]][1], "$"), names(dim_params)))) { + stop(paste0("All '*_var' parameters must be associated to a dimension parameter. Found parameter '", + names(var_params)[i], "' but no parameter '", + strsplit(names(var_params)[i], "_var$")[[1]][1], + "'.")) + } + i <- i + 1 + } + if (length(var_params) < 1) { + var_params <- NULL + } else { + names(var_params) <- gsub("_var$", "", names(var_params)) + } + dim_reorder_params_ind <- grep("_reorder$", names(dim_params)) + dim_reorder_params <- dim_params[dim_reorder_params_ind] + if (length(dim_reorder_params) < 1) { + dim_reorder_params <- NULL + } else { + names(dim_reorder_params) <- gsub("_reorder$", "", names(dim_reorder_params)) + } + tolerance_params_ind <- grep("_tolerance$", names(dim_params)) + tolerance_params <- dim_params[tolerance_params_ind] + depends_params_ind <- grep("_depends$", names(dim_params)) + depends_params <- dim_params[depends_params_ind] + i <- 1 + for (depends_param in depends_params) { + if (!is.character(depends_param) || (length(depends_param) > + 1)) { + stop("All '*_depends' parameters must be single character strings.") + } + else if (!any(grepl(paste0("^", strsplit(names(depends_params)[i], + "_depends$")[[1]][1], "$"), names(dim_params)))) { + stop(paste0("All '*_depends' parameters must be associated to a dimension parameter. Found parameter '", + names(depends_params)[i], "' but no parameter '", + strsplit(names(depends_params)[i], "_depends$")[[1]][1], + "'.")) + } + i <- i + 1 + } + if (length(depends_params) < 1) { + depends_params <- NULL + } else { + names(depends_params) <- gsub("_depends$", "", names(depends_params)) + } + depending_file_dims <- depends_params + across_params_ind <- grep("_across$", names(dim_params)) + across_params <- dim_params[across_params_ind] + i <- 1 + for (across_param in across_params) { + if (!is.character(across_param) || (length(across_param) > + 1)) { + stop("All '*_across' parameters must be single character strings.") + } + else if (!any(grepl(paste0("^", strsplit(names(across_params)[i], + "_across$")[[1]][1], "$"), names(dim_params)))) { + stop(paste0("All '*_across' parameters must be associated to a dimension parameter. Found parameter '", + names(across_params)[i], "' but no parameter '", + strsplit(names(across_params)[i], "_across$")[[1]][1], + "'.")) + } + i <- i + 1 + } + if (length(across_params) < 1) { + across_params <- NULL + } else { + names(across_params) <- gsub("_across$", "", names(across_params)) + } + inner_dims_across_files <- across_params + if (!is.logical(merge_across_dims)) { + stop("Parameter 'merge_across_dims' must be TRUE or FALSE.") + } + if (length(c(var_params_ind, dim_reorder_params_ind, tolerance_params_ind, + depends_params_ind, across_params_ind)) > 0) { + dim_params <- dim_params[-c(var_params_ind, dim_reorder_params_ind, + tolerance_params_ind, depends_params_ind, across_params_ind)] + if (merge_across_dims) { + for (inner_dim_across in names(inner_dims_across_files)) { + inner_dim_pos <- which(names(dim_params) == inner_dim_across) + file_dim_pos <- which(names(dim_params) == inner_dims_across_files[[inner_dim_across]]) + new_pos <- inner_dim_pos + if (file_dim_pos < inner_dim_pos) { + new_pos <- new_pos - 1 + } + dim_params_to_move <- dim_params[c(inner_dim_pos, + file_dim_pos)] + dim_params <- dim_params[-c(inner_dim_pos, file_dim_pos)] + new_dim_params <- list() + if (new_pos > 1) { + new_dim_params <- c(new_dim_params, dim_params[1:(new_pos - + 1)]) + } + new_dim_params <- c(new_dim_params, dim_params_to_move) + if (length(dim_params) >= new_pos) { + new_dim_params <- c(new_dim_params, dim_params[new_pos:length(dim_params)]) + } + dim_params <- new_dim_params + } + } + } + dim_names <- names(dim_params) + if (is.null(dim_names)) { + stop("At least one pattern dim must be specified.") + } + chunks <- vector("list", length(dim_names)) + names(chunks) <- dim_names + for (dim_name in dim_names) { + if (!is.null(attr(dim_params[[dim_name]], "chunk"))) { + chunks[[dim_name]] <- attr(dim_params[[dim_name]], + "chunk") + attributes(dim_params[[dim_name]]) <- attributes(dim_params[[dim_name]])[-which(names(attributes(dim_params[[dim_name]])) == + "chunk")] + } else { + chunks[[dim_name]] <- c(chunk = 1, n_chunks = 1) + } + } + chunk_indices <- function(n_indices, chunk, n_chunks, dim_name) { + if (n_chunks > n_indices) { + stop("Requested to divide dimension '", dim_name, + "' of length ", n_indices, " in ", n_chunks, + " chunks, which is not possible.") + } + chunk_sizes <- rep(floor(n_indices/n_chunks), n_chunks) + chunks_to_extend <- n_indices - chunk_sizes[1] * n_chunks + if (chunks_to_extend > 0) { + chunk_sizes[1:chunks_to_extend] <- chunk_sizes[1:chunks_to_extend] + + 1 + } + chunk_size <- chunk_sizes[chunk] + offset <- 0 + if (chunk > 1) { + offset <- sum(chunk_sizes[1:(chunk - 1)]) + } + indices <- 1:chunk_sizes[chunk] + offset + array(indices, dim = setNames(length(indices), dim_name)) + } + if (is.null(pattern_dims)) { + warning(paste0("Parameter 'pattern_dims' not specified. Taking the first dimension, '", + dim_names[1], "' as 'pattern_dims'.")) + pattern_dims <- dim_names[1] + } else if (is.character(pattern_dims) && (length(pattern_dims) > + 0)) { + pattern_dims <- unique(pattern_dims) + } else { + stop("Parameter 'pattern_dims' must be a vector of character strings.") + } + if (any(names(var_params) %in% pattern_dims)) { + stop("'*_var' parameters specified for pattern dimensions. Remove or fix them.") + } + found_pattern_dim <- NULL + for (pattern_dim in pattern_dims) { + dat <- datasets <- dim_params[[pattern_dim]] + if (is.null(dat) || !(is.character(dat) && all(nchar(dat) > + 0)) && !is.list(dat)) { + stop(paste0("Parameter '", pattern_dim, "' must be a list of lists with pattern specifications or a vector of character strings.")) + } + if (!is.null(dim_reorder_params[[pattern_dim]])) { + warning(paste0("A reorder for the selectors of '", + pattern_dim, "' has been specified, but it is a pattern dimension and the reorder will be ignored.")) + } + if (is.list(dat) || any(sapply(dat, is.list))) { + if (is.null(found_pattern_dim)) { + found_pattern_dim <- pattern_dim + } else { + stop("Found more than one pattern dim with pattern specifications (list of lists). One and only one pattern dim must contain pattern specifications.") + } + } + } + if (is.null(found_pattern_dim)) { + warning(paste0("Could not find any pattern dim with explicit data set descriptions (in the form of list of lists). Taking the first pattern dim, '", + pattern_dims[1], "', as dimension with pattern specifications.")) + found_pattern_dim <- pattern_dims[1] + } + i <- 1 + for (dim_reorder_param in dim_reorder_params) { + if (!is.function(dim_reorder_param)) { + stop("All '*_reorder' parameters must be functions.") + } else if (!any(grepl(paste0("^", strsplit(names(dim_reorder_params)[i], + "_reorder$")[[1]][1], "$"), names(dim_params)))) { + stop(paste0("All '*_reorder' parameters must be associated to a dimension parameter. Found parameter '", + names(dim_reorder_params)[i], "' but no parameter '", + strsplit(names(dim_reorder_params)[i], "_reorder$")[[1]][1], + "'.")) + } + i <- i + 1 + } + i <- 1 + for (tolerance_param in tolerance_params) { + if (!any(grepl(paste0("^", strsplit(names(tolerance_params)[i], + "_tolerance$")[[1]][1], "$"), names(dim_params)))) { + stop(paste0("All '*_tolerance' parameters must be associated to a dimension parameter. Found parameter '", + names(tolerance_params)[i], "' but no parameter '", + strsplit(names(tolerance_params)[i], "_tolerance$")[[1]][1], + "'.")) + } + i <- i + 1 + } + if (length(tolerance_params) < 1) { + tolerance_params <- NULL + } else { + names(tolerance_params) <- gsub("_tolerance$", "", names(tolerance_params)) + } + if (!is.null(metadata_dims)) { + if (is.na(metadata_dims)) { + metadata_dims <- NULL + } else if (!is.character(metadata_dims) || (length(metadata_dims) < + 1)) { + stop("Parameter 'metadata' dims must be a vector of at least one character string.") + } + } else { + metadata_dims <- pattern_dims + } + dats_to_take <- chunk_indices(length(dim_params[[found_pattern_dim]]), + chunks[[found_pattern_dim]]["chunk"], chunks[[found_pattern_dim]]["n_chunks"], + found_pattern_dim) + dim_params[[found_pattern_dim]] <- dim_params[[found_pattern_dim]][dats_to_take] + dat <- datasets <- dim_params[[found_pattern_dim]] + dat_info_names <- c("name", "path") + dat_to_fetch <- c() + dat_names <- c() + if (!is.list(dat)) { + dat <- as.list(dat) + } else { + if (!any(sapply(dat, is.list))) { + dat <- list(dat) + } + } + for (i in 1:length(dat)) { + if (is.character(dat[[i]]) && length(dat[[i]]) == 1 && + nchar(dat[[i]]) > 0) { + if (grepl("^(\\./|\\.\\./|/.*/|~/)", dat[[i]])) { + dat[[i]] <- list(path = dat[[i]]) + } else { + dat[[i]] <- list(name = dat[[i]]) + } + } else if (!is.list(dat[[i]])) { + stop(paste0("Parameter '", pattern_dim, "' is incorrect. It must be a list of lists or character strings.")) + } + if (!("name" %in% names(dat[[i]]))) { + dat[[i]][["name"]] <- paste0("dat", i) + if (!("path" %in% names(dat[[i]]))) { + stop(paste0("Parameter '", found_pattern_dim, + "' is incorrect. A 'path' should be provided for each dataset if no 'name' is provided.")) + } + } else if (!("path" %in% names(dat[[i]]))) { + dat_to_fetch <- c(dat_to_fetch, i) + } + dat_names <- c(dat_names, dat[[i]][["name"]]) + } + if ((length(dat_to_fetch) > 0) && (length(dat_to_fetch) < + length(dat))) { + warning("'path' has been provided for some datasets. Any information in the configuration file related to these will be ignored.") + } + if (length(dat_to_fetch) > 0) { + stop("Specified only the name for some data sets, but not the path ", + "pattern. This option has not been yet implemented.") + } + if (!is.null(inner_dims_across_files)) { + new_idaf <- list() + for (i in names(inner_dims_across_files)) { + if (!(inner_dims_across_files[[i]] %in% names(new_idaf))) { + new_idaf[[inner_dims_across_files[[i]]]] <- i + } else { + new_idaf[[inner_dims_across_files[[i]]]] <- c(new_idaf[[inner_dims_across_files[[i]]]], + i) + } + } + inner_dims_across_files <- new_idaf + } + if (is.null(return_vars)) { + return_vars <- list() + } + if (!is.list(return_vars)) { + stop("Parameter 'return_vars' must be a list or NULL.") + } + if (length(return_vars) > 0 && is.null(names(return_vars))) { + stop("Parameter 'return_vars' must be a named list.") + } + i <- 1 + while (i <= length(return_vars)) { + if (length(return_vars[[i]]) > 0) { + if (!is.character(return_vars[[i]])) { + stop("The ", i, "th specification in 'return_vars' is malformed. It ", + "must be a vector of character strings of valid file dimension ", + "names.") + } + } + i <- i + 1 + } + if (!is.null(synonims)) { + error <- FALSE + if (!is.list(synonims)) { + error <- TRUE + } + for (synonim_entry in names(synonims)) { + if (!(synonim_entry %in% names(dim_params)) && !(synonim_entry %in% + names(return_vars))) { + error <- TRUE + } + if (!is.character(synonims[[synonim_entry]]) || length(synonims[[synonim_entry]]) < + 1) { + error <- TRUE + } + } + if (error) { + stop("Parameter 'synonims' must be a named list, where the names are ", + "a name of a requested dimension or variable and the values are ", + "vectors of character strings with at least one alternative name ", + " for each dimension or variable in 'synonims'.") + } + } + if (length(unique(names(synonims))) < length(names(synonims))) { + stop("There must not be repeated entries in 'synonims'.") + } + if (length(unique(unlist(synonims))) < length(unlist(synonims))) { + stop("There must not be repeated values in 'synonims'.") + } + dim_entries_to_add <- which(!(names(dim_params) %in% names(synonims))) + if (length(dim_entries_to_add) > 0) { + synonims[names(dim_params)[dim_entries_to_add]] <- as.list(names(dim_params)[dim_entries_to_add]) + } + var_entries_to_add <- which(!(names(var_params) %in% names(synonims))) + if (length(var_entries_to_add) > 0) { + synonims[names(var_params)[var_entries_to_add]] <- as.list(names(var_params)[var_entries_to_add]) + } + if (is.null(selector_checker) || !is.function(selector_checker)) { + stop("Parameter 'selector_checker' must be a function.") + } + if (is.null(file_opener) || !is.function(file_opener)) { + stop("Parameter 'file_opener' must be a function.") + } + if (!is.null(file_var_reader) && !is.function(file_var_reader)) { + stop("Parameter 'file_var_reader' must be a function.") + } + if (!is.null(file_dim_reader) && !is.function(file_dim_reader)) { + stop("Parameter 'file_dim_reader' must be a function.") + } + if (is.null(file_data_reader) || !is.function(file_data_reader)) { + stop("Parameter 'file_data_reader' must be a function.") + } + if (is.null(file_closer) || !is.function(file_closer)) { + stop("Parameter 'file_closer' must be a function.") + } + if (!is.null(transform)) { + if (!is.function(transform)) { + stop("Parameter 'transform' must be a function.") + } + } + if (!is.null(transform_params)) { + if (!is.list(transform_params)) { + stop("Parameter 'transform_params' must be a list.") + } + if (is.null(names(transform_params))) { + stop("Parameter 'transform_params' must be a named list.") + } + } + if (!is.null(transform_vars)) { + if (!is.character(transform_vars)) { + stop("Parameter 'transform_vars' must be a vector of character strings.") + } + } + if (any(!(transform_vars %in% names(return_vars)))) { + stop("All the variables specified in 'transform_vars' must also be specified in 'return_vars'.") + } + if (!is.logical(apply_indices_after_transform)) { + stop("Parameter 'apply_indices_after_transform' must be either TRUE or FALSE.") + } + aiat <- apply_indices_after_transform + if (!is.numeric(transform_extra_cells)) { + stop("Parameter 'transform_extra_cells' must be numeric.") + } + transform_extra_cells <- round(transform_extra_cells) + if (!is.logical(split_multiselected_dims)) { + stop("Parameter 'split_multiselected_dims' must be TRUE or FALSE.") + } + if (!is.numeric(path_glob_permissive) && !is.logical(path_glob_permissive)) { + stop("Parameter 'path_glob_permissive' must be TRUE, FALSE or an integer.") + } + if (length(path_glob_permissive) != 1) { + stop("Parameter 'path_glob_permissive' must be of length 1.") + } + if (!is.logical(retrieve)) { + stop("Parameter 'retrieve' must be TRUE or FALSE.") + } + if (!is.null(num_procs)) { + if (!is.numeric(num_procs)) { + stop("Parameter 'num_procs' must be numeric.") + } + else { + num_procs <- round(num_procs) + } + } + if (!is.logical(silent)) { + stop("Parameter 'silent' must be logical.") + } + dim_params[[found_pattern_dim]] <- dat_names + if (!silent) { + message(paste0("Exploring files... This will take a variable amount ", + "of time depending on the issued request and the ", + "performance of the file server...")) + } + if (!is.character(debug)) { + dims_to_check <- c("time") + } else { + dims_to_check <- debug + debug <- TRUE + } + array_of_files_to_load <- NULL + array_of_not_found_files <- NULL + indices_of_first_files_with_data <- vector("list", length(dat)) + selectors_of_first_files_with_data <- vector("list", length(dat)) + dataset_has_files <- rep(FALSE, length(dat)) + found_file_dims <- vector("list", length(dat)) + expected_inner_dims <- vector("list", length(dat)) + for (i in 1:length(dat)) { + dat_selectors <- dim_params + dat_selectors[[found_pattern_dim]] <- dat_selectors[[found_pattern_dim]][i] + dim_vars <- paste0("$", dim_names, "$") + file_dims <- which(sapply(dim_vars, grepl, dat[[i]][["path"]], + fixed = TRUE)) + if (length(file_dims) > 0) { + file_dims <- dim_names[file_dims] + } + file_dims <- unique(c(pattern_dims, file_dims)) + found_file_dims[[i]] <- file_dims + expected_inner_dims[[i]] <- dim_names[which(!(dim_names %in% + file_dims))] + if (any(c(names(depending_file_dims), unlist(depending_file_dims)) %in% + expected_inner_dims[[i]])) { + stop(paste0("The dimension dependancies specified in ", + "'depending_file_dims' can only be between file ", + "dimensions, but some inner dimensions found in ", + "dependancies for '", dat[[i]][["name"]], "', which ", + "has the following file dimensions: ", paste(paste0("'", + file_dims, "'"), collapse = ", "), ".")) + } else { + a <- names(depending_file_dims) %in% file_dims + b <- unlist(depending_file_dims) %in% file_dims + ab <- a & b + if (any(!ab)) { + warning(paste0("Detected some dependancies in 'depending_file_dims' with ", + "non-existing dimension names. These will be disregarded.")) + depending_file_dims <- depending_file_dims[-which(!ab)] + } + if (any(names(depending_file_dims) == unlist(depending_file_dims))) { + depending_file_dims <- depending_file_dims[-which(names(depending_file_dims) == + unlist(depending_file_dims))] + } + } + if (any(!(names(inner_dims_across_files) %in% file_dims)) || + any(!(unlist(inner_dims_across_files) %in% expected_inner_dims[[i]]))) { + stop(paste0("All relationships specified in ", "'_across' parameters must be between a inner ", + "dimension and a file dimension. Found wrong ", + "specification for '", dat[[i]][["name"]], "', which ", + "has the following file dimensions: ", paste(paste0("'", + file_dims, "'"), collapse = ", "), ", and the following inner dimensions: ", + paste(paste0("'", expected_inner_dims[[i]], "'"), + collapse = ", "), ".")) + } + j <- 1 + while (j <= length(return_vars)) { + if (any(!(return_vars[[j]] %in% file_dims))) { + if (any(return_vars[[j]] %in% expected_inner_dims[[i]])) { + stop("Found variables in 'return_vars' requested ", + "for some inner dimensions (for dataset '", + dat[[i]][["name"]], "'), but variables can only be ", + "requested for file dimensions.") + } else { + stop("Found variables in 'return_vars' requested ", + "for non-existing dimensions.") + } + } + j <- j + 1 + } + if (!is.null(metadata_dims)) { + if (any(!(metadata_dims %in% file_dims))) { + stop("All dimensions in 'metadata_dims' must be file dimensions.") + } + } + for (dim_name in dim_names) { + if (!(dim_name %in% pattern_dims)) { + if (is.null(attr(dat_selectors[[dim_name]], "values")) || + is.null(attr(dat_selectors[[dim_name]], "indices"))) { + flag <- ((dat_selectors[[dim_name]] %in% c("all", + "first", "last")) || (is.numeric(unlist(dat_selectors[[dim_name]])))) + attr(dat_selectors[[dim_name]], "values") <- !flag + attr(dat_selectors[[dim_name]], "indices") <- flag + } + if ((attr(dat_selectors[[dim_name]], "values") || + (dim_name %in% c("var", "variable"))) && !(dim_name %in% + names(var_params)) && !(dim_name %in% file_dims)) { + if (dim_name %in% c("var", "variable")) { + var_params <- c(var_params, setNames(list("var_names"), + dim_name)) + warning(paste0("Found specified values for dimension '", + dim_name, "' but no '", dim_name, "_var' requested. ", + "\"", dim_name, "_var = '", "var_names", + "'", "\"", " has been automatically added to ", + "the Start call.")) + } else { + var_params <- c(var_params, setNames(list(dim_name), + dim_name)) + warning(paste0("Found specified values for dimension '", + dim_name, "' but no '", dim_name, "_var' requested. ", + "\"", dim_name, "_var = '", dim_name, "'", + "\"", " has been automatically added to ", + "the Start call.")) + } + } + } + } + if (any(!(unlist(var_params) %in% names(return_vars)))) { + vars_to_add <- which(!(unlist(var_params) %in% names(return_vars))) + new_return_vars <- vector("list", length(vars_to_add)) + names(new_return_vars) <- unlist(var_params)[vars_to_add] + return_vars <- c(return_vars, new_return_vars) + warning(paste0("All '*_var' params must associate a dimension to one of the ", + "requested variables in 'return_vars'. The following variables", + " have been added to 'return_vars': ", paste(paste0("'", + unlist(var_params), "'"), collapse = ", "))) + } + replace_values <- vector("list", length = length(file_dims)) + names(replace_values) <- file_dims + for (file_dim in file_dims) { + if (file_dim %in% names(var_params)) { + warning(paste0("The '", file_dim, "_var' param will be ignored since '", + file_dim, "' is a file dimension (for the dataset with pattern ", + dat[[i]][["path"]], ").")) + } + if (!is.list(dat_selectors[[file_dim]]) || (is.list(dat_selectors[[file_dim]]) && + length(dat_selectors[[file_dim]]) == 2 && is.null(names(dat_selectors[[file_dim]])))) { + dat_selectors[[file_dim]] <- list(dat_selectors[[file_dim]]) + } + first_class <- class(dat_selectors[[file_dim]][[1]]) + first_length <- length(dat_selectors[[file_dim]][[1]]) + for (j in 1:length(dat_selectors[[file_dim]])) { + sv <- selector_vector <- dat_selectors[[file_dim]][[j]] + if (!identical(first_class, class(sv)) || !identical(first_length, + length(sv))) { + stop("All provided selectors for depending dimensions must ", + "be vectors of the same length and of the same class.") + } + if (is.character(sv) && !((length(sv) == 1) && + (sv[1] %in% c("all", "first", "last")))) { + dat_selectors[[file_dim]][[j]] <- selector_checker(selectors = sv, + return_indices = FALSE) + dat_selectors[[file_dim]][[j]] <- dat_selectors[[file_dim]][[j]][chunk_indices(length(dat_selectors[[file_dim]][[j]]), + chunks[[file_dim]]["chunk"], chunks[[file_dim]]["n_chunks"], + file_dim)] + } + else if (!(is.numeric(sv) || (is.character(sv) && + (length(sv) == 1) && (sv %in% c("all", "first", + "last"))) || (is.list(sv) && (length(sv) == + 2) && (all(sapply(sv, is.character)) || all(sapply(sv, + is.numeric)))))) { + stop("All explicitly provided selectors for file dimensions must be character strings.") + } + } + sv <- dat_selectors[[file_dim]][[1]] + if (is.character(sv) && !((length(sv) == 1) && (sv[1] %in% + c("all", "first", "last")))) { + replace_values[[file_dim]] <- dat_selectors[[file_dim]][[1]][1] + } + } + undefined_file_dims <- file_dims[which(sapply(replace_values, + is.null))] + defined_file_dims <- file_dims[which(!(file_dims %in% + undefined_file_dims))] + for (file_dim in file_dims) { + if (file_dim %in% names(depending_file_dims)) { + if (all(c(file_dim, depending_file_dims[[file_dim]]) %in% + defined_file_dims)) { + if (length(dat_selectors[[file_dim]]) != length(dat_selectors[[depending_file_dims[[file_dim]]]][[1]])) { + stop(paste0("If providing selectors for the depending ", + "dimension '", file_dim, "', a ", "vector of selectors must be provided for ", + "each selector of the dimension it depends on, '", + depending_file_dims[[file_dim]], "'.")) + } + else if (!all(names(dat_selectors[[file_dim]]) == + dat_selectors[[depending_file_dims[[file_dim]]]][[1]])) { + stop(paste0("If providing selectors for the depending ", + "dimension '", file_dim, "', the name of the ", + "provided vectors of selectors must match ", + "exactly the selectors of the dimension it ", + "depends on, '", depending_file_dims[[file_dim]], + "'.")) + } + } + } + } + if (length(grep("^http", dat[[i]][["path"]])) > 0) { + if (length(undefined_file_dims) > 0) { + stop(paste0("All selectors for the file dimensions must be ", + "character strings if requesting data to a remote ", + "server. Found invalid selectors for the file dimensions ", + paste(paste0("'", undefined_file_dims, "'"), + collapse = ", "), ".")) + } + dataset_has_files[i] <- TRUE + } else { + dat[[i]][["path"]] <- path.expand(dat[[i]][["path"]]) + first_file <- NULL + first_file_selectors <- NULL + if (length(undefined_file_dims) > 0) { + replace_values[undefined_file_dims] <- "*" + } + files_to_check <- sapply(dat_selectors[defined_file_dims], + function(x) length(x[[1]])) + sub_array_of_files_to_check <- array(1:prod(files_to_check), + dim = files_to_check) + j <- 1 + while (j <= prod(files_to_check) && is.null(first_file)) { + selector_indices <- which(sub_array_of_files_to_check == + j, arr.ind = TRUE)[1, ] + selectors <- sapply(1:length(defined_file_dims), + function(x) { + vector_to_pick <- 1 + if (defined_file_dims[x] %in% names(depending_file_dims)) { + vector_to_pick <- selector_indices[which(defined_file_dims == + depending_file_dims[[defined_file_dims[x]]])] + } + dat_selectors[defined_file_dims][[x]][[vector_to_pick]][selector_indices[x]] + }) + replace_values[defined_file_dims] <- selectors + file_path <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values) + file_path <- Sys.glob(file_path) + if (length(file_path) > 0) { + first_file <- file_path[1] + first_file_selectors <- selectors + } + j <- j + 1 + } + if (is.null(first_file)) { + warning(paste0("No found files for the datset '", + dat[[i]][["name"]], "'. Provide existing selectors for the file dimensions ", + " or check and correct its path pattern: ", + dat[[i]][["path"]])) + } else { + dataset_has_files[i] <- TRUE + if (length(undefined_file_dims) > 0) { + first_values <- vector("list", length = length(undefined_file_dims)) + names(first_values) <- undefined_file_dims + found_values <- 0 + stop <- FALSE + try_dim <- 1 + last_success <- 1 + while ((found_values < length(undefined_file_dims)) && + !stop) { + u_file_dim <- undefined_file_dims[try_dim] + if (is.null(first_values[[u_file_dim]])) { + path_with_globs_and_tag <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values[-which(file_dims == u_file_dim)], + allow_undefined_key_vars = TRUE) + found_value <- .FindTagValue(path_with_globs_and_tag, + first_file, u_file_dim) + if (!is.null(found_value)) { + found_values <- found_values + 1 + last_success <- try_dim + first_values[[u_file_dim]] <- found_value + replace_values[[u_file_dim]] <- found_value + } + } + try_dim <- (try_dim%%length(undefined_file_dims)) + + 1 + if (try_dim == last_success) { + stop <- TRUE + } + } + if (found_values < length(undefined_file_dims)) { + stop(paste0("Path pattern of dataset '", + dat[[i]][["name"]], "' is too complex. Could not automatically ", + "detect values for all non-explicitly defined ", + "indices. Check its pattern: ", dat[[i]][["path"]])) + } + dat[[i]][["path"]] <- .ReplaceGlobExpressions(dat[[i]][["path"]], + first_file, replace_values, file_dims, dat[[i]][["name"]], + path_glob_permissive) + ufd <- c(undefined_file_dims[which(!(undefined_file_dims %in% + names(depending_file_dims)))], undefined_file_dims[which(undefined_file_dims %in% + names(depending_file_dims))]) + for (u_file_dim in ufd) { + replace_values[undefined_file_dims] <- first_values + replace_values[[u_file_dim]] <- "*" + depended_dim <- NULL + depended_dim_values <- NA + selectors <- dat_selectors[[u_file_dim]][[1]] + if (u_file_dim %in% names(depending_file_dims)) { + depended_dim <- depending_file_dims[[u_file_dim]] + depended_dim_values <- dat_selectors[[depended_dim]][[1]] + dat_selectors[[u_file_dim]] <- vector("list", + length = length(depended_dim_values)) + names(dat_selectors[[u_file_dim]]) <- depended_dim_values + } else { + dat_selectors[[u_file_dim]] <- list() + } + if (u_file_dim %in% unlist(depending_file_dims)) { + depending_dims <- names(depending_file_dims)[which(sapply(depending_file_dims, + function(x) u_file_dim %in% x))] + replace_values[depending_dims] <- rep("*", + length(depending_dims)) + } + for (j in 1:length(depended_dim_values)) { + parsed_values <- c() + if (!is.null(depended_dim)) { + replace_values[[depended_dim]] <- depended_dim_values[j] + } + path_with_globs <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values) + found_files <- Sys.glob(path_with_globs) + if (length(found_files) == 0) { + warning(paste0("Could not find files for any '", + u_file_dim, "' for '", depended_dim, + "' = '", depended_dim_values[j], "'.")) + dat_selectors[[u_file_dim]][[j]] <- NA + } else { + for (found_file in found_files) { + path_with_globs_and_tag <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values[-which(file_dims == + u_file_dim)], allow_undefined_key_vars = TRUE) + parsed_values <- c(parsed_values, .FindTagValue(path_with_globs_and_tag, + found_file, u_file_dim)) + } + dat_selectors[[u_file_dim]][[j]] <- selector_checker(selectors = selectors, + var = unique(parsed_values), return_indices = FALSE) + dat_selectors[[u_file_dim]][[j]] <- dat_selectors[[u_file_dim]][[j]][chunk_indices(length(dat_selectors[[u_file_dim]][[j]]), + chunks[[u_file_dim]]["chunk"], chunks[[u_file_dim]]["n_chunks"], + u_file_dim)] + } + } + } + } else { + dat[[i]][["path"]] <- .ReplaceGlobExpressions(dat[[i]][["path"]], + first_file, replace_values, defined_file_dims, + dat[[i]][["name"]], path_glob_permissive) + } + } + } + if (dataset_has_files[i]) { + known_dims <- file_dims + } else { + known_dims <- defined_file_dims + } + replace_values <- vector("list", length = length(known_dims)) + names(replace_values) <- known_dims + files_to_load <- sapply(dat_selectors[known_dims], function(x) length(x[[1]])) + files_to_load[found_pattern_dim] <- 1 + sub_array_of_files_to_load <- array(1:prod(files_to_load), + dim = files_to_load) + names(dim(sub_array_of_files_to_load)) <- known_dims + sub_array_of_not_found_files <- array(!dataset_has_files[i], + dim = files_to_load) + names(dim(sub_array_of_not_found_files)) <- known_dims + j <- 1 + while (j <= prod(files_to_load)) { + selector_indices <- which(sub_array_of_files_to_load == + j, arr.ind = TRUE)[1, ] + names(selector_indices) <- known_dims + selectors <- sapply(1:length(known_dims), function(x) { + vector_to_pick <- 1 + if (known_dims[x] %in% names(depending_file_dims)) { + vector_to_pick <- selector_indices[which(known_dims == + depending_file_dims[[known_dims[x]]])] + } + dat_selectors[known_dims][[x]][[vector_to_pick]][selector_indices[x]] + }) + names(selectors) <- known_dims + replace_values[known_dims] <- selectors + if (!dataset_has_files[i]) { + if (any(is.na(selectors))) { + replace_values <- replace_values[-which(names(replace_values) %in% + names(selectors[which(is.na(selectors))]))] + } + file_path <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values, TRUE) + sub_array_of_files_to_load[j] <- file_path + } else { + if (any(is.na(selectors))) { + replace_values <- replace_values[-which(names(replace_values) %in% + names(selectors[which(is.na(selectors))]))] + file_path <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values, TRUE) + sub_array_of_files_to_load[j] <- file_path + sub_array_of_not_found_files[j] <- TRUE + } else { + file_path <- .ReplaceVariablesInString(dat[[i]][["path"]], + replace_values) + if (!(length(grep("^http", file_path)) > 0)) { + if (grepl(file_path, "*", fixed = TRUE)) { + file_path_full <- Sys.glob(file_path)[1] + if (nchar(file_path_full) > 0) { + file_path <- file_path_full + } + } + } + sub_array_of_files_to_load[j] <- file_path + if (is.null(indices_of_first_files_with_data[[i]])) { + if (!(length(grep("^http", file_path)) > + 0)) { + if (!file.exists(file_path)) { + file_path <- NULL + } + } + if (!is.null(file_path)) { + test_file <- NULL + test_file <- file_opener(file_path) + if (!is.null(test_file)) { + selector_indices[which(known_dims == + found_pattern_dim)] <- i + indices_of_first_files_with_data[[i]] <- selector_indices + selectors_of_first_files_with_data[[i]] <- selectors + file_closer(test_file) + } + } + } + } + } + j <- j + 1 + } + if (is.null(array_of_files_to_load)) { + array_of_files_to_load <- sub_array_of_files_to_load + array_of_not_found_files <- sub_array_of_not_found_files + } else { + array_of_files_to_load <- .MergeArrays(array_of_files_to_load, + sub_array_of_files_to_load, along = found_pattern_dim) + array_of_not_found_files <- .MergeArrays(array_of_not_found_files, + sub_array_of_not_found_files, along = found_pattern_dim) + } + dat[[i]][["selectors"]] <- dat_selectors + } + if (all(sapply(indices_of_first_files_with_data, is.null))) { + stop("No data files found for any of the specified datasets.") + } + dims_to_iterate <- NULL + for (return_var in names(return_vars)) { + dims_to_iterate <- unique(c(dims_to_iterate, return_vars[[return_var]])) + } + if (found_pattern_dim %in% dims_to_iterate) { + dims_to_iterate <- dims_to_iterate[-which(dims_to_iterate == + found_pattern_dim)] + } + common_return_vars <- NULL + common_first_found_file <- NULL + common_return_vars_pos <- NULL + if (length(return_vars) > 0) { + common_return_vars_pos <- which(sapply(return_vars, function(x) !(found_pattern_dim %in% + x))) + } + if (length(common_return_vars_pos) > 0) { + common_return_vars <- return_vars[common_return_vars_pos] + return_vars <- return_vars[-common_return_vars_pos] + common_first_found_file <- rep(FALSE, length(which(sapply(common_return_vars, + length) == 0))) + names(common_first_found_file) <- names(common_return_vars[which(sapply(common_return_vars, + length) == 0)]) + } + return_vars <- lapply(return_vars, function(x) { + if (found_pattern_dim %in% x) { + x[-which(x == found_pattern_dim)] + } else { + x + } + }) + if (length(common_return_vars) > 0) { + picked_common_vars <- vector("list", length = length(common_return_vars)) + names(picked_common_vars) <- names(common_return_vars) + } else { + picked_common_vars <- NULL + } + picked_common_vars_ordered <- picked_common_vars + picked_common_vars_unorder_indices <- picked_common_vars + picked_vars <- vector("list", length = length(dat)) + names(picked_vars) <- dat_names + picked_vars_ordered <- picked_vars + picked_vars_unorder_indices <- picked_vars + for (i in 1:length(dat)) { + if (dataset_has_files[i]) { + for (inner_dim in expected_inner_dims[[i]]) { + if (!is.list(dat[[i]][["selectors"]][[inner_dim]]) || + (is.list(dat[[i]][["selectors"]][[inner_dim]]) && + length(dat[[i]][["selectors"]][[inner_dim]]) == + 2 && is.null(names(dat[[i]][["selectors"]][[inner_dim]])))) { + dat[[i]][["selectors"]][[inner_dim]] <- list(dat[[i]][["selectors"]][[inner_dim]]) + } + } + if (length(return_vars) > 0) { + picked_vars[[i]] <- vector("list", length = length(return_vars)) + names(picked_vars[[i]]) <- names(return_vars) + picked_vars_ordered[[i]] <- picked_vars[[i]] + picked_vars_unorder_indices[[i]] <- picked_vars[[i]] + } + indices_of_first_file <- as.list(indices_of_first_files_with_data[[i]]) + array_file_dims <- sapply(dat[[i]][["selectors"]][found_file_dims[[i]]], + function(x) length(x[[1]])) + names(array_file_dims) <- found_file_dims[[i]] + if (length(dims_to_iterate) > 0) { + indices_of_first_file[dims_to_iterate] <- lapply(array_file_dims[dims_to_iterate], + function(x) 1:x) + } + array_of_var_files <- do.call("[", c(list(x = array_of_files_to_load), + indices_of_first_file, list(drop = FALSE))) + array_of_var_indices <- array(1:length(array_of_var_files), + dim = dim(array_of_var_files)) + array_of_not_found_var_files <- do.call("[", c(list(x = array_of_not_found_files), + indices_of_first_file, list(drop = FALSE))) + previous_indices <- rep(-1, length(indices_of_first_file)) + names(previous_indices) <- names(indices_of_first_file) + first_found_file <- NULL + if (length(return_vars) > 0) { + first_found_file <- rep(FALSE, length(which(sapply(return_vars, + length) == 0))) + names(first_found_file) <- names(return_vars[which(sapply(return_vars, + length) == 0)]) + } + for (j in 1:length(array_of_var_files)) { + current_indices <- which(array_of_var_indices == + j, arr.ind = TRUE)[1, ] + names(current_indices) <- names(indices_of_first_file) + if (!is.na(array_of_var_files[j]) && !array_of_not_found_var_files[j]) { + changed_dims <- which(current_indices != previous_indices) + vars_to_read <- NULL + if (length(return_vars) > 0) { + vars_to_read <- names(return_vars)[sapply(return_vars, + function(x) any(names(changed_dims) %in% + x))] + } + if (!is.null(first_found_file)) { + if (any(!first_found_file)) { + vars_to_read <- c(vars_to_read, names(first_found_file[which(!first_found_file)])) + } + } + if ((i == 1) && (length(common_return_vars) > + 0)) { + vars_to_read <- c(vars_to_read, names(common_return_vars)[sapply(common_return_vars, + function(x) any(names(changed_dims) %in% + x))]) + } + if (!is.null(common_first_found_file)) { + if (any(!common_first_found_file)) { + vars_to_read <- c(vars_to_read, names(common_first_found_file[which(!common_first_found_file)])) + } + } + file_object <- file_opener(array_of_var_files[j]) + if (!is.null(file_object)) { + for (var_to_read in vars_to_read) { + if (var_to_read %in% unlist(var_params)) { + associated_dim_name <- names(var_params)[which(unlist(var_params) == + var_to_read)] + } + var_name_to_reader <- var_to_read + names(var_name_to_reader) <- "var" + var_dims <- file_dim_reader(NULL, file_object, + var_name_to_reader, NULL, synonims) + names(var_dims) <- sapply(names(var_dims), + function(x) { + which_entry <- which(sapply(synonims, + function(y) x %in% y)) + if (length(which_entry) > 0) { + names(synonims)[which_entry] + } else { + x + } + }) + if (!is.null(var_dims)) { + var_file_dims <- NULL + if (var_to_read %in% names(common_return_vars)) { + var_to_check <- common_return_vars[[var_to_read]] + } else { + var_to_check <- return_vars[[var_to_read]] + } + if (any(names(dim(array_of_files_to_load)) %in% + var_to_check)) { + var_file_dims <- dim(array_of_files_to_load)[which(names(dim(array_of_files_to_load)) %in% + var_to_check)] + } + if (((var_to_read %in% names(common_return_vars)) && + is.null(picked_common_vars[[var_to_read]])) || + ((var_to_read %in% names(return_vars)) && + is.null(picked_vars[[i]][[var_to_read]]))) { + if (any(names(var_file_dims) %in% names(var_dims))) { + stop("Found a requested var in 'return_var' requested for a ", + "file dimension which also appears in the dimensions of ", + "the variable inside the file.\n", + array_of_var_files[j]) + } + special_types <- list(POSIXct = as.POSIXct, + POSIXlt = as.POSIXlt, Date = as.Date) + first_sample <- file_var_reader(NULL, + file_object, NULL, var_to_read, synonims) + if (any(class(first_sample) %in% names(special_types))) { + array_size <- prod(c(var_file_dims, + var_dims)) + new_array <- rep(special_types[[class(first_sample)[1]]](NA), + array_size) + dim(new_array) <- c(var_file_dims, + var_dims) + } else { + new_array <- array(dim = c(var_file_dims, + var_dims)) + } + attr(new_array, "variables") <- attr(first_sample, + "variables") + if (var_to_read %in% names(common_return_vars)) { + picked_common_vars[[var_to_read]] <- new_array + pick_ordered <- FALSE + if (var_to_read %in% unlist(var_params)) { + if (associated_dim_name %in% names(dim_reorder_param) && + !aiat) { + picked_common_vars_ordered[[var_to_read]] <- new_array + pick_ordered <- TRUE + } + } + if (!pick_ordered) { + picked_common_vars_ordered[[var_to_read]] <- NULL + } + } else { + picked_vars[[i]][[var_to_read]] <- new_array + pick_ordered <- FALSE + if (var_to_read %in% unlist(var_params)) { + if (associated_dim_name %in% names(dim_reorder_params) && + !aiat) { + picked_vars_ordered[[i]][[var_to_read]] <- new_array + pick_ordered <- TRUE + } + } + if (!pick_ordered) { + picked_vars_ordered[[i]][[var_to_read]] <- NULL + } + } + } else { + if (var_to_read %in% names(common_return_vars)) { + array_var_dims <- dim(picked_common_vars[[var_to_read]]) + } else { + array_var_dims <- dim(picked_vars[[i]][[var_to_read]]) + } + full_array_var_dims <- array_var_dims + if (any(names(array_var_dims) %in% + names(var_file_dims))) { + array_var_dims <- array_var_dims[-which(names(array_var_dims) %in% + names(var_file_dims))] + } + if (names(array_var_dims) != names(var_dims)) { + stop("Error while reading the variable '", + var_to_read, "' from ", "the file. Dimensions do not match.\nExpected ", + paste(paste0("'", names(array_var_dims), + "'"), collapse = ", "), " but found ", + paste(paste0("'", names(var_dims), + "'"), collapse = ", "), ".\n", + array_of_var_files[j]) + } + if (any(var_dims > array_var_dims)) { + longer_dims <- which(var_dims > array_var_dims) + if (length(longer_dims) == 1) { + longer_dims_in_full_array <- longer_dims + if (any(names(full_array_var_dims) %in% + names(var_file_dims))) { + candidates <- (1:length(full_array_var_dims))[-which(names(full_array_var_dims) %in% + names(var_file_dims))] + longer_dims_in_full_array <- candidates[longer_dims] + } + padding_dims <- full_array_var_dims + padding_dims[longer_dims_in_full_array] <- var_dims[longer_dims] - + array_var_dims[longer_dims] + special_types <- list(POSIXct = as.POSIXct, + POSIXlt = as.POSIXlt, Date = as.Date) + if (var_to_read %in% names(common_return_vars)) { + var_class <- class(picked_common_vars[[var_to_read]]) + } else { + var_class <- class(picked_vars[[i]][[var_to_read]]) + } + if (any(var_class %in% names(special_types))) { + padding_size <- prod(padding_dims) + padding <- rep(special_types[[var_class[1]]](NA), + padding_size) + dim(padding) <- padding_dims + } else { + padding <- array(dim = padding_dims) + } + if (var_to_read %in% names(common_return_vars)) { + picked_common_vars[[var_to_read]] <- .abind2(picked_common_vars[[var_to_read]], + padding, names(full_array_var_dims)[longer_dims_in_full_array]) + } else { + picked_vars[[i]][[var_to_read]] <- .abind2(picked_vars[[i]][[var_to_read]], + padding, names(full_array_var_dims)[longer_dims_in_full_array]) + } + } else { + stop("Error while reading the variable '", + var_to_read, "' from ", "the file. Found size (", + paste(var_dims, collapse = " x "), + ") is greater than expected maximum size (", + array_var_dims, ").") + } + } + } + var_store_indices <- c(as.list(current_indices[names(var_file_dims)]), + lapply(var_dims, function(x) 1:x)) + var_values <- file_var_reader(NULL, file_object, + NULL, var_to_read, synonims) + if (var_to_read %in% unlist(var_params)) { + if ((associated_dim_name %in% names(dim_reorder_params)) && + !aiat) { + if (length(dim(var_values)) > 1) { + stop("Requested a '", associated_dim_name, + "_reorder' for a dimension ", + "whose coordinate variable that has more than 1 dimension. This is ", + "not supported.") + } + ordered_var_values <- dim_reorder_params[[associated_dim_name]](var_values) + attr(ordered_var_values$x, "variables") <- attr(var_values, + "variables") + if (!all(c("x", "ix") %in% names(ordered_var_values))) { + stop("All the dimension reorder functions must return a list with the components 'x' and 'ix'.") + } + unorder <- sort(ordered_var_values$ix, + index.return = TRUE)$ix + if (var_to_read %in% names(common_return_vars)) { + picked_common_vars_ordered[[var_to_read]] <- do.call("[<-", + c(list(x = picked_common_vars_ordered[[var_to_read]]), + var_store_indices, list(value = ordered_var_values$x))) + picked_common_vars_unorder_indices[[var_to_read]] <- do.call("[<-", + c(list(x = picked_common_vars_unorder_indices[[var_to_read]]), + var_store_indices, list(value = unorder))) + } else { + picked_vars_ordered[[i]][[var_to_read]] <- do.call("[<-", + c(list(x = picked_vars_ordered[[i]][[var_to_read]]), + var_store_indices, list(value = ordered_var_values$x))) + picked_vars_unorder_indices[[i]][[var_to_read]] <- do.call("[<-", + c(list(x = picked_vars_unorder_indices[[i]][[var_to_read]]), + var_store_indices, list(value = unorder))) + } + } + } + if (var_to_read %in% names(common_return_vars)) { + picked_common_vars[[var_to_read]] <- do.call("[<-", + c(list(x = picked_common_vars[[var_to_read]]), + var_store_indices, list(value = var_values))) + } else { + picked_vars[[i]][[var_to_read]] <- do.call("[<-", + c(list(x = picked_vars[[i]][[var_to_read]]), + var_store_indices, list(value = var_values))) + } + if (var_to_read %in% names(first_found_file)) { + first_found_file[var_to_read] <- TRUE + } + if (var_to_read %in% names(common_first_found_file)) { + common_first_found_file[var_to_read] <- TRUE + } + } else { + stop("Could not find variable '", var_to_read, + "' in the file ", array_of_var_files[j]) + } + } + file_closer(file_object) + } + } + previous_indices <- current_indices + } + } + } + transform_indices <- function(v, n, m) { + unique2 <- function(v) { + if (length(v) < 2) { + v + } else { + v[c(1, v[2:length(v)] - v[1:(length(v) - 1)]) != + 0] + } + } + unique2(round(((v - 1)/(n - 1)) * (m - 1))) + 1 + } + beta <- transform_extra_cells + dims_to_crop <- vector("list") + transformed_vars <- vector("list", length = length(dat)) + names(transformed_vars) <- dat_names + transformed_vars_ordered <- transformed_vars + transformed_vars_unorder_indices <- transformed_vars + transformed_common_vars <- NULL + transformed_common_vars_ordered <- NULL + transformed_common_vars_unorder_indices <- NULL + for (i in 1:length(dat)) { + if (dataset_has_files[i]) { + indices <- indices_of_first_files_with_data[[i]] + if (!is.null(indices)) { + file_path <- do.call("[", c(list(array_of_files_to_load), + as.list(indices_of_first_files_with_data[[i]]))) + data_dims <- NULL + if (length(unlist(var_params[expected_inner_dims[[i]]])) < + length(expected_inner_dims[[i]])) { + file_to_open <- file_path + data_dims <- file_dim_reader(file_to_open, + NULL, selectors_of_first_files_with_data[[i]], + lapply(dat[[i]][["selectors"]][expected_inner_dims[[i]]], + "[[", 1), synonims) + names(data_dims) <- sapply(names(data_dims), + function(x) { + which_entry <- which(sapply(synonims, function(y) x %in% + y)) + if (length(which_entry) > 0) { + names(synonims)[which_entry] + } else { + x + } + }) + } + if (!is.null(transform) && (length(transform_vars) > + 0)) { + if (!all(transform_vars %in% c(names(picked_vars[[i]]), + names(picked_common_vars)))) { + stop("Could not find all the required variables in 'transform_vars' ", + "for the dataset '", dat[[i]][["name"]], + "'.") + } + vars_to_transform <- NULL + picked_vars_to_transform <- which(names(picked_vars[[i]]) %in% + transform_vars) + if (length(picked_vars_to_transform) > 0) { + picked_vars_to_transform <- names(picked_vars[[i]])[picked_vars_to_transform] + new_vars_to_transform <- picked_vars[[i]][picked_vars_to_transform] + which_are_ordered <- which(!sapply(picked_vars_ordered[[i]][picked_vars_to_transform], + is.null)) + if (length(which_are_ordered) > 0) { + new_vars_to_transform[which_are_ordered] <- picked_vars_ordered[[i]][which_are_ordered] + } + vars_to_transform <- c(vars_to_transform, + new_vars_to_transform) + } + picked_common_vars_to_transform <- which(names(picked_common_vars) %in% + transform_vars) + if (length(picked_common_vars_to_transform) > + 0) { + picked_common_vars_to_transform <- names(picked_common_vars)[picked_common_vars_to_transform] + new_vars_to_transform <- picked_common_vars[[i]][picked_common_vars_to_transform] + which_are_ordered <- which(!sapply(picked_common_vars_ordered[[i]][picked_common_vars_to_transform], + is.null)) + if (length(which_are_ordered) > 0) { + new_vars_to_transform[which_are_ordered] <- picked_common_vars_ordered[[i]][which_are_ordered] + } + vars_to_transform <- c(vars_to_transform, + new_vars_to_transform) + } + transformed_data <- do.call(transform, c(list(data_array = NULL, + variables = vars_to_transform, file_selectors = selectors_of_first_files_with_data[[i]]), + transform_params)) + if (!is.null(transformed_common_vars)) { + common_ones <- which(names(picked_common_vars) %in% + names(transformed_data$variables)) + if (length(common_ones) > 0) { + transformed_data$variables <- transformed_data$variables[-common_ones] + } + } + transformed_vars[[i]] <- list() + transformed_vars_ordered[[i]] <- list() + transformed_vars_unorder_indices[[i]] <- list() + for (var_to_read in names(transformed_data$variables)) { + if (var_to_read %in% unlist(var_params)) { + associated_dim_name <- names(var_params)[which(unlist(var_params) == + var_to_read)] + if ((associated_dim_name %in% names(dim_reorder_params)) && + aiat) { + if (length(dim(transformed_data$variables[[associated_dim_name]])) > + 1) { + stop("Requested a '", associated_dim_name, + "_reorder' for a dimension ", "whose coordinate variable that has more than 1 dimension (after ", + "transform). This is not supported.") + } + ordered_var_values <- dim_reorder_params[[associated_dim_name]](transformed_data$variables[[associated_dim_name]]) + attr(ordered_var_values, "variables") <- attr(transformed_data$variables[[associated_dim_name]], + "variables") + if (!all(c("x", "ix") %in% names(ordered_var_values))) { + stop("All the dimension reorder functions must return a list with the components 'x' and 'ix'.") + } + unorder <- sort(ordered_var_values$ix, + index.return = TRUE)$ix + if (var_to_read %in% names(picked_common_vars)) { + transformed_common_vars_ordered[[var_to_read]] <- ordered_var_values$x + transformed_common_vars_unorder_indices[[var_to_read]] <- unorder + } + else { + transformed_vars_ordered[[i]][[var_to_read]] <- ordered_var_values$x + transformed_vars_unorder_indices[[i]][[var_to_read]] <- unorder + } + } + } + } + transformed_picked_vars <- which(names(picked_vars[[i]]) %in% + names(transformed_data$variables)) + if (length(transformed_picked_vars) > 0) { + transformed_picked_vars <- names(picked_vars[[i]])[transformed_picked_vars] + transformed_vars[[i]][transformed_picked_vars] <- transformed_data$variables[transformed_picked_vars] + } + if (is.null(transformed_common_vars)) { + transformed_picked_common_vars <- which(names(picked_common_vars) %in% + names(transformed_data$variables)) + if (length(transformed_picked_common_vars) > + 0) { + transformed_picked_common_vars <- names(picked_common_vars)[transformed_picked_common_vars] + transformed_common_vars <- transformed_data$variables[transformed_picked_common_vars] + } + } + } + for (inner_dim in expected_inner_dims[[i]]) { + if (debug) { + print("-> DEFINING INDICES FOR INNER DIMENSION:") + print(inner_dim) + } + file_dim <- NULL + if (inner_dim %in% unlist(inner_dims_across_files)) { + file_dim <- names(inner_dims_across_files)[which(sapply(inner_dims_across_files, + function(x) inner_dim %in% x))[1]] + chunk_amount <- length(dat[[i]][["selectors"]][[file_dim]][[1]]) + names(chunk_amount) <- file_dim + } + else { + chunk_amount <- 1 + } + if ((dat[[i]][["selectors"]][[inner_dim]][[1]] %in% + c("all", "first", "last")) && (chunks[[inner_dim]]["n_chunks"] != + 1)) { + selectors <- dat[[i]][["selectors"]][[inner_dim]][[1]] + if (selectors == "all") { + selectors <- indices(1:(data_dims[[inner_dim]] * + chunk_amount)) + } + else if (selectors == "first") { + selectors <- indices(1) + } + else { + selectors <- indices(data_dims[[inner_dim]] * + chunk_amount) + } + dat[[i]][["selectors"]][[inner_dim]][[1]] <- selectors + } + selector_array <- dat[[i]][["selectors"]][[inner_dim]][[1]] + if (debug) { + if (inner_dim %in% dims_to_check) { + print(paste0("-> DEBUG MESSAGES FOR THE DATASET", + i, " AND INNER DIMENSION '", inner_dim, + "':")) + print("-> STRUCTURE OF SELECTOR ARRAY:") + print(str(selector_array)) + print("-> PICKED VARS:") + print(picked_vars) + print("-> TRANSFORMED VARS:") + print(transformed_vars) + } + } + if (is.null(dim(selector_array))) { + dim(selector_array) <- length(selector_array) + } + if (is.null(names(dim(selector_array)))) { + if (length(dim(selector_array)) == 1) { + names(dim(selector_array)) <- inner_dim + } + else { + stop("Provided selector arrays must be provided with dimension ", + "names. Found an array of selectors without dimension names ", + "for the dimension '", inner_dim, "'.") + } + } + selectors_are_indices <- FALSE + if (!is.null(attr(selector_array, "indices"))) { + if (!is.logical(attr(selector_array, "indices"))) { + stop("The atribute 'indices' for the selectors for the dimension '", + inner_dim, "' must be TRUE or FALSE.") + } + selectors_are_indices <- attr(selector_array, + "indices") + } + taken_chunks <- rep(FALSE, chunk_amount) + selector_file_dims <- 1 + if (any(found_file_dims[[i]] %in% names(dim(selector_array)))) { + selector_file_dims <- dim(selector_array)[which(names(dim(selector_array)) %in% + found_file_dims[[i]])] + } + selector_inner_dims <- dim(selector_array)[which(!(names(dim(selector_array)) %in% + found_file_dims[[i]]))] + var_with_selectors <- NULL + var_with_selectors_name <- var_params[[inner_dim]] + var_ordered <- NULL + var_unorder_indices <- NULL + with_transform <- FALSE + if (!is.null(var_with_selectors_name)) { + if ((var_with_selectors_name %in% transform_vars) && + (!is.null(transform))) { + with_transform <- TRUE + if (!is.null(file_dim)) { + stop("Requested a transformation over the dimension '", + inner_dim, "', wich goes across files. This feature ", + "is not supported. Either do the request without the ", + "transformation or request it over dimensions that do ", + "not go across files.") + } + } + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> NAME OF THE VARIABLE WITH THE SELECTOR VALUES FOR THE CURRENT INNER DIMENSION:") + print(var_with_selectors_name) + print("-> NAMES OF THE VARIABLES TO BE TRANSFORMED:") + print(transform_vars) + print("-> STRUCTURE OF THE TRANSFORMATION FUNCTION:") + print(str(transform)) + } + } + if (var_with_selectors_name %in% names(picked_vars[[i]])) { + var_with_selectors <- picked_vars[[i]][[var_with_selectors_name]] + var_ordered <- picked_vars_ordered[[i]][[var_with_selectors_name]] + var_unorder_indices <- picked_vars_unorder_indices[[i]][[var_with_selectors_name]] + } + else if (var_with_selectors_name %in% names(picked_common_vars)) { + var_with_selectors <- picked_common_vars[[var_with_selectors_name]] + var_ordered <- picked_common_vars_ordered[[var_with_selectors_name]] + var_unorder_indices <- picked_common_vars_unorder_indices[[var_with_selectors_name]] + } + n <- prod(dim(var_with_selectors)) + if (is.null(var_unorder_indices)) { + var_unorder_indices <- 1:n + } + if (with_transform) { + if (var_with_selectors_name %in% names(transformed_vars[[i]])) { + m <- prod(dim(transformed_vars[[i]][[var_with_selectors_name]])) + if (aiat) { + var_with_selectors <- transformed_vars[[i]][[var_with_selectors_name]] + var_ordered <- transformed_vars_ordered[[i]][[var_with_selectors_name]] + var_unorder_indices <- transformed_vars_unorder_indices[[i]][[var_with_selectors_name]] + } + } + else if (var_with_selectors_name %in% names(transformed_common_vars)) { + m <- prod(dim(transformed_common_vars[[var_with_selectors_name]])) + if (aiat) { + var_with_selectors <- transformed_common_vars[[var_with_selectors_name]] + var_ordered <- transformed_common_vars_ordered[[var_with_selectors_name]] + var_unorder_indices <- transformed_common_vars_unorder_indices[[var_with_selectors_name]] + } + } + if (is.null(var_unorder_indices)) { + var_unorder_indices <- 1:m + } + } + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> SIZE OF ORIGINAL VARIABLE:") + print(n) + print("-> SIZE OF TRANSFORMED VARIABLE:") + if (with_transform) + print(m) + print("-> STRUCTURE OF ORDERED VAR:") + print(str(var_ordered)) + print("-> UNORDER INDICES:") + print(var_unorder_indices) + } + } + var_dims <- dim(var_with_selectors) + var_file_dims <- 1 + if (any(names(var_dims) %in% found_file_dims[[i]])) { + if (with_transform) { + stop("Requested transformation for inner dimension '", + inner_dim, "' but provided selectors for such dimension ", + "over one or more file dimensions. This is not ", + "supported. Either request no transformation for the ", + "dimension '", inner_dim, "' or specify the ", + "selectors for this dimension without the file dimensions.") + } + var_file_dims <- var_dims[which(names(var_dims) %in% + found_file_dims[[i]])] + var_dims <- var_dims[-which(names(var_dims) %in% + found_file_dims[[i]])] + } + if (inner_dim %in% unlist(inner_dims_across_files)) { + if (length(var_dims) > 1) { + stop("Specified a '", inner_dim, "_var' for the dimension '", + inner_dim, "', which goes across files (across '", + file_dim, "'). The specified variable, '", + var_with_selectors_name, "', has more ", + "than one dimension and can not be used as selector variable. ", + "Select another variable or fix it in the files.") + } + } + var_full_dims <- dim(var_with_selectors) + if (!(inner_dim %in% names(var_full_dims))) { + stop("Could not find the dimension '", + inner_dim, "' in ", "the file. Either change the dimension name in ", + "your request, adjust the parameter ", + "'dim_names_in_files' or fix the dimension name in ", + "the file.\n", file_path) + } + } + else if (((is.numeric(selector_array) || is.list(selector_array)) && + selectors_are_indices) || (is.character(selector_array) && + (length(selector_array) == 1) && (selector_array %in% + c("all", "first", "last")) && !is.null(file_dim_reader))) { + if (!(inner_dim %in% names(data_dims))) { + stop("Could not find the dimension '", + inner_dim, "' in ", "the file. Either change the dimension name in ", + "your request, adjust the parameter ", + "'dim_names_in_files' or fix the dimension name in ", + "the file.\n", file_path) + } + } + else { + stop(paste0("Can not translate the provided selectors for '", + inner_dim, "' to numeric indices. Provide numeric indices and a ", + "'file_dim_reader' function, or a '", inner_dim, + "_var' in order to calculate the indices.")) + } + fri <- first_round_indices <- NULL + sri <- second_round_indices <- NULL + tvi <- tranaformed_variable_indices <- NULL + ordered_fri <- NULL + ordered_sri <- NULL + if ((length(selector_array) == 1) && is.character(selector_array) && + (selector_array %in% c("all", "first", "last")) && + (chunks[[inner_dim]]["n_chunks"] == 1)) { + if (is.null(var_with_selectors_name)) { + fri <- vector("list", length = chunk_amount) + dim(fri) <- c(chunk_amount) + sri <- vector("list", length = chunk_amount) + dim(sri) <- c(chunk_amount) + if (selector_array == "all") { + fri[] <- replicate(chunk_amount, list(1:(data_dims[inner_dim]))) + taken_chunks <- rep(TRUE, chunk_amount) + } + else if (selector_array == "first") { + fri[[1]] <- 1 + taken_chunks[1] <- TRUE + } + else if (selector_array == "last") { + fri[[chunk_amount]] <- data_dims[inner_dim] + taken_chunks[length(taken_chunks)] <- TRUE + } + } + else { + if ((!is.null(file_dim)) && !(file_dim %in% + names(var_file_dims))) { + stop("The variable '", var_with_selectors_name, + "' must also be ", "requested for the file dimension '", + file_dim, "' in ", "this configuration.") + } + fri <- vector("list", length = prod(var_file_dims)) + dim(fri) <- var_file_dims + ordered_fri <- fri + sri <- vector("list", length = prod(var_file_dims)) + dim(sri) <- var_file_dims + ordered_sri <- sri + if (selector_array == "all") { + ordered_fri[] <- replicate(prod(var_file_dims), + list(1:n)) + fri[] <- replicate(prod(var_file_dims), + list(var_unorder_indices[1:n])) + taken_chunks <- rep(TRUE, chunk_amount) + if (!with_transform) { + } + else { + ordered_sri[] <- replicate(prod(var_file_dims), + list(1:m)) + sri[] <- replicate(prod(var_file_dims), + list(1:m)) + tvi <- 1:m + } + } + else if (selector_array == "first") { + taken_chunks[1] <- TRUE + if (!with_transform) { + ordered_fri[[1]] <- 1 + fri[[1]] <- var_unorder_indices[1] + } + else { + if (!aiat) { + ordered_fri[[1]] <- 1 + fri[[1]] <- var_unorder_indices[1] + ordered_sri[[1]] <- 1:ceiling(m/n) + sri[[1]] <- 1:ceiling(m/n) + tvi <- 1:ceiling(m/n) + } + else { + ordered_fri[[1]] <- 1:ceiling(m/n) + fri[[1]] <- var_unorder_indices[1:ceiling(m/n)] + ordered_sri[[1]] <- 1 + sri[[1]] <- 1 + tvi <- 1 + } + } + } + else if (selector_array == "last") { + taken_chunks[length(taken_chunks)] <- TRUE + if (!with_transform) { + ordered_fri[[prod(var_file_dims)]] <- n + fri[[prod(var_file_dims)]] <- var_unorder_indices[n] + } + else { + if (!aiat) { + ordered_fri[[prod(var_file_dims)]] <- prod(var_dims) + fri[[prod(var_file_dims)]] <- var_unorder_indices[prod(var_dims)] + ordered_sri[[prod(var_file_dims)]] <- 1:ceiling(m/n) + sri[[prod(var_file_dims)]] <- 1:ceiling(m/n) + tvi <- 1:ceiling(m/n) + } + else { + ordered_fri[[prod(var_file_dims)]] <- (n - + ceiling(m/n) + 1):n + fri[[prod(var_file_dims)]] <- var_unorder_indices[(n - + ceiling(m/n) + 1):n] + ordered_sri[[prod(var_file_dims)]] <- 1 + sri[[prod(var_file_dims)]] <- 1 + tvi <- 1 + } + } + } + } + } + else { + if (!is.null(var_with_selectors_name)) { + unmatching_file_dims <- which(!(names(var_file_dims) %in% + names(selector_file_dims))) + if ((length(unmatching_file_dims) > 0)) { + raise_error <- FALSE + if (is.null(file_dim)) { + raise_error <- TRUE + } + else { + if (!((length(unmatching_file_dims) == + 1) && (names(var_file_dims)[unmatching_file_dims] == + file_dim) && (inner_dim %in% names(selector_inner_dims)))) { + raise_error <- TRUE + } + } + if (raise_error) { + stop("Provided selectors for the dimension '", + inner_dim, "' must have as many ", + "file dimensions as the variable the dimension is defined along, '", + var_with_selectors_name, "', with the exceptions of the file pattern dimension ('", + found_pattern_dim, "') and any depended file dimension (if specified as ", + "depended dimension in parameter 'inner_dims_across_files' and the ", + "depending file dimension is present in the provided selector array).") + } + } + if (any(names(selector_file_dims) %in% + names(dim(var_with_selectors)))) { + if (any(dim(var_with_selectors)[names(selector_file_dims)] != + selector_file_dims)) { + stop("Size of selector file dimensions must mach size of requested ", + "variable dimensions.") + } + } + } + if (is.null(names(selector_file_dims))) { + if (is.null(file_dim)) { + fri_dims <- 1 + } + else { + fri_dims <- chunk_amount + names(fri_dims) <- file_dim + } + } + else { + fri_dim_names <- names(selector_file_dims) + if (!is.null(file_dim)) { + fri_dim_names <- c(fri_dim_names, file_dim) + } + fri_dim_names <- found_file_dims[[i]][which(found_file_dims[[i]] %in% + fri_dim_names)] + fri_dims <- rep(NA, length(fri_dim_names)) + names(fri_dims) <- fri_dim_names + fri_dims[names(selector_file_dims)] <- selector_file_dims + if (!is.null(file_dim)) { + fri_dims[file_dim] <- chunk_amount + } + } + fri <- vector("list", length = prod(fri_dims)) + dim(fri) <- fri_dims + sri <- vector("list", length = prod(fri_dims)) + dim(sri) <- fri_dims + selector_file_dim_array <- array(1:prod(selector_file_dims), + dim = selector_file_dims) + selector_store_position <- fri_dims + for (j in 1:prod(dim(selector_file_dim_array))) { + selector_indices_to_take <- which(selector_file_dim_array == + j, arr.ind = TRUE)[1, ] + names(selector_indices_to_take) <- names(selector_file_dims) + selector_store_position[names(selector_indices_to_take)] <- selector_indices_to_take + sub_array_of_selectors <- Subset(selector_array, + names(selector_indices_to_take), as.list(selector_indices_to_take), + drop = "selected") + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> ITERATING OVER FILE DIMENSIONS OF THE SELECTORS.") + print("-> STRUCTURE OF A SUB ARRAY:") + print(str(sub_array_of_selectors)) + print("-> STRUCTURE OF THE VARIABLE WITH SELECTORS:") + print(str(var_with_selectors)) + print(dim(var_with_selectors)) + } + } + if (selectors_are_indices) { + sub_array_of_values <- NULL + } + else { + if (length(var_file_dims) > 0) { + var_indices_to_take <- selector_indices_to_take[which(names(selector_indices_to_take) %in% + names(var_file_dims))] + sub_array_of_values <- Subset(var_with_selectors, + names(var_indices_to_take), as.list(var_indices_to_take), + drop = "selected") + } + else { + sub_array_of_values <- var_with_selectors + } + } + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> STRUCTURE OF THE SUB ARRAY FROM THE VARIABLE CORRESPONDING TO THE SUB ARRAY OF SELECTORS") + print(str(sub_array_of_values)) + print(dim(sub_array_of_values)) + print("-> NAME OF THE FILE DIMENSION THE CURRENT INNER DIMENSION EXTENDS ALONG:") + print(file_dim) + } + } + if ((!is.null(file_dim) && (file_dim %in% + names(selector_file_dims))) || is.null(file_dim)) { + if (length(sub_array_of_selectors) > + 0) { + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> THE INNER DIMENSION DOES NOT GO ACROSS ANY FILE DIMENSION OR IT DOES BUT IS IN THE PROVIDED SELECTOR ARRAY.") + } + } + if (selectors_are_indices) { + if (!is.null(var_with_selectors_name)) { + max_allowed <- ifelse(aiat, m, + n) + } + else { + max_allowed <- data_dims[inner_dim] + } + if (any(na.omit(unlist(sub_array_of_selectors)) > + max_allowed) || any(na.omit(unlist(sub_array_of_selectors)) < + 1)) { + stop("Provided indices out of range for dimension '", + inner_dim, "' ", "for dataset '", + dat[[i]][["name"]], "' (accepted range: 1 to ", + max_allowed, ").") + } + } + goes_across_prime_meridian <- FALSE + if (!is.null(var_ordered) && !selectors_are_indices) { + if (!is.null(dim_reorder_params[[inner_dim]])) { + if (is.list(sub_array_of_selectors)) { + sub_array_reordered <- dim_reorder_params[[inner_dim]](unlist(sub_array_of_selectors)) + sub_array_unorder <- sort(sub_array_reordered$ix, + index.return = TRUE)$ix + sub_array_of_selectors <- as.list(sub_array_reordered$x[sub_array_unorder]) + is_circular_dim <- attr(dim_reorder_params[[inner_dim]], + "circular") + if (!is.null(is_circular_dim)) { + if (is_circular_dim) { + goes_across_prime_meridian <- abs(sub_array_of_selectors[[1]]) > + abs(sub_array_of_selectors[[2]]) + } + } + } + else { + sub_array_of_selectors <- dim_reorder_params[[inner_dim]](sub_array_of_selectors)$x + } + } + sub_array_of_indices <- selector_checker(sub_array_of_selectors, + var_ordered, tolerance = if (aiat) { + NULL + } + else { + tolerance_params[[inner_dim]] + }) + } + else { + sub_array_of_indices <- selector_checker(sub_array_of_selectors, + sub_array_of_values, tolerance = if (aiat) { + NULL + } + else { + tolerance_params[[inner_dim]] + }) + } + sub_array_of_indices <- sub_array_of_indices[chunk_indices(length(sub_array_of_indices), + chunks[[inner_dim]]["chunk"], chunks[[inner_dim]]["n_chunks"], + inner_dim)] + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> TRANSFORMATION REQUESTED?") + print(with_transform) + print("-> BETA:") + print(beta) + } + } + if (with_transform) { + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> SELECTORS REQUESTED BEFORE TRANSFORM.") + } + } + if (goes_across_prime_meridian) { + sub_array_of_fri <- 1:n + } + else { + if (is.list(sub_array_of_indices)) { + sub_array_of_indices <- sub_array_of_indices[[1]]:sub_array_of_indices[[2]] + } + first_index <- min(unlist(sub_array_of_indices)) + last_index <- max(unlist(sub_array_of_indices)) + start_padding <- min(beta, first_index - + 1) + end_padding <- min(beta, n - last_index) + sub_array_of_fri <- (first_index - + start_padding):(last_index + + end_padding) + } + subset_vars_to_transform <- vars_to_transform + if (!is.null(var_ordered)) { + subset_vars_to_transform[[var_with_selectors_name]] <- Subset(var_ordered, + inner_dim, sub_array_of_fri) + } + else { + subset_vars_to_transform[[var_with_selectors_name]] <- Subset(sub_array_of_values, + inner_dim, sub_array_of_fri) + } + transformed_subset_var <- do.call(transform, + c(list(data_array = NULL, variables = subset_vars_to_transform, + file_selectors = selectors_of_first_files_with_data[[i]]), + transform_params))$variables[[var_with_selectors_name]] + if (!is.null(dim_reorder_params[[inner_dim]])) { + transformed_subset_var_reorder <- dim_reorder_params[[inner_dim]](transformed_subset_var) + transformed_subset_var <- transformed_subset_var_reorder$x + transformed_subset_var_unorder <- sort(transformed_subset_var_reorder$ix, + index.return = TRUE)$ix + } + else { + transformed_subset_var_unorder <- 1:length(transformed_subset_var) + } + sub_array_of_sri <- selector_checker(sub_array_of_selectors, + transformed_subset_var, tolerance = if (aiat) { + tolerance_params[[inner_dim]] + } + else { + NULL + }) + if (goes_across_prime_meridian) { + sub_array_of_sri <- c(1:sub_array_of_sri[[2]], + sub_array_of_sri[[1]]:length(transformed_subset_var)) + } + else if (is.list(sub_array_of_sri)) { + sub_array_of_sri <- sub_array_of_sri[[1]]:sub_array_of_sri[[2]] + } + ordered_sri <- sub_array_of_sri + sub_array_of_sri <- transformed_subset_var_unorder[sub_array_of_sri] + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> FIRST INDEX:") + print(first_index) + print("-> LAST INDEX:") + print(last_index) + print("-> STRUCTURE OF FIRST ROUND INDICES:") + print(str(sub_array_of_fri)) + print("-> STRUCTURE OF SECOND ROUND INDICES:") + print(str(sub_array_of_sri)) + print("-> STRUCTURE OF TRANSFORMED VARIABLE INDICES:") + print(str(tvi)) + } + } + sri <- do.call("[[<-", c(list(x = sri), + as.list(selector_store_position), + list(value = sub_array_of_sri))) + } + else { + if (goes_across_prime_meridian) { + sub_array_of_fri <- c(1:sub_array_of_indices[[2]], + sub_array_of_indices[[1]]:n) + } + else if (is.list(sub_array_of_indices)) { + sub_array_of_fri <- sub_array_of_indices[[1]]:sub_array_of_indices[[2]] + } + else { + sub_array_of_fri <- sub_array_of_indices + } + } + if (!is.null(var_unorder_indices)) { + if (is.null(ordered_fri)) { + ordered_fri <- sub_array_of_fri + } + sub_array_of_fri <- var_unorder_indices[sub_array_of_fri] + } + fri <- do.call("[[<-", c(list(x = fri), + as.list(selector_store_position), + list(value = sub_array_of_fri))) + if (!is.null(file_dim)) { + taken_chunks[selector_store_position[[file_dim]]] <- TRUE + } + else { + taken_chunks <- TRUE + } + } + } + else { + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> THE INNER DIMENSION GOES ACROSS A FILE DIMENSION.") + } + } + if (inner_dim %in% names(dim(sub_array_of_selectors))) { + if (is.null(var_with_selectors_name)) { + if (any(na.omit(unlist(sub_array_of_selectors)) < + 1) || any(na.omit(unlist(sub_array_of_selectors)) > + data_dims[inner_dim] * chunk_amount)) { + stop("Provided indices out of range for dimension '", + inner_dim, "' ", "for dataset '", + dat[[i]][["name"]], "' (accepted range: 1 to ", + data_dims[inner_dim] * chunk_amount, + ").") + } + } + else { + if (inner_dim %in% names(dim(sub_array_of_values))) { + inner_dim_pos_in_sub_array <- which(names(dim(sub_array_of_values)) == + inner_dim) + if (inner_dim_pos_in_sub_array != + 1) { + new_sub_array_order <- (1:length(dim(sub_array_of_values)))[-inner_dim_pos_in_sub_array] + new_sub_array_order <- c(inner_dim_pos_in_sub_array, + new_sub_array_order) + sub_array_of_values <- .aperm2(sub_array_of_values, + new_sub_array_order) + } + } + } + inner_dim_pos_in_sub_array <- which(names(dim(sub_array_of_selectors)) == + inner_dim) + if (inner_dim_pos_in_sub_array != 1) { + new_sub_array_order <- (1:length(dim(sub_array_of_selectors)))[-inner_dim_pos_in_sub_array] + new_sub_array_order <- c(inner_dim_pos_in_sub_array, + new_sub_array_order) + sub_array_of_selectors <- .aperm2(sub_array_of_selectors, + new_sub_array_order) + } + sub_array_of_indices <- selector_checker(sub_array_of_selectors, + sub_array_of_values, tolerance = tolerance_params[[inner_dim]]) + if (is.list(sub_array_of_indices)) { + sub_array_of_indices <- sub_array_of_indices[[1]]:sub_array_of_indices[[2]] + } + sub_array_of_indices <- sub_array_of_indices[chunk_indices(length(sub_array_of_indices), + chunks[[inner_dim]]["chunk"], chunks[[inner_dim]]["n_chunks"], + inner_dim)] + sub_array_is_list <- FALSE + if (is.list(sub_array_of_indices)) { + sub_array_is_list <- TRUE + sub_array_of_indices <- unlist(sub_array_of_indices) + } + if (is.null(var_with_selectors_name)) { + indices_chunk <- floor((sub_array_of_indices - + 1)/data_dims[inner_dim]) + 1 + transformed_indices <- ((sub_array_of_indices - + 1)%%data_dims[inner_dim]) + 1 + } + else { + indices_chunk <- floor((sub_array_of_indices - + 1)/var_full_dims[inner_dim]) + + 1 + transformed_indices <- ((sub_array_of_indices - + 1)%%var_full_dims[inner_dim]) + + 1 + } + if (sub_array_is_list) { + sub_array_of_indices <- as.list(sub_array_of_indices) + } + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> GOING TO ITERATE ALONG CHUNKS.") + } + } + for (chunk in 1:chunk_amount) { + if (!is.null(names(selector_store_position))) { + selector_store_position[file_dim] <- chunk + } + else { + selector_store_position <- chunk + } + chunk_selectors <- transformed_indices[which(indices_chunk == + chunk)] + sub_array_of_indices <- chunk_selectors + if (with_transform) { + if (!aiat) { + first_index <- min(unlist(sub_array_of_indices)) + last_index <- max(unlist(sub_array_of_indices)) + sub_array_of_fri <- max(c(first_index - + beta, 1)):min(c(last_index + + beta, n)) + sub_array_of_sri <- transform_indices(unlist(sub_array_of_indices) - + first_index + 1, n, m) + if (is.list(sub_array_of_indices)) { + if (length(sub_array_of_sri) > + 1) { + sub_array_of_sri <- sub_array_of_sri[[1]]:sub_array_of_sri[[2]] + } + } + } + else { + first_index <- min(unlist(sub_array_of_indices)) + last_index <- max(unlist(sub_array_of_indices)) + first_index_before_transform <- max(transform_indices(first_index, + m, n) - beta, 1) + last_index_before_transform <- min(transform_indices(last_index, + m, n) + beta, n) + sub_array_of_fri <- first_index_before_transform:last_index_before_transform + if (is.list(sub_array_of_indices) && + (length(sub_array_of_indices) > + 1)) { + sub_array_of_sri <- 1:(last_index - + first_index + 1) + round(beta/n * + m) + } + else { + sub_array_of_sri <- sub_array_of_indices - + first_index + 1 + round(beta/n * + m) + } + } + sri <- do.call("[[<-", c(list(x = sri), + as.list(selector_store_position), + list(value = sub_array_of_sri))) + if (length(sub_array_of_sri) > + 0) { + taken_chunks[chunk] <- TRUE + } + } + else { + sub_array_of_fri <- sub_array_of_indices + if (length(sub_array_of_fri) > + 0) { + taken_chunks[chunk] <- TRUE + } + } + if (!is.null(var_unorder_indices)) { + ordered_fri <- sub_array_of_fri + sub_array_of_fri <- var_unorder_indices[sub_array_of_fri] + } + fri <- do.call("[[<-", c(list(x = fri), + as.list(selector_store_position), + list(value = sub_array_of_fri))) + } + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> FINISHED ITERATING ALONG CHUNKS") + } + } + } + else { + stop("Provided array of indices for dimension '", + inner_dim, "', ", "which goes across the file dimension '", + file_dim, "', but ", "the provided array does not have the dimension '", + inner_dim, "', which is mandatory.") + } + } + } + } + if (debug) { + if (inner_dim %in% dims_to_check) { + print("-> PROCEEDING TO CROP VARIABLES") + } + } + empty_chunks <- which(!taken_chunks) + if (length(empty_chunks) >= length(taken_chunks)) { + stop("Selectors do not match any of the possible values for the dimension '", + inner_dim, "'.") + } + if (length(empty_chunks) > 0) { + chunks_to_keep <- which(taken_chunks) + dims_to_crop[[file_dim]] <- c(dims_to_crop[[file_dim]], + list(chunks_to_keep)) + } + dat[[i]][["selectors"]][[inner_dim]] <- list(fri = fri, + sri = sri) + types_of_var_to_crop <- "picked" + if (with_transform) { + types_of_var_to_crop <- c(types_of_var_to_crop, + "transformed") + } + if (!is.null(dim_reorder_params[[inner_dim]])) { + types_of_var_to_crop <- c(types_of_var_to_crop, + "reordered") + } + for (type_of_var_to_crop in types_of_var_to_crop) { + if (type_of_var_to_crop == "transformed") { + if (is.null(tvi)) { + if (!is.null(dim_reorder_params[[inner_dim]])) { + crop_indices <- unique(unlist(ordered_sri)) + } + else { + crop_indices <- unique(unlist(sri)) + } + } + else { + crop_indices <- unique(unlist(tvi)) + } + vars_to_crop <- transformed_vars[[i]] + common_vars_to_crop <- transformed_common_vars + } + else if (type_of_var_to_crop == "reordered") { + crop_indices <- unique(unlist(ordered_fri)) + vars_to_crop <- picked_vars_ordered[[i]] + common_vars_to_crop <- picked_common_vars_ordered + } + else { + crop_indices <- unique(unlist(fri)) + vars_to_crop <- picked_vars[[i]] + common_vars_to_crop <- picked_common_vars + } + for (var_to_crop in names(vars_to_crop)) { + if (inner_dim %in% names(dim(vars_to_crop[[var_to_crop]]))) { + if (!is.null(crop_indices)) { + if (type_of_var_to_crop == "transformed") { + if (!aiat) { + vars_to_crop[[var_to_crop]] <- Subset(transformed_subset_var, + inner_dim, crop_indices) + } + else { + vars_to_crop[[var_to_crop]] <- Subset(vars_to_crop[[var_to_crop]], + inner_dim, crop_indices) + } + } + else { + vars_to_crop[[var_to_crop]] <- Subset(vars_to_crop[[var_to_crop]], + inner_dim, crop_indices) + } + } + } + } + if (i == length(dat)) { + for (common_var_to_crop in names(common_vars_to_crop)) { + if (inner_dim %in% names(dim(common_vars_to_crop[[common_var_to_crop]]))) { + common_vars_to_crop[[common_var_to_crop]] <- Subset(common_vars_to_crop[[common_var_to_crop]], + inner_dim, crop_indices) + } + } + } + if (type_of_var_to_crop == "transformed") { + if (!is.null(vars_to_crop)) { + transformed_vars[[i]] <- vars_to_crop + } + if (i == length(dat)) { + transformed_common_vars <- common_vars_to_crop + } + } + else if (type_of_var_to_crop == "reordered") { + if (!is.null(vars_to_crop)) { + picked_vars_ordered[[i]] <- vars_to_crop + } + if (i == length(dat)) { + picked_common_vars_ordered <- common_vars_to_crop + } + } + else { + if (!is.null(vars_to_crop)) { + picked_vars[[i]] <- vars_to_crop + } + if (i == length(dat)) { + picked_common_vars <- common_vars_to_crop + } + } + } + } + } + } + } + for (file_dim in names(dims_to_crop)) { + indices_to_keep <- unique(unlist(dims_to_crop[[file_dim]])) + array_of_files_to_load <- Subset(array_of_files_to_load, + file_dim, indices_to_keep) + array_of_not_found_files <- Subset(array_of_not_found_files, + file_dim, indices_to_keep) + for (i in 1:length(dat)) { + for (selector_dim in names(dat[[i]][["selectors"]])) { + if (selector_dim == file_dim) { + for (j in 1:length(dat[[i]][["selectors"]][[selector_dim]][["fri"]])) { + dat[[i]][["selectors"]][[selector_dim]][["fri"]][[j]] <- dat[[i]][["selectors"]][[selector_dim]][["fri"]][[j]][indices_to_keep] + } + for (j in 1:length(dat[[i]][["selectors"]][[selector_dim]][["sri"]])) { + dat[[i]][["selectors"]][[selector_dim]][["sri"]][[j]] <- dat[[i]][["selectors"]][[selector_dim]][["sri"]][[j]][indices_to_keep] + } + } + if (file_dim %in% names(dim(dat[[i]][["selectors"]][[selector_dim]][["fri"]]))) { + dat[[i]][["selectors"]][[selector_dim]][["fri"]] <- Subset(dat[[i]][["selectors"]][[selector_dim]][["fri"]], + file_dim, indices_to_keep) + dat[[i]][["selectors"]][[selector_dim]][["sri"]] <- Subset(dat[[i]][["selectors"]][[selector_dim]][["sri"]], + file_dim, indices_to_keep) + } + } + for (picked_var in names(picked_vars[[i]])) { + if (file_dim %in% names(dim(picked_vars[[i]][[picked_var]]))) { + picked_vars[[i]][[picked_var]] <- Subset(picked_vars[[i]][[picked_var]], + file_dim, indices_to_keep) + } + } + for (transformed_var in names(transformed_vars[[i]])) { + if (file_dim %in% names(dim(transformed_vars[[i]][[transformed_var]]))) { + transformed_vars[[i]][[transformed_var]] <- Subset(transformed_vars[[i]][[transformed_var]], + file_dim, indices_to_keep) + } + } + } + for (picked_common_var in names(picked_common_vars)) { + if (file_dim %in% names(dim(picked_common_vars[[picked_common_var]]))) { + picked_common_vars[[picked_common_var]] <- Subset(picked_common_vars[[picked_common_var]], + file_dim, indices_to_keep) + } + } + for (transformed_common_var in names(transformed_common_vars)) { + if (file_dim %in% names(dim(transformed_common_vars[[transformed_common_var]]))) { + transformed_common_vars[[transformed_common_var]] <- Subset(transformed_common_vars[[transformed_common_var]], + file_dim, indices_to_keep) + } + } + } + total_inner_dims <- NULL + for (i in 1:length(dat)) { + if (dataset_has_files[i]) { + inner_dims <- expected_inner_dims[[i]] + inner_dims <- sapply(inner_dims, function(x) { + if (!all(sapply(dat[[i]][["selectors"]][[x]][["sri"]], + is.null))) { + max(sapply(dat[[i]][["selectors"]][[x]][["sri"]], + length)) + } + else { + if (length(var_params[[x]]) > 0) { + if (var_params[[x]] %in% names(transformed_vars[[i]])) { + length(transformed_vars[[i]][[var_params[[x]]]]) + } + else if (var_params[[x]] %in% names(transformed_common_vars)) { + length(transformed_common_vars[[var_params[[x]]]]) + } + else { + max(sapply(dat[[i]][["selectors"]][[x]][["fri"]], + length)) + } + } + else { + max(sapply(dat[[i]][["selectors"]][[x]][["fri"]], + length)) + } + } + }) + names(inner_dims) <- expected_inner_dims[[i]] + if (is.null(total_inner_dims)) { + total_inner_dims <- inner_dims + } + else { + new_dims <- .MergeArrayDims(total_inner_dims, + inner_dims) + total_inner_dims <- new_dims[[3]] + } + } + } +print('total_inner_dims') +print(total_inner_dims) +print('dim(array_of_files_to_load)') +print(dim(array_of_files_to_load)) + + new_dims <- .MergeArrayDims(dim(array_of_files_to_load), + total_inner_dims) + final_dims <- new_dims[[3]][dim_names] +print('new_dims') +print(new_dims) +print('final_dims in the beginning') +print(final_dims) + final_dims_fake <- final_dims + if (merge_across_dims) { + if (!is.null(inner_dims_across_files)) { + for (file_dim_across in names(inner_dims_across_files)) { + inner_dim_pos <- which(names(final_dims_fake) == + inner_dims_across_files[[file_dim_across]]) + new_dims <- c() + if (inner_dim_pos > 1) { + new_dims <- c(new_dims, final_dims_fake[1:(inner_dim_pos - + 1)]) + } + ##Bug: 'prod' time x chunk + new_dims <- c(new_dims, setNames(prod(final_dims_fake[c(inner_dim_pos, + inner_dim_pos + 1)]), inner_dims_across_files[[file_dim_across]])) + if (inner_dim_pos + 1 < length(final_dims_fake)) { + new_dims <- c(new_dims, final_dims_fake[(inner_dim_pos + + 2):length(final_dims_fake)]) + } + final_dims_fake <- new_dims + } + } + } + + all_split_dims <- NULL + if (split_multiselected_dims) { + for (dim_param in 1:length(dim_params)) { + if (!is.null(dim(dim_params[[dim_param]]))) { + if (length(dim(dim_params[[dim_param]])) > 1) { + split_dims <- dim(dim_params[[dim_param]]) + all_split_dims <- c(all_split_dims, setNames(list(split_dims), + names(dim_params)[dim_param])) + if (is.null(names(split_dims))) { + names(split_dims) <- paste0(names(dim_params)[dim_param], + 1:length(split_dims)) + } + old_dim_pos <- which(names(final_dims_fake) == + names(dim_params)[dim_param]) + new_dims <- c() + if (old_dim_pos > 1) { + new_dims <- c(new_dims, final_dims_fake[1:(old_dim_pos - + 1)]) + } + new_dims <- c(new_dims, split_dims) + if (old_dim_pos < length(final_dims_fake)) { + new_dims <- c(new_dims, final_dims_fake[(old_dim_pos + + 1):length(final_dims_fake)]) + } + final_dims_fake <- new_dims + } + } + } + } + if (!silent) { + .message("Detected dimension sizes:") + longest_dim_len <- max(sapply(names(final_dims_fake), nchar)) + longest_size_len <- max(sapply(paste0(final_dims_fake, ''), nchar)) + sapply(names(final_dims_fake), + function(x) { + message(paste0("* ", paste(rep(' ', longest_dim_len - nchar(x)), collapse = ''), + x, ": ", paste(rep(' ', longest_size_len - nchar(paste0(final_dims_fake[x], ''))), collapse = ''), + final_dims_fake[x])) + }) + bytes <- prod(c(final_dims_fake, 8)) + dim_sizes <- paste(final_dims_fake, collapse = ' x ') + if (retrieve) { + .message(paste("Total size of requested data:")) + } else { + .message(paste("Total size of involved data:")) + } + .message(paste(dim_sizes, " x 8 bytes =", + format(structure(bytes, class = "object_size"), units = "auto")), + indent = 2) + } + + # The following several lines will only be run if retrieve = TRUE + if (retrieve) { + + ########## CREATING THE SHARED MATRIX AND DISPATCHING WORK PIECES ########### + # TODO: try performance of storing all in cols instead of rows + # Create the shared memory array, and a pointer to it, to be sent + # to the work pieces. + data_array <- big.matrix(nrow = prod(final_dims), ncol = 1) + shared_matrix_pointer <- describe(data_array) + if (is.null(num_procs)) { + num_procs <- availableCores() + } + # Creating a shared tmp folder to store metadata from each chunk + array_of_metadata_flags <- array(FALSE, dim = dim(array_of_files_to_load)) + if (!is.null(metadata_dims)) { + metadata_indices_to_load <- as.list(rep(1, length(dim(array_of_files_to_load)))) + names(metadata_indices_to_load) <- names(dim(array_of_files_to_load)) + metadata_indices_to_load[metadata_dims] <- as.list(rep(TRUE, length(metadata_dims))) + array_of_metadata_flags <- do.call('[<-', c(list(array_of_metadata_flags), metadata_indices_to_load, + list(value = rep(TRUE, prod(dim(array_of_files_to_load)[metadata_dims]))))) + } + metadata_file_counter <- 0 + metadata_folder <- tempfile('metadata') + dir.create(metadata_folder) + # Build the work pieces, each with: + # - file path + # - total size (dims) of store array + # - start position in store array + # - file selectors (to provide extra info. useful e.g. to select variable) + # - indices to take from file + work_pieces <- list() + for (i in 1:length(dat)) { + if (dataset_has_files[i]) { + selectors <- dat[[i]][['selectors']] + file_dims <- found_file_dims[[i]] + inner_dims <- expected_inner_dims[[i]] + sub_array_dims <- final_dims[file_dims] + sub_array_dims[found_pattern_dim] <- 1 + sub_array_of_files_to_load <- array(1:prod(sub_array_dims), + dim = sub_array_dims) + names(dim(sub_array_of_files_to_load)) <- names(sub_array_dims) + # Detect which of the dimensions of the dataset go across files. + file_dim_across_files <- lapply(inner_dims, + function(x) { + dim_across <- sapply(inner_dims_across_files, function(y) x %in% y) + if (any(dim_across)) { + names(inner_dims_across_files)[which(dim_across)[1]] + } else { + NULL + } + }) + names(file_dim_across_files) <- inner_dims + j <- 1 + while (j <= prod(sub_array_dims)) { + # Work out file path. + file_to_load_sub_indices <- which(sub_array_of_files_to_load == j, arr.ind = TRUE)[1, ] + names(file_to_load_sub_indices) <- names(sub_array_dims) + file_to_load_sub_indices[found_pattern_dim] <- i + big_dims <- rep(1, length(dim(array_of_files_to_load))) + names(big_dims) <- names(dim(array_of_files_to_load)) + file_to_load_indices <- .MergeArrayDims(file_to_load_sub_indices, big_dims)[[1]] + file_to_load <- do.call('[[', c(list(array_of_files_to_load), + as.list(file_to_load_indices))) + not_found_file <- do.call('[[', c(list(array_of_not_found_files), + as.list(file_to_load_indices))) + load_file_metadata <- do.call('[', c(list(array_of_metadata_flags), + as.list(file_to_load_indices))) + if (load_file_metadata) { + metadata_file_counter <- metadata_file_counter + 1 + } + if (!is.na(file_to_load) && !not_found_file) { + # Work out indices to take + first_round_indices <- lapply(inner_dims, + function (x) { + if (is.null(file_dim_across_files[[x]])) { + selectors[[x]][['fri']][[1]] + } else { + which_chunk <- file_to_load_sub_indices[file_dim_across_files[[x]]] + selectors[[x]][['fri']][[which_chunk]] + } + }) + names(first_round_indices) <- inner_dims + second_round_indices <- lapply(inner_dims, + function (x) { + if (is.null(file_dim_across_files[[x]])) { + selectors[[x]][['sri']][[1]] + } else { + which_chunk <- file_to_load_sub_indices[file_dim_across_files[[x]]] + selectors[[x]][['sri']][[which_chunk]] + } + }) +if (debug) { +print("-> BUILDING A WORK PIECE") +#print(str(selectors)) +} + names(second_round_indices) <- inner_dims + if (!any(sapply(first_round_indices, length) == 0)) { + work_piece <- list() + work_piece[['first_round_indices']] <- first_round_indices + work_piece[['second_round_indices']] <- second_round_indices + work_piece[['file_indices_in_array_of_files']] <- file_to_load_indices + work_piece[['file_path']] <- file_to_load + work_piece[['store_dims']] <- final_dims + # Work out store position + store_position <- final_dims + store_position[names(file_to_load_indices)] <- file_to_load_indices + store_position[inner_dims] <- rep(1, length(inner_dims)) + work_piece[['store_position']] <- store_position + # Work out file selectors + file_selectors <- sapply(file_dims, + function (x) { + vector_to_pick <- 1 + if (x %in% names(depending_file_dims)) { + vector_to_pick <- file_to_load_indices[depending_file_dims[[x]]] + } + selectors[file_dims][[x]][[vector_to_pick]][file_to_load_indices[x]] + }) + names(file_selectors) <- file_dims + work_piece[['file_selectors']] <- file_selectors + # Send variables for transformation + if (!is.null(transform) && (length(transform_vars) > 0)) { + vars_to_transform <- NULL + picked_vars_to_transform <- which(names(picked_vars[[i]]) %in% transform_vars) + if (length(picked_vars_to_transform) > 0) { + picked_vars_to_transform <- names(picked_vars[[i]])[picked_vars_to_transform] + vars_to_transform <- c(vars_to_transform, picked_vars[[i]][picked_vars_to_transform]) + if (any(picked_vars_to_transform %in% names(picked_vars_ordered[[i]]))) { + picked_vars_ordered_to_transform <- picked_vars_to_transform[which(picked_vars_to_transform %in% names(picked_vars_ordered[[i]]))] + vars_to_transform[picked_vars_ordered_to_transform] <- picked_vars_ordered[[i]][picked_vars_ordered_to_transform] + } + } + picked_common_vars_to_transform <- which(names(picked_common_vars) %in% transform_vars) + if (length(picked_common_vars_to_transform) > 0) { + picked_common_vars_to_transform <- names(picked_common_vars)[picked_common_vars_to_transform] + vars_to_transform <- c(vars_to_transform, picked_common_vars[picked_common_vars_to_transform]) + if (any(picked_common_vars_to_transform %in% names(picked_common_vars_ordered))) { + picked_common_vars_ordered_to_transform <- picked_common_vars_to_transform[which(picked_common_vars_to_transform %in% names(picked_common_vars_ordered))] + vars_to_transform[picked_common_vars_ordered_to_transform] <- picked_common_vars_ordered[picked_common_vars_ordered_to_transform] + } + } + work_piece[['vars_to_transform']] <- vars_to_transform + } + # Send flag to load metadata + if (load_file_metadata) { + work_piece[['save_metadata_in']] <- paste0(metadata_folder, '/', metadata_file_counter) + } + work_pieces <- c(work_pieces, list(work_piece)) + } + } + j <- j + 1 + } + } + } +#print("N") +if (debug) { +print("-> WORK PIECES BUILT") +} + + # Calculate the progress %s that will be displayed and assign them to + # the appropriate work pieces. + if (length(work_pieces) / num_procs >= 2 && !silent) { + if (length(work_pieces) / num_procs < 10) { + amount <- 100 / ceiling(length(work_pieces) / num_procs) + reps <- ceiling(length(work_pieces) / num_procs) + } else { + amount <- 10 + reps <- 10 + } + progress_steps <- rep(amount, reps) + if (length(work_pieces) < (reps + 1)) { + selected_pieces <- length(work_pieces) + progress_steps <- c(sum(head(progress_steps, reps)), + tail(progress_steps, reps)) + } else { + selected_pieces <- round(seq(1, length(work_pieces), + length.out = reps + 1))[-1] + } + progress_steps <- paste0(' + ', round(progress_steps, 2), '%') + progress_message <- 'Progress: 0%' + } else { + progress_message <- '' + selected_pieces <- NULL + } + piece_counter <- 1 + step_counter <- 1 + work_pieces <- lapply(work_pieces, + function (x) { + if (piece_counter %in% selected_pieces) { + wp <- c(x, list(progress_amount = progress_steps[step_counter])) + step_counter <<- step_counter + 1 + } else { + wp <- x + } + piece_counter <<- piece_counter + 1 + wp + }) + if (!silent) { + .message("If the size of the requested data is close to or above the free shared RAM memory, R may crash.") + .message("If the size of the requested data is close to or above the half of the free RAM memory, R may crash.") + .message(paste0("Will now proceed to read and process ", length(work_pieces), " data files:")) + if (length(work_pieces) < 30) { + lapply(work_pieces, function (x) .message(x[['file_path']], indent = 2)) + } else { + .message("The list of files is long. You can check it after Start() finishes in the output '$Files'.", indent = 2, exdent = 5) + } + } + + # Build the cluster of processes that will do the work and dispatch work pieces. + # The function .LoadDataFile is applied to each work piece. This function will + # open the data file, regrid if needed, subset, apply the mask, + # compute and apply the weights if needed, + # disable extreme values and store in the shared memory matrix. +#print("O") + if (!silent) { + .message("Loading... This may take several minutes...") + if (progress_message != '') { + .message(progress_message, appendLF = FALSE) + } + } + if (num_procs == 1) { + found_files <- lapply(work_pieces, .LoadDataFile, + shared_matrix_pointer = shared_matrix_pointer, + file_data_reader = file_data_reader, + synonims = synonims, + transform = transform, + transform_params = transform_params, + silent = silent, debug = debug) + } else { + cluster <- makeCluster(num_procs, outfile = "") + # Send the heavy work to the workers + work_errors <- try({ + found_files <- clusterApplyLB(cluster, work_pieces, .LoadDataFile, + shared_matrix_pointer = shared_matrix_pointer, + file_data_reader = file_data_reader, + synonims = synonims, + transform = transform, + transform_params = transform_params, + silent = silent, debug = debug) + }) + stopCluster(cluster) + } + + if (!silent) { + if (progress_message != '') { + .message("\n", tag = '') + } + } +#print("P") + data_array <- array(bigmemory::as.matrix(data_array), dim = final_dims_fake) + gc() + + # Load metadata and remove the metadata folder + if (!is.null(metadata_dims)) { + loaded_metadata_files <- list.files(metadata_folder) + loaded_metadata <- lapply(paste0(metadata_folder, '/', loaded_metadata_files), readRDS) + unlink(metadata_folder, recursive = TRUE) + return_metadata <- vector('list', length = prod(dim(array_of_metadata_flags)[metadata_dims])) + return_metadata[as.numeric(loaded_metadata_files)] <- loaded_metadata + dim(return_metadata) <- dim(array_of_metadata_flags[metadata_dims]) + attr(data_array, 'Variables') <- return_metadata + # TODO: Try to infer data type from loaded_metadata + # as.integer(data_array) + } + + failed_pieces <- work_pieces[which(unlist(found_files))] + for (failed_piece in failed_pieces) { + array_of_not_found_files <- do.call('[<-', + c(list(array_of_not_found_files), + as.list(failed_piece[['file_indices_in_array_of_files']]), + list(value = TRUE))) + } + if (any(array_of_not_found_files)) { + for (i in 1:prod(dim(array_of_files_to_load))) { + if (is.na(array_of_not_found_files[i])) { + array_of_files_to_load[i] <- NA + } else { + if (array_of_not_found_files[i]) { + array_of_not_found_files[i] <- array_of_files_to_load[i] + array_of_files_to_load[i] <- NA + } else { + array_of_not_found_files[i] <- NA + } + } + } + } else { + array_of_not_found_files <- NULL + } + + } # End if (retrieve) + + # Replace the vars and common vars by the transformed vars and common vars + for (i in 1:length(dat)) { + if (length(names(transformed_vars[[i]])) > 0) { + picked_vars[[i]][names(transformed_vars[[i]])] <- transformed_vars[[i]] + } else if (length(names(picked_vars_ordered[[i]])) > 0) { + picked_vars[[i]][names(picked_vars_ordered[[i]])] <- picked_vars_ordered[[i]] + } + } + if (length(names(transformed_common_vars)) > 0) { + picked_common_vars[names(transformed_common_vars)] <- transformed_common_vars + } else if (length(names(picked_common_vars_ordered)) > 0) { + picked_common_vars[names(picked_common_vars_ordered)] <- picked_common_vars_ordered + } +if (debug) { +print("-> THE TRANSFORMED VARS:") +print(str(transformed_vars)) +print("-> THE PICKED VARS:") +print(str(picked_vars)) +} + + file_selectors <- NULL + for (i in 1:length(dat)) { + file_selectors[[dat[[i]][['name']]]] <- dat[[i]][['selectors']][which(names(dat[[i]][['selectors']]) %in% found_file_dims[[i]])] + } + if (retrieve) { + if (!silent) { + .message("Successfully retrieved data.") + } + var_backup <- attr(data_array, 'Variables')[[1]] + attr(data_array, 'Variables') <- NULL + attributes(data_array) <- c(attributes(data_array), + list(Variables = c(list(common = c(picked_common_vars, var_backup)), + picked_vars), + Files = array_of_files_to_load, + NotFoundFiles = array_of_not_found_files, + FileSelectors = file_selectors, + PatternDim = found_pattern_dim) + ) + attr(data_array, 'class') <- c('startR_array', attr(data_array, 'class')) + data_array +print('in the end of code, dim(data_array) = ') +print(dim(data_array)) + } else { + if (!silent) { + .message("Successfully discovered data dimensions.") + } + start_call <- match.call() + for (i in 2:length(start_call)) { + if (class(start_call[[i]]) %in% c('name', 'call')) { + start_call[[i]] <- eval.parent(start_call[[i]]) + } + } + start_call[['retrieve']] <- TRUE + attributes(start_call) <- c(attributes(start_call), + list(Dimensions = final_dims_fake, + Variables = c(list(common = picked_common_vars), picked_vars), + ExpectedFiles = array_of_files_to_load, + FileSelectors = file_selectors, + PatternDim = found_pattern_dim, + MergedDims = if (merge_across_dims) { + inner_dims_across_files + } else { + NULL + }, + SplitDims = if (split_multiselected_dims) { + all_split_dims + } else { + NULL + }) + ) + attr(start_call, 'class') <- c('startR_cube', attr(start_call, 'class')) + start_call + } + -- GitLab From f2c261eda4d7c7afff41be90da2208045b9f6015 Mon Sep 17 00:00:00 2001 From: aho Date: Fri, 9 Aug 2019 16:38:32 +0200 Subject: [PATCH 2/3] Fix --- startR/.Start_test.R.swp | Bin 212992 -> 0 bytes startR/Start_test.R | 4 ---- 2 files changed, 4 deletions(-) delete mode 100644 startR/.Start_test.R.swp diff --git a/startR/.Start_test.R.swp b/startR/.Start_test.R.swp deleted file mode 100644 index bf2d7b6ab150dea7448db0ae293b610bb4dca5b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212992 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 OHj|Q=h)fli8vh4{0&a}} diff --git a/startR/Start_test.R b/startR/Start_test.R index 6fc5c1e..9ba28ba 100644 --- a/startR/Start_test.R +++ b/startR/Start_test.R @@ -3083,14 +3083,10 @@ print(data_array[matrix_indices]) } print('total_inner_dims') print(total_inner_dims) -print('dim(array_of_files_to_load)') -print(dim(array_of_files_to_load)) new_dims <- .MergeArrayDims(dim(array_of_files_to_load), total_inner_dims) final_dims <- new_dims[[3]][dim_names] -print('new_dims') -print(new_dims) print('final_dims in the beginning') print(final_dims) final_dims_fake <- final_dims -- GitLab From e5f981552540e27b80cb3071f92d5c9da215824f Mon Sep 17 00:00:00 2001 From: aho Date: Fri, 9 Aug 2019 16:41:20 +0200 Subject: [PATCH 3/3] Keep the folder. --- s2dv_review/issue/test.R | 1 + 1 file changed, 1 insertion(+) create mode 100644 s2dv_review/issue/test.R diff --git a/s2dv_review/issue/test.R b/s2dv_review/issue/test.R new file mode 100644 index 0000000..d72af31 --- /dev/null +++ b/s2dv_review/issue/test.R @@ -0,0 +1 @@ +asd -- GitLab