API Reference

Circuitscape exports nothing: every function, compute included, is reached as Circuitscape.name (or brought in explicitly with using Circuitscape: compute). The names in the Public API section are covered by semantic versioning: they keep working, with the documented meaning, within a major version. Anything not listed there is internal and may change in any release. Omniscape.jl depends on exactly three of these, compute_omniscape_current, init_config and update!, and they are stable.

Public API

Circuitscape.computeFunction
Circuitscape.compute(path::String) -> Matrix
Circuitscape.compute(cfg::Dict{String,String}) -> Matrix

Run a Circuitscape job. path names an INI file; alternatively pass a dictionary of INI keys and string values, as returned by Circuitscape.init_config, with the entries you need changed. Keys that are not given take their default value.

The configuration is validated before any data is read (see Circuitscape.validate), written in INI form to output_file, and run. Output files are written next to output_file; the return value is the result matrix: pairwise resistances (first row and column hold the focal node IDs), one-to-all resistances per focal node, node voltages in advanced mode. Set parallelize = True and start Julia with several threads to run pair solves in parallel.

source
Circuitscape.init_configFunction
init_config() -> Dict{String,String}

The default configuration as INI strings, one entry per key compute accepts. Modify the entries you need and pass the dictionary to compute, or convert it with CSConfig(dict). File paths default to INI Builder placeholders such as "(Browse for a resistance file)", which validate treats as unset. A few Circuitscape 4 keys (print_timings, print_rusages, screenprint_log, profiler_log_file) are present so that old INI files load without warnings; they have no effect.

source
Circuitscape.update!Function
update!(cfg::Dict{String,String}, new) -> cfg

Merge the INI keys and values in new (any iterable of key => value pairs, typically a Dict{String,String}) into the configuration dictionary cfg, overwriting existing entries. Mutates and returns cfg. This is how a caller layers its own settings on top of init_config before passing the result to compute or compute_omniscape_current; no values are parsed or checked here (that happens in CSConfig(cfg)).

source
Circuitscape.compute_omniscape_currentFunction
compute_omniscape_current(conductance, source, ground, cs_cfg) -> Matrix{Float64}

Advanced-mode solve on in-memory arrays; Omniscape's entry point for every moving-window solve. No files are read or written.

Arguments:

  • conductance::Matrix{T}, T <: Union{Float32, Float64}: per-cell conductance (not resistance). Cells with a value of 0 are dropped from the graph; every other cell becomes a node connected to its eight neighbours (four if connect_four_neighbors_only = True in cs_cfg) with the average conductance of the two cells as edge weight.
  • source::Matrix{T}: current injected at each cell, in amps; 0 where there is no source.
  • ground::Matrix{T}: conductance to ground at each cell (Inf for a direct ground, 0 where there is no ground). Where a cell is both a source and a ground the source is removed (remove_src_or_gnd = rmvsrc).
  • cs_cfg::Dict{String,String}: INI keys and values as from init_config merged with update!. It is parsed with the same strict rules as an INI file, so scenario must be set and values must be valid. Of it, only solver, cholmod_batch_size and residual_tolerance (solver settings) and connect_four_neighbors_only affect the result; data_type, scenario, ground_file_is_resistances, remove_src_or_gnd, connect_using_avg_resistances and every map-writing option are overridden. The arithmetic precision is T, the element type of conductance, not cs_cfg["precision"]; node indices are always Int64.

Returns a Matrix{Float64} of size(conductance): the current through each cell (max(inflow, outflow) at its node), summed over every connected component that contains both a source and a ground. Cells with no node, or in components without a source or a ground, are 0. The solve of each component is accepted only if its residual passes the gate described at residual_tolerance.

source
Circuitscape.startFunction
Circuitscape.start()

Build an INI file interactively, step by step, and optionally run it. The builder lives in an extension on the REPL stdlib; calling start() loads REPL on demand, so it works from a script as well as from the prompt.

source

Configuration

A configuration is a Dict{String,String} of INI keys and values while it is being built (init_config, update!), and a CSConfig once parsed. compute accepts the dictionary form and does the parsing and validation itself; these functions expose the same steps for scripts that want to read, check or write INI files without running them.

Circuitscape.parse_configFunction
parse_config(path::String) -> CSConfig

Read a Circuitscape INI file. Section headers are ignored; every key = value line is parsed on top of the defaults from init_config, so keys that are absent take their default value. Unrecognised values for solver, scenario, data_type, precision, log_level, remove_src_or_gnd and any boolean raise an ArgumentError naming the key; unknown keys produce a warning and are ignored. File existence is not checked here but in validate.

source
Circuitscape.CSConfigType
CSConfig

Typed, immutable configuration for one Circuitscape run: one field per INI key, with the INI's string values parsed into enums, booleans and numbers. Built by parse_config(path) or CSConfig(dict::Dict{String,String}); every parser rejects values it does not recognise rather than falling back to a default. Dict{String,String}(cfg) converts back to INI strings, and CSConfig(cfg; field = value, ...) returns a copy with fields replaced.

Field names match the INI keys documented in the options reference. Notable defaults: data_type = dt_raster, solver = st_cg_amg, precision = pr_double, cholmod_batch_size = 1000, residual_tolerance = 0.0 (meaning automatic: 1e-4 in double, 1e-3 in single), log_level = Logging.Info. scenario has no usable default and must be set.

source
Circuitscape.validateFunction
validate(cfg::CSConfig)

Fail fast, before any data is read, on configurations that cannot run: input files that do not exist, an output directory that does not exist, and options that Circuitscape.jl parses for compatibility with the Python version but has never implemented. Each message names the INI key so the user knows what to change.

source
Circuitscape.write_configFunction
write_config(cfg::CSConfig)
write_config(cfg::Dict{String,String})

Write the configuration, in Circuitscape INI form, to the file named by cfg.output_file. compute calls this at the start of every run so that the options used are recorded next to the results.

source

Internals

Internal

Nothing below is part of the public API. These functions and types carry no semantic-versioning guarantee and may be renamed, changed or removed in any release. They are documented for contributors and for readers who want to follow a run through the code.

Problem geometry

After loading, a raster landscape and a network are the same problem: a symmetric Laplacian over graph nodes, its connected components, and focal nodes. The one thing that differs downstream is where a node's voltage or current goes in the output, and that is what a Geometry knows.

Circuitscape.GeometryType
Geometry

How graph nodes map back onto the user's coordinates when maps are written. Once loaded, a raster landscape and a network are the same problem: a symmetric Laplacian over graph nodes, its connected components and focal nodes. The only thing that differs downstream of loading is where a node's voltage or current goes in the output, and that is what a Geometry knows. Output code dispatches on it rather than on the data type in the config.

source
Circuitscape.RasterGeometryType
RasterGeometry(nodemap, polymap, hbmeta, cellmap)

Nodes are cells of a grid: nodemap[i,j] is the node of cell (i,j), zero for cells outside the habitat, and cells of one short-circuit polygon share a node. hbmeta gives the grid's extent and georeferencing, cellmap the conductance per cell (needed to mark null cells as NODATA).

source
Circuitscape.NetworkGeometryType
NetworkGeometry(nodes)

Nodes are the user's own node ids: row k of the matrix being solved is node nodes[k]. For the whole graph this is the identity; restricted to a connected component it is the component's node list. Branch coordinates live on Cumulative.

source
Circuitscape.restrictFunction
restrict(geometry, component)

The geometry of one connected component, whose matrix rows are numbered 1:length(component): a raster nodemap relabelled to those local indices, or the component's node ids.

source

Problem construction and mode drivers

load_data reads a RasterData or NetworkData; build_problem turns it into a GraphProblem (pairwise) or an AdvancedProblem (advanced) with the matching geometry; run_pairwise and run_advanced are the mode drivers, and one-to-all/all-to-one build an AdvancedProblem per focal node and call advanced_kernel on it.

Circuitscape.build_problemFunction
build_problem(data, cfg)
build_problem(T, V, cfg)

The problem cfg describes over the loaded (or, given T, V, freshly read) data: a GraphProblem in pairwise mode, an AdvancedProblem in advanced mode. Both are built the same way for rasters and networks; build_graph, focal_nodes and initialize_cum dispatch on the data type to construct the graph and its output geometry.

source
Circuitscape.run_pairwiseFunction
run_pairwise(data, cfg)
run_pairwise(problem::GraphProblem, cfg)

Pairwise mode: solve every pair of focal nodes and write the cumulative maps. A raster whose focal nodes are regions rebuilds the graph per pair (pairwise_regions); everything else solves one shared problem.

source
Circuitscape.AdvancedProblemType
AdvancedProblem

An advanced-mode problem: the graph Laplacian G, its connected components cc, the Geometry mapping nodes to output coordinates, the source and ground vectors over the nodes (sources, grounds, with the finite grounds separated out in finitegrounds for the solver) and the solver. Built by build_problem in advanced mode and, one focal node at a time, by the one-to-all / all-to-one driver; solved by advanced_kernel.

source
Circuitscape.advanced_kernelFunction
advanced_kernel(prob, cfg; check_node, name, accumulate_currents)
    -> (voltages, current_map, solver_called)

Solve every connected component that has both a source and a ground, and write the voltage and current maps the options ask for, suffixed name. voltages is over all graph nodes (zero in components not solved); the current map is the per-cell sum over components for a raster (empty for a network), filled when accumulate_currents. One-to-all / all-to-one solve one focal node at a time and pass it as check_node to skip every other component.

source

Pairwise driver

The pairwise driver is shared by every solver. solve iterates the connected components of a GraphProblem, calling prepare! once per component (an AMG preconditioner or a factorization), pair_jobs to enumerate the PairJobs of that component, and solve_pairs! to run them, which hands every solved pair to handle_pair to store the resistance and to postprocess to accumulate and write maps through the Cumulative accumulators. Only prepare! and solve_pairs! differ between the iterative and direct paths.

Circuitscape.GraphProblemType
GraphProblem

A pairwise problem: the graph Laplacian G, its connected components cc, the focal nodes as graph indices (points) with the user's ids alongside (user_points), the pairs of user ids not to solve (exclude_pairs), the Geometry that maps nodes back to output coordinates, the Cumulative accumulators and the solver. Raster and network runs differ only in geometry. Built by build_problem and consumed by solve.

source
Circuitscape.CumulativeType
Cumulative

Accumulators shared by every pair solve of a pairwise run, guarded by lock: the cumulative and maximum current grids of a raster (cum_curr, max_curr; max_curr is empty unless write_max_cur_maps), or the cumulative node and branch currents of a network (cum_node_curr, cum_branch_curr, with coords naming each branch and branch_index mapping an edge in either orientation to its slot).

source
Circuitscape.PairJobType
PairJob

One pair of focal nodes to solve within a connected component: the two graph nodes (src_node, dst_node), their local indices in the component (comp_i, comp_j), and src_indices / dst_indices, the positions in points that map to each node; a focal region contributes several positions, all sharing one solve. Produced by pair_jobs.

source
Circuitscape.pair_jobsFunction
pair_jobs(csub, comp, points, orig_pts, exclude, first_source_only;
          positions = point_positions(points))

The node pairs to solve in one component, grouped by source node, in the order the log numbers them. A pair is skipped when every combination of its focal indices is excluded. With first_source_only (the resistance shortcut) only pairs anchored at csub[1] are produced. A group is returned for every source considered, even when it has no pairs, so callers can iterate sources. comp must be sorted ascending, as connected_components returns it.

source
Circuitscape.get_num_pairsFunction
get_num_pairs(ccs, fp, exclude_pairs, user_points = fp) -> (n, numbering)

Number of pairs the pairwise driver will solve without the resistance shortcut: for every connected component, the pairs of distinct focal nodes in it whose user ids are not in exclude_pairs. numbering maps each (node, node) pair to its position in that count, which is how the log labels Solving pair k of n.

source
Circuitscape.solveFunction
solve(prob, solver, cfg, log)

Pairwise driver shared by every solver. Enumerates the pairs per connected component, hands them to solve_pairs! for the solver-specific linear algebra, and stores resistances / postprocesses maps in handle_pair. Only prepare! and solve_pairs! differ between the iterative and direct paths.

source
Circuitscape.prepare!Function
prepare!(solver, matrix)

Per-component setup, run once before the pairs of a component are solved. Returns whatever solve_pairs! needs for that solver: the AMG preconditioner for AMGSolver (after regularizing matrix in place), or the factorization for a direct solver.

source
Circuitscape.solve_pairs!Function
solve_pairs!(handle_pair, handle, solver, matrix, groups, cfg, log, pair_label)

Solve every job in groups and call handle_pair(job, voltages) with the voltages referenced to the source node. The iterative path solves one pair at a time in equal-sized chunks of pairs, each task with its own preconditioner workspace; the direct path factorizes once and solves batches of right-hand sides, parallelizing the postprocessing of each batch.

source

Solvers and convergence

Circuitscape.SolverType
Solver

Abstract supertype of the solver backends. prepare! and solve_pairs! dispatch on it; everything else in the pairwise driver is shared.

source
Circuitscape.AMGSolverType
AMGSolver()

Iterative solver: conjugate gradient preconditioned by smoothed-aggregation algebraic multigrid (solver = cg+amg, the default). One preconditioner is built per connected component and shared, with a private workspace per task, by the parallel pair solves.

source
Circuitscape.CholmodSolverType
CholmodSolver(bs)

Direct solver: sparse Cholesky factorization through SuiteSparse CHOLMOD (solver = cholmod), solving bs right-hand sides per batch. Available without extra packages, in double and single precision.

source
Circuitscape.PardisoSolverType
PardisoSolver(bs)

Direct solver through Intel MKL Pardiso (solver = pardiso), bs right-hand sides per batch. Requires the Pardiso.jl package extension (using Pardiso) and double precision.

source
Circuitscape.AccelerateSolverType
AccelerateSolver(bs)

Direct solver through Apple's Accelerate sparse Cholesky (solver = accelerate), bs right-hand sides per batch. Requires the AppleAccelerate.jl package extension (using AppleAccelerate) on macOS; double and single precision.

source
Circuitscape.get_solverFunction
get_solver(cfg) -> Solver

The solver backend selected by cfg.solver, with cfg.cholmod_batch_size as the batch size of the direct solvers. Logs which solver is used.

source
Circuitscape.residual_toleranceFunction
residual_tolerance(cfg, T)

Largest acceptable true relative residual ‖Gv − b‖ / ‖b‖ for a solve in precision T. cfg.residual_tolerance when the user set one; otherwise 1e-4 in double and 1e-3 in single, since Float32 round-off puts 1e-4 at the edge of what CG can reach (the fixed 1e-4 gate broke every single precision run).

source
Circuitscape.solve_linear_systemFunction
solve_linear_system(G, curr, M; tol)

Preconditioned CG. Krylov's stopping test is on the preconditioned residual sqrt(r' M⁻¹ r), while the result is accepted on the true 2-norm relative residual. When the AMG preconditioner is a poor fit for a component the two diverge: CG reports success in its own norm while the true residual is still above tol (issue #470). Rather than abort a multi-hour job on one such solve, tighten the stopping tolerances and retry; solves that pass first time are unaffected.

source
Circuitscape.refine_columns!Function
refine_columns!(lhs, factor, matrix, rhs, tol, name)

Check every column of a direct solve against tol, applying iterative refinement with the existing factor where needed. In single precision a Cholesky solve of an ill-conditioned Laplacian lands at 1e-3..1e-2, well short of what CG reaches with its 1e-5 rtol; each step costs one triangular solve and recovers several digits. Double precision residuals are far below the target and take no steps. Shared by the CHOLMOD and Accelerate backends.

source

Input and output

Circuitscape.read_rasterFunction
read_raster(path, T) -> (array, wkt, transform)

Read a single-band raster as a nrows × ncols matrix of T with NODATA and NaN mapped to -9999. ESRI ASCII grids (.asc, optionally gzipped) are parsed natively; every other format goes through GDAL.

source
Circuitscape.write_rasterFunction
write_raster(fn_prefix, array, wkt, transform, file_format)

Write a single-band raster. .asc is written natively with writedlm — several times faster than the GDAL AAIGrid driver, and thread-safe, so it takes no lock; .tif goes through GDAL under IO_LOCK.

source
Circuitscape.connected_componentsFunction
connected_components(A::SparseMatrixCSC) -> Vector{Vector{Ti}}

Connected components of the graph whose edges are the nonzero off-diagonal entries of the symmetric matrix A, computed directly on the CSC structure. Components are ordered by their smallest vertex and vertices ascend within each — the same contract as Graphs.connected_components(SimpleGraph(A)), which this replaces. That path built an adjacency-list copy of A first: at 4M cells, 1.4 GB allocated and 458 MB retained to produce 31 MB of labels.

source