Update app.py
Browse files
app.py
CHANGED
|
@@ -1,97 +1,138 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import numpy as np
|
| 3 |
import plotly.graph_objects as go
|
| 4 |
-
import plotly.express as px
|
| 5 |
-
import pandas as pd
|
| 6 |
-
from plotly.subplots import make_subplots
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
xaxis_title="Year", yaxis_title="Cash Flow ($)",
|
| 68 |
-
height=400, showlegend=True
|
| 69 |
-
)
|
| 70 |
-
|
| 71 |
-
# Sensitivity analysis
|
| 72 |
-
rev_range = np.linspace(revenue*0.6, revenue*1.4, 15)
|
| 73 |
-
npv_range = []
|
| 74 |
-
for r in rev_range:
|
| 75 |
-
temp_cf = [-capex]
|
| 76 |
-
for y in range(1, int(plant_life)+1):
|
| 77 |
-
opex_t = (opex_fixed + opex_var) * (1 + inflation_pct)**y
|
| 78 |
-
rev_t = r * (1 + inflation_pct)**y
|
| 79 |
-
temp_cf.append(rev_t - opex_t)
|
| 80 |
-
temp_cf[-1] += capex * salvage_pct
|
| 81 |
-
npv_range.append(npv(marr, temp_cf))
|
| 82 |
-
|
| 83 |
-
fig_sens = px.line(x=rev_range/1e6, y=np.array(npv_range)/1e6,
|
| 84 |
-
labels={'x':'Revenue ($M)', 'y':'NPV ($M)'},
|
| 85 |
-
title="π NPV Sensitivity to Revenue")
|
| 86 |
-
fig_sens.add_hline(y=0, line_dash="dash", line_color="red")
|
| 87 |
-
|
| 88 |
-
# Results table
|
| 89 |
-
df = pd.DataFrame({
|
| 90 |
-
'Year': years,
|
| 91 |
-
'Cash Flow': np.round(cashflows, 0),
|
| 92 |
-
'Cumulative': np.round(cum_cf, 0)
|
| 93 |
-
})
|
| 94 |
-
|
| 95 |
-
# Format results
|
| 96 |
-
results = f"""
|
| 97 |
-
## π― **Economic Summary**
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import numpy as np
|
| 3 |
import plotly.graph_objects as go
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
+
def calculate_chemeng_tvm(capex, revenue, opex_fixed, opex_var, plant_life, marr_pct, salvage_pct, inflation_pct):
|
| 6 |
+
capex = float(capex)
|
| 7 |
+
revenue = float(revenue)
|
| 8 |
+
opex_fixed = float(opex_fixed)
|
| 9 |
+
opex_var = float(opex_var)
|
| 10 |
+
plant_life = int(plant_life)
|
| 11 |
+
marr = float(marr_pct) / 100
|
| 12 |
+
salvage = float(salvage_pct) / 100
|
| 13 |
+
inflation = float(inflation_pct) / 100
|
| 14 |
+
|
| 15 |
+
# Generate cash flows
|
| 16 |
+
cashflows = [-capex]
|
| 17 |
+
for year in range(1, plant_life + 1):
|
| 18 |
+
opex_total = (opex_fixed + opex_var) * (1 + inflation) ** year
|
| 19 |
+
rev = revenue * (1 + inflation) ** year
|
| 20 |
+
net_cf = rev - opex_total
|
| 21 |
+
cashflows.append(net_cf)
|
| 22 |
+
cashflows[-1] += capex * salvage
|
| 23 |
+
|
| 24 |
+
# NPV Calculation
|
| 25 |
+
npv_val = sum(cf / (1 + marr)**i for i, cf in enumerate(cashflows))
|
| 26 |
+
|
| 27 |
+
# IRR Approximation (Newton-Raphson)
|
| 28 |
+
irr_guess = 0.1
|
| 29 |
+
for _ in range(50):
|
| 30 |
+
npv_est = sum(cf / (1 + irr_guess)**i for i, cf in enumerate(cashflows))
|
| 31 |
+
deriv = sum(-i * cf / (1 + irr_guess)**(i+1) for i, cf in enumerate(cashflows))
|
| 32 |
+
if abs(deriv) < 1e-10: break
|
| 33 |
+
irr_guess -= npv_est / deriv
|
| 34 |
+
irr_val = max(0, irr_guess * 100)
|
| 35 |
+
|
| 36 |
+
# Payback Period
|
| 37 |
+
cum_cf = [0]
|
| 38 |
+
for cf in cashflows[1:]:
|
| 39 |
+
cum_cf.append(cum_cf[-1] + cf)
|
| 40 |
+
payback = next((i for i, total in enumerate(cum_cf) if total >= 0), plant_life)
|
| 41 |
+
|
| 42 |
+
# Results HTML
|
| 43 |
+
results_html = f"""
|
| 44 |
+
<div style='font-family: Arial; padding: 20px;'>
|
| 45 |
+
<h2 style='color: #1f77b4;'>π§ͺ Chemical Engineering Economics</h2>
|
| 46 |
+
<table style='width: 100%; border-collapse: collapse; margin: 20px 0;'>
|
| 47 |
+
<tr style='background: #f0f8ff;'>
|
| 48 |
+
<th style='border: 1px solid #ddd; padding: 12px;'>Metric</th>
|
| 49 |
+
<th style='border: 1px solid #ddd; padding: 12px; text-align: right;'>Value</th>
|
| 50 |
+
</tr>
|
| 51 |
+
<tr>
|
| 52 |
+
<td style='border: 1px solid #ddd; padding: 12px;'><b>π NPV</b></td>
|
| 53 |
+
<td style='border: 1px solid #ddd; padding: 12px; text-align: right; font-size: 18px; color: {'green' if npv_val > 0 else 'red'};'>${npv_val:,.0f}</td>
|
| 54 |
+
</tr>
|
| 55 |
+
<tr style='background: #f9f9f9;'>
|
| 56 |
+
<td style='border: 1px solid #ddd; padding: 12px;'><b>β‘ IRR</b></td>
|
| 57 |
+
<td style='border: 1px solid #ddd; padding: 12px; text-align: right; font-size: 18px;'>{irr_val:.1f}%</td>
|
| 58 |
+
</tr>
|
| 59 |
+
<tr>
|
| 60 |
+
<td style='border: 1px solid #ddd; padding: 12px;'><b>β±οΈ Payback</b></td>
|
| 61 |
+
<td style='border: 1px solid #ddd; padding: 12px; text-align: right;'>{payback:.1f} years</td>
|
| 62 |
+
</tr>
|
| 63 |
+
<tr style='background: #f9f9f9;'>
|
| 64 |
+
<td style='border: 1px solid #ddd; padding: 12px;'><b>π Profitability Index</b></td>
|
| 65 |
+
<td style='border: 1px solid #ddd; padding: 12px; text-align: right;'>{abs(npv_val/capex)*100:.1f}%</td>
|
| 66 |
+
</tr>
|
| 67 |
+
</table>
|
| 68 |
+
<div style='background: {'#d4edda' if npv_val > 0 else '#f8d7da'}; padding: 15px; border-radius: 8px; border-left: 5px solid {'#28a745' if npv_val > 0 else '#dc3545'};'>
|
| 69 |
+
<b>Status: {'β
PROJECT VIABLE (NPV > 0)' if npv_val > 0 else 'β οΈ REVIEW REQUIRED'}</b>
|
| 70 |
+
</div>
|
| 71 |
+
</div>
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
# Cash Flow Chart
|
| 75 |
+
years = list(range(len(cashflows)))
|
| 76 |
+
fig = go.Figure()
|
| 77 |
+
fig.add_trace(go.Bar(x=years[1:], y=cashflows[1:],
|
| 78 |
+
name="Annual Cash Flow", marker_color='#1f77b4'))
|
| 79 |
+
fig.add_trace(go.Scatter(x=years, y=cum_cf,
|
| 80 |
+
mode='lines+markers', name="Cumulative CF",
|
| 81 |
+
line=dict(color='#ff4444', width=4)))
|
| 82 |
+
fig.add_hline(y=0, line_dash="dash", line_color="gray", annotation_text="Breakeven")
|
| 83 |
+
fig.update_layout(
|
| 84 |
+
title="πΈ Chemical Plant Cash Flow Analysis",
|
| 85 |
+
xaxis_title="Year", yaxis_title="Cash Flow ($)",
|
| 86 |
+
height=400, showlegend=True,
|
| 87 |
+
font=dict(size=12)
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
return results_html, fig.to_html(full_html=False, div_id="cashflow-chart")
|
| 91 |
|
| 92 |
+
# Gradio Interface
|
| 93 |
+
with gr.Blocks(title="ChemEng TVM Calculator") as demo:
|
| 94 |
+
gr.Markdown("# π§ͺ Chemical Engineering TVM Calculator")
|
| 95 |
+
gr.Markdown("**CAPEX β’ OPEX β’ NPV β’ IRR β’ Plant Investment Analysis**")
|
| 96 |
|
| 97 |
+
with gr.Row():
|
| 98 |
+
with gr.Column(scale=1):
|
| 99 |
+
gr.Markdown("### π Chemical Plant Inputs")
|
| 100 |
+
|
| 101 |
+
gr.Markdown("**π° Investment & Revenue**")
|
| 102 |
+
with gr.Row():
|
| 103 |
+
capex_input = gr.Number(value=10000000, label="CAPEX ($)", precision=0)
|
| 104 |
+
revenue_input = gr.Number(value=5000000, label="Revenue/Year ($)", precision=0)
|
| 105 |
+
|
| 106 |
+
gr.Markdown("**π Operating Costs**")
|
| 107 |
+
with gr.Row():
|
| 108 |
+
opex_fixed_input = gr.Number(value=1500000, label="Fixed OPEX ($)", precision=0)
|
| 109 |
+
opex_var_input = gr.Number(value=2000000, label="Variable OPEX ($)", precision=0)
|
| 110 |
+
|
| 111 |
+
gr.Markdown("**π
Economic Parameters**")
|
| 112 |
+
with gr.Row():
|
| 113 |
+
plant_life_input = gr.Slider(5, 30, value=15, step=1, label="Plant Life (years)")
|
| 114 |
+
marr_input = gr.Slider(8, 20, value=12, step=1, label="MARR (%)")
|
| 115 |
+
|
| 116 |
+
with gr.Row():
|
| 117 |
+
salvage_input = gr.Slider(0, 20, value=10, step=1, label="Salvage Value (%)")
|
| 118 |
+
inflation_input = gr.Slider(1, 8, value=3, step=0.5, label="Inflation (%)")
|
| 119 |
+
|
| 120 |
+
calculate_btn = gr.Button("π¬ RUN ECONOMIC ANALYSIS", variant="primary", size="lg")
|
| 121 |
|
| 122 |
+
with gr.Column(scale=2):
|
| 123 |
+
results_output = gr.HTML()
|
| 124 |
+
chart_output = gr.HTML()
|
| 125 |
+
|
| 126 |
+
# Connect button to function
|
| 127 |
+
calculate_btn.click(
|
| 128 |
+
calculate_chemeng_tvm,
|
| 129 |
+
inputs=[capex_input, revenue_input, opex_fixed_input, opex_var_input,
|
| 130 |
+
plant_life_input, marr_input, salvage_input, inflation_input],
|
| 131 |
+
outputs=[results_output, chart_output]
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
gr.Markdown("---")
|
| 135 |
+
gr.Markdown("*Professional tool for Chemical Engineers | Powered by Gradio*")
|
| 136 |
+
|
| 137 |
+
# Launch (Hugging Face auto-runs this)
|
| 138 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|