topocast 0.0.4
-
topocast()now brings every coarse grid a call fits -- each response's
coefficients and diagnostics, plus the shared valid-cell count -- onto the target
in one resample (grid target) or extract (point target), resolves the target
predictors once, and assembles the coarse grids as a single multi-layer object
rather than one terra object per coefficient. Each of those was previously done
once per response, so a call repeated its whole coarse-to-target trip for every
response it fit. The per-call fixed cost falls from about 0.58 s to about 0.11 s,
a 25,000-point call with 19 responses from 1.28 s to 0.46 s, and the S4
construction and dispatch that took over a fifth of a call no longer appears in
its profile. Withanomaly, the period stack likewise travels in one trip rather
than one per period. Results are unchanged (#35). -
The moving-window engine's summed-area tables are now built and queried in
double-double (extended) precision rather than plaindouble. A summed-area
table entry near the far side of a large grid holds a sum over most of the
grid's cells, so its magnitude grows with the grid size; on a plain-double
table, a window's sum (extracted as the difference of four such entries) then
loses precision proportional to that magnitude rather than to the window's own
size, which by roughly a million cells could already turn a genuinely zero
within-window variance into a small positive number that wrongly cleared
min_variance, defeating the no-spread guard (#21). -
arma::uwordis now explicitly 64-bit (ARMA_64BIT_WORD), and the moving
window's row/column bounds are computed in a 64-bit type, closing two overflow
classes left open after #13/#14 for a grid whose total cell count or single
dimension is large enough to overflow a 32-bit computation (#31, #32). -
The C++ engine calls
Rcpp::checkUserInterrupt()between chunks of rows (and
during single-threaded setup), so a long-running fit on a large grid can now be
interrupted rather than only checked for completion (#33). -
A response or predictor named
r.squared,residual.sd,n.valid, or
(Intercept)is now rejected at the formula-parsing stage; those names are
reserved fortopocast()'s own diagnostic and coefficient columns, and a
response sharing one previously had its downscaled values silently overwritten
by the diagnostic column of the same name whendiagnostics = TRUE(#22). -
n.validis now bounded to[0, (2 * radius + 1)^2]after resampling, the
true range a window's valid-cell count can hold; liker.squared, it could
previously be carried slightly outside its true range by some resampling
methods (#24). -
radius = 0is now rejected: its one-cell window can never hold thek + 1
valid cells a fit withkpredictors needs, so it previously returned an
all-NAresult with no error or warning (#25). -
An invalid
aggregateor grid-targetmethodnow errors with a message
naming the actualtopocast()argument, instead of terra's raw
'arg' should be one of ...message, which named neither (#26). -
With
type = "ratio", a coarse cell where the baseline and the anomaly period
are both exactly zero (e.g. a dry-season precipitation cell with no rain in
either) is now treated as ratio1, "no change", rather than discarding the
fine baseline the same way a truex / 0does (#27). -
A CRS mismatch error now falls back to a PROJ4 or WKT snippet when neither
side carries an identifiable EPSG code (common for a custom or regional
projection), instead of printing "unknown CRS" on both sides with no way to
tell how they actually differ (#28). -
An entirely non-finite response/predictor stack now errors with a message
naming the actual inputs (data/ontofortopocast(),y/xfor
window_regression()) instead of the C++ engine's generic
"no finite cells to regress"(#30, #34). -
output = "raster"no longer silently renames the(Intercept)coefficient
column via therasterpackage's ownmake.names()-basednames<-, which is
now documented as an inherent limitation of that output class rather than left
unmentioned (#23). -
inst/CITATIONnow reports the installed package version dynamically instead
of a hardcoded string that had drifted out of date (#29). -
Several responses that share the predictors are downscaled in one call by naming
them ascbind(prec, tmin) ~ elev. The moving-window design depends only on the
predictors, so it is assembled and factored once and solved against every response;
each extra response adds only a back-substitution. The result has one layer (or
column) per response, andcoefficients/diagnosticsgrids are prefixed by the
response name.window_regression()likewise accepts a list of response matrices. -
diagnostics = TRUEreturns anr.squaredgrid: the per-window coefficient of
determination of the local fit, computed from the same summed-area sufficient
statistics, mapping where the terrain relationship is strong.window_regression()
now returnsr_squaredalongsideinterceptandslope. -
diagnostics = TRUEalso returnsresidual.sd(the residual standard deviation of
the local fit, in the response's own units) andn.valid(the count of valid
coarse cells the window held), from the same sufficient statistics asr.squared.
n.validis shared across responses rather than prefixed, since the valid-cell mask
is complete-case across them;residual.sdis prefixed liker.squaredis.
window_regression()likewise returnsresidual_sdandn_valid(#8). -
clamp = TRUEbounds the downscaled field to the observed range of the coarse
response, a guard against the local linear fit extrapolating without limit where a
fine predictor lies outside the range it was fit on. -
The per-cell window solve runs in parallel with OpenMP where the toolchain
provides it; results are unchanged. -
anomalyandbaselineaccept aRaster*(raster) orstarsobject, likedata
andontoalready did, instead of only aSpatRaster(#5). -
radiusandmin_cellsare validated as non-negative whole numbers before
reaching the C++ engine. A negativeradiuspreviously indexed the summed-area
table out of bounds and crashed the R session rather than erroring (#4); the same
validation now also rejects a value too large for a 32-bit integer, which
previously overflowed toNAon coercion and crashed the session the same way (#9). -
min_varianceis validated as a non-negative number.NAor a negative value
previously compared as false against every window variance, silently disabling
the documented guard against a predictor with no spread instead of being
rejected (#10). -
window_regression()rejects a partially-named list of responses instead of
silently making the unnamed ones' results unreachable by name (#6). -
window_regression()rejects a list of responses with a repeated name, which
had the same unreachable-by-name problem as #6 but was not caught by it (#15). -
A
min_cellsclose to.Machine$integer.maxno longer overflows the C++
engine's valid-cell threshold; previously the overflow silently disabled the
guard instead of enforcing it, so windows with far fewer than the requested
minimum could still be fit (#13). -
A
radiusclose to.Machine$integer.maxno longer crashes the R session;
it is now clamped to the grid's span, which covers every cell in every
window anyway and so changes no result (#14). -
diagnostics = TRUE'sn.valid(andwindow_regression()'sn_valid) now
reports a window's valid-cell count whenever the window held enough cells to
attempt a fit, rather than only when the fit also succeeded; a degenerate
(no-spread predictor) or singular window previously reportedn.valid = NA
indistinguishably from a window that never had enough cells (#16). -
A window whose predictors are exactly collinear (rank-deficient design) now
reliably returnsNAinstead of a spurious least-squares coefficient;
Armadillo's default solver silently falls back to an approximate solution on
a detected-singular system rather than failing, which defeated the
documented singular-design guard (#17).