Spaces:
Build error
Build error
| 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 | |