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=100000, 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