UCS2014 commited on
Commit
c377a59
·
verified ·
1 Parent(s): 13daa17

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -26
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py — ST_GeoMech_YM (mirrors your UCS GUI, adapted for Young's Modulus)
2
  import io, json, os, base64, math
3
  from pathlib import Path
4
  import streamlit as st
@@ -17,17 +17,18 @@ import plotly.graph_objects as go
17
  from sklearn.metrics import mean_squared_error, mean_absolute_error
18
 
19
  # =========================
20
- # Constants (Ym variant)
21
  # =========================
22
- APP_NAME = "ST_GeoMech_YM"
23
- TAGLINE = "Real-Time Young's Modulus Tracking"
24
 
 
25
  FEATURES = ["WOB(klbf)", "TORQUE(kft.lbf)", "SPP(psi)", "RPM(1/min)", "ROP(ft/h)", "Flow Rate, gpm"]
26
- TARGET = "Actual Ym"
27
- PRED_COL = "Ym_Pred"
28
 
29
  MODELS_DIR = Path("models")
30
- DEFAULT_MODEL = MODELS_DIR / "ym_rf.joblib"
31
  MODEL_FALLBACKS = [MODELS_DIR / "model.joblib", MODELS_DIR / "model.pkl"]
32
  COLORS = {"pred": "#1f77b4", "actual": "#f2b702", "ref": "#5a5a5a"}
33
 
@@ -193,7 +194,7 @@ def find_sheet(book, names):
193
  return None
194
 
195
  def _nice_tick0(xmin: float, step: float = 0.1) -> float:
196
- # Rounded start tick for continuous Ym scales (unit-agnostic)
197
  return step * math.floor(xmin / step) if np.isfinite(xmin) else xmin
198
 
199
  def df_centered_rounded(df: pd.DataFrame, hide_index=True):
@@ -349,7 +350,7 @@ def build_export_workbook(selected: list[str], ndigits: int = 3, do_autofit: boo
349
  _excel_autofit(writer, sheet, df)
350
  bio.seek(0)
351
 
352
- fname = f"YM_Export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
353
  return bio.getvalue(), fname, order
354
 
355
  # --------- SIMPLE export UI (dropdown checklist, starts empty) ----------
@@ -380,7 +381,7 @@ def render_export_button(phase_key: str) -> None:
380
  st.download_button(
381
  label="⬇️ Export Excel",
382
  data=b"",
383
- file_name="YM_Export.xlsx",
384
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
385
  disabled=True,
386
  key=f"download_{phase_key}",
@@ -394,16 +395,16 @@ def render_export_button(phase_key: str) -> None:
394
  st.download_button(
395
  "⬇️ Export Excel",
396
  data=(data or b""),
397
- file_name=(fname or "YM_Export.xlsx"),
398
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
399
  disabled=(data is None),
400
  key=f"download_{phase_key}",
401
  )
402
 
403
  # =========================
404
- # Cross plot (Matplotlib) — auto-scaled for Ym
405
  # =========================
406
- def cross_plot_static(actual, pred, xlabel="Actual Ym", ylabel="Predicted Ym"):
407
  a = pd.Series(actual, dtype=float)
408
  p = pd.Series(pred, dtype=float)
409
 
@@ -426,7 +427,7 @@ def cross_plot_static(actual, pred, xlabel="Actual Ym", ylabel="Predicted Ym"):
426
  ax.set_yticks(ticks)
427
  ax.set_aspect("equal", adjustable="box")
428
 
429
- # Generic numeric formatting (2 decimals)
430
  fmt = FuncFormatter(lambda x, _: f"{x:.2f}")
431
  ax.xaxis.set_major_formatter(fmt)
432
  ax.yaxis.set_major_formatter(fmt)
@@ -495,15 +496,15 @@ def track_plot(df, include_actual=True):
495
  legend_title_text=""
496
  )
497
 
498
- # X axis with NO decimals
499
  fig.update_xaxes(
500
- title_text="Ym",
501
  title_font=dict(size=20, family=BOLD_FONT, color="#000"),
502
  tickfont=dict(size=15, family=BOLD_FONT, color="#000"),
503
  side="top",
504
  range=[xmin, xmax],
505
  ticks="outside",
506
- tickformat=",.0f", # integers, thousands separated
507
  tickmode="auto",
508
  tick0=tick0,
509
  showline=True, linewidth=1.2, linecolor="#444", mirror=True,
@@ -598,7 +599,7 @@ def ensure_model() -> Path|None:
598
 
599
  mpath = ensure_model()
600
  if not mpath:
601
- st.error("Model not found. Upload models/ym_rf.joblib (or set MODEL_URL).")
602
  st.stop()
603
  try:
604
  model = load_model(str(mpath))
@@ -608,13 +609,16 @@ except Exception as e:
608
 
609
  # ---------- Load meta (optional) ----------
610
  meta = {}
611
- meta_candidates = [MODELS_DIR / "meta.json", MODELS_DIR / "ym_meta.json"]
 
612
  meta_path = next((p for p in meta_candidates if p.exists()), None)
613
  if meta_path:
614
  try:
615
  meta = json.loads(meta_path.read_text(encoding="utf-8"))
616
  FEATURES = meta.get("features", FEATURES)
617
  TARGET = meta.get("target", TARGET)
 
 
618
  except Exception as e:
619
  st.warning(f"Could not parse meta file ({meta_path.name}): {e}")
620
 
@@ -679,12 +683,12 @@ def sticky_header(title, message):
679
  # =========================
680
  if st.session_state.app_step == "intro":
681
  st.header("Welcome!")
682
- st.markdown("This software is developed by *Smart Thinking AI-Solutions Team* to estimate Young's Modulus (Ym) from drilling data.")
683
  st.subheader("How It Works")
684
  st.markdown(
685
  "1) **Upload your data to build the case and preview the model performance.** \n"
686
  "2) Click **Run Model** to compute metrics and plots. \n"
687
- "3) **Proceed to Validation** (with actual Ym) or **Proceed to Prediction** (no Ym)."
688
  )
689
  if st.button("Start Showcase", type="primary"):
690
  st.session_state.app_step = "dev"; st.rerun()
@@ -791,7 +795,7 @@ if st.session_state.app_step == "dev":
791
  render_export_button(phase_key="dev")
792
 
793
  # =========================
794
- # VALIDATION (with actual Ym)
795
  # =========================
796
  if st.session_state.app_step == "validate":
797
  st.sidebar.header("Validate the Model")
@@ -807,7 +811,7 @@ if st.session_state.app_step == "validate":
807
  if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
808
  if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
809
 
810
- sticky_header("Validate the Model", "Upload a dataset with the same **features** and **Actual Ym** to evaluate performance.")
811
 
812
  if go_btn and up is not None:
813
  book = read_book_bytes(up.getvalue())
@@ -874,10 +878,10 @@ if st.session_state.app_step == "validate":
874
  df_centered_rounded(st.session_state.results["oor_tbl"])
875
 
876
  # =========================
877
- # PREDICTION (no actual Ym)
878
  # =========================
879
  if st.session_state.app_step == "predict":
880
- st.sidebar.header("Prediction (No Actual Ym)")
881
  up = st.sidebar.file_uploader("Upload Prediction Excel", type=["xlsx","xls"])
882
  if up is not None:
883
  book = read_book_bytes(up.getvalue())
@@ -889,7 +893,7 @@ if st.session_state.app_step == "predict":
889
  go_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
890
  if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
891
 
892
- sticky_header("Prediction", "Upload a dataset with the feature columns (no **Actual Ym**).")
893
 
894
  if go_btn and up is not None:
895
  book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
 
1
+ # app.py — ST_Sonic_Ts (mirrors your Ym GUI, adapted for Shear Slowness Ts)
2
  import io, json, os, base64, math
3
  from pathlib import Path
4
  import streamlit as st
 
17
  from sklearn.metrics import mean_squared_error, mean_absolute_error
18
 
19
  # =========================
20
+ # Constants (Ts variant)
21
  # =========================
22
+ APP_NAME = "ST_Sonic_Ts"
23
+ TAGLINE = "Real-Time Shear Slowness (Ts) Tracking"
24
 
25
+ # Keep your drilling features the same (match training & Excel headers)
26
  FEATURES = ["WOB(klbf)", "TORQUE(kft.lbf)", "SPP(psi)", "RPM(1/min)", "ROP(ft/h)", "Flow Rate, gpm"]
27
+ TARGET = "Ts" # <-- actual Ts column in your sheets
28
+ PRED_COL = "Ts_Pred" # <-- predicted Ts column added by the app
29
 
30
  MODELS_DIR = Path("models")
31
+ DEFAULT_MODEL = MODELS_DIR / "ts_model.joblib"
32
  MODEL_FALLBACKS = [MODELS_DIR / "model.joblib", MODELS_DIR / "model.pkl"]
33
  COLORS = {"pred": "#1f77b4", "actual": "#f2b702", "ref": "#5a5a5a"}
34
 
 
194
  return None
195
 
196
  def _nice_tick0(xmin: float, step: float = 0.1) -> float:
197
+ # Rounded start tick for continuous Ts scales (unit-agnostic)
198
  return step * math.floor(xmin / step) if np.isfinite(xmin) else xmin
199
 
200
  def df_centered_rounded(df: pd.DataFrame, hide_index=True):
 
350
  _excel_autofit(writer, sheet, df)
351
  bio.seek(0)
352
 
353
+ fname = f"TS_Export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
354
  return bio.getvalue(), fname, order
355
 
356
  # --------- SIMPLE export UI (dropdown checklist, starts empty) ----------
 
381
  st.download_button(
382
  label="⬇️ Export Excel",
383
  data=b"",
384
+ file_name="TS_Export.xlsx",
385
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
386
  disabled=True,
387
  key=f"download_{phase_key}",
 
395
  st.download_button(
396
  "⬇️ Export Excel",
397
  data=(data or b""),
398
+ file_name=(fname or "TS_Export.xlsx"),
399
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
400
  disabled=(data is None),
401
  key=f"download_{phase_key}",
402
  )
403
 
404
  # =========================
405
+ # Cross plot (Matplotlib) — auto-scaled for Ts
406
  # =========================
407
+ def cross_plot_static(actual, pred, xlabel="Actual Ts (µs/ft)", ylabel="Predicted Ts (µs/ft)"):
408
  a = pd.Series(actual, dtype=float)
409
  p = pd.Series(pred, dtype=float)
410
 
 
427
  ax.set_yticks(ticks)
428
  ax.set_aspect("equal", adjustable="box")
429
 
430
+ # Generic numeric formatting (2 decimals) in plot; export uses 3 decimals
431
  fmt = FuncFormatter(lambda x, _: f"{x:.2f}")
432
  ax.xaxis.set_major_formatter(fmt)
433
  ax.yaxis.set_major_formatter(fmt)
 
496
  legend_title_text=""
497
  )
498
 
499
+ # X axis with NO decimals (Ts is in µs/ft; typically integer-like)
500
  fig.update_xaxes(
501
+ title_text="Ts (µs/ft)",
502
  title_font=dict(size=20, family=BOLD_FONT, color="#000"),
503
  tickfont=dict(size=15, family=BOLD_FONT, color="#000"),
504
  side="top",
505
  range=[xmin, xmax],
506
  ticks="outside",
507
+ tickformat=",.0f",
508
  tickmode="auto",
509
  tick0=tick0,
510
  showline=True, linewidth=1.2, linecolor="#444", mirror=True,
 
599
 
600
  mpath = ensure_model()
601
  if not mpath:
602
+ st.error("Model not found. Upload models/ts_model.joblib (or set MODEL_URL).")
603
  st.stop()
604
  try:
605
  model = load_model(str(mpath))
 
609
 
610
  # ---------- Load meta (optional) ----------
611
  meta = {}
612
+ # Prefer a Ts-specific meta, fall back to a generic one if present
613
+ meta_candidates = [MODELS_DIR / "ts_meta.json", MODELS_DIR / "meta.json"]
614
  meta_path = next((p for p in meta_candidates if p.exists()), None)
615
  if meta_path:
616
  try:
617
  meta = json.loads(meta_path.read_text(encoding="utf-8"))
618
  FEATURES = meta.get("features", FEATURES)
619
  TARGET = meta.get("target", TARGET)
620
+ # If meta provides a custom pred column, respect it
621
+ PRED_COL = meta.get("pred_col", PRED_COL)
622
  except Exception as e:
623
  st.warning(f"Could not parse meta file ({meta_path.name}): {e}")
624
 
 
683
  # =========================
684
  if st.session_state.app_step == "intro":
685
  st.header("Welcome!")
686
+ st.markdown("This software is developed by *Smart Thinking AI-Solutions Team* to estimate **Shear Slowness (Ts)** from drilling data.")
687
  st.subheader("How It Works")
688
  st.markdown(
689
  "1) **Upload your data to build the case and preview the model performance.** \n"
690
  "2) Click **Run Model** to compute metrics and plots. \n"
691
+ "3) **Proceed to Validation** (with actual Ts) or **Proceed to Prediction** (no Ts)."
692
  )
693
  if st.button("Start Showcase", type="primary"):
694
  st.session_state.app_step = "dev"; st.rerun()
 
795
  render_export_button(phase_key="dev")
796
 
797
  # =========================
798
+ # VALIDATION (with actual Ts)
799
  # =========================
800
  if st.session_state.app_step == "validate":
801
  st.sidebar.header("Validate the Model")
 
811
  if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
812
  if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
813
 
814
+ sticky_header("Validate the Model", "Upload a dataset with the same **features** and **Ts** to evaluate performance.")
815
 
816
  if go_btn and up is not None:
817
  book = read_book_bytes(up.getvalue())
 
878
  df_centered_rounded(st.session_state.results["oor_tbl"])
879
 
880
  # =========================
881
+ # PREDICTION (no actual Ts)
882
  # =========================
883
  if st.session_state.app_step == "predict":
884
+ st.sidebar.header("Prediction (No Actual Ts)")
885
  up = st.sidebar.file_uploader("Upload Prediction Excel", type=["xlsx","xls"])
886
  if up is not None:
887
  book = read_book_bytes(up.getvalue())
 
893
  go_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
894
  if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
895
 
896
+ sticky_header("Prediction", "Upload a dataset with the feature columns (no **Ts**).")
897
 
898
  if go_btn and up is not None:
899
  book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]