-
Notifications
You must be signed in to change notification settings - Fork 282
Multisite SA support with input coordination and README tracking #3660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
0c3d0fc
18a3564
f075e4d
f7b0c3a
81f50f6
5dcfe59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,10 +104,15 @@ run.write.configs <- function(settings, ensemble.size, input_design, write = TRU | |
| options(scipen = 12) | ||
|
|
||
| samples.file <- file.path(settings$outdir, "samples.Rdata") | ||
| existing_data <- NULL | ||
| if (file.exists(samples.file)) { | ||
| samples <- new.env() | ||
| load(samples.file, envir = samples) ## loads ensemble.samples, trait.samples, sa.samples, runs.samples, env.samples | ||
| trait.samples <- samples$trait.samples | ||
| existing_data <- new.env() | ||
| load(samples.file, envir = existing_data) | ||
| trait.samples <- existing_data$trait.samples | ||
| sa.samples <- existing_data$sa.samples | ||
| runs.samples <- existing_data$runs.samples | ||
|
|
||
| # Generate ensemble.samples for current site | ||
| trait_sample_indices <- input_design[["param"]] | ||
| ensemble.samples <- list() | ||
| for (pft in names(trait.samples)) { | ||
|
|
@@ -120,11 +125,9 @@ run.write.configs <- function(settings, ensemble.size, input_design, write = TRU | |
| ) | ||
| names(ensemble.samples[[pft]]) <- names(pft_traits) | ||
| } | ||
| sa.samples <- samples$sa.samples | ||
| runs.samples <- samples$runs.samples | ||
| ## env.samples <- samples$env.samples | ||
| } else { | ||
| PEcAn.logger::logger.error(samples.file, "not found, this file is required by the run.write.configs function") | ||
| # cannot proceed without sample.Rdata | ||
| PEcAn.logger::logger.severe(samples.file, "not found, this file is required by the run.write.configs function") | ||
| } | ||
|
|
||
| ## remove previous runs.txt | ||
|
|
@@ -170,6 +173,7 @@ run.write.configs <- function(settings, ensemble.size, input_design, write = TRU | |
| quantile.samples = sa.samples, | ||
| settings = settings, | ||
| model = model, | ||
| input_design = input_design, | ||
| write.to.db = write | ||
| ) | ||
|
|
||
|
|
@@ -211,6 +215,80 @@ run.write.configs <- function(settings, ensemble.size, input_design, write = TRU | |
| PEcAn.logger::logger.info("###### Finished writing model run config files #####") | ||
| PEcAn.logger::logger.info("config files samples in ", file.path(settings$outdir, "run")) | ||
|
|
||
| ## Use existing_data from earlier load | ||
| if (!is.null(existing_data)) { | ||
| # Merge ensemble.samples | ||
| if (!is.null(existing_data$ensemble.samples)) { | ||
| for (pft_name in names(ensemble.samples)) { | ||
| if (pft_name %in% names(existing_data$ensemble.samples)) { | ||
| # combine parameter samples for same PFT across sites | ||
| ensemble.samples[[pft_name]] <- rbind( | ||
| existing_data$ensemble.samples[[pft_name]], | ||
| ensemble.samples[[pft_name]] | ||
| ) | ||
| } | ||
| # New PFTs from current site are automatically preserved | ||
| } | ||
| # preserves existing PFTs -- add PFTs that exist in file but not current site | ||
| for (existing_pft in names(existing_data$ensemble.samples)) { | ||
| if (!existing_pft %in% names(ensemble.samples)) { | ||
| ensemble.samples[[existing_pft]] <- existing_data$ensemble.samples[[existing_pft]] | ||
| } | ||
| } | ||
| } | ||
| # Merge trait.samples | ||
| if (!is.null(existing_data$trait.samples)) { | ||
| trait.samples <- utils::modifyList(existing_data$trait.samples, trait.samples) | ||
|
Comment on lines
+240
to
+241
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trait.samples ought to be generated exactly once at the beginning of setup (to define the set of posteriors that |
||
| } | ||
| # Merge sa.samples | ||
| if (!is.null(existing_data$sa.samples)) { | ||
| sa.samples <- utils::modifyList(existing_data$sa.samples, sa.samples) | ||
| } | ||
| # Merge runs.samples | ||
| if (!is.null(existing_data$runs.samples)) { | ||
| # Merge ensemble runs | ||
| if (!is.null(runs.samples$ensemble)) { | ||
| if (!is.null(existing_data$runs.samples$ensemble)) { | ||
| runs.samples$ensemble <- rbind( | ||
| existing_data$runs.samples$ensemble, | ||
| runs.samples$ensemble | ||
| ) | ||
| } | ||
| } else if (!is.null(existing_data$runs.samples$ensemble)) { | ||
| # Current site has no ensemble, preserve existing | ||
| runs.samples$ensemble <- existing_data$runs.samples$ensemble | ||
| } | ||
| # Merge SA runs | ||
| if (!is.null(runs.samples$sa)) { | ||
| if (!is.null(existing_data$runs.samples$sa)) { | ||
| for (pft_name in names(runs.samples$sa)) { | ||
| if (pft_name %in% names(existing_data$runs.samples$sa)) { | ||
| runs.samples$sa[[pft_name]] <- rbind( | ||
| existing_data$runs.samples$sa[[pft_name]], | ||
| runs.samples$sa[[pft_name]] | ||
| ) | ||
| } | ||
| } | ||
| for (existing_pft in names(existing_data$runs.samples$sa)) { | ||
| if (!existing_pft %in% names(runs.samples$sa)) { | ||
| runs.samples$sa[[existing_pft]] <- existing_data$runs.samples$sa[[existing_pft]] | ||
| } | ||
| } | ||
| } | ||
| } else if (!is.null(existing_data$runs.samples$sa)) { | ||
| # Current site has no SA, preserve existing | ||
| runs.samples$sa <- existing_data$runs.samples$sa | ||
| } | ||
| } | ||
| # Merge pft.names | ||
| if (!is.null(existing_data$pft.names)) { | ||
| pft.names <- unique(c(existing_data$pft.names, pft.names)) | ||
| } | ||
| # Merge trait.names | ||
| if (!is.null(existing_data$trait.names)) { | ||
| trait.names <- utils::modifyList(existing_data$trait.names, trait.names) | ||
| } | ||
| } | ||
| ### Save output from SA/Ensemble runs | ||
| # A lot of this is duplicate with the ensemble/sa specific output above, but kept for backwards compatibility. | ||
| save(ensemble.samples, trait.samples, sa.samples, runs.samples, pft.names, trait.names, | ||
|
|
@@ -221,3 +299,4 @@ run.write.configs <- function(settings, ensemble.size, input_design, write = TRU | |
| invisible(settings) | ||
| return(settings) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,14 +94,15 @@ read.sa.output <- function(traits, quantiles, pecandir, outdir, pft.name = "", | |
| #' by \code{settings$rundir} before writing to it? | ||
| #' @param write.to.db logical: Record this run to BETY? If TRUE, uses connection | ||
| #' settings specified in \code{settings$database} | ||
| #' @param input_design data.frame coordinating input files across runs | ||
| #' | ||
| #' @return list, containing $runs = data frame of runids, | ||
| #' and $ensemble.id = the ensemble ID for these runs. | ||
| #' Also writes sensitivity analysis configuration files as a side effect | ||
| #' @export | ||
| #' @author David LeBauer, Carl Davidson | ||
| write.sa.configs <- function(defaults, quantile.samples, settings, model, | ||
| clean = FALSE, write.to.db = TRUE) { | ||
| clean = FALSE, write.to.db = TRUE, input_design = NULL) { | ||
| scipen <- getOption("scipen") | ||
| options(scipen = 12) | ||
| my.write.config <- paste("write.config.", model, sep = "") | ||
|
|
@@ -223,6 +224,50 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| dir.create(file.path(settings$rundir, run.id), recursive = TRUE) | ||
| dir.create(file.path(settings$modeloutdir, run.id), recursive = TRUE) | ||
|
|
||
| ## select single met path for SA runs without input_design (backward compatibility) | ||
| ## when input_design is provided (recommended), it supersedes this selection | ||
| ## and allows different met files per SA run | ||
| # I check to make sure the path under the met is a list. | ||
| # if it's specified what met needs to be used in 'met.id' under sensitivity | ||
| # analysis of pecan xml we used that otherwise, I use the first met. | ||
| if (is.list(settings$run$inputs$met$path)) { | ||
| # This checks for met.id tag in the settings under sensitivity analysis - | ||
| # if it's not there it creates it. Then it's gonna use what it created. | ||
| if (is.null(settings$sensitivity.analysis$met.id)) { | ||
| settings$sensitivity.analysis$met.id <- 1 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 to the overall goal of picking one met (/other input) path to use across the whole SA. But calling this "met.id" is confusing to me -- looks like this is basically an index into the ensemble, whereas I would usually expect "met ID" to mean an identifier for which source (e.g. CRUNCEP ERA5, etc) the met data came from. Could it instead be something like
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...But wait! Doesn't
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good question, it duplicates with
short-term fix (This PR): Long-term (Further iteration):
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re met path: You've talked me out of the idea, especially since it would need to be site-specific -- so my proposal wouldn't need just one
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re input design: I'll defer to @dlebauer but my sense is that since we've bought into making
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
that's renaming to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm in favor of having a consistent
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am curious - where is met.id used? I can't find the string in either the PEcAn repository or in xml settings files used by CCMMF or Dongchen's workflows.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dlebauer it's only used immediately below to set the met$path. I agree with the consensus to drop the met.id solution proposed here and just use input_design. If one wants to only vary parameters and hold everything else fixed it's really easy to generate an input_design that's all 1's for everything other than parameters. The met.id "solution" isn't really a solution anyways as it only fixes met ensemble members and not the other ensembled inputs (phenology, soil physics, initial conditions, etc.) |
||
| } | ||
| settings$run$inputs$met$path <- settings$run$inputs$met$path[[settings$sensitivity.analysis$met.id]] | ||
| } | ||
|
|
||
| # Apply input design coordination for median run | ||
| median_settings <- settings | ||
| if (!is.null(input_design)) { | ||
| # Coordinate inputs for median run (use first row) | ||
| for (input_tag in colnames(input_design)) { | ||
| if (input_tag != "param" && !is.null(median_settings$run$inputs[[input_tag]]$path)) { | ||
| input_paths <- median_settings$run$inputs[[input_tag]]$path | ||
| # Assume list structure (consistent with write.ensemble.configs) | ||
| if (length(input_paths) > 1) { | ||
| input_index <- input_design[[input_tag]][1] | ||
| median_settings$run$inputs[[input_tag]]$path <- input_paths[[input_index]] | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| median_input_info <- "" | ||
| for (input_tag in names(median_settings$run$inputs)) { | ||
| input_data <- median_settings$run$inputs[[input_tag]] | ||
| # At SA stage, path is ALWAYS a resolved string (thanks to input design) | ||
| if (!is.null(input_data) && !is.null(input_data$path)) { | ||
| median_input_info <- paste0(median_input_info, | ||
| format(input_tag, width = 12, justify = "left"), | ||
| ": ", | ||
| input_data$path, | ||
| "\n") | ||
| } | ||
| } | ||
|
|
||
| # write run information to disk TODO need to print list of pft names and trait | ||
| # names | ||
| cat("runtype : sensitivity analysis\n", | ||
|
|
@@ -236,7 +281,7 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| "model id : ", settings$model$id, "\n", | ||
| "site : ", settings$run$site$name, "\n", | ||
| "site id : ", settings$run$site$id, "\n", | ||
| "met data : ", settings$run$site$met, "\n", | ||
| median_input_info, | ||
| "start date : ", settings$run$start.date, "\n", | ||
| "end date : ", settings$run$end.date, "\n", | ||
| "hostname : ", settings$host$name, "\n", | ||
|
|
@@ -246,24 +291,10 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| sep = "") | ||
|
|
||
|
|
||
| # I check to make sure the path under the met is a list. | ||
| # if it's specified what met needs to be used in 'met.id' under sensitivity | ||
| # analysis of pecan xml we used that otherwise, I use the first met. | ||
| if (is.list(settings$run$inputs$met$path)) { | ||
| # This checks for met.id tag in the settings under sensitivity analysis - | ||
| # if it's not there it creates it. Then it's gonna use what it created. | ||
| if (is.null(settings$sensitivity.analysis$met.id)) { | ||
| settings$sensitivity.analysis$met.id <- 1 | ||
| } | ||
| settings$run$inputs$met$path <- settings$run$inputs$met$path[[settings$sensitivity.analysis$met.id]] | ||
|
|
||
| } | ||
|
|
||
|
|
||
| # write configuration | ||
| do.call(my.write.config, args = list(defaults = defaults, | ||
| trait.values = median.samples, | ||
| settings = settings, | ||
| settings = median_settings, | ||
| run.id = run.id)) | ||
| cat( | ||
| run.id, | ||
|
|
@@ -272,16 +303,18 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| append = TRUE | ||
| ) | ||
|
|
||
| run_index <- 1 | ||
|
|
||
| ## loop over pfts | ||
| runs <- list() | ||
| for (i in seq_along(names(quantile.samples))) { | ||
| pftname <- names(quantile.samples)[i] | ||
| for (pft_idx in seq_along(names(quantile.samples))) { | ||
infotroph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| pftname <- names(quantile.samples)[pft_idx] | ||
| if (pftname == "env") { | ||
| next | ||
| } | ||
|
|
||
| traits <- colnames(quantile.samples[[i]]) | ||
| quantiles.str <- rownames(quantile.samples[[i]]) | ||
| traits <- colnames(quantile.samples[[pft_idx]]) | ||
| quantiles.str <- rownames(quantile.samples[[pft_idx]]) | ||
|
|
||
| runs[[pftname]] <- data.frame() | ||
|
|
||
|
|
@@ -293,7 +326,7 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| } else { | ||
| quantile <- as.numeric(quantile.str) / 100 | ||
| trait.samples <- median.samples | ||
| trait.samples[[i]][trait] <- quantile.samples[[i]][quantile.str, trait, drop = FALSE] | ||
| trait.samples[[pft_idx]][trait] <- quantile.samples[[pft_idx]][quantile.str, trait, drop = FALSE] | ||
|
|
||
| if (!is.null(con)) { | ||
| paramlist <- paste0("quantile=", quantile.str, ",trait=", trait, ",pft=", pftname) | ||
|
|
@@ -343,12 +376,15 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| run.type = "SA", | ||
| index = round(quantile, 3), | ||
| trait = trait, | ||
| pft.name = names(trait.samples)[i], | ||
| pft.name = names(trait.samples)[pft_idx], | ||
| site.id = settings$run$site$id | ||
| ) | ||
| } | ||
| runs[[pftname]][quantile.str, trait] <- run.id | ||
|
|
||
| # Increment run counter | ||
| run_index <- run_index + 1 | ||
|
|
||
| # create folders (cleaning up old ones if needed) | ||
| if (clean) { | ||
| unlink(file.path(settings$rundir, run.id)) | ||
|
|
@@ -357,19 +393,46 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| dir.create(file.path(settings$rundir, run.id), recursive = TRUE) | ||
| dir.create(file.path(settings$modeloutdir, run.id), recursive = TRUE) | ||
|
|
||
| # write run information to disk | ||
| # Apply input design coordination for SA runs | ||
| settings_copy <- settings | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's confusing that below here some values are taken from
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SA loop without settings_copy:
The answer for question: why BUT inputs must come from
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but the path is updated earlier in the loop than the write.config call, so by the time write.config gets called for run 2 the path from run 1 has already been overwritten by the path from run 2. As long as you're sure that every iteration will update the same fields of the settings object, it doesn't matter whether it started from a "clean" object or not. Similarly, if you are using settings_copy then it also contains all the fields from
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that it makes sense to use settings_copy instead of settings. It seems this only concerns the README block that pulls fields from the original settings. If we just switch those I've proposed this change below in https://github.com/PecanProject/pecan/pull/3660/files/f075e4d7d0f9ad85ee340bbe01f7dca464988677#r2561591261 |
||
| if (!is.null(input_design)) { | ||
| for (input_tag in colnames(input_design)) { | ||
| if (input_tag != "param" && !is.null(settings_copy$run$inputs[[input_tag]]$path)) { | ||
| input_paths <- settings_copy$run$inputs[[input_tag]]$path | ||
| if (length(input_paths) > 1) { | ||
| input_index <- input_design[[input_tag]][run_index] | ||
| settings_copy$run$inputs[[input_tag]]$path <- input_paths[[input_index]] | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| # Build dynamic input info string for SA run README | ||
| sa_input_info <- "" | ||
| for (input_tag in names(settings_copy$run$inputs)) { | ||
| input_data <- settings_copy$run$inputs[[input_tag]] | ||
| if (!is.null(input_data) && !is.null(input_data$path)) { | ||
| sa_input_info <- paste0(sa_input_info, | ||
| format(input_tag, width = 12, justify = "left"), | ||
| ": ", | ||
| input_data$path, | ||
| "\n") | ||
| } | ||
| } | ||
|
|
||
| # write SA run information to disk | ||
| cat("runtype : sensitivity analysis\n", | ||
| "workflow id : ", workflow.id, "\n", | ||
| "ensemble id : ", ensemble.id, "\n", | ||
| "pft name : ", names(trait.samples)[i], "\n", | ||
| "pft name : ", names(trait.samples)[pft_idx], "\n", | ||
| "quantile : ", quantile.str, "\n", | ||
| "trait : ", trait, "\n", | ||
| "run id : ", run.id, "\n", | ||
| "model : ", model, "\n", | ||
| "model id : ", settings$model$id, "\n", | ||
| "site : ", settings$run$site$name, "\n", | ||
| "site id : ", settings$run$site$id, "\n", | ||
| "met data : ", settings$run$site$met, "\n", | ||
| sa_input_info, | ||
| "start date : ", settings$run$start.date, "\n", | ||
| "end date : ", settings$run$end.date, "\n", | ||
| "hostname : ", settings$host$name, "\n", | ||
|
|
@@ -382,7 +445,7 @@ write.sa.configs <- function(defaults, quantile.samples, settings, model, | |
| # write configuration | ||
| do.call(my.write.config, args = list(defaults = defaults, | ||
| trait.values = trait.samples, | ||
| settings = settings, | ||
| settings = settings_copy, | ||
| run.id)) | ||
| cat( | ||
| run.id, | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not seeing how one figures out after the fact which samples were used for which site
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The site information is preserved through the run id's; Each run id contains the site id
(e.g,
ENS-00001-1000004925--- > site 1000004925), and this becomes the output folder name. Downstream functions don't parse the site id, they just iterate through all run id's and read from folders named with those id's.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case I don't think ensemble.samples should grow at all -- The point of passing in an input design is to enforce that every run with a given ensemble member uses the same value of
growth_resp(and every other parameter too), so ensemble.samples needs to stay n_ens lines long. I guess one could repeat the same set of values for each site, but that seems VERY inefficient.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@infotroph @mdietze @dlebauer want to make sure that i got right logic with the merging approach ?
Explained in comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems overly complicated. get.param.samples should generate one matrix of parameters per PFT and currently there is not within-PFT variability in parameter samples across sites (though this may change in the future). For a particular site, the PFT and input_design should determine which parameter matrix you use, and which row in that matrix you select, respectively. There should be no need to rbind matrices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Mike here and am leaning toward an even stronger claim that run.write.configs should not be rewriting samples.Rdata at all. Its contents are all either generated once by get.parameter.samples before run.write.configs starts (ensemble.samples, sa.samples, trait.samples) or are not actually samples and can be taken from runs.txt (runs.samples)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the downstream analysis expect runs.samples to be in samples.Rdata ?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into read.ensemble.output and read.sa.output to verify if we can stop writing samples.Rdata, but those functions explicitly load samples.Rdata to find the run ids :
ens.run.ids <- samples$runs.samples$ensemblesa.run.ids <- samples$runs.samples$sathey do not currently have logic to parse runs.txt
so here we can ---
1 ) reload the existing parameters (avoid duplication/growth) but I must append the run ids to runs.samples and save the file. (
run.write.cofigs)2 ) or refactor downstream to read runs.txt ( does this feel like to be a separate PR ? )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know what you mean by "stop writing samples.Rdata". No one is saying this isn't needed, we're saying it should already exist before run.write.configs is called (it is now generated by the new input design function, which should be called before the write configs functions) and it shouldn't need to be modified.
Only start model runs should rely on runs.txt. There's not enough information in there for anything else downstream
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was responding specifically to chris's suggestion that runs.samples might be redundant cuz that info
I was verifying if
run.write.configscould stop appending even the new run ids(runs.samples) to samples.Rdata ( relaying on runs.txt instead) , but downstream (read.ensemble.output , .. ) depends on finding those ids in samples.Rdata.But it's clear now