Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import numpy as np
|
| 3 |
+
import matplotlib.pyplot as plt
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from sklearn.model_selection import train_test_split
|
| 6 |
+
from sklearn.ensemble import RandomForestRegressor
|
| 7 |
+
from sklearn.metrics import mean_squared_error, r2_score
|
| 8 |
+
import joblib
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
# Generate synthetic dataset for beam behavior prediction
|
| 12 |
+
def generate_beam_dataset(n_samples=1000):
|
| 13 |
+
np.random.seed(42)
|
| 14 |
+
|
| 15 |
+
# Generate random beam configurations
|
| 16 |
+
lengths = np.random.uniform(1, 20, n_samples)
|
| 17 |
+
|
| 18 |
+
# Generate 1-3 random point loads for each beam
|
| 19 |
+
point_loads_data = []
|
| 20 |
+
for i in range(n_samples):
|
| 21 |
+
n_loads = np.random.randint(1, 4)
|
| 22 |
+
loads = []
|
| 23 |
+
for _ in range(n_loads):
|
| 24 |
+
pos = np.random.uniform(0, lengths[i])
|
| 25 |
+
magnitude = np.random.uniform(-10000, 10000)
|
| 26 |
+
loads.append((pos, magnitude))
|
| 27 |
+
point_loads_data.append(loads)
|
| 28 |
+
|
| 29 |
+
# Generate random UDL parameters
|
| 30 |
+
udl_starts = np.random.uniform(0, lengths * 0.4, n_samples)
|
| 31 |
+
udl_lengths = np.random.uniform(1, lengths * 0.6, n_samples)
|
| 32 |
+
udl_ends = np.minimum(udl_starts + udl_lengths, lengths)
|
| 33 |
+
udl_values = np.random.uniform(-5000, 5000, n_samples)
|
| 34 |
+
|
| 35 |
+
# Calculate features for ML
|
| 36 |
+
features = []
|
| 37 |
+
max_sfd = []
|
| 38 |
+
max_bmd = []
|
| 39 |
+
|
| 40 |
+
for i in range(n_samples):
|
| 41 |
+
# Calculate beam characteristics
|
| 42 |
+
length = lengths[i]
|
| 43 |
+
point_loads = point_loads_data[i]
|
| 44 |
+
udl_start = udl_starts[i]
|
| 45 |
+
udl_end = udl_ends[i]
|
| 46 |
+
udl_value = udl_values[i]
|
| 47 |
+
|
| 48 |
+
# Calculate SFD and BMD
|
| 49 |
+
x = np.linspace(0, length, 100)
|
| 50 |
+
shear_force = np.zeros_like(x)
|
| 51 |
+
bending_moment = np.zeros_like(x)
|
| 52 |
+
|
| 53 |
+
# Apply point loads
|
| 54 |
+
for pos, load in point_loads:
|
| 55 |
+
shear_force[x >= pos] += load
|
| 56 |
+
|
| 57 |
+
# Apply UDL
|
| 58 |
+
for j, xi in enumerate(x):
|
| 59 |
+
if udl_start <= xi <= udl_end:
|
| 60 |
+
for k in range(j, len(x)):
|
| 61 |
+
shear_force[k] += udl_value * (min(x[k], udl_end) - max(xi, udl_start))
|
| 62 |
+
|
| 63 |
+
# Compute Bending Moment by integrating shear force
|
| 64 |
+
for j in range(1, len(x)):
|
| 65 |
+
bending_moment[j] = bending_moment[j-1] + shear_force[j-1] * (x[j] - x[j-1])
|
| 66 |
+
|
| 67 |
+
# Extract features
|
| 68 |
+
num_loads = len(point_loads)
|
| 69 |
+
total_point_load = sum(load for _, load in point_loads)
|
| 70 |
+
avg_load_position = sum(pos*load for pos, load in point_loads) / max(1, abs(total_point_load)) if total_point_load != 0 else 0
|
| 71 |
+
udl_coverage = (udl_end - udl_start) / length if length > 0 else 0
|
| 72 |
+
total_udl_force = udl_value * (udl_end - udl_start)
|
| 73 |
+
|
| 74 |
+
features.append([
|
| 75 |
+
length,
|
| 76 |
+
num_loads,
|
| 77 |
+
total_point_load,
|
| 78 |
+
avg_load_position,
|
| 79 |
+
udl_start,
|
| 80 |
+
udl_end,
|
| 81 |
+
udl_value,
|
| 82 |
+
udl_coverage,
|
| 83 |
+
total_udl_force
|
| 84 |
+
])
|
| 85 |
+
|
| 86 |
+
max_sfd.append(np.max(np.abs(shear_force)))
|
| 87 |
+
max_bmd.append(np.max(np.abs(bending_moment)))
|
| 88 |
+
|
| 89 |
+
# Create dataframe
|
| 90 |
+
feature_cols = [
|
| 91 |
+
'beam_length',
|
| 92 |
+
'num_point_loads',
|
| 93 |
+
'total_point_load',
|
| 94 |
+
'avg_load_position',
|
| 95 |
+
'udl_start',
|
| 96 |
+
'udl_end',
|
| 97 |
+
'udl_value',
|
| 98 |
+
'udl_coverage',
|
| 99 |
+
'total_udl_force'
|
| 100 |
+
]
|
| 101 |
+
|
| 102 |
+
X_df = pd.DataFrame(features, columns=feature_cols)
|
| 103 |
+
y_df = pd.DataFrame({
|
| 104 |
+
'max_shear_force': max_sfd,
|
| 105 |
+
'max_bending_moment': max_bmd
|
| 106 |
+
})
|
| 107 |
+
|
| 108 |
+
# Combine into a single dataset
|
| 109 |
+
dataset = pd.concat([X_df, y_df], axis=1)
|
| 110 |
+
|
| 111 |
+
return dataset, X_df.columns, y_df.columns
|
| 112 |
+
|
| 113 |
+
# ML Model Training Function
|
| 114 |
+
def train_beam_ml_models(dataset):
|
| 115 |
+
# Split features and targets
|
| 116 |
+
X = dataset.iloc[:, :9] # First 9 columns are features
|
| 117 |
+
y_sfd = dataset['max_shear_force']
|
| 118 |
+
y_bmd = dataset['max_bending_moment']
|
| 119 |
+
|
| 120 |
+
# Split into training and testing sets
|
| 121 |
+
X_train, X_test, y_sfd_train, y_sfd_test = train_test_split(X, y_sfd, test_size=0.2, random_state=42)
|
| 122 |
+
_, _, y_bmd_train, y_bmd_test = train_test_split(X, y_bmd, test_size=0.2, random_state=42)
|
| 123 |
+
|
| 124 |
+
# Train SFD model
|
| 125 |
+
sfd_model = RandomForestRegressor(n_estimators=100, random_state=42)
|
| 126 |
+
sfd_model.fit(X_train, y_sfd_train)
|
| 127 |
+
|
| 128 |
+
# Train BMD model
|
| 129 |
+
bmd_model = RandomForestRegressor(n_estimators=100, random_state=42)
|
| 130 |
+
bmd_model.fit(X_train, y_bmd_train)
|
| 131 |
+
|
| 132 |
+
# Evaluate models
|
| 133 |
+
sfd_preds = sfd_model.predict(X_test)
|
| 134 |
+
bmd_preds = bmd_model.predict(X_test)
|
| 135 |
+
|
| 136 |
+
sfd_rmse = np.sqrt(mean_squared_error(y_sfd_test, sfd_preds))
|
| 137 |
+
bmd_rmse = np.sqrt(mean_squared_error(y_bmd_test, bmd_preds))
|
| 138 |
+
|
| 139 |
+
sfd_r2 = r2_score(y_sfd_test, sfd_preds)
|
| 140 |
+
bmd_r2 = r2_score(y_bmd_test, bmd_preds)
|
| 141 |
+
|
| 142 |
+
# Save models
|
| 143 |
+
joblib.dump(sfd_model, 'sfd_model.pkl')
|
| 144 |
+
joblib.dump(bmd_model, 'bmd_model.pkl')
|
| 145 |
+
|
| 146 |
+
return {
|
| 147 |
+
'sfd_model': sfd_model,
|
| 148 |
+
'bmd_model': bmd_model,
|
| 149 |
+
'metrics': {
|
| 150 |
+
'sfd_rmse': sfd_rmse,
|
| 151 |
+
'bmd_rmse': bmd_rmse,
|
| 152 |
+
'sfd_r2': sfd_r2,
|
| 153 |
+
'bmd_r2': bmd_r2
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
# Improved calculation function using exact integrals
|
| 158 |
+
def calculate_sfd_bmd(length, point_loads, udl_start, udl_end, udl_value):
|
| 159 |
+
x = np.linspace(0, length, 200)
|
| 160 |
+
shear_force = np.zeros_like(x)
|
| 161 |
+
bending_moment = np.zeros_like(x)
|
| 162 |
+
|
| 163 |
+
# Apply point loads (corrected)
|
| 164 |
+
for pos, load in point_loads:
|
| 165 |
+
shear_force[x >= pos] += load
|
| 166 |
+
|
| 167 |
+
# Calculate bending moment directly for point loads
|
| 168 |
+
for i, xi in enumerate(x):
|
| 169 |
+
if xi >= pos:
|
| 170 |
+
bending_moment[i] += load * (xi - pos)
|
| 171 |
+
|
| 172 |
+
# Apply UDL (corrected calculation)
|
| 173 |
+
udl_length = udl_end - udl_start
|
| 174 |
+
if udl_length > 0 and udl_value != 0:
|
| 175 |
+
for i, xi in enumerate(x):
|
| 176 |
+
# Shear force at point xi due to UDL
|
| 177 |
+
if xi <= udl_start:
|
| 178 |
+
# Before UDL: no contribution
|
| 179 |
+
pass
|
| 180 |
+
elif xi >= udl_end:
|
| 181 |
+
# After UDL: full contribution
|
| 182 |
+
shear_force[i] += udl_value * udl_length
|
| 183 |
+
else:
|
| 184 |
+
# Within UDL region: partial contribution
|
| 185 |
+
shear_force[i] += udl_value * (xi - udl_start)
|
| 186 |
+
|
| 187 |
+
# Bending moment calculations for UDL
|
| 188 |
+
if xi <= udl_start:
|
| 189 |
+
# No contribution before UDL
|
| 190 |
+
pass
|
| 191 |
+
elif xi >= udl_end:
|
| 192 |
+
# Full contribution after UDL
|
| 193 |
+
centroid_distance = xi - (udl_start + udl_end)/2
|
| 194 |
+
bending_moment[i] += udl_value * udl_length * centroid_distance
|
| 195 |
+
else:
|
| 196 |
+
# Partial contribution within UDL
|
| 197 |
+
partial_length = xi - udl_start
|
| 198 |
+
centroid_distance = partial_length/2
|
| 199 |
+
bending_moment[i] += udl_value * partial_length * centroid_distance
|
| 200 |
+
|
| 201 |
+
return x, shear_force, bending_moment
|
| 202 |
+
|
| 203 |
+
# AI Agent for beam analysis and optimization
|
| 204 |
+
class BeamAnalysisAgent:
|
| 205 |
+
def __init__(self):
|
| 206 |
+
# Initialize models
|
| 207 |
+
if os.path.exists('sfd_model.pkl') and os.path.exists('bmd_model.pkl'):
|
| 208 |
+
self.sfd_model = joblib.load('sfd_model.pkl')
|
| 209 |
+
self.bmd_model = joblib.load('bmd_model.pkl')
|
| 210 |
+
self.models_loaded = True
|
| 211 |
+
else:
|
| 212 |
+
print("Training new ML models...")
|
| 213 |
+
dataset, _, _ = generate_beam_dataset(1000)
|
| 214 |
+
model_data = train_beam_ml_models(dataset)
|
| 215 |
+
self.sfd_model = model_data['sfd_model']
|
| 216 |
+
self.bmd_model = model_data['bmd_model']
|
| 217 |
+
self.models_loaded = True
|
| 218 |
+
print(f"ML models trained. Metrics: {model_data['metrics']}")
|
| 219 |
+
|
| 220 |
+
def analyze_beam(self, length, point_loads, udl_start, udl_end, udl_value):
|
| 221 |
+
# Calculate exact SFD and BMD
|
| 222 |
+
x, shear_force, bending_moment = calculate_sfd_bmd(length, point_loads, udl_start, udl_end, udl_value)
|
| 223 |
+
|
| 224 |
+
# Extract feature vector for ML prediction
|
| 225 |
+
num_loads = len(point_loads)
|
| 226 |
+
total_point_load = sum(load for _, load in point_loads) if point_loads else 0
|
| 227 |
+
avg_load_position = sum(pos*load for pos, load in point_loads) / max(1, abs(total_point_load)) if point_loads and total_point_load != 0 else 0
|
| 228 |
+
udl_coverage = (udl_end - udl_start) / length if length > 0 else 0
|
| 229 |
+
total_udl_force = udl_value * (udl_end - udl_start)
|
| 230 |
+
|
| 231 |
+
feature_vector = np.array([[
|
| 232 |
+
length,
|
| 233 |
+
num_loads,
|
| 234 |
+
total_point_load,
|
| 235 |
+
avg_load_position,
|
| 236 |
+
udl_start,
|
| 237 |
+
udl_end,
|
| 238 |
+
udl_value,
|
| 239 |
+
udl_coverage,
|
| 240 |
+
total_udl_force
|
| 241 |
+
]])
|
| 242 |
+
|
| 243 |
+
# Make predictions
|
| 244 |
+
if self.models_loaded:
|
| 245 |
+
predicted_max_sfd = self.sfd_model.predict(feature_vector)[0]
|
| 246 |
+
predicted_max_bmd = self.bmd_model.predict(feature_vector)[0]
|
| 247 |
+
else:
|
| 248 |
+
predicted_max_sfd = np.max(np.abs(shear_force))
|
| 249 |
+
predicted_max_bmd = np.max(np.abs(bending_moment))
|
| 250 |
+
|
| 251 |
+
# Calculate actual maximum values
|
| 252 |
+
actual_max_sfd = np.max(np.abs(shear_force))
|
| 253 |
+
actual_max_bmd = np.max(np.abs(bending_moment))
|
| 254 |
+
|
| 255 |
+
# Identify critical points
|
| 256 |
+
sfd_critical_idx = np.argmin(np.abs(shear_force))
|
| 257 |
+
sfd_critical_point = x[sfd_critical_idx]
|
| 258 |
+
bmd_max_idx = np.argmax(np.abs(bending_moment))
|
| 259 |
+
bmd_max_point = x[bmd_max_idx]
|
| 260 |
+
|
| 261 |
+
# Generate visualizations
|
| 262 |
+
fig, axs = plt.subplots(2, 1, figsize=(10, 12))
|
| 263 |
+
|
| 264 |
+
# Plot beam with loads
|
| 265 |
+
beam_ax = plt.subplot2grid((3, 1), (0, 0), fig=fig)
|
| 266 |
+
beam_ax.plot([0, length], [0, 0], 'k-', linewidth=3) # Beam
|
| 267 |
+
beam_ax.set_ylim(-1, 1)
|
| 268 |
+
|
| 269 |
+
# Draw point loads
|
| 270 |
+
for pos, load in point_loads:
|
| 271 |
+
if load > 0:
|
| 272 |
+
beam_ax.arrow(pos, 0.5, 0, -0.3, head_width=0.2, head_length=0.1, fc='r', ec='r')
|
| 273 |
+
beam_ax.text(pos, 0.6, f"{load}N", ha='center')
|
| 274 |
+
else:
|
| 275 |
+
beam_ax.arrow(pos, -0.5, 0, 0.3, head_width=0.2, head_length=0.1, fc='b', ec='b')
|
| 276 |
+
beam_ax.text(pos, -0.6, f"{abs(load)}N", ha='center')
|
| 277 |
+
|
| 278 |
+
# Draw UDL
|
| 279 |
+
if udl_end > udl_start and udl_value != 0:
|
| 280 |
+
if udl_value > 0:
|
| 281 |
+
for u in np.linspace(udl_start, udl_end, 20):
|
| 282 |
+
beam_ax.arrow(u, 0.3, 0, -0.2, head_width=0.05, head_length=0.05, fc='r', ec='r')
|
| 283 |
+
beam_ax.plot([udl_start, udl_end], [0.3, 0.3], 'r-', linewidth=2)
|
| 284 |
+
beam_ax.text((udl_start + udl_end)/2, 0.4, f"{udl_value}N/m", ha='center')
|
| 285 |
+
else:
|
| 286 |
+
for u in np.linspace(udl_start, udl_end, 20):
|
| 287 |
+
beam_ax.arrow(u, -0.3, 0, 0.2, head_width=0.05, head_length=0.05, fc='b', ec='b')
|
| 288 |
+
beam_ax.plot([udl_start, udl_end], [-0.3, -0.3], 'b-', linewidth=2)
|
| 289 |
+
beam_ax.text((udl_start + udl_end)/2, -0.4, f"{abs(udl_value)}N/m", ha='center')
|
| 290 |
+
|
| 291 |
+
beam_ax.set_title("Beam Configuration")
|
| 292 |
+
beam_ax.set_xlabel("Position (m)")
|
| 293 |
+
beam_ax.set_yticks([])
|
| 294 |
+
beam_ax.grid(True)
|
| 295 |
+
|
| 296 |
+
# Plot SFD
|
| 297 |
+
axs[0].plot(x, shear_force, 'b-', linewidth=2)
|
| 298 |
+
axs[0].fill_between(x, 0, shear_force, alpha=0.3, color='blue')
|
| 299 |
+
axs[0].axhline(0, color='k', linestyle='-', alpha=0.5)
|
| 300 |
+
axs[0].set_title("Shear Force Diagram (SFD)")
|
| 301 |
+
axs[0].set_xlabel("Position (m)")
|
| 302 |
+
axs[0].set_ylabel("Shear Force (N)")
|
| 303 |
+
axs[0].grid(True)
|
| 304 |
+
|
| 305 |
+
# Plot BMD
|
| 306 |
+
axs[1].plot(x, bending_moment, 'r-', linewidth=2)
|
| 307 |
+
axs[1].fill_between(x, 0, bending_moment, alpha=0.3, color='red')
|
| 308 |
+
axs[1].axhline(0, color='k', linestyle='-', alpha=0.5)
|
| 309 |
+
axs[1].set_title("Bending Moment Diagram (BMD)")
|
| 310 |
+
axs[1].set_xlabel("Position (m)")
|
| 311 |
+
axs[1].set_ylabel("Bending Moment (N⋅m)")
|
| 312 |
+
axs[1].grid(True)
|
| 313 |
+
|
| 314 |
+
plt.tight_layout()
|
| 315 |
+
|
| 316 |
+
# Prepare analysis report
|
| 317 |
+
report = {
|
| 318 |
+
"beam_configuration": {
|
| 319 |
+
"length": length,
|
| 320 |
+
"point_loads": point_loads,
|
| 321 |
+
"udl": {
|
| 322 |
+
"start": udl_start,
|
| 323 |
+
"end": udl_end,
|
| 324 |
+
"value": udl_value
|
| 325 |
+
}
|
| 326 |
+
},
|
| 327 |
+
"analysis_results": {
|
| 328 |
+
"max_shear_force": {
|
| 329 |
+
"actual": float(actual_max_sfd),
|
| 330 |
+
"predicted": float(predicted_max_sfd),
|
| 331 |
+
"prediction_error": float(abs(predicted_max_sfd - actual_max_sfd) / actual_max_sfd * 100) if actual_max_sfd != 0 else 0
|
| 332 |
+
},
|
| 333 |
+
"max_bending_moment": {
|
| 334 |
+
"actual": float(actual_max_bmd),
|
| 335 |
+
"predicted": float(predicted_max_bmd),
|
| 336 |
+
"prediction_error": float(abs(predicted_max_bmd - actual_max_bmd) / actual_max_bmd * 100) if actual_max_bmd != 0 else 0
|
| 337 |
+
},
|
| 338 |
+
"critical_points": {
|
| 339 |
+
"zero_shear_force": float(sfd_critical_point),
|
| 340 |
+
"max_bending_moment": float(bmd_max_point)
|
| 341 |
+
}
|
| 342 |
+
}
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
return fig, report
|
| 346 |
+
|
| 347 |
+
def suggest_optimizations(self, length, point_loads, udl_start, udl_end, udl_value, target_criterion="minimize_bending"):
|
| 348 |
+
original_x, original_sf, original_bm = calculate_sfd_bmd(length, point_loads, udl_start, udl_end, udl_value)
|
| 349 |
+
original_max_bm = np.max(np.abs(original_bm))
|
| 350 |
+
original_max_sf = np.max(np.abs(original_sf))
|
| 351 |
+
|
| 352 |
+
suggestions = []
|
| 353 |
+
|
| 354 |
+
# Try repositioning point loads
|
| 355 |
+
if len(point_loads) > 0:
|
| 356 |
+
for i, (pos, load) in enumerate(point_loads):
|
| 357 |
+
# Try moving each load to 5 different positions
|
| 358 |
+
test_positions = np.linspace(0.1 * length, 0.9 * length, 5)
|
| 359 |
+
for new_pos in test_positions:
|
| 360 |
+
if abs(new_pos - pos) < 0.05 * length: # Skip if too close to original
|
| 361 |
+
continue
|
| 362 |
+
|
| 363 |
+
# Create new point loads list with moved load
|
| 364 |
+
new_point_loads = point_loads.copy()
|
| 365 |
+
new_point_loads[i] = (new_pos, load)
|
| 366 |
+
|
| 367 |
+
# Calculate new diagrams
|
| 368 |
+
_, new_sf, new_bm = calculate_sfd_bmd(length, new_point_loads, udl_start, udl_end, udl_value)
|
| 369 |
+
new_max_bm = np.max(np.abs(new_bm))
|
| 370 |
+
new_max_sf = np.max(np.abs(new_sf))
|
| 371 |
+
|
| 372 |
+
# Check if this is an improvement
|
| 373 |
+
if target_criterion == "minimize_bending" and new_max_bm < original_max_bm * 0.9:
|
| 374 |
+
bm_reduction = (original_max_bm - new_max_bm) / original_max_bm * 100
|
| 375 |
+
suggestions.append({
|
| 376 |
+
"type": "reposition_load",
|
| 377 |
+
"original": {"position": pos, "load": load},
|
| 378 |
+
"suggestion": {"position": float(new_pos), "load": load},
|
| 379 |
+
"improvement": f"{bm_reduction:.1f}% reduction in maximum bending moment"
|
| 380 |
+
})
|
| 381 |
+
elif target_criterion == "minimize_shear" and new_max_sf < original_max_sf * 0.9:
|
| 382 |
+
sf_reduction = (original_max_sf - new_max_sf) / original_max_sf * 100
|
| 383 |
+
suggestions.append({
|
| 384 |
+
"type": "reposition_load",
|
| 385 |
+
"original": {"position": pos, "load": load},
|
| 386 |
+
"suggestion": {"position": float(new_pos), "load": load},
|
| 387 |
+
"improvement": f"{sf_reduction:.1f}% reduction in maximum shear force"
|
| 388 |
+
})
|
| 389 |
+
|
| 390 |
+
# Try adjusting UDL
|
| 391 |
+
if udl_end > udl_start and udl_value != 0:
|
| 392 |
+
# Try different UDL positions
|
| 393 |
+
test_starts = np.linspace(0, 0.5 * length, 3)
|
| 394 |
+
test_lengths = np.linspace(0.2 * length, 0.5 * length, 3)
|
| 395 |
+
|
| 396 |
+
for new_start in test_starts:
|
| 397 |
+
for new_length in test_lengths:
|
| 398 |
+
new_end = new_start + new_length
|
| 399 |
+
if new_end > length:
|
| 400 |
+
continue
|
| 401 |
+
|
| 402 |
+
# Too similar to original
|
| 403 |
+
if abs(new_start - udl_start) < 0.1 * length and abs(new_end - udl_end) < 0.1 * length:
|
| 404 |
+
continue
|
| 405 |
+
|
| 406 |
+
# Calculate new diagrams
|
| 407 |
+
_, new_sf, new_bm = calculate_sfd_bmd(length, point_loads, new_start, new_end, udl_value)
|
| 408 |
+
new_max_bm = np.max(np.abs(new_bm))
|
| 409 |
+
new_max_sf = np.max(np.abs(new_sf))
|
| 410 |
+
|
| 411 |
+
# Check if this is an improvement
|
| 412 |
+
if target_criterion == "minimize_bending" and new_max_bm < original_max_bm * 0.9:
|
| 413 |
+
bm_reduction = (original_max_bm - new_max_bm) / original_max_bm * 100
|
| 414 |
+
suggestions.append({
|
| 415 |
+
"type": "adjust_udl",
|
| 416 |
+
"original": {"start": udl_start, "end": udl_end},
|
| 417 |
+
"suggestion": {"start": float(new_start), "end": float(new_end)},
|
| 418 |
+
"improvement": f"{bm_reduction:.1f}% reduction in maximum bending moment"
|
| 419 |
+
})
|
| 420 |
+
elif target_criterion == "minimize_shear" and new_max_sf < original_max_sf * 0.9:
|
| 421 |
+
sf_reduction = (original_max_sf - new_max_sf) / original_max_sf * 100
|
| 422 |
+
suggestions.append({
|
| 423 |
+
"type": "adjust_udl",
|
| 424 |
+
"original": {"start": udl_start, "end": udl_end},
|
| 425 |
+
"suggestion": {"start": float(new_start), "end": float(new_end)},
|
| 426 |
+
"improvement": f"{sf_reduction:.1f}% reduction in maximum shear force"
|
| 427 |
+
})
|
| 428 |
+
|
| 429 |
+
# Sort suggestions by improvement
|
| 430 |
+
if suggestions:
|
| 431 |
+
suggestions.sort(key=lambda x: float(x["improvement"].split('%')[0]), reverse=True)
|
| 432 |
+
return suggestions[:3] # Return top 3 suggestions
|
| 433 |
+
else:
|
| 434 |
+
return [{"type": "no_suggestions", "message": "No significant improvements found with the attempted modifications."}]
|
| 435 |
+
|
| 436 |
+
# Gradio Interface Functions
|
| 437 |
+
def format_report(report):
|
| 438 |
+
text = f"## Beam Analysis Report\n\n"
|
| 439 |
+
text += f"### Configuration\n"
|
| 440 |
+
text += f"- Beam Length: {report['beam_configuration']['length']}m\n"
|
| 441 |
+
text += f"- Point Loads: {report['beam_configuration']['point_loads']}\n"
|
| 442 |
+
text += f"- UDL: {report['beam_configuration']['udl']['value']} N/m from {report['beam_configuration']['udl']['start']}m to {report['beam_configuration']['udl']['end']}m\n\n"
|
| 443 |
+
|
| 444 |
+
text += f"### Results\n"
|
| 445 |
+
text += f"- Maximum Shear Force: {report['analysis_results']['max_shear_force']['actual']:.2f} N\n"
|
| 446 |
+
text += f"- Maximum Bending Moment: {report['analysis_results']['max_bending_moment']['actual']:.2f} N⋅m\n"
|
| 447 |
+
text += f"- Critical Points:\n"
|
| 448 |
+
text += f" - Zero Shear Force (likely max bending): {report['analysis_results']['critical_points']['zero_shear_force']:.2f}m\n"
|
| 449 |
+
text += f" - Max Bending Moment: {report['analysis_results']['critical_points']['max_bending_moment']:.2f}m\n\n"
|
| 450 |
+
|
| 451 |
+
text += f"### ML Predictions\n"
|
| 452 |
+
text += f"- Predicted Max Shear Force: {report['analysis_results']['max_shear_force']['predicted']:.2f} N (Error: {report['analysis_results']['max_shear_force']['prediction_error']:.2f}%)\n"
|
| 453 |
+
text += f"- Predicted Max Bending Moment: {report['analysis_results']['max_bending_moment']['predicted']:.2f} N⋅m (Error: {report['analysis_results']['max_bending_moment']['prediction_error']:.2f}%)\n"
|
| 454 |
+
|
| 455 |
+
return text
|
| 456 |
+
|
| 457 |
+
def format_optimization_suggestions(suggestions):
|
| 458 |
+
text = f"## Optimization Suggestions\n\n"
|
| 459 |
+
|
| 460 |
+
if suggestions[0].get("type") == "no_suggestions":
|
| 461 |
+
text += "No significant improvements found with the attempted modifications.\n"
|
| 462 |
+
return text
|
| 463 |
+
|
| 464 |
+
for i, suggestion in enumerate(suggestions, 1):
|
| 465 |
+
text += f"### Suggestion {i}\n"
|
| 466 |
+
|
| 467 |
+
if suggestion["type"] == "reposition_load":
|
| 468 |
+
text += f"- Type: Reposition Point Load\n"
|
| 469 |
+
text += f"- Original Position: {suggestion['original']['position']:.2f}m with {suggestion['original']['load']}N\n"
|
| 470 |
+
text += f"- Suggested Position: {suggestion['suggestion']['position']:.2f}m\n"
|
| 471 |
+
text += f"- Improvement: {suggestion['improvement']}\n\n"
|
| 472 |
+
|
| 473 |
+
elif suggestion["type"] == "adjust_udl":
|
| 474 |
+
text += f"- Type: Adjust UDL Position\n"
|
| 475 |
+
text += f"- Original: {suggestion['original']['start']:.2f}m to {suggestion['original']['end']:.2f}m\n"
|
| 476 |
+
text += f"- Suggested: {suggestion['suggestion']['start']:.2f}m to {suggestion['suggestion']['end']:.2f}m\n"
|
| 477 |
+
text += f"- Improvement: {suggestion['improvement']}\n\n"
|
| 478 |
+
|
| 479 |
+
return text
|
| 480 |
+
|
| 481 |
+
def parse_point_loads(point_loads_str):
|
| 482 |
+
if not point_loads_str or point_loads_str.strip() == "":
|
| 483 |
+
return []
|
| 484 |
+
|
| 485 |
+
try:
|
| 486 |
+
# Handle different input formats
|
| 487 |
+
point_loads_str = point_loads_str.replace(';', ',')
|
| 488 |
+
if '[' not in point_loads_str:
|
| 489 |
+
# Try to parse as simple comma-separated numbers in pairs
|
| 490 |
+
values = [float(x.strip()) for x in point_loads_str.split(',') if x.strip()]
|
| 491 |
+
if len(values) % 2 != 0:
|
| 492 |
+
raise ValueError("Point loads must be specified as position,load pairs")
|
| 493 |
+
|
| 494 |
+
return [(values[i], values[i+1]) for i in range(0, len(values), 2)]
|
| 495 |
+
else:
|
| 496 |
+
# Parse as literal Python expression
|
| 497 |
+
return eval(point_loads_str)
|
| 498 |
+
except Exception as e:
|
| 499 |
+
return [(0, 0)] # Return a default value on error
|
| 500 |
+
|
| 501 |
+
# Initialize the agent
|
| 502 |
+
beam_agent = BeamAnalysisAgent()
|
| 503 |
+
|
| 504 |
+
def analyze_beam_ui(length, point_loads_str, udl_start, udl_end, udl_value):
|
| 505 |
+
point_loads = parse_point_loads(point_loads_str)
|
| 506 |
+
fig, report = beam_agent.analyze_beam(length, point_loads, udl_start, udl_end, udl_value)
|
| 507 |
+
formatted_report = format_report(report)
|
| 508 |
+
return fig, formatted_report
|
| 509 |
+
|
| 510 |
+
def optimize_beam_ui(length, point_loads_str, udl_start, udl_end, udl_value, target_criterion):
|
| 511 |
+
point_loads = parse_point_loads(point_loads_str)
|
| 512 |
+
suggestions = beam_agent.suggest_optimizations(length, point_loads, udl_start, udl_end, udl_value, target_criterion)
|
| 513 |
+
formatted_suggestions = format_optimization_suggestions(suggestions)
|
| 514 |
+
return formatted_suggestions
|
| 515 |
+
|
| 516 |
+
def train_model_ui():
|
| 517 |
+
dataset, feature_names, target_names = generate_beam_dataset(2000)
|
| 518 |
+
model_data = train_beam_ml_models(dataset)
|
| 519 |
+
|
| 520 |
+
# Update the agent's models
|
| 521 |
+
global beam_agent
|
| 522 |
+
beam_agent.sfd_model = model_data['sfd_model']
|
| 523 |
+
beam_agent.bmd_model = model_data['bmd_model']
|
| 524 |
+
|
| 525 |
+
# Format training results
|
| 526 |
+
metrics = model_data['metrics']
|
| 527 |
+
report = f"## ML Model Training Results\n\n"
|
| 528 |
+
report += f"### Dataset\n"
|
| 529 |
+
report += f"- Generated {len(dataset)} sample beam configurations\n"
|
| 530 |
+
report += f"- Features: {', '.join(feature_names)}\n"
|
| 531 |
+
report += f"- Targets: {', '.join(target_names)}\n\n"
|
| 532 |
+
|
| 533 |
+
report += f"### Model Performance\n"
|
| 534 |
+
report += f"- Shear Force Prediction:\n"
|
| 535 |
+
report += f" - RMSE: {metrics['sfd_rmse']:.2f}\n"
|
| 536 |
+
report += f" - R²: {metrics['sfd_r2']:.4f}\n"
|
| 537 |
+
report += f"- Bending Moment Prediction:\n"
|
| 538 |
+
report += f" - RMSE: {metrics['bmd_rmse']:.2f}\n"
|
| 539 |
+
report += f" - R²: {metrics['bmd_r2']:.4f}\n"
|
| 540 |
+
|
| 541 |
+
return report
|
| 542 |
+
|
| 543 |
+
# Create the Gradio interface
|
| 544 |
+
with gr.Blocks(title="Beam Analysis with ML Agent") as iface:
|
| 545 |
+
gr.Markdown("# Structural Beam Analysis with ML Agent")
|
| 546 |
+
gr.Markdown("This application uses machine learning to analyze and optimize structural beams.")
|
| 547 |
+
|
| 548 |
+
with gr.Tab("Beam Analysis"):
|
| 549 |
+
with gr.Row():
|
| 550 |
+
with gr.Column(scale=1):
|
| 551 |
+
length_input = gr.Number(value=10.0, label="Beam Length (m)")
|
| 552 |
+
point_loads_input = gr.Textbox(value="[(2, -1000), (8, -2000)]", label="Point Loads [(position, magnitude)]")
|
| 553 |
+
udl_start_input = gr.Number(value=4.0, label="UDL Start Position (m)")
|
| 554 |
+
udl_end_input = gr.Number(value=7.0, label="UDL End Position (m)")
|
| 555 |
+
udl_value_input = gr.Number(value=-500, label="UDL Value (N/m)")
|
| 556 |
+
analyze_btn = gr.Button("Analyze Beam")
|
| 557 |
+
|
| 558 |
+
with gr.Column(scale=2):
|
| 559 |
+
output_plot = gr.Plot(label="Beam Analysis Plot")
|
| 560 |
+
output_report = gr.Markdown(label="Analysis Report")
|
| 561 |
+
|
| 562 |
+
analyze_btn.click(
|
| 563 |
+
analyze_beam_ui,
|
| 564 |
+
inputs=[length_input, point_loads_input, udl_start_input, udl_end_input, udl_value_input],
|
| 565 |
+
outputs=[output_plot, output_report]
|
| 566 |
+
)
|
| 567 |
+
|
| 568 |
+
with gr.Tab("Beam Optimization"):
|
| 569 |
+
with gr.Row():
|
| 570 |
+
with gr.Column(scale=1):
|
| 571 |
+
opt_length_input = gr.Number(value=10.0, label="Beam Length (m)")
|
| 572 |
+
opt_point_loads_input = gr.Textbox(value="[(2, -1000), (8, -2000)]", label="Point Loads [(position, magnitude)]")
|
| 573 |
+
opt_udl_start_input = gr.Number(value=4.0, label="UDL Start Position (m)")
|
| 574 |
+
opt_udl_end_input = gr.Number(value=7.0, label="UDL End Position (m)")
|
| 575 |
+
opt_udl_value_input = gr.Number(value=-500, label="UDL Value (N/m)")
|
| 576 |
+
opt_criterion = gr.Radio(
|
| 577 |
+
["minimize_bending", "minimize_shear"],
|
| 578 |
+
label="Optimization Target",
|
| 579 |
+
value="minimize_bending"
|
| 580 |
+
)
|
| 581 |
+
optimize_btn = gr.Button("Suggest Optimizations")
|
| 582 |
+
|
| 583 |
+
with gr.Column(scale=2):
|
| 584 |
+
optimization_suggestions = gr.Markdown(label="Optimization Suggestions")
|
| 585 |
+
|
| 586 |
+
optimize_btn.click(
|
| 587 |
+
optimize_beam_ui,
|
| 588 |
+
inputs=[
|
| 589 |
+
opt_length_input,
|
| 590 |
+
opt_point_loads_input,
|
| 591 |
+
opt_udl_start_input,
|
| 592 |
+
opt_udl_end_input,
|
| 593 |
+
opt_udl_value_input,
|
| 594 |
+
opt_criterion
|
| 595 |
+
],
|
| 596 |
+
outputs=[optimization_suggestions]
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
with gr.Tab("ML Model Training"):
|
| 600 |
+
train_btn = gr.Button("Train New ML Models")
|
| 601 |
+
training_results = gr.Markdown(label="Training Results")
|
| 602 |
+
|
| 603 |
+
train_btn.click(
|
| 604 |
+
train_model_ui,
|
| 605 |
+
inputs=[],
|
| 606 |
+
outputs=[training_results]
|
| 607 |
+
)
|
| 608 |
+
|
| 609 |
+
# Launch the app
|
| 610 |
+
if __name__ == "__main__":
|
| 611 |
+
iface.launch()
|