library(jsonlite)
library(readr)
library(stringr)
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# File paths
apsimx_file <- "D:/Mes Donnees/R/Biomass_Crops/Modeling/APSIM/apsimx_runs/BioCrops_Base200.apsimx"
apsimx_out  <- "D:/Mes Donnees/R/Biomass_Crops/Modeling/APSIM/apsimx_runs/BioCrops_Final200.apsimx"
par_dir     <- "D:/Mes Donnees/R/Biomass_Crops/Modeling/APSIM/soils_par"
weather_dir <- "D:/Mes Donnees/R/Biomass_Crops/Modeling/APSIM/weather"

# Helper: constrain values
clip_soil_layer <- function(x, lower = -Inf, upper = Inf) {
  x <- pmax(x, lower)
  x <- pmin(x, upper)
  return(x)
}

# Update soil node
update_soil_from_par <- function(existing_soil, par_path) {
  lines <- read_lines(par_path)
  data <- lines[!str_starts(lines, "\\*") & str_detect(lines, "=")]
  data <- str_split_fixed(data, "=", 2)
  df <- as.data.frame(data, stringsAsFactors = FALSE)
  colnames(df) <- c("key", "value")
  df <- df %>% mutate(key = tolower(trimws(key)), value = trimws(value))
  
  get_val <- function(k) df$value[df$key == tolower(k)]
  parse_num_vec <- function(k) {
    if (tolower(k) %in% df$key) {
      as.numeric(unlist(str_split(get_val(k), ",")))
    } else {
      NULL
    }
  }

  idx_physical <- which(sapply(existing_soil$Children, \(x) x$Name == "Physical"))
  if (length(idx_physical) == 1) {
    physical <- existing_soil$Children[[idx_physical]]
    if (!is.null(v <- parse_num_vec("dlayer")))  physical$Thickness <- v
    if (!is.null(v <- parse_num_vec("bd")))      physical$BD <- v
    if (!is.null(v <- parse_num_vec("airdry")))  physical$AirDry <- v
    if (!is.null(v <- parse_num_vec("ll15")))    physical$LL15 <- clip_soil_layer(v, lower = physical$AirDry)
    if (!is.null(v <- parse_num_vec("dul")))     physical$DUL  <- clip_soil_layer(v, lower = physical$LL15)
    if (!is.null(v <- parse_num_vec("sat")))     physical$SAT  <- clip_soil_layer(v, lower = physical$DUL)
    if (!is.null(v <- parse_num_vec("ks")))      physical$KS   <- v
    
    # Add sand and clay
    sand <- parse_num_vec("sand")
    clay <- parse_num_vec("clay")
    if (!is.null(sand)) physical$ParticleSizeSand <- sand
    if (!is.null(clay)) physical$ParticleSizeClay <- clay

    # Compute silt if sand and clay are present
    if (!is.null(sand) && !is.null(clay)) {
      silt <- 100 - sand - clay
      physical$ParticleSizeSilt <- clip_soil_layer(silt, lower = 0)
    }

    existing_soil$Children[[idx_physical]] <- physical
  }

  idx_organic <- which(sapply(existing_soil$Children, \(x) x$Name == "Organic"))
  if (length(idx_organic) == 1) {
    organic <- existing_soil$Children[[idx_organic]]
    if (!is.null(v <- parse_num_vec("carbon"))) {
      organic$Carbon <- v
      organic$Thickness <- parse_num_vec("dlayer")
      existing_soil$Children[[idx_organic]] <- organic
    }
  }

  idx_chemical <- which(sapply(existing_soil$Children, \(x) x$Name == "Chemical"))
  if (length(idx_chemical) == 1) {
    chemical <- existing_soil$Children[[idx_chemical]]
    if (!is.null(v <- parse_num_vec("ph")))  chemical$PH <- v
    if (!is.null(v <- parse_num_vec("cec"))) chemical$CEC <- v
    if (!is.null(v <- parse_num_vec("ec")))  chemical$EC <- v
    if (!is.null(v <- parse_num_vec("dlayer"))) chemical$Thickness <- v
    existing_soil$Children[[idx_chemical]] <- chemical
  }

  idx_water <- which(sapply(existing_soil$Children, \(x) x$Name == "Water"))
  if (length(idx_water) == 1 && exists("physical")) {
    iw <- existing_soil$Children[[idx_water]]
    if (
      !is.null(iw$InitialValues) &&
      is.numeric(iw$InitialValues) &&
      !is.null(physical$SAT) &&
      is.numeric(physical$SAT)
    ) {
      n <- min(length(iw$InitialValues), length(physical$SAT), length(physical$Thickness))
      if (n > 0) {
        iw$InitialValues <- iw$InitialValues[1:n]
        iw$InitialValues <- clip_soil_layer(iw$InitialValues, upper = physical$SAT[1:n])
        iw$Thickness <- physical$Thickness[1:n]
        existing_soil$Children[[idx_water]] <- iw
      }
    }
  }

  # Meta fields
  if (!is.null(v <- get_val("latitude")))  existing_soil$Latitude <- as.numeric(v)
  if (!is.null(v <- get_val("longitude"))) existing_soil$Longitude <- as.numeric(v)
  if (!is.null(v <- get_val("site")))      existing_soil$Site     <- v
  if (!is.null(v <- get_val("country")))   existing_soil$Country  <- v

  return(existing_soil)
}

# Load APSIMX file
apsim_json <- fromJSON(apsimx_file, simplifyVector = FALSE)

# Loop through all simulations
for (i in seq_along(apsim_json$Children)) {
  sim <- apsim_json$Children[[i]]
  if (sim$`$type` == "Models.Core.Simulation, Models") {
    sim_name <- sim$Name
    point_id <- str_extract(sim_name, "point\\d+")
    if (is.na(point_id)) next
    par_file <- file.path(par_dir, paste0(point_id, ".par"))
    weather_file <- file.path(weather_dir, paste0(point_id, ".met"))

    for (j in seq_along(sim$Children)) {
      if (sim$Children[[j]]$`$type` == "Models.Climate.Weather, Models") {
        sim$Children[[j]]$FileName <- weather_file
      }
    }

    existing_soil <- sim$Children[[6]]$Children[[3]]
    updated_soil <- update_soil_from_par(existing_soil, par_file)
    sim$Children[[6]]$Children[[3]] <- updated_soil
    apsim_json$Children[[i]] <- sim

    message(paste0("✅ Updated simulation: ", sim_name))
  }
}
## ✅ Updated simulation: Mungbean_point1
## ✅ Updated simulation: Mungbean_point2
## ✅ Updated simulation: Mungbean_point3
## ✅ Updated simulation: Mungbean_point4
## ✅ Updated simulation: Mungbean_point5
## ✅ Updated simulation: Mungbean_point6
## ✅ Updated simulation: Mungbean_point7
## ✅ Updated simulation: Mungbean_point8
## ✅ Updated simulation: Mungbean_point9
## ✅ Updated simulation: Mungbean_point10
## ✅ Updated simulation: Mungbean_point11
## ✅ Updated simulation: Mungbean_point12
## ✅ Updated simulation: Mungbean_point13
## ✅ Updated simulation: Mungbean_point14
## ✅ Updated simulation: Mungbean_point15
## ✅ Updated simulation: Mungbean_point16
## ✅ Updated simulation: Mungbean_point17
## ✅ Updated simulation: Mungbean_point18
## ✅ Updated simulation: Mungbean_point19
## ✅ Updated simulation: Mungbean_point20
## ✅ Updated simulation: Mungbean_point21
## ✅ Updated simulation: Mungbean_point22
## ✅ Updated simulation: Mungbean_point23
## ✅ Updated simulation: Mungbean_point24
## ✅ Updated simulation: Mungbean_point25
## ✅ Updated simulation: Mungbean_point26
## ✅ Updated simulation: Mungbean_point27
## ✅ Updated simulation: Mungbean_point28
## ✅ Updated simulation: Mungbean_point29
## ✅ Updated simulation: Mungbean_point30
## ✅ Updated simulation: Mungbean_point31
## ✅ Updated simulation: Mungbean_point32
## ✅ Updated simulation: Mungbean_point33
## ✅ Updated simulation: Mungbean_point34
## ✅ Updated simulation: Mungbean_point35
## ✅ Updated simulation: Mungbean_point36
## ✅ Updated simulation: Mungbean_point37
## ✅ Updated simulation: Mungbean_point38
## ✅ Updated simulation: Mungbean_point39
## ✅ Updated simulation: Mungbean_point40
## ✅ Updated simulation: Mungbean_point41
## ✅ Updated simulation: Mungbean_point42
## ✅ Updated simulation: Mungbean_point43
## ✅ Updated simulation: Mungbean_point44
## ✅ Updated simulation: Mungbean_point45
## ✅ Updated simulation: Mungbean_point46
## ✅ Updated simulation: Mungbean_point47
## ✅ Updated simulation: Mungbean_point48
## ✅ Updated simulation: Mungbean_point49
## ✅ Updated simulation: Mungbean_point50
## ✅ Updated simulation: Mungbean_point51
## ✅ Updated simulation: Mungbean_point52
## ✅ Updated simulation: Mungbean_point53
## ✅ Updated simulation: Mungbean_point54
## ✅ Updated simulation: Mungbean_point55
## ✅ Updated simulation: Mungbean_point56
## ✅ Updated simulation: Mungbean_point57
## ✅ Updated simulation: Mungbean_point58
## ✅ Updated simulation: Mungbean_point59
## ✅ Updated simulation: Mungbean_point60
## ✅ Updated simulation: Mungbean_point61
## ✅ Updated simulation: Mungbean_point62
## ✅ Updated simulation: Mungbean_point63
## ✅ Updated simulation: Mungbean_point64
## ✅ Updated simulation: Mungbean_point65
## ✅ Updated simulation: Mungbean_point66
## ✅ Updated simulation: Mungbean_point67
## ✅ Updated simulation: Mungbean_point68
## ✅ Updated simulation: Mungbean_point69
## ✅ Updated simulation: Mungbean_point70
## ✅ Updated simulation: Mungbean_point71
## ✅ Updated simulation: Mungbean_point72
## ✅ Updated simulation: Mungbean_point73
## ✅ Updated simulation: Mungbean_point74
## ✅ Updated simulation: Mungbean_point75
## ✅ Updated simulation: Mungbean_point76
## ✅ Updated simulation: Mungbean_point77
## ✅ Updated simulation: Mungbean_point78
## ✅ Updated simulation: Mungbean_point79
## ✅ Updated simulation: Mungbean_point80
## ✅ Updated simulation: Mungbean_point81
## ✅ Updated simulation: Mungbean_point82
## ✅ Updated simulation: Mungbean_point83
## ✅ Updated simulation: Mungbean_point84
## ✅ Updated simulation: Mungbean_point85
## ✅ Updated simulation: Mungbean_point86
## ✅ Updated simulation: Mungbean_point87
## ✅ Updated simulation: Mungbean_point88
## ✅ Updated simulation: Mungbean_point89
## ✅ Updated simulation: Mungbean_point90
## ✅ Updated simulation: Mungbean_point91
## ✅ Updated simulation: Mungbean_point92
## ✅ Updated simulation: Mungbean_point93
## ✅ Updated simulation: Mungbean_point94
## ✅ Updated simulation: Mungbean_point95
## ✅ Updated simulation: Mungbean_point96
## ✅ Updated simulation: Mungbean_point97
## ✅ Updated simulation: Mungbean_point98
## ✅ Updated simulation: Mungbean_point99
## ✅ Updated simulation: Mungbean_point100
## ✅ Updated simulation: Mungbean_point101
## ✅ Updated simulation: Mungbean_point102
## ✅ Updated simulation: Mungbean_point103
## ✅ Updated simulation: Mungbean_point104
## ✅ Updated simulation: Mungbean_point105
## ✅ Updated simulation: Mungbean_point106
## ✅ Updated simulation: Mungbean_point107
## ✅ Updated simulation: Mungbean_point108
## ✅ Updated simulation: Mungbean_point109
## ✅ Updated simulation: Mungbean_point110
## ✅ Updated simulation: Mungbean_point111
## ✅ Updated simulation: Mungbean_point112
## ✅ Updated simulation: Mungbean_point113
## ✅ Updated simulation: Mungbean_point114
## ✅ Updated simulation: Mungbean_point115
## ✅ Updated simulation: Mungbean_point116
## ✅ Updated simulation: Mungbean_point117
## ✅ Updated simulation: Mungbean_point118
## ✅ Updated simulation: Mungbean_point119
## ✅ Updated simulation: Mungbean_point120
## ✅ Updated simulation: Mungbean_point121
## ✅ Updated simulation: Mungbean_point122
## ✅ Updated simulation: Mungbean_point123
## ✅ Updated simulation: Mungbean_point124
## ✅ Updated simulation: Mungbean_point125
## ✅ Updated simulation: Mungbean_point126
## ✅ Updated simulation: Mungbean_point127
## ✅ Updated simulation: Mungbean_point128
## ✅ Updated simulation: Mungbean_point129
## ✅ Updated simulation: Mungbean_point130
## ✅ Updated simulation: Mungbean_point131
## ✅ Updated simulation: Mungbean_point132
## ✅ Updated simulation: Mungbean_point133
## ✅ Updated simulation: Mungbean_point134
## ✅ Updated simulation: Mungbean_point135
## ✅ Updated simulation: Mungbean_point136
## ✅ Updated simulation: Mungbean_point137
## ✅ Updated simulation: Mungbean_point138
## ✅ Updated simulation: Mungbean_point139
## ✅ Updated simulation: Mungbean_point140
## ✅ Updated simulation: Mungbean_point141
## ✅ Updated simulation: Mungbean_point142
## ✅ Updated simulation: Mungbean_point143
## ✅ Updated simulation: Mungbean_point144
## ✅ Updated simulation: Mungbean_point145
## ✅ Updated simulation: Mungbean_point146
## ✅ Updated simulation: Mungbean_point147
## ✅ Updated simulation: Mungbean_point148
## ✅ Updated simulation: Mungbean_point149
## ✅ Updated simulation: Mungbean_point150
## ✅ Updated simulation: Mungbean_point151
## ✅ Updated simulation: Mungbean_point152
## ✅ Updated simulation: Mungbean_point153
## ✅ Updated simulation: Mungbean_point154
## ✅ Updated simulation: Mungbean_point155
## ✅ Updated simulation: Mungbean_point156
## ✅ Updated simulation: Mungbean_point157
## ✅ Updated simulation: Mungbean_point158
## ✅ Updated simulation: Mungbean_point159
## ✅ Updated simulation: Mungbean_point160
## ✅ Updated simulation: Mungbean_point161
## ✅ Updated simulation: Mungbean_point162
## ✅ Updated simulation: Mungbean_point163
## ✅ Updated simulation: Mungbean_point164
## ✅ Updated simulation: Mungbean_point165
## ✅ Updated simulation: Mungbean_point166
## ✅ Updated simulation: Mungbean_point167
## ✅ Updated simulation: Mungbean_point168
## ✅ Updated simulation: Mungbean_point169
## ✅ Updated simulation: Mungbean_point170
## ✅ Updated simulation: Mungbean_point171
## ✅ Updated simulation: Mungbean_point172
## ✅ Updated simulation: Mungbean_point173
## ✅ Updated simulation: Mungbean_point174
## ✅ Updated simulation: Mungbean_point175
## ✅ Updated simulation: Mungbean_point176
## ✅ Updated simulation: Mungbean_point177
## ✅ Updated simulation: Mungbean_point178
## ✅ Updated simulation: Mungbean_point179
## ✅ Updated simulation: Mungbean_point180
## ✅ Updated simulation: Mungbean_point181
## ✅ Updated simulation: Mungbean_point182
## ✅ Updated simulation: Mungbean_point183
## ✅ Updated simulation: Mungbean_point184
## ✅ Updated simulation: Mungbean_point185
## ✅ Updated simulation: Mungbean_point186
## ✅ Updated simulation: Mungbean_point187
## ✅ Updated simulation: Mungbean_point188
## ✅ Updated simulation: Mungbean_point189
## ✅ Updated simulation: Mungbean_point190
## ✅ Updated simulation: Mungbean_point191
## ✅ Updated simulation: Mungbean_point192
## ✅ Updated simulation: Mungbean_point193
## ✅ Updated simulation: Mungbean_point194
## ✅ Updated simulation: Mungbean_point195
## ✅ Updated simulation: Mungbean_point196
## ✅ Updated simulation: Mungbean_point197
## ✅ Updated simulation: Mungbean_point198
## ✅ Updated simulation: Mungbean_point199
## ✅ Updated simulation: Mungbean_point200
# Write the modified APSIMX
json_text <- toJSON(apsim_json, pretty = TRUE, auto_unbox = TRUE, null = "null")
writeLines(json_text, apsimx_out)

cat("\n🎉 All simulations updated successfully!\n")
## 
## 🎉 All simulations updated successfully!
cat("📁 Updated APSIMX saved to:\n", apsimx_out, "\n")
## 📁 Updated APSIMX saved to:
##  D:/Mes Donnees/R/Biomass_Crops/Modeling/APSIM/apsimx_runs/BioCrops_Final200.apsimx