Skip to content

Commit a1eda48

Browse files
authored
Merge pull request #54 from ApexRMS/ha-update-enmeval-version
Ha fixing bugs with conda environment and new ENMeval version
2 parents 94f2cf9 + 49cd813 commit a1eda48

4 files changed

Lines changed: 86 additions & 33 deletions

File tree

src/3-postprocess.R

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,19 @@ if (nrow(ruleReclassDataframe) != 0) {
215215
# Rules
216216
for (i in seq_len(nrow(ruleReclassDataframe))) {
217217

218+
# Disable disk-backing for this iteration
219+
old_todisk <- terraOptions()$todisk
220+
terraOptions(todisk = FALSE)
221+
218222
# Load rule raster
219223
rulePath <- ruleReclassDataframe$ruleRasterFile[i]
220224
if (length(rulePath) == 0 || is.na(rulePath) || !file.exists(rulePath)) {
221225
updateRunLog(
222226
paste0("Rule raster missing for rule index ", i, "; skipping."),
223227
type = "warning"
224228
)
229+
# Restore before next
230+
terraOptions(todisk = old_todisk)
225231
next
226232
}
227233
ruleRaster <- rast(rulePath)
@@ -232,6 +238,8 @@ if (nrow(ruleReclassDataframe) != 0) {
232238
"Rule raster extent does not match unfiltered training raster extent. Skipping reclassification.",
233239
type = "warning"
234240
)
241+
# Restore before next
242+
terraOptions(todisk = old_todisk)
235243
next
236244
}
237245

@@ -244,6 +252,8 @@ if (nrow(ruleReclassDataframe) != 0) {
244252
paste0("Rule values (min/max/reclass) contain NA for rule index ", i, "; skipping."),
245253
type = "warning"
246254
)
255+
# Restore before next
256+
terraOptions(todisk = old_todisk)
247257
next
248258
}
249259
if (vmin > vmax) {
@@ -285,6 +295,8 @@ if (nrow(ruleReclassDataframe) != 0) {
285295
rm(classedMask); gc()
286296
}
287297

298+
# Restore setting at end of iteration
299+
terraOptions(todisk = old_todisk)
288300
gc()
289301
}
290302

@@ -362,13 +374,19 @@ if (nrow(ruleReclassDataframe) != 0) {
362374
# Apply rules (if any)
363375
for (i in seq_len(nrow(ruleReclassDataframe))) {
364376

377+
# Disable disk-backing for this iteration
378+
old_todisk <- terraOptions()$todisk
379+
terraOptions(todisk = FALSE)
380+
365381
# Load rule raster
366382
rulePath <- ruleReclassDataframe$ruleRasterFile[i]
367383
if (length(rulePath) == 0 || is.na(rulePath) || !file.exists(rulePath)) {
368384
updateRunLog(
369385
paste0("Rule raster missing for rule index ", i, "; skipping."),
370386
type = "warning"
371387
)
388+
# Restore before next
389+
terraOptions(todisk = old_todisk)
372390
next
373391
}
374392
ruleRaster <- rast(rulePath)
@@ -379,6 +397,8 @@ if (nrow(ruleReclassDataframe) != 0) {
379397
"Rule raster extent does not match predicting raster extent. Skipping reclassification.",
380398
type = "warning"
381399
)
400+
# Restore before next
401+
terraOptions(todisk = old_todisk)
382402
next
383403
}
384404

@@ -391,6 +411,8 @@ if (nrow(ruleReclassDataframe) != 0) {
391411
paste0("Rule values (min/max/reclass) contain NA for rule index ", i, "; skipping."),
392412
type = "warning"
393413
)
414+
# Restore before next
415+
terraOptions(todisk = old_todisk)
394416
next
395417
}
396418
if (vmin > vmax) {
@@ -432,6 +454,8 @@ if (nrow(ruleReclassDataframe) != 0) {
432454
rm(classedMask); gc()
433455
}
434456

457+
# Restore setting at end of iteration
458+
terraOptions(todisk = old_todisk)
435459
gc()
436460
}
437461

src/functions/0.4-CNN.r

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ predictCNN <- function(model, newdata, isRaster = TRUE, filename = "", memfrac =
314314
return(mat[, "presence"])
315315
}
316316

317-
# Handle raster data - use terra::predict for chunked processing
317+
# Store metadata in local variables
318318
num_vars <- model$num_vars
319319
cat_vars <- model$cat_vars
320320
cat_levels <- model$cat_levels
@@ -326,24 +326,24 @@ predictCNN <- function(model, newdata, isRaster = TRUE, filename = "", memfrac =
326326

327327
# Create wrapper function for terra::predict
328328
# This function will be called many times (once per chunk)
329-
predictFn <- function(model, data, ...) {
329+
predictFn <- function(m, data, ...) {
330330
# Validate column presence
331-
missing_vars <- setdiff(model$num_vars, names(data))
331+
missing_vars <- setdiff(num_vars, names(data))
332332
if (length(missing_vars) > 0) {
333333
stop(sprintf("Missing numeric predictors: %s", paste(missing_vars, collapse = ", ")))
334334
}
335335

336336
# Prepare numeric data
337-
X_num <- as.matrix(data[, model$num_vars, drop = FALSE])
337+
X_num <- as.matrix(data[, num_vars, drop = FALSE])
338338
storage.mode(X_num) <- "double"
339339

340340
# Handle categorical variables
341-
if (length(model$cat_vars) == 0) {
341+
if (length(cat_vars) == 0) {
342342
X_cat_tensor <- list()
343343
} else {
344-
X_cat <- lapply(seq_along(model$cat_vars), function(i) {
345-
var <- model$cat_vars[i]
346-
levels_train <- model$cat_levels[[i]]
344+
X_cat <- lapply(seq_along(cat_vars), function(i) {
345+
var <- cat_vars[i]
346+
levels_train <- cat_levels[[i]]
347347
x <- data[[var]]
348348
if (!is.factor(x)) x <- factor(x, levels = levels_train)
349349

src/functions/0.4-maxent.r

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -111,32 +111,55 @@ getMaxentModel <- function(allTrainData, nCores, isTuningOn) {
111111
{
112112
usableCores <- max(1, min(nCores, parallel::detectCores() - 1))
113113
useParallel <- usableCores > 1
114-
ENMevaluate(
115-
occ = presenceTrainData,
116-
bg.coords = absenceTrainData,
117-
tune.args = tuneArgs,
118-
progbar = FALSE,
119-
partitions = "randomkfold",
120-
parallel = useParallel,
121-
numCores = usableCores,
122-
quiet = TRUE,
123-
algorithm = 'maxent.jar'
124-
)
114+
115+
# Try new ENMeval 2.0+ API first
116+
tryCatch({
117+
ENMeval::ENMevaluate(
118+
occs = presenceTrainData,
119+
bg = absenceTrainData,
120+
tune.args = tuneArgs,
121+
partitions = "randomkfold",
122+
algorithm = 'maxent.jar',
123+
parallel = useParallel,
124+
numCores = usableCores,
125+
quiet = TRUE
126+
)
127+
}, error = function(e1) {
128+
# If that fails, try old ENMeval < 2.0 API
129+
if (grepl("unused argument|unexpected.*argument|object.*not found", conditionMessage(e1))) {
130+
rsyncrosim::updateRunLog(type = "warning", message = "ENMevaluate failed, update ENMeval package to version 2.0")
131+
stop(e1)
132+
} else {
133+
stop(e1)
134+
}
135+
})
125136
},
126137
error = function(e) {
127138
warning(
128-
"Parallel Maxent failed due to memory issue. Retrying in serial mode..."
129-
)
130-
ENMevaluate(
131-
occ = presenceTrainData,
132-
bg.coords = absenceTrainData,
133-
tune.args = tuneArgs,
134-
progbar = FALSE,
135-
partitions = "randomkfold",
136-
parallel = FALSE,
137-
quiet = TRUE,
138-
algorithm = 'maxent.jar'
139+
"Parallel Maxent failed. Retrying in serial mode...\n",
140+
"Original error: ", conditionMessage(e)
139141
)
142+
143+
# Retry in serial mode with new API
144+
tryCatch({
145+
ENMeval::ENMevaluate(
146+
occs = presenceTrainData,
147+
bg = absenceTrainData,
148+
tune.args = tuneArgs,
149+
partitions = "randomkfold",
150+
algorithm = 'maxent.jar',
151+
parallel = FALSE,
152+
quiet = TRUE
153+
)
154+
}, error = function(e2) {
155+
# If that fails, try old API in serial
156+
if (grepl("unused argument|unexpected.*argument|object.*not found", conditionMessage(e2))) {
157+
rsyncrosim::updateRunLog(type = "warning", message = "ENMevaluate failed, update ENMeval package to version 2.0")
158+
stop(e2)
159+
} else {
160+
stop(e2)
161+
}
162+
})
140163
}
141164
)
142165

@@ -174,7 +197,7 @@ getMaxentModel <- function(allTrainData, nCores, isTuningOn) {
174197

175198
# ENMeval/MaxEnt logistic output is a numeric vector of P(presence)
176199
p1 <- tryCatch(
177-
as.numeric(predict(mxModel, newdata, type = "logistic")),
200+
as.numeric(dismo::predict(mxModel, newdata, type = "logistic")),
178201
error = function(e) {
179202
# some MaxEnt builds require 'dismo::predict' signature
180203
as.numeric(dismo::predict(mxModel, newdata, args = "logistic"))
@@ -249,7 +272,7 @@ predictMaxent <- function(raster, model, filename = "", memfrac = 0.5) {
249272

250273
# MaxEnt logistic prediction
251274
p1 <- tryCatch(
252-
as.numeric(predict(m$model, data, type = "logistic")),
275+
as.numeric(dismo::predict(m$model, data, type = "logistic")),
253276
error = function(e) {
254277
# some MaxEnt builds require 'dismo::predict' signature
255278
as.numeric(dismo::predict(m$model, data, args = "logistic"))

src/functions/0.6-post-processing.r

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ plotVariableImportance <- function(importanceData, transferDir) {
288288
#'
289289
#' @noRd
290290
filterPredictionRaster <- function(r, filterValue = NULL, fillValue = NULL) {
291+
# Temporarily disable disk-backing to avoid file conflicts
292+
old_todisk <- terraOptions()$todisk
293+
terraOptions(todisk = FALSE)
294+
291295
rBin <- classify(
292296
r,
293297
matrix(c(-Inf, 0.5, 0, 0.5, Inf, 1), ncol = 3, byrow = TRUE)
@@ -320,10 +324,12 @@ filterPredictionRaster <- function(r, filterValue = NULL, fillValue = NULL) {
320324
rBin <- ifel(rBin == 0 & neighborSum >= fillValue, 1, rBin)
321325
}
322326

327+
# Restore original setting
328+
terraOptions(todisk = old_todisk)
329+
323330
return(rBin)
324331
}
325332

326-
327333
#' Filter predicted presence raster and write to file ----
328334
#'
329335
#' @description

0 commit comments

Comments
 (0)