Circuit connectivity¶
Connectivity from electrical circuit theory: omnidirectional current with moving windows (Omniscape), current and effective resistance between focal points, sources and grounds, on rasters and on networks (Circuitscape). Tutorial: circuit connectivity.
ls.omniscape is at the top level; the other functions are in the
ls.circuit module (from landschaft import circuit).
Resistance, conductance and current
A resistance map says how hard each cell is to cross (1 = easy, larger = harder); conductance is its inverse. Current is injected at sources and leaves at grounds; the current through a cell shows how much movement funnels through it. The effective resistance between two points falls when there are more, shorter or easier paths between them.
Omnidirectional current¶
omniscape
¶
omniscape(
resistance,
radius,
*,
source=None,
block_size=1,
buffer=0,
source_threshold: float = 0.0,
r_cutoff: float = inf,
resistance_is_conductance: bool = False,
four_neighbors: bool = False,
flow_potential: bool = False,
normalized_current: bool = False,
correct_artifacts: bool = True,
mask_nodata: bool = True,
reclass_table=None,
condition1=None,
condition2=None,
condition1_future=None,
condition2_future=None,
compare_to_future: str = "none",
comparison1: str = "within",
comparison2: str = "within",
condition1_lower: float = 0.0,
condition1_upper: float = 0.0,
condition2_lower: float = 0.0,
condition2_upper: float = 0.0,
precision: str = "double",
memory_limit: float | None = None,
n_threads: int = 0,
)
Omnidirectional connectivity with Omniscape's moving-window circuits.
Omniscape 0.7 (run_omniscape; McRae et al. 2016, Landau et al. 2021):
every block_size × block_size block of cells whose source
strengths add up to more than 0 is a target. Its window is the circle of
radius + buffer cells around the block's centre; the block's
strength is injected into the sources within radius (the target block
left out, shared in proportion to their strengths), the target cell is
grounded, and the current through every cell of the window is added to the
cumulative map. Windows are solved exactly (Circuitscape's circuit, see
advanced()) in parallel and summed in a fixed
order, so the result does not depend on the number of threads.
Omniscape's details are kept: windows and circles cut at the map edges,
the target block zeroed at radius + buffer cells from the window's
corner (so not at the target when the window is cut on the left or the
top), and, with block_size > 1, its block-artifact correction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resistance
|
Resistance raster (path, DataArray or array); NaN or the nodata value is NoData (infinite resistance). Values must be > 0. |
required | |
radius
|
Radius of the moving window, in cells. |
required | |
source
|
Source strengths (NaN is 0; values below |
None
|
|
block_size
|
Side of the target blocks (made odd as Omniscape does: an even size gets + 1, with a warning). |
1
|
|
buffer
|
Cells added to the window beyond |
0
|
|
source_threshold
|
float
|
Omniscape's options of the same names. |
0.0
|
r_cutoff
|
float
|
Omniscape's options of the same names. |
0.0
|
resistance_is_conductance
|
bool
|
The raster holds conductances. |
False
|
four_neighbors
|
bool
|
|
False
|
flow_potential
|
bool
|
Also compute the flow potential (the same circuits with conductance 1) and the current normalised by it (0 where both are 0). |
False
|
normalized_current
|
bool
|
Also compute the flow potential (the same circuits with conductance 1) and the current normalised by it (0 where both are 0). |
False
|
correct_artifacts
|
bool
|
Omniscape's correction of block artifacts (only with |
True
|
mask_nodata
|
bool
|
NaN on the NoData cells of the resistance. |
True
|
reclass_table
|
Reclassify the resistance first ( |
None
|
|
condition1
|
Conditional connectivity ( |
None
|
|
condition2
|
Conditional connectivity ( |
None
|
|
condition1_future
|
Conditional connectivity ( |
None
|
|
condition2_future
|
Conditional connectivity ( |
None
|
|
comparison1
|
str
|
|
'within'
|
comparison2
|
str
|
|
'within'
|
condition1_lower
|
str
|
|
'within'
|
condition1_upper
|
str
|
|
'within'
|
condition2_lower
|
str
|
|
'within'
|
condition2_upper
|
str
|
|
'within'
|
precision
|
str
|
|
'double'
|
memory_limit
|
float | None
|
Bytes the solves may use (default: half of the physical memory); with
large windows fewer threads run when each thread's factorisation of a
full window would not fit ( |
None
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
Dataset
|
|
See Also
landschaft.circuit.omniscape_ini : run an Omniscape INI file. landschaft.circuit.circuitscape_pairwise : current between focal points.
Examples:
A 30 x 30 map with a hard barrier (resistance 20) that has a gap in the middle:
>>> import numpy as np
>>> import landschaft as ls
>>> resistance = np.ones((30, 30))
>>> resistance[:, 14:16] = 20
>>> resistance[10:20, 14:16] = 1 # the gap
>>> flow = ls.omniscape(resistance, radius=6, flow_potential=True, normalized_current=True)
>>> list(flow.data_vars)
['cum_currmap', 'flow_potential', 'normalized_cum_currmap']
Current concentrates in the gap:
omniscape_ini
¶
Run an Omniscape INI file, as run_omniscape(path) does.
Every option of Omniscape 0.7 is read with its defaults (radius,
resistance_file and project_name are required); file paths are
taken from the working directory, or else next to the INI file. With
write_outputs the outputs go to project_name (_1, _2…
when it exists) as Omniscape writes them: config.ini, cum_currmap,
flow_potential, normalized_cum_currmap and
classified_resistance, GeoTIFF (write_as_tif) or ASCII grids,
NoData -9999. solver, parallelize, parallel_batch_size and
suppress_cs_messages have no effect: windows are always solved exactly,
with n_threads threads (0: all cores but one).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
The INI file. |
required | |
write_outputs
|
bool
|
Write the outputs to the project folder. |
True
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
Dataset
|
As |
Examples:
>>> import numpy as np
>>> import xarray as xr
>>> import landschaft as ls
>>> from landschaft import circuit
>>> from rasterio.transform import from_origin
>>> resistance = xr.DataArray(np.ones((10, 10)), dims=("y", "x")).rio.write_transform(
... from_origin(0, 10, 1, 1)).rio.write_crs("EPSG:3035")
>>> _ = ls.write_raster(resistance, "resistance.tif")
>>> _ = open("omniscape.ini", "w").write('''[Required]
... resistance_file = resistance.tif
... radius = 3
... project_name = corridors
... source_from_resistance = true
... ''')
>>> flow = circuit.omniscape_ini("omniscape.ini")
>>> import os
>>> sorted(os.listdir("corridors"))
['config.ini', 'cum_currmap.tif']
source_from_resistance
¶
source_from_resistance(
resistance,
*,
r_cutoff: float = inf,
resistance_is_conductance: bool = False,
reclass_table=None,
)
Source strengths from the resistance, as Omniscape's source_from_resistance.
The conductance (1 / resistance, or the values themselves with
resistance_is_conductance) after the optional reclassification, set to
0 where it is below 1 / r_cutoff and on NoData.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resistance
|
Resistance (or conductance) raster. |
required | |
r_cutoff
|
float
|
Resistances above this give no source. |
inf
|
resistance_is_conductance
|
bool
|
The raster holds conductances. |
False
|
reclass_table
|
Reclassification applied first (see |
None
|
Returns:
| Type | Description |
|---|---|
ndarray or DataArray
|
Source strengths. |
Examples:
read_reclass_table
¶
Read an Omniscape reclass table.
Two whitespace-separated columns (value, new value); missing in the new
column makes NoData.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
The text file. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Examples:
reclassify
¶
Reclassify a resistance array, as Omniscape's reclassify_resistance!.
Every row (value, new value) is applied in turn, matched against the original values; NaN in the new column makes NoData. Values not in the table are kept.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resistance
|
ndarray
|
The array. |
required |
table
|
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Examples:
parse_ini
¶
Read an INI file as Omniscape's parse_cfg does.
Lines starting with [ are skipped; every other line is split at its
first = (key right-stripped, value stripped).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
The INI file. |
required |
Returns:
| Type | Description |
|---|---|
dict of str to str
|
|
Examples:
Focal points on rasters¶
circuitscape_pairwise
¶
circuitscape_pairwise(
habitat,
points,
*,
habitat_is_resistance: bool = True,
polygons=None,
mask=None,
four_neighbors: bool = False,
avg_resistances: bool = False,
included_pairs=None,
voltage_maps: bool = False,
current_maps: bool = False,
cum_map: bool = False,
max_map: bool = False,
set_focal_node_currents_to_zero: bool = False,
set_null_currents_to_nodata: bool = False,
set_null_voltages_to_nodata: bool = False,
log_transform_maps: bool = False,
reclass: dict | None = None,
n_threads: int = 0,
) -> CircuitscapeResult
Circuitscape's pairwise mode on rasters (scenario = pairwise).
Effective resistances between every pair of focal points (points: a
raster of ids, cells of one id forming a focal region, or a
FocalPoints), on the graph of circuitscape_advanced().
Every component is factorised once and its pairs solved in batches
(without maps or exclusions, Circuitscape's shortcut: only the pairs of the
first focal point are solved). included_pairs (IncludedPairs
or a path) restricts the pairs as Circuitscape does. current_maps and
voltage_maps keep every pair's maps in result.maps (keyed by the
two ids); cum_map / max_map sum / take the maximum of the pairs'
current maps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
habitat
|
Resistance (or conductance) raster: path, DataArray or array. |
required | |
points
|
Raster of focal-point ids (0 / NoData elsewhere; the cells of one id form
a focal region) or a |
required | |
habitat_is_resistance
|
bool
|
|
True
|
polygons
|
Short-circuit regions: a raster of ids (> 0); the habitat cells of one region are merged into one node. |
None
|
|
mask
|
Raster; cells where it is NoData or not positive are removed. |
None
|
|
four_neighbors
|
bool
|
Connect the four orthogonal neighbours only. |
False
|
avg_resistances
|
bool
|
Join neighbours by the average of their resistances instead of their conductances. |
False
|
included_pairs
|
|
None
|
|
voltage_maps
|
bool
|
Keep the voltage / current map of every pair (or point) in
|
False
|
current_maps
|
bool
|
Keep the voltage / current map of every pair (or point) in
|
False
|
cum_map
|
bool
|
Sum / maximum of the current maps ( |
False
|
max_map
|
bool
|
Sum / maximum of the current maps ( |
False
|
set_focal_node_currents_to_zero
|
bool
|
Current 0 on the focal nodes in the maps. |
False
|
set_null_currents_to_nodata
|
bool
|
NoData instead of 0 where the current / voltage is 0 in the maps. |
False
|
set_null_voltages_to_nodata
|
bool
|
NoData instead of 0 where the current / voltage is 0 in the maps. |
False
|
log_transform_maps
|
bool
|
log10 of the current maps. |
False
|
reclass
|
dict | None
|
|
None
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
CircuitscapeResult
|
|
See Also
omniscape : omnidirectional current without focal points.
Examples:
Three focal points on a map with a harder column in the middle:
>>> import numpy as np
>>> from landschaft import circuit
>>> resistance = np.ones((10, 10))
>>> resistance[:, 5] = 10 # a harder column in the middle
>>> points = np.zeros((10, 10), dtype=int)
>>> points[1, 1], points[8, 1], points[5, 8] = 1, 2, 3
>>> result = circuit.circuitscape_pairwise(resistance, points, cum_map=True)
>>> result.resistances.round(4)
array([[0. , 1. , 2. , 3. ],
[1. , 0. , 0.8515, 0.8567],
[2. , 0.8515, 0. , 0.8409],
[3. , 0.8567, 0.8409, 0. ]])
>>> result.cum_current.shape
(10, 10)
circuitscape_one_to_all
¶
circuitscape_one_to_all(
habitat,
points,
*,
all_to_one: bool = False,
habitat_is_resistance: bool = True,
polygons=None,
mask=None,
four_neighbors: bool = False,
avg_resistances: bool = False,
included_pairs=None,
strengths=None,
voltage_maps: bool = False,
current_maps: bool = False,
cum_map: bool = False,
max_map: bool = False,
set_focal_node_currents_to_zero: bool = False,
reclass: dict | None = None,
n_threads: int = 0,
) -> CircuitscapeResult
Circuitscape's one-to-all (or, with all_to_one, all-to-one) mode on rasters.
For every focal point in turn (points as in
circuitscape_pairwise(); the focal points and regions are merged
into the short-circuit regions): one-to-all injects 1 A (or its
strengths value: a two-column table of id and strength) there with
every other focal point a direct ground; all-to-one grounds it and injects
1 A (or the strengths) at the others. Only the point's component is
solved; the points run in parallel. Circuitscape's quirks are kept
(points pruned by included_pairs in both of its modes, excluded points
still sourcing in all-to-one, strengths indexed by position).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
habitat
|
Resistance (or conductance) raster: path, DataArray or array. |
required | |
points
|
Raster of focal-point ids or a |
required | |
all_to_one
|
bool
|
All-to-one instead of one-to-all. |
False
|
habitat_is_resistance
|
bool
|
|
True
|
polygons
|
Short-circuit regions: a raster of ids (> 0); the habitat cells of one region are merged into one node. |
None
|
|
mask
|
Raster; cells where it is NoData or not positive are removed. |
None
|
|
four_neighbors
|
bool
|
Connect the four orthogonal neighbours only. |
False
|
avg_resistances
|
bool
|
Join neighbours by the average of their resistances instead of their conductances. |
False
|
strengths
|
Two-column table (id, strength) of the current of every point. |
None
|
|
included_pairs
|
|
None
|
|
voltage_maps
|
bool
|
Keep the voltage / current map of every pair (or point) in
|
False
|
current_maps
|
bool
|
Keep the voltage / current map of every pair (or point) in
|
False
|
cum_map
|
bool
|
Sum / maximum of the current maps ( |
False
|
max_map
|
bool
|
Sum / maximum of the current maps ( |
False
|
set_focal_node_currents_to_zero
|
bool
|
Current 0 on the focal nodes in the maps. |
False
|
reclass
|
dict | None
|
|
None
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
CircuitscapeResult
|
|
Examples:
>>> import numpy as np
>>> from landschaft import circuit
>>> resistance = np.ones((10, 10))
>>> resistance[:, 5] = 10 # a harder column in the middle
>>> points = np.zeros((10, 10), dtype=int)
>>> points[1, 1], points[8, 1], points[5, 8] = 1, 2, 3
>>> result = circuit.circuitscape_one_to_all(resistance, points, cum_map=True)
>>> result.resistances.round(4)
array([[1. , 0.6438],
[2. , 0.632 ],
[3. , 0.6359]])
circuitscape_ini
¶
Run a Circuitscape INI file, as Circuitscape.compute(path) does.
Raster data give a CircuitscapeResult; networks (data_type =
network, pairwise or advanced) a NetworkResult
(see circuitscape_network_pairwise()).
Paths are read from the working directory, or else next to the INI file.
With write_outputs the maps go next to output_file as Circuitscape
names them (<base>_voltmap, <base>_curmap...; ASCII grids, or
GeoTIFF with write_as_tif) and output_file holds the configuration.
solver, precision and parallelize have no effect: every solve is
exact, in double precision, with n_threads threads.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
The INI file (Circuitscape 5 / 6 options). |
required | |
write_outputs
|
bool
|
Write the output files, as Circuitscape does. |
True
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
CircuitscapeResult or NetworkResult
|
|
Examples:
A pairwise run from GeoTIFF inputs:
>>> import numpy as np
>>> from landschaft import circuit
>>> resistance = np.ones((10, 10))
>>> resistance[:, 5] = 10 # a harder column in the middle
>>> points = np.zeros((10, 10), dtype=int)
>>> points[1, 1], points[8, 1], points[5, 8] = 1, 2, 3
>>> import xarray as xr
>>> import landschaft as ls
>>> from rasterio.transform import from_origin
>>> def as_raster(values):
... grid = xr.DataArray(values.astype("float64"), dims=("y", "x"))
... return grid.rio.write_transform(from_origin(0, 10, 1, 1)).rio.write_crs("EPSG:3035")
>>> _ = ls.write_raster(as_raster(resistance), "resistance.tif")
>>> _ = ls.write_raster(as_raster(points), "points.tif")
>>> _ = open("pairwise.ini", "w").write('''[Options]
... data_type = raster
... scenario = pairwise
... habitat_file = resistance.tif
... habitat_map_is_resistances = True
... point_file = points.tif
... output_file = out/pairwise.out
... ''')
>>> result = circuit.circuitscape_ini("pairwise.ini")
>>> round(float(result.resistances[1, 2]), 4)
0.8515
>>> import os
>>> sorted(os.listdir("out"))
['pairwise.out', 'pairwise_resistances.out', 'pairwise_resistances_3columns.out']
CircuitscapeResult
dataclass
¶
CircuitscapeResult(
voltage: ndarray | None = None,
current: ndarray | None = None,
nodemap: ndarray | None = None,
resistances: ndarray | None = None,
cum_current: ndarray | None = None,
max_current: ndarray | None = None,
maps: dict = dict(),
)
Bases: Saveable
Circuitscape's outputs of a raster run.
Attributes:
| Name | Type | Description |
|---|---|---|
voltage, current |
Per-cell maps (0 on cells without a node, as Circuitscape writes them in advanced mode). |
|
nodemap |
ndarray | None
|
Node number of every cell (-1: no node). |
resistances |
ndarray | None
|
Pairwise and one-to-all: the matrix Circuitscape returns and writes, ids in the first row and column (-1: not connected). |
cum_current, max_current |
Sum and maximum of the pairs' (or points') current maps. |
|
maps |
dict
|
Per-pair (or per-point) maps, keyed by the ids. |
Examples:
>>> import numpy as np
>>> from landschaft import circuit
>>> resistance = np.ones((10, 10))
>>> resistance[:, 5] = 10 # a harder column in the middle
>>> points = np.zeros((10, 10), dtype=int)
>>> points[1, 1], points[8, 1], points[5, 8] = 1, 2, 3
>>> result = circuit.circuitscape_pairwise(resistance, points, cum_map=True)
>>> result.resistances[0, 1:].tolist()
[1.0, 2.0, 3.0]
FocalPoints
dataclass
¶
Focal points as Circuitscape reads them: 1-based rows and columns and ids, sorted by id.
Cells sharing an id form a focal region.
Attributes:
| Name | Type | Description |
|---|---|---|
rows, cols |
1-based row and column of every cell. |
|
ids |
ndarray
|
Focal-point id of every cell. |
Examples:
IncludedPairs
dataclass
¶
Circuitscape's include / exclude pairs.
Attributes:
| Name | Type | Description |
|---|---|---|
mode |
str
|
|
ids |
ndarray
|
Focal-point ids. |
matrix |
ndarray
|
|
Examples:
Solve only the pairs of point 1:
>>> import numpy as np
>>> from landschaft import circuit
>>> resistance = np.ones((10, 10))
>>> resistance[:, 5] = 10 # a harder column in the middle
>>> points = np.zeros((10, 10), dtype=int)
>>> points[1, 1], points[8, 1], points[5, 8] = 1, 2, 3
>>> pairs = circuit.IncludedPairs("include", np.array([1, 2, 3]),
... np.array([[1, 1, 1], [1, 1, 0], [1, 0, 1]]))
>>> sorted(pairs.excluded())
[(2, 3), (3, 2)]
>>> result = circuit.circuitscape_pairwise(resistance, points, included_pairs=pairs)
>>> float(result.resistances[2, 3])
-1.0
excluded
¶
The pairs of ids that are not solved.
Returns:
| Type | Description |
|---|---|
set of (int, int)
|
|
Sources and grounds¶
circuitscape_advanced
¶
circuitscape_advanced(
habitat,
source,
ground,
*,
habitat_is_resistance: bool = True,
ground_is_resistance: bool = True,
polygons=None,
mask=None,
four_neighbors: bool = False,
avg_resistances: bool = False,
remove_src_or_gnd: str = "keepall",
unit_currents: bool = False,
direct_grounds: bool = False,
reclass: dict | None = None,
n_threads: int = 0,
) -> CircuitscapeResult
Circuitscape's advanced mode on rasters (scenario = advanced).
Every cell of habitat (resistance, or conductance with
habitat_is_resistance=False; NaN or -9999 is NoData) with a conductance
0 is a node, joined to its 8 (or 4) neighbours by the average conductance (or, with
avg_resistances, the average resistance) of the two cells; the cells of a short-circuit region (polygons: ids > 0) that holds habitat are one node.sourcegives the current injected per cell,groundthe resistance (or conductance) to ground per cell (NoData: no ground; 0 resistance: a direct ground); values of the cells of one node add up.remove_src_or_gndresolves nodes that are both (Circuitscape's policies, with their quirks:rmvallonly drops the source, and finite grounds dropped byrmvgndstay on the matrix diagonal). Every connected component with a source and a ground is solved exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
habitat
|
Resistance (or conductance) raster: path, DataArray or array. |
required | |
source
|
Current injected per cell (NoData: none). |
required | |
ground
|
Resistance (or conductance) to ground per cell (NoData: no ground; 0 resistance: a direct ground). |
required | |
habitat_is_resistance
|
bool
|
|
True
|
polygons
|
Short-circuit regions: a raster of ids (> 0); the habitat cells of one region are merged into one node. |
None
|
|
mask
|
Raster; cells where it is NoData or not positive are removed. |
None
|
|
four_neighbors
|
bool
|
Connect the four orthogonal neighbours only. |
False
|
avg_resistances
|
bool
|
Join neighbours by the average of their resistances instead of their conductances. |
False
|
ground_is_resistance
|
bool
|
|
True
|
remove_src_or_gnd
|
str
|
Nodes that are both a source and a ground: |
'keepall'
|
unit_currents
|
bool
|
Every source injects 1 A. |
False
|
direct_grounds
|
bool
|
Every ground is a direct ground (0 V). |
False
|
reclass
|
dict | None
|
|
None
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
CircuitscapeResult
|
|
Examples:
>>> import numpy as np
>>> from landschaft import circuit
>>> resistance = np.ones((3, 5))
>>> source = np.full((3, 5), np.nan)
>>> source[1, 0] = 1.0 # 1 A on the left
>>> ground = np.full((3, 5), np.nan)
>>> ground[1, 4] = 0.0 # a direct ground on the right
>>> result = circuit.circuitscape_advanced(resistance, source, ground)
>>> result.voltage[1].round(3)
array([0.887, 0.615, 0.444, 0.272, 0. ])
advanced
¶
advanced(
conductance, source, ground, *, four_neighbors: bool = False, n_threads: int = 0
) -> AdvancedResult
Currents and voltages of a raster circuit with sources and grounds.
Circuitscape 6.1's compute_omniscape_current (advanced mode, the
solve behind every Omniscape moving window): each cell with a
conductance > 0 is a node joined to its 8 neighbours (4 with
four_neighbors) by the average conductance of the two cells, over √2
on the diagonals. source injects current (amps) into its cells;
ground connects cells to ground with the given conductance (inf:
a direct ground at 0 V). A cell that is both loses its source. Every
connected component with a source and a ground is solved exactly (sparse
Cholesky in a nested-dissection order; the components in parallel), and
a node's current is the larger of its inflow and outflow, leaving out
branch currents under 1e-8 of the largest one, as Circuitscape does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conductance
|
Rasters of the same shape (paths, DataArrays or arrays). NaN (or the
nodata value) in |
required | |
source
|
Rasters of the same shape (paths, DataArrays or arrays). NaN (or the
nodata value) in |
required | |
ground
|
Rasters of the same shape (paths, DataArrays or arrays). NaN (or the
nodata value) in |
required | |
four_neighbors
|
bool
|
Connect the four orthogonal neighbours only. |
False
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
AdvancedResult
|
|
See Also
circuitscape_advanced : the same with Circuitscape's options and inputs.
Examples:
One amp injected on the left of a 3 x 5 grid, grounded on the right:
>>> import numpy as np
>>> from landschaft import circuit
>>> conductance = np.ones((3, 5))
>>> source = np.zeros((3, 5))
>>> source[1, 0] = 1.0
>>> ground = np.zeros((3, 5))
>>> ground[1, 4] = np.inf
>>> result = circuit.advanced(conductance, source, ground)
>>> result.voltage.round(3)
array([[0.716, 0.615, 0.444, 0.272, 0.172],
[0.887, 0.615, 0.444, 0.272, 0. ],
[0.716, 0.615, 0.444, 0.272, 0.172]])
>>> result.current.round(3)
array([[0.172, 0.293, 0.293, 0.293, 0.172],
[1. , 0.414, 0.414, 0.414, 1. ],
[0.172, 0.293, 0.293, 0.293, 0.172]])
AdvancedResult
dataclass
¶
Bases: Saveable
Node currents and voltages of advanced().
DataArrays when the conductance was one, arrays otherwise.
Attributes:
| Name | Type | Description |
|---|---|---|
current |
object
|
Current through every cell ( |
voltage |
object
|
Node voltages (0 in components without a source or a ground, NaN on cells without a node). |
Networks¶
circuitscape_network_pairwise
¶
circuitscape_network_pairwise(
graph,
points,
*,
graph_is_resistance: bool = True,
included_pairs=None,
voltage_maps: bool = False,
current_maps: bool = False,
n_threads: int = 0,
) -> NetworkResult
Circuitscape's pairwise mode on a network (data_type = network,
scenario = pairwise).
Effective resistances between every pair of focal nodes (points: node
numbers, a path or an array; numbered from 0 they are shifted by 1) on the
graph of circuitscape_network_advanced(). Every component is
factorised once and its pairs solved in batches (Circuitscape's resistance
shortcut is for rasters only). included_pairs (IncludedPairs
or a path) restricts the pairs. With voltage_maps every pair's node
voltages, and with any of the two options every pair's node and branch
currents (as Circuitscape writes them) go to result.maps, and the node
and branch currents are summed over the pairs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Edge list: path or |
required | |
points
|
Focal node numbers: path or array. |
required | |
graph_is_resistance
|
bool
|
The edge values are resistances (default) or conductances. |
True
|
included_pairs
|
|
None
|
|
voltage_maps
|
bool
|
Keep every pair's node voltages / node and branch currents in
|
False
|
current_maps
|
bool
|
Keep every pair's node voltages / node and branch currents in
|
False
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
NetworkResult
|
|
Examples:
>>> import numpy as np
>>> from landschaft import circuit
>>> graph = np.array([[1, 2, 1.0], # node, node, resistance
... [2, 3, 1.0],
... [1, 3, 2.0],
... [3, 4, 0.5]])
>>> result = circuit.circuitscape_network_pairwise(graph, [1, 3, 4])
>>> result.resistances
array([[0. , 1. , 3. , 4. ],
[1. , 0. , 1. , 1.5],
[3. , 1. , 0. , 0.5],
[4. , 1.5, 0.5, 0. ]])
circuitscape_network_advanced
¶
circuitscape_network_advanced(
graph,
sources,
grounds,
*,
graph_is_resistance: bool = True,
ground_is_resistance: bool = True,
remove_src_or_gnd: str = "keepall",
n_threads: int = 0,
) -> NetworkResult
Circuitscape's advanced mode on a network (data_type = network,
scenario = advanced).
graph: edge list (node, node, resistance or, with
graph_is_resistance=False, conductance), a path or a (k, 3)
array; nodes numbered from 1, or from 0 (then every number, the sources'
and grounds' too, is shifted by 1 as Circuitscape.jl does). Duplicate
edges add up; edges whose conductances add up to 0 are left out.
sources and grounds: (node, value) lists (paths or arrays),
the current injected and the resistance (or conductance) to ground; a
ground resistance of 0 is a direct ground. Every connected component with
a source and a ground is solved exactly (remove_src_or_gnd as in
circuitscape_advanced()).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Edge list: path or |
required | |
sources
|
|
required | |
grounds
|
|
required | |
graph_is_resistance
|
bool
|
The values are resistances (default) or conductances. |
True
|
ground_is_resistance
|
bool
|
The values are resistances (default) or conductances. |
True
|
remove_src_or_gnd
|
str
|
Nodes that are both: |
'keepall'
|
n_threads
|
int
|
Threads (0 or |
0
|
Returns:
| Type | Description |
|---|---|
NetworkResult
|
|
Examples:
One amp into node 1, node 4 grounded:
>>> import numpy as np
>>> from landschaft import circuit
>>> graph = np.array([[1, 2, 1.0], # node, node, resistance
... [2, 3, 1.0],
... [1, 3, 2.0],
... [3, 4, 0.5]])
>>> result = circuit.circuitscape_network_advanced(graph, [[1, 1.0]], [[4, 0.0]])
>>> result.voltages
array([[1. , 1.5],
[2. , 1. ],
[3. , 0.5],
[4. , 0. ]])
>>> result.branch_currents
array([[1. , 2. , 0.5],
[1. , 3. , 0.5],
[2. , 3. , 0.5],
[3. , 4. , 1. ]])
NetworkResult
dataclass
¶
NetworkResult(
voltages: ndarray | None = None,
node_currents: ndarray | None = None,
branch_currents: ndarray | None = None,
resistances: ndarray | None = None,
cum_node_currents: ndarray | None = None,
cum_branch_currents: ndarray | None = None,
maps: dict = dict(),
n_components: int = 0,
)
Bases: Saveable
Circuitscape's network outputs, with the node numbers it writes (from 1).
Attributes:
| Name | Type | Description |
|---|---|---|
voltages |
ndarray | None
|
Advanced: |
node_currents, branch_currents |
Advanced: |
|
resistances |
ndarray | None
|
Pairwise: effective resistances, ids in the first row and column (-1: not connected). |
cum_node_currents, cum_branch_currents |
Pairwise with maps: node and branch currents summed over the pairs. |
|
maps |
dict
|
Per-pair tables keyed |
n_components |
int
|
Number of connected components of the graph. |
Examples:
>>> import numpy as np
>>> from landschaft import circuit
>>> graph = np.array([[1, 2, 1.0], # node, node, resistance
... [2, 3, 1.0],
... [1, 3, 2.0],
... [3, 4, 0.5]])
>>> result = circuit.circuitscape_network_advanced(graph, [[1, 1.0]], [[4, 0.0]])
>>> result.n_components
1
read_graph
¶
Read a Circuitscape edge list, as its load_graph.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
edges
|
Path of a text file or |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
|
Examples: