Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -120,6 +120,22 @@ add_password_gate()
|
|
| 120 |
# Utilities
|
| 121 |
# =========================
|
| 122 |
def rmse(y_true, y_pred): return float(np.sqrt(mean_squared_error(y_true, y_pred)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
def pearson_r(y_true, y_pred):
|
| 124 |
a = np.asarray(y_true, dtype=float); p = np.asarray(y_pred, dtype=float)
|
| 125 |
if a.size < 2: return float("nan")
|
|
@@ -414,6 +430,7 @@ def preview_tracks(df: pd.DataFrame, cols: list[str]):
|
|
| 414 |
if i == 0: ax.set_ylabel(y_label)
|
| 415 |
else: ax.tick_params(labelleft=False); ax.set_ylabel("")
|
| 416 |
fig.tight_layout(); return fig
|
|
|
|
| 417 |
# =========================
|
| 418 |
# Load models + metas
|
| 419 |
# =========================
|
|
@@ -424,7 +441,6 @@ def _first_in_dirs(names): # prefer uploaded over repo copy
|
|
| 424 |
if p.exists() and p.stat().st_size > 0:
|
| 425 |
return p
|
| 426 |
return None
|
| 427 |
-
|
| 428 |
|
| 429 |
def _load_meta(p: Path) -> dict:
|
| 430 |
if not p or not p.exists(): return {}
|
|
@@ -471,12 +487,8 @@ model_bo, info_bo = _unwrap(payload_bo)
|
|
| 471 |
model_bd, info_bd = _unwrap(payload_bd)
|
| 472 |
|
| 473 |
# Resolve features per model (prefer model_info → meta → fallback)
|
| 474 |
-
features_bo = list((info_bo.get("features")
|
| 475 |
-
|
| 476 |
-
or FEATURES_DEFAULT))
|
| 477 |
-
features_bd = list((info_bd.get("features")
|
| 478 |
-
or meta_bd.get("features")
|
| 479 |
-
or FEATURES_DEFAULT))
|
| 480 |
|
| 481 |
# Use the UNION so BD's 'Depth (ft)' is required and both models can run from one input
|
| 482 |
# Order: keep BO order, then append BD-only columns in order
|
|
@@ -495,7 +507,6 @@ st.session_state["TARGET_BO"] = TARGET_BO
|
|
| 495 |
st.session_state["TARGET_BD"] = TARGET_BD
|
| 496 |
st.session_state["X_UNITS"] = X_UNITS
|
| 497 |
|
| 498 |
-
|
| 499 |
# =========================
|
| 500 |
# Session state
|
| 501 |
# =========================
|
|
@@ -539,6 +550,7 @@ def sticky_header(title, message):
|
|
| 539 |
if st.session_state.app_step == "intro":
|
| 540 |
st.header("Welcome!")
|
| 541 |
st.markdown("This software estimates **Breakout** and **Breakdown** mud-weight limits from drilling data.")
|
|
|
|
| 542 |
st.subheader("How It Works")
|
| 543 |
st.markdown("1) **Upload data** and preview.\n2) **Run Model** to compute Train/Test metrics.\n3) Go to **Validation** (with actual BO/BD) or **Prediction** (no actuals).\n4) Use **Combined** tab to see both limits on one track.")
|
| 544 |
if st.button("Start Showcase", type="primary"):
|
|
@@ -589,6 +601,7 @@ if st.session_state.app_step == "dev":
|
|
| 589 |
sticky_header("Case Building", "📄 **Preview** then click **Run Model**.")
|
| 590 |
else:
|
| 591 |
sticky_header("Case Building", "**Upload your data** and run the model.")
|
|
|
|
| 592 |
|
| 593 |
if run and st.session_state.dev_file_bytes:
|
| 594 |
book = read_book_bytes(st.session_state.dev_file_bytes)
|
|
@@ -605,14 +618,12 @@ if st.session_state.app_step == "dev":
|
|
| 605 |
Xtr_bd = _make_X(tr, st.session_state["FEATURES_BD"])
|
| 606 |
Xte_bo = _make_X(te, st.session_state["FEATURES_BO"])
|
| 607 |
Xte_bd = _make_X(te, st.session_state["FEATURES_BD"])
|
| 608 |
-
|
| 609 |
tr[PRED_BO] = model_bo.predict(Xtr_bo)
|
| 610 |
tr[PRED_BD] = model_bd.predict(Xtr_bd)
|
| 611 |
te[PRED_BO] = model_bo.predict(Xte_bo)
|
| 612 |
te[PRED_BD] = model_bd.predict(Xte_bd)
|
| 613 |
|
| 614 |
-
|
| 615 |
-
|
| 616 |
st.session_state.results["Train"]=tr; st.session_state.results["Test"]=te
|
| 617 |
st.session_state.results["m_train_bo"]={"R": pearson_r(tr[TARGET_BO], tr[PRED_BO]), "RMSE": rmse(tr[TARGET_BO], tr[PRED_BO]), "MAE": mean_absolute_error(tr[TARGET_BO], tr[PRED_BO])}
|
| 618 |
st.session_state.results["m_train_bd"]={"R": pearson_r(tr[TARGET_BD], tr[PRED_BD]), "RMSE": rmse(tr[TARGET_BD], tr[PRED_BD]), "MAE": mean_absolute_error(tr[TARGET_BD], tr[PRED_BD])}
|
|
@@ -630,14 +641,15 @@ if st.session_state.app_step == "dev":
|
|
| 630 |
_metrics_block("BO", mbo); _metrics_block("BD", mbd)
|
| 631 |
st.markdown("<div style='text-align:left;font-size:0.8em;color:#6b7280;margin-top:-16px;margin-bottom:8px;'><strong>R</strong> = Pearson correlation • <strong>RMSE/MAE</strong> in MW (pcf)</div>", unsafe_allow_html=True)
|
| 632 |
t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
|
|
|
|
| 633 |
with t1:
|
| 634 |
st.plotly_chart(track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
|
| 635 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 636 |
-
st.pyplot(cross_plot_static(df[TARGET_BO], df[PRED_BO], f"Actual {TARGET_BO}", f"Predicted {TARGET_BO}", COLORS["pred_bo"]), use_container_width=False)
|
| 637 |
with t2:
|
| 638 |
st.plotly_chart(track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
|
| 639 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 640 |
-
st.pyplot(cross_plot_static(df[TARGET_BD], df[PRED_BD], f"Actual {TARGET_BD}", f"Predicted {TARGET_BD}", COLORS["pred_bd"]), use_container_width=False)
|
| 641 |
with t3:
|
| 642 |
st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 643 |
|
|
@@ -667,6 +679,7 @@ if st.session_state.app_step == "validate":
|
|
| 667 |
if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
|
| 668 |
|
| 669 |
sticky_header("Validate the Models", "Upload a dataset with the same **feature** columns and **BO/BD** actuals.")
|
|
|
|
| 670 |
|
| 671 |
if go_btn and up is not None:
|
| 672 |
book = read_book_bytes(up.getvalue())
|
|
@@ -702,15 +715,16 @@ if st.session_state.app_step == "validate":
|
|
| 702 |
st.markdown("<div style='text-align:left;font-size:0.8em;color:#6b7280;margin-top:-16px;margin-bottom:8px;'>R = Pearson correlation</div>", unsafe_allow_html=True)
|
| 703 |
|
| 704 |
t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
|
|
|
|
| 705 |
with t1:
|
| 706 |
st.plotly_chart(track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
|
| 707 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 708 |
-
st.pyplot(cross_plot_static(df[TARGET_BO], df[PRED_BO], f"Actual {TARGET_BO}", f"Predicted {TARGET_BO}", COLORS["pred_bo"]),
|
| 709 |
use_container_width=False)
|
| 710 |
with t2:
|
| 711 |
st.plotly_chart(track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
|
| 712 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 713 |
-
st.pyplot(cross_plot_static(df[TARGET_BD], df[PRED_BD], f"Actual {TARGET_BD}", f"Predicted {TARGET_BD}", COLORS["pred_bd"]),
|
| 714 |
use_container_width=False)
|
| 715 |
with t3:
|
| 716 |
st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
|
@@ -738,6 +752,7 @@ if st.session_state.app_step == "predict":
|
|
| 738 |
if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
|
| 739 |
|
| 740 |
sticky_header("Prediction", "Upload a dataset with **feature columns only** (no BO/BD actuals).")
|
|
|
|
| 741 |
|
| 742 |
if go_btn and up is not None:
|
| 743 |
book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
|
|
|
|
| 120 |
# Utilities
|
| 121 |
# =========================
|
| 122 |
def rmse(y_true, y_pred): return float(np.sqrt(mean_squared_error(y_true, y_pred)))
|
| 123 |
+
|
| 124 |
+
def render_bo_bd_note():
|
| 125 |
+
st.markdown(
|
| 126 |
+
"""
|
| 127 |
+
<div class="st-message-box">
|
| 128 |
+
<b>What do BO and BD mean?</b><br>
|
| 129 |
+
<ul style="margin-top:6px;">
|
| 130 |
+
<li><b>BO (Breakout)</b> — the <i>lower</i> mud-weight bound to avoid compressive/shear failure of the wellbore (breakout). Drilling below BO increases breakout risk.</li>
|
| 131 |
+
<li><b>BD (Breakdown)</b> — the <i>upper</i> mud-weight bound to avoid tensile fracturing (hydraulic breakdown). Drilling above BD risks induced fractures and losses.</li>
|
| 132 |
+
</ul>
|
| 133 |
+
The safe mud-weight window is typically between <b>BO</b> and <b>BD</b>.
|
| 134 |
+
</div>
|
| 135 |
+
""",
|
| 136 |
+
unsafe_allow_html=True,
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
def pearson_r(y_true, y_pred):
|
| 140 |
a = np.asarray(y_true, dtype=float); p = np.asarray(y_pred, dtype=float)
|
| 141 |
if a.size < 2: return float("nan")
|
|
|
|
| 430 |
if i == 0: ax.set_ylabel(y_label)
|
| 431 |
else: ax.tick_params(labelleft=False); ax.set_ylabel("")
|
| 432 |
fig.tight_layout(); return fig
|
| 433 |
+
|
| 434 |
# =========================
|
| 435 |
# Load models + metas
|
| 436 |
# =========================
|
|
|
|
| 441 |
if p.exists() and p.stat().st_size > 0:
|
| 442 |
return p
|
| 443 |
return None
|
|
|
|
| 444 |
|
| 445 |
def _load_meta(p: Path) -> dict:
|
| 446 |
if not p or not p.exists(): return {}
|
|
|
|
| 487 |
model_bd, info_bd = _unwrap(payload_bd)
|
| 488 |
|
| 489 |
# Resolve features per model (prefer model_info → meta → fallback)
|
| 490 |
+
features_bo = list((info_bo.get("features") or meta_bo.get("features") or FEATURES_DEFAULT))
|
| 491 |
+
features_bd = list((info_bd.get("features") or meta_bd.get("features") or FEATURES_DEFAULT))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
|
| 493 |
# Use the UNION so BD's 'Depth (ft)' is required and both models can run from one input
|
| 494 |
# Order: keep BO order, then append BD-only columns in order
|
|
|
|
| 507 |
st.session_state["TARGET_BD"] = TARGET_BD
|
| 508 |
st.session_state["X_UNITS"] = X_UNITS
|
| 509 |
|
|
|
|
| 510 |
# =========================
|
| 511 |
# Session state
|
| 512 |
# =========================
|
|
|
|
| 550 |
if st.session_state.app_step == "intro":
|
| 551 |
st.header("Welcome!")
|
| 552 |
st.markdown("This software estimates **Breakout** and **Breakdown** mud-weight limits from drilling data.")
|
| 553 |
+
render_bo_bd_note()
|
| 554 |
st.subheader("How It Works")
|
| 555 |
st.markdown("1) **Upload data** and preview.\n2) **Run Model** to compute Train/Test metrics.\n3) Go to **Validation** (with actual BO/BD) or **Prediction** (no actuals).\n4) Use **Combined** tab to see both limits on one track.")
|
| 556 |
if st.button("Start Showcase", type="primary"):
|
|
|
|
| 601 |
sticky_header("Case Building", "📄 **Preview** then click **Run Model**.")
|
| 602 |
else:
|
| 603 |
sticky_header("Case Building", "**Upload your data** and run the model.")
|
| 604 |
+
render_bo_bd_note()
|
| 605 |
|
| 606 |
if run and st.session_state.dev_file_bytes:
|
| 607 |
book = read_book_bytes(st.session_state.dev_file_bytes)
|
|
|
|
| 618 |
Xtr_bd = _make_X(tr, st.session_state["FEATURES_BD"])
|
| 619 |
Xte_bo = _make_X(te, st.session_state["FEATURES_BO"])
|
| 620 |
Xte_bd = _make_X(te, st.session_state["FEATURES_BD"])
|
| 621 |
+
|
| 622 |
tr[PRED_BO] = model_bo.predict(Xtr_bo)
|
| 623 |
tr[PRED_BD] = model_bd.predict(Xtr_bd)
|
| 624 |
te[PRED_BO] = model_bo.predict(Xte_bo)
|
| 625 |
te[PRED_BD] = model_bd.predict(Xte_bd)
|
| 626 |
|
|
|
|
|
|
|
| 627 |
st.session_state.results["Train"]=tr; st.session_state.results["Test"]=te
|
| 628 |
st.session_state.results["m_train_bo"]={"R": pearson_r(tr[TARGET_BO], tr[PRED_BO]), "RMSE": rmse(tr[TARGET_BO], tr[PRED_BO]), "MAE": mean_absolute_error(tr[TARGET_BO], tr[PRED_BO])}
|
| 629 |
st.session_state.results["m_train_bd"]={"R": pearson_r(tr[TARGET_BD], tr[PRED_BD]), "RMSE": rmse(tr[TARGET_BD], tr[PRED_BD]), "MAE": mean_absolute_error(tr[TARGET_BD], tr[PRED_BD])}
|
|
|
|
| 641 |
_metrics_block("BO", mbo); _metrics_block("BD", mbd)
|
| 642 |
st.markdown("<div style='text-align:left;font-size:0.8em;color:#6b7280;margin-top:-16px;margin-bottom:8px;'><strong>R</strong> = Pearson correlation • <strong>RMSE/MAE</strong> in MW (pcf)</div>", unsafe_allow_html=True)
|
| 643 |
t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
|
| 644 |
+
units = st.session_state.get("X_UNITS","MW (pcf)")
|
| 645 |
with t1:
|
| 646 |
st.plotly_chart(track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
|
| 647 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 648 |
+
st.pyplot(cross_plot_static(df[TARGET_BO], df[PRED_BO], f"Actual {TARGET_BO} ({units})", f"Predicted {TARGET_BO} ({units})", COLORS["pred_bo"]), use_container_width=False)
|
| 649 |
with t2:
|
| 650 |
st.plotly_chart(track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
|
| 651 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 652 |
+
st.pyplot(cross_plot_static(df[TARGET_BD], df[PRED_BD], f"Actual {TARGET_BD} ({units})", f"Predicted {TARGET_BD} ({units})", COLORS["pred_bd"]), use_container_width=False)
|
| 653 |
with t3:
|
| 654 |
st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 655 |
|
|
|
|
| 679 |
if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
|
| 680 |
|
| 681 |
sticky_header("Validate the Models", "Upload a dataset with the same **feature** columns and **BO/BD** actuals.")
|
| 682 |
+
render_bo_bd_note()
|
| 683 |
|
| 684 |
if go_btn and up is not None:
|
| 685 |
book = read_book_bytes(up.getvalue())
|
|
|
|
| 715 |
st.markdown("<div style='text-align:left;font-size:0.8em;color:#6b7280;margin-top:-16px;margin-bottom:8px;'>R = Pearson correlation</div>", unsafe_allow_html=True)
|
| 716 |
|
| 717 |
t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
|
| 718 |
+
units = st.session_state.get("X_UNITS","MW (pcf)")
|
| 719 |
with t1:
|
| 720 |
st.plotly_chart(track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
|
| 721 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 722 |
+
st.pyplot(cross_plot_static(df[TARGET_BO], df[PRED_BO], f"Actual {TARGET_BO} ({units})", f"Predicted {TARGET_BO} ({units})", COLORS["pred_bo"]),
|
| 723 |
use_container_width=False)
|
| 724 |
with t2:
|
| 725 |
st.plotly_chart(track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
|
| 726 |
use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
| 727 |
+
st.pyplot(cross_plot_static(df[TARGET_BD], df[PRED_BD], f"Actual {TARGET_BD} ({units})", f"Predicted {TARGET_BD} ({units})", COLORS["pred_bd"]),
|
| 728 |
use_container_width=False)
|
| 729 |
with t3:
|
| 730 |
st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
|
|
|
|
| 752 |
if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
|
| 753 |
|
| 754 |
sticky_header("Prediction", "Upload a dataset with **feature columns only** (no BO/BD actuals).")
|
| 755 |
+
render_bo_bd_note()
|
| 756 |
|
| 757 |
if go_btn and up is not None:
|
| 758 |
book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
|