Spaces:
Sleeping
Sleeping
[FIX] Tab4: Deactivate methods with missing resources and fix batch coefficient injection for non-Other methods
Browse files- tabs/tab4_optimization.py +45 -0
- utils/data_utils.py +1 -1
tabs/tab4_optimization.py
CHANGED
|
@@ -425,6 +425,26 @@ def run_and_store_result():
|
|
| 425 |
if m in applied_costs
|
| 426 |
}
|
| 427 |
safe_constraints = neutralize_zero_coefficient_methods(applied_costs, standard_constraints)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
neutralized = [
|
| 429 |
m for m in safe_constraints
|
| 430 |
if safe_constraints[m].get("active") is False
|
|
@@ -480,6 +500,31 @@ def run_and_store_result():
|
|
| 480 |
# Display functions keep the full list so group/unit info is available.
|
| 481 |
custom_resources_for_display = list(custom_resources)
|
| 482 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 483 |
success, result = run_optimization(
|
| 484 |
resource_caps=resource_caps_for_lp,
|
| 485 |
method_constraints=safe_constraints,
|
|
|
|
| 425 |
if m in applied_costs
|
| 426 |
}
|
| 427 |
safe_constraints = neutralize_zero_coefficient_methods(applied_costs, standard_constraints)
|
| 428 |
+
|
| 429 |
+
# Deactivate any method that requires a resource not provided in tab1.
|
| 430 |
+
# Hidden resources (e.g. "Other water") are never in resource_caps because
|
| 431 |
+
# they are not shown in tab1. Without this check, methods that consume such
|
| 432 |
+
# resources go unconstrained in the LP and produce misleading results.
|
| 433 |
+
# A resource is "missing" when it is absent from resource_caps (not set by
|
| 434 |
+
# the user) AND has no custom batch covering it.
|
| 435 |
+
custom_batch_names_pre = {b["name"] for b in custom_resources}
|
| 436 |
+
available_resources = set(st.session_state.resource_caps) | custom_batch_names_pre
|
| 437 |
+
for m, costs in applied_costs.items():
|
| 438 |
+
if not safe_constraints.get(m, {}).get("active", True):
|
| 439 |
+
continue
|
| 440 |
+
missing = [
|
| 441 |
+
r for r, c in costs.items()
|
| 442 |
+
if c > 0 and r not in available_resources
|
| 443 |
+
]
|
| 444 |
+
if missing:
|
| 445 |
+
safe_constraints.setdefault(m, {"active": True, "cap_type": "percent", "cap_value": 100})
|
| 446 |
+
safe_constraints[m] = {**safe_constraints[m], "active": False}
|
| 447 |
+
|
| 448 |
neutralized = [
|
| 449 |
m for m in safe_constraints
|
| 450 |
if safe_constraints[m].get("active") is False
|
|
|
|
| 500 |
# Display functions keep the full list so group/unit info is available.
|
| 501 |
custom_resources_for_display = list(custom_resources)
|
| 502 |
|
| 503 |
+
# Non-"Other" methods that shared an expanded batch (e.g. "Mineral OAE" also
|
| 504 |
+
# used "Limestone") lost their z-variable when the batch was removed from
|
| 505 |
+
# custom_resources_for_lp. Inject the batch as a standard resource coefficient
|
| 506 |
+
# into their applied_costs so the type-A constraint still applies to them.
|
| 507 |
+
#
|
| 508 |
+
# Also zero out the standard group resource coefficient when the standard pool
|
| 509 |
+
# is empty (cap = 0). In the z-variable path the B-constraint allows
|
| 510 |
+
# "y_m - z_{m,n} ≤ 0", letting the batch fully cover the method. In the
|
| 511 |
+
# standard-resource path that substitute mechanism is gone, so a method with
|
| 512 |
+
# cost(group) > 0 and cap(group) = 0 would be incorrectly forced to y = 0
|
| 513 |
+
# by the type-A constraint before it can use the batch at all.
|
| 514 |
+
for batch in custom_resources:
|
| 515 |
+
if batch["name"] not in expanded_batch_names:
|
| 516 |
+
continue
|
| 517 |
+
batch_group = batch.get("group", "")
|
| 518 |
+
group_cap_is_zero = resource_caps_for_lp.get(batch_group, 0.0) == 0.0
|
| 519 |
+
for method_name, coeff_dict in batch.get("methods", {}).items():
|
| 520 |
+
if method_name not in applied_costs:
|
| 521 |
+
continue # "Other" method was already replaced by variants above
|
| 522 |
+
applied_costs[method_name][batch["name"]] = coeff_dict.get("median", 0.0)
|
| 523 |
+
# If the standard pool for this group is empty, zero its coefficient so
|
| 524 |
+
# the type-A constraint doesn't force the method's output to zero.
|
| 525 |
+
if group_cap_is_zero and batch_group in applied_costs[method_name]:
|
| 526 |
+
applied_costs[method_name][batch_group] = 0.0
|
| 527 |
+
|
| 528 |
success, result = run_optimization(
|
| 529 |
resource_caps=resource_caps_for_lp,
|
| 530 |
method_constraints=safe_constraints,
|
utils/data_utils.py
CHANGED
|
@@ -260,5 +260,5 @@ def get_other_method_variants(method_name: str, custom_resources: list) -> list:
|
|
| 260 |
|
| 261 |
|
| 262 |
def make_variant_name(method_name: str, batch_name: str) -> str:
|
| 263 |
-
"""Build the display/LP name for a method variant: '<Method>
|
| 264 |
return f"{method_name}: {batch_name}"
|
|
|
|
| 260 |
|
| 261 |
|
| 262 |
def make_variant_name(method_name: str, batch_name: str) -> str:
|
| 263 |
+
"""Build the display/LP name for a method variant: '<Method>: <Batch>'."""
|
| 264 |
return f"{method_name}: {batch_name}"
|