Skip to content

Commit 698bbc5

Browse files
authored
Merge branch 'main' into storm
2 parents 4793e46 + 047e457 commit 698bbc5

14 files changed

Lines changed: 1666 additions & 158 deletions

File tree

docs/HPC_GUIDE.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
### Snellius Usage Breakdown
2+
3+
```
4+
ssh <your_username>@snellius.surf.nl
5+
6+
# On a separate terminal run the following
7+
8+
# Upload the entire project directory (including your models/ folder)
9+
scp -r ~/Documents/CSS_Project <your_username>@snellius.surf.nl:~/
10+
11+
# On the Snellius terminal
12+
13+
module load 2023 Python/3.11.3-GCCcore-12.3.0
14+
python3 -m venv ~/css_env
15+
source ~/css_env/bin/activate
16+
pip install numpy scipy matplotlib joblib
17+
18+
# To do a dry run for testing the entire environment
19+
20+
python3 pp_analysis.py --mode full --dry-run
21+
22+
# For async run
23+
24+
python3 pp_analysis.py --mode full --output results_${SLURM_JOB_ID} --cores $SLURM_CPUS_PER_TASK --async
25+
26+
# To submit a job
27+
28+
sbatch run_analysis.sh
29+
30+
# Check Queue Status
31+
32+
squeue -u $USER
33+
34+
# Cancel a job
35+
36+
scancel <JOBID>
37+
38+
# Monitoring live progress
39+
40+
tail -f logs_<JOBID>.err
41+
42+
# Fetching the results once the job is done
43+
44+
scp -r <your_username>@snellius.surf.nl:~/results_18514601 ~/Downloads/
45+
```
46+
47+
The jobscript template can be found in ```run_analysis.sh``` (default rome paritition).
48+
49+
50+
Snellius Partitions Page: https://servicedesk.surf.nl/wiki/spaces/WIKI/pages/30660209/Snellius+partitions+and+accounting

docs/prompts.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,4 +691,20 @@ Lastly, add functionality to stop evolution after a certain time-step. This shou
691691

692692
7. Create a bifuracation diagram to confirm the monotonic relationship for a varying prey death rate vs. equilibrium density.
693693

694-
###
694+
---
695+
696+
### Testing CA class
697+
698+
1. Create a comprehensive testing suite for the CA and PP classes. Test initialization, async update changes, synchronous update changes, prey growth in isolation behavior, predator starvation, parameter evolution and long run dynamics. Also make sur ethe test_viz mehtod works as desired
699+
700+
### Parameter Sweep and PP Class Analysis
701+
702+
2. Create a skeletal version of a .py script that will be subimtted into Snellius for parameter analysis. The purpose of this script should be to identify power law distribution with the consideration of finite size scaling, the hydra effect, and suitable parameter configurtaions for time series analysis for model evolution. Compare the non-evo to the evo model.
703+
704+
3. Create a config class adjustable depending on the CPU budget. We want to run a prey_birth vs. predator_death parameter sweep (2D), quantify the hydra effect using the derivative, search for the critical point (power law relartionship paramameter), quantify evolution sensitivity and analyze finite grid size scaling. Include script options for cost optimal runs as well. Make sure to have a summary of collected data stored for reference and future usage.
705+
706+
707+
4. Add configuration option to run the asynchronous version of the CA class. The synchronous functionality should also be preserved. Provide me with a small smoke test to see if the updated file runs as expected.
708+
709+
5. Create a minimal bash script for Snellius. Use the rome configiuration.
710+

models/CA.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ def run(self, steps: int, stop_evolution_at: Optional[int] = None, snapshot_iter
208208
# normalize snapshot iteration list
209209
snapshot_set = set(snapshot_iters) if snapshot_iters is not None else set()
210210

211+
# normalize snapshot iteration list
212+
snapshot_set = set(snapshot_iters) if snapshot_iters is not None else set()
213+
211214
for i in range(steps):
212215
self.update()
213216
# Update visualization if enabled every `interval` iterations
@@ -241,6 +244,30 @@ def run(self, steps: int, stop_evolution_at: Optional[int] = None, snapshot_iter
241244
# disable further evolution
242245
self._evolve_info = {}
243246

247+
# create snapshots if requested at this iteration
248+
if (i + 1) in snapshot_set:
249+
try:
250+
# create snapshot folder if not present
251+
if not hasattr(self, "_viz_snapshot_dir") or self._viz_snapshot_dir is None:
252+
import os, time
253+
254+
base = "results"
255+
ts = int(time.time())
256+
run_folder = f"run-{ts}"
257+
full = os.path.join(base, run_folder)
258+
os.makedirs(full, exist_ok=True)
259+
self._viz_snapshot_dir = full
260+
self._viz_save_snapshot(i + 1)
261+
except Exception:
262+
pass
263+
264+
# stop evolution at specified time-step (disable further evolution)
265+
if stop_evolution_at is not None and (i + 1) == int(stop_evolution_at):
266+
try:
267+
self._evolve_info = {}
268+
except Exception:
269+
pass
270+
244271
def visualize(
245272
self,
246273
interval: int = 1,

models/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
from .mean_field import MeanFieldModel
2-
from .CA import CA
2+
from .CA import CA, PP

models/firebreak.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
""""
2+
Altruistic Suicide Firebreak Model
3+
4+
Concept:
5+
6+
- Standard Prey: Dies of old age or being eaten
7+
- Firebreak prey: Has a sensor to detect if a predator is nearby. If so, then it has
8+
the option to protect the colony by commiting suicde before the predator can reproduce
9+
into it
10+
- Result: Creates empty cells around the predator cluster. Prevents percolation through
11+
the cluster
12+
13+
14+
TO DO:
15+
16+
1. Override update_sync and update_async logic.
17+
New Logic:
18+
1. Calculate predator_neighbors for every cell
19+
2. Create a death rate grid where every cell gets assigned a death rate based on neighbors
20+
3. Calc the mask against the dynamic grid
21+
"""
22+
23+
import numpy as np
24+
from models.CA import PP
25+
26+
27+
class Firebreak(PP):
28+
"""
29+
PP CA where prey commit suicide when threatend to creaty empty firebreaks
30+
that starve predators
31+
"""
32+
33+
def __init__(
34+
self,
35+
rows,
36+
cols,
37+
densities,
38+
neighborhood="moore",
39+
params=None,
40+
cell_params=None,
41+
seed=None,
42+
synchronous=True,
43+
):
44+
# get firebreak specific parameter
45+
self.alt_dr = 0.5 # FIXME: Random default value
46+
clean_params = dict(params) if params else {}
47+
if "altruistic_dr" in clean_params:
48+
self.alt_dr = float(clean_params.pop("altruistic_dr"))
49+
50+
super().__init__(
51+
rows,
52+
cols,
53+
densities,
54+
neighborhood,
55+
clean_params,
56+
cell_params,
57+
seed,
58+
synchronous,
59+
)
60+
self.params["altruistic_dr"] = self.alt_dr
61+
62+
def update_sync(self) -> None:
63+
"""Override syncrhonous update. Similar to PP update except Prey Death Logic."""
64+
pass

models/helpers.py

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,73 @@
11
import numpy as np
22
from scipy.ndimage import label
33

4-
def find_clusters(grid: np.ndarray, state: int = 1, connectivity: int = 'moore'):
4+
5+
def find_clusters(grid: np.ndarray, state: int = 1, connectivity: int = "moore"):
56
"""Locate connected clusters of a given state."""
6-
if connectivity == 'moore':
7+
if connectivity == "moore":
78
structure = np.ones((3, 3), dtype=int)
89
else:
9-
structure = ([[0, 1, 0],
10-
[1, 1, 1],
11-
[0, 1, 0]])
12-
10+
structure = [[0, 1, 0], [1, 1, 1], [0, 1, 0]]
11+
1312
binary_mask = (grid == state).astype(int)
1413
labeled_array, num_clusters = label(binary_mask, structure=structure)
15-
14+
1615
return labeled_array, num_clusters
1716

17+
1818
def get_cluster_sizes(grid: np.ndarray, state: int = 1):
1919
"""Returns an array containing the sizes of all detected clusters."""
2020
labeled, n = find_clusters(grid, state)
21-
21+
2222
if n == 0:
2323
return np.array([])
24-
24+
2525
sizes = np.bincount(labeled.ravel())[1:] # Exclude background count
26-
26+
2727
return sizes
2828

29+
2930
def fit_power_law_mle(sizes: np.ndarray, s_min: float = 1.0):
3031
"""
3132
Maximum Likelihood Estimator for the power-law exponent.
3233
"""
3334
# Filter sizes below the minimum threshold
3435
sizes = sizes[sizes >= s_min]
35-
36+
3637
if len(sizes) < 10:
37-
return {'tau': np.nan, 'tau_err': np.nan, 'n_samples': len(sizes)}
38-
38+
return {"tau": np.nan, "tau_err": np.nan, "n_samples": len(sizes)}
39+
3940
n = len(sizes)
40-
41+
4142
# Standard MLE formula for power-law index
4243
tau = 1 + n / np.sum(np.log(sizes / s_min))
4344
tau_err = (tau - 1) / np.sqrt(n)
44-
45-
return {
46-
'tau': tau,
47-
'tau_err': tau_err,
48-
'n_samples': n
49-
}
45+
46+
return {"tau": tau, "tau_err": tau_err, "n_samples": n}
47+
5048

5149
def detect_hydra_effect(d_r_vals: np.ndarray, R_vals: np.ndarray):
5250
"""Detects hydra effect by identifying increase in prey population with increased death rate."""
53-
51+
5452
dR = np.gradient(R_vals, d_r_vals)
5553
hydra_mask = dR > 0
56-
54+
5755
if not np.any(hydra_mask):
58-
return {
59-
'detected': False,
60-
'region': None,
61-
'max_strength': 0.0
62-
}
56+
return {"detected": False, "region": None, "max_strength": 0.0}
6357
hydra_indices = np.where(hydra_mask)[0]
64-
58+
6559
results = {
66-
'detected': True,
67-
'd_r_start': d_r_vals[hydra_indices[0]],
68-
'd_r_end': d_r_vals[hydra_indices[-1]],
69-
'max_slope': np.max(dR),
70-
'd_r_at_max_slope': d_r_vals[np.argmax(dR)],
71-
'peak_density': np.max(R_vals[hydra_mask])
60+
"detected": True,
61+
"d_r_start": d_r_vals[hydra_indices[0]],
62+
"d_r_end": d_r_vals[hydra_indices[-1]],
63+
"max_slope": np.max(dR),
64+
"d_r_at_max_slope": d_r_vals[np.argmax(dR)],
65+
"peak_density": np.max(R_vals[hydra_mask]),
7266
}
73-
67+
7468
return results
7569

70+
7671
def find_spatial_correlation(d_r_vals, ca_results):
7772
"""ca_results: List of grids (np.ndarray) at equilibrium of each d_r value."""
78-
pass
73+
pass

0 commit comments

Comments
 (0)