Spatial and Spatiotemporal SPDE-Based GLMMs with 'TMB'
Overview
Implements spatial and spatiotemporal GLMMs (Generalized Linear Mixed Effect Models) using 'TMB', 'fmesher', and the SPDE (Stochastic Partial Differential Equation) Gaussian Markov random field approximation to Gaussian random fields. One common application is for spatially explicit species distribution models (SDMs). See Anderson et al. (2025) doi:10.18637/jss.v115.i02.
Breaking changes
- Removed the previously deprecated
delta_poisson_link_gamma()anddelta_poisson_link_lognormal(). Usedelta_gamma(type = "poisson-link")ordelta_lognormal(type = "poisson-link"). - Removed the previously deprecated
extract_mcmc()andadd_barrier_mesh()functions; use the versions in sdmTMBextra. Also removed the previously deprecatedprevious_fitargument ofsimulate_new()(usesimulate.sdmTMB()). sdmTMB_cv()now scores held-out data with a predictive density integrated over the approximate posterior of the random effects (predictive = "mle-mvn") instead of at their estimated values ("mle-eb").predictive = "joint"also integrates over the fixed effects and variance parameters.nsimsets the number of draws per fold, sosum_logliknow depends on the random seed. The"mle-eb"values are always returned incv_loglik_mle_ebandsum_loglik_mle_eb. Random field uncertainty on out-of-sample predictions generally increases with more spatial knots, so it becomes increasingly important to account for random field uncertainty at finer mesh resolutions and it's particularly important for comparing across mesh resolutions.
Show all changes in 1.2.0Hide the rest of the changes in 1.2.0
get_index_split()now defaults tobias_correct = TRUE, matchingget_index().predict(allow_new_levels = FALSE)now gives an error for new levels of a random effect grouping factor, as in lme4 and glmmTMB. Previously it gave the same warning as the default (NULL).- The arguments of
sdmTMB()afterfamilyare reordered into groups: core model, observation-level adjustments, field configuration, additional model structure, specialized response options, estimation, and deprecated arguments. Code that passes arguments afterfamilyby position must name them. - Censoring bounds for
censored_poisson()andcensored_betabinomial()are now supplied with thecensored_upperargument ofsdmTMB(), which also accepts a column name indata. Thecensored_upperargument ofsdmTMBcontrol()has been removed. UseInfinstead ofNAfor no upper bound;NAis deprecated and is treated asInfwith a warning.get_censored_upper()now caps the bounds at the number of hooks. This also fixessdmTMB_cv()misaligning the bounds with the data when folds were assigned randomly with atimecolumn. predict.sdmTMB()now errors fortype = "response"withse_fit = TRUE(previously a warning, with link-scale output) and with asims_varother than"est". Usetype = "link"for standard errors ornsimfor response-scale uncertainty.sims_varaccepts only"est","omega_s","zeta_s","epsilon_st","est_rf", and"est_non_rf"; usereturn_tmb_report = TRUEwithnsimfor other reported variables.diffusion(x) + time_lag(x)innonlocal_formulanow fits one stationary joint space--time distributed-lag operator with one coefficient. Temporal persistence is constrained to be non-negative. The temporal parameter is estimated aslog_kappaT_nl(equal tologit(rhoT)) rather thankappaT_nl_raw,sdmTMB_simulate()requires0 < lags_rhoT < 1, and the derived summaries are namedMSDKandRMSDKrather thanMSDandRMSD.time_lag()now starts from a stationary state by default (the covariate is assumed to have held at its first time slice); usestart = "zero"for the previous behaviour, which matches Thorson et al. (2026).spatial_varyingnow respects its own model matrix, and the(Intercept)column is dropped only whenspatial = "on". Forspatial = "off", spatial_varying = ~ 1 + factor, the model now fits a genuine intercept field plusK - 1deviation fields instead of omitting the reference field, with a one-time warning. Thesvc-factor-modelsarticle clarifies howspatial,spatial_varying, and the intercept of thespatial_varyingdesign matrix interact.- For
truncated_nbinom1()andtruncated_nbinom2(),phinow comes from each fit's own parameters rather than the family object, so callingfamily$linkinv()directly for these families requiresphi. - The experimental
epsilon_modelandepsilon_predictoroptions are removed and now error. File an issue if you're interested in using this functionality and we can consider adding it back.
New features
- **RTMB is now the default backend.** The model code is written in R (via RTMB) with about 50% fewer lines, and gives the same results as the TMB backend in all our tests, although simulated values differ between backends for the same seed. The TMB backend is retained for now but will eventually be dropped. To keep using it, set
control = sdmTMBcontrol(backend = "tmb")per fit oroptions(sdmTMB.backend = "tmb")per session. The move uncovered several bugs, which are fixed as listed below, and allowed for optimization of the model. Speedups relative to sdmTMB 1.1.0 on an example model with the built-inpcoddataset: - fitting: 1.2x
predict(): 13.5xget_index(bias_correct = FALSE): 10.4xget_index(bias_correct = TRUE): 1.5xsimulate(nsim = 400): 1.9xproject(nsim = 400)(30-year AR1 projection): 9.3xsimulate_new()now works with areal SAR/CAR models: pass an areal domain frommake_areal_domain()asmesh, setspatial_model = "sar"or"car", and supplyrho_saroralpha_carinstead ofrange. #552- Added
lognormal_prior()as an option forsdmTMBpriors(sigma_V = ...)priors on time-varying coefficient SDs. simulate.sdmTMB()gains asimulate_reargument to choose which random effects are simulated as new (any of"spatial","spatiotemporal","spatial_varying","group_re","time_varying"). Random effects not named are held at their empirical Bayes estimates (type = "mle-eb") or at an approximate posterior draw (type = "mle-mvn"). For example,simulate_re = "spatiotemporal"withtype = "mle-mvn"holds the spatial fields at one posterior draw while simulating new spatiotemporal fields.- Add custom priors for the RTMB backend:
sdmTMBpriors(custom = function(par, theta) ...)adds arbitrary log densities on raw (par) or natural-scale (theta) parameters, including random effects, to the joint objective. Withbayesian = TRUE,custom_log_jacobiansupplies the matching Jacobian adjustment. Newget_prior_parameters()lists the available parameters andget_prior_densities()evaluates the terms at the estimates. See the new vignette "Custom priors and penalties". - Add experimental multi-family models, where each row of the data can use a different observation family (e.g., binomial, count, and delta-lognormal data in one model with shared fields). Supply a named list to
familyand name the column mapping rows to families withdistribution_column. See the new multi-family vignette/article. - Add
ordbeta()for ordered beta regression (Kubinec 2023), an alternative to zero-one-inflated beta for continuous data on[0, 1]with point masses at 0 and 1 (#515). Response-scale predictions and indices include the point masses. - Add
censored_betabinomial()for censored beta-binomial counts, e.g., to account for hook competition in hook-and-line surveys with the number of hooks as the number of trials. It usessdmTMB(censored_upper = ...)likecensored_poisson(), withInfmeaning censoring between the observed count and the number of trials. It needs the RTMB backend. The likelihood sums the shorter of each interval and its complement, with a precision check and fallback to the direct sum after fitting (sdmTMBcontrol(censored_method = "direct")forces the direct sum). See the new article "Accounting for hook competition with censored count models". - Add
censored_binomial()for censored binomial counts. It works likecensored_betabinomial()(RTMB backend, number of trials viaweights). Withlink = "cloglog"and an observation-level random intercept, it is a Poisson-lognormal model of the per-hook catch rate with at most one fish per hook, soget_index(derived_link = "log")is proportional to the mean catch rate. - Add
censored_nbinom1()andcensored_nbinom2()for censored negative binomial counts (RTMB backend). They usesdmTMB(censored_upper = ...)likecensored_poisson(), with no upper limit on the count, e.g., for catch without hook competition with an offset of log hooks. - Add the
dispformulaargument tosdmTMB()for modelling the observation dispersion parameter with fixed-effect predictors. It is not supported for multi-family models or truncated negative binomial families. - Add
compare_deviance()for deviance explained (a pseudo-R^2) relative to a simpler model. - Add a
loo::elpd()method forsdmTMB_cv()output so models can be compared withloo::loo_compare(), including a standard error of the difference. See?sdmTMB_cvfor an example. - Add deviance residuals and
deviance()forgengamma()andcensored_poisson(). - Add
make_zero_one_map()for categories (e.g., year factors) where the response is all zeros or ones. #386 - Add
sample_feargument topredict.sdmTMB(). Withnsim > 0andsample_fe = FALSE, fixed effects are held at their estimates and only random effects are drawn. get_index(),get_cog(),get_eao(), andget_weighted_average()can now take a fitted model and prediction grid directly, e.g.,get_index(fit, newdata = grid). These functions andget_index_split()also gain anoffsetargument.project()gainssample_fe,sample_historical_re, andsample_future_rearguments to separate parameter uncertainty, historical latent state uncertainty, and future process variation, and afuture_reargument for including (the default), zeroing, or fixing future spatiotemporal and time-varying effects. Projections are faster whennewdatarepeats spatial locations across time, andproject()now keeps only the simulated values it returns, which more than halves peak memory use with many simulations.sdmTMB_cv(lfo = TRUE)now supports delta/hurdle families.- Add opt-in
collapse_spatiotemporal_ar1andcollapse_ar1_thresholdcontrols to simplify spatiotemporal AR1 fields to IID or random-walk fields when the estimated correlation is near zero or one. - Covariate diffusion handles non-local covariates better: time-indexed formulas are distinguished, external grids are supported for prediction and cross-validation, and insufficient grid coverage gives clear errors.
- Covariate diffusion's
kappaS_nlnow starts at a value scaled to the data's spatial extent andlog_kappaS_nlis bounded by default, which avoids degenerate over-smoothed fits. Override the bounds withlowerorupperinsdmTMBcontrol().
Bug fixes: statistical and likelihood
- Fixed wrong gradients for
censored_poisson()(and other censored count families) whencensored_upperhad non-integer values. Bounds are now rounded down, as documented, when the model is set up. - Simulating new correlated random intercepts and slopes, e.g.,
(1 + x | g), withsimulate(re_form = ~0)on the TMB backend no longer gives an error. - Rows with missing values in the response or any variable the model uses (including
weightsandoffset) are now omitted before fitting, as withna.omitinglm(), with a message unlesssilent = TRUE. Previously, such rows were not removed from the spatial, temporal, random-effect, and spatially varying inputs, so later rows were matched to the wrong locations and times and fits were silently incorrect.predict(),residuals(), andsdmTMB_cv()now work for these fits, and the fitted object'sdataholds the rows used. sdmTMB_simulate()now errors ifsigma_Ehas more than one value, which previously silently simulated a constant SD.truncated_nbinom1()andtruncated_nbinom2()now error on a response of zero in non-delta models instead of reporting convergence with an infinite objective.- Fix three bugs in priors.
normal()priors onbused the standard deviations as variances; a singlebprior with multiple coefficients was silently dropped and is now applied to every coefficient; and withbayesian = TRUEthe Jacobian forpc_matern()priors was incorrect. - Fix several other statistical bugs in both backends:
ordbeta()cutpoints are now constrained to be ordered.gengamma()returns aNaNlikelihood where its mean does not exist.- A
matern_stpc_matern()prior with a shared range no longer drops its range term when there is nomatern_sprior. - With
bayesian = TRUE, thethreshold_logistic_s95prior includes its Jacobian adjustment. - Response means without observation error (e.g.,
simulate(observation_error = FALSE)) are now expectations for the beta-binomial, truncated negative binomial, mixture, and ordered beta families. get_eao()weights density by area.sigma_Vpriors are no longer applied twice in delta models.- Fix
censored_poisson()likelihoods and gradients, which since sdmTMB 0.4.0 evaluated the censored CDF terms at the starting values. Log likelihoods are also now computed on the log scale, avoiding-Inffor observations far in the tails. - Fix
time_varyingmodels with more than one column (e.g.,~ 1 + x), where estimatedb_rw_tandsigma_Vfor all but the last term were shrunk. Fitted values and predictions are unchanged, but the log likelihood (and therefore AIC) shifts relative to earlier versions, so don't compare these across versions. - Fix covariate diffusion with
diffusion()but notime_lag()innonlocal_formula. The time column of the nonlocal grid was ignored, so the diffused covariate was zero in every time slice after the first and the fit was silently wrong. - Fix
student(link = "inverse"), which errored ("illegalfamilyargument") while computing starting values. - Fix
breakpt()likelihoods and gradients after the cutpoint moves from its starting value in the TMB backend. - Fix
ordbeta()simulation, which understated ones and the mean. The likelihood was unaffected. - Fix TMB-backend simulation of AR1 and RW spatiotemporal fields with a barrier mesh, which had the wrong variance.
- Fix deviance and deviance residuals: they now include observation weights, binomial residuals work for responses with more than one trial,
nbinom1()uses the saturated model withphifixed, and Poisson-link delta encounter residuals are negative for zeros. - Fix
sdmTMB_cv(lfo = TRUE)log likelihoods for lognormal models, which omitted a logarithm. - Mixture-family predictions now respect non-log links and population-level predictions (
re_form = NA). - Fix delta models with
spatial_varyingwhere a component hasspatial = "off"(e.g.,spatial = list("on", "off")). That component's spatially varying coefficient fields entered the model without a random field penalty and with an unidentified SD parameter. They now have a random field density and their own estimated range.
Bug fixes: prediction, indices, and other
simulate()withnewdatanow uses the covariate-diffusion field built fromnewdata(ornonlocal_newdata) rather than the fitted one.- Response-scale
predict()draws (nsimormcmc_samples) now use each draw's ordered-beta cutpoints and truncated-NB dispersion instead of the fitted estimates. compare_deviance()now errors if the models use different likelihoodweightsorcensored_upperbounds.tidy(effects = "ran_vals")for delta models withtime_varyingcoefficients now labels each row with the correctmodelcomponent. It was returning both components labelled with the requestedmodel. As with IID random effects, both components are returned regardless ofmodel.tidy(effects = "ran_pars")again reportssigma_V, the SD of time-varying coefficients, for alltime_varying_typevalues. It was being silently dropped, and AR1 time-varying models were skipped entirely. With more than one time-varying coefficient, thesigma_Vand
The NEWS section is longer than shown here.
From NEWS.md in the release
Install
Health
- OK13 OK2026-10-21
- NOTE10 OK, 3 NOTE2026-10-17
- WARNING7 OK, 5 NOTE, 1 WARNING2026-10-01
- NOTE10 OK, 3 NOTE2026-07-16
- OK13 OK2026-07-04
Show 12 earlier changesHide 12 earlier changes
- WARNING12 OK, 1 WARNING2026-07-03
- OK13 OK2026-06-27
- ERROR12 OK, 1 ERROR2026-06-24
- OK13 OK2026-06-19
- WARNING12 OK, 1 WARNING2026-06-17
- OK13 OK2026-06-09
- ERROR12 OK, 1 ERROR2026-06-07
- OK13 OK2026-05-20
- ERROR12 OK, 1 ERROR2026-05-17
- OK13 OK2026-04-28
- ERROR10 OK, 3 NOTE, 1 ERROR2026-04-24
- NOTE11 OK, 3 NOTE2026-03-10
Documentation
The repository also keeps web-only articles.
- Help pages
- 48 (2 internal)
- Examples that run
- 86%37 run in a plain check (11 of them under a condition) · 6 only when donttest examples are enabled (R CMD check --as-cran)
- Exported functions with examples
- 95%
- Documented parameters
- 96%
- Return-value docs
- 97%
- Help pages citing references
- 31%
Downloads
Repository
- PRs merged (all time)
- 80%
- Median days to close, 50 most recently updated closed issues
- 152
- Median days to close, 50 most recently updated closed PRs
- 1
Stars over time
Forks over time
Releases over time
Issues over time
PRs over time
Commits over time
Commit identities over time
11 development-tooling and community-health practices detected across 6 families in the upstream repository
Checks run against github.com/sdmtmb/sdmtmb on 2026-10-18.
The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel
GitHub Pages site last deployed 2026-10-17 · The repository has a site deploy workflow
pkgdown site last built 2026-10-17
Show all practicesHide 2 practices
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2025-07-12Named in .Rbuildignore: .claude, CLAUDE.mdNamed in .gitignore: .claude, CLAUDE.mdPull request it wrote: opened by a project memberCommits: 2 by its own account · at least 6 crediting it in the message
- Gemini CLIfirst seen on or before 2026-07-18Config: a file or ignore line, not recorded which
- Claude Opus 5.56 commits · Sep to Oct 2026
Dependencies
Installing sdmTMB adds 39 packages (27 with compiled code, 39 MB of source tarballs). Heaviest: sf 9.3 MB, igraph 4.8 MB, sp 4.3 MB.
Code & Tests
Datasets
People & History
Show 9 more contributorsHide 9 contributors
Show 8 of 9 peopleHide 8 people
Pick two releases to compare their code metrics.
- 1.2.0Latest2026-07-03 · current release · diff ↗ ·
removed 4 exports
add_barrier_mesh, delta_poisson_link_gamma, delta_poisson_link_lognormal, extract_mcmcadded 13 exports
censored_betabinomial, censored_binomial, censored_nbinom1, censored_nbinom2, compare_deviance, diffusion, get_censored_upper, get_prior_densities, get_prior_parameters, lognormal_prior, make_zero_one_map, ordbeta, time_lag - 1.1.02026-07-03 · diff ↗ ·
added 3 exports
make_areal_domain, plot_nonlocal_covariate, plot_nonlocal_kernel - RR 4.6.0 released · 2026-04-24
- 1.0.0
- 0.8.12026-01-08 · diff ↗
- 0.8.02025-11-26 · diff ↗ ·
added 5 exports
betabinomial, cv_to_waywiser, get_range_edge, get_weighted_average, make_category_svc - 0.7.42025-07-29 · diff ↗
- 0.7.22025-06-19 · diff ↗
- RR 4.5.0 released · 2025-04-11
- 0.7.0
- 0.6.02024-05-30 · diff ↗
- RR 4.4.0 released · 2024-04-24
- 0.5.0
- 0.4.32024-02-29 · diff ↗
- 0.4.22024-01-18 · diff ↗
- 0.4.1
Show 7 earlier eventsHide 7 earlier events
- 0.4.0
- RR 4.3.0 released · 2023-04-21
- unarchivedReturned to CRAN2023-01-28
- 0.3.0
- archivedRemoved from CRAN2023-01-25issues were not corrected in time
- 0.2.12023-01-10
- RR 4.2.0 released · 2022-04-22
Package metadata
- First published
- 2023-01-10
- Total releases
- 16 / 3 yrs
- License
- GPL-3 OSI
- Copyright
- inst/COPYRIGHTS
- Minimum R
- ≥ 4.1.0
- Bundled data
- 483 KB / 10 files
- Download size
- 1.1 MB
Cite
Cite this package
Run in R for the authors' entry:
citation("sdmTMB")The package provides a citation file, so citation() returns the authors' own entry.
It points to 10.18637/jss.v115.i02.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-22, which the citation names so these numbers can be found later. More on citing and the projects behind them.