Replies: 7 comments 6 replies
|
You are correct that the current SDA code does the restart outside the job.sh, which we have to do because in the Analysis step we're reading in the current state, modifying it, and writing it back out on the fly. While the CARB workflow restarts don't need this functionality now, I'd argue that retaining this approach, while possibly a tad slower, may make it easier to eventually bring the SDA and events codes together in the future (either to do SDA for CARB and/or to include events in the North American SDA) |
|
I believe that summary is correct for the SIPNET side of this. For those interested, see PecanProject/sipnet#271 for my tracking of where SIPNET is currently falling short in outputting state. (Though, as @ashiklom points out, our current proposed approach is to output a "restart" file that contains a complete state snapshot.) |
|
That lines up with #180. To write down what I recall from our discussions of how to do this:
So it sounds like the path forward is:
In the current implementation, it seems |
|
How to Restart? To support parameter and state updates, we have at least two options:
I think 1) is more standard and 2) is less work / less change from current workflow. |
|
Progress update: To get restarts working outside of a Note that this depends on:
I'll be returning to this on Friday, but I wanted to put this here for discussion and input to (1) confirm that I'm basically on the right track; (2) I'm not duplicating anyone else's effort; (3) to confirm that, at a high level, this is the approach we want; and (4) to give folks an early chance to weigh in on design preferences/tweaks before I resume work on this in a few days. #!/usr/bin/env Rscript
# devtools::install("~/projects/pecan/sipnet-events/modules/data.remote", upgrade = FALSE)
# devtools::install("~/projects/pecan/sipnet-events/base/workflow", upgrade = FALSE)
devtools::install("~/projects/pecan/sipnet-events/models/sipnet", upgrade = FALSE)
devtools::load_all("~/projects/pecan/sipnet-events/modules/data.land")
devtools::load_all("~/projects/pecan/sipnet-events/models/sipnet")
site1 <- "~/projects/pecan/sipnet-events/modules/data.land/inst/events_fixtures/events_site1.json"
site1_multi <- "~/projects/pecan/sipnet-events/modules/data.land/inst/events_fixtures/events_site1_multi.json"
site_12 <- "~/projects/pecan/sipnet-events/modules/data.land/inst/events_fixtures/events_site1_site2.json"
binary <- path.expand("~/projects/pecan/_helpers/sipnet/sipnet.restart")
met <- path.expand("~/data/ERA5-site-processed/8773c58306e8d9a0/ERA5.1.2016-01-01.2023-12-31.clim")
start_date <- as.Date("2022-01-02")
end_date <- as.Date("2023-07-01")
site_id <- "8773c58306e8d9a0"
################################################################################
events_json <- site1
outdir <- normalizePath("_test/segments")
dir.create(outdir, showWarnings = FALSE)
settings <- PEcAn.settings::as.Settings(list(
outdir = file.path(outdir, "out"),
rundir = file.path(outdir, "run"),
modeloutdir = file.path(outdir, "out"),
pfts = list(list(
name = "grassland",
constants = list(num = 1)
)),
model = list(
type = "SIPNET",
binary = binary,
revision = "v2"
),
run = list(
site = list(
id = site_id,
name = site_id,
lat = 32.71585,
lon = -115.47163
),
start.date = start_date,
end.date = end_date,
inputs = list(met = list(path = met))
),
host = list(
name = "localhost"
)
))
events <- jsonlite::fromJSON(events_json, simplifyVector = FALSE)
# TODO: Iterate over events
site_events_obj <- events[[1]]
site_id <- site_events_obj[["site_id"]]
site_events_list <- site_events_obj[["events"]]
site_events_common <- site_events_obj
site_events_common[["events"]] <- NULL
crop_cycles <- events_to_crop_cycle_starts(site1_multi)
# Empty example
# crop_cycles <- tibble::tibble(
# site_id = character(0),
# date = as.Date(NULL),
# crop_code = character(0)
# )
# Get segments
segments <- tibble::tibble(
start_date = c(start_date, crop_cycles[["date"]]),
end_date = c(crop_cycles[["date"]] - 1, end_date)
) |>
dplyr::mutate(segment_id = dplyr::row_number())
################################################################################
for (isegment in seq_len(nrow(segments))) {
# isegment <- 1
segment <- segments[isegment, ]
segment_id <- sprintf("%03d", isegment)
dstart <- segment[["start_date"]]
dend <- segment[["end_date"]]
segment_dir <- file.path(outdir, paste0("segment_", segment_id))
if (dir.exists(segment_dir)) {
unlink(segment_dir, recursive = TRUE)
}
dir.create(segment_dir, showWarnings = FALSE, recursive = TRUE)
# Filter events to relevant dates
events_sub <- site_events_list |>
purrr::keep(~as.Date(.x[["date"]]) >= dstart) |>
purrr::keep(~as.Date(.x[["date"]]) <= dend)
# Segment-separated events file
eventfile <- file.path(segment_dir, "events.json")
segment_event_obj <- list(c(site_events_common, events = list(events_sub)) )
jsonlite::write_json(segment_event_obj, eventfile, auto_unbox = TRUE)
segment_eventfile <- PEcAn.SIPNET::write.events.SIPNET(eventfile, segment_dir)
# Subset the met to only the dates in this segment. SIPNET does not respect
# start/end date, only the dates in the .clim file.
met_orig <- read.table(settings[[c("run", "inputs", "met", "path")]])
met_segment <- met_orig |>
# Create a date from the year + DOY
dplyr::mutate(date = as.Date(paste0(V2, "-01-01")) + V3) |>
dplyr::filter(date >= dstart, date <= dend) |>
dplyr::select(-c("date"))
met_segment_file <- file.path(segment_dir, "met.clim")
write.table(
met_segment,
met_segment_file,
quote = FALSE,
sep = "\t",
row.names = FALSE,
col.names = FALSE
)
# Segment-specific settings
segment_outdir <- file.path(segment_dir, "out")
dir.create(segment_outdir, showWarnings = FALSE, recursive = TRUE)
segment_rundir <- file.path(segment_dir, "run")
dir.create(segment_rundir, showWarnings = FALSE, recursive = TRUE)
segment_rundir_withid <- file.path(segment_rundir, segment_id)
dir.create(segment_rundir_withid, showWarnings = FALSE, recursive = TRUE)
segment_settings <- settings
segment_settings[["outdir"]] <- segment_outdir
segment_settings[["rundir"]] <- segment_rundir
segment_settings[["modeloutdir"]] <- segment_rundir
segment_settings[[c("run", "start.date")]] <- dstart
segment_settings[[c("run", "end.date")]] <- dend
segment_settings[[c("run", "inputs", "met", "path")]] <- met_segment_file
segment_settings[[c("run", "inputs", "events")]] <- list(path = segment_eventfile)
if (isegment > 1) {
# For isegment > 1, we restart from the *previous* segment's restart.out
segment_settings[[c("model", "restart_in")]] <- restart_out
}
# ...and now, define a new restart.out for *this* segment
restart_out <- file.path(segment_dir, "restart.out")
segment_settings[[c("model", "restart_out")]] <- restart_out
# Write runs file
writeLines(segment_id, file.path(segment_rundir, "runs.txt"))
# TODO: Logic to get the trait values corresponding to the segment's PFT.
# 1. Cross-reference crop_code against PFT
# 2. Get traits from PFT posterior file.
segment_traits <- list(list())
config <- PEcAn.SIPNET::write.config.SIPNET(
defaults = settings[["pfts"]],
trait.values = segment_traits,
settings = segment_settings,
run.id = segment_id
)
runs <- PEcAn.workflow::start_model_runs(segment_settings, write = FALSE)
}
# TODO: Post processing. Combine all the segments together and return output in
# PEcAn standard.
|
|
Here's an attempted hop test using the existing restart tools from PEcAn.SIPNET (and not yet calling Sipnet's binary restarts). This runs top to bottom for me, but is buggy -- for reasons I haven't yet tracked down, the ICs never get updated even though I can see their new values being passed in to write_restart. See s3://carb/tmp/hop_demo.zip for the full working directory I ran this in. |
Uh oh!
There was an error while loading. Please reload this page.
Summarizing today's discussion of SIPNET restarts (i.e., what I'll be working on next).
We need a way for SIPNET to handle exogenous changes to its internal state, such as crop rotation or land use changes.
The PEcAn standard way to describe these "events" is via
events.jsonfiles. PEcAn then converts these to model-specific input files.In ensemble simulations, we are expecting one
events.jsonfile per ensemble member.There is some machinery for stopping models, adjusting the state, and then restarting the model in the PEcAn state data assimilation code. We should see if we can adapt this first, before doing new implementation.
One consideration is whether the logic for restarting should be inside a
job.shfile or outside of it (i.e., in the parent process / calling code). The current SDA implementation seems to do the latter, modifyingjob.shat each restart step. Pseudocode for a job.sh file implementing the former approach might look something like this:(A cleverer implementation may be possible with loops / arrays, etc.).
Separately from this workflow, @Alomir noted that SIPNET does not currently have the ability to export a snapshot of its entire current state, which makes it tricky to "perfectly" restart SIPNET. We collectively agreed that the cleanest approach was to separate SIPNET time-series outputs for analysis (current
.outfiles) from "restart" files that contain a complete snapshot of SIPNET's state but only at the indicated timestep.Let me know if I missed anything!
All reactions