Skip to content

Commit

Permalink
added SEALS allocation script
Browse files Browse the repository at this point in the history
  • Loading branch information
pvjeetze committed Sep 18, 2024
1 parent 3163bfa commit 23ca6a7
Show file tree
Hide file tree
Showing 5 changed files with 408 additions and 11 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Imports:
m4fsdp,
madrat,
magclass (>= 6.14.0),
magpie4 (>= 2.11.0),
magpie4 (>= 2.12.0),
MagpieNCGains,
magpiesets,
mip,
Expand Down
21 changes: 12 additions & 9 deletions config/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ cfg$gms$kfo_rd <- "livst_rum" #def = livst_rum

# * Switch for supplemental fat needed as ingredient for scp-based meat alternatives
# * options: 0 (=off), 1 (=on)
cfg$gms$s15_scp_supplement_fat_meat <- 0 # def = 0
cfg$gms$s15_scp_supplement_fat_meat <- 0 # def = 0

# * Convergence of livestock food calorie supply towards a kcal/cap/day target with or w/o substitution.
# * The functional form and the start and target years of the food substitution can be specified below.
Expand Down Expand Up @@ -695,7 +695,7 @@ cfg$gms$ageclass <- "feb21" # def = feb21
# ***--------------------- 29_cropland -----------------------------------
# * Cropland is defined as the sum of croparea, fallow land and tree cover
# * Croparea is provided by 30_crop.
# * Fallow land and tree cover are defined by 29_cropland,
# * Fallow land and tree cover are defined by 29_cropland,
# * (simple_apr24): Fallow land and tree cover on cropland are fixed to zero
# * (detail_apr24): Fallow land and tree cover based on rules or incentives
# NOTE: It is recommended to recalibrate the model when changing this setting!
Expand Down Expand Up @@ -737,7 +737,7 @@ cfg$gms$s29_snv_scenario_target <- 2050 # def = 2050
# * plausible options: "secdforest, forestry, past, other",
# * "secdforest, other",
# * "secdforest, past, other" etc.
cfg$gms$land_snv <- "secdforest, forestry, past, other" #def = "secdforest, forestry, past, other"
cfg$gms$land_snv <- "secdforest, other" #def = "secdforest, other"

# *** Options only available for `detail_apr24` realization ***

Expand All @@ -755,7 +755,7 @@ cfg$gms$s29_treecover_map <- 0 # def = 0
# s29_treecover_target_noselect applies to all other countries.
cfg$gms$s29_treecover_target <- 0 # def = 0
cfg$gms$s29_treecover_target_noselect <- 0 # def = 0
# * Avoid loss of existing treecover (1=yes 0=no).
# * Avoid loss of existing treecover (1=yes 0=no).
# * If set to 1, `s29_treecover_target` will be adjusted based existing treecover area.
cfg$gms$s29_treecover_keep <- 0 # def = 0
# * Maximum share of treecover on total cropland (1)
Expand Down Expand Up @@ -808,12 +808,12 @@ cfg$gms$policy_countries30 <- all_iso_countries
## Agroforestry settings for bioenergy trees
# * Sigmoid fader for minimum area share of bioenergy trees (betr) on total cropland at cluster level
# * Minimum area share of bioenergy trees on total cropland in start year
# Note: s30_betr_start applies to countries selected in policy_countries30,
# Note: s30_betr_start applies to countries selected in policy_countries30,
# s30_betr_start_noselect applies to all other countries.
cfg$gms$s30_betr_start <- 0 # def = 0
cfg$gms$s30_betr_start_noselect <- 0 # def = 0
# * Minimum area share of bioenergy trees on total cropland in target year
# Note: s30_betr_target applies to countries selected in policy_countries30,
# Note: s30_betr_target applies to countries selected in policy_countries30,
# s30_betr_target_noselect applies to all other countries.
cfg$gms$s30_betr_target <- 0 # def = 0
cfg$gms$s30_betr_target_noselect <- 0 # def = 0
Expand Down Expand Up @@ -841,14 +841,14 @@ cfg$gms$s30_implementation <- 1 # def = 1
# * Rotation rules:
# * min (minimal constraints), default (best guess), good (good practice),
# * good_20div (good practice - 20% for other crops), setaside (default plus fallow),
# * legumes (minimum share of legumes), sixfoldrotation (crops can only repeat after 6 years),
# * legumes (minimum share of legumes), sixfoldrotation (crops can only repeat after 6 years),
# * agroecology (mix of previous scenarios), FSEC (similar to agroecology)
# * betr0 (minimum share of short rotation agroforestry trees), betr10, betr20, betr25, betr30, betr40, betr50.
cfg$gms$c30_rotation_rules <- "default" # def = "default"

# * Rotation incentives:
# * none (no incentives), default (best guess),
# * legumes (increased incentives for legumes), agroecology (mix),
# * none (no incentives), default (best guess),
# * legumes (increased incentives for legumes), agroecology (mix),
# * betr500 and betr1000 (incentives for short rotation agroforestry trees)
cfg$gms$c30_rotation_incentives <- "none" # def = "none"

Expand Down Expand Up @@ -2072,6 +2072,9 @@ cfg$gms$s80_secondsolve <- 0 # def = 0
# bjoernAR6_C_SSP2-NPi.mif, bjoernAR6_C_SSP2-PkBudg900.mif
cfg$magicc_emis_scen <- "bjoernAR6_C_SSP2-NDC.mif"

# Years in which SEALS downscaling is applied
cfg$seals_years <- c(2020, 2050)

# Decide whether the runs should be run sequentially (TRUE),
# or in parallel (FALSE)
# NA means that this decision is taken automatically
Expand Down
1 change: 0 additions & 1 deletion scripts/output/extra/disaggregation.R
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ if (length(map_file) > 1) {
base::message(message)
x <- .fixCoords(x)
write.magpie(x, file, comment = comment)
write.magpie(x, sub(".mz", ".nc", file), comment = comment)
}

.dissagcrop <- function(gdx, land_hr, map_file) {
Expand Down
231 changes: 231 additions & 0 deletions scripts/output/extra/runSEALSallocation.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# | (C) 2008-2024 Potsdam Institute for Climate Impact Research (PIK)
# | authors, and contributors see CITATION.cff file. This file is part
# | of MAgPIE and licensed under AGPL-3.0-or-later. Under Section 7 of
# | AGPL-3.0, you are granted additional permissions described in the
# | MAgPIE License Exception, version 1.0 (see LICENSE file).
# | Contact: [email protected]

# --------------------------------------------------------------
# description: Starts SEALS allocation run based on gridded MAgPIE land cover projections (SEALS)
# comparison script: FALSE
# ---------------------------------------------------------------

# Version 1.00 - Patrick v. Jeetze, Pascal Sauer
# 1.00: first working version

library(gms)
library(gdx)
library(magpie4)
library(filelock)

message("Initialising SEALS allocation run")

############################# BASIC CONFIGURATION #######################################
if (!exists("source_include")) {
title <- NULL
outputdir <- NULL

# Define arguments that can be read from command line
readArgs("outputdir", "title")
}
#########################################################################################

# ========================
# User settings
# ========================

### SEALS python environment name
# see https://justinandrewjohnson.com/earth_economy_devstack/installation.html
# for instructions on how to set up a python environment for SEALS
sealsEnv <- "seals_dev"

### Path to miniforge installation
# For instructions on how to install miniforge see https://github.com/conda-forge/miniforge
miniforgePath <- "/home/vjeetze/miniforge3/bin/activate"

### Path to SEALS base input file directory
dirBaseFiles <- "/p/projects/magpie/users/vjeetze/seals/files"

### Path to SEALS code base
dirSEALS <- "/p/projects/magpie/users/vjeetze/seals/files/seals/seals_dev"


# ========================
# Prepare MAgPIE output
# ========================

message("Script started for output directory: ", outputdir)

cfg <- gms::loadConfig(file.path(outputdir, "config.yml"))
title <- cfg$title

if (length(cfg$seals_years) != 0) {
rep_years <- cfg$seals_years[cfg$seals_years > 2020]
rep_years <- c(2020, rep_years)
} else {
rep_years <- seq(2020, 2050, 5)
}

# Restructure data to conform to SEALS
reportLandUseForSEALS(
magCellLand = "cell.land_0.5_share.mz",
outFile = paste0("cell.land_0.5_SEALS_", title, ".nc"),
dir = outputdir, selectyears = rep_years
)


# ========================
# Setup SEALS run
# ========================

# --------------------------------
# Prepare SEALS start script
# --------------------------------

.setupSEALSrun <- function(title, dir, dirProject, dirSEALS, dirBaseFiles) {
if (!dir.exists(file.path(dirProject, "scripts"))) {
dir.create(file.path(dirProject, "scripts"), recursive = TRUE)
}

file.copy(
from = list.files(file.path(dirSEALS, "seals"), full.names = TRUE),
to = file.path(dirProject, "scripts"),
overwrite = TRUE,
recursive = TRUE
)

if (!dir.exists(file.path(dirProject, "inputs"))) {
dir.create(file.path(dirProject, "inputs"), recursive = TRUE)
}

file.copy(
from = file.path(dir, paste0("seals_scenario_config_", title, ".csv")),
to = file.path(dirProject, "inputs", paste0("seals_scenario_config_", title, ".csv")),
overwrite = TRUE
)

main <- readLines(file.path(dirProject, "scripts/run_test_standard.py"))

main[min(which(grepl(" p.user_dir =", main)))] <- paste0(" p.user_dir = \'", dirBaseFiles, "\'")
main[min(which(grepl(" p.extra_dirs", main)))] <- paste0(" p.extra_dirs = '.'")
main[min(which(grepl(" p.project_name =", main)))] <- paste0(" p.project_name = \'", title, "\'")
main[min(which(grepl(" p.project_dir =", main)))] <- paste0(
" p.project_dir = \'", normalizePath(dirProject), "\'"
)
main[min(which(grepl("hb.pretty_time()", main)))] <- " "
main[min(which(grepl(" p.base_data_dir =", main)))] <- paste0(
" p.base_data_dir = \'", dirBaseFiles, "/base_data\'"
)
main[min(which(grepl(" p.scenario_definitions_filename =", main)))] <- paste0(
" p.scenario_definitions_filename = \'", paste0("seals_scenario_config_", title, ".csv"), "\'"
)

writeLines(main, file.path(dirProject, "scripts", paste0("run_seals_", title, ".py")))


if (!dir.exists(file.path(dirProject, "run_submit"))) {
dir.create(file.path(dirProject, "run_submit"), recursive = TRUE)
}
}

# --------------------------------
# Submit run
# --------------------------------

.submitSEALS <- function(title, dirProject, miniforgePath, sealsEnv, qos = "short",
slurmID = FALSE, dependsID = NULL) {
submitFile <- file.path(dirProject, "run_submit", paste0("submit_seals_", title, ".sh"))

submit <- c(
"#!/bin/bash", "\n",
paste0("#SBATCH --qos=", qos),
ifelse(qos %in% c("priority", "standby"), "#SBATCH --partition=priority", "#SBATCH --partition=standard"),
"#SBATCH --job-name=seals_allocation",
paste0("#SBATCH --chdir=", normalizePath(file.path(dirProject, "scripts"))),
"#SBATCH --output=outfile_%j.out",
"#SBATCH --error=outfile_%j.err",
"#SBATCH --mail-type=END,FAIL",
"#SBATCH --time=3:00:00",
"#SBATCH --nodes=1",
"#SBATCH --ntasks=1",
ifelse(!is.null(dependsID), paste0(
"#SBATCH --dependency=afterok:", dependsID,
"\n#SBATCH --kill-on-invalid-dep=yes"
),
"#SBATCH --dependency=singleton"
), "\n",
ifelse(qos == "priority", "#SBATCH --cpus-per-task=64", "#SBATCH --cpus-per-task=128"), "\n",
paste("source", miniforgePath),
paste("conda activate", sealsEnv), "\n",
paste("python", paste0("run_seals_", title, ".py"))
)
writeLines(submit, submitFile)

id <- system(paste("sbatch --parsable", submitFile), intern = TRUE)
if (slurmID) {
return(id)
}
}

# create output directory
dirProject <- "./output/seals"

if (!dir.exists(file.path(dirProject))) {
dir.create(file.path(dirProject), recursive = TRUE)
}

iniLock <- file.path(dirProject, ".lock")
lockOn <- filelock::lock(iniLock, exclusive = TRUE, timeout = Inf)
Sys.chmod(iniLock, mode = "0664")

# --------------------------------
# Run SEALS allocation
# --------------------------------

if (!is.null(lockOn)) {
sealsLock <- file.path(dirProject, "seals.lock")

if (!file.exists(sealsLock) || file.size(sealsLock) == 0) {
message("Starting SEALS allocation with input data creation.")
.setupSEALSrun(
title = title,
dir = outputdir,
dirProject = dirProject,
dirSEALS = dirSEALS,
dirBaseFiles = dirBaseFiles
)

id <- .submitSEALS(
title = title,
dirProject = dirProject,
miniforgePath = miniforgePath,
sealsEnv = sealsEnv,
qos = "short",
slurmID = TRUE
)

writeLines(id, sealsLock)
} else {
message("Starting SEALS allocation using existing input data.")
id <- readLines(sealsLock)

.setupSEALSrun(
title = title,
dir = outputdir,
dirProject = dirProject,
dirSEALS = dirSEALS,
dirBaseFiles = dirBaseFiles
)

.submitSEALS(
title = title,
dirProject = dirProject,
miniforgePath = miniforgePath,
sealsEnv = sealsEnv,
qos = "short",
slurmID = FALSE,
dependsID = id
)
}
unlink(iniLock)
}
Loading

0 comments on commit 23ca6a7

Please sign in to comment.