Cattle_Elk_R4 / Sim_Engine.py
jeffrey1963's picture
Update Sim_Engine.py
f834679 verified
def initialize_parcels(parcel_map, cluster_labels):
parcel_dict = {}
for i in range(parcel_map.shape[0]):
for j in range(parcel_map.shape[1]):
cluster_id = parcel_map[i, j]
land_type = cluster_labels.get(cluster_id, "Unknown")
parcel_dict[(i, j)] = {
"land_type": land_type,
"forage": None,
"health": 1.0,
"degraded": False,
"cattle_grazing": {
"conservative": land_type == "Productive Grass",
"moderate": land_type in ["Productive Grass", "Pasture/Desert"],
"aggressive": land_type not in ["Water"]
},
"elk_grazing": {
"default": land_type in ["Riparian Sensitive Zone", "Productive Grass"]
}
}
return parcel_dict
def get_land_forage_rates():
return {
'Productive Grass': 1.0,
'Pasture/Desert': 0.4,
'Riparian Sensitive Zone': 1.2,
'Rocky Area': 0.2,
'Water': 0.0
}
def assign_initial_forage(parcel_dict, land_forage_rates):
for parcel in parcel_dict.values():
rate = land_forage_rates.get(parcel["land_type"], 0.0)
parcel["forage"] = rate * 100 # initial AUMs
import numpy as np
import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
from matplotlib.colors import ListedColormap
import matplotlib.patches as mpatches
def simulate_period(parcel_dict, grazing_strategy="moderate", cattle_stocking_rate=1000, elk_pressure=3000):
print(f"\n🟢 Running LP-based simulation using grazing strategy: **{grazing_strategy.upper()}**")
import numpy as np
from scipy.optimize import linprog
keys = list(parcel_dict.keys())
n_rows = max(i for i, _ in keys) + 1
n_cols = max(j for _, j in keys) + 1
num_cells = n_rows * n_cols
# Step 1: Regrowth
land_growth_rates = {
'Productive Grass': 1.0,
'Pasture/Desert': 0.4,
'Riparian Sensitive Zone': 1.2,
'Rocky Area': 0.2,
'Water': 0.0
}
for parcel in parcel_dict.values():
base_growth = land_growth_rates.get(parcel["land_type"], 0.0)
weather = np.random.normal(1.0, 0.15)
regrowth = base_growth * weather * 1
regrowth *= parcel["health"]
parcel["forage"] = min(parcel["forage"] + regrowth, 100)
# Step 2: Subtract uniform elk grazing from all parcels
elk_grazing_per_parcel = elk_pressure / num_cells
for parcel in parcel_dict.values():
parcel["forage"] -= elk_grazing_per_parcel
parcel["forage"] = max(parcel["forage"], 0.0)
# Step 3: LP for cattle
cost = []
bounds = []
eligible_keys = []
for i in range(n_rows):
for j in range(n_cols):
p = parcel_dict[(i, j)]
if not p["cattle_grazing"].get(grazing_strategy, False):
cost.append(0)
bounds.append((0, 0))
continue
if grazing_strategy in {"conservative", "moderate"} and p["land_type"] == "Riparian Sensitive Zone":
cost.append(0)
bounds.append((0, 0))
continue
cost.append((i + j) * 0.02)
bounds.append((0, p["forage"]))
eligible_keys.append((i, j))
A_eq = [1.0 if b[1] > 0 else 0.0 for b in bounds]
b_eq = [cattle_stocking_rate]
result = linprog(c=cost, A_eq=[A_eq], b_eq=b_eq, bounds=bounds, method="highs")
if not result.success:
raise RuntimeError("Grazing LP failed: " + result.message)
grazing_values = result.x
# Step 4: Apply grazing and your specified health rule
for idx, ((i, j), x) in enumerate(zip(parcel_dict.keys(), grazing_values)):
parcel = parcel_dict[(i, j)]
parcel["forage"] -= x
# ✅ Your health rule (fully time-dynamic)
if parcel["forage"] <= 0:
parcel["health"] = max(parcel["health"] - 0.25, 0.0)
elif parcel["forage"] < 20:
parcel["health"] = max(parcel["health"] - 0.1, 0.0)
else:
parcel["health"] = min(parcel["health"] + 0.02, 1.0)
def simulate_periodold(parcel_dict, grazing_strategy="moderate", cattle_stocking_rate=5000, elk_pressure=3000):
print(f"\n🟢 Running simulation using cattle grazing strategy: **{grazing_strategy.upper()}**")
land_growth_rates = {
'Productive Grass': 1.0,
'Pasture/Desert': 0.4,
'Riparian Sensitive Zone': 1.2,
'Rocky Area': 0.2,
'Water': 0.0
}
# 1. Simulate forage regrowth for 8 months
for parcel in parcel_dict.values():
base_growth = land_growth_rates.get(parcel["land_type"], 0.0)
weather = np.random.normal(1.0, 0.15)
regrowth = base_growth * weather * 8 # ← 8 months, as you said
regrowth *= parcel["health"] # degrade means slower regrowth
parcel["forage"] = min(parcel["forage"] + regrowth, 100)
# 2. Count eligible parcels
total_grazed_parcels = sum(
1 for parcel in parcel_dict.values() if parcel["cattle_grazing"].get(grazing_strategy, False)
)
if total_grazed_parcels == 0:
print("⚠️ No parcels match the selected grazing strategy.")
return
cattle_grazing_per_parcel = cattle_stocking_rate / total_grazed_parcels
elk_grazing_per_parcel = elk_pressure / len(parcel_dict)
# 3. Simulate grazing and degradation
for parcel in parcel_dict.values():
if not parcel["cattle_grazing"].get(grazing_strategy, False):
continue
total_grazing = cattle_grazing_per_parcel + elk_grazing_per_parcel
if total_grazing > parcel["forage"]:
parcel["degraded"] = True
parcel["health"] = max(parcel["health"] - 0.1, 0.0)
else:
parcel["health"] = min(parcel["health"] + 0.02, 1.0)
parcel["forage"] = max(parcel["forage"] - total_grazing, 0)
def get_forage_map(parcel_dict, n_rows, n_cols):
return np.array([[parcel_dict[(i, j)]["forage"] for j in range(n_cols)] for i in range(n_rows)])
def get_health_map(parcel_dict, n_rows, n_cols):
"""
Returns a 2D numpy array representing the health of each parcel.
"""
return np.array([[parcel_dict[(i, j)]["health"] for j in range(n_cols)] for i in range(n_rows)])
def plot_health_map(health_map, title="Parcel Health Levels", save_path=None):
"""
Plots a heatmap of the parcel health values.
"""
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
plt.imshow(health_map, cmap='RdYlGn', origin='upper', vmin=0, vmax=1)
plt.colorbar(label="Health Index (0–1)")
plt.title(title)
plt.axis('off')
plt.tight_layout()
if save_path:
plt.savefig(save_path)
plt.show()
def plot_forage_map(forage_map, title="Parcel Forage Levels"):
plt.figure(figsize=(8, 6))
plt.imshow(forage_map, cmap='YlGn', origin='upper')
plt.colorbar(label="Forage AUMs")
plt.title(title)
plt.axis('off')
plt.tight_layout()
plt.show()
def run_full_simulation(parcel_map, cluster_labels, n_rows, n_cols, strategy="moderate"):
parcel_dict = initialize_parcels(parcel_map, cluster_labels)
land_forage_rates = get_land_forage_rates()
assign_initial_forage(parcel_dict, land_forage_rates)
simulate_period(parcel_dict, grazing_strategy=strategy)
return parcel_dict