Spatial API Reference (Ecospace)¶
Ecospace spatial modeling — grid creation, dispersal, connectivity, habitat, and simulation integration.
Core Data Structures¶
pypath.spatial.ecospace_params ¶
ECOSPACE spatial parameter data structures.
This module defines the core data structures for spatial-temporal ecosystem modeling: - EcospaceGrid: Spatial grid configuration (irregular polygons) - EcospaceParams: Spatial parameters (habitat, dispersal, external flux) - SpatialState: Extended state for spatial simulation - ExternalFluxTimeseries: External flux data from ocean models
EcospaceGrid
dataclass
¶
Spatial grid configuration using irregular polygons.
Attributes:
| Name | Type | Description |
|---|---|---|
n_patches |
int
|
Number of spatial patches/cells |
patch_ids |
ndarray
|
Unique identifiers for each patch [n_patches] |
patch_areas |
ndarray
|
Area of each patch in km² [n_patches] |
patch_centroids |
ndarray
|
(lon, lat) coordinates of patch centroids [n_patches, 2] |
adjacency_matrix |
csr_matrix
|
Sparse adjacency matrix [n_patches, n_patches] 1 if patches share border, 0 otherwise |
edge_lengths |
Dict[Tuple[int, int], float]
|
Border lengths (km) for adjacent patch pairs |
crs |
str
|
Coordinate reference system (default: "EPSG:4326") |
geometry |
Optional[GeoDataFrame]
|
GeoDataFrame with polygon geometries (if available) |
Source code in pypath/spatial/ecospace_params.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
__post_init__ ¶
__post_init__()
Validate grid data.
Source code in pypath/spatial/ecospace_params.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
from_regular_grid
classmethod
¶
from_regular_grid(bounds: Tuple[float, float, float, float], nx: int, ny: int) -> EcospaceGrid
Create regular rectangular grid (for testing).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bounds
|
Tuple[float, float, float, float]
|
(min_lon, min_lat, max_lon, max_lat) |
required |
nx
|
int
|
Number of grid cells in x direction |
required |
ny
|
int
|
Number of grid cells in y direction |
required |
Returns:
| Type | Description |
|---|---|
EcospaceGrid
|
|
Source code in pypath/spatial/ecospace_params.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | |
from_shapefile
classmethod
¶
from_shapefile(filepath: str, id_field: str = 'id', area_field: Optional[str] = None, crs: Optional[str] = None) -> EcospaceGrid
Create grid from shapefile or GeoJSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to .shp, .geojson, or .gpkg file |
required |
id_field
|
str
|
Field containing unique patch IDs (default: "id") |
'id'
|
area_field
|
str
|
Field with pre-computed areas in km² If None, calculates from geometry |
None
|
crs
|
str
|
Force coordinate reference system (e.g., "EPSG:4326") |
None
|
Returns:
| Type | Description |
|---|---|
EcospaceGrid
|
|
Raises:
| Type | Description |
|---|---|
ImportError
|
If geopandas is not installed |
Source code in pypath/spatial/ecospace_params.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
get_edge_length ¶
get_edge_length(patch_i: int, patch_j: int) -> float
Get border length between two patches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
patch_i
|
int
|
Patch indices |
required |
patch_j
|
int
|
Patch indices |
required |
Returns:
| Type | Description |
|---|---|
float
|
Border length in km (0 if not adjacent) |
Source code in pypath/spatial/ecospace_params.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
get_neighbors ¶
get_neighbors(patch_idx: int) -> np.ndarray
Get indices of neighboring patches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
patch_idx
|
int
|
Index of patch |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Indices of neighboring patches |
Source code in pypath/spatial/ecospace_params.py
162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
EcospaceParams
dataclass
¶
Spatial parameters for ECOSPACE simulation.
This extends the standard Ecosim parameters with spatial components. If ecospace=None in RsimScenario, simulation runs as non-spatial.
Attributes:
| Name | Type | Description |
|---|---|---|
grid |
EcospaceGrid
|
Spatial grid configuration |
habitat_preference |
ndarray
|
Habitat preference/suitability [n_groups, n_patches] Values 0-1 where 1 = optimal habitat |
habitat_capacity |
ndarray
|
Habitat capacity multiplier [n_groups, n_patches] Affects local carrying capacity (1.0 = no effect) |
dispersal_rate |
ndarray
|
Diffusion coefficient (km²/month) [n_groups] 0 = no dispersal |
advection_enabled |
ndarray
|
Enable habitat-directed movement [n_groups], boolean |
gravity_strength |
ndarray
|
Strength of biomass-weighted movement [n_groups] 0 = no gravity effect |
external_flux |
Optional[ExternalFluxTimeseries]
|
Externally provided flux (e.g., from ocean models) If provided for a group, overrides model-calculated dispersal |
environmental_drivers |
Optional[object]
|
Time-varying environmental drivers (EnvironmentalDrivers instance) |
Source code in pypath/spatial/ecospace_params.py
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 | |
__post_init__ ¶
__post_init__()
Validate spatial parameters.
Source code in pypath/spatial/ecospace_params.py
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 | |
ExternalFluxTimeseries
dataclass
¶
Externally generated flux timeseries from ocean models.
Allows users to provide pre-computed transport between patches from: - Ocean circulation models (ROMS, MITgcm, HYCOM) - Particle tracking systems (Ichthyop, OpenDrift, Parcels) - Connectivity matrices (genetic data, telemetry, mark-recapture)
Attributes:
| Name | Type | Description |
|---|---|---|
flux_data |
Union[ndarray, csr_matrix]
|
Flux timeseries with shape: - [n_timesteps, n_groups, n_patches, n_patches] for full format - Sparse format supported for memory efficiency flux_data[t, g, p, q] = flux from patch p to patch q for group g at time t |
times |
ndarray
|
Time points (in years) corresponding to flux_data [n_timesteps] |
group_indices |
ndarray
|
Which groups have external flux [n_groups_with_flux] Groups not in this list will use model-calculated dispersal |
interpolate |
bool
|
Whether to use temporal interpolation (default: True) |
format |
str
|
Data format: "flux_matrix" or "connectivity_matrix" - "flux_matrix": Direct flux values (biomass/time) - "connectivity_matrix": Proportions (0-1) scaled by biomass |
validated |
bool
|
Whether flux conservation has been validated |
Source code in pypath/spatial/ecospace_params.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | |
__post_init__ ¶
__post_init__()
Validate external flux data.
Source code in pypath/spatial/ecospace_params.py
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | |
from_netcdf
classmethod
¶
from_netcdf(filepath: str, time_var: str = 'time', flux_var: str = 'flux', group_mapping: Optional[Dict[str, int]] = None) -> ExternalFluxTimeseries
Load external flux from NetCDF file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to NetCDF file |
required |
time_var
|
str
|
Name of time variable |
'time'
|
flux_var
|
str
|
Name of flux variable |
'flux'
|
group_mapping
|
dict
|
Map species names to group indices |
None
|
Returns:
| Type | Description |
|---|---|
ExternalFluxTimeseries
|
|
Source code in pypath/spatial/ecospace_params.py
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | |
get_flux_at_time ¶
get_flux_at_time(t: float, group_idx: int) -> np.ndarray
Get flux matrix at given time for group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
float
|
Simulation time (years) |
required |
group_idx
|
int
|
Group index |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Flux matrix [n_patches, n_patches] flux[p, q] = flux from patch p to patch q |
Source code in pypath/spatial/ecospace_params.py
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | |
SpatialState
dataclass
¶
Extended state for spatial simulation.
Attributes:
| Name | Type | Description |
|---|---|---|
Biomass |
ndarray
|
Biomass state [n_groups+1, n_patches] Index 0 = "Outside" (detritus, patch-invariant) |
N |
Optional[ndarray]
|
Numbers (for multi-stanza groups) [n_groups+1, n_patches] |
Ftime |
Optional[ndarray]
|
Foraging time [n_groups+1, n_patches] |
Source code in pypath/spatial/ecospace_params.py
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | |
collapse_to_total ¶
collapse_to_total() -> np.ndarray
Sum biomass across patches for compatibility.
Returns:
| Type | Description |
|---|---|
ndarray
|
Total biomass [n_groups+1] |
Source code in pypath/spatial/ecospace_params.py
463 464 465 466 467 468 469 470 471 | |
get_patch_biomass ¶
get_patch_biomass(patch_idx: int) -> np.ndarray
Get biomass in a specific patch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
patch_idx
|
int
|
Patch index |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Biomass in patch [n_groups+1] |
Source code in pypath/spatial/ecospace_params.py
473 474 475 476 477 478 479 480 481 482 483 484 485 486 | |
Grid Creation & GIS Utilities¶
pypath.spatial.gis_utils ¶
GIS utilities for ECOSPACE spatial grids.
Functions for loading spatial grids from shapefiles/GeoJSON and creating regular grids for testing.
create_1d_grid ¶
create_1d_grid(n_patches: int, spacing: float = 1.0) -> 'EcospaceGrid'
Create 1D chain of patches for testing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_patches
|
int
|
Number of patches |
required |
spacing
|
float
|
Distance between patch centers (km) |
1.0
|
Returns:
| Type | Description |
|---|---|
EcospaceGrid
|
|
Source code in pypath/spatial/gis_utils.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | |
create_regular_grid ¶
create_regular_grid(bounds: Tuple[float, float, float, float], nx: int, ny: int) -> 'EcospaceGrid'
Create regular rectangular grid for testing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bounds
|
Tuple[float, float, float, float]
|
(min_lon, min_lat, max_lon, max_lat) |
required |
nx
|
int
|
Number of grid cells in x direction |
required |
ny
|
int
|
Number of grid cells in y direction |
required |
Returns:
| Type | Description |
|---|---|
EcospaceGrid
|
|
Source code in pypath/spatial/gis_utils.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
load_spatial_grid ¶
load_spatial_grid(filepath: str, id_field: str = 'id', area_field: Optional[str] = None, crs: Optional[str] = None) -> 'EcospaceGrid'
Load spatial grid from shapefile or GeoJSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to .shp, .geojson, or .gpkg file |
required |
id_field
|
str
|
Field containing unique patch IDs |
'id'
|
area_field
|
str
|
Field with pre-computed areas in km² If None, calculates from geometry |
None
|
crs
|
str
|
Force coordinate reference system (e.g., "EPSG:4326") |
None
|
Returns:
| Type | Description |
|---|---|
EcospaceGrid
|
|
Raises:
| Type | Description |
|---|---|
ImportError
|
If geopandas is not installed |
FileNotFoundError
|
If filepath does not exist |
ValueError
|
If required fields are missing |
Source code in pypath/spatial/gis_utils.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
Connectivity¶
pypath.spatial.connectivity ¶
Connectivity and adjacency calculations for spatial grids.
Functions for building adjacency matrices from polygon geometries, calculating edge properties, and spatial indexing.
build_adjacency_from_gdf ¶
build_adjacency_from_gdf(gdf: 'gpd.GeoDataFrame', method: str = 'rook') -> Tuple[scipy.sparse.csr_matrix, Dict]
Build adjacency matrix from GeoDataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gdf
|
GeoDataFrame
|
GeoDataFrame with polygon geometries |
required |
method
|
str
|
Adjacency method: - "rook": Shared border (edge) required - "queen": Shared point or border |
'rook'
|
Returns:
| Name | Type | Description |
|---|---|---|
adjacency |
csr_matrix
|
Sparse adjacency matrix [n_patches, n_patches] adjacency[i, j] = 1 if patches i and j are adjacent |
metadata |
dict
|
Dictionary with: - 'border_lengths': Dict[(i, j)] = border length in km - 'method': adjacency method used |
Raises:
| Type | Description |
|---|---|
ImportError
|
If geopandas is not installed |
Source code in pypath/spatial/connectivity.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
build_distance_matrix ¶
build_distance_matrix(grid: 'EcospaceGrid', method: str = 'haversine') -> np.ndarray
Build distance matrix between all patch pairs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
EcospaceGrid
|
Spatial grid |
required |
method
|
str
|
Distance calculation method: - "haversine": Great circle distance (accurate for lat/lon) - "euclidean": Euclidean distance (fast, approximate) |
'haversine'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Distance matrix [n_patches, n_patches] in km |
Source code in pypath/spatial/connectivity.py
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | |
calculate_patch_distances ¶
calculate_patch_distances(grid: 'EcospaceGrid') -> np.ndarray
Calculate pairwise distances between patch centroids.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
EcospaceGrid
|
Spatial grid |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Distance matrix [n_patches, n_patches] in km |
Source code in pypath/spatial/connectivity.py
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
find_k_nearest_neighbors ¶
find_k_nearest_neighbors(grid: 'EcospaceGrid', k: int, method: str = 'haversine') -> np.ndarray
Find k nearest neighbors for each patch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
EcospaceGrid
|
Spatial grid |
required |
k
|
int
|
Number of nearest neighbors to find |
required |
method
|
str
|
Distance calculation method |
'haversine'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Neighbor indices [n_patches, k] neighbors[i, :] = indices of k nearest patches to patch i |
Source code in pypath/spatial/connectivity.py
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | |
get_connectivity_graph_stats ¶
get_connectivity_graph_stats(adjacency: csr_matrix) -> Dict
Calculate graph statistics from adjacency matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adjacency
|
csr_matrix
|
Adjacency matrix |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary with: - 'n_nodes': Number of patches - 'n_edges': Number of edges (undirected) - 'mean_degree': Average number of neighbors - 'max_degree': Maximum number of neighbors - 'min_degree': Minimum number of neighbors - 'isolated_patches': Patches with no neighbors |
Source code in pypath/spatial/connectivity.py
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | |
haversine_distance ¶
haversine_distance(lon1: ndarray, lat1: ndarray, lon2: ndarray, lat2: ndarray) -> np.ndarray
Calculate great circle distance between points.
Uses the haversine formula for accurate distances on a sphere.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lon1
|
ndarray
|
Longitude and latitude of first point(s) in degrees |
required |
lat1
|
ndarray
|
Longitude and latitude of first point(s) in degrees |
required |
lon2
|
ndarray
|
Longitude and latitude of second point(s) in degrees |
required |
lat2
|
ndarray
|
Longitude and latitude of second point(s) in degrees |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Distance in kilometers |
Source code in pypath/spatial/connectivity.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
validate_adjacency_symmetry ¶
validate_adjacency_symmetry(adjacency: csr_matrix) -> bool
Check if adjacency matrix is symmetric.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
adjacency
|
csr_matrix
|
Adjacency matrix |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if symmetric (within tolerance) |
Source code in pypath/spatial/connectivity.py
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
Dispersal¶
pypath.spatial.dispersal ¶
Dispersal and movement mechanics for ECOSPACE.
Implements spatial flux calculations: - Diffusion (Fick's law) - Habitat-directed advection - Gravity models (biomass-weighted movement) - Hybrid flux (external + model-calculated)
apply_external_flux ¶
apply_external_flux(biomass_vector: ndarray, external_flux: ExternalFluxTimeseries, group_idx: int, t: float) -> np.ndarray
Apply externally provided flux matrix to biomass.
External flux can come from: - Ocean circulation models (ROMS, MITgcm, HYCOM) - Particle tracking (Ichthyop, OpenDrift, Parcels) - Connectivity matrices (genetic data, telemetry)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
biomass_vector
|
ndarray
|
Biomass in each patch [n_patches] |
required |
external_flux
|
ExternalFluxTimeseries
|
External flux data |
required |
group_idx
|
int
|
Group index |
required |
t
|
float
|
Simulation time (years) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Net flux for each patch [n_patches] |
Notes
flux_matrix[p, q] = flux from patch p to patch q net_flux[p] = Σ_q flux_matrix[q, p] - Σ_q flux_matrix[p, q] = inflow - outflow
Source code in pypath/spatial/dispersal.py
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | |
apply_flux_limiter ¶
apply_flux_limiter(flux: ndarray, biomass: ndarray, dt: float = 1.0) -> np.ndarray
Apply flux limiter to prevent negative biomass.
Limits outflow so that biomass cannot go negative during the timestep.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flux
|
ndarray
|
Net flux [n_patches] |
required |
biomass
|
ndarray
|
Current biomass [n_patches] |
required |
dt
|
float
|
Timestep size (fraction of month, default: 1.0) |
1.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Limited flux [n_patches] |
Source code in pypath/spatial/dispersal.py
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 | |
calculate_spatial_flux ¶
calculate_spatial_flux(state: ndarray, ecospace: EcospaceParams, params: dict, t: float) -> np.ndarray
Calculate total spatial flux (diffusion + advection + external).
Priority order for each group: 1. If external_flux provided for group -> use external 2. Else if dispersal_rate > 0 -> calculate model flux 3. Else -> no movement for this group
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
ndarray
|
Spatial state [n_groups+1, n_patches] |
required |
ecospace
|
EcospaceParams
|
Spatial parameters |
required |
params
|
dict
|
Ecosim parameters |
required |
t
|
float
|
Simulation time (years) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Spatial flux [n_groups+1, n_patches] flux[g, p] = net flux for group g in patch p |
Source code in pypath/spatial/dispersal.py
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | |
diffusion_flux ¶
diffusion_flux(biomass_vector: ndarray, dispersal_rate: float, grid: EcospaceGrid, adjacency: csr_matrix) -> np.ndarray
Calculate diffusion flux using Fick's law.
Flux between adjacent patches follows: flux_pq = -D * (B_p - B_q) * (border_length / distance)
where D is the dispersal rate (diffusion coefficient).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
biomass_vector
|
ndarray
|
Biomass in each patch [n_patches] |
required |
dispersal_rate
|
float
|
Diffusion coefficient (km²/month) Typical values: 1-100 km²/month for fish |
required |
grid
|
EcospaceGrid
|
Spatial grid configuration |
required |
adjacency
|
csr_matrix
|
Adjacency matrix [n_patches, n_patches] |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Net flux for each patch [n_patches] - Positive = inflow (biomass increases) - Negative = outflow (biomass decreases) - Sum over all patches = 0 (conservation) |
Source code in pypath/spatial/dispersal.py
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | |
gravity_model_flux ¶
gravity_model_flux(biomass_vector: ndarray, attractiveness: ndarray, gravity_strength: float, grid: EcospaceGrid, adjacency: csr_matrix, distance_decay: float = 1.0) -> np.ndarray
Calculate gravity model flux (biomass-weighted attraction).
Movement rate from patch i to patch j: flux_ij ∝ (biomass_i) * (attractiveness_j) / (distance_ij ^ decay)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
biomass_vector
|
ndarray
|
Biomass in each patch [n_patches] |
required |
attractiveness
|
ndarray
|
Attractiveness of each patch [n_patches] Could be: biomass (aggregation), habitat quality, resources |
required |
gravity_strength
|
float
|
Overall movement rate |
required |
grid
|
EcospaceGrid
|
Spatial grid |
required |
adjacency
|
csr_matrix
|
Adjacency matrix |
required |
distance_decay
|
float
|
Distance decay exponent (default: 1.0) Higher values = stronger distance penalty |
1.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Net flux [n_patches] |
Source code in pypath/spatial/dispersal.py
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
habitat_advection ¶
habitat_advection(biomass_vector: ndarray, habitat_preference: ndarray, gravity_strength: float, grid: EcospaceGrid, adjacency: csr_matrix) -> np.ndarray
Calculate habitat-directed movement (advection).
Organisms move toward patches with higher habitat quality, proportional to biomass and habitat gradient.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
biomass_vector
|
ndarray
|
Biomass in each patch [n_patches] |
required |
habitat_preference
|
ndarray
|
Habitat quality [n_patches], values 0-1 |
required |
gravity_strength
|
float
|
Movement strength (0-1) 0 = no movement, 1 = strong habitat-seeking |
required |
grid
|
EcospaceGrid
|
Spatial grid configuration |
required |
adjacency
|
csr_matrix
|
Adjacency matrix |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Net flux for each patch [n_patches] |
Source code in pypath/spatial/dispersal.py
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | |
validate_flux_conservation ¶
validate_flux_conservation(flux: ndarray, tolerance: float = 1e-08) -> bool
Validate that spatial flux conserves mass.
The sum of flux over all patches should be zero (no net creation or destruction of biomass).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flux
|
ndarray
|
Flux array [n_groups, n_patches] or [n_patches] |
required |
tolerance
|
float
|
Numerical tolerance for zero (default: 1e-8) |
1e-08
|
Returns:
| Type | Description |
|---|---|
bool
|
True if flux is conserved (within tolerance) |
Source code in pypath/spatial/dispersal.py
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | |
Habitat Suitability¶
pypath.spatial.habitat ¶
Habitat capacity and response functions for ECOSPACE.
Implements functions that map environmental conditions to habitat suitability: - Gaussian response (optimal value ± tolerance) - Threshold response (trapezoidal) - Linear response - Custom response functions - Multi-driver habitat capacity calculations
apply_habitat_preference_and_suitability ¶
apply_habitat_preference_and_suitability(base_preference: ndarray, environmental_suitability: ndarray, combine_method: str = 'multiplicative') -> np.ndarray
Combine base habitat preference with environmental suitability.
Base preference represents intrinsic patch quality (structure, substrate), while environmental suitability represents dynamic factors (temperature).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_preference
|
ndarray
|
Base habitat preference [n_patches], values in [0, 1] |
required |
environmental_suitability
|
ndarray
|
Environmental suitability [n_patches], values in [0, 1] |
required |
combine_method
|
str
|
How to combine: - "multiplicative": preference * suitability (default) - "minimum": min(preference, suitability) - "average": (preference + suitability) / 2 |
'multiplicative'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Combined habitat quality [n_patches], values in [0, 1] |
Examples:
>>> base_pref = np.array([1.0, 0.5, 0.8]) # Intrinsic quality
>>> env_suit = np.array([0.8, 1.0, 0.6]) # Environmental suitability
>>>
>>> # Multiplicative (strict)
>>> apply_habitat_preference_and_suitability(base_pref, env_suit, "multiplicative")
array([0.8 , 0.5 , 0.48])
>>>
>>> # Minimum (limiting factor)
>>> apply_habitat_preference_and_suitability(base_pref, env_suit, "minimum")
array([0.8, 0.5, 0.6])
Source code in pypath/spatial/habitat.py
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | |
calculate_habitat_suitability ¶
calculate_habitat_suitability(environmental_values: ndarray, response_functions: List[Callable], combine_method: str = 'multiplicative') -> np.ndarray
Calculate habitat suitability from multiple environmental drivers.
Combines multiple environmental responses into overall habitat suitability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
environmental_values
|
ndarray
|
Environmental values [n_patches, n_drivers] |
required |
response_functions
|
list of callable
|
Response function for each driver Must match number of drivers |
required |
combine_method
|
str
|
How to combine responses: - "multiplicative": product of all responses (default) - "minimum": minimum of all responses - "geometric_mean": geometric mean - "average": arithmetic mean |
'multiplicative'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Habitat suitability [n_patches], values in [0, 1] |
Examples:
>>> # Temperature and depth responses
>>> env = np.array([
... [10, 50], # Patch 0: 10°C, 50m
... [15, 100], # Patch 1: 15°C, 100m
... [8, 30] # Patch 2: 8°C, 30m
... ])
>>>
>>> temp_response = create_gaussian_response(optimal_value=12, tolerance=4)
>>> depth_response = create_linear_response(min_value=0, max_value=200)
>>>
>>> suitability = calculate_habitat_suitability(
... env,
... [temp_response, depth_response],
... combine_method="multiplicative"
... )
Source code in pypath/spatial/habitat.py
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | |
create_gaussian_response ¶
create_gaussian_response(optimal_value: float, tolerance: float, min_value: Optional[float] = None, max_value: Optional[float] = None) -> Callable[[np.ndarray], np.ndarray]
Create Gaussian (normal) response function.
Habitat suitability peaks at optimal value and decreases with distance from optimum:
response(x) = exp(-((x - optimal) / tolerance)²)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
optimal_value
|
float
|
Optimal environmental value (maximum suitability) |
required |
tolerance
|
float
|
Tolerance range (standard deviation) Suitability = 0.607 at optimal ± tolerance |
required |
min_value
|
float
|
Minimum valid environmental value (hard cutoff) Values below this return 0 suitability |
None
|
max_value
|
float
|
Maximum valid environmental value (hard cutoff) |
None
|
Returns:
| Type | Description |
|---|---|
callable
|
Response function: env_value -> suitability [0, 1] |
Examples:
>>> # Cod prefer 8°C ± 4°C
>>> response = create_gaussian_response(optimal_value=8.0, tolerance=4.0)
>>> response(np.array([4, 8, 12, 16]))
array([0.60653066, 1. , 0.60653066, 0.13533528])
>>> # With hard cutoffs
>>> response = create_gaussian_response(
... optimal_value=15.0,
... tolerance=5.0,
... min_value=5.0,
... max_value=25.0
... )
>>> response(np.array([0, 10, 15, 20, 30]))
array([0. , 0.60653066, 1. , 0.60653066, 0. ])
Source code in pypath/spatial/habitat.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
create_linear_response ¶
create_linear_response(min_value: float, max_value: float, increasing: bool = True) -> Callable[[np.ndarray], np.ndarray]
Create linear response function.
Suitability increases or decreases linearly with environmental value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_value
|
float
|
Environmental value where suitability = 0 (or 1 if decreasing) |
required |
max_value
|
float
|
Environmental value where suitability = 1 (or 0 if decreasing) |
required |
increasing
|
bool
|
If True, suitability increases with env value (default) If False, suitability decreases |
True
|
Returns:
| Type | Description |
|---|---|
callable
|
Response function: env_value -> suitability [0, 1] |
Examples:
>>> # Deeper is better (increasing)
>>> response = create_linear_response(min_value=0, max_value=100, increasing=True)
>>> response(np.array([0, 50, 100]))
array([0. , 0.5, 1. ])
>>> # Shallower is better (decreasing)
>>> response = create_linear_response(min_value=0, max_value=100, increasing=False)
>>> response(np.array([0, 50, 100]))
array([1. , 0.5, 0. ])
Source code in pypath/spatial/habitat.py
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
create_step_response ¶
create_step_response(threshold: float, above_threshold: float = 1.0, below_threshold: float = 0.0) -> Callable[[np.ndarray], np.ndarray]
Create step (binary) response function.
Suitability is constant above/below threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
Threshold value |
required |
above_threshold
|
float
|
Suitability when env >= threshold (default: 1.0) |
1.0
|
below_threshold
|
float
|
Suitability when env < threshold (default: 0.0) |
0.0
|
Returns:
| Type | Description |
|---|---|
callable
|
Response function: env_value -> suitability |
Examples:
>>> # Requires minimum depth of 50m
>>> response = create_step_response(threshold=50, above_threshold=1.0, below_threshold=0.0)
>>> response(np.array([30, 50, 100]))
array([0., 1., 1.])
Source code in pypath/spatial/habitat.py
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
create_threshold_response ¶
create_threshold_response(min_value: float, max_value: float, optimal_min: Optional[float] = None, optimal_max: Optional[float] = None) -> Callable[[np.ndarray], np.ndarray]
Create threshold (trapezoidal) response function.
Response forms a trapezoid: - 0 below min_value - Linear increase from min_value to optimal_min - 1 (optimal) from optimal_min to optimal_max - Linear decrease from optimal_max to max_value - 0 above max_value
If optimal_min/max not specified, response is triangular with peak at midpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_value
|
float
|
Minimum tolerable value (suitability = 0) |
required |
max_value
|
float
|
Maximum tolerable value (suitability = 0) |
required |
optimal_min
|
float
|
Start of optimal range (suitability = 1) Default: midpoint between min and max |
None
|
optimal_max
|
float
|
End of optimal range (suitability = 1) Default: same as optimal_min (triangular response) |
None
|
Returns:
| Type | Description |
|---|---|
callable
|
Response function: env_value -> suitability [0, 1] |
Examples:
>>> # Herring tolerate 0-20°C, optimal 8-12°C
>>> response = create_threshold_response(
... min_value=0.0,
... max_value=20.0,
... optimal_min=8.0,
... optimal_max=12.0
... )
>>> response(np.array([-5, 0, 4, 10, 16, 20, 25]))
array([0. , 0. , 0.5, 1. , 0.5, 0. , 0. ])
>>> # Triangular response (no optimal plateau)
>>> response = create_threshold_response(min_value=5, max_value=25)
>>> response(np.array([5, 15, 25]))
array([0., 1., 0.])
Source code in pypath/spatial/habitat.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
Environmental Drivers¶
pypath.spatial.environmental ¶
Environmental drivers for ECOSPACE.
Implements time-varying spatial environmental fields: - Temperature, salinity, depth, currents - Multiple environmental layers - Temporal interpolation - Integration with habitat capacity models
EnvironmentalDrivers ¶
Manager for multiple environmental layers.
Coordinates multiple environmental variables and provides combined environmental state for habitat calculations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layers
|
dict
|
Dictionary of EnvironmentalLayer objects {name: layer} |
None
|
Examples:
>>> drivers = EnvironmentalDrivers()
>>> drivers.add_layer(temp_layer)
>>> drivers.add_layer(depth_layer)
>>>
>>> # Get all drivers at specific time
>>> env = drivers.get_drivers_at_time(t=0.5) # [n_patches, n_layers]
>>>
>>> # Get specific layer
>>> temp = drivers.get_layer_at_time('temperature', t=0.5) # [n_patches]
Source code in pypath/spatial/environmental.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |
__init__ ¶
__init__(layers: Optional[Dict[str, EnvironmentalLayer]] = None)
Initialize environmental drivers.
Source code in pypath/spatial/environmental.py
192 193 194 195 | |
add_layer ¶
add_layer(layer: EnvironmentalLayer)
Add environmental layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
layer
|
EnvironmentalLayer
|
Environmental layer to add |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If layer name already exists or n_patches doesn't match |
Source code in pypath/spatial/environmental.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | |
get_drivers_at_time ¶
get_drivers_at_time(t: float, layer_names: Optional[list] = None) -> np.ndarray
Get all environmental drivers at time t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
float
|
Time (years) |
required |
layer_names
|
list
|
Specific layers to include (default: all layers) Order matters - returned array will match this order |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Environmental drivers [n_patches, n_layers] |
Source code in pypath/spatial/environmental.py
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | |
get_layer_at_time ¶
get_layer_at_time(name: str, t: float) -> np.ndarray
Get specific layer values at time t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Layer name |
required |
t
|
float
|
Time (years) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Environmental values [n_patches] |
Source code in pypath/spatial/environmental.py
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | |
get_statistics ¶
get_statistics() -> Dict[str, Dict]
Get statistics for all layers.
Returns:
| Type | Description |
|---|---|
dict
|
{layer_name: statistics_dict} |
Source code in pypath/spatial/environmental.py
327 328 329 330 331 332 333 334 335 | |
get_time_range ¶
get_time_range() -> Tuple[float, float]
Get overall time range across all layers.
Returns:
| Type | Description |
|---|---|
tuple
|
(min_time, max_time) |
Source code in pypath/spatial/environmental.py
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |
remove_layer ¶
remove_layer(name: str)
Remove environmental layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Name of layer to remove |
required |
Raises:
| Type | Description |
|---|---|
KeyError
|
If layer doesn't exist |
Source code in pypath/spatial/environmental.py
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | |
EnvironmentalLayer
dataclass
¶
Time-varying spatial environmental field.
Represents a single environmental variable (temperature, depth, etc.) that varies across patches and potentially over time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Variable name (e.g., "temperature", "depth", "salinity") |
required |
units
|
str
|
Units of measurement (e.g., "celsius", "meters", "psu") |
required |
values
|
ndarray
|
Environmental values [n_timesteps, n_patches] or [n_patches] If 1D, assumed constant over time |
required |
times
|
ndarray
|
Time points corresponding to values (years) Required if values is 2D |
None
|
interpolate
|
bool
|
Whether to interpolate between timesteps (default: True) |
True
|
Examples:
>>> # Constant depth layer
>>> depth = EnvironmentalLayer(
... name='depth',
... units='meters',
... values=np.array([10, 20, 30, 40, 50])
... )
>>> # Time-varying temperature
>>> temp = EnvironmentalLayer(
... name='temperature',
... units='celsius',
... values=np.array([[5, 6, 7], [10, 12, 14], [8, 9, 10]]),
... times=np.array([0.0, 0.5, 1.0])
... )
>>> temp.get_value_at_time(0.25) # Interpolates between t=0 and t=0.5
array([7.5, 9., 10.5])
Source code in pypath/spatial/environmental.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
__post_init__ ¶
__post_init__()
Validate layer after initialization.
Source code in pypath/spatial/environmental.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
get_statistics ¶
get_statistics() -> Dict[str, float]
Get summary statistics for this layer.
Returns:
| Type | Description |
|---|---|
dict
|
Statistics: min, max, mean, std |
Source code in pypath/spatial/environmental.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
get_value_at_time ¶
get_value_at_time(t: float) -> np.ndarray
Get environmental values at time t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
float
|
Time (years) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Environmental values [n_patches] |
Source code in pypath/spatial/environmental.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |
create_constant_layer ¶
create_constant_layer(name: str, values: ndarray, units: str = '') -> EnvironmentalLayer
Create constant (time-invariant) environmental layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Layer name (e.g., "depth", "slope") |
required |
values
|
ndarray
|
Environmental values [n_patches] |
required |
units
|
str
|
Units of measurement |
''
|
Returns:
| Type | Description |
|---|---|
EnvironmentalLayer
|
|
Source code in pypath/spatial/environmental.py
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | |
create_seasonal_temperature ¶
create_seasonal_temperature(baseline_temp: ndarray, amplitude: float = 10.0, n_months: int = 12) -> EnvironmentalLayer
Create seasonal temperature variation.
Temperature follows sinusoidal pattern: T(month) = baseline + amplitude * sin(2π * month / 12)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
baseline_temp
|
ndarray
|
Baseline temperature for each patch [n_patches] |
required |
amplitude
|
float
|
Seasonal amplitude (default: 10°C) |
10.0
|
n_months
|
int
|
Number of monthly timesteps (default: 12) |
12
|
Returns:
| Type | Description |
|---|---|
EnvironmentalLayer
|
Time-varying temperature layer |
Examples:
>>> baseline = np.array([15, 18, 20]) # Baseline temps
>>> temp = create_seasonal_temperature(baseline, amplitude=8.0)
>>> # Winter (t=0): ~7-12°C
>>> # Summer (t=0.5): ~23-28°C
Source code in pypath/spatial/environmental.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | |
External Flux¶
pypath.spatial.external_flux ¶
External flux loading and validation.
Functions for loading flux timeseries from: - NetCDF files (ocean models: ROMS, MITgcm, HYCOM) - CSV files (connectivity matrices, telemetry data) - Numpy arrays (pre-computed flux)
convert_connectivity_to_flux ¶
convert_connectivity_to_flux(connectivity_matrix: ndarray, biomass: ndarray) -> np.ndarray
Convert connectivity matrix to flux matrix.
Connectivity represents proportions (0-1), while flux represents actual biomass movement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connectivity_matrix
|
ndarray
|
Connectivity proportions [n_patches, n_patches] connectivity[i, j] = fraction of biomass in i that moves to j |
required |
biomass
|
ndarray
|
Biomass in each patch [n_patches] |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Flux matrix [n_patches, n_patches] flux[i, j] = biomass moving from i to j |
Source code in pypath/spatial/external_flux.py
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | |
create_flux_from_connectivity_matrix ¶
create_flux_from_connectivity_matrix(connectivity_matrix: ndarray, times: Optional[ndarray] = None, seasonal_pattern: Optional[ndarray] = None) -> 'ExternalFluxTimeseries'
Create flux timeseries from connectivity matrix.
Connectivity matrix represents proportion of individuals/biomass moving from patch i to patch j per timestep.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connectivity_matrix
|
ndarray
|
Connectivity matrix [n_patches, n_patches] connectivity[i, j] = proportion moving from i to j |
required |
times
|
ndarray
|
Time points (years). If None, uses monthly for 1 year |
None
|
seasonal_pattern
|
ndarray
|
Seasonal variation in connectivity strength [n_timesteps] If None, assumes constant connectivity |
None
|
Returns:
| Type | Description |
|---|---|
ExternalFluxTimeseries
|
|
Source code in pypath/spatial/external_flux.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
load_external_flux_from_csv ¶
load_external_flux_from_csv(filepath: str, n_patches: int, time_column: str = 'time', patch_from_column: str = 'from', patch_to_column: str = 'to', flux_column: str = 'flux') -> 'ExternalFluxTimeseries'
Load external flux from CSV file.
CSV format (edge list): time, from, to, flux 0.0, 0, 1, 0.5 0.0, 1, 2, 0.3 ...
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to CSV file |
required |
n_patches
|
int
|
Number of patches in grid |
required |
time_column
|
str
|
Name of time column |
'time'
|
patch_from_column
|
str
|
Name of source patch column |
'from'
|
patch_to_column
|
str
|
Name of destination patch column |
'to'
|
flux_column
|
str
|
Name of flux value column |
'flux'
|
Returns:
| Type | Description |
|---|---|
ExternalFluxTimeseries
|
|
Source code in pypath/spatial/external_flux.py
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | |
load_external_flux_from_netcdf ¶
load_external_flux_from_netcdf(filepath: str, time_var: str = 'time', flux_var: str = 'flux', group_mapping: Optional[Dict[str, int]] = None) -> 'ExternalFluxTimeseries'
Load external flux from NetCDF file.
Typical NetCDF structure from ocean models: dimensions: time = n_timesteps group = n_groups (or species) patch_from = n_patches patch_to = n_patches
variables:
float time(time) # Time in years or days
float flux(time, group, patch_from, patch_to)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to NetCDF file |
required |
time_var
|
str
|
Name of time dimension/variable (default: "time") |
'time'
|
flux_var
|
str
|
Name of flux variable (default: "flux") Expected shape: [time, group, patch_from, patch_to] |
'flux'
|
group_mapping
|
dict
|
Map from species names to group indices Example: {'cod': 3, 'herring': 5} If None, uses sequential indices |
None
|
Returns:
| Type | Description |
|---|---|
ExternalFluxTimeseries
|
|
Raises:
| Type | Description |
|---|---|
ImportError
|
If netCDF4/xarray not installed |
FileNotFoundError
|
If filepath does not exist |
ValueError
|
If required variables not found |
Source code in pypath/spatial/external_flux.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
rescale_flux_for_conservation ¶
rescale_flux_for_conservation(flux_matrix: ndarray) -> np.ndarray
Rescale flux matrix to enforce mass conservation.
Normalizes each row so that total outflow fraction does not exceed 1.0 (i.e., a patch cannot export more than 100% of its biomass per step). Rows whose outflow sums to <= 1.0 are left unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flux_matrix
|
ndarray
|
Flux matrix [n_patches, n_patches] flux_matrix[i, j] = fraction of biomass in patch i moving to patch j. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Rescaled flux matrix where no row sums to more than 1.0. |
Source code in pypath/spatial/external_flux.py
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | |
summarize_external_flux ¶
summarize_external_flux(external_flux: 'ExternalFluxTimeseries') -> Dict
Summarize external flux timeseries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
external_flux
|
ExternalFluxTimeseries
|
External flux data |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Summary statistics: - 'n_timesteps': Number of time points - 'time_range': (min_time, max_time) - 'n_groups': Number of groups with external flux - 'n_patches': Number of patches - 'mean_flux': Mean flux value - 'max_flux': Maximum flux value - 'is_conserved': Whether flux conserves mass |
Source code in pypath/spatial/external_flux.py
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | |
validate_external_flux_conservation ¶
validate_external_flux_conservation(flux_matrix: ndarray, tolerance: float = 1e-10) -> bool
Validate that external flux conserves mass.
Sum over all patches: inflow - outflow should be 0 (within numerical tolerance).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flux_matrix
|
ndarray
|
Flux matrix [n_patches, n_patches] flux_matrix[i, j] = flux from patch i to patch j |
required |
tolerance
|
float
|
Numerical tolerance for zero |
1e-10
|
Returns:
| Type | Description |
|---|---|
bool
|
True if flux is conserved |
Notes
For mass conservation: Σ_i Σ_j flux[i, j] = Σ_j Σ_i flux[i, j] (total outflow = total inflow)
Equivalently: Σ_i (Σ_j flux[i, j] - Σ_j flux[j, i]) = 0
Source code in pypath/spatial/external_flux.py
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
Spatial Fishing¶
pypath.spatial.fishing ¶
Spatial fishing effort allocation for ECOSPACE.
Implements spatially-explicit fishing with multiple allocation strategies: - Uniform: Equal effort across all patches - Gravity: Biomass-weighted effort (fish where fish are) - Port-based: Distance from fishing ports - Prescribed: User-defined spatial patterns - Habitat-based: Target preferred habitats
SpatialFishing
dataclass
¶
Spatial fishing effort allocation.
Represents how fishing effort is distributed across spatial patches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allocation_type
|
str
|
Method for allocating effort: - "uniform": Equal across patches - "gravity": Biomass-weighted (alpha, beta parameters) - "port": Distance from ports (beta parameter) - "prescribed": User-defined pattern - "habitat": Target specific habitat types |
'uniform'
|
effort_allocation
|
ndarray
|
Pre-computed effort distribution [n_months, n_gears, n_patches] Normalized so sum over patches = ForcedEffort[month, gear] |
None
|
gravity_alpha
|
float
|
Biomass attraction exponent (default: 1.0) Higher = stronger attraction to high biomass |
1.0
|
gravity_beta
|
float
|
Distance penalty exponent (default: 0.5) Higher = stronger distance penalty from ports |
0.5
|
port_patches
|
ndarray
|
Indices of patches containing ports |
None
|
target_groups
|
List[int]
|
Group indices to target for gravity allocation |
None
|
custom_allocation_function
|
Callable
|
Custom function(biomass, t, params) -> allocation [n_patches] |
None
|
Examples:
>>> # Uniform allocation
>>> fishing = SpatialFishing(allocation_type="uniform")
>>> # Gravity model (fish where fish are)
>>> fishing = SpatialFishing(
... allocation_type="gravity",
... gravity_alpha=1.5, # Strong biomass attraction
... target_groups=[3, 5, 7] # Target specific species
... )
>>> # Port-based (effort decreases with distance)
>>> fishing = SpatialFishing(
... allocation_type="port",
... port_patches=np.array([0, 5, 10]), # Three ports
... gravity_beta=1.0 # Distance penalty
... )
Source code in pypath/spatial/fishing.py
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
__post_init__ ¶
__post_init__()
Validate parameters after initialization.
Source code in pypath/spatial/fishing.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
allocate_gravity ¶
allocate_gravity(biomass: ndarray, target_groups: Optional[List[int]], total_effort: float, alpha: float = 1.0, beta: float = 0.0, port_patches: Optional[ndarray] = None, grid: Optional['EcospaceGrid'] = None) -> np.ndarray
Allocate effort using gravity model (biomass attraction + distance penalty).
Effort follows: effort[p] ∝ (Σ_g biomass[g, p]^alpha) / distance[port, p]^beta
where g ∈ target_groups, and distance is from nearest port.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
biomass
|
ndarray
|
Biomass by group and patch [n_groups+1, n_patches] |
required |
target_groups
|
list of int
|
Group indices to target (if None, use all groups) |
required |
total_effort
|
float
|
Total effort to allocate |
required |
alpha
|
float
|
Biomass attraction exponent (default: 1.0) - 0 = ignore biomass (random) - 1 = proportional to biomass - >1 = concentrate on high biomass |
1.0
|
beta
|
float
|
Distance penalty exponent (default: 0.0) - 0 = ignore distance - >0 = avoid distant patches |
0.0
|
port_patches
|
ndarray
|
Indices of patches with ports If None, assumes uniform accessibility |
None
|
grid
|
EcospaceGrid
|
Spatial grid (required if beta > 0) |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Effort per patch [n_patches], sums to total_effort |
Examples:
>>> biomass = np.array([[0, 0, 0], [10, 20, 5]]) # 1 group, 3 patches
>>> allocate_gravity(biomass, target_groups=[1], total_effort=100, alpha=1.0)
array([28.57142857, 57.14285714, 14.28571429]) # Proportional to biomass
Source code in pypath/spatial/fishing.py
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | |
allocate_habitat_based ¶
allocate_habitat_based(habitat_preference: ndarray, total_effort: float, threshold: float = 0.5) -> np.ndarray
Allocate effort based on habitat preference.
Targets patches with high habitat quality for target species.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
habitat_preference
|
ndarray
|
Habitat preference values [n_patches] Values in [0, 1] |
required |
total_effort
|
float
|
Total effort to allocate |
required |
threshold
|
float
|
Minimum habitat preference to fish (default: 0.5) Patches below this get zero effort |
0.5
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Effort per patch [n_patches], sums to total_effort |
Examples:
>>> habitat = np.array([0.2, 0.6, 0.8, 0.4, 0.9])
>>> allocate_habitat_based(habitat, total_effort=100, threshold=0.5)
array([ 0., 26.08695652, 34.78260870, 0., 39.13043478])
# Only patches with preference > 0.5 get effort
Source code in pypath/spatial/fishing.py
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | |
allocate_port_based ¶
allocate_port_based(grid: 'EcospaceGrid', port_patches: ndarray, total_effort: float, beta: float = 1.0, max_distance: Optional[float] = None) -> np.ndarray
Allocate effort based on distance from fishing ports.
Effort decreases with distance from nearest port: effort[p] ∝ 1 / distance[p]^beta
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
EcospaceGrid
|
Spatial grid |
required |
port_patches
|
ndarray
|
Indices of patches containing ports |
required |
total_effort
|
float
|
Total effort to allocate |
required |
beta
|
float
|
Distance decay exponent (default: 1.0) Higher = faster decay with distance |
1.0
|
max_distance
|
float
|
Maximum fishing distance from ports (km) Patches beyond this get zero effort |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Effort per patch [n_patches], sums to total_effort |
Examples:
>>> grid = create_1d_grid(n_patches=5)
>>> allocate_port_based(grid, port_patches=np.array([0]), total_effort=100, beta=1.0)
# Returns effort decreasing with distance from patch 0
Source code in pypath/spatial/fishing.py
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
allocate_uniform ¶
allocate_uniform(n_patches: int, total_effort: float = 1.0) -> np.ndarray
Allocate effort uniformly across all patches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_patches
|
int
|
Number of spatial patches |
required |
total_effort
|
float
|
Total effort to allocate (default: 1.0) |
1.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Effort per patch [n_patches], sums to total_effort |
Examples:
>>> allocate_uniform(5, total_effort=100)
array([20., 20., 20., 20., 20.])
Source code in pypath/spatial/fishing.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
calculate_distance_penalty ¶
calculate_distance_penalty(grid: 'EcospaceGrid', port_patches: ndarray, beta: float) -> np.ndarray
Calculate distance penalty from nearest port.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
EcospaceGrid
|
Spatial grid |
required |
port_patches
|
ndarray
|
Indices of port patches |
required |
beta
|
float
|
Distance decay exponent |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Distance penalty [n_patches] penalty[p] = distance_to_nearest_port[p]^beta |
Source code in pypath/spatial/fishing.py
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | |
create_spatial_fishing ¶
create_spatial_fishing(n_months: int, n_gears: int, n_patches: int, forced_effort: ndarray, allocation_type: str = 'uniform', **kwargs) -> SpatialFishing
Create spatial fishing with pre-computed effort allocation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_months
|
int
|
Number of monthly timesteps |
required |
n_gears
|
int
|
Number of fishing gears/fleets |
required |
n_patches
|
int
|
Number of spatial patches |
required |
forced_effort
|
ndarray
|
Total effort by month and gear [n_months, n_gears+1] Column 0 is "Outside" (ignored) |
required |
allocation_type
|
str
|
Allocation method (see SpatialFishing) |
'uniform'
|
**kwargs
|
Additional parameters for allocation method (e.g., gravity_alpha, port_patches, etc.) |
{}
|
Returns:
| Type | Description |
|---|---|
SpatialFishing
|
Spatial fishing object with effort_allocation computed |
Examples:
>>> forced_effort = np.ones((12, 3)) # 12 months, 2 gears + Outside
>>> fishing = create_spatial_fishing(
... n_months=12,
... n_gears=2,
... n_patches=10,
... forced_effort=forced_effort,
... allocation_type="uniform"
... )
Source code in pypath/spatial/fishing.py
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | |
validate_effort_allocation ¶
validate_effort_allocation(effort_allocation: ndarray, forced_effort: ndarray, tolerance: float = 1e-08) -> bool
Validate that spatial effort allocation sums correctly.
For each month and gear: Σ_patches effort_allocation[m, g, p] = forced_effort[m, g]
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
effort_allocation
|
ndarray
|
Spatial effort [n_months, n_gears+1, n_patches] |
required |
forced_effort
|
ndarray
|
Total effort [n_months, n_gears+1] |
required |
tolerance
|
float
|
Numerical tolerance (default: 1e-8) |
1e-08
|
Returns:
| Type | Description |
|---|---|
bool
|
True if allocation is valid |
Source code in pypath/spatial/fishing.py
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | |
Spatial Integration¶
pypath.spatial.integration ¶
Spatial-temporal integration for ECOSPACE.
Integrates ECOSPACE spatial dynamics with Ecosim temporal dynamics: - Spatial derivative calculation (local dynamics + movement) - RK4 integration extended for spatial state - Wrapper functions for spatial simulations - Backward compatibility with non-spatial Ecosim
deriv_vector_spatial ¶
deriv_vector_spatial(state_spatial: ndarray, params: Dict, forcing: Dict, fishing: Dict, ecospace: EcospaceParams, environmental_drivers: Optional[EnvironmentalDrivers], t: float = 0.0, dt: float = 1.0 / 12.0) -> np.ndarray
Calculate spatial derivative (local dynamics + movement).
For each patch p: 1. Calculate local Ecosim dynamics (production, predation, fishing, M0) 2. Apply habitat capacity to carrying capacity (if environmental drivers present) 3. Add spatial fluxes (migration/dispersal)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_spatial
|
ndarray
|
Spatial state [n_groups+1, n_patches] Index 0 = "Outside" (no dynamics) Index 1+ = Living and detritus groups |
required |
params
|
dict
|
Ecosim parameters (from RsimParams) |
required |
forcing
|
dict
|
Environmental forcing (from RsimForcing) |
required |
fishing
|
dict
|
Fishing forcing (from RsimFishing) |
required |
ecospace
|
EcospaceParams
|
Spatial parameters |
required |
environmental_drivers
|
EnvironmentalDrivers
|
Time-varying environmental layers for habitat capacity |
required |
t
|
float
|
Simulation time (years) |
0.0
|
dt
|
float
|
Timestep size (default: 1/12 year = 1 month) |
1.0 / 12.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Spatial derivative [n_groups+1, n_patches] deriv[g, p] = rate of change for group g in patch p |
Notes
This function extends the standard Ecosim derivative to spatial grids. For each patch, the local Ecosim dynamics are calculated independently, then spatial fluxes (movement) are added to account for dispersal.
Habitat capacity can be calculated from environmental drivers: capacity = f(temperature, depth, salinity, ...)
Source code in pypath/spatial/integration.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
rsim_run_spatial ¶
rsim_run_spatial(scenario: RsimScenario, method: str = 'RK4', years: Optional[range] = None, ecospace: Optional[EcospaceParams] = None, environmental_drivers: Optional[EnvironmentalDrivers] = None) -> RsimOutput
Run spatial Ecosim simulation.
Wrapper for Ecosim that extends to spatial grids. If ecospace is None, falls back to standard non-spatial Ecosim.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scenario
|
RsimScenario
|
Simulation scenario (params, forcing, fishing, start state) |
required |
method
|
str
|
Integration method (default: 'RK4') Currently only RK4 is implemented |
'RK4'
|
years
|
range
|
Years to simulate (default: use scenario years) Example: range(1, 101) for 100 years |
None
|
ecospace
|
EcospaceParams
|
Spatial parameters If None, runs standard non-spatial Ecosim |
None
|
environmental_drivers
|
EnvironmentalDrivers
|
Time-varying environmental layers for habitat capacity |
None
|
Returns:
| Type | Description |
|---|---|
RsimOutput
|
Simulation results - out_Biomass: Total biomass (summed over patches) for compatibility - out_Biomass_spatial: Spatial biomass [n_months, n_groups+1, n_patches] (if spatial) - Other outputs as per standard Ecosim |
Examples:
>>> # Non-spatial (standard Ecosim)
>>> result = rsim_run_spatial(scenario)
>>> # Spatial ECOSPACE
>>> from pypath.spatial import EcospaceGrid, EcospaceParams
>>> grid = EcospaceGrid.from_shapefile('grid.shp')
>>> ecospace = EcospaceParams(grid, ...)
>>> result = rsim_run_spatial(scenario, ecospace=ecospace)
>>> spatial_biomass = result.out_Biomass_spatial # [n_months, n_groups, n_patches]
>>> total_biomass = result.out_Biomass # [n_months, n_groups] (summed over patches)
Source code in pypath/spatial/integration.py
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | |