My personal function works independently but not when I call it in my overall code

Hello everyone,

I am using R to create my calibration code for my research project using the model called SWAT+. My code takes samples 27 different parameters and then replaces the old values for those parameters with the new ones and then runs SWAT+, and does this 1,000 times. SWAT+ holds it's data in different text files, and for it to run correctly the code must replace the original values with new values without changing the formatting of the text files. If the formatting is off by even a little, SWAT won't run.
My function of interest, "SWAT_SOILSK_Mod", successfully replaced the soil_k value in the text file called soils.sol when I test it independently. Soils.sol contains all the information for my different soil types. I've attached an example of it being successful. However, when I call on the function in my overall code, called "SWAT_Calibration" it doesn't work and messes up the file. I've also attached an example of this. In the overall code there is another function that edits the same text file called "SWAT_SOILSAWC_Mod" and when I ran the functions myself sequentially there was no issue. There are no other functions that edit this text file. My SOILSAWC_MOD replaces the the soils.sol with a template that is the exact same as soils.sol, but with different awc values, so the formatting is exactly the same. Does anyone know why my function does not work when called on by the overall code? There are no mixups in variable arguments. Since I'm a new user and I can only add one piece of media, I've pasted my functions below and attached a picture of the correct and broken soils.sol files. Since my overall code is so large, I only included the section that calls on the function of interest and a little extra. I've tried to use ChatGPT to help solve my problem, but I've been at it for a week and made no progress.

Thank you for you attention and help.
Please let me know if more information is needed.

SWAT_SOILSK_MOD:

SWAT_SOILSK_Mod <- function(path,template,k_mod_value) {

  #path = paste0("D:/SWAT+/Calibration/SWAT_Watershed_Tool_NoLake/", watershed, "/TxtInOut")
  soils_file <- file.path(path, "soils.sol")
 
  
  # 1. Read soils.sol
  soils <- readLines(soils_file, warn = FALSE)
  
  # Layer lines = indented numeric rows that start with dp (e.g. "    25.40000")
  # Soil header lines start with "33", "15", etc. (no decimal) and are skipped.
  layer_idx <- grep("^\\s*[0-9]+\\.[0-9]{5}", soils)
  
  # 2. Read original soil_k table and keep only layer rows
  #    (rows with numeric dp; header rows have NA in dp, isolates where the soil_k will be inserted)
  soil_tbl  = read.csv(paste(template,"SWAT_SOILSK_Table.csv",sep=""))
  
  # Trim column names so they become "dp", "bd", "awc", "soil_k", etc.
  names(soil_tbl) <- trimws(names(soil_tbl))
  
  # Keep only layer rows (dp not NA)
  soil_layers <- subset(soil_tbl, !is.na(dp))
  
  # Sanity check: number of CSV layer rows vs number of layer lines in soils.sol
  if (nrow(soil_layers) != length(layer_idx)) {
    stop(sprintf(
      "Mismatch: %d layer lines in soils.sol but %d rows in SWAT_SoilSK_Table.csv",
      length(layer_idx), nrow(soil_layers)
    ))
  }
  
  # 3. Build new soil_k values
  k_new <- soil_layers$soil_k * k_mod_value
  
  # Enforce 12–1607 max and min bounds
  k_new <- pmax(12, pmin(1607, k_new))
  
  # Format exactly 10 characters wide with 5 decimals
  k_new_fmt <- sprintf("%10.5f", k_new)
  

  # 4. Replace ONLY the soil_k field in-place on each layer line
  #   soil_k = cols 169–178 (10 characters wide)
  soil_start <- 169L            # first character of soil_k
  soil_width <- 10L
  soil_end   <- soil_start + soil_width - 1L
  
  for (i in seq_along(layer_idx)) {
    line <- soils[layer_idx[i]]
    
    # Replace only the soil_k field; everything else stays identical
    substr(line, soil_start, soil_end) <- k_new_fmt[i]
    
    soils[layer_idx[i]] <- line
  }
  
  # 5. Backup and write modified soils.sol-
  backup_file <- file.path(path, "soils_before_SOILK_MOD.sol")
  file.copy(soils_file, backup_file, overwrite = TRUE)
  
  writeLines(soils, soils_file, useBytes = TRUE)
  
  invisible(NULL)
}

SWAT_SOILSAWC_MOD:

SWAT_SOILSAWC_Mod <- function(path,template,AWC_Value) {
  
  awc <- sprintf("%07.5f", AWC_Value)
  
  #Review Note: put paths in dynamically. Will avoid simple mistakes
  awctable <- readLines(paste(template,"soils-awc_template.txt",sep="")) |>
    stringr::str_replace_all(c("changme" = awc))
  writeLines(awctable, con = paste(path,"/soils.sol",sep=""))
}

Part of SWAT_Calibration:

#Run for loop to run calibration
for(j in restart:iterations){
  
  #1a. Resample AWC
  Output$awc[j] <- runif(1, min = 0, max = 0.7)
  SWAT_SOILSAWC_Mod(txtinoutpath,template,Output$awc[j])

  #1b. Resample Soil K
  Output$k_mult[j] <- runif(1, min = 0.5, max = 1.5)
  SWAT_SOILSK_Mod(txtinoutpath, template,Output$soilk_mult[j])
  
  
  #2. Resample bf_max, flo_min, NO3_N, and HL_NO3N
  Output$no3_n[j] <- runif(1, min = 0, max = 1000)
  Output$bf_max[j] <- runif(1, min = 0.5, max = 2)
  Output$hl_no3n[j] <- runif(1, min = 0, max = 200)
  Output$flo_min[j]  <- runif(1, min = 0.1, max = 10)
  SWAT_AQUIFER_Mod(txtinoutpath,template,Output$no3_n[j],Output$bf_max[j],Output$hl_no3n[j],Output$flo_min[j])
  
  #3a. Resample bed_K
  Output$bed_K[j] <- runif(1, min = 0, max = 500)
  SWAT_CHANHYD_Mod(txtinoutpath,template,Output$bed_K[j])
  
  #3b. Resample channel nutrient parameters
  Output$plt_n[j] <- runif(1, min = 0, max = 100)
  Output$ben_nh3n[j] <- runif(1, min = 0, max = 1)
  Output$ptln_stl[j] <- runif(1, min = 0.001, max = 0.1)
  Output$nh3n_no2n[j]  <- runif(1, min = 0.1, max = 1)
  Output$no2n_no3n[j] <- runif(1, min = 0.2, max = 2)
  Output$ptln_nh3n[j] <- runif(1, min = 0.2, max = 0.4)
  SWAT_CHANNUTR_Mod(txtinoutpath,template,Output$plt_n[j],Output$ben_nh3n[j],Output$ptln_stl[j],Output$nh3n_no2n[j],Output$no2n_no3n[j],Output$ptln_nh3n[j])
  
  #4. Resample SWAT Curve Number
  Output$cn2_mult[j] <- runif(1, min = 0.5, max = 1.5)
  SWAT_CN_Mod(txtinoutpath,template,Output$cn2_mult[j])
  
  #5. Resample SWAT Hydrology
  Output$lat_time[j] <- runif(1,1,180)
  Output$esco[j] <- runif(1, min = 0.01, max = 1)
  Output$epco[j] <- runif(1, min = 0.01, max = 1)
  Output$orgn_enrich[j] <- runif(1, min = 0, max = 1)
  SWAT_HYDROLOGY_Mod(txtinoutpath,template,Output$lat_time[j],Output$esco[j],Output$epco[j],Output$orgn_enrich[j])
  
  #6. Resample snowmelt parameters
  Output$fall_tmp[j] <- runif(1, min = -5, max = 5)
  Output$melt_tmp[j] <- runif(1, min = -5, max = 5)
  Output$melt_max[j] <- runif(1, min = 0, max = 10)
  Output$melt_min[j] <- runif(1, min = 0, max = Output$melt_max[j])
  Output$melt_lag[j] <- runif(1, min = 0.01, max = 1)
  SWAT_SNOW_Mod(txtinoutpath,template,Output$fall_tmp[j],Output$melt_tmp[j],Output$melt_max[j],Output$melt_min[j],Output$melt_lag[j])
  
  #7. Resample surface lag and nutrient parameters
  Output$surlag[j] <- runif(1, min = 0.05, max = 24)
  Output$orgn_min[j] <- runif(1, min = 0.00001, max = 0.06)
  Output$n_uptake[j] <- runif(1, min = 0, max = 100)
  Output$n_perc[j] <- runif(1, min = 0.01, max = 1)
  Output$rsd_decomp[j] <- runif(1, min = 0.02, max = 0.1)
  Output$denit_exp[j] <- runif(1, min = 0, max = 3)
  Output$denit_frac[j] <- runif(1, min = 0, max = 1)
  SWAT_BASIN_Mod(txtinoutpath,template,Output$surlag[j],Output$orgn_min[j],Output$n_uptake[j],Output$n_perc[j],
                 Output$rsd_decomp[j],Output$denit_exp[j],Output$denit_frac[j])

  #7. Run SWAT Model
  system(paste(txtinoutpath,"/swatplus-61.0.2-ifx-win_amd64-Rel.exe",sep=""), intern = TRUE)
  Sys.sleep(0.5)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.