From 36036edba49341e5aad3f278d0ccb5f184416db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Tue, 29 Jan 2019 16:48:27 +0100 Subject: [PATCH 01/88] file system changes --- RBBGCMuso/R/calibration.R | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 RBBGCMuso/R/calibration.R diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R new file mode 100644 index 0000000..23edf8e --- /dev/null +++ b/RBBGCMuso/R/calibration.R @@ -0,0 +1,2 @@ +GPP_mes <- read.csv("hhs_GPP_measured.csv", stringAsFactors) +head(GPP_mes$year) From c63cfff17b4694a0f28f3f9d34783ec2338cb48e Mon Sep 17 00:00:00 2001 From: hollorol Date: Tue, 12 Feb 2019 21:22:14 +0100 Subject: [PATCH 02/88] Using highly optimized lubridate instead of custom functions --- RBBGCMuso/DESCRIPTION | 1 + RBBGCMuso/R/calibMuso.R | 41 ++++---- RBBGCMuso/R/musoTime.R | 210 ++++++++++++---------------------------- RBBGCMuso/R/setupMuso.R | 4 +- 4 files changed, 86 insertions(+), 170 deletions(-) diff --git a/RBBGCMuso/DESCRIPTION b/RBBGCMuso/DESCRIPTION index b51225f..1c5cf6f 100644 --- a/RBBGCMuso/DESCRIPTION +++ b/RBBGCMuso/DESCRIPTION @@ -11,6 +11,7 @@ Packaged: 2017-07-19 14:00:04 UTCs; hollorol Author: Roland Hollo's [aut, cre] Imports: grDevices, + lubridate, limSolve, stats, utils, diff --git a/RBBGCMuso/R/calibMuso.R b/RBBGCMuso/R/calibMuso.R index a3bf6c6..8deb21c 100644 --- a/RBBGCMuso/R/calibMuso.R +++ b/RBBGCMuso/R/calibMuso.R @@ -29,7 +29,7 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, debugging=FALSE, logfilename=NULL, keepEpc=FALSE, export=FALSE, silent=FALSE, aggressive=FALSE, - leapYear=FALSE,keepBinary=FALSE, + keepBinary=FALSE, binaryPlace="./", fileToChange="epc", skipSpinup = TRUE, modifyOriginal =FALSE, prettyOut = FALSE){ ######################################################################## @@ -325,15 +325,11 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, if(!prettyOut){ colnames(Reva) <- unlist(settings$outputVars[[1]]) } else{ - dates <- as.Date(musoDate(startYear = settings$startYear, - numYears = settings$numYears, - timestep="d",combined = TRUE,corrigated = FALSE), - "%d.%m.%Y") - Reva <- cbind.data.frame(dates, - musoDate(startYear = settings$startYear, - numYears = settings$numYears, - timestep = "d", combined = FALSE, corrigated = FALSE), - Reva) + Reva <- cbind.data.frame( + musoDate(startYear = settings$startYear, + numYears = settings$numYears, + combined = FALSE, prettyOut = TRUE), + Reva) colnames(Reva) <- as.character(c("date","day","month","year",unlist(settings$outputVars[[1]])) ) } @@ -344,19 +340,22 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, - if(leapYear){ - Reva <- corrigMuso(settings,Reva) - if(!prettyOut){ - rownames(Reva) <- musoDate(settings$startYear,settings$numYears) - } + ## if(leapYear){ + ## Reva <- corrigMuso(settings,Reva) + ## if(!prettyOut){ + ## rownames(Reva) <- musoDate(settings$startYear,settings$numYears) + ## } - } else { - if(!prettyOut){ - rownames(Reva) <- musoDate(settings$startYear, settings$numYears, corrigated=FALSE) - } + ## } else { + ## if(!prettyOut){ + ## rownames(Reva) <- musoDate(settings$startYear, settings$numYears) + ## } - } - + ## } + + if(!prettyOut){ + rownames(Reva) <- musoDate(settings$startYear, numYears = settings$numYears) + } if(export!=FALSE){ diff --git a/RBBGCMuso/R/musoTime.R b/RBBGCMuso/R/musoTime.R index b8b5853..042dde1 100644 --- a/RBBGCMuso/R/musoTime.R +++ b/RBBGCMuso/R/musoTime.R @@ -1,160 +1,76 @@ -#' isLeapyear +#' musoDate #' -#'This function tells us if its argument a leapyear or not. -#' -#'@param year a year -#'@usage isLeapyear(year) -#'@return TRUE, if leapyear, FALSE if dont. -#' @keywords internal -isLeapyear <- function(year){ - ##This Boolean function tells us whether the given year is leapyear or not - - if(((year%%4==0)&(year%%100!=0))|(year%%400==0)){ - return(TRUE) - } else { - return(FALSE) - } -} - -#' dayOfMonths -#' -#'This function gives as a vector which contains the number of the days per each month -#' -#'@param year a year -#'@param corrigated Do you want to handle the leapyears, if yes choose TRUE -#'@usage dayOfMonths(year, corrigated=TRUE) -#'@return vector with 12 element. First is January, the last is December. All of the vector element represents the number of the days in that specific month -#'@keywords internal - - - -dayOfMonths <- function(year,corrigated=TRUE){ - ##This function tells us how many days are in the months in the choosen year. - - dayMonths <- c(31,28,31,30,31,30,31,31,30,31,30,31) - - if(corrigated){ - - if(isLeapyear(year)==TRUE){ - dayMonths[2] <-29 - } - } - - return(dayMonths) -} - - - -#' This function tells us how many days are in the given year. -#' -#' This function tells us how many days are in the given year. -#' @author Roland Hollos -#' @keywords internal - -dayOfYears <- function(year, corrigated=TRUE){ - ##This function tells us how many days are in the given year. - - if(corrigated){ - if(isLeapyear(year)==TRUE){ - return(366) - } else { - return(365) - } - } else { - return(365) - } - -} - -#' How many days are from the given date and given period length(periodlen)? -#' -#'How many days are from the given date and given period length(periodlen)? -#' @author Roland Hollos -#' @keywords internal - -sumDaysOfPeriod <- function(year, periodlen, corrigated=TRUE){ - ##How many days are from the given date and given period length(periodlen)? - - years <- year:(year+periodlen) - - if(corrigated){ - years100 <-length(which(years%%100==0)) - years400 <-length(which(years%%400==0)) - years4 <- length(which(years%%4==0)) - numberOfLeapdays <- years4-years100+years400 - days <- periodlen*365+numberOfLeapdays - return(days) - } else { - days <- periodlen*365 - return(days) - } -} - -#' Musoleapyear -#' -#' How many days are from the given date and given period length(periodlen)? -#' @author Roland Hollos -#' @keywords internal - -musoLeapYears <- function(settings){ - days <- 365*settings$numyears - years <- settings$startyear:(settings$startyear+settings$numyears-1) - Leapyears <-unlist(lapply(years,isLeapyear)) - return(Leapyears) -} - -#' It generates BiomeBGC-MuSo dates -#' -#' It generates all of the day-dates which are between the start and endyear of BiomeBGC-MuSo run. -#' How many days are from the given date and given period length(periodlen)? -#' @author Roland Hollos -#' @param timestep timestep, which can be daily ("d"), monthly ("m"), yearly("y") -#' @param settings You have to run the setupMuso function before musoDate. It is its output which contains all of the necessary system variables. It sets the whole environment. -#' @param combined If FALSE the output is a vector of 3 string: day, month year, if true, these strings will be concatenated. -#' @param corrigated If True it counts with leapyears, else dont. -#' @param format This is the format of the date. It can be "en" (dd.mm.yyyy), or "hu" (yyyy.mm.dd) -#' @return The exact date-vectors for the BioBGC-MuSo output. You can use this for labelling purpose for example. +#' This function generates MuSo compatibla dates for the data +#' @author Roland HOLLOS +#' @param startYear +#' @param numYears +#' @param timestep +#' @param combined +#' @param corrigated +#' @param format +#' @importFrom lubridate leap_year #' @export -musoDate <- function(startYear, numYears, timestep="d", combined=TRUE, corrigated=TRUE, format="en"){ - ##purpose: generate date label for muso +musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leapYearHandling = FALSE, prettyOut = FALSE){ + + if(is.null(endYears) & is.null(numYears)){ + stop("You should provide endYears or numYears") + } - + if(is.null(endYears)){ + endYear <- startYear + numYears -1 + } - days <- sumDaysOfPeriod(startYear, numYears, corrigated=corrigated) - dates <- matrix(rep(NA,days*3),ncol=3) - - dates[1,] <- c(1,1,startYear) - for(i in 2:days){ - dates[i,]<-dates[(i-1),] - if((dates[i-1,2]==12)&(dates[i-1,1]==31)){ - dates[i,] <-c(1,1,(dates[(i-1),3]+1)) - } else { - - if(dates[i-1,1]==(dayOfMonths(dates[(i-1),3],corrigated=corrigated)[dates[(i-1),2]] )){ - dates[i,]<-c(1,(dates[(i-1),2]+1),dates[(i-1),3]) - } else { - dates[i,1]<-dates[(i-1),1]+1 - } - } - + dates <- seq(from = as.Date(paste0(startYear,"01","01"),format = "%Y%m%d"), to = as.Date(paste0(endYear,"12","31"),format = "%Y%m%d"), by = "day") + if(leapYearHandling){ + if(prettyOut){ + return(cbind(format(dates,"%d.%m.%Y"),as.numeric(format(dates,"%d")),as.numeric(format(dates,"%m")),as.numeric(format(dates,"%Y"))) ) } - if(format=="en"){ - } else { - if(format=="hu"){ - dates<-dates[,c(3,2,1)] + if(combined == FALSE){ + return(cbind(format(dates,"%d"),format(dates,"%m"),format(dates,"%Y"))) } else { - cat("format is coerced to english, because I don't know what do you mean by:",format) + return(format(dates,"%d.%m.%Y")) + } + + } else { + if(prettyOut){ + return(cbind(format(dates,"%d.%m.%Y"),as.numeric(format(dates,"%d")),as.numeric(format(dates,"%m")),as.numeric(format(dates,"%Y"))) ) + } + dates <- dates[format(dates,"%m%d")!="0229"] + + if(combined == FALSE){ + return(cbind(format(dates,"%d"),format(dates,"%m"),format(dates,"%Y"))) + } else { + return(format(dates,"%d.%m.%Y")) } } - - if(combined==TRUE){ - dates <- apply(dates,1,function(x) paste(x,collapse=".")) - return(dates) - } - - return(dates) } + +corrigLeapYear <- function(data, dataCol, modellSettings = NULL, startYear, fromDate = NULL,toDate = NULL,formatString = "%Y-%m-%d"){ + data <- as.data.frame(data) + numDays <- nrow(data) + dates <- seq(as.Date(paste0(startYear,"01","01"),format = "%Y%m%d"), by= "day", length = numDays) + goodInd <- which(!(leap_year(dates)& + (format(date,"%m") == "12")& + (format(date,"%d") == "31"))) + realDate <- musoDate(startYear = startYear, numYears = numYears) + + data <- cbind.data.frame(real,data[goodInd]) + + modellDates <- musoDate(startYear = settings$startYear,numYears = settings$numYears) + + + + if(is.null(modellSettings)){ + modellSettings <- setupMuso() + } + + +} + +alignDataWithModelIndex <- function(){ + +} diff --git a/RBBGCMuso/R/setupMuso.R b/RBBGCMuso/R/setupMuso.R index 42882df..45d4a58 100644 --- a/RBBGCMuso/R/setupMuso.R +++ b/RBBGCMuso/R/setupMuso.R @@ -229,7 +229,7 @@ setupMuso <- function(executable=NULL, ## numValues will be replaced to numVar ## numValues<-unlist(read.table(iniInput[2],skip=102,nrows = 1)[1]) startYear <- as.numeric(unlist(strsplit(iniFiles[[2]][grep("TIME_DEFINE",iniFiles[[2]])+3],"[\ \t]"))[1]) - numData[1] <- numValues * sumDaysOfPeriod(startYear,numYears,corrigated=leapYear) + numData[1] <- numValues * numYears*365 #LEAPYEAR CORRECTION NEEDED numData[2] <- numYears * numValues*12 numData[3] <- numYears * numValues @@ -241,7 +241,7 @@ setupMuso <- function(executable=NULL, if(!is.null(modelOutputs)){ outVarChanges <- putOutVars(iniFile = iniInput[2],outputVars = modelOutputs, modifyOriginal = TRUE) - numData <- round(numDate*outVarChanges[[2]]) + numData <- round(numData*outVarChanges[[2]]) outputVars[[1]] <-outVarChanges[[1]] } From 32657c7330504c5ebff45b51ffd53967a8d2a521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Tue, 12 Feb 2019 21:24:16 +0100 Subject: [PATCH 03/88] Adding element to setupmuso, oneCsv option in musoMonte --- RBBGCMuso/R/calibration.R | 4 +- RBBGCMuso/R/musoMonte.R | 80 +++++++++++++++++++++++++++++---------- RBBGCMuso/R/setupMuso.R | 4 +- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 23edf8e..f6796ff 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -1,2 +1,2 @@ -GPP_mes <- read.csv("hhs_GPP_measured.csv", stringAsFactors) -head(GPP_mes$year) +## GPP_mes <- read.csv("hhs_GPP_measured.csv", stringAsFactors) +## head(GPP_mes$year) diff --git a/RBBGCMuso/R/musoMonte.R b/RBBGCMuso/R/musoMonte.R index 0bb315a..e4b34a6 100644 --- a/RBBGCMuso/R/musoMonte.R +++ b/RBBGCMuso/R/musoMonte.R @@ -181,27 +181,69 @@ musoMonte <- function(settings=NULL, ## csv files for each run oneCsv <- function () { - stop("This function is not implemented yet") - ## numDays <- settings$numdata[1] - ## if(!onDisk){ - ## for(i in 1:iterations){ - - ## parVar <- apply(parameters,1,function (x) { - ## runif(1, as.numeric(x[3]), as.numeric(x[4]))}) - - ## preservedEpc[(i+1),] <- parVar - ## exportName <- paste0(preTag,".csv") - ## write.csv(parvar,"preservedEpc.csv",append=TRUE) - ## calibMuso(settings,debugging = "stamplog", - ## parameters = parVar,keepEpc = TRUE) %>% - ## {mutate(.,iD = i)} %>% - ## {write.csv(.,file=exportName,append=TRUE)} - ## } + # stop("This function is not implemented yet") + settings$iniInput[2] %>% + (function(x) paste0(dirname(x),"/",tools::file_path_sans_ext(basename(x)),"-tmp.",tools::file_ext(x))) %>% + unlink + randValues <- randVals[[2]] + settings$calibrationPar <- randVals[[1]] + ## randValues <- randValues[,randVals[[1]] %in% parameters[,2]][,rank(parameters[,2])] + modellOut <- matrix(ncol = numVars, nrow = iterations + 1) + + origModellOut <- calibMuso(settings=settings,silent=TRUE) + write.csv(x=origModellOut, file=paste0(pretag,".csv")) + + if(!is.list(fun)){ + funct <- rep(list(fun), numVars) + } - ## return(preservedEpc) - ## } else { + tmp2 <- numeric(numVars) + + for(j in 1:numVars){ + tmp2[j]<-funct[[j]](origModellOut[,j]) + } + modellOut[1,]<- tmp2 + + for(i in 2:(iterations+1)){ + tmp <- tryCatch(calibMuso(settings = settings, + parameters = randValues[(i-1),], + silent= TRUE, + skipSpinup = skipSpinup, + keepEpc = keepEpc, + debugging = debugging, + outVars = outVars), error = function (e) NA) - ## } + if(!is.na(tmp)){ + for(j in 1:numVars){ + tmp2[j]<-funct[[j]](tmp[,j]) + } + } else { + for(j in 1:numVars){ + tmp2[j]<-rep(NA,length(settings$outputVars[[1]])) + } + } + + + + modellOut[i,]<- tmp2 + write.table(x=tmp, file=paste0(pretag,".csv"), append = TRUE,col.names = FALSE, sep = ",") + setTxtProgressBar(progBar,i) + } + + paramLines <- parameters[,2] + paramLines <- order(paramLines) + randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] + randInd <- order(randInd) + + + epcStrip <- rbind(origEpc[order(parameters[,2])], + randValues[,randVals[[1]] %in% parameters[,2]][,randInd]) + + + preservedEpc <- cbind(epcStrip, + modellOut) + colnames(preservedEpc) <- c(parameterNames[paramLines], sapply(outVarNames, function (x) paste0("mod.", x))) + return(preservedEpc) } netCDF <- function () { diff --git a/RBBGCMuso/R/setupMuso.R b/RBBGCMuso/R/setupMuso.R index 42882df..473b070 100644 --- a/RBBGCMuso/R/setupMuso.R +++ b/RBBGCMuso/R/setupMuso.R @@ -273,7 +273,9 @@ setupMuso <- function(executable=NULL, numYears=numYears, outputVars=outputVars, dailyVarCodes= gsub("\\s.*","",dailyVarCodes), - annualVarCodes = gsub("\\s.*","",annualVarCodes) + annualVarCodes = gsub("\\s.*","",annualVarCodes), + numVarY = length(outputVars[[2]]), + numVarD = length(outputVars[[1]]) ) if(writep!=nrow(grepHelper)){ From 587faa780488562dc7744399cbbec176869e3d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Wed, 13 Feb 2019 08:02:16 +0100 Subject: [PATCH 04/88] GLUE first working example --- RBBGCMuso/DESCRIPTION | 3 +- RBBGCMuso/NAMESPACE | 9 ++ RBBGCMuso/R/assistantFunctions.R | 48 ++++++++++ RBBGCMuso/R/calibration.R | 149 +++++++++++++++++++++++++++++- RBBGCMuso/R/musoTime.R | 72 ++++++++++----- RBBGCMuso/man/alignData.Rd | 13 +++ RBBGCMuso/man/calibMuso.Rd | 4 +- RBBGCMuso/man/dayOfMonths.Rd | 20 ---- RBBGCMuso/man/dayOfYears.Rd | 15 --- RBBGCMuso/man/isLeapyear.Rd | 18 ---- RBBGCMuso/man/musoDate.Rd | 25 +---- RBBGCMuso/man/musoLeapYears.Rd | 15 --- RBBGCMuso/man/optiMuso.Rd | 59 ++++++++++++ RBBGCMuso/man/readMeasuredMuso.Rd | 15 +++ RBBGCMuso/man/sumDaysOfPeriod.Rd | 15 --- 15 files changed, 349 insertions(+), 131 deletions(-) create mode 100644 RBBGCMuso/man/alignData.Rd delete mode 100644 RBBGCMuso/man/dayOfMonths.Rd delete mode 100644 RBBGCMuso/man/dayOfYears.Rd delete mode 100644 RBBGCMuso/man/isLeapyear.Rd delete mode 100644 RBBGCMuso/man/musoLeapYears.Rd create mode 100644 RBBGCMuso/man/optiMuso.Rd create mode 100644 RBBGCMuso/man/readMeasuredMuso.Rd delete mode 100644 RBBGCMuso/man/sumDaysOfPeriod.Rd diff --git a/RBBGCMuso/DESCRIPTION b/RBBGCMuso/DESCRIPTION index 1c5cf6f..42e5060 100644 --- a/RBBGCMuso/DESCRIPTION +++ b/RBBGCMuso/DESCRIPTION @@ -24,7 +24,8 @@ Imports: tibble, tidyr, tcltk, - digest + digest, + data.table LinkingTo: Rcpp SystemRequirements: C++11 Maintainer: Roland Hollo's diff --git a/RBBGCMuso/NAMESPACE b/RBBGCMuso/NAMESPACE index a11b3e9..c50660c 100644 --- a/RBBGCMuso/NAMESPACE +++ b/RBBGCMuso/NAMESPACE @@ -18,10 +18,12 @@ export(musoRand) export(musoRandomizer) export(musoSensi) export(normalMuso) +export(optiMuso) export(paramSweep) export(plotMuso) export(plotMusoWithData) export(randEpc) +export(readMeasuredMuso) export(runMuso) export(rungetMuso) export(saveAllMusoPlots) @@ -32,6 +34,7 @@ export(updateMusoMapping) import(ggplot2) import(utils) importFrom(Rcpp,evalCpp) +importFrom(data.table,fread) importFrom(digest,digest) importFrom(dplyr,'%>%') importFrom(dplyr,filter) @@ -40,6 +43,10 @@ importFrom(dplyr,mutate) importFrom(dplyr,select) importFrom(dplyr,summarize) importFrom(dplyr,tbl_df) +importFrom(ggplot,aes_string) +importFrom(ggplot,geom_point) +importFrom(ggplot,ggplot) +importFrom(ggplot,ggsave) importFrom(ggplot2,aes) importFrom(ggplot2,aes_string) importFrom(ggplot2,element_blank) @@ -57,7 +64,9 @@ importFrom(ggplot2,theme) importFrom(ggplot2,theme_classic) importFrom(ggplot2,xlab) importFrom(ggplot2,ylab) +importFrom(gridExtra,grid.arrange) importFrom(limSolve,xsample) +importFrom(lubridate,leap_year) importFrom(magrittr,'%<>%') importFrom(magrittr,'%>%') importFrom(rmarkdown,pandoc_version) diff --git a/RBBGCMuso/R/assistantFunctions.R b/RBBGCMuso/R/assistantFunctions.R index d198b84..a8fd3da 100644 --- a/RBBGCMuso/R/assistantFunctions.R +++ b/RBBGCMuso/R/assistantFunctions.R @@ -124,3 +124,51 @@ dynRound <- function(x,y,seqLen){ return(round(a,digitNum)) } + +readValuesFromFile <- function(epc, linums){ + epcFile <- readLines(epc) + rows <- numeric(2) + values <- sapply(linums, function(x){ + rows[1] <- as.integer(x) + rows[2] <- as.integer(round(100*x)) %% 10 + 1 + epcFile <- readLines(epc) + selRow <- unlist(strsplit(epcFile[rows[1]], split= "[\t ]")) + selRow <- selRow[selRow!=""] + return(as.numeric(selRow[rows[2]])) + + }) + + return(values) +} +#' readMeasuredMuso +#' +#' MuSo data reader +#' @importFrom data.table fread +#' @export + +readMeasuredMuso <- function(inFile, + naString = getOption("datatable.na.strings","NA"), sep = ",", + leapYearHandling = TRUE, + convert.var = NULL, + convert.scalar = 1, + convert.fun = (function (x) {x * convert.scalar}), + convert.file = NULL, + filterCol = NULL, + filterVal = 1, + selVar = NULL + ){ + + baseData <- fread(file = inFile, na.strings = as.character(naString), sep=sep) + baseData <- as.data.frame(baseData) + if(!is.null(filterCol)){ + filterVar<- colnames(baseData)[filterCol] + baseData[(baseData[,filterVar] == filterVal),selVar] <- NA + } + head(baseData) + if(!is.null(selVar)){ + baseData <- cbind.data.frame(baseData,convert.fun(baseData[,selVar])) + colnames(baseData)[ncol(baseData)]<- paste0("M",selVar) + } + + return(data.table(baseData)) +} diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index f6796ff..69f1c17 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -1,2 +1,147 @@ -## GPP_mes <- read.csv("hhs_GPP_measured.csv", stringAsFactors) -## head(GPP_mes$year) +#' optiMuso +#' +#' This function calculates the -users specified- likelihood for random model input. +#' +#' @author Roland HOLLOS +#' @param measuredDataFile a +#' @param parameters b +#' @param sep c +#' @param startDate d +#' @param endDate e +#' @param formatString a +#' @param filterCol a +#' @param filterVal b +#' @param selVar c +#' @param outLoc c +#' @param pretag a +#' @param calPar a +#' @param skipSpinup a +#' @param iterations c +#' @param constrains d +#' @param likelihood d +#' @param settings e +#' @param leapYear b +#' @param plotName u +#' @importFrom ggplot ggplot aes_string geom_point ggsave +#' @importFrom magrittr '%>%' +#' @importFrom gridExtra grid.arrange +#' @export +optiMuso <- function(measuredDataFile, parameters = NULL, + sep = ",", startDate, + endDate, formatString = "%Y-%m-%d", + naString = NULL, leapYear = TRUE, + filterCol = NULL, filterVal = 1, + selVar, outLoc = "./calib", + preTag = "cal-", + settings = NULL, + outVars = NULL, + iterations = 30, + skipSpinup = TRUE, + constrains = NULL, + plotName = "calib.jpg", + likelihood = function(x, y){ + exp(-sqrt(mean((x-y)^2))) + }, + calPar = 3009) +{ + measuredData <- readMeasuredMuso(inFile = measuredDataFile, sep = sep, selVar = selVar,filterCol = filterCol, filterVal = filterVal) + + if(is.null(parameters)){ + parameters <- tryCatch(read.csv("parameters.csv", stringsAsFactor=FALSE), error = function (e) { + stop("You need to specify a path for the parameters.csv, or a matrix.") + }) + } else { + if((!is.list(parameters)) & (!is.matrix(parameters))){ + parameters <- tryCatch(read.csv(parameters, stringsAsFactor=FALSE), error = function (e){ + stop("Cannot find neither parameters file neither the parameters matrix") + }) + }} + + outLoc <- normalizePath(outLoc) + outLocPlain <- basename(outLoc) + currDir <- getwd() + + if(!dir.exists(outLoc)){ + dir.create(outLoc) + warning(paste(outLoc," is not exists, so it was created")) + } + + outLoc <- normalizePath(outLoc) + + if(is.null(settings)){ + settings <- setupMuso() + } + + parameterNames <- parameters[,1] + pretag <- file.path(outLoc,preTag) + npar <- length(settings$calibrationPar) + + ##reading the original epc file at the specified + ## row numbers + if(iterations < 3000){ + randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = 3000) + randVals[[2]]<- randVals[[2]][sample(1:3000,iterations),] + } else { + randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = iterations) + } + + origEpc <- readValuesFromFile(settings$epc[2],parameters[,2]) + + ## Prepare the preservedCalib matrix for the faster + ## run. + + pretag <- file.path(outLoc,preTag) + + ## Creating function for generating separate + ## csv files for each run + + progBar <- txtProgressBar(1,iterations,style=3) + colNumb <- which(settings$dailyVarCodes == calPar) + settings$iniInput[2] %>% + (function(x) paste0(dirname(x),"/",tools::file_path_sans_ext(basename(x)),"-tmp.",tools::file_ext(x))) %>% + unlink + randValues <- randVals[[2]] + settings$calibrationPar <- randVals[[1]] + list2env(alignData(measuredData,dataCol = 8,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = FALSE),envir=environment()) + + modellOut <- numeric(iterations + 1) # single variable solution + origModellOut <- calibMuso(settings=settings,silent=TRUE) + write.csv(x=origModellOut, file=paste0(pretag,1,".csv")) + modellOut[1] <- likelihood(measuredData,origModellOut[modIndex,colNumb]) + for(i in 2:(iterations+1)){ + tmp <- tryCatch(calibMuso(settings = settings, + parameters = randValues[(i-1),], + silent= TRUE, + skipSpinup = skipSpinup)[modIndex,colNumb], error = function (e) NA) + + modellOut[i]<- likelihood(measuredData,tmp) + write.csv(x=tmp, file=paste0(pretag,(i+1),".csv")) + setTxtProgressBar(progBar,i) + } + modellOut + paramLines <- parameters[,2] + paramLines <- order(paramLines) + randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] + randInd <- order(randInd) + + + + epcStrip <- rbind(origEpc[order(parameters[,2])], + randValues[,randVals[[1]] %in% parameters[,2]][,randInd]) + + + preservedCalib <- cbind(epcStrip, + modellOut) + colnames(preservedCalib) <- c(parameterNames[paramLines], "likelihood") + p<-list() + + for(i in seq_along(colnames(preservedCalib)[-ncol(preservedCalib)])){ + p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(size=0.9) + } + + ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 600) + + return(preservedCalib[preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"]),]) +} + + diff --git a/RBBGCMuso/R/musoTime.R b/RBBGCMuso/R/musoTime.R index 042dde1..88dffb6 100644 --- a/RBBGCMuso/R/musoTime.R +++ b/RBBGCMuso/R/musoTime.R @@ -25,7 +25,10 @@ musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leap dates <- seq(from = as.Date(paste0(startYear,"01","01"),format = "%Y%m%d"), to = as.Date(paste0(endYear,"12","31"),format = "%Y%m%d"), by = "day") if(leapYearHandling){ if(prettyOut){ - return(cbind(format(dates,"%d.%m.%Y"),as.numeric(format(dates,"%d")),as.numeric(format(dates,"%m")),as.numeric(format(dates,"%Y"))) ) + return(cbind(format(dates,"%d.%m.%Y"), + as.numeric(format(dates,"%d")), + as.numeric(format(dates,"%m")), + as.numeric(format(dates,"%Y"))) ) } if(combined == FALSE){ @@ -35,10 +38,14 @@ musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leap } } else { + dates <- dates[format(dates,"%m%d")!="0229"] if(prettyOut){ - return(cbind(format(dates,"%d.%m.%Y"),as.numeric(format(dates,"%d")),as.numeric(format(dates,"%m")),as.numeric(format(dates,"%Y"))) ) + return(data.frame(date = format(dates,"%d.%m.%Y"), + day = as.numeric(format(dates,"%d")), + month = as.numeric(format(dates,"%m")), + year = as.numeric(format(dates,"%Y")))) } - dates <- dates[format(dates,"%m%d")!="0229"] + if(combined == FALSE){ return(cbind(format(dates,"%d"),format(dates,"%m"),format(dates,"%Y"))) @@ -48,29 +55,48 @@ musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leap } } +#' alignData +#' +#' This function align the data to the model and the model to the data +#' @importFrom lubridate leap_year +#' @keywords internal +alignData <- function(mdata, dataCol, modellSettings = NULL, startDate, endDate, formatString = "%Y-%m-%d", leapYear = TRUE){ -corrigLeapYear <- function(data, dataCol, modellSettings = NULL, startYear, fromDate = NULL,toDate = NULL,formatString = "%Y-%m-%d"){ - data <- as.data.frame(data) - numDays <- nrow(data) - dates <- seq(as.Date(paste0(startYear,"01","01"),format = "%Y%m%d"), by= "day", length = numDays) - goodInd <- which(!(leap_year(dates)& - (format(date,"%m") == "12")& - (format(date,"%d") == "31"))) - realDate <- musoDate(startYear = startYear, numYears = numYears) + startDate <- as.Date(startDate, format = formatString) + endDate <- as.Date(endDate, format = formatString) + mdata <- as.data.frame(mdata) - data <- cbind.data.frame(real,data[goodInd]) - - modellDates <- musoDate(startYear = settings$startYear,numYears = settings$numYears) - - - if(is.null(modellSettings)){ modellSettings <- setupMuso() } - - -} - -alignDataWithModelIndex <- function(){ -} + dates <- seq(startDate, to = endDate, by= "day") + if(!leapYear){ + dates <- dates[which(format(dates,"%m%d") != "0229")] + } + mdata <- mdata[dates >= as.Date(paste0(modellSettings$startYear,"01","01"),format = "%Y%m%d"),] + dates <- dates[dates >= as.Date(paste0(modellSettings$startYear,"01","01"),format = "%Y%m%d")] + goodInd <- which(!(leap_year(dates)& + (format(dates,"%m") == "12")& + (format(dates,"%d") == "31"))) + if(leapYear){ + goodInd <- which(!(leap_year(dates)& + (format(dates,"%m") == "12")& + (format(dates,"%d") == "31"))) + } else { + goodInd <-seq_along(dates) + } + realDate <- dates[which(format(dates,"%m%d") != "0229")] + if(leapYear){ + mdata <- cbind.data.frame(realDate,mdata) + } else { + mdata <- cbind.data.frame(dates,mdata) + } + modellDates <- as.Date(musoDate(startYear = modellSettings$startYear,numYears = modellSettings$numYears), format = "%d.%m.%Y") + mdata <- mdata[mdata[,1] %in% modellDates,] + nonEmpty <- which(!is.na(mdata[,dataCol+1])) + mdata <- mdata[nonEmpty,] + modIndex <- which(modellDates %in% mdata[,1]) + + list(measuredData = mdata[,dataCol +1], modIndex = modIndex) +} diff --git a/RBBGCMuso/man/alignData.Rd b/RBBGCMuso/man/alignData.Rd new file mode 100644 index 0000000..a304852 --- /dev/null +++ b/RBBGCMuso/man/alignData.Rd @@ -0,0 +1,13 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/musoTime.R +\name{alignData} +\alias{alignData} +\title{alignData} +\usage{ +alignData(mdata, dataCol, modellSettings = NULL, startDate, endDate, + formatString = "\%Y-\%m-\%d", leapYear = TRUE) +} +\description{ +This function align the data to the model and the model to the data +} +\keyword{internal} diff --git a/RBBGCMuso/man/calibMuso.Rd b/RBBGCMuso/man/calibMuso.Rd index 4731e8e..99d96cb 100644 --- a/RBBGCMuso/man/calibMuso.Rd +++ b/RBBGCMuso/man/calibMuso.Rd @@ -26,8 +26,6 @@ keepEpc=FALSE, export=FALSE, silent=FALSE, aggressive=FALSE, leapYear=FALSE) \item{aggressive}{It deletes every possible modell-outputs from the previous modell runs.} -\item{leapYear}{Should the function do a leapyear correction on the outputdata? If TRUE, then the 31.12 day will be doubled.} - \item{keepBinary}{In default RBBGCMuso to keep working area as clean as possible, deletes all the regular output files. The results are directly printed to the standard output, but you can redirect it, and save it to a variable, or you can export your results to the desired destination in a desired format. Whith this variable you can enable to keep the binary output files. If you want to set the location of the binary output, please take a look at the binaryPlace argument.} \item{binaryPlace}{The place of the binary output files.} @@ -37,6 +35,8 @@ keepEpc=FALSE, export=FALSE, silent=FALSE, aggressive=FALSE, leapYear=FALSE) \item{skipSpinup}{If TRUE, calibMuso wont do spinup simulation} \item{prettyOut}{date ad Date type, separate year, month, day vectors} + +\item{leapYear}{Should the function do a leapyear correction on the outputdata? If TRUE, then the 31.12 day will be doubled.} } \value{ No return, outputs are written to file diff --git a/RBBGCMuso/man/dayOfMonths.Rd b/RBBGCMuso/man/dayOfMonths.Rd deleted file mode 100644 index 9bf83bc..0000000 --- a/RBBGCMuso/man/dayOfMonths.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/musoTime.R -\name{dayOfMonths} -\alias{dayOfMonths} -\title{dayOfMonths} -\usage{ -dayOfMonths(year, corrigated=TRUE) -} -\arguments{ -\item{year}{a year} - -\item{corrigated}{Do you want to handle the leapyears, if yes choose TRUE} -} -\value{ -vector with 12 element. First is January, the last is December. All of the vector element represents the number of the days in that specific month -} -\description{ -This function gives as a vector which contains the number of the days per each month -} -\keyword{internal} diff --git a/RBBGCMuso/man/dayOfYears.Rd b/RBBGCMuso/man/dayOfYears.Rd deleted file mode 100644 index ffea8ab..0000000 --- a/RBBGCMuso/man/dayOfYears.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/musoTime.R -\name{dayOfYears} -\alias{dayOfYears} -\title{This function tells us how many days are in the given year.} -\usage{ -dayOfYears(year, corrigated = TRUE) -} -\description{ -This function tells us how many days are in the given year. -} -\author{ -Roland Hollos -} -\keyword{internal} diff --git a/RBBGCMuso/man/isLeapyear.Rd b/RBBGCMuso/man/isLeapyear.Rd deleted file mode 100644 index e502643..0000000 --- a/RBBGCMuso/man/isLeapyear.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/musoTime.R -\name{isLeapyear} -\alias{isLeapyear} -\title{isLeapyear} -\usage{ -isLeapyear(year) -} -\arguments{ -\item{year}{a year} -} -\value{ -TRUE, if leapyear, FALSE if dont. -} -\description{ -This function tells us if its argument a leapyear or not. -} -\keyword{internal} diff --git a/RBBGCMuso/man/musoDate.Rd b/RBBGCMuso/man/musoDate.Rd index 011a92a..d8096e7 100644 --- a/RBBGCMuso/man/musoDate.Rd +++ b/RBBGCMuso/man/musoDate.Rd @@ -2,29 +2,14 @@ % Please edit documentation in R/musoTime.R \name{musoDate} \alias{musoDate} -\title{It generates BiomeBGC-MuSo dates} +\title{musoDate} \usage{ -musoDate(startYear, numYears, timestep = "d", combined = TRUE, - corrigated = TRUE, format = "en") -} -\arguments{ -\item{timestep}{timestep, which can be daily ("d"), monthly ("m"), yearly("y")} - -\item{combined}{If FALSE the output is a vector of 3 string: day, month year, if true, these strings will be concatenated.} - -\item{corrigated}{If True it counts with leapyears, else dont.} - -\item{format}{This is the format of the date. It can be "en" (dd.mm.yyyy), or "hu" (yyyy.mm.dd)} - -\item{settings}{You have to run the setupMuso function before musoDate. It is its output which contains all of the necessary system variables. It sets the whole environment.} -} -\value{ -The exact date-vectors for the BioBGC-MuSo output. You can use this for labelling purpose for example. +musoDate(startYear, endYears = NULL, numYears, combined = TRUE, + leapYearHandling = FALSE, prettyOut = FALSE) } \description{ -It generates all of the day-dates which are between the start and endyear of BiomeBGC-MuSo run. -How many days are from the given date and given period length(periodlen)? +This function generates MuSo compatibla dates for the data } \author{ -Roland Hollos +Roland HOLLOS } diff --git a/RBBGCMuso/man/musoLeapYears.Rd b/RBBGCMuso/man/musoLeapYears.Rd deleted file mode 100644 index 3a4c3fb..0000000 --- a/RBBGCMuso/man/musoLeapYears.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/musoTime.R -\name{musoLeapYears} -\alias{musoLeapYears} -\title{Musoleapyear} -\usage{ -musoLeapYears(settings) -} -\description{ -How many days are from the given date and given period length(periodlen)? -} -\author{ -Roland Hollos -} -\keyword{internal} diff --git a/RBBGCMuso/man/optiMuso.Rd b/RBBGCMuso/man/optiMuso.Rd new file mode 100644 index 0000000..1db9db9 --- /dev/null +++ b/RBBGCMuso/man/optiMuso.Rd @@ -0,0 +1,59 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/calibration.R +\name{optiMuso} +\alias{optiMuso} +\title{optiMuso} +\usage{ +optiMuso(measuredDataFile, parameters = NULL, sep = ",", startDate, + endDate, formatString, naString = NULL, leapYear = TRUE, + filterCol = NULL, filterVal = 1, selVar, outLoc = "./calib", + preTag = "cal-", settings = NULL, outVars = NULL, + iterations = 30, skipSpinup = TRUE, constrains = NULL, + plotName = "calib.jpg", likelihood = function(x, y) { + exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) +} +\arguments{ +\item{measuredDataFile}{a} + +\item{parameters}{b} + +\item{sep}{c} + +\item{startDate}{d} + +\item{endDate}{e} + +\item{formatString}{a} + +\item{leapYear}{b} + +\item{filterCol}{a} + +\item{filterVal}{b} + +\item{selVar}{c} + +\item{outLoc}{c} + +\item{settings}{e} + +\item{iterations}{c} + +\item{skipSpinup}{a} + +\item{constrains}{d} + +\item{plotName}{u} + +\item{likelihood}{d} + +\item{calPar}{a} + +\item{pretag}{a} +} +\description{ +This function calculates the -users specified- likelihood for random model input. +} +\author{ +Roland HOLLOS +} diff --git a/RBBGCMuso/man/readMeasuredMuso.Rd b/RBBGCMuso/man/readMeasuredMuso.Rd new file mode 100644 index 0000000..9e15211 --- /dev/null +++ b/RBBGCMuso/man/readMeasuredMuso.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assistantFunctions.R +\name{readMeasuredMuso} +\alias{readMeasuredMuso} +\title{readMeasuredMuso} +\usage{ +readMeasuredMuso(inFile, naString = getOption("datatable.na.strings", + "NA"), sep = ",", leapYearHandling = TRUE, convert.var = NULL, + convert.scalar = 1, convert.fun = (function(x) { x * + convert.scalar }), convert.file = NULL, filterCol = NULL, + filterVal = 1, selVar = NULL) +} +\description{ +MuSo data reader +} diff --git a/RBBGCMuso/man/sumDaysOfPeriod.Rd b/RBBGCMuso/man/sumDaysOfPeriod.Rd deleted file mode 100644 index ea7e49f..0000000 --- a/RBBGCMuso/man/sumDaysOfPeriod.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/musoTime.R -\name{sumDaysOfPeriod} -\alias{sumDaysOfPeriod} -\title{How many days are from the given date and given period length(periodlen)?} -\usage{ -sumDaysOfPeriod(year, periodlen, corrigated = TRUE) -} -\description{ -How many days are from the given date and given period length(periodlen)? -} -\author{ -Roland Hollos -} -\keyword{internal} From d00d513949c0eb61b41807f483a32e709e8c2c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Wed, 13 Feb 2019 08:10:35 +0100 Subject: [PATCH 05/88] minor bugfix and major version upgrade --- RBBGCMuso/DESCRIPTION | 5 +++-- RBBGCMuso/NAMESPACE | 4 ---- RBBGCMuso/R/calibration.R | 2 +- RBBGCMuso/man/optiMuso.Rd | 12 ++++++------ 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/RBBGCMuso/DESCRIPTION b/RBBGCMuso/DESCRIPTION index 42e5060..cc75f13 100644 --- a/RBBGCMuso/DESCRIPTION +++ b/RBBGCMuso/DESCRIPTION @@ -1,6 +1,6 @@ Package: RBBGCMuso Title: An R package for BiomeBGC-MuSo ecosystem modelling -Version: 0.6.3.0 +Version: 0.7.0.0 Authors@R: person("Roland", "Hollo's", , "hollorol@gmail.com", role = c("aut", "cre")) Description: What the package does (one paragraph). Depends: R (>= 3.3.2) @@ -25,7 +25,8 @@ Imports: tidyr, tcltk, digest, - data.table + data.table, + gridExtra LinkingTo: Rcpp SystemRequirements: C++11 Maintainer: Roland Hollo's diff --git a/RBBGCMuso/NAMESPACE b/RBBGCMuso/NAMESPACE index c50660c..080f02f 100644 --- a/RBBGCMuso/NAMESPACE +++ b/RBBGCMuso/NAMESPACE @@ -43,10 +43,6 @@ importFrom(dplyr,mutate) importFrom(dplyr,select) importFrom(dplyr,summarize) importFrom(dplyr,tbl_df) -importFrom(ggplot,aes_string) -importFrom(ggplot,geom_point) -importFrom(ggplot,ggplot) -importFrom(ggplot,ggsave) importFrom(ggplot2,aes) importFrom(ggplot2,aes_string) importFrom(ggplot2,element_blank) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 69f1c17..0456bb2 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -22,7 +22,7 @@ #' @param settings e #' @param leapYear b #' @param plotName u -#' @importFrom ggplot ggplot aes_string geom_point ggsave +#' @importFrom ggplot2 ggplot aes_string geom_point ggsave #' @importFrom magrittr '%>%' #' @importFrom gridExtra grid.arrange #' @export diff --git a/RBBGCMuso/man/optiMuso.Rd b/RBBGCMuso/man/optiMuso.Rd index 1db9db9..1decc2c 100644 --- a/RBBGCMuso/man/optiMuso.Rd +++ b/RBBGCMuso/man/optiMuso.Rd @@ -5,12 +5,12 @@ \title{optiMuso} \usage{ optiMuso(measuredDataFile, parameters = NULL, sep = ",", startDate, - endDate, formatString, naString = NULL, leapYear = TRUE, - filterCol = NULL, filterVal = 1, selVar, outLoc = "./calib", - preTag = "cal-", settings = NULL, outVars = NULL, - iterations = 30, skipSpinup = TRUE, constrains = NULL, - plotName = "calib.jpg", likelihood = function(x, y) { - exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) + endDate, formatString = "\%Y-\%m-\%d", naString = NULL, + leapYear = TRUE, filterCol = NULL, filterVal = 1, selVar, + outLoc = "./calib", preTag = "cal-", settings = NULL, + outVars = NULL, iterations = 30, skipSpinup = TRUE, + constrains = NULL, plotName = "calib.jpg", likelihood = function(x, + y) { exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) } \arguments{ \item{measuredDataFile}{a} From 9363ef8c9f3a1c70392de7fecea1f6e1288e0fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Thu, 14 Feb 2019 18:54:43 +0100 Subject: [PATCH 06/88] Fixing few bugs --- RBBGCMuso/NAMESPACE | 1 + RBBGCMuso/R/assistantFunctions.R | 2 +- RBBGCMuso/R/calibration.R | 16 +++++----- RBBGCMuso/R/plotMuso.R | 49 +++++++++++++------------------ RBBGCMuso/man/optiMuso.Rd | 13 ++++---- RBBGCMuso/man/plotMusoWithData.Rd | 12 ++++---- 6 files changed, 43 insertions(+), 50 deletions(-) diff --git a/RBBGCMuso/NAMESPACE b/RBBGCMuso/NAMESPACE index 080f02f..656816d 100644 --- a/RBBGCMuso/NAMESPACE +++ b/RBBGCMuso/NAMESPACE @@ -34,6 +34,7 @@ export(updateMusoMapping) import(ggplot2) import(utils) importFrom(Rcpp,evalCpp) +importFrom(data.table,data.table) importFrom(data.table,fread) importFrom(digest,digest) importFrom(dplyr,'%>%') diff --git a/RBBGCMuso/R/assistantFunctions.R b/RBBGCMuso/R/assistantFunctions.R index a8fd3da..04a74a8 100644 --- a/RBBGCMuso/R/assistantFunctions.R +++ b/RBBGCMuso/R/assistantFunctions.R @@ -143,7 +143,7 @@ readValuesFromFile <- function(epc, linums){ #' readMeasuredMuso #' #' MuSo data reader -#' @importFrom data.table fread +#' @importFrom data.table fread data.table #' @export readMeasuredMuso <- function(inFile, diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 0456bb2..a697f7a 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -26,12 +26,10 @@ #' @importFrom magrittr '%>%' #' @importFrom gridExtra grid.arrange #' @export -optiMuso <- function(measuredDataFile, parameters = NULL, - sep = ",", startDate, +optiMuso <- function(measuredData, parameters = NULL, startDate, endDate, formatString = "%Y-%m-%d", - naString = NULL, leapYear = TRUE, - filterCol = NULL, filterVal = 1, - selVar, outLoc = "./calib", + leapYear = TRUE, + dataVar, outLoc = "./calib", preTag = "cal-", settings = NULL, outVars = NULL, @@ -44,8 +42,8 @@ optiMuso <- function(measuredDataFile, parameters = NULL, }, calPar = 3009) { - measuredData <- readMeasuredMuso(inFile = measuredDataFile, sep = sep, selVar = selVar,filterCol = filterCol, filterVal = filterVal) - + dataCol <- grep(dataVar, colnames(measuredData)) + if(is.null(parameters)){ parameters <- tryCatch(read.csv("parameters.csv", stringsAsFactor=FALSE), error = function (e) { stop("You need to specify a path for the parameters.csv, or a matrix.") @@ -102,7 +100,7 @@ optiMuso <- function(measuredDataFile, parameters = NULL, unlink randValues <- randVals[[2]] settings$calibrationPar <- randVals[[1]] - list2env(alignData(measuredData,dataCol = 8,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = FALSE),envir=environment()) + list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = FALSE),envir=environment()) modellOut <- numeric(iterations + 1) # single variable solution origModellOut <- calibMuso(settings=settings,silent=TRUE) @@ -140,7 +138,7 @@ optiMuso <- function(measuredDataFile, parameters = NULL, } ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 600) - + write.csv(preservedCalib,"preservedCalib.csv") return(preservedCalib[preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"]),]) } diff --git a/RBBGCMuso/R/plotMuso.R b/RBBGCMuso/R/plotMuso.R index 36d7fac..cee07c8 100644 --- a/RBBGCMuso/R/plotMuso.R +++ b/RBBGCMuso/R/plotMuso.R @@ -247,40 +247,33 @@ plotMuso <- function(settings = NULL, variable = 1, #' debugging=FALSE, keepEpc=FALSE, #' logfilename=NULL, aggressive=FALSE, #' leapYear=FALSE, export=FALSE) -#' @import ggplot2 +#' @importFrom ggplot2 ggplot geom_line geom_point aes aes_string labs theme element_blank #' @export -plotMusoWithData <- function(csvFile, variable, NACHAR=NA, settings=NULL, sep=",", savePlot=NULL,colour=c("black","blue"), calibrationPar=NULL, parameters=NULL){ - if(!is.na(NACHAR)){ - warning("NACHAR is not implemented yet") - } - if(is.null(settings)){ - settings <- setupMuso() - } - - numberOfYears <- settings$numYears - startYear <- settings$startYear - yearVec <- seq(from = startYear, length=numberOfYears,by=1) +plotMusoWithData <- function(mdata, plotName=NULL, + startDate, endDate, + colour=c("black","blue"),dataVar, modelVar, settings = setupMuso(), silent = TRUE){ - - data <- read.table(csvFile,header = TRUE, sep = ",") %>% - select(variable) - - baseData <- calibMuso(settings,silent=TRUE) %>% - as.data.frame() %>% - rownames_to_column("date") %>% - mutate(date2=date,date=as.Date(date,"%d.%m.%Y"),yearDay=rep(1:365,numberOfYears)) %>% - separate(date2,c("day","month","year"),sep="\\.") - baseData <- cbind(baseData,data) - colnames(baseData)[ncol(baseData)] <- "measuredData" + dataCol<- grep(paste0("^",dataVar,"$"), colnames(mdata)) + selVar <- grep(modelVar,(settings$dailyVarCodes))+4 - p <- baseData %>% - ggplot(aes_string("date",variable)) + + list2env(alignData(mdata, dataCol = dataCol, + modellSettings = settings, + startDate = startDate, + endDate = endDate, leapYear = FALSE),envir=environment()) + + + ## measuredData is created + baseData <- calibMuso(settings = settings, silent = silent, prettyOut = TRUE)[modIndex,] + baseData[,1] <- as.Date(baseData[,1],format = "%d.%m.%Y") + selVarName <- colnames(baseData)[selVar] + p <- baseData %>% + ggplot(aes_string("date",selVarName)) + geom_line(colour=colour[1]) + geom_point(colour=colour[2], aes(date,measuredData)) + - labs(y = paste0(variable,"_measured"))+ + labs(y = paste0(selVarName,"_measured"))+ theme(axis.title.x = element_blank()) - if(!is.null(savePlot)){ - ggsave(savePlot,p) + if(!is.null(plotName)){ + ggsave(plotName,p) return(p) } else { return(p) diff --git a/RBBGCMuso/man/optiMuso.Rd b/RBBGCMuso/man/optiMuso.Rd index 1decc2c..23b584d 100644 --- a/RBBGCMuso/man/optiMuso.Rd +++ b/RBBGCMuso/man/optiMuso.Rd @@ -5,12 +5,13 @@ \title{optiMuso} \usage{ optiMuso(measuredDataFile, parameters = NULL, sep = ",", startDate, - endDate, formatString = "\%Y-\%m-\%d", naString = NULL, - leapYear = TRUE, filterCol = NULL, filterVal = 1, selVar, - outLoc = "./calib", preTag = "cal-", settings = NULL, - outVars = NULL, iterations = 30, skipSpinup = TRUE, - constrains = NULL, plotName = "calib.jpg", likelihood = function(x, - y) { exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) + endDate, formatString = "\%Y-\%m-\%d", + naString = getOption("datatable.na.strings", "NA"), leapYear = TRUE, + filterCol = NULL, filterVal = 1, selVar, outLoc = "./calib", + preTag = "cal-", settings = NULL, outVars = NULL, + iterations = 30, skipSpinup = TRUE, constrains = NULL, + plotName = "calib.jpg", likelihood = function(x, y) { + exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) } \arguments{ \item{measuredDataFile}{a} diff --git a/RBBGCMuso/man/plotMusoWithData.Rd b/RBBGCMuso/man/plotMusoWithData.Rd index 6bdaa42..d7b8b3d 100644 --- a/RBBGCMuso/man/plotMusoWithData.Rd +++ b/RBBGCMuso/man/plotMusoWithData.Rd @@ -11,18 +11,18 @@ logfilename=NULL, aggressive=FALSE, leapYear=FALSE, export=FALSE) } \arguments{ -\item{csvFile}{This specifies the filename of the measurements. It must contain a header. Typically this is a CSV file.} - -\item{variable}{The name of the output variable to plot} - -\item{NACHAR}{This is not implemented yet} - \item{settings}{RBBGCMuso uses variables that define the entire simulation environment. Those environment variables include the name of the INI files, the name of the meteorology files, the path to the model executable and its file name, the entire output list, the entire output variable matrix, the dependency rules for the EPC parameters etc. Using the runMuso function RBBGCMuso can automatically create those environment variables by inspecting the files in the working directory (this happens through the setupMuso function). It means that by default model setup is performed automatically in the background and the user has nothing to do. With this settings parameter we can force runMuso to skip automatic environment setup as we provide the environment settings to runMuso. In a typical situation the user can skip this option.} \item{sep}{This is the separator symbol used in the measurement file (that is supposed to be a delimited text file)} \item{savePlot}{It it is specified, the plot will be saved in a graphical format specified by the immanent extension. For example, it the savePlot is set to image01.png then a PNG graphics file will be created.} +\item{variable}{The name of the output variable to plot} + +\item{NACHAR}{This is not implemented yet} + +\item{csvFile}{This specifies the filename of the measurements. It must contain a header. Typically this is a CSV file.} + \item{calibrationPar}{You might want to change some parameters in your EPC file before running the model. The function offers possibility for this without editing the EPC file. In this situation you have to select the appropirate model parameters first. You can refer to these parameters with the number of the line in the EPC file. Indexing of lines start from one. You should use a vector for this referencing like c(1,5,8)} \item{parameters}{Using the function it is possible to change some of the EPC parameters prior to model execution. This can be achieved with this option. In the parameters variable you have set the row indices of the variables that you wish to change. In this parameters you can give an exact value for them in a vector form like c(1,2,3,4).} From 86f1ba4f97cf469e7d3c9341b1ef44390adfccdf Mon Sep 17 00:00:00 2001 From: hollorol Date: Sun, 17 Feb 2019 09:43:46 +0100 Subject: [PATCH 07/88] type = "rda " in xsample --- RBBGCMuso/R/musoRand.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RBBGCMuso/R/musoRand.R b/RBBGCMuso/R/musoRand.R index bb995cc..ed9350f 100644 --- a/RBBGCMuso/R/musoRand.R +++ b/RBBGCMuso/R/musoRand.R @@ -171,10 +171,10 @@ musoRand <- function(parameters, constrains = NULL, iterations=3000){ h <- c(Gh0$h,h) E <- do.call(rbind,lapply(Ef,function(x){x$E})) f <- do.call(c,lapply(Ef,function(x){x$f})) - randVal <- suppressWarnings(limSolve::xsample(G=G,H=h,E=E,F=f,iter = iterations))$X + randVal <- suppressWarnings(limSolve::xsample(G=G,H=h,E=E,F=f,iter = iterations, type = "rda"))$X } else{ Gh0<-genMat0(dependences) - randVal <- suppressWarnings(xsample(G=Gh0$G,H=Gh0$h, iter = iterations))$X + randVal <- suppressWarnings(xsample(G=Gh0$G,H=Gh0$h, iter = iterations, type = "rda"))$X } results <- list(INDEX =dependences$INDEX, randVal=randVal) From 2318804ea0bd9e4fe7301188e0a83d9887c3ee7e Mon Sep 17 00:00:00 2001 From: hollorol Date: Sun, 17 Feb 2019 13:40:21 +0100 Subject: [PATCH 08/88] string handling --- RBBGCMuso/R/assistantFunctions.R | 21 ++++++++++++++++++--- RBBGCMuso/R/plotMuso.R | 11 ++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/RBBGCMuso/R/assistantFunctions.R b/RBBGCMuso/R/assistantFunctions.R index 04a74a8..150df05 100644 --- a/RBBGCMuso/R/assistantFunctions.R +++ b/RBBGCMuso/R/assistantFunctions.R @@ -147,7 +147,7 @@ readValuesFromFile <- function(epc, linums){ #' @export readMeasuredMuso <- function(inFile, - naString = getOption("datatable.na.strings","NA"), sep = ",", + naString = NULL, sep = ",", leapYearHandling = TRUE, convert.var = NULL, convert.scalar = 1, @@ -157,9 +157,24 @@ readMeasuredMuso <- function(inFile, filterVal = 1, selVar = NULL ){ + + if(!is.null(naString)){ + if(is.numeric(naString)){ + baseData <- fread(file = inFile, sep=sep) + baseData <- as.data.frame(baseData) + baseData[baseData[,selVar] == naString,selVar] <- NA + } else { + baseData <- fread(file = inFile, sep=sep, naString = naString) + baseData <- as.data.frame(baseData) + } + + + } else { + + baseData <- fread(file = inFile, sep=sep) + baseData <- as.data.frame(baseData) + } - baseData <- fread(file = inFile, na.strings = as.character(naString), sep=sep) - baseData <- as.data.frame(baseData) if(!is.null(filterCol)){ filterVar<- colnames(baseData)[filterCol] baseData[(baseData[,filterVar] == filterVal),selVar] <- NA diff --git a/RBBGCMuso/R/plotMuso.R b/RBBGCMuso/R/plotMuso.R index cee07c8..dbb392b 100644 --- a/RBBGCMuso/R/plotMuso.R +++ b/RBBGCMuso/R/plotMuso.R @@ -260,12 +260,17 @@ plotMusoWithData <- function(mdata, plotName=NULL, modellSettings = settings, startDate = startDate, endDate = endDate, leapYear = FALSE),envir=environment()) - - + + ## measuredData is created baseData <- calibMuso(settings = settings, silent = silent, prettyOut = TRUE)[modIndex,] baseData[,1] <- as.Date(baseData[,1],format = "%d.%m.%Y") - selVarName <- colnames(baseData)[selVar] + selVarName <- colnames(baseData)[selVar] + if(colnames(baseData) != unique(colnames(baseData))){ + notUnique <- setdiff((unlist(settings$dailyVarCodes)),unique(unlist(settings$dailyVarCodes))) + stop(paste0("Error: daily output variable list in the ini file must contain unique numbers. Check your ini files! Not unique codes: ",notUnique)) + } + p <- baseData %>% ggplot(aes_string("date",selVarName)) + geom_line(colour=colour[1]) + From 4ab2b60f4917a98a78d09c9349d6408f394b484e Mon Sep 17 00:00:00 2001 From: hollorol Date: Sun, 17 Feb 2019 20:23:38 +0100 Subject: [PATCH 09/88] back to the original --- RBBGCMuso/R/musoRand.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RBBGCMuso/R/musoRand.R b/RBBGCMuso/R/musoRand.R index ed9350f..bb995cc 100644 --- a/RBBGCMuso/R/musoRand.R +++ b/RBBGCMuso/R/musoRand.R @@ -171,10 +171,10 @@ musoRand <- function(parameters, constrains = NULL, iterations=3000){ h <- c(Gh0$h,h) E <- do.call(rbind,lapply(Ef,function(x){x$E})) f <- do.call(c,lapply(Ef,function(x){x$f})) - randVal <- suppressWarnings(limSolve::xsample(G=G,H=h,E=E,F=f,iter = iterations, type = "rda"))$X + randVal <- suppressWarnings(limSolve::xsample(G=G,H=h,E=E,F=f,iter = iterations))$X } else{ Gh0<-genMat0(dependences) - randVal <- suppressWarnings(xsample(G=Gh0$G,H=Gh0$h, iter = iterations, type = "rda"))$X + randVal <- suppressWarnings(xsample(G=Gh0$G,H=Gh0$h, iter = iterations))$X } results <- list(INDEX =dependences$INDEX, randVal=randVal) From 0d9d551fd92b6656331448dac233dd704843d6b1 Mon Sep 17 00:00:00 2001 From: hollorol Date: Tue, 19 Feb 2019 09:48:29 +0100 Subject: [PATCH 10/88] Modify many --- RBBGCMuso/DESCRIPTION | 2 +- RBBGCMuso/NAMESPACE | 2 +- RBBGCMuso/R/assistantFunctions.R | 5 ++--- RBBGCMuso/R/calibration.R | 24 ++++++++++++++-------- RBBGCMuso/R/musoTime.R | 27 ++++++++++++++++-------- RBBGCMuso/R/plotMuso.R | 6 +++--- RBBGCMuso/man/alignData.Rd | 5 +++-- RBBGCMuso/man/optiMuso.Rd | 34 +++++++++++++++---------------- RBBGCMuso/man/readMeasuredMuso.Rd | 15 -------------- RBBGCMuso/man/readObservedData.Rd | 15 ++++++++++++++ 10 files changed, 74 insertions(+), 61 deletions(-) delete mode 100644 RBBGCMuso/man/readMeasuredMuso.Rd create mode 100644 RBBGCMuso/man/readObservedData.Rd diff --git a/RBBGCMuso/DESCRIPTION b/RBBGCMuso/DESCRIPTION index cc75f13..dd37868 100644 --- a/RBBGCMuso/DESCRIPTION +++ b/RBBGCMuso/DESCRIPTION @@ -1,6 +1,6 @@ Package: RBBGCMuso Title: An R package for BiomeBGC-MuSo ecosystem modelling -Version: 0.7.0.0 +Version: 0.7.0.1 Authors@R: person("Roland", "Hollo's", , "hollorol@gmail.com", role = c("aut", "cre")) Description: What the package does (one paragraph). Depends: R (>= 3.3.2) diff --git a/RBBGCMuso/NAMESPACE b/RBBGCMuso/NAMESPACE index 656816d..4649a8c 100644 --- a/RBBGCMuso/NAMESPACE +++ b/RBBGCMuso/NAMESPACE @@ -23,7 +23,7 @@ export(paramSweep) export(plotMuso) export(plotMusoWithData) export(randEpc) -export(readMeasuredMuso) +export(readObservedData) export(runMuso) export(rungetMuso) export(saveAllMusoPlots) diff --git a/RBBGCMuso/R/assistantFunctions.R b/RBBGCMuso/R/assistantFunctions.R index 150df05..6c8c904 100644 --- a/RBBGCMuso/R/assistantFunctions.R +++ b/RBBGCMuso/R/assistantFunctions.R @@ -146,7 +146,7 @@ readValuesFromFile <- function(epc, linums){ #' @importFrom data.table fread data.table #' @export -readMeasuredMuso <- function(inFile, +readObservedData <- function(inFile, naString = NULL, sep = ",", leapYearHandling = TRUE, convert.var = NULL, @@ -181,8 +181,7 @@ readMeasuredMuso <- function(inFile, } head(baseData) if(!is.null(selVar)){ - baseData <- cbind.data.frame(baseData,convert.fun(baseData[,selVar])) - colnames(baseData)[ncol(baseData)]<- paste0("M",selVar) + baseData[,selVar] <-convert.fun(baseData[,selVar]) } return(data.table(baseData)) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index a697f7a..28fe2d6 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -28,7 +28,7 @@ #' @export optiMuso <- function(measuredData, parameters = NULL, startDate, endDate, formatString = "%Y-%m-%d", - leapYear = TRUE, + leapYearHandling = TRUE, dataVar, outLoc = "./calib", preTag = "cal-", settings = NULL, @@ -40,7 +40,8 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, likelihood = function(x, y){ exp(-sqrt(mean((x-y)^2))) }, - calPar = 3009) + continious, + modelVar = 3009) { dataCol <- grep(dataVar, colnames(measuredData)) @@ -75,7 +76,8 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, npar <- length(settings$calibrationPar) ##reading the original epc file at the specified - ## row numbers + ## row numbers + print("optiMuso is randomizing the epc parameters now...",quote = FALSE) if(iterations < 3000){ randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = 3000) randVals[[2]]<- randVals[[2]][sample(1:3000,iterations),] @@ -94,18 +96,22 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, ## csv files for each run progBar <- txtProgressBar(1,iterations,style=3) - colNumb <- which(settings$dailyVarCodes == calPar) + colNumb <- which(settings$dailyVarCodes == modelVar) settings$iniInput[2] %>% (function(x) paste0(dirname(x),"/",tools::file_path_sans_ext(basename(x)),"-tmp.",tools::file_ext(x))) %>% unlink randValues <- randVals[[2]] settings$calibrationPar <- randVals[[1]] - list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = FALSE),envir=environment()) + list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = leapYearHandling, continious = continious),envir=environment()) modellOut <- numeric(iterations + 1) # single variable solution - origModellOut <- calibMuso(settings=settings,silent=TRUE) + rmse <- numeric(iterations + 1) + origModellOut <- calibMuso(settings=settings,silent=TRUE, skipSpinup = skipSpinup) + + write.csv(x=origModellOut, file=paste0(pretag,1,".csv")) modellOut[1] <- likelihood(measuredData,origModellOut[modIndex,colNumb]) + print("Running the model with the random epc values...", quote = FALSE) for(i in 2:(iterations+1)){ tmp <- tryCatch(calibMuso(settings = settings, parameters = randValues[(i-1),], @@ -113,10 +119,10 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, skipSpinup = skipSpinup)[modIndex,colNumb], error = function (e) NA) modellOut[i]<- likelihood(measuredData,tmp) + rmse[i] <- sqrt(mean((measuredData-tmp)^2)) write.csv(x=tmp, file=paste0(pretag,(i+1),".csv")) setTxtProgressBar(progBar,i) } - modellOut paramLines <- parameters[,2] paramLines <- order(paramLines) randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] @@ -128,7 +134,7 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, randValues[,randVals[[1]] %in% parameters[,2]][,randInd]) - preservedCalib <- cbind(epcStrip, + preservedCalib <- cbind(epcStrip,rmsr, modellOut) colnames(preservedCalib) <- c(parameterNames[paramLines], "likelihood") p<-list() @@ -137,7 +143,7 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(size=0.9) } - ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 600) + ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 3000) write.csv(preservedCalib,"preservedCalib.csv") return(preservedCalib[preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"]),]) } diff --git a/RBBGCMuso/R/musoTime.R b/RBBGCMuso/R/musoTime.R index 88dffb6..92d5ce4 100644 --- a/RBBGCMuso/R/musoTime.R +++ b/RBBGCMuso/R/musoTime.R @@ -11,7 +11,6 @@ #' @importFrom lubridate leap_year #' @export - musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leapYearHandling = FALSE, prettyOut = FALSE){ if(is.null(endYears) & is.null(numYears)){ @@ -60,8 +59,9 @@ musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leap #' This function align the data to the model and the model to the data #' @importFrom lubridate leap_year #' @keywords internal -alignData <- function(mdata, dataCol, modellSettings = NULL, startDate, endDate, formatString = "%Y-%m-%d", leapYear = TRUE){ +alignData <- function(mdata, dataCol, modellSettings = NULL, startDate=NULL, endDate=NULL, formatString = "%Y-%m-%d", leapYear = TRUE, continious = TRUE){ + startDate <- as.Date(startDate, format = formatString) endDate <- as.Date(endDate, format = formatString) mdata <- as.data.frame(mdata) @@ -69,16 +69,25 @@ alignData <- function(mdata, dataCol, modellSettings = NULL, startDate, endDate if(is.null(modellSettings)){ modellSettings <- setupMuso() } - - dates <- seq(startDate, to = endDate, by= "day") - if(!leapYear){ - dates <- dates[which(format(dates,"%m%d") != "0229")] + + if(continious){ + dates <- seq(startDate, to = endDate, by= "day") + } else{ + dates <- do.call(c,lapply(seq(nrow(mdata)), function(i){ as.Date(paste0(mdata[i,1],sprintf("%02d",mdata[i,2]),mdata[i,3]),format = "%Y%m%d")})) } + + if(!leapYear){ + casualDays <- which(format(dates,"%m%d") != "0229") + #mdata <- mdata[casualDays,] + dates <- dates[casualDays] + } + mdata <- mdata[dates >= as.Date(paste0(modellSettings$startYear,"01","01"),format = "%Y%m%d"),] dates <- dates[dates >= as.Date(paste0(modellSettings$startYear,"01","01"),format = "%Y%m%d")] - goodInd <- which(!(leap_year(dates)& - (format(dates,"%m") == "12")& - (format(dates,"%d") == "31"))) + ## goodInd <- which(!(leap_year(dates)& + ## (format(dates,"%m") == "12")& + ## (format(dates,"%d") == "31"))) + if(leapYear){ goodInd <- which(!(leap_year(dates)& (format(dates,"%m") == "12")& diff --git a/RBBGCMuso/R/plotMuso.R b/RBBGCMuso/R/plotMuso.R index dbb392b..2d58118 100644 --- a/RBBGCMuso/R/plotMuso.R +++ b/RBBGCMuso/R/plotMuso.R @@ -251,7 +251,7 @@ plotMuso <- function(settings = NULL, variable = 1, #' @export plotMusoWithData <- function(mdata, plotName=NULL, startDate, endDate, - colour=c("black","blue"),dataVar, modelVar, settings = setupMuso(), silent = TRUE){ + colour=c("black","blue"),dataVar, modelVar, settings = setupMuso(), silent = TRUE, continious = TRUE){ dataCol<- grep(paste0("^",dataVar,"$"), colnames(mdata)) selVar <- grep(modelVar,(settings$dailyVarCodes))+4 @@ -259,14 +259,14 @@ plotMusoWithData <- function(mdata, plotName=NULL, list2env(alignData(mdata, dataCol = dataCol, modellSettings = settings, startDate = startDate, - endDate = endDate, leapYear = FALSE),envir=environment()) + endDate = endDate, leapYear = FALSE, continious = continious),envir=environment()) ## measuredData is created baseData <- calibMuso(settings = settings, silent = silent, prettyOut = TRUE)[modIndex,] baseData[,1] <- as.Date(baseData[,1],format = "%d.%m.%Y") selVarName <- colnames(baseData)[selVar] - if(colnames(baseData) != unique(colnames(baseData))){ + if(!all.equal(colnames(baseData),unique(colnames(baseData)))){ notUnique <- setdiff((unlist(settings$dailyVarCodes)),unique(unlist(settings$dailyVarCodes))) stop(paste0("Error: daily output variable list in the ini file must contain unique numbers. Check your ini files! Not unique codes: ",notUnique)) } diff --git a/RBBGCMuso/man/alignData.Rd b/RBBGCMuso/man/alignData.Rd index a304852..d747d3f 100644 --- a/RBBGCMuso/man/alignData.Rd +++ b/RBBGCMuso/man/alignData.Rd @@ -4,8 +4,9 @@ \alias{alignData} \title{alignData} \usage{ -alignData(mdata, dataCol, modellSettings = NULL, startDate, endDate, - formatString = "\%Y-\%m-\%d", leapYear = TRUE) +alignData(mdata, dataCol, modellSettings = NULL, startDate = NULL, + endDate = NULL, formatString = "\%Y-\%m-\%d", leapYear = TRUE, + continious = TRUE) } \description{ This function align the data to the model and the model to the data diff --git a/RBBGCMuso/man/optiMuso.Rd b/RBBGCMuso/man/optiMuso.Rd index 23b584d..819e91e 100644 --- a/RBBGCMuso/man/optiMuso.Rd +++ b/RBBGCMuso/man/optiMuso.Rd @@ -4,22 +4,16 @@ \alias{optiMuso} \title{optiMuso} \usage{ -optiMuso(measuredDataFile, parameters = NULL, sep = ",", startDate, - endDate, formatString = "\%Y-\%m-\%d", - naString = getOption("datatable.na.strings", "NA"), leapYear = TRUE, - filterCol = NULL, filterVal = 1, selVar, outLoc = "./calib", - preTag = "cal-", settings = NULL, outVars = NULL, - iterations = 30, skipSpinup = TRUE, constrains = NULL, - plotName = "calib.jpg", likelihood = function(x, y) { - exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) +optiMuso(measuredData, parameters = NULL, startDate, endDate, + formatString = "\%Y-\%m-\%d", leapYear = TRUE, dataVar, + outLoc = "./calib", preTag = "cal-", settings = NULL, + outVars = NULL, iterations = 30, skipSpinup = TRUE, + constrains = NULL, plotName = "calib.jpg", likelihood = function(x, + y) { exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) } \arguments{ -\item{measuredDataFile}{a} - \item{parameters}{b} -\item{sep}{c} - \item{startDate}{d} \item{endDate}{e} @@ -28,12 +22,6 @@ optiMuso(measuredDataFile, parameters = NULL, sep = ",", startDate, \item{leapYear}{b} -\item{filterCol}{a} - -\item{filterVal}{b} - -\item{selVar}{c} - \item{outLoc}{c} \item{settings}{e} @@ -50,6 +38,16 @@ optiMuso(measuredDataFile, parameters = NULL, sep = ",", startDate, \item{calPar}{a} +\item{measuredDataFile}{a} + +\item{sep}{c} + +\item{filterCol}{a} + +\item{filterVal}{b} + +\item{selVar}{c} + \item{pretag}{a} } \description{ diff --git a/RBBGCMuso/man/readMeasuredMuso.Rd b/RBBGCMuso/man/readMeasuredMuso.Rd deleted file mode 100644 index 9e15211..0000000 --- a/RBBGCMuso/man/readMeasuredMuso.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assistantFunctions.R -\name{readMeasuredMuso} -\alias{readMeasuredMuso} -\title{readMeasuredMuso} -\usage{ -readMeasuredMuso(inFile, naString = getOption("datatable.na.strings", - "NA"), sep = ",", leapYearHandling = TRUE, convert.var = NULL, - convert.scalar = 1, convert.fun = (function(x) { x * - convert.scalar }), convert.file = NULL, filterCol = NULL, - filterVal = 1, selVar = NULL) -} -\description{ -MuSo data reader -} diff --git a/RBBGCMuso/man/readObservedData.Rd b/RBBGCMuso/man/readObservedData.Rd new file mode 100644 index 0000000..622b4a3 --- /dev/null +++ b/RBBGCMuso/man/readObservedData.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/assistantFunctions.R +\name{readObservedData} +\alias{readObservedData} +\title{readMeasuredMuso} +\usage{ +readObservedData(inFile, naString = NULL, sep = ",", + leapYearHandling = TRUE, convert.var = NULL, convert.scalar = 1, + convert.fun = (function(x) { x * convert.scalar }), + convert.file = NULL, filterCol = NULL, filterVal = 1, + selVar = NULL) +} +\description{ +MuSo data reader +} From cfd6a1f7be1b523ebf5022385574fe2c3a9ac2f1 Mon Sep 17 00:00:00 2001 From: hollorol Date: Tue, 19 Feb 2019 15:44:26 +0100 Subject: [PATCH 11/88] use of testthat and post processing --- RBBGCMuso/R/calibMuso.R | 13 ++++++++++--- RBBGCMuso/R/postProc.R | 10 ++++++++++ RBBGCMuso/inst/examples/hhs/muso | Bin RBBGCMuso/inst/tests/test_postProcMuso.R | 21 +++++++++++++++++++++ RBBGCMuso/man/optiMuso.Rd | 12 ++++++------ 5 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 RBBGCMuso/R/postProc.R mode change 100644 => 100755 RBBGCMuso/inst/examples/hhs/muso create mode 100644 RBBGCMuso/inst/tests/test_postProcMuso.R diff --git a/RBBGCMuso/R/calibMuso.R b/RBBGCMuso/R/calibMuso.R index 8deb21c..10938fd 100644 --- a/RBBGCMuso/R/calibMuso.R +++ b/RBBGCMuso/R/calibMuso.R @@ -31,7 +31,8 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, silent=FALSE, aggressive=FALSE, keepBinary=FALSE, binaryPlace="./", fileToChange="epc", - skipSpinup = TRUE, modifyOriginal =FALSE, prettyOut = FALSE){ + skipSpinup = TRUE, modifyOriginal =FALSE, prettyOut = FALSE, + postProcString = NULL){ # ######################################################################## ###########################Set local variables and places############### ######################################################################## @@ -321,6 +322,9 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, stop("Modell Failure") } + + + if(timee=="d"){ if(!prettyOut){ colnames(Reva) <- unlist(settings$outputVars[[1]]) @@ -338,7 +342,9 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, colnames(Reva) <- unlist(settings$outputVars[[2]]) } - + if(!is.null(postProcString)){ + Reva <- postProcMuso(Reva,postProcString) + } ## if(leapYear){ ## Reva <- corrigMuso(settings,Reva) @@ -372,5 +378,6 @@ calibMuso <- function(settings=NULL, calibrationPar=NULL, } else{ setwd(whereAmI) - return(Reva)} + return(Reva) + } } diff --git a/RBBGCMuso/R/postProc.R b/RBBGCMuso/R/postProc.R new file mode 100644 index 0000000..502cece --- /dev/null +++ b/RBBGCMuso/R/postProc.R @@ -0,0 +1,10 @@ +postProcMuso <- function(modelData, procString){ + cNames <- colnames(modelData) + tocalc <- gsub("(@)(\\d)","modelData[,\\2]",procString) + newVarName <- gsub("\\s","",unlist(strsplit(procString,"<-"))[1]) + assign(newVarName,eval(parse(text = unlist(strsplit(tocalc,"<-"))[2]))) + modelData <- cbind.data.frame(modelData,eval(parse(text = newVarName))) + colnames(modelData) <- c(cNames,newVarName) + modelData +} + diff --git a/RBBGCMuso/inst/examples/hhs/muso b/RBBGCMuso/inst/examples/hhs/muso old mode 100644 new mode 100755 diff --git a/RBBGCMuso/inst/tests/test_postProcMuso.R b/RBBGCMuso/inst/tests/test_postProcMuso.R new file mode 100644 index 0000000..1234d0f --- /dev/null +++ b/RBBGCMuso/inst/tests/test_postProcMuso.R @@ -0,0 +1,21 @@ +context("Post processing") +library(testthat) +library(RBBGCMuso) +setwd(system.file("examples/hhs","",package = "RBBGCMuso")) + +test_that("Post processing string",{ + testMatrix1 <- data.frame(first = rep(1,5), second = rep(2,5), third = rep(3,5)) + testMatrix1c <- testMatrix1 + testMatrix1c[,"newCol"] <- testMatrix1c[,2] + 3 * testMatrix1c[,3] + expect_equal(postProcMuso(testMatrix1,"newCol <- @2 + 3*@3"),testMatrix1c) +}) + +test_that("calibMuso with postprocessing",{ + model <- calibMuso(skipSpinup = FALSE, silent = TRUE) + modelc<- model + newCol <- modelc[,1] + modelc<- cbind.data.frame(modelc,newCol) + modelc[,"newCol"]<- model[,5]+3*model[,7] + expect_equal(calibMuso(skipSpinup = FALSE,silent = TRUE, postProcString = "newCol <- @5 + 3* @7"), modelc) +}) + diff --git a/RBBGCMuso/man/optiMuso.Rd b/RBBGCMuso/man/optiMuso.Rd index 819e91e..85ab4b3 100644 --- a/RBBGCMuso/man/optiMuso.Rd +++ b/RBBGCMuso/man/optiMuso.Rd @@ -5,11 +5,11 @@ \title{optiMuso} \usage{ optiMuso(measuredData, parameters = NULL, startDate, endDate, - formatString = "\%Y-\%m-\%d", leapYear = TRUE, dataVar, + formatString = "\%Y-\%m-\%d", leapYearHandling = TRUE, dataVar, outLoc = "./calib", preTag = "cal-", settings = NULL, outVars = NULL, iterations = 30, skipSpinup = TRUE, constrains = NULL, plotName = "calib.jpg", likelihood = function(x, - y) { exp(-sqrt(mean((x - y)^2))) }, calPar = 3009) + y) { exp(-sqrt(mean((x - y)^2))) }, continious, modelVar = 3009) } \arguments{ \item{parameters}{b} @@ -20,8 +20,6 @@ optiMuso(measuredData, parameters = NULL, startDate, endDate, \item{formatString}{a} -\item{leapYear}{b} - \item{outLoc}{c} \item{settings}{e} @@ -36,8 +34,6 @@ optiMuso(measuredData, parameters = NULL, startDate, endDate, \item{likelihood}{d} -\item{calPar}{a} - \item{measuredDataFile}{a} \item{sep}{c} @@ -49,6 +45,10 @@ optiMuso(measuredData, parameters = NULL, startDate, endDate, \item{selVar}{c} \item{pretag}{a} + +\item{calPar}{a} + +\item{leapYear}{b} } \description{ This function calculates the -users specified- likelihood for random model input. From eb23330d13e1b8382dcef54591aa52bb11e8aa3d Mon Sep 17 00:00:00 2001 From: hollorol Date: Tue, 19 Feb 2019 16:16:15 +0100 Subject: [PATCH 12/88] postProc in calibMuso --- RBBGCMuso/R/calibration.R | 138 ++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 66 deletions(-) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 28fe2d6..e0ba5b2 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -41,88 +41,94 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, exp(-sqrt(mean((x-y)^2))) }, continious, - modelVar = 3009) + modelVar = 3009, + postProcString = NULL) { dataCol <- grep(dataVar, colnames(measuredData)) - if(is.null(parameters)){ - parameters <- tryCatch(read.csv("parameters.csv", stringsAsFactor=FALSE), error = function (e) { - stop("You need to specify a path for the parameters.csv, or a matrix.") - }) - } else { - if((!is.list(parameters)) & (!is.matrix(parameters))){ - parameters <- tryCatch(read.csv(parameters, stringsAsFactor=FALSE), error = function (e){ - stop("Cannot find neither parameters file neither the parameters matrix") - }) - }} + if(is.null(parameters)){ + parameters <- tryCatch(read.csv("parameters.csv", stringsAsFactor=FALSE), error = function (e) { + stop("You need to specify a path for the parameters.csv, or a matrix.") + }) + } else { + if((!is.list(parameters)) & (!is.matrix(parameters))){ + parameters <- tryCatch(read.csv(parameters, stringsAsFactor=FALSE), error = function (e){ + stop("Cannot find neither parameters file neither the parameters matrix") + }) + }} - outLoc <- normalizePath(outLoc) - outLocPlain <- basename(outLoc) - currDir <- getwd() + outLoc <- normalizePath(outLoc) + outLocPlain <- basename(outLoc) + currDir <- getwd() + + if(!dir.exists(outLoc)){ + dir.create(outLoc) + warning(paste(outLoc," is not exists, so it was created")) + } - if(!dir.exists(outLoc)){ - dir.create(outLoc) - warning(paste(outLoc," is not exists, so it was created")) - } + outLoc <- normalizePath(outLoc) - outLoc <- normalizePath(outLoc) - - if(is.null(settings)){ - settings <- setupMuso() - } - - parameterNames <- parameters[,1] - pretag <- file.path(outLoc,preTag) - npar <- length(settings$calibrationPar) - + if(is.null(settings)){ + settings <- setupMuso() + } + + parameterNames <- parameters[,1] + pretag <- file.path(outLoc,preTag) + npar <- length(settings$calibrationPar) + ##reading the original epc file at the specified ## row numbers print("optiMuso is randomizing the epc parameters now...",quote = FALSE) - if(iterations < 3000){ - randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = 3000) - randVals[[2]]<- randVals[[2]][sample(1:3000,iterations),] - } else { - randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = iterations) - } - - origEpc <- readValuesFromFile(settings$epc[2],parameters[,2]) - - ## Prepare the preservedCalib matrix for the faster - ## run. - - pretag <- file.path(outLoc,preTag) - + if(iterations < 3000){ + randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = 3000) + randVals[[2]]<- randVals[[2]][sample(1:3000,iterations),] + } else { + randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = iterations) + } + + origEpc <- readValuesFromFile(settings$epc[2],parameters[,2]) + + ## Prepare the preservedCalib matrix for the faster + ## run. + + pretag <- file.path(outLoc,preTag) + ## Creating function for generating separate - ## csv files for each run - - progBar <- txtProgressBar(1,iterations,style=3) - colNumb <- which(settings$dailyVarCodes == modelVar) - settings$iniInput[2] %>% + ## csv files for each run + + progBar <- txtProgressBar(1,iterations,style=3) + colNumb <- which(settings$dailyVarCodes == modelVar) + settings$iniInput[2] %>% (function(x) paste0(dirname(x),"/",tools::file_path_sans_ext(basename(x)),"-tmp.",tools::file_ext(x))) %>% unlink - randValues <- randVals[[2]] - settings$calibrationPar <- randVals[[1]] - list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = leapYearHandling, continious = continious),envir=environment()) + randValues <- randVals[[2]] + settings$calibrationPar <- randVals[[1]] + list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = leapYearHandling, continious = continious),envir=environment()) - modellOut <- numeric(iterations + 1) # single variable solution - rmse <- numeric(iterations + 1) + modellOut <- numeric(iterations + 1) # single variable solution + rmse <- numeric(iterations + 1) origModellOut <- calibMuso(settings=settings,silent=TRUE, skipSpinup = skipSpinup) - write.csv(x=origModellOut, file=paste0(pretag,1,".csv")) - modellOut[1] <- likelihood(measuredData,origModellOut[modIndex,colNumb]) - print("Running the model with the random epc values...", quote = FALSE) - for(i in 2:(iterations+1)){ - tmp <- tryCatch(calibMuso(settings = settings, - parameters = randValues[(i-1),], - silent= TRUE, - skipSpinup = skipSpinup)[modIndex,colNumb], error = function (e) NA) - - modellOut[i]<- likelihood(measuredData,tmp) - rmse[i] <- sqrt(mean((measuredData-tmp)^2)) - write.csv(x=tmp, file=paste0(pretag,(i+1),".csv")) - setTxtProgressBar(progBar,i) - } + write.csv(x=origModellOut, file=paste0(pretag,1,".csv")) + modellOut[1] <- likelihood(measuredData,origModellOut[modIndex,colNumb]) + print("Running the model with the random epc values...", quote = FALSE) + + if(!is.null(postProcString)){ + colNumb <- length(settings$dailyVarCodes) + 1 + } + + for(i in 2:(iterations+1)){ + tmp <- tryCatch(calibMuso(settings = settings, + parameters = randValues[(i-1),], + silent= TRUE, + skipSpinup = skipSpinup, postProcString = postProcString)[modIndex,colNumb], error = function (e) NA) + + modellOut[i]<- likelihood(measuredData,tmp) + rmse[i] <- sqrt(mean((measuredData-tmp)^2)) + write.csv(x=tmp, file=paste0(pretag,(i+1),".csv")) + setTxtProgressBar(progBar,i) + } paramLines <- parameters[,2] paramLines <- order(paramLines) randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] From 2bfe5d6dbaa83daa8fdbb47e9c4e0f64cab49cc8 Mon Sep 17 00:00:00 2001 From: hollorol Date: Thu, 21 Feb 2019 10:32:05 +0100 Subject: [PATCH 13/88] Fixing grouping --- RBBGCMuso/NAMESPACE | 1 + RBBGCMuso/R/plotMuso.R | 35 ++++++++++++++++++----------------- RBBGCMuso/man/optiMuso.Rd | 3 ++- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/RBBGCMuso/NAMESPACE b/RBBGCMuso/NAMESPACE index 4649a8c..ed4477d 100644 --- a/RBBGCMuso/NAMESPACE +++ b/RBBGCMuso/NAMESPACE @@ -34,6 +34,7 @@ export(updateMusoMapping) import(ggplot2) import(utils) importFrom(Rcpp,evalCpp) +importFrom(data.table,':=') importFrom(data.table,data.table) importFrom(data.table,fread) importFrom(digest,digest) diff --git a/RBBGCMuso/R/plotMuso.R b/RBBGCMuso/R/plotMuso.R index 2d58118..0ca7507 100644 --- a/RBBGCMuso/R/plotMuso.R +++ b/RBBGCMuso/R/plotMuso.R @@ -25,6 +25,7 @@ #' @importFrom dplyr filter group_by summarize mutate '%>%' #' @importFrom tibble rownames_to_column #' @importFrom tidyr separate gather +#' @importFrom data.table ':=' data.table #' @export plotMuso <- function(settings = NULL, variable = 1, @@ -38,7 +39,7 @@ plotMuso <- function(settings = NULL, variable = 1, layerPlot = FALSE, colour = "blue", skipSpinup = TRUE, fromData = FALSE, timeFrame = "day", selectYear = NULL, - groupFun = mean, separateFile = FALSE, dpi=300){ + groupFun = mean, separateFile = FALSE, dpi=300, postProcString = NULL){ if( plotType!="cts" && plotType != "dts"){ warning(paste0("The plotType ", plotType," is not implemented, plotType is set to cts")) @@ -60,13 +61,12 @@ plotMuso <- function(settings = NULL, variable = 1, ## logfilename=logfilename, ## export=export) - groupByTimeFrame <- function(data, timeFrame, groupFun){ - datas <- data %>% - group_by(eval(parse(text=timeFrame))) %>% - summarize(variable=groupFun(eval(parse(text=variable)))) - datas[,1]<-as.numeric(unlist(datas[,1])) - colnames(datas) <- c("date",variable) - datas + groupByTimeFrame <- function(Data, timeFrame, groupFun){ + Data <- data.table(Data) + Data[,c(variable):=groupFun(get(variable)),get(timeFrame)] + Data <- as.data.frame(Data) + Data[,1] <- as.Date(Data[,1],"%d.%m.%Y") + Data } if(fromData){ @@ -84,20 +84,16 @@ plotMuso <- function(settings = NULL, variable = 1, mutate(date=as.Date(as.character(date),"%d.%m.%Y")) } else { if(!is.element("cum_yieldC_HRV",unlist(settings$outputVars[[1]]))){ - musoData <- calibMuso(settings,silent = TRUE,skipSpinup=skipSpinup) %>% - as.data.frame() %>% - rownames_to_column("date") %>% - mutate(date2=date,date=as.Date(date,"%d.%m.%Y")) %>% - separate(date2,c("day","month","year"),sep="\\.") + musoData <- calibMuso(postProcString = postProcString,settings,silent = TRUE,skipSpinup=skipSpinup,prettyOut = TRUE) if(!is.null(selectYear)){ musoData <- musoData %>% filter(year == get("selectYear")) } if(timeFrame!="day"){ - musoData <- tryCatch(groupByTimeFrame(data=musoData, timeFrame = timeFrame, groupFun = groupFun), - error=function(e){stop("The timeFrame or the gropFun is not found")}) + musoData <- tryCatch(groupByTimeFrame(Data=musoData, timeFrame = timeFrame, groupFun = groupFun), + error=function(e){stop("The timeFrame or the groupFun is not found")}) }} else { - musoData <- calibMuso(settings,silent = TRUE,skipSpinup=skipSpinup,parameters = parameters, calibrationPar = calibrationPar,fileToChange = fileToChange) %>% + musoData <- calibMuso(postProcString = postProcString,settings,silent = TRUE,skipSpinup=skipSpinup,parameters = parameters, calibrationPar = calibrationPar,fileToChange = fileToChange) %>% as.data.frame() %>% rownames_to_column("date") %>% mutate(date2=date,date=as.Date(date,"%d.%m.%Y"), @@ -204,7 +200,12 @@ plotMuso <- function(settings = NULL, variable = 1, if(identical(character(0),setdiff(variable,as.character(settings$outputVars[[1]])))){ variableName <- variable } else { - stop("The symmetric difference of the set of the output variables specified in the ini files and the set specified with your variable parameter is not the empty set.") + if(!is.null(postProcString)){ + variableName <- variable + } else { + stop("The symmetric difference of the set of the output variables specified in the ini files and the set specified with your variable parameter is not the empty set.") + } + } } diff --git a/RBBGCMuso/man/optiMuso.Rd b/RBBGCMuso/man/optiMuso.Rd index 85ab4b3..3673eb2 100644 --- a/RBBGCMuso/man/optiMuso.Rd +++ b/RBBGCMuso/man/optiMuso.Rd @@ -9,7 +9,8 @@ optiMuso(measuredData, parameters = NULL, startDate, endDate, outLoc = "./calib", preTag = "cal-", settings = NULL, outVars = NULL, iterations = 30, skipSpinup = TRUE, constrains = NULL, plotName = "calib.jpg", likelihood = function(x, - y) { exp(-sqrt(mean((x - y)^2))) }, continious, modelVar = 3009) + y) { exp(-sqrt(mean((x - y)^2))) }, continious, modelVar = 3009, + postProcString = NULL) } \arguments{ \item{parameters}{b} From 59cd97679c3e493c6d62c674759fc52e94f7e17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 4 Mar 2019 10:44:49 +0100 Subject: [PATCH 14/88] fix typo in calibration.R --- RBBGCMuso/R/calibration.R | 57 ++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index e0ba5b2..61d1365 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -65,9 +65,9 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, dir.create(outLoc) warning(paste(outLoc," is not exists, so it was created")) } - + outLoc <- normalizePath(outLoc) - + if(is.null(settings)){ settings <- setupMuso() } @@ -93,18 +93,18 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, pretag <- file.path(outLoc,preTag) - ## Creating function for generating separate + ## Creating function for generating separate ## csv files for each run progBar <- txtProgressBar(1,iterations,style=3) colNumb <- which(settings$dailyVarCodes == modelVar) settings$iniInput[2] %>% - (function(x) paste0(dirname(x),"/",tools::file_path_sans_ext(basename(x)),"-tmp.",tools::file_ext(x))) %>% - unlink + (function(x) paste0(dirname(x),"/",tools::file_path_sans_ext(basename(x)),"-tmp.",tools::file_ext(x))) %>% + unlink randValues <- randVals[[2]] settings$calibrationPar <- randVals[[1]] list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = leapYearHandling, continious = continious),envir=environment()) - + modellOut <- numeric(iterations + 1) # single variable solution rmse <- numeric(iterations + 1) origModellOut <- calibMuso(settings=settings,silent=TRUE, skipSpinup = skipSpinup) @@ -129,29 +129,32 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, write.csv(x=tmp, file=paste0(pretag,(i+1),".csv")) setTxtProgressBar(progBar,i) } - paramLines <- parameters[,2] - paramLines <- order(paramLines) - randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] - randInd <- order(randInd) - - + paramLines <- parameters[,2] + paramLines <- order(paramLines) + randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] + randInd <- order(randInd) + + - epcStrip <- rbind(origEpc[order(parameters[,2])], - randValues[,randVals[[1]] %in% parameters[,2]][,randInd]) - - - preservedCalib <- cbind(epcStrip,rmsr, - modellOut) - colnames(preservedCalib) <- c(parameterNames[paramLines], "likelihood") - p<-list() - - for(i in seq_along(colnames(preservedCalib)[-ncol(preservedCalib)])){ - p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(size=0.9) - } + epcStrip <- rbind(origEpc[order(parameters[,2])], + randValues[,randVals[[1]] %in% parameters[,2]][,randInd]) + + + preservedCalib <- cbind(epcStrip,rmse, + modellOut) + columNames <- c(parameterNames[paramLines],"rmse", "likelihood") + colnames(preservedCalib) <- columNames + write.csv(preservedCalib,"preservedCalib.csv") + p<-list() + preservedCalib <- preservedCalib[-1,] + dontInclude <-c((ncol(preservedCalib)-1),ncol(preservedCalib)) + for(i in seq_along(colnames(preservedCalib)[-dontInclude])){ + p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(size=0.9) + } - ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 3000) - write.csv(preservedCalib,"preservedCalib.csv") - return(preservedCalib[preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"]),]) + ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 3000) + + return(preservedCalib[preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"],na.rm = TRUE),]) } From a116873ac4fce084e867c792a7ad07fc07ec59ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Tue, 5 Mar 2019 13:46:48 +0100 Subject: [PATCH 15/88] Smaller dots in dotplot --- RBBGCMuso/R/calibration.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 61d1365..7237d41 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -149,7 +149,7 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, preservedCalib <- preservedCalib[-1,] dontInclude <-c((ncol(preservedCalib)-1),ncol(preservedCalib)) for(i in seq_along(colnames(preservedCalib)[-dontInclude])){ - p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(size=0.9) + p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(shape='.',size=1,alpha=0.8) } ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 3000) From 760bbaef9a61323711d6a8ec18a1c3c61cfc3bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Fri, 8 Mar 2019 14:07:01 +0100 Subject: [PATCH 16/88] fix the changeMuso order bug --- RBBGCMuso/R/changeMuso.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/changeMuso.R b/RBBGCMuso/R/changeMuso.R index 6e3e1f5..714e6da 100644 --- a/RBBGCMuso/R/changeMuso.R +++ b/RBBGCMuso/R/changeMuso.R @@ -27,7 +27,7 @@ changemulline <- function(filePaths, calibrationPar, contents, fileOut, fileToCh if(fileToChange == "epc" | fileToChange == "ini"){ parMat<-cbind(calibrationPar, contents) - parMat[order(parMat[,1]),] + parMat<- parMat[order(parMat[,1]),] changeMusoC(inFile = filePaths[selectFileToWrite(filePaths, fileToChange)], outFile = fileOut[selectFileToWrite(filePaths, fileToChange)], parMat) From 4b1c5fe5a3541b7ac782df2dfa796a87196475aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 11 Mar 2019 13:06:37 +0100 Subject: [PATCH 17/88] plot optimized muso --- RBBGCMuso/R/calibration.R | 63 +++++++++++++++++++++++---------------- RBBGCMuso/R/musoTime.R | 17 +++++++---- RBBGCMuso/R/plotMuso.R | 32 +++++++++++++++----- 3 files changed, 72 insertions(+), 40 deletions(-) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 7237d41..29ad708 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -25,7 +25,7 @@ #' @importFrom ggplot2 ggplot aes_string geom_point ggsave #' @importFrom magrittr '%>%' #' @importFrom gridExtra grid.arrange -#' @export +#' @export optiMuso <- function(measuredData, parameters = NULL, startDate, endDate, formatString = "%Y-%m-%d", leapYearHandling = TRUE, @@ -44,8 +44,9 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, modelVar = 3009, postProcString = NULL) { + mdata <- measuredData dataCol <- grep(dataVar, colnames(measuredData)) - + if(is.null(parameters)){ parameters <- tryCatch(read.csv("parameters.csv", stringsAsFactor=FALSE), error = function (e) { stop("You need to specify a path for the parameters.csv, or a matrix.") @@ -56,26 +57,26 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, stop("Cannot find neither parameters file neither the parameters matrix") }) }} - + outLoc <- normalizePath(outLoc) outLocPlain <- basename(outLoc) currDir <- getwd() - + if(!dir.exists(outLoc)){ dir.create(outLoc) warning(paste(outLoc," is not exists, so it was created")) } - + outLoc <- normalizePath(outLoc) - + if(is.null(settings)){ settings <- setupMuso() } - + parameterNames <- parameters[,1] pretag <- file.path(outLoc,preTag) npar <- length(settings$calibrationPar) - + ##reading the original epc file at the specified ## row numbers print("optiMuso is randomizing the epc parameters now...",quote = FALSE) @@ -85,17 +86,17 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, } else { randVals <- musoRand(parameters = parameters,constrains = constrains, iterations = iterations) } - + origEpc <- readValuesFromFile(settings$epc[2],parameters[,2]) - + ## Prepare the preservedCalib matrix for the faster ## run. - + pretag <- file.path(outLoc,preTag) - + ## Creating function for generating separate ## csv files for each run - + progBar <- txtProgressBar(1,iterations,style=3) colNumb <- which(settings$dailyVarCodes == modelVar) settings$iniInput[2] %>% @@ -104,12 +105,13 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, randValues <- randVals[[2]] settings$calibrationPar <- randVals[[1]] list2env(alignData(measuredData,dataCol = dataCol,modellSettings = settings,startDate = startDate,endDate = endDate,leapYear = leapYearHandling, continious = continious),envir=environment()) - + ## modIndex and measuredData are created. + modellOut <- numeric(iterations + 1) # single variable solution rmse <- numeric(iterations + 1) origModellOut <- calibMuso(settings=settings,silent=TRUE, skipSpinup = skipSpinup) - - + + write.csv(x=origModellOut, file=paste0(pretag,1,".csv")) modellOut[1] <- likelihood(measuredData,origModellOut[modIndex,colNumb]) print("Running the model with the random epc values...", quote = FALSE) @@ -117,13 +119,13 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, if(!is.null(postProcString)){ colNumb <- length(settings$dailyVarCodes) + 1 } - + for(i in 2:(iterations+1)){ tmp <- tryCatch(calibMuso(settings = settings, parameters = randValues[(i-1),], silent= TRUE, skipSpinup = skipSpinup, postProcString = postProcString)[modIndex,colNumb], error = function (e) NA) - + modellOut[i]<- likelihood(measuredData,tmp) rmse[i] <- sqrt(mean((measuredData-tmp)^2)) write.csv(x=tmp, file=paste0(pretag,(i+1),".csv")) @@ -133,13 +135,13 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, paramLines <- order(paramLines) randInd <- randVals[[1]][(randVals[[1]] %in% parameters[,2])] randInd <- order(randInd) - - + + epcStrip <- rbind(origEpc[order(parameters[,2])], randValues[,randVals[[1]] %in% parameters[,2]][,randInd]) - - + + preservedCalib <- cbind(epcStrip,rmse, modellOut) columNames <- c(parameterNames[paramLines],"rmse", "likelihood") @@ -149,12 +151,21 @@ optiMuso <- function(measuredData, parameters = NULL, startDate, preservedCalib <- preservedCalib[-1,] dontInclude <-c((ncol(preservedCalib)-1),ncol(preservedCalib)) for(i in seq_along(colnames(preservedCalib)[-dontInclude])){ - p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood"))+geom_point(shape='.',size=1,alpha=0.8) + p[[i]] <- ggplot(as.data.frame(preservedCalib),aes_string(colnames(preservedCalib)[i],"likelihood")) + + geom_point(shape='.',size=1,alpha=0.8) } - ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 3000) - - return(preservedCalib[preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"],na.rm = TRUE),]) + ggsave(plotName,grid.arrange(grobs = p, ncol = floor(sqrt(ncol(preservedCalib)-1))),dpi = 300) + maxLikelihoodPlace <- which(preservedCalib[,"likelihood"]==max(preservedCalib[,"likelihood"],na.rm = TRUE)) + resPlot <- plotMusoWithData(mdata = mdata, startDate = startDate, endDate = endDate, + dataVar = dataVar, modelVar = modelVar, settings = settings, continious = continious) + + plotMuso(settings = settings, parameters = randValues[maxLikelihoodPlace,], + postProcString = postProcString, skipSpinup = FALSE, variable = colNumb, layerPlot = TRUE, colour = "green") + + print(resPlot) + tempEpc <- paste0(tools::file_path_sans_ext(basename(settings$epcInput[2])),"-tmp.",tools::file_ext(settings$epcInput[2])) + file.rename(tempEpc, "optimizedEpc.epc") + return(preservedCalib[maxLikelihoodPlace,]) } diff --git a/RBBGCMuso/R/musoTime.R b/RBBGCMuso/R/musoTime.R index 92d5ce4..cf457d0 100644 --- a/RBBGCMuso/R/musoTime.R +++ b/RBBGCMuso/R/musoTime.R @@ -59,17 +59,22 @@ musoDate <- function(startYear, endYears = NULL, numYears, combined = TRUE, leap #' This function align the data to the model and the model to the data #' @importFrom lubridate leap_year #' @keywords internal -alignData <- function(mdata, dataCol, modellSettings = NULL, startDate=NULL, endDate=NULL, formatString = "%Y-%m-%d", leapYear = TRUE, continious = TRUE){ +alignData <- function(mdata, dataCol, modellSettings = NULL, startDate=NULL, endDate=NULL, formatString = "%Y-%m-%d", leapYear = TRUE, continious = FALSE){ + + if(continious){ + if((is.null(startDate) | is.null(endDate))){ + stop("If your date is continuous, you have to provide both startDate and endDate. ") + } + startDate <- as.Date(startDate, format = formatString) + endDate <- as.Date(endDate, format = formatString) + } - - startDate <- as.Date(startDate, format = formatString) - endDate <- as.Date(endDate, format = formatString) - mdata <- as.data.frame(mdata) - if(is.null(modellSettings)){ modellSettings <- setupMuso() } + mdata <- as.data.frame(mdata) + if(continious){ dates <- seq(startDate, to = endDate, by= "day") } else{ diff --git a/RBBGCMuso/R/plotMuso.R b/RBBGCMuso/R/plotMuso.R index 0ca7507..9d11588 100644 --- a/RBBGCMuso/R/plotMuso.R +++ b/RBBGCMuso/R/plotMuso.R @@ -251,8 +251,12 @@ plotMuso <- function(settings = NULL, variable = 1, #' @importFrom ggplot2 ggplot geom_line geom_point aes aes_string labs theme element_blank #' @export plotMusoWithData <- function(mdata, plotName=NULL, - startDate, endDate, - colour=c("black","blue"),dataVar, modelVar, settings = setupMuso(), silent = TRUE, continious = TRUE){ + startDate = NULL, endDate = NULL, + colour=c("black","blue"), dataVar, modelVar, settings = setupMuso(), silent = TRUE, continious = FALSE){ + + if(continious & (is.null(startDate) | is.null(endDate))){ + stop("If your date is continuous, you have to provide both startDate and endDate. ") + } dataCol<- grep(paste0("^",dataVar,"$"), colnames(mdata)) selVar <- grep(modelVar,(settings$dailyVarCodes))+4 @@ -261,24 +265,36 @@ plotMusoWithData <- function(mdata, plotName=NULL, modellSettings = settings, startDate = startDate, endDate = endDate, leapYear = FALSE, continious = continious),envir=environment()) - - + mesData <- numeric(settings$numYears*365) + k <- 1 + for(i in seq(mesData)){ + if(i %in% modIndex){ + mesData[i] <- measuredData[k] + k <- k + 1 + } else { + mesData[i] <- NA + } + } + rm(k) + # modIndex and measuredData are created. ## measuredData is created - baseData <- calibMuso(settings = settings, silent = silent, prettyOut = TRUE)[modIndex,] + ## baseData <- calibMuso(settings = settings, silent = silent, prettyOut = TRUE)[modIndex,] + baseData <- calibMuso(settings = settings, silent = silent, prettyOut = TRUE) baseData[,1] <- as.Date(baseData[,1],format = "%d.%m.%Y") selVarName <- colnames(baseData)[selVar] if(!all.equal(colnames(baseData),unique(colnames(baseData)))){ notUnique <- setdiff((unlist(settings$dailyVarCodes)),unique(unlist(settings$dailyVarCodes))) stop(paste0("Error: daily output variable list in the ini file must contain unique numbers. Check your ini files! Not unique codes: ",notUnique)) } - + mesData<-cbind.data.frame(baseData[,1],mesData) + colnames(mesData) <- c("date", "measured") p <- baseData %>% ggplot(aes_string("date",selVarName)) + geom_line(colour=colour[1]) + - geom_point(colour=colour[2], aes(date,measuredData)) + + geom_point(data = mesData, colour=colour[2], aes(date,measured)) + labs(y = paste0(selVarName,"_measured"))+ theme(axis.title.x = element_blank()) - if(!is.null(plotName)){ + if(!is.null(plotName)){ ggsave(plotName,p) return(p) } else { From e125c1dba5b6ee2cb96960378c9bf57e8b9e25c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Wed, 13 Mar 2019 14:53:58 +0100 Subject: [PATCH 18/88] startDate and endDate --- RBBGCMuso/R/calibration.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RBBGCMuso/R/calibration.R b/RBBGCMuso/R/calibration.R index 29ad708..245a39b 100644 --- a/RBBGCMuso/R/calibration.R +++ b/RBBGCMuso/R/calibration.R @@ -26,8 +26,8 @@ #' @importFrom magrittr '%>%' #' @importFrom gridExtra grid.arrange #' @export -optiMuso <- function(measuredData, parameters = NULL, startDate, - endDate, formatString = "%Y-%m-%d", +optiMuso <- function(measuredData, parameters = NULL, startDate = NULL, + endDate = NULL, formatString = "%Y-%m-%d", leapYearHandling = TRUE, dataVar, outLoc = "./calib", preTag = "cal-", From 2471b7138c1e2744d1271cba4d745079ec6859ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Thu, 14 Mar 2019 12:54:15 +0100 Subject: [PATCH 19/88] correct the leapYearHandling bug --- RBBGCMuso/R/musoTime.R | 12 ++++++------ RBBGCMuso/R/plotMuso.R | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/RBBGCMuso/R/musoTime.R b/RBBGCMuso/R/musoTime.R index cf457d0..28cd2a9 100644 --- a/RBBGCMuso/R/musoTime.R +++ b/RBBGCMuso/R/musoTime.R @@ -81,11 +81,11 @@ alignData <- function(mdata, dataCol, modellSettings = NULL, startDate=NULL, en dates <- do.call(c,lapply(seq(nrow(mdata)), function(i){ as.Date(paste0(mdata[i,1],sprintf("%02d",mdata[i,2]),mdata[i,3]),format = "%Y%m%d")})) } - if(!leapYear){ - casualDays <- which(format(dates,"%m%d") != "0229") - #mdata <- mdata[casualDays,] - dates <- dates[casualDays] - } + ## if(!leapYear){ + ## casualDays <- which(format(dates,"%m%d") != "0229") + ## #mdata <- mdata[casualDays,] + ## dates <- dates[casualDays] + ## } mdata <- mdata[dates >= as.Date(paste0(modellSettings$startYear,"01","01"),format = "%Y%m%d"),] dates <- dates[dates >= as.Date(paste0(modellSettings$startYear,"01","01"),format = "%Y%m%d")] @@ -102,7 +102,7 @@ alignData <- function(mdata, dataCol, modellSettings = NULL, startDate=NULL, en } realDate <- dates[which(format(dates,"%m%d") != "0229")] if(leapYear){ - mdata <- cbind.data.frame(realDate,mdata) + mdata <- cbind.data.frame(realDate,mdata[goodInd,]) } else { mdata <- cbind.data.frame(dates,mdata) } diff --git a/RBBGCMuso/R/plotMuso.R b/RBBGCMuso/R/plotMuso.R index 9d11588..85090ae 100644 --- a/RBBGCMuso/R/plotMuso.R +++ b/RBBGCMuso/R/plotMuso.R @@ -252,7 +252,7 @@ plotMuso <- function(settings = NULL, variable = 1, #' @export plotMusoWithData <- function(mdata, plotName=NULL, startDate = NULL, endDate = NULL, - colour=c("black","blue"), dataVar, modelVar, settings = setupMuso(), silent = TRUE, continious = FALSE){ + colour=c("black","blue"), dataVar, modelVar, settings = setupMuso(), silent = TRUE, continious = FALSE, leapYearHandling = FALSE){ if(continious & (is.null(startDate) | is.null(endDate))){ stop("If your date is continuous, you have to provide both startDate and endDate. ") @@ -264,7 +264,7 @@ plotMusoWithData <- function(mdata, plotName=NULL, list2env(alignData(mdata, dataCol = dataCol, modellSettings = settings, startDate = startDate, - endDate = endDate, leapYear = FALSE, continious = continious),envir=environment()) + endDate = endDate, leapYear = leapYearHandling, continious = continious),envir=environment()) mesData <- numeric(settings$numYears*365) k <- 1 for(i in seq(mesData)){ From ae29113f667f363baa945dae159bb8dbfc918cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 15 Apr 2019 11:42:59 +0200 Subject: [PATCH 20/88] adding visual dependency installer --- installDeps.R | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 installDeps.R diff --git a/installDeps.R b/installDeps.R new file mode 100644 index 0000000..acdaae6 --- /dev/null +++ b/installDeps.R @@ -0,0 +1,8 @@ +(function(){ + packagesToInstall <- c("shiny","shinyjs","plotly","promises","future","data.table","rhandsontable") + sapply(packagesToInstall, function(pkgs){ + if(!is.element(pkgs,installed.packages()[,1])){ + install.packages(pkgs) + } + }) +})() From 2c9b268402d769382845a5edd48df814129c6091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 15 Apr 2019 11:57:55 +0200 Subject: [PATCH 21/88] Modify the installation scripts! --- RBBGCMuso/R/setupMuso6.R | 47 ++++++++++++++++++++++++++++++++++++++++ installDeps.R | 12 +++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 RBBGCMuso/R/setupMuso6.R diff --git a/RBBGCMuso/R/setupMuso6.R b/RBBGCMuso/R/setupMuso6.R new file mode 100644 index 0000000..d6f465b --- /dev/null +++ b/RBBGCMuso/R/setupMuso6.R @@ -0,0 +1,47 @@ +## #' setupMuso6 +## #' +## #' This is the setup function for MuSo version: 6 +## #' +## #' @author Roland HOLLOS +## #' @param setupFile +## #' @export + +## setupMuso6<- function(setupFile){ + +## } + +## ini <- readLines("./hhs_apriori_MuSo6_normal.ini") +## flags <- c("MET_INPUT", +## "RESTART", +## "TIME_DEFINE", +## "CO2_CONTROL", +## "NDEP_CONTROL", +## "SITE", +## "SOILPROP_FILE", +## "EPC_FILE", +## "MANAGEMENT_FILE", +## "SIMULATION_CONTROL", +## "W_STATE", +## "CN_STATE", +## "CLIM_CHANGE", +## "CONDITIONAL_MANAGEMENT_STRATEGIES", +## "OUTPUT_CONTROL", +## "DAILY_OUTPUT", +## "ANNUAL_OUTPUT", +## "END_INIT") +## getSegments <- function(ini, flags){ +## output <- list() +## flagIterator <- 1:(length(flags)-1) +## for(i in flagIterator){ +## output[[flags[i]]] <- lapply(ini[(grep(flags[i],ini)+1):(grep(flags[i+1],ini)-2)], function(x){ +## unlist(strsplit(x,split = "\\["))[1] +## }) +## } +## output +## } +## getSegments(ini,flags) + +## gsub("(.*\\[\\|)([a-zA-Z1-9_]*)","",ini) +## stringi::stri_trim_right("rexamine.com/", "\\[r\\]") +## stri_extract("asdfasdf [|Ezat|]",regex = "\\[\\|*\\]") +## lapply(ini,function(x) gsub("\\s","",(strsplit(x,split= "T]"))[[1]][2])) diff --git a/installDeps.R b/installDeps.R index acdaae6..3f46dcd 100644 --- a/installDeps.R +++ b/installDeps.R @@ -1,8 +1,18 @@ (function(){ packagesToInstall <- c("shiny","shinyjs","plotly","promises","future","data.table","rhandsontable") - sapply(packagesToInstall, function(pkgs){ + installedp<- sapply(packagesToInstall, function(pkgs){ if(!is.element(pkgs,installed.packages()[,1])){ install.packages(pkgs) + if(!is.element(pkgs,installed.packages([,1]))){ + return(FALSE) + } else { + return(TRUE) + } + } else { + return(TRUE) } }) + if(any(!installedp)){ + stop("The installation process was not successful. Please try rerun the installation!") + } })() From c613710a0cdd48aef02836b71fff3fddd3618000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 15 Apr 2019 12:24:29 +0200 Subject: [PATCH 22/88] some minor bugfix in installscript --- installDeps.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installDeps.R b/installDeps.R index 3f46dcd..e636bba 100644 --- a/installDeps.R +++ b/installDeps.R @@ -3,7 +3,7 @@ installedp<- sapply(packagesToInstall, function(pkgs){ if(!is.element(pkgs,installed.packages()[,1])){ install.packages(pkgs) - if(!is.element(pkgs,installed.packages([,1]))){ + if(!is.element(pkgs,installed.packages())[,1]){ return(FALSE) } else { return(TRUE) From a6f7b9210bb63afc1b1e4baafa85de3b256f1b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 15 Apr 2019 12:28:44 +0200 Subject: [PATCH 23/88] bugfix in installScript --- installDeps.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installDeps.R b/installDeps.R index e636bba..b96c2b0 100644 --- a/installDeps.R +++ b/installDeps.R @@ -3,7 +3,7 @@ installedp<- sapply(packagesToInstall, function(pkgs){ if(!is.element(pkgs,installed.packages()[,1])){ install.packages(pkgs) - if(!is.element(pkgs,installed.packages())[,1]){ + if(!is.element(pkgs,installed.packages()[,1])){ return(FALSE) } else { return(TRUE) From 8d99710fa3997a8237a0aaeb62849cf73dec4b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Mon, 15 Apr 2019 12:51:45 +0200 Subject: [PATCH 24/88] adding more packages to installation script --- installDeps.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installDeps.R b/installDeps.R index b96c2b0..7198aec 100644 --- a/installDeps.R +++ b/installDeps.R @@ -1,5 +1,5 @@ (function(){ - packagesToInstall <- c("shiny","shinyjs","plotly","promises","future","data.table","rhandsontable") + packagesToInstall <- c("shiny","shinyjs","plotly","promises","future","data.table","rhandsontable", "dplyr", "digest", "ggplot2", "magrittr", "tibble", "limSolve", "rmarkdown") installedp<- sapply(packagesToInstall, function(pkgs){ if(!is.element(pkgs,installed.packages()[,1])){ install.packages(pkgs) From ccd2230ec026c7e7c32f35c70a23a9bd86088ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20BARCZA?= Date: Mon, 1 Mar 2021 13:11:37 +0100 Subject: [PATCH 25/88] Update epcConstMatrix6.json added dependence on extreme high temperature effect on senescence mortality --- RBBGCMuso/inst/data/epcConstMatrix6.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/RBBGCMuso/inst/data/epcConstMatrix6.json b/RBBGCMuso/inst/data/epcConstMatrix6.json index 00d421b..9a4974b 100644 --- a/RBBGCMuso/inst/data/epcConstMatrix6.json +++ b/RBBGCMuso/inst/data/epcConstMatrix6.json @@ -865,8 +865,9 @@ "UNIT": "prop", "MIN": 0, "MAX": 0.1, - "GROUP": 0, - "TYPE": 0 + "DEPENDENCE": 0, + "GROUP": 22, + "TYPE": 1 }, { "X": 85, @@ -875,8 +876,9 @@ "UNIT": "prop", "MIN": 0, "MAX": 0.1, - "GROUP": 0, - "TYPE": 0 + "DEPENDENCE": 0, + "GROUP": 22, + "TYPE": 1 }, { "X": 86, From e3d447c5b1ac969c068b46cf80f0d1c0cae0594a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Holl=C3=B3s?= Date: Tue, 2 Mar 2021 11:22:40 +0100 Subject: [PATCH 26/88] change dependence value for group 22 --- RBBGCMuso/inst/data/epcConstMatrix6.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/inst/data/epcConstMatrix6.json b/RBBGCMuso/inst/data/epcConstMatrix6.json index 9a4974b..adf2eff 100644 --- a/RBBGCMuso/inst/data/epcConstMatrix6.json +++ b/RBBGCMuso/inst/data/epcConstMatrix6.json @@ -876,7 +876,7 @@ "UNIT": "prop", "MIN": 0, "MAX": 0.1, - "DEPENDENCE": 0, + "DEPENDENCE": 1, "GROUP": 22, "TYPE": 1 }, From c178e76f2addee8f77bf11ce2debdee90d4a880b Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Thu, 18 Mar 2021 21:06:27 +0100 Subject: [PATCH 27/88] spatial calibration --- RBBGCMuso/NAMESPACE | 5 + RBBGCMuso/R/atStart.R | 6 +- RBBGCMuso/R/flat.R | 268 +++++++++ RBBGCMuso/R/multiSite.R | 509 ++++++++++++++++++ RBBGCMuso/inst/data/depTree.csv | 18 + RBBGCMuso/inst/data/epcConstMatrix6.json | 8 +- RBBGCMuso/man/calibrateMuso.Rd | 3 +- RBBGCMuso/man/checkFileSystem.Rd | 16 + .../man/compareCalibratedWithOriginal.Rd | 21 + RBBGCMuso/man/flatMuso.Rd | 25 + RBBGCMuso/man/getFilePath.Rd | 23 + RBBGCMuso/man/getFilesFromIni.Rd | 20 + RBBGCMuso/man/multiSiteCalib.Rd | 26 + RBBGCMuso/man/saveAllMusoPlots.Rd | 2 +- RBBGCMuso/man/updateMusoMapping.Rd | 2 +- 15 files changed, 944 insertions(+), 8 deletions(-) create mode 100644 RBBGCMuso/R/flat.R create mode 100644 RBBGCMuso/R/multiSite.R create mode 100644 RBBGCMuso/inst/data/depTree.csv create mode 100644 RBBGCMuso/man/checkFileSystem.Rd create mode 100644 RBBGCMuso/man/compareCalibratedWithOriginal.Rd create mode 100644 RBBGCMuso/man/flatMuso.Rd create mode 100644 RBBGCMuso/man/getFilePath.Rd create mode 100644 RBBGCMuso/man/getFilesFromIni.Rd create mode 100644 RBBGCMuso/man/multiSiteCalib.Rd diff --git a/RBBGCMuso/NAMESPACE b/RBBGCMuso/NAMESPACE index 19adfe4..210bb71 100644 --- a/RBBGCMuso/NAMESPACE +++ b/RBBGCMuso/NAMESPACE @@ -3,17 +3,22 @@ export(calibMuso) export(calibrateMuso) export(changemulline) +export(checkFileSystem) export(checkMeteoBGC) export(cleanupMuso) export(compareMuso) export(copyMusoExampleTo) export(corrigMuso) export(createSoilFile) +export(flatMuso) export(getAnnualOutputList) export(getConstMatrix) export(getDailyOutputList) +export(getFilePath) +export(getFilesFromIni) export(getyearlycum) export(getyearlymax) +export(multiSiteCalib) export(musoDate) export(musoGlue) export(musoMapping) diff --git a/RBBGCMuso/R/atStart.R b/RBBGCMuso/R/atStart.R index e5a7805..4c35a70 100644 --- a/RBBGCMuso/R/atStart.R +++ b/RBBGCMuso/R/atStart.R @@ -23,9 +23,13 @@ RMuso_varTable[[version]] <<- varTable }) + RMuso_depTree<- read.csv(file.path(system.file("data",package="RBBGCMuso"),"depTree.csv"), stringsAsFactors=FALSE) + options(RMuso_version=RMuso_version, RMuso_constMatrix=RMuso_constMatrix, - RMuso_varTable=RMuso_varTable) + RMuso_varTable=RMuso_varTable, + RMuso_depTree=RMuso_depTree + ) # getOption("RMuso_constMatrix")$soil[[as.character(getOption("RMuso_version"))]] } diff --git a/RBBGCMuso/R/flat.R b/RBBGCMuso/R/flat.R new file mode 100644 index 0000000..fc70986 --- /dev/null +++ b/RBBGCMuso/R/flat.R @@ -0,0 +1,268 @@ +options(RMuso_depTree = read.csv("~/projects/RBBGCMuso/RBBGCMuso/inst/data/depTree.csv", stringsAsFactors = FALSE)) +getQueue <- function(depTree=options("RMuso_depTree")[[1]], startPoint){ + + if(length(startPoint) == 0){ + return(c()) + } + parent <- depTree[depTree[,"name"] == startPoint,"parent"] + c(getQueue(depTree, depTree[depTree[,"child"] == depTree[depTree[,"name"] == startPoint,"parent"],"name"]),parent) +} + +isRelative <- function(path){ + substr(path,1,1) != '/' +} + +#' getFilePath +#' +#' This function reads the ini file and for a chosen fileType it gives you the filePath +#' @param iniName The name of the ini file +#' @param filetype The type of the choosen file. For options see options("RMuso_depTree")[[1]]$name +#' @param depTree The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]] +#' @export + +getFilePath <- function(iniName, fileType, execPath = "./", depTree=options("RMuso_depTree")[[1]]){ + if(!file.exists(iniName) || dir.exists(iniName)){ + stop(sprintf("Cannot find iniFile: %s", iniName)) + } + + startPoint <- fileType + startRow <- depTree[depTree[,"name"] == startPoint,] + startExt <- startRow$child + + parentFile <- Reduce(function(x,y){ + tryCatch(file.path(execPath,gsub(sprintf("\\.%s.*",y), + sprintf("\\.%s",y), + grep(sprintf("\\.%s",y),readLines(x),value=TRUE,perl=TRUE))), error = function(e){ + stop(sprintf("Cannot find %s",x)) + }) + }, + getQueue(depTree,startPoint)[-1], + init=iniName) + if(startRow$mod > 0){ + tryCatch( + gsub(sprintf("\\.%s.*", startExt), + sprintf("\\.%s", startExt), + grep(sprintf("\\.%s",startExt),readLines(parentFile),value=TRUE,perl=TRUE))[startRow$mod] + ,error = function(e){stop(sprintf("Cannot read %s",parentFile))}) + } else { + res <- tryCatch( + gsub(sprintf("\\.%s.*", startExt), + sprintf("\\.%s",startExt), + grep(sprintf("\\.%s",startExt),readLines(parentFile),value=TRUE, perl=TRUE)) + ,error = function(e){stop(sprintf("Cannot read %s", parentFile))}) + unique(gsub(".*\\t","",res)) + } +} + + + +#' getFilesFromIni +#' +#' This function reads the ini file and gives yout back the path of all file involved in model run +#' @param iniName The name of the ini file +#' @param depTree The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]] +#' @export + +getFilesFromIni <- function(iniName, execPath = "./", depTree=options("RMuso_depTree")[[1]]){ + res <- lapply(depTree$name,function(x){ + tryCatch(getFilePath(iniName,x,execPath,depTree), error = function(e){ + return(NA); + }) + }) + names(res) <- depTree$name + res +} + +#' flatMuso +#' +#' This function reads the ini file and creates a directory (named after the directory argument) with all the files the modell uses with this file. the directory will be flat. +#' @param iniName The name of the ini file +#' @param depTree The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]] +#' @param directory The destination directory for flattening. At default it will be flatdir +#' @export + +flatMuso <- function(iniName, execPath="./", depTree=options("RMuso_depTree")[[1]], directory="flatdir", d=TRUE,outE=TRUE){ + dir.create(directory, showWarnings=FALSE, recursive = TRUE) + files <- getFilesFromIni(iniName,execPath,depTree) + files <- sapply(unlist(files)[!is.na(files)], function(x){ifelse(isRelative(x),file.path(execPath,x),x)}) + file.copy(unlist(files), directory, overwrite=TRUE) + file.copy(iniName, directory, overwrite=TRUE) + + filesByName <- getFilesFromIni(iniName, execPath, depTree) + for(i in seq_along(filesByName)){ + fileLines <- readLines(file.path(directory,list.files(directory, pattern = sprintf("*\\.%s", depTree$parent[i])))[1]) + + sapply(filesByName[[i]],function(origname){ + if(!is.na(origname)){ + fileLines <<- gsub(origname, basename(origname), fileLines, fixed=TRUE) + } + }) + + if(!is.na(filesByName[[i]][1])){ + writeLines(fileLines, file.path(directory,list.files(directory, pattern = sprintf("*\\.%s", depTree$parent[i])))[1]) + } + + } + + iniLines <- readLines(file.path(directory, basename(iniName))) + outPlace <- grep("OUTPUT_CONTROL", iniLines, perl=TRUE)+1 + if(outE){ + iniLines[outPlace] <- tools::file_path_sans_ext(basename(iniName)) + } else { + iniLines[outPlace] <- basename(strsplit(iniLines[outPlace], split = "\\s+")[[1]][1]) + } + if(d){ + iniLines[outPlace + 1] <- 1 + } + writeLines(iniLines, file.path(directory, basename(iniName))) +} + +#' checkFileSystem +#' +#' This function checks the MuSo file system, if it is correct +#' @param iniName The name of the ini file +#' @param depTree The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]] +#' @export + +checkFileSystem <- function(iniName,root = ".", depTree = options("RMuso_depTree")[[1]]){ + recoverAfterEval({ + setwd(root) + fileNames <- getFilesFromIni(iniName, depTree) + if(is.na(fileNames$management)){ + fileNames[getLeafs("management")] <- NA + } + fileNames <- fileNames[!is.na(fileNames)] + errorFiles <- fileNames[!file.exists(unlist(fileNames))] + }) + return(errorFiles) +} + +recoverAfterEval <- function(expr){ + wd <- getwd() + tryCatch({ + eval(expr) + setwd(wd) + }, error=function(e){ + setwd(wd) + stop(e) + }) +} + +getLeafs <- function(name, depTree=options("RMuso_depTree")[[1]]){ + + if(length(name) == 0){ + return(NULL) + } + + if(name[1] == "ini"){ + return(getLeafs(depTree$name)) + } + + pname <- depTree[ depTree[,"name"] == name[1] , "child"] + children <- depTree[depTree[,"parent"] == pname,"child"] + + if(length(children)==0){ + if(length(name) == 1){ + return(NULL) + } else{ + apname <- depTree[ depTree[,"name"] == name[2] , "child"] + achildren <- depTree[depTree[,"parent"] == apname,"child"] + if(length(achildren)!=0){ + return(c(name[1],name[2],getLeafs(name[-1]))) + } else{ + return(c(name[1], getLeafs(name[-1]))) + } + + } + } + + childrenLogic <-depTree[,"child"] %in% children + parentLogic <- depTree[,"parent"] ==pname + res <- depTree[childrenLogic & parentLogic, "name"] + getChildelem <- depTree[depTree[,"child"] == intersect(depTree[,"parent"], children), "name"] + unique(c(res,getLeafs(getChildelem))) +} + +getParent <- function (name, depTree=options("RMuso_depTree")[[1]]) { + parentExt <- depTree[depTree$name == name,"parent"] + # if(length(parentExt) == 0){ + # browser() + # } + if(parentExt == "ini"){ + return("iniFile") + } + + depTree[depTree[,"child"] == parentExt,"name"] +} + + + +getFilePath2 <- function(iniName, fileType, depTree=options("RMuso_depTree")[[1]]){ + if(!file.exists(iniName) || dir.exists(iniName)){ + stop(sprintf("Cannot find iniFile: %s", iniName)) + } + + startPoint <- fileType + startRow <- depTree[depTree[,"name"] == startPoint,] + startExt <- startRow$child + + parentFile <- Reduce(function(x,y){ + tryCatch(gsub(sprintf("\\.%s.*",y), + sprintf("\\.%s",y), + grep(sprintf("\\.%s",y),readLines(x),value=TRUE,perl=TRUE)), error = function(e){ + stop(sprintf("Cannot find %s",x)) + }) + }, + getQueue(depTree,startPoint)[-1], + init=iniName) + res <- list() + res["parent"] <- parentFile + if(startRow$mod > 0){ + res["children"] <- tryCatch( + gsub(sprintf("\\.%s.*", startExt), + sprintf("\\.%s", startExt), + grep(sprintf("\\.%s",startExt),readLines(parentFile),value=TRUE,perl=TRUE))[startRow$mod] + ,error = function(e){stop(sprintf("Cannot read %s",parentFile))}) + + } else { + rows <- tryCatch( + gsub(sprintf("\\.%s.*", startExt), + sprintf("\\.%s",startExt), + grep(sprintf("\\.%s",startExt),readLines(parentFile),value=TRUE, perl=TRUE)) + + ,error = function(e){stop(sprintf("Cannot read %s", parentFile))}) + unique(gsub(".*\\t","",res)) + res["children"] <- unique(gsub(".*\\s+(.*\\.epc)","\\1",rows)) + } + res +} + +getFilesFromIni2 <- function(iniName, depTree=options("RMuso_depTree")[[1]]){ + res <- lapply(depTree$name,function(x){ + tryCatch(getFilePath2(iniName,x,depTree), error = function(e){ + return(NA); + }) + }) + names(res) <- depTree$name + res +} + +checkFileSystemForNotif <- function(iniName,root = ".", depTree = options("RMuso_depTree")[[1]]){ + recoverAfterEval({ + setwd(root) + fileNames <- suppressWarnings(getFilesFromIni2(iniName, depTree)) + if(is.atomic(fileNames$management)){ + fileNames[getLeafs("management")] <- NA + } + + hasparent <- sapply(fileNames, function(x){ + !is.atomic(x) + }) + notNA <- ! sapply(fileNames[hasparent], function(x) {is.na(x$children)}) + errorIndex <- ! sapply(fileNames[hasparent & notNA], function(x) file.exists(x$children)) + + }) + return(fileNames[hasparent & notNA][errorIndex]) +} + + diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R new file mode 100644 index 0000000..ed36220 --- /dev/null +++ b/RBBGCMuso/R/multiSite.R @@ -0,0 +1,509 @@ + + +copyToThreadDirs2 <- function(iniSource, thread_prefix = "thread", numCores, execPath="./", + + executable = ifelse(Sys.info()[1]=="Linux", file.path(execPath, "muso"), + file.path(execPath,"muso.exe"))){ + sapply(iniSource, function(x){ + flatMuso(x, execPath, + directory=file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)),""), d =TRUE) + file.copy(executable, + file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))) + }) + + sapply(2:numCores,function(thread){ + dir.create(sprintf("tmp/%s_%s",thread_prefix,thread), showWarnings=FALSE) + file.copy(list.files(sprintf("tmp/%s_1",thread_prefix),full.names = TRUE),sprintf("tmp/%s_%s/",thread_prefix,thread), + recursive=TRUE, overwrite = TRUE) + }) + +} + + +#' multiSiteCalib +#' +#' This funtion uses the Monte Carlo technique to uniformly sample the parameter space from user defined parameters of the Biome-BGCMuSo model. The sampling algorithm ensures that the parameters are constrained by the model logic which means that parameter dependencies are fully taken into account (parameter dependency means that e.g leaf C:N ratio must be smaller than C:N ratio of litter; more complicated rules apply to the allocation parameters where the allocation fractions to different plant compartments must sum up 1). This function implements a mathematically correct solution to provide uniform distriution for all selected parameters. +#' @author Roland HOLLOS +#' @importFrom future future +#' @export +multiSiteCalib <- function(measurements, + calTable, + parameters, + dataVar, + iterations = 100, + likelihood, + execPath, + thread_prefix="thread", + numCores = (parallel::detectCores()-1), + pb = txtProgressBar(min=0, max=iterations, style=3), + pbUpdate = setTxtProgressBar){ + + future::plan(future::multisession) + # file.remove(list.files(path = "tmp", pattern="progress.txt", recursive = TRUE, full.names=TRUE)) + # file.remove(list.files(path = "tmp", pattern="preservedCalib.csv", recursive = TRUE, full.names=TRUE)) + unlink("tmp",recursive=TRUE) + + # ____ _ _ _ _ + # / ___|_ __ ___ __ _| |_ ___ | |_| |__ _ __ ___ __ _ __| |___ + # | | | '__/ _ \/ _` | __/ _ \ | __| '_ \| '__/ _ \/ _` |/ _` / __| + # | |___| | | __/ (_| | || __/ | |_| | | | | | __/ (_| | (_| \__ \ + # \____|_| \___|\__,_|\__\___| \__|_| |_|_| \___|\__,_|\__,_|___/ + + + copyToThreadDirs2(iniSource=calTable$site_id, numCores=numCores, execPath=execPath) + + # ____ _ _ _ + # | _ \ _ _ _ __ | |_| |__ _ __ ___ __ _ __| |___ + # | |_) | | | | '_ \ | __| '_ \| '__/ _ \/ _` |/ _` / __| + # | _ <| |_| | | | | | |_| | | | | | __/ (_| | (_| \__ \ + # |_| \_\\__,_|_| |_| \__|_| |_|_| \___|\__,_|\__,_|___/ + + threadCount <- distributeCores(iterations, numCores) + fut <- lapply(1:numCores, function(i) { + # browser() + future({ + tryCatch( + + multiSiteThread(measuredData = measurements, parameters = parameters, calTable=calTable, + dataVar = dataVar, iterations = threadCount[i], + likelihood = likelihood, threadNumber= i) + , error = function(e){ + writeLines(as.character(iterations),"progress.txt") + }) + }) + }) + + # _ _ + # __ ____ _| |_ ___| |__ _ __ _ __ ___ __ _ _ __ ___ ___ ___ + # \ \ /\ / / _` | __/ __| '_ \ | '_ \| '__/ _ \ / _` | '__/ _ \/ __/ __| + # \ V V / (_| | || (__| | | | | |_) | | | (_) | (_| | | | __/\__ \__ \ + # \_/\_/ \__,_|\__\___|_| |_| | .__/|_| \___/ \__, |_| \___||___/___/ + # |_| |___/ + + getProgress <- function(){ + # threadfiles <- list.files(settings$inputLoc, pattern="progress.txt", recursive = TRUE) + threadfiles <- list.files(pattern="progress.txt", recursive = TRUE) + if(length(threadfiles)==0){ + return(0) + } else { + sum(sapply(threadfiles, function(x){ + partRes <- readLines(x) + if(length(partRes)==0){ + return(0) + } else { + return(as.numeric(partRes)) + } + + })) + + } + } + + progress <- 0 + while(progress < iterations){ + Sys.sleep(1) + progress <- tryCatch(getProgress(), error=function(e){progress}) + if(is.null(pb)){ + pbUpdate(as.numeric(progress)) + } else { + pbUpdate(pb,as.numeric(progress)) + } + } + if(!is.null(pb)){ + close(pb) + } + + # ____ _ _ + # / ___|___ _ __ ___ | |__ (_)_ __ ___ + # | | / _ \| '_ ` _ \| '_ \| | '_ \ / _ \ + # | |__| (_) | | | | | | |_) | | | | | __/ + # \____\___/|_| |_| |_|_.__/|_|_| |_|\___| + + resultFiles <- list.files(pattern="preservedCalib.*csv$",recursive=TRUE) + res0 <- read.csv(grep("thread_1/",resultFiles, value=TRUE),stringsAsFactors=FALSE) + resultFilesSans0 <- grep("thread_1/", resultFiles, value=TRUE, invert=TRUE) + # results <- do.call(rbind,lapply(resultFilesSans0, function(f){read.csv(f, stringsAsFactors=FALSE)})) + resultsSans0 <- lapply(resultFilesSans0, function(f){read.csv(f, stringsAsFactors=FALSE, header=FALSE)}) + resultsSans0 <- do.call(rbind,resultsSans0) + colnames(resultsSans0) <- colnames(res0) + results <- (rbind(res0,resultsSans0)) + write.csv(results,"result.csv") + calibrationPar <- future::value(fut[[1]], stdout = FALSE, signal=FALSE)[["calibrationPar"]] + origModOut <- future::value(fut[[1]], stdout = FALSE, signal=FALSE)[["origModOut"]] + # Just single objective version TODO:Multiobjective + bestCase <- which.max(results[,ncol(results)-1]) + parameters <- results[bestCase,1:(ncol(results)-2)] # the last two column is the (log) likelihood and the rmse + #TODO: Have to put that before multiSiteThread, we should not have to calculate it at every iterations + + firstDir <- list.dirs("tmp/thread_1",full.names=TRUE,recursive =FALSE)[1] + epcFile <- list.files(firstDir, pattern = "\\.epc",full.names=TRUE) + settingsProto <- setupMuso(inputLoc = firstDir, + iniInput =rep(list.files(firstDir, pattern = "\\.ini",full.names=TRUE),2)) + alignIndexes <- commonIndexes(settingsProto, measurements) + musoCodeToIndex <- sapply(dataVar,function(musoCode){ + settingsProto$dailyOutputTable[settingsProto$dailyOutputTable$code == musoCode,"index"] + }) + setwd("tmp/thread_1") + aposteriori<- spatialRun(settingsProto, calibrationPar, parameters, calTable) + file.copy(list.files(list.dirs(full.names=TRUE, recursive=FALSE)[1],pattern=".*\\.epc", full.names=TRUE), + "../../multiSiteOptim.epc", overwrite=TRUE) + setwd("../../") + #TODO: Have to put that before multiSiteThread, we should not have to calculate it at every iterations + nameGroupTable <- calTable + nameGroupTable[,1] <- tools::file_path_sans_ext(basename(nameGroupTable[,1])) + res <- list() + res[["calibrationPar"]] <- calibrationPar + res[["parameters"]] <- parameters + res[["comparison"]] <- compareCalibratedWithOriginal(key="grainDM", modOld=origModOut, modNew=aposteriori, mes=measurements, + likelihoods=likelihood, + alignIndexes=alignIndexes, + musoCodeToIndex = musoCodeToIndex, + nameGroupTable = nameGroupTable, mean) + res[["likelihood"]] <- results[bestCase,ncol(results)-1] + comp <- res$comparison + res[["originalMAE"]] <-mean(abs((comp[,1]-comp[,3]))) + res[["MAE"]] <- mean(abs((comp[,2]-comp[,3]))) + res[["RMSE"]] <- results[bestCase,ncol(results)] + res[["originalRMSE"]] <- sqrt(mean((comp[,1]-comp[,3])^2)) + png("calibRes.png") + opar <- par(mar=c(5,5,4,2)+0.1, xpd=FALSE) + with(data=res$comparison, { + plot(measured,original, + ylim=c(min(c(measured,original,calibrated)), + max(c(measured,original,calibrated))), + xlim=c(min(c(measured,original,calibrated)), + max(c(measured,original,calibrated))), + xlab=expression("measured "~(kg[C]~m^-2)), + ylab=expression("simulated "~(kg[C]~m^-2)), + cex.lab=1.3, + pty="s" + ) + points(measured,calibrated,pch=20) + abline(0,1) + legend(x="top", + pch=c(1,19), + inset=c(0,-0.1), + legend=c("original","calibrated"), + ncol=2, + box.lty=0, + xpd=TRUE + ) + }) + dev.off() + return(res) +} + +multiSiteThread <- function(measuredData, parameters = NULL, startDate = NULL, + endDate = NULL, formatString = "%Y-%m-%d", calTable, + dataVar, outLoc = "./calib", + outVars = NULL, iterations = 300, + skipSpinup = TRUE, plotName = "calib.jpg", + modifyOriginal=TRUE, likelihood, uncertainity = NULL, + naVal = NULL, postProcString = NULL, threadNumber) { + + originalRun <- list() + nameGroupTable <- calTable + nameGroupTable[,1] <- tools::file_path_sans_ext(basename(nameGroupTable[,1])) + setwd(paste0("tmp/thread_",threadNumber)) + firstDir <- list.dirs(full.names=FALSE,recursive =FALSE)[1] + epcFile <- list.files(firstDir, pattern = "\\.epc",full.names=TRUE) + settingsProto <- setupMuso(inputLoc = firstDir, + iniInput =rep(list.files(firstDir, pattern = "\\.ini",full.names=TRUE),2)) + + # Exanding likelihood + likelihoodFull <- as.list(rep(NA,length(dataVar))) + names(likelihoodFull) <- names(dataVar) + if(!missing(likelihood)) { + lapply(names(likelihood),function(x){ + likelihoodFull[[x]] <<- likelihood[[x]] + }) + } + + defaultLikelihood <- which(is.na(likelihood)) + if(length(defaultLikelihood)>0){ + likelihoodFull[[defaultLikelihood]] <- (function(x, y){ + exp(-sqrt(mean((x-y)^2))) + }) + } + + mdata <- measuredData + if(is.null(parameters)){ + parameters <- tryCatch(read.csv("parameters.csv", stringsAsFactor=FALSE), error = function (e) { + stop("You need to specify a path for the parameters.csv, or a matrix.") + }) + } else { + if((!is.list(parameters)) & (!is.matrix(parameters))){ + parameters <- tryCatch(read.csv(parameters, stringsAsFactor=FALSE), error = function (e){ + stop("Cannot find neither parameters file neither the parameters matrix") + }) + }} + + print("optiMuso is randomizing the epc parameters now...",quote = FALSE) + if(iterations < 3000){ + randVals <- musoRand(parameters = parameters,constrains = NULL, iterations = 3000) + randVals[[2]]<- randVals[[2]][sample(1:3000,iterations),] + } else { + randVals <- musoRand(parameters = parameters,constrains = NULL, iterations = iterations) + } + + origEpc <- readValuesFromFile(epcFile, randVals[[1]]) + partialResult <- matrix(ncol=length(randVals[[1]])+2*length(dataVar)) + colN <- randVals[[1]] + colN[match(parameters[,2],randVals[[1]])] <- parameters[,1] + colN[match(parameters[,2], randVals[[1]])[!is.na(match(parameters[,2],randVals[[1]]))]] <- parameters[,1] + colnames(partialResult) <- c(colN,sprintf("%s_likelihood",names(dataVar)), + sprintf("%s_rmse",names(dataVar))) + numParameters <- length(colN) + partialResult[1:numParameters] <- origEpc + ## Prepare the preservedCalib matrix for the faster + ## run. + musoCodeToIndex <- sapply(dataVar,function(musoCode){ + settingsProto$dailyOutputTable[settingsProto$dailyOutputTable$code == musoCode,"index"] + }) + + resultRange <- (numParameters + 1):(ncol(partialResult)) + randValues <- randVals[[2]] + + settingsProto$calibrationPar <- randVals[[1]] + + if(!is.null(naVal)){ + measuredData <- as.data.frame(measuredData) + measuredData[measuredData == naVal] <- NA + } + resIterate <- 1:nrow(calTable) + names(resIterate) <- tools::file_path_sans_ext(basename(calTable[,1])) + alignIndexes <- commonIndexes(settingsProto, measuredData) + if(threadNumber == 1){ + originalRun[["calibrationPar"]] <- randVals[[1]] + + origModOut <- lapply(resIterate, function(i){ + dirName <- tools::file_path_sans_ext(basename(calTable[i,1])) + setwd(dirName) + settings <- settingsProto + settings$outputLoc <- settings$inputLoc <- "./" + settings$iniInput <- settings$inputFiles <- rep(paste0(dirName,".ini"),2) + settings$outputNames <- rep(dirName,2) + settings$executable <- ifelse(Sys.info()[1]=="Linux","./muso","./muso.exe") # set default exe option at start wold be better + res <- tryCatch(calibMuso(settings=settings,parameters =origEpc, silent = TRUE, skipSpinup = TRUE), error=function(e){NA}) + setwd("../") + res + }) + originalRun[["origModOut"]] <- origModOut + + partialResult[,resultRange] <- calcLikelihoodsForGroups(dataVar=dataVar, + mod=origModOut, + mes=measuredData, + likelihoods=likelihood, + alignIndexes=alignIndexes, + musoCodeToIndex = musoCodeToIndex,nameGroupTable = nameGroupTable, mean) + + write.csv(x=randVals[[1]],"../randIndexes.csv") + write.csv(x=partialResult, file="preservedCalib.csv",row.names=FALSE) + } + + print("Running the model with the random epc values...", quote = FALSE) + for(i in 2:(iterations+1)){ + # browser() + tmp <- lapply(resIterate, function(siteI){ + dirName <- tools::file_path_sans_ext(basename(calTable[siteI,1])) + setwd(dirName) + settings <- settingsProto + settings$outputLoc <- settings$inputLoc <- "./" + settings$iniInput <- settings$inputFiles <- rep(paste0(dirName,".ini"),2) + settings$outputNames <- rep(dirName,2) + settings$executable <- ifelse(Sys.info()[1]=="Linux","./muso","./muso.exe") # set default exe option at start wold be better + + res <- tryCatch(calibMuso(settings=settings,parameters=randValues[(i-1),], silent = TRUE, skipSpinup = TRUE), error=function(e){NA}) + setwd("../") + res + }) + + if(is.null(tmp)){ + partialResult[,resultRange] <- NA + } else { + partialResult[,resultRange] <- calcLikelihoodsForGroups(dataVar=dataVar, + mod=tmp, + mes=measuredData, + likelihoods=likelihood, + alignIndexes=alignIndexes, + musoCodeToIndex = musoCodeToIndex,nameGroupTable = nameGroupTable, mean) + + + + + + + partialResult[1:numParameters] <- randValues[(i-1),] + write.table(x=partialResult, file="preservedCalib.csv", append=TRUE, row.names=FALSE, + sep=",", col.names=FALSE) + # write.csv(x=tmp, file=paste0(pretag, (i+1),".csv")) + writeLines(as.character(i-1),"progress.txt") #UNCOMMENT IMPORTANT + } + } + if(threadNumber == 1){ + return(originalRun) + } +} +distributeCores <- function(iterations, numCores){ + perProcess<- iterations %/% numCores + numSimu <- rep(perProcess,numCores) + gainers <- sample(1:numCores, iterations %% numCores) + numSimu[gainers] <- numSimu[gainers] + 1 + numSimu +} + +prepareFromAgroMo <- function(fName){ + obs <- read.table(fName, stringsAsFactors=FALSE, sep = ";", header=T) + obs <- reshape(obs, timevar="var_id", idvar = "date", direction = "wide") + dateCols <- apply(do.call(rbind,(strsplit(obs$date, split = "-"))),2,as.numeric) + colnames(dateCols) <- c("year", "month", "day") + cbind.data.frame(dateCols, obs) +} + +calcLikelihoodsForGroups <- function(dataVar, mod, mes, likelihoods, alignIndexes, musoCodeToIndex, nameGroupTable, groupFun){ + likelihoodRMSE <- sapply(names(dataVar),function(key){ + modelled <- as.vector(unlist(sapply(sort(names(alignIndexes)), + function(domain_id){ + apply(do.call(cbind, + lapply(nameGroupTable[,1][nameGroupTable[,2] == domain_id], + function(site){mod[[site]][alignIndexes[[domain_id]]$model,musoCodeToIndex[key]] + })),1,groupFun) + + + + + }))) + + + measuredGroups <- split(mes,mes$domain_id) + measured <- do.call(rbind.data.frame, lapply(names(measuredGroups), function(domain_id){ + measuredGroups[[domain_id]][alignIndexes[[domain_id]]$meas,] + })) + measured <- measured[measured$var_id == key,] + + # measured <- measured[!is.na(measured)] + res <- c(likelihoods[[key]](modelled, measured), + sqrt(mean((modelled-measured$mean)^2)) + ) + print(abs(mean(modelled)-mean(measured$mean))) + # browser() + res + }) + names(likelihoodRMSE) <- c(sprintf("%s_likelihood",dataVar), sprintf("%s_rmse",dataVar)) + return(c(likelihoodRMSE[1,],likelihoodRMSE[2,])) +} + +commonIndexes <- function (settings,measuredData) { + # Have to fix for other starting points also + modelDates <- seq(from= as.Date(sprintf("%s-01-01",settings$startYear)), + by="days", + to=as.Date(sprintf("%s-12-31",settings$startYear+settings$numYears-1))) + modelDates <- grep("-02-29",modelDates,invert=TRUE, value=TRUE) + + lapply(split(measuredData,measuredData$domain_id),function(x){ + measuredDates <- x$date + modIndex <- match(as.Date(measuredDates), as.Date(modelDates)) + measIndex <- which(!is.na(modIndex)) + modIndex <- modIndex[!is.na(modIndex)] + cbind.data.frame(model=modIndex,meas=measIndex) + }) +} + +agroLikelihood <- function(modVector,measured){ + mu <- measured[,grep("mean", colnames(measured))] + stdev <- measured[,grep("^sd", colnames(measured))] + ndata <- nrow(measured) + sum(sapply(1:ndata, function(x){ + dnorm(modVector, mu[x], stdev[x], log = TRUE) + }), na.rm=TRUE) +} + + +#' compareCalibratedWithOriginal +#' +#' This functions compareses the likelihood and the RMSE values of the simulations and the measurements +#' @param key +compareCalibratedWithOriginal <- function(key, modOld, modNew, mes, + likelihoods, alignIndexes, musoCodeToIndex, nameGroupTable, + groupFun){ + + original <- as.vector(unlist(sapply(sort(names(alignIndexes)), + function(domain_id){ + apply(do.call(cbind, + lapply(nameGroupTable$site_id[nameGroupTable$domain_id == domain_id], + function(site){ + modOld[[site]][alignIndexes[[domain_id]]$model,musoCodeToIndex[key]] + })),1,groupFun) + }))) + calibrated <- as.vector(unlist(sapply(sort(names(alignIndexes)), + function(domain_id){ + apply(do.call(cbind, + lapply(nameGroupTable$site_id[nameGroupTable$domain_id == domain_id], + function(site){ + modNew[[site]][alignIndexes[[domain_id]]$model,musoCodeToIndex[key]] + })),1,groupFun) + }))) + measuredGroups <- split(mes,mes$domain_id) + measured <- do.call(rbind.data.frame, lapply(names(measuredGroups), function(domain_id){ + measuredGroups[[domain_id]][alignIndexes[[domain_id]]$meas,] + })) + measured <- measured[measured$var_id == key,] + return(data.frame(original = original, calibrated = calibrated,measured=measured$mean)) +} + +# plotDiff(key="grainDM", +# modOld=origModOut, +# modNew=tmp, +# # mes=measuredData, +# mes=mes, +# likelihoods=likelihood, +# alignIndexes=alignIndexes, +# musoCodeToIndex = musoCodeToIndex,nameGroupTable = nameGroupTable, mean) +# +# +# comp <- compareCalibratedWithOriginal(key="grainDM", +# modOld=origModOut, +# modNew=tmp, +# # mes=measuredData, +# mes=mes, +# likelihoods=likelihood, +# alignIndexes=alignIndexes, +# musoCodeToIndex = musoCodeToIndex,nameGroupTable = nameGroupTable, mean) +# +# +# +# +# +# plotDiff <- function(key, modOld, modNew, mes, +# likelihoods, alignIndexes, musoCodeToIndex, nameGroupTable, +# groupFun){ +# compme <- compareCalibratedWithOriginal(key,modOld,modNew,mes, +# likelihoods,alignIndexes,musoCodeToIndex,nameGroupTable,groupFun) +# +# with(data=compme, { +# plot(measured,original,ylim=c(min(measured),max(measured))) +# points(measured,calibrated,pch=20) +# abline(0,1) +# })} +# +# likelihood <- list(grainDM=function(x,y){exp(-1./2.*sqrt(mean((x-y$mean)^2.)))}) +# likelihood <- list(grainDM=agroLikelihood) + +spatialRun <- function(settingsProto,calibrationPar, parameters, calTable){ + resIterate <- 1:nrow(calTable) + names(resIterate) <- tools::file_path_sans_ext(basename(calTable[,1])) + modOut <- lapply(resIterate, function(i){ + dirName <- tools::file_path_sans_ext(basename(calTable[i,1])) + setwd(dirName) + settings <- settingsProto + settings$outputLoc <- settings$inputLoc <- "./" + settings$iniInput <- settings$inputFiles <- rep(paste0(dirName,".ini"),2) + settings$outputNames <- rep(dirName,2) + settings$calibrationPar <- calibrationPar + settings$executable <- ifelse(Sys.info()[1]=="Linux","./muso","./muso.exe") # set default exe option at start wold be better + res <- tryCatch(calibMuso(settings=settings,parameters =parameters, silent = TRUE, skipSpinup = TRUE), error=function(e){NA}) + setwd("../") + res + }) + modOut +} diff --git a/RBBGCMuso/inst/data/depTree.csv b/RBBGCMuso/inst/data/depTree.csv new file mode 100644 index 0000000..afa513a --- /dev/null +++ b/RBBGCMuso/inst/data/depTree.csv @@ -0,0 +1,18 @@ +"child","parent","mod","name" +"wth","ini",1,"weather" +"endpoint","ini",1,"endpointIn" +"endpoint","ini",2,"endpointOut" +"txt","ini",1,"co2" +"txt","ini",2,"nitrogen" +"soi","ini",1,"soil" +"epc","ini",1,"startEpc" +"mgm","ini",1,"management" +"plt","mgm",1,"planting" +"thn","mgm",1,"thining" +"mow","mgm",1,"mowing" +"grz","mgm",1,"grazing" +"hrv","mgm",1,"harvest" +"cul","mgm",1,"cultivation" +"frz","mgm",1,"fertilization" +"irr","mgm",1,"irrigation" +"epc","plt",0,"plantEpc" diff --git a/RBBGCMuso/inst/data/epcConstMatrix6.json b/RBBGCMuso/inst/data/epcConstMatrix6.json index adf2eff..337faac 100644 --- a/RBBGCMuso/inst/data/epcConstMatrix6.json +++ b/RBBGCMuso/inst/data/epcConstMatrix6.json @@ -866,8 +866,8 @@ "MIN": 0, "MAX": 0.1, "DEPENDENCE": 0, - "GROUP": 22, - "TYPE": 1 + "GROUP": 0, + "TYPE": 0 }, { "X": 85, @@ -877,8 +877,8 @@ "MIN": 0, "MAX": 0.1, "DEPENDENCE": 1, - "GROUP": 22, - "TYPE": 1 + "GROUP": 0, + "TYPE": 0 }, { "X": 86, diff --git a/RBBGCMuso/man/calibrateMuso.Rd b/RBBGCMuso/man/calibrateMuso.Rd index 8530339..b352155 100644 --- a/RBBGCMuso/man/calibrateMuso.Rd +++ b/RBBGCMuso/man/calibrateMuso.Rd @@ -6,7 +6,7 @@ \usage{ calibrateMuso( measuredData, - parameters = NULL, + parameters = read.csv("parameters.csv", stringsAsFactor = FALSE), startDate = NULL, endDate = NULL, formatString = "\%Y-\%m-\%d", @@ -28,6 +28,7 @@ calibrateMuso( pb = txtProgressBar(min = 0, max = iterations, style = 3), maxLikelihoodEpc = TRUE, pbUpdate = setTxtProgressBar, + outputLoc = "./", method = "GLUE", lg = FALSE, w = NULL, diff --git a/RBBGCMuso/man/checkFileSystem.Rd b/RBBGCMuso/man/checkFileSystem.Rd new file mode 100644 index 0000000..ab4f105 --- /dev/null +++ b/RBBGCMuso/man/checkFileSystem.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/flat.R +\name{checkFileSystem} +\alias{checkFileSystem} +\title{checkFileSystem} +\usage{ +checkFileSystem(iniName, root = ".", depTree = options("RMuso_depTree")[[1]]) +} +\arguments{ +\item{iniName}{The name of the ini file} + +\item{depTree}{The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]]} +} +\description{ +This function checks the MuSo file system, if it is correct +} diff --git a/RBBGCMuso/man/compareCalibratedWithOriginal.Rd b/RBBGCMuso/man/compareCalibratedWithOriginal.Rd new file mode 100644 index 0000000..1b46fc9 --- /dev/null +++ b/RBBGCMuso/man/compareCalibratedWithOriginal.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/multiSite.R +\name{compareCalibratedWithOriginal} +\alias{compareCalibratedWithOriginal} +\title{compareCalibratedWithOriginal} +\usage{ +compareCalibratedWithOriginal( + key, + modOld, + modNew, + mes, + likelihoods, + alignIndexes, + musoCodeToIndex, + nameGroupTable, + groupFun +) +} +\description{ +This functions compareses the likelihood and the RMSE values of the simulations and the measurements +} diff --git a/RBBGCMuso/man/flatMuso.Rd b/RBBGCMuso/man/flatMuso.Rd new file mode 100644 index 0000000..4fd2907 --- /dev/null +++ b/RBBGCMuso/man/flatMuso.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/flat.R +\name{flatMuso} +\alias{flatMuso} +\title{flatMuso} +\usage{ +flatMuso( + iniName, + execPath = "./", + depTree = options("RMuso_depTree")[[1]], + directory = "flatdir", + d = TRUE, + outE = TRUE +) +} +\arguments{ +\item{iniName}{The name of the ini file} + +\item{depTree}{The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]]} + +\item{directory}{The destination directory for flattening. At default it will be flatdir} +} +\description{ +This function reads the ini file and creates a directory (named after the directory argument) with all the files the modell uses with this file. the directory will be flat. +} diff --git a/RBBGCMuso/man/getFilePath.Rd b/RBBGCMuso/man/getFilePath.Rd new file mode 100644 index 0000000..3855cd5 --- /dev/null +++ b/RBBGCMuso/man/getFilePath.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/flat.R +\name{getFilePath} +\alias{getFilePath} +\title{getFilePath} +\usage{ +getFilePath( + iniName, + fileType, + execPath = "./", + depTree = options("RMuso_depTree")[[1]] +) +} +\arguments{ +\item{iniName}{The name of the ini file} + +\item{depTree}{The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]]} + +\item{filetype}{The type of the choosen file. For options see options("RMuso_depTree")[[1]]$name} +} +\description{ +This function reads the ini file and for a chosen fileType it gives you the filePath +} diff --git a/RBBGCMuso/man/getFilesFromIni.Rd b/RBBGCMuso/man/getFilesFromIni.Rd new file mode 100644 index 0000000..8124081 --- /dev/null +++ b/RBBGCMuso/man/getFilesFromIni.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/flat.R +\name{getFilesFromIni} +\alias{getFilesFromIni} +\title{getFilesFromIni} +\usage{ +getFilesFromIni( + iniName, + execPath = "./", + depTree = options("RMuso_depTree")[[1]] +) +} +\arguments{ +\item{iniName}{The name of the ini file} + +\item{depTree}{The file dependency defining dataframe. At default it is: options("RMuso_depTree")[[1]]} +} +\description{ +This function reads the ini file and gives yout back the path of all file involved in model run +} diff --git a/RBBGCMuso/man/multiSiteCalib.Rd b/RBBGCMuso/man/multiSiteCalib.Rd new file mode 100644 index 0000000..d05df02 --- /dev/null +++ b/RBBGCMuso/man/multiSiteCalib.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/multiSite.R +\name{multiSiteCalib} +\alias{multiSiteCalib} +\title{multiSiteCalib} +\usage{ +multiSiteCalib( + measurements, + calTable, + parameters, + dataVar, + iterations = 100, + likelihood, + execPath, + thread_prefix = "thread", + numCores = (parallel::detectCores() - 1), + pb = txtProgressBar(min = 0, max = iterations, style = 3), + pbUpdate = setTxtProgressBar +) +} +\description{ +This funtion uses the Monte Carlo technique to uniformly sample the parameter space from user defined parameters of the Biome-BGCMuSo model. The sampling algorithm ensures that the parameters are constrained by the model logic which means that parameter dependencies are fully taken into account (parameter dependency means that e.g leaf C:N ratio must be smaller than C:N ratio of litter; more complicated rules apply to the allocation parameters where the allocation fractions to different plant compartments must sum up 1). This function implements a mathematically correct solution to provide uniform distriution for all selected parameters. +} +\author{ +Roland HOLLOS +} diff --git a/RBBGCMuso/man/saveAllMusoPlots.Rd b/RBBGCMuso/man/saveAllMusoPlots.Rd index ec4c9d8..4f07af1 100644 --- a/RBBGCMuso/man/saveAllMusoPlots.Rd +++ b/RBBGCMuso/man/saveAllMusoPlots.Rd @@ -10,7 +10,7 @@ saveAllMusoPlots( silent = TRUE, type = "line", outFile = "annual.csv", - colour = NULL, + colour = "blue", skipSpinup = FALSE ) } diff --git a/RBBGCMuso/man/updateMusoMapping.Rd b/RBBGCMuso/man/updateMusoMapping.Rd index bbf40b9..ecdffc0 100644 --- a/RBBGCMuso/man/updateMusoMapping.Rd +++ b/RBBGCMuso/man/updateMusoMapping.Rd @@ -13,7 +13,7 @@ updateMusoMapping(excelName, dest = "./", version = getOption("RMuso_version")) The output code-variable matrix, and also the function changes the global variable } \description{ -This function updates the Biome-BGCMuSo output code-variable matrix. Within Biome-BGCMuSo the state variables and fluxes are marked by integer numbers. In order to provide meaningful variable names (e.g. 3009 means Gross Primary Production in Biome-BGCMuSo v5) a conversion table is needed which is handled by this function. +This function updates the Biome-BGCMuSo output code-variable matrix (creates a json file that is used internally by RBBGCMuso). Within Biome-BGCMuSo the output state variablesare marked by integer numbers (see the User's Guide). In order to provide meaningful variable names (e.g. 3009 means Gross Primary Production) a conversion table is needed which is handled by this function. The input Excel file must have the following column order: name, index, units, description (plus other optional columns line group). name refers to the abbreviation of the variable; index is the integer number of the output variable; unit is the unit of the variable; description is a meaningful text to explain the variable. The script will NOT work with other column order! } \author{ Roland HOLLOS From 7dd2cc97dc1c7d33f9f8919ab7a7aa9d45f7c210 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 09:15:26 +0100 Subject: [PATCH 28/88] allow dataload to fail --- RBBGCMuso/R/atStart.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/atStart.R b/RBBGCMuso/R/atStart.R index 4c35a70..cd92a89 100644 --- a/RBBGCMuso/R/atStart.R +++ b/RBBGCMuso/R/atStart.R @@ -23,7 +23,7 @@ RMuso_varTable[[version]] <<- varTable }) - RMuso_depTree<- read.csv(file.path(system.file("data",package="RBBGCMuso"),"depTree.csv"), stringsAsFactors=FALSE) + RMuso_depTree<- try(read.csv(file.path(system.file("data",package="RBBGCMuso"),"depTree.csv"), stringsAsFactors=FALSE)) options(RMuso_version=RMuso_version, From 819dcda9053f916e479ee449aef5aee7bc7124cd Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 09:17:40 +0100 Subject: [PATCH 29/88] removing unnecessary row --- RBBGCMuso/R/flat.R | 1 - 1 file changed, 1 deletion(-) diff --git a/RBBGCMuso/R/flat.R b/RBBGCMuso/R/flat.R index fc70986..0f5fec1 100644 --- a/RBBGCMuso/R/flat.R +++ b/RBBGCMuso/R/flat.R @@ -1,4 +1,3 @@ -options(RMuso_depTree = read.csv("~/projects/RBBGCMuso/RBBGCMuso/inst/data/depTree.csv", stringsAsFactors = FALSE)) getQueue <- function(depTree=options("RMuso_depTree")[[1]], startPoint){ if(length(startPoint) == 0){ From 809b26a9c5d850ff773e3a32a0293666d0b872dd Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 09:19:02 +0100 Subject: [PATCH 30/88] disallow dataload to fail --- RBBGCMuso/R/atStart.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/atStart.R b/RBBGCMuso/R/atStart.R index cd92a89..4c35a70 100644 --- a/RBBGCMuso/R/atStart.R +++ b/RBBGCMuso/R/atStart.R @@ -23,7 +23,7 @@ RMuso_varTable[[version]] <<- varTable }) - RMuso_depTree<- try(read.csv(file.path(system.file("data",package="RBBGCMuso"),"depTree.csv"), stringsAsFactors=FALSE)) + RMuso_depTree<- read.csv(file.path(system.file("data",package="RBBGCMuso"),"depTree.csv"), stringsAsFactors=FALSE) options(RMuso_version=RMuso_version, From b43f5542bd77205e307a76612c697c5ba75484c2 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 14:41:28 +0100 Subject: [PATCH 31/88] copy cygwin.dll --- RBBGCMuso/R/multiSite.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index ed36220..af8bef5 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -9,6 +9,8 @@ copyToThreadDirs2 <- function(iniSource, thread_prefix = "thread", numCores, exe directory=file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)),""), d =TRUE) file.copy(executable, file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))) + tryCatch(file.copy(file.path(execPath,"cygwin.dll"), + file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))),function(e){"If you are in Windows..."}) }) sapply(2:numCores,function(thread){ From 9f52704bce57c5750fc01adabab9bebeec7e9a68 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 14:46:23 +0100 Subject: [PATCH 32/88] color swithch in dotplot --- RBBGCMuso/R/multiSite.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index af8bef5..7482ea7 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -178,9 +178,11 @@ multiSiteCalib <- function(measurements, xlab=expression("measured "~(kg[C]~m^-2)), ylab=expression("simulated "~(kg[C]~m^-2)), cex.lab=1.3, + col="red", + pch=19, pty="s" ) - points(measured,calibrated,pch=20) + points(measured,calibrated, pch=19, col="blue") abline(0,1) legend(x="top", pch=c(1,19), From ac9e6478e8f8e636849f93a023f25b579931654f Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 14:54:01 +0100 Subject: [PATCH 33/88] cygwin -> cygwin1 --- RBBGCMuso/R/multiSite.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index 7482ea7..baa44fc 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -9,7 +9,7 @@ copyToThreadDirs2 <- function(iniSource, thread_prefix = "thread", numCores, exe directory=file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)),""), d =TRUE) file.copy(executable, file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))) - tryCatch(file.copy(file.path(execPath,"cygwin.dll"), + tryCatch(file.copy(file.path(execPath,"cygwin1.dll"), file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))),function(e){"If you are in Windows..."}) }) From 9d8ac6bbdc130ba5556be2537077369b8f300ca0 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 15:01:49 +0100 Subject: [PATCH 34/88] file copy bug solve --- RBBGCMuso/R/multiSite.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index baa44fc..ba1e636 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -10,7 +10,7 @@ copyToThreadDirs2 <- function(iniSource, thread_prefix = "thread", numCores, exe file.copy(executable, file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))) tryCatch(file.copy(file.path(execPath,"cygwin1.dll"), - file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))),function(e){"If you are in Windows..."}) + file.path("tmp", paste0(thread_prefix,"_1"))),function(e){"If you are in Windows..."}) }) sapply(2:numCores,function(thread){ From cdea7f5338c852dea9fc4f7405bd936c51c40129 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 15:08:46 +0100 Subject: [PATCH 35/88] fixing bad handler bug --- RBBGCMuso/R/multiSite.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index ba1e636..216f3f0 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -10,7 +10,7 @@ copyToThreadDirs2 <- function(iniSource, thread_prefix = "thread", numCores, exe file.copy(executable, file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))) tryCatch(file.copy(file.path(execPath,"cygwin1.dll"), - file.path("tmp", paste0(thread_prefix,"_1"))),function(e){"If you are in Windows..."}) + file.path("tmp", paste0(thread_prefix,"_1"))), error = function(e){"If you are in Windows..."}) }) sapply(2:numCores,function(thread){ From 30c788a43c328f3c0e550602178cba2504be008f Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 15:23:12 +0100 Subject: [PATCH 36/88] cygwin copy problem --- RBBGCMuso/R/multiSite.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index 216f3f0..13b621b 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -10,9 +10,9 @@ copyToThreadDirs2 <- function(iniSource, thread_prefix = "thread", numCores, exe file.copy(executable, file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))) tryCatch(file.copy(file.path(execPath,"cygwin1.dll"), - file.path("tmp", paste0(thread_prefix,"_1"))), error = function(e){"If you are in Windows..."}) + file.path("tmp", paste0(thread_prefix,"_1"),tools::file_path_sans_ext(basename(x)))), + error = function(e){"If you are in Windows..."}) }) - sapply(2:numCores,function(thread){ dir.create(sprintf("tmp/%s_%s",thread_prefix,thread), showWarnings=FALSE) file.copy(list.files(sprintf("tmp/%s_1",thread_prefix),full.names = TRUE),sprintf("tmp/%s_%s/",thread_prefix,thread), From cda23ca4bd330346b392fb0504053c4dbe759f55 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 15:47:58 +0100 Subject: [PATCH 37/88] fixing colors in calibRes --- RBBGCMuso/R/multiSite.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index 13b621b..1ef9b73 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -185,7 +185,8 @@ multiSiteCalib <- function(measurements, points(measured,calibrated, pch=19, col="blue") abline(0,1) legend(x="top", - pch=c(1,19), + pch=c(19,19), + col=c("red","blue") inset=c(0,-0.1), legend=c("original","calibrated"), ncol=2, From 760020fdd1152772cdfe397fde64a15239c0c438 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Fri, 19 Mar 2021 15:53:15 +0100 Subject: [PATCH 38/88] adding coma --- RBBGCMuso/R/multiSite.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index 1ef9b73..cc85309 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -186,7 +186,7 @@ multiSiteCalib <- function(measurements, abline(0,1) legend(x="top", pch=c(19,19), - col=c("red","blue") + col=c("red","blue"), inset=c(0,-0.1), legend=c("original","calibrated"), ncol=2, From 701ef4ba9b44c186d3b4e5d5e53475b95931f188 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Wed, 7 Apr 2021 14:41:17 +0200 Subject: [PATCH 39/88] R2 add --- RBBGCMuso/R/multiSite.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RBBGCMuso/R/multiSite.R b/RBBGCMuso/R/multiSite.R index cc85309..9c5c0b0 100644 --- a/RBBGCMuso/R/multiSite.R +++ b/RBBGCMuso/R/multiSite.R @@ -167,6 +167,9 @@ multiSiteCalib <- function(measurements, res[["MAE"]] <- mean(abs((comp[,2]-comp[,3]))) res[["RMSE"]] <- results[bestCase,ncol(results)] res[["originalRMSE"]] <- sqrt(mean((comp[,1]-comp[,3])^2)) + res[["originalR2"]] <- summary(lm(measured ~ original,data=res$comparison))$r.squared + res[["R2"]] <- summary(lm(measured ~ calibrated, data=res$comparison))$r.squared + saveRDS(res,"results.RDS") png("calibRes.png") opar <- par(mar=c(5,5,4,2)+0.1, xpd=FALSE) with(data=res$comparison, { From 877f8b6a39a1e59063b81979644030218da5ec76 Mon Sep 17 00:00:00 2001 From: Hollos Roland Date: Mon, 19 Apr 2021 13:33:24 +0200 Subject: [PATCH 40/88] removing unnecessary files --- Development_branch.md | 1 - RBBGCMuso_0.6.1.zip | Bin 3116548 -> 0 bytes test.sh | 1 - untestedFunctions/musoFilter.R | 4 -- untestedFunctions/musoGLUE.R | 72 --------------------------------- 5 files changed, 78 deletions(-) delete mode 100644 Development_branch.md delete mode 100644 RBBGCMuso_0.6.1.zip delete mode 100644 test.sh delete mode 100644 untestedFunctions/musoFilter.R delete mode 100644 untestedFunctions/musoGLUE.R diff --git a/Development_branch.md b/Development_branch.md deleted file mode 100644 index 4142667..0000000 --- a/Development_branch.md +++ /dev/null @@ -1 +0,0 @@ -This is the unstable development branch of the RBBGCMuso package. --->outDated diff --git a/RBBGCMuso_0.6.1.zip b/RBBGCMuso_0.6.1.zip deleted file mode 100644 index d790dddf10eecb34b0d7815889a5dc6e5bef48d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3116548 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