Improve MIP optimization and add log export feature
This commit is contained in:
110
mip.py
110
mip.py
@@ -135,14 +135,51 @@ def design_with_mip(
|
||||
def cluster_var(k):
|
||||
return pulp.LpVariable(f"cluster_{k}", cat="Binary")
|
||||
|
||||
# 目标函数:最小化总投资(近似为风机到升压站的总距离)
|
||||
prob += pulp.lpSum(
|
||||
[
|
||||
dist_matrix_full[0, i + 1] * assign_var(i, k)
|
||||
for i in range(n_turbines)
|
||||
for k in range(max_clusters)
|
||||
]
|
||||
)
|
||||
def cluster_connection_var(k):
|
||||
return pulp.LpVariable(f"cluster_connection_{k}", cat="Binary")
|
||||
|
||||
turbine_coords = turbines[["x", "y"]].values
|
||||
turbine_powers = turbines["power"].values
|
||||
|
||||
# Calculate cost per meter for cluster-to-substation connections
|
||||
# Higher power clusters need thicker cables = higher cost
|
||||
cost_per_meter_per_mw = 1000 # Base cost per MW per meter (can be adjusted)
|
||||
|
||||
# Objective function: minimize total investment including:
|
||||
# 1. Intra-cluster connections (estimated using pairwise distances)
|
||||
# 2. Cluster-to-substation connections (based on distance and power)
|
||||
objective_terms = []
|
||||
|
||||
# Intra-cluster connection costs (estimated)
|
||||
for k in range(max_clusters):
|
||||
for i in range(n_turbines):
|
||||
for j in range(i + 1, n_turbines):
|
||||
# Only count if both turbines are in the same cluster
|
||||
# This is a simplified approximation of MST cost
|
||||
both_in_cluster = assign_var(i, k) + assign_var(j, k) - 1
|
||||
distance_ij = np.linalg.norm(turbine_coords[i] - turbine_coords[j])
|
||||
objective_terms.append(distance_ij * both_in_cluster * 0.5)
|
||||
|
||||
# Cluster-to-substation connection costs
|
||||
for k in range(max_clusters):
|
||||
cluster_power = pulp.lpSum(
|
||||
[turbine_powers[i] * assign_var(i, k) for i in range(n_turbines)]
|
||||
)
|
||||
cluster_to_substation_distance = dist_matrix_full[
|
||||
0, :
|
||||
] # Distance from each turbine to substation
|
||||
|
||||
# Use minimum distance from any turbine in cluster to substation
|
||||
for i in range(n_turbines):
|
||||
objective_terms.append(
|
||||
cluster_to_substation_distance[i + 1]
|
||||
* assign_var(i, k)
|
||||
* cost_per_meter_per_mw
|
||||
* turbine_powers[i]
|
||||
* 0.001
|
||||
)
|
||||
|
||||
prob += pulp.lpSum(objective_terms)
|
||||
|
||||
for i in range(n_turbines):
|
||||
prob += pulp.lpSum([assign_var(i, k) for k in range(max_clusters)]) == 1
|
||||
@@ -151,7 +188,7 @@ def design_with_mip(
|
||||
cluster_power = pulp.lpSum(
|
||||
[turbines.iloc[i]["power"] * assign_var(i, k) for i in range(n_turbines)]
|
||||
)
|
||||
prob += cluster_power <= max_mw * 1.2 * cluster_var(k)
|
||||
prob += cluster_power <= max_mw * 1.0 * cluster_var(k)
|
||||
|
||||
for k in range(max_clusters):
|
||||
for i in range(n_turbines):
|
||||
@@ -247,7 +284,62 @@ def design_with_mip(
|
||||
connections.append((f"turbine_{closest}", "substation", min(dists)))
|
||||
|
||||
turbines["cluster"] = cluster_assign
|
||||
|
||||
# Check cluster distances
|
||||
min_cluster_distance = check_cluster_distances(clusters, turbines)
|
||||
if min_cluster_distance is not None:
|
||||
print(
|
||||
f"Cluster validation: Minimum distance between clusters = {min_cluster_distance:.2f} m"
|
||||
)
|
||||
if min_cluster_distance < 1000:
|
||||
print(
|
||||
f"WARNING: Clusters are very close to each other ({min_cluster_distance:.2f} m < 1000 m)"
|
||||
)
|
||||
elif min_cluster_distance < 2000:
|
||||
print(
|
||||
f"NOTICE: Clusters are relatively close ({min_cluster_distance:.2f} m)"
|
||||
)
|
||||
|
||||
print(
|
||||
f"MIP optimization completed successfully, {len(connections)} connections generated"
|
||||
)
|
||||
return connections, turbines
|
||||
|
||||
|
||||
def calculate_cluster_centroids(clusters, turbines):
|
||||
"""Calculate the centroid coordinates for each cluster."""
|
||||
centroids = {}
|
||||
for c, members in clusters.items():
|
||||
if len(members) == 0:
|
||||
centroids[c] = (0, 0)
|
||||
else:
|
||||
coords = turbines.iloc[members][["x", "y"]].values
|
||||
centroid_x = np.mean(coords[:, 0])
|
||||
centroid_y = np.mean(coords[:, 1])
|
||||
centroids[c] = (centroid_x, centroid_y)
|
||||
return centroids
|
||||
|
||||
|
||||
def check_cluster_distances(clusters, turbines, min_distance_threshold=1000):
|
||||
"""Check if any clusters are too close to each other."""
|
||||
if len(clusters) < 2:
|
||||
return None
|
||||
|
||||
centroids = calculate_cluster_centroids(clusters, turbines)
|
||||
active_clusters = [c for c, members in clusters.items() if len(members) > 0]
|
||||
|
||||
min_distance = float("inf")
|
||||
min_pair = None
|
||||
|
||||
for i in range(len(active_clusters)):
|
||||
for j in range(i + 1, len(active_clusters)):
|
||||
c1, c2 = active_clusters[i], active_clusters[j]
|
||||
centroid1 = np.array(centroids[c1])
|
||||
centroid2 = np.array(centroids[c2])
|
||||
distance = np.linalg.norm(centroid1 - centroid2)
|
||||
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
min_pair = (c1, c2)
|
||||
|
||||
return min_distance
|
||||
|
||||
Reference in New Issue
Block a user