razaali10 commited on
Commit
4742593
·
verified ·
1 Parent(s): 9c25769

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -25
app.py CHANGED
@@ -7,41 +7,87 @@ from hardy_cross_plot import plot_hardy_cross_network
7
  import os
8
  import numpy as np
9
 
 
10
  def hardy_cross_tab(file, loops_json, max_iterations, tolerance):
 
 
 
 
 
 
 
11
  try:
 
 
 
 
 
 
 
 
 
 
12
  df = pd.read_csv(file.name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  pipes_data = df.to_dict(orient='records')
14
- loops = json.loads(loops_json)
 
15
  results, final_flows = hardy_cross_solver(
16
  pipes_data,
17
  loops,
18
  max_iterations=int(max_iterations),
19
  tolerance=float(tolerance)
20
  )
 
 
21
  plot_filename = "hardy_cross_network_result.png"
22
  plot_path = plot_hardy_cross_network(final_flows, save_path=plot_filename)
 
 
23
 
24
- # Create a downloadable CSV for head loss calculation
25
  auto_hl_csv = "final_flows_for_head_loss.csv"
26
- # Merge original parameters with computed flows
27
- merge_df = df[["pipe_id", "length", "diameter", "friction_factor"]].copy()
28
- merge_df = merge_df.drop_duplicates(subset="pipe_id") # remove potential duplicates
 
 
 
 
 
 
 
 
 
 
 
29
  merge_df = pd.merge(
30
  merge_df,
31
  final_flows.rename(columns={"Q": "flow_m3s"}),
32
  on="pipe_id",
33
  how="left"
34
- )
35
- # Rename columns for head loss calc
36
- merge_df = merge_df.rename(columns={
37
- "length": "length_m",
38
- "diameter": "diameter_m"
39
- })
40
  merge_df.to_csv(auto_hl_csv, index=False)
41
-
42
- # Safety check
43
- if not (os.path.isfile(plot_path) and plot_path.endswith(".png")):
44
- plot_path = ""
45
 
46
  return (
47
  final_flows.to_markdown(index=False),
@@ -49,44 +95,77 @@ def hardy_cross_tab(file, loops_json, max_iterations, tolerance):
49
  plot_path,
50
  auto_hl_csv
51
  )
 
52
  except Exception as e:
53
  tb = traceback.format_exc()
54
  error_message = f"❌ **Error:** {str(e)}\n\n```\n{tb}\n```"
55
- return error_message, "", "", None
 
 
56
 
57
  def head_loss_tab(file):
 
 
 
 
 
58
  try:
 
 
 
59
  df = pd.read_csv(file.name)
 
 
 
 
 
 
 
 
 
60
  g = 9.81
61
  df["area_m2"] = np.pi * (df["diameter_m"] / 2) ** 2
 
 
62
  df["velocity_ms"] = np.abs(df["flow_m3s"]) / df["area_m2"]
63
  df["head_loss_m"] = (
64
- df["friction_factor"]
65
- * df["length_m"]
66
- / df["diameter_m"]
67
- * df["velocity_ms"] ** 2
68
- / (2 * g)
69
  )
 
70
  output_csv = "head_loss_results.csv"
71
  df.to_csv(output_csv, index=False)
 
 
 
72
  return (
73
  df[["pipe_id", "velocity_ms", "head_loss_m"]].to_markdown(index=False),
74
  output_csv
75
  )
 
76
  except Exception as e:
77
  tb = traceback.format_exc()
78
  error_message = f"❌ **Error:** {str(e)}\n\n```\n{tb}\n```"
79
  return error_message, None
80
 
 
81
  with gr.Blocks(title="Hardy Cross Method and Head Loss Calculator") as demo:
82
  gr.Markdown("# Hardy Cross Method Solver and Head Loss Calculator")
 
83
  with gr.Tab("Hardy Cross Solver"):
84
- gr.Markdown("Upload your pipe network CSV and loops JSON to compute flows and visualize using the Hardy Cross Method. A CSV ready for head loss calculation will also be generated for you.")
 
 
 
85
  file_input = gr.File(label="Upload Pipe Network CSV")
86
- loops_json_input = gr.Textbox(label="Loops JSON", placeholder='{"L1": ["AB", "BC", "CD"], "L2": ["DE", "EF", "BC"]}')
 
 
 
87
  max_iter_input = gr.Number(label="Max Iterations", value=10)
88
  tol_input = gr.Number(label="Tolerance", value=0.001)
89
  run_button = gr.Button("Run Hardy Cross")
 
90
  flows_output = gr.Markdown(label="Final Pipe Flows")
91
  iterations_output = gr.Markdown(label="Iteration Details")
92
  plot_output = gr.Image(label="Flow Direction Plot")
@@ -99,7 +178,10 @@ with gr.Blocks(title="Hardy Cross Method and Head Loss Calculator") as demo:
99
  )
100
 
101
  with gr.Tab("Head Loss Calculator"):
102
- gr.Markdown("Upload your final flows CSV with columns: `pipe_id`, `length_m`, `diameter_m`, `friction_factor`, `flow_m3s` to compute head losses.")
 
 
 
103
  file_input_hl = gr.File(label="Upload Final Flows CSV")
104
  run_button_hl = gr.Button("Compute Head Losses")
105
  head_loss_output = gr.Markdown(label="Head Loss Results")
@@ -111,5 +193,8 @@ with gr.Blocks(title="Hardy Cross Method and Head Loss Calculator") as demo:
111
  outputs=[head_loss_output, download_output]
112
  )
113
 
 
114
  if __name__ == "__main__":
115
- demo.launch()
 
 
 
7
  import os
8
  import numpy as np
9
 
10
+
11
  def hardy_cross_tab(file, loops_json, max_iterations, tolerance):
12
+ """
13
+ Runs the Hardy Cross solver tab:
14
+ - Validates inputs
15
+ - Computes flows & iterations
16
+ - Saves a PNG plot (if possible) and a CSV ready for head-loss tab
17
+ - Returns Markdown tables + image path (or None) + CSV path (or None)
18
+ """
19
  try:
20
+ # Basic presence checks
21
+ if file is None:
22
+ return "❌ **Error:** No CSV uploaded.", "", None, None
23
+
24
+ try:
25
+ loops = json.loads(loops_json)
26
+ except json.JSONDecodeError as e:
27
+ return f"❌ **Invalid Loops JSON:** {e}", "", None, None
28
+
29
+ # Read CSV
30
  df = pd.read_csv(file.name)
31
+
32
+ # Validate loop pipe IDs exist in CSV
33
+ if "pipe_id" not in df.columns:
34
+ return "❌ **Error:** CSV must include a 'pipe_id' column.", "", None, None
35
+
36
+ csv_ids = set(df["pipe_id"].astype(str))
37
+ missing = {}
38
+ for loop_id, ids in loops.items():
39
+ ids = [str(x) for x in ids]
40
+ miss = [p for p in ids if p not in csv_ids]
41
+ if miss:
42
+ missing[loop_id] = miss
43
+ if missing:
44
+ msg = "❌ **Loops reference unknown pipes**:\n" + "\n".join(
45
+ f"- {lid}: {', '.join(miss)}" for lid, miss in missing.items()
46
+ )
47
+ return msg, "", None, None
48
+
49
+ # Prepare data for solver
50
  pipes_data = df.to_dict(orient='records')
51
+
52
+ # Run solver
53
  results, final_flows = hardy_cross_solver(
54
  pipes_data,
55
  loops,
56
  max_iterations=int(max_iterations),
57
  tolerance=float(tolerance)
58
  )
59
+
60
+ # Plot and verify
61
  plot_filename = "hardy_cross_network_result.png"
62
  plot_path = plot_hardy_cross_network(final_flows, save_path=plot_filename)
63
+ if not (isinstance(plot_path, str) and os.path.isfile(plot_path) and plot_path.lower().endswith(".png")):
64
+ plot_path = None # critical: don't return "", use None for gr.Image
65
 
66
+ # Build CSV for head-loss tab
67
  auto_hl_csv = "final_flows_for_head_loss.csv"
68
+ needed_cols = ["pipe_id", "length", "diameter", "friction_factor"]
69
+ missing_cols = [c for c in needed_cols if c not in df.columns]
70
+ if missing_cols:
71
+ # If geometry columns are missing, we still return flows, but skip head-loss CSV
72
+ flows_md = final_flows.to_markdown(index=False)
73
+ iters_md = pd.DataFrame(results).to_markdown(index=False)
74
+ warn = (
75
+ f"⚠️ **Note:** Could not create Head Loss CSV. Missing columns in input CSV: "
76
+ f"{', '.join(missing_cols)}. Include these to enable head-loss download."
77
+ )
78
+ flows_md = warn + "\n\n" + flows_md
79
+ return flows_md, iters_md, plot_path, None
80
+
81
+ merge_df = df[needed_cols].copy().drop_duplicates(subset="pipe_id")
82
  merge_df = pd.merge(
83
  merge_df,
84
  final_flows.rename(columns={"Q": "flow_m3s"}),
85
  on="pipe_id",
86
  how="left"
87
+ ).rename(columns={"length": "length_m", "diameter": "diameter_m"})
 
 
 
 
 
88
  merge_df.to_csv(auto_hl_csv, index=False)
89
+ if not os.path.isfile(auto_hl_csv):
90
+ auto_hl_csv = None # critical: gr.File expects a real file path or None
 
 
91
 
92
  return (
93
  final_flows.to_markdown(index=False),
 
95
  plot_path,
96
  auto_hl_csv
97
  )
98
+
99
  except Exception as e:
100
  tb = traceback.format_exc()
101
  error_message = f"❌ **Error:** {str(e)}\n\n```\n{tb}\n```"
102
+ # critical: return None for image/file slots on error
103
+ return error_message, "", None, None
104
+
105
 
106
  def head_loss_tab(file):
107
+ """
108
+ Head loss calculator tab:
109
+ - Expects CSV with: pipe_id, length_m, diameter_m, friction_factor, flow_m3s
110
+ - Computes velocity and head loss (Darcy–Weisbach) and returns Markdown + downloadable CSV
111
+ """
112
  try:
113
+ if file is None:
114
+ return "❌ **Error:** No CSV uploaded.", None
115
+
116
  df = pd.read_csv(file.name)
117
+
118
+ required = ["pipe_id", "length_m", "diameter_m", "friction_factor", "flow_m3s"]
119
+ missing_cols = [c for c in required if c not in df.columns]
120
+ if missing_cols:
121
+ return (
122
+ f"❌ **Error:** Head loss CSV is missing required columns: {', '.join(missing_cols)}",
123
+ None
124
+ )
125
+
126
  g = 9.81
127
  df["area_m2"] = np.pi * (df["diameter_m"] / 2) ** 2
128
+ # avoid division by zero
129
+ df["area_m2"] = df["area_m2"].replace({0.0: np.nan})
130
  df["velocity_ms"] = np.abs(df["flow_m3s"]) / df["area_m2"]
131
  df["head_loss_m"] = (
132
+ df["friction_factor"] * df["length_m"] / df["diameter_m"] *
133
+ df["velocity_ms"] ** 2 / (2 * g)
 
 
 
134
  )
135
+
136
  output_csv = "head_loss_results.csv"
137
  df.to_csv(output_csv, index=False)
138
+ if not os.path.isfile(output_csv):
139
+ output_csv = None # critical: gr.File expects path or None
140
+
141
  return (
142
  df[["pipe_id", "velocity_ms", "head_loss_m"]].to_markdown(index=False),
143
  output_csv
144
  )
145
+
146
  except Exception as e:
147
  tb = traceback.format_exc()
148
  error_message = f"❌ **Error:** {str(e)}\n\n```\n{tb}\n```"
149
  return error_message, None
150
 
151
+
152
  with gr.Blocks(title="Hardy Cross Method and Head Loss Calculator") as demo:
153
  gr.Markdown("# Hardy Cross Method Solver and Head Loss Calculator")
154
+
155
  with gr.Tab("Hardy Cross Solver"):
156
+ gr.Markdown(
157
+ "Upload your pipe network CSV and loops JSON to compute flows and visualize using the Hardy Cross Method. "
158
+ "A CSV ready for head loss calculation will also be generated for you (if geometry columns are present)."
159
+ )
160
  file_input = gr.File(label="Upload Pipe Network CSV")
161
+ loops_json_input = gr.Textbox(
162
+ label="Loops JSON",
163
+ placeholder='{"L1": ["AB", "BC", "CD"], "L2": ["DE", "EF", "BC"]}'
164
+ )
165
  max_iter_input = gr.Number(label="Max Iterations", value=10)
166
  tol_input = gr.Number(label="Tolerance", value=0.001)
167
  run_button = gr.Button("Run Hardy Cross")
168
+
169
  flows_output = gr.Markdown(label="Final Pipe Flows")
170
  iterations_output = gr.Markdown(label="Iteration Details")
171
  plot_output = gr.Image(label="Flow Direction Plot")
 
178
  )
179
 
180
  with gr.Tab("Head Loss Calculator"):
181
+ gr.Markdown(
182
+ "Upload your final flows CSV with columns: "
183
+ "`pipe_id`, `length_m`, `diameter_m`, `friction_factor`, `flow_m3s` to compute head losses."
184
+ )
185
  file_input_hl = gr.File(label="Upload Final Flows CSV")
186
  run_button_hl = gr.Button("Compute Head Losses")
187
  head_loss_output = gr.Markdown(label="Head Loss Results")
 
193
  outputs=[head_loss_output, download_output]
194
  )
195
 
196
+
197
  if __name__ == "__main__":
198
+ # If you see intermittent SSR caching issues on Spaces, you can set ssr_mode=False:
199
+ # demo.launch(ssr_mode=False)
200
+ demo.launch()