Rui Wan commited on
Commit
14fdcca
·
1 Parent(s): a19943b

add neural network model

Browse files
Data/DataForThermoforming.xlsx ADDED
Binary file (82.1 kB). View file
 
Dataset.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ np.random.seed(42)
5
+ epsilon = 1e-8
6
+
7
+ class Dataset:
8
+ def __init__(self, inverse=False):
9
+ filename = './Data/DataForThermoforming.xlsx'
10
+ self.df = pd.read_excel(filename, sheet_name='CFPEEK')
11
+ # remove rows by index
12
+ # self.df = self.df.drop([20, 48], axis=0)
13
+ self.df = self.df.drop([7, 78, 101, 129], axis=0)
14
+
15
+ # normalize data
16
+ if inverse:
17
+ self.input_columns = ['Ply_Number', 'Fiber_Volume_Fractions', 'A1(abs)', 'B1(abs)', 'C1(abs)', 'Stress(Max) MPa']
18
+ self.output_columns = ['Initial_Temp (degree celsius)', 'Punch_Velocity (mm/s)', 'Cooling_Time (s)']
19
+ else:
20
+ self.input_columns = ['Ply_Number', 'Fiber_Volume_Fractions', 'Initial_Temp (degree celsius)', 'Punch_Velocity (mm/s)', 'Cooling_Time (s)']
21
+ self.output_columns = ['A1(abs)', 'B1(abs)', 'C1(abs)', 'Stress(Max) MPa']
22
+
23
+ self.input_mean = self.df[self.input_columns].mean().to_numpy(dtype=np.float32)
24
+ self.input_std = self.df[self.input_columns].std().to_numpy(dtype=np.float32) + epsilon
25
+ self.output_mean = self.df[self.output_columns].mean().to_numpy(dtype=np.float32)
26
+ self.output_std = self.df[self.output_columns].std().to_numpy(dtype=np.float32) + epsilon
27
+
28
+
29
+ def get_input(self, normalize=False):
30
+ data = self.df[self.input_columns].to_numpy(dtype=np.float32)
31
+ if normalize:
32
+ data = self.normalize_input(data)
33
+ return data
34
+
35
+
36
+ def get_output(self, normalize=False):
37
+ data = self.df[self.output_columns].to_numpy(dtype=np.float32)
38
+ if normalize:
39
+ data = self.normalize_output(data)
40
+ return data
41
+
42
+ def __str__(self):
43
+ return str(self.df.head())
44
+
45
+ def normalize_input(self, input_data):
46
+ return (input_data - self.input_mean) / self.input_std
47
+
48
+ def normalize_output(self, output_data):
49
+ return (output_data - self.output_mean) / self.output_std
50
+
51
+ def denormalize_input(self, normalized_input):
52
+ return normalized_input * self.input_std + self.input_mean
53
+
54
+ def denormalize_output(self, normalized_output):
55
+ return normalized_output * self.output_std + self.output_mean
56
+
57
+ if __name__ == "__main__":
58
+ dataset = Dataset()
59
+
60
+ # Example usage
61
+ input_data = dataset.get_input(normalize=True)
62
+ output_data = dataset.get_output(normalize=True)
63
+
64
+ print("Input shape:", input_data.shape)
65
+ print("Output shape:", output_data.shape)
__pycache__/Dataset.cpython-312.pyc ADDED
Binary file (4.49 kB). View file
 
__pycache__/model_inverse.cpython-312.pyc ADDED
Binary file (17.6 kB). View file
 
app.py CHANGED
@@ -3,12 +3,13 @@
3
  import streamlit as st
4
  import pandas as pd
5
  import altair as alt
6
- import plotly.express as px
7
  from PIL import Image # Used to open and handle image files
8
  import matplotlib
9
  import matplotlib.pyplot as plt
10
  import numpy as np
11
 
 
12
 
13
  #######################
14
  # Page configuration
@@ -181,7 +182,7 @@ E1bV=0 # 10% strain longitudinal stiffness
181
  G12aV=0 # initial longitudinal stiffness
182
  G12bV=0 # 10% strain longitudinal stiffness
183
  nlayers=4
184
- vf=0.5
185
  angle=30
186
 
187
  #######################
@@ -239,7 +240,7 @@ if st.session_state.input_changed == True:
239
  st.session_state.forming_design_button_clicked = False
240
  st.session_state.input_changed = False
241
 
242
- st.button("Generate required stress-strain curves", width=400, on_click=input_curve_click)
243
 
244
  if st.session_state.input_curve_button_clicked == True:
245
  #st.write(E1aV)
@@ -299,7 +300,7 @@ if st.session_state.input_curve_button_clicked == True:
299
  st.pyplot(fig)
300
 
301
  st.write("")
302
- st.button("Material Inverse Design", width=400, on_click=material_design_click)
303
  if st.session_state.material_design_button_clicked == True:
304
  #st.write("")
305
 
@@ -307,7 +308,7 @@ if st.session_state.input_curve_button_clicked == True:
307
  col1_row3, col2_row3, col3_row3, col4_row3, col5_row3= st.columns([0.15,0.15,0.23,0.23,0.23])
308
  with col1_row3:
309
  with st.container(border=False): # Container with a border
310
- st.write("Matrix material = ", "PP")
311
  st.write("Fiber material = ", "Carbon")
312
 
313
  with col2_row3:
@@ -359,14 +360,14 @@ if st.session_state.input_curve_button_clicked == True:
359
 
360
 
361
  st.write("")
362
- st.button("Thermoforming Requirements", width=400, on_click=forming_input_click)
363
  if st.session_state.forming_input_button_clicked == True:
364
  #st.write("")
365
  # 4th row with 3 columns
366
  col1_row4, col2_row4, col3_row4, col4_row4, col5_row4 = st.columns([0.16,0.16,0.2,0.24,0.24])
367
  with col1_row4:
368
  with st.container(border=False): # Container with a border
369
- st.write("Matrix material", "PP")
370
  st.write("Fiber material=", "Carbon")
371
  st.write("Number of layers=", nlayers)
372
  st.write("Volume fraction=", vf)
@@ -395,23 +396,29 @@ if st.session_state.input_curve_button_clicked == True:
395
  angleC= st.number_input("Maximum warpage angle C (degree):", format="%.2f", width=300, key="C", on_change=forming_typed_in)
396
  max_stress= st.number_input("Maximum residual stress (MPa):", format="%.2f", width=300, key="max_stress", on_change=forming_typed_in)
397
 
 
398
  st.write("")
399
  if st.session_state.forming_input_changed == True:
400
  st.session_state.forming_design_button_clicked = False
401
  st.session_state.forming_input_changed = False
402
- st.button("Thermoforming process design", width=400, on_click=forming_design_click)
403
  if st.session_state.forming_design_button_clicked == True:
 
 
 
 
 
404
  # 5th row with 3 columns
405
  col1_row5, col2_row5,col3_row5 = st.columns([0.25,0.25,0.25])
406
  with col1_row5:
407
  with st.container(border=False): # Container with a border
408
- st.write("Forming temperature (C)=", nlayers)
409
  with col2_row5:
410
  with st.container(border=False): # Container with a border
411
- st.write("Punching velocity (mm/s)=", nlayers)
412
  with col3_row5:
413
  with st.container(border=False): # Container with a border
414
- st.write("Cooling time (s)=", nlayers)
415
 
416
 
417
 
@@ -419,4 +426,3 @@ if st.session_state.input_curve_button_clicked == True:
419
 
420
 
421
 
422
-
 
3
  import streamlit as st
4
  import pandas as pd
5
  import altair as alt
6
+ # import plotly.express as px
7
  from PIL import Image # Used to open and handle image files
8
  import matplotlib
9
  import matplotlib.pyplot as plt
10
  import numpy as np
11
 
12
+ from model_inverse import inverse_design
13
 
14
  #######################
15
  # Page configuration
 
182
  G12aV=0 # initial longitudinal stiffness
183
  G12bV=0 # 10% strain longitudinal stiffness
184
  nlayers=4
185
+ vf=0.6
186
  angle=30
187
 
188
  #######################
 
240
  st.session_state.forming_design_button_clicked = False
241
  st.session_state.input_changed = False
242
 
243
+ st.button("Generate required stress-strain curves", use_container_width=True, on_click=input_curve_click)
244
 
245
  if st.session_state.input_curve_button_clicked == True:
246
  #st.write(E1aV)
 
300
  st.pyplot(fig)
301
 
302
  st.write("")
303
+ st.button("Material Inverse Design", use_container_width=True, on_click=material_design_click)
304
  if st.session_state.material_design_button_clicked == True:
305
  #st.write("")
306
 
 
308
  col1_row3, col2_row3, col3_row3, col4_row3, col5_row3= st.columns([0.15,0.15,0.23,0.23,0.23])
309
  with col1_row3:
310
  with st.container(border=False): # Container with a border
311
+ st.write("Matrix material = ", "PEEK")
312
  st.write("Fiber material = ", "Carbon")
313
 
314
  with col2_row3:
 
360
 
361
 
362
  st.write("")
363
+ st.button("Thermoforming Requirements", use_container_width=True, on_click=forming_input_click)
364
  if st.session_state.forming_input_button_clicked == True:
365
  #st.write("")
366
  # 4th row with 3 columns
367
  col1_row4, col2_row4, col3_row4, col4_row4, col5_row4 = st.columns([0.16,0.16,0.2,0.24,0.24])
368
  with col1_row4:
369
  with st.container(border=False): # Container with a border
370
+ st.write("Matrix material", "PEEK")
371
  st.write("Fiber material=", "Carbon")
372
  st.write("Number of layers=", nlayers)
373
  st.write("Volume fraction=", vf)
 
396
  angleC= st.number_input("Maximum warpage angle C (degree):", format="%.2f", width=300, key="C", on_change=forming_typed_in)
397
  max_stress= st.number_input("Maximum residual stress (MPa):", format="%.2f", width=300, key="max_stress", on_change=forming_typed_in)
398
 
399
+
400
  st.write("")
401
  if st.session_state.forming_input_changed == True:
402
  st.session_state.forming_design_button_clicked = False
403
  st.session_state.forming_input_changed = False
404
+ st.button("Thermoforming process design", use_container_width=True, on_click=forming_design_click)
405
  if st.session_state.forming_design_button_clicked == True:
406
+ best = inverse_design(ply_number=nlayers,
407
+ fiber_vf=vf,
408
+ y_target=[angleA, angleB, angleC, max_stress],
409
+ n_restarts=5,
410
+ epochs=100)
411
  # 5th row with 3 columns
412
  col1_row5, col2_row5,col3_row5 = st.columns([0.25,0.25,0.25])
413
  with col1_row5:
414
  with st.container(border=False): # Container with a border
415
+ st.write("Forming temperature (C)=", best["input"][0])
416
  with col2_row5:
417
  with st.container(border=False): # Container with a border
418
+ st.write("Punching velocity (mm/s)=", best["input"][1])
419
  with col3_row5:
420
  with st.container(border=False): # Container with a border
421
+ st.write("Cooling time (s)=", best["input"][2])
422
 
423
 
424
 
 
426
 
427
 
428
 
 
main_thermo.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from Dataset import Dataset
5
+ from model import NeuralNetwork
6
+
7
+ DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
8
+ # Set global plotting parameters
9
+ plt.rcParams.update({'font.size': 14,
10
+ 'figure.figsize': (10, 8),
11
+ 'lines.linewidth': 2,
12
+ 'lines.markersize': 6,
13
+ 'axes.grid': True,
14
+ 'axes.labelsize': 16,
15
+ 'legend.fontsize': 14,
16
+ 'xtick.labelsize': 14,
17
+ 'ytick.labelsize': 14,
18
+ 'figure.autolayout': True
19
+ })
20
+
21
+ def set_seed(seed=42):
22
+ np.random.seed(seed)
23
+ torch.manual_seed(seed)
24
+ if torch.cuda.is_available():
25
+ torch.cuda.manual_seed_all(seed)
26
+
27
+ def train_neural_network(model, inputs, outputs, optimizer, epochs=1000, lr_scheduler=None):
28
+ model.train()
29
+ for epoch in range(epochs):
30
+ optimizer.zero_grad()
31
+ predictions = model(inputs)
32
+ loss = torch.mean(torch.square(predictions - outputs))
33
+ loss.backward()
34
+ optimizer.step()
35
+
36
+ if lr_scheduler:
37
+ lr_scheduler.step()
38
+
39
+ if epoch % 100 == 0:
40
+ print(f'Epoch {epoch}, Loss: {loss.item()}, Learning Rate: {optimizer.param_groups[0]["lr"]}')
41
+
42
+ def main():
43
+ set_seed(5324)
44
+ dataset = Dataset()
45
+ inputs = dataset.get_input(normalize=True)
46
+ outputs = dataset.get_output(normalize=True)
47
+
48
+ idx_train = np.random.choice(len(inputs), size=int(0.98 * len(inputs)), replace=False)
49
+ idx_test = np.setdiff1d(np.arange(len(inputs)), idx_train)
50
+
51
+ inputs_train = torch.tensor(inputs[idx_train], dtype=torch.float32).to(DEVICE)
52
+ outputs_train = torch.tensor(outputs[idx_train], dtype=torch.float32).to(DEVICE)
53
+
54
+ inputs_test = torch.tensor(inputs[idx_test], dtype=torch.float32).to(DEVICE)
55
+ outputs_test = torch.tensor(outputs[idx_test], dtype=torch.float32).to(DEVICE)
56
+
57
+ layer_sizes = [inputs.shape[1]] + [64] * 4 + [outputs.shape[1]]
58
+ dropout_rate = 0.00
59
+ model = NeuralNetwork(layer_sizes, dropout_rate=dropout_rate, activation=torch.nn.ReLU).to(DEVICE)
60
+ optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
61
+ lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5000, gamma=0.9)
62
+
63
+ # Create a proper dataset that keeps input-output pairs together
64
+ train_dataset = torch.utils.data.TensorDataset(inputs_train, outputs_train)
65
+ train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, shuffle=True)
66
+
67
+ # Train the model
68
+ epochs = 20000
69
+ for epoch in range(epochs):
70
+ model.train()
71
+ for inputs_batch, outputs_batch in train_loader:
72
+ inputs_batch = inputs_batch.to(DEVICE)
73
+ outputs_batch = outputs_batch.to(DEVICE)
74
+ optimizer.zero_grad()
75
+ predictions = model(inputs_batch)
76
+ loss = torch.mean(torch.square(predictions - outputs_batch))
77
+ loss.backward()
78
+ optimizer.step()
79
+
80
+ if lr_scheduler:
81
+ lr_scheduler.step()
82
+
83
+ if epoch % 500 == 0:
84
+ train_pred = model(inputs_train)
85
+ train_loss = torch.mean(torch.square(train_pred - outputs_train))
86
+ test_pred = model(inputs_test)
87
+ test_loss = torch.mean(torch.square(test_pred - outputs_test))
88
+ print(f'Epoch {epoch}, Train Loss: {train_loss.item():.6f}, Test Loss: {test_loss.item():.6f}')
89
+ # print(f'Learning Rate: {optimizer.param_groups[0]["lr"]}')
90
+
91
+
92
+ predictions = model.predict(inputs_test)
93
+ test_loss = torch.mean(torch.square(predictions - outputs_test))
94
+ print(f'Test Loss: {test_loss.item()}. Samples: {idx_test}')
95
+
96
+ x = np.arange(0, len(idx_test))
97
+
98
+ outputs_test = dataset.denormalize_output(outputs_test.cpu().numpy())
99
+ predictions = dataset.denormalize_output(predictions.cpu().numpy())
100
+ # for sample in outputs_test:
101
+ # print(f'Test samples: {sample}')
102
+ plt.figure(figsize=(10, 6))
103
+ plt.plot(x, outputs_test[:, 0], color='b', linestyle='--', label='True A1')
104
+ plt.plot(x, predictions[:, 0], color='b', linestyle='-', label='Predicted A1')
105
+ plt.plot(x, outputs_test[:, 1], color='r', linestyle='--', label='True B1')
106
+ plt.plot(x, predictions[:, 1], color='r', linestyle='-', label='Predicted B1')
107
+ plt.plot(x, outputs_test[:, 2], color='g', linestyle='--', label='True C1')
108
+ plt.plot(x, predictions[:, 2], color='g', linestyle='-', label='Predicted C1')
109
+ plt.gca().xaxis.set_major_locator(plt.MaxNLocator(integer=True))
110
+ plt.xlabel('Sample Index')
111
+ plt.xticks(ticks=range(len(idx_test)),labels=idx_test + 1)
112
+ plt.ylabel('Springback Angle (Degrees)')
113
+ plt.title('Springback Angle Prediction')
114
+ plt.legend(loc='upper right')
115
+ plt.savefig('springback_angle_prediction.png')
116
+
117
+
118
+ plt.figure(figsize=(10, 6))
119
+ plt.plot(x, outputs_test[:, 3], color='m', linestyle='--', label='True Stress(Max)')
120
+ plt.plot(x, predictions[:, 3], color='m', linestyle='-', label='Predicted Stress(Max)')
121
+ plt.xlabel('Sample Index')
122
+ plt.xticks(ticks=range(len(idx_test)),labels=idx_test + 1)
123
+ plt.ylabel('Stress (MPa)')
124
+ plt.legend(loc='upper left')
125
+ plt.savefig('stress_max_prediction.png')
126
+
127
+
128
+
129
+ # MSE
130
+ mse = np.mean((predictions - outputs_test) ** 2, axis=0)
131
+ print(f'Mean Squared Error for A1: {mse[0]:.6f}, B1: {mse[1]:.6f}, C1: {mse[2]:.6f}, Stress(Max): {mse[3]:.6f}')
132
+
133
+ # R 2 score
134
+ ss_ress = np.sum((outputs_test - predictions) ** 2, axis=0)
135
+ ss_tots = np.sum((outputs_test - np.mean(outputs_test, axis=0)) ** 2, axis=0)
136
+ r2_scores = 1 - ss_ress / ss_tots
137
+ print(f'R² Score for A1: {r2_scores[0]:.6f}, B1: {r2_scores[1]:.6f}, C1: {r2_scores[2]:.6f}, Stress(Max): {r2_scores[3]:.6f}')
138
+
139
+ # Error
140
+
141
+ # Save the model
142
+ model_save_path = './model_checkpoint.pth'
143
+ model_config = {'layer_sizes': layer_sizes,
144
+ 'dropout_rate': dropout_rate
145
+ }
146
+ checkpoint = {
147
+ 'model_state_dict': model.state_dict(),
148
+ 'model_config': model_config
149
+ }
150
+ torch.save(checkpoint, model_save_path)
151
+ # Load the model
152
+ # model = NeuralNetwork(layer_sizes)
153
+ # model.load_state_dict(torch.load(model_save_path))
154
+
155
+ def load_model(model_path):
156
+ checkpoint = torch.load(model_path)
157
+ model_config = checkpoint['model_config']
158
+ model = NeuralNetwork(model_config['layer_sizes'], dropout_rate=model_config['dropout_rate'], activation=torch.nn.ReLU).to(DEVICE)
159
+ model.load_state_dict(checkpoint['model_state_dict'])
160
+ print(f"Model loaded from {model_path}")
161
+ return model
162
+
163
+
164
+ if __name__ == "__main__":
165
+ main()
166
+
167
+ # model = load_model('./model_checkpoint.pth').to(torch.device('cpu'))
168
+ # data = Dataset()
169
+ # data = Dataset()
170
+ # print(np.unique(data.df['Fiber_Volume_Fractions'].to_numpy())[:10])
171
+
172
+ # test_input = torch.tensor([[2, 0.6, 450.0, 100.0, 500.0]], dtype=torch.float32)
173
+ # test_output = model.predict((test_input - torch.tensor(data.input_mean)) / torch.tensor(data.input_std))
174
+ # test_output = test_output * torch.tensor(data.output_std) + torch.tensor(data.output_mean)
175
+ # print(f"Test Prediction for fixed input {test_input.numpy()}: {test_output.numpy()}")
176
+
model.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+
4
+ class NeuralNetwork(torch.nn.Module):
5
+ def __init__(self, layer_sizes, dropout_rate=0.0, activation=torch.nn.ReLU):
6
+ super(NeuralNetwork, self).__init__()
7
+
8
+ if dropout_rate > 0:
9
+ self.dropout_layer = torch.nn.Dropout(dropout_rate)
10
+
11
+ self.layer_sizes = layer_sizes
12
+ self.layers = torch.nn.ModuleList()
13
+ for i in range(len(layer_sizes) - 2):
14
+ self.layers.append(torch.nn.Linear(layer_sizes[i], layer_sizes[i + 1]))
15
+ self.layers.append(activation())
16
+ self.layers.append(torch.nn.Linear(layer_sizes[-2], layer_sizes[-1]))
17
+
18
+ # self.sequential = torch.nn.Sequential(*self.layers)
19
+
20
+ self.init_weights()
21
+
22
+ def init_weights(self):
23
+ for layer in self.layers:
24
+ if isinstance(layer, torch.nn.Linear):
25
+ torch.nn.init.xavier_normal_(layer.weight)
26
+ layer.bias.data.fill_(0.0)
27
+
28
+ def forward(self, x, train=True):
29
+ for layer in self.layers:
30
+ x = layer(x)
31
+ if train and hasattr(self, 'dropout_layer'):
32
+ x = self.dropout_layer(x)
33
+
34
+ return x
35
+
36
+ def predict(self, x, train=False):
37
+ self.eval()
38
+ with torch.no_grad():
39
+ return self.forward(x, train)
model_checkpoint.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6170b3c02cd10a5db53184cfc0438ce279fe87ed4ffd4e7cf5a31a3945391326
3
+ size 57109
model_inverse.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from Dataset import Dataset
5
+
6
+ # DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
7
+ DEVICE = torch.device('cpu')
8
+
9
+ # Set global plotting parameters
10
+ plt.rcParams.update({'font.size': 14,
11
+ 'figure.figsize': (10, 8),
12
+ 'lines.linewidth': 2,
13
+ 'lines.markersize': 6,
14
+ 'axes.grid': True,
15
+ 'axes.labelsize': 16,
16
+ 'legend.fontsize': 14,
17
+ 'xtick.labelsize': 14,
18
+ 'ytick.labelsize': 14,
19
+ 'figure.autolayout': True
20
+ })
21
+
22
+ def set_seed(seed=42):
23
+ np.random.seed(seed)
24
+ torch.manual_seed(seed)
25
+ if torch.cuda.is_available():
26
+ torch.cuda.manual_seed_all(seed)
27
+
28
+ class NeuralNetwork(torch.nn.Module):
29
+ def __init__(self, layer_sizes, dropout_rate=0.0, activation=torch.nn.ReLU):
30
+ super(NeuralNetwork, self).__init__()
31
+
32
+ if dropout_rate > 0:
33
+ self.dropout_layer = torch.nn.Dropout(dropout_rate)
34
+
35
+ self.layer_sizes = layer_sizes
36
+ self.layers = torch.nn.ModuleList()
37
+ for i in range(len(layer_sizes) - 2):
38
+ self.layers.append(torch.nn.Linear(layer_sizes[i], layer_sizes[i + 1]))
39
+ self.layers.append(activation())
40
+ self.layers.append(torch.nn.Linear(layer_sizes[-2], layer_sizes[-1]))
41
+
42
+ # self.sequential = torch.nn.Sequential(*self.layers)
43
+
44
+ self.init_weights()
45
+
46
+ def init_weights(self):
47
+ for layer in self.layers:
48
+ if isinstance(layer, torch.nn.Linear):
49
+ torch.nn.init.xavier_normal_(layer.weight)
50
+ layer.bias.data.fill_(0.0)
51
+
52
+ def forward(self, x, train=True):
53
+ for layer in self.layers:
54
+ x = layer(x)
55
+ if train and hasattr(self, 'dropout_layer'):
56
+ x = self.dropout_layer(x)
57
+
58
+ return x
59
+
60
+ def predict(self, x, train=False):
61
+ self.eval()
62
+ with torch.no_grad():
63
+ return self.forward(x, train)
64
+
65
+ def train_neural_network(model, inputs, outputs, optimizer, epochs=1000, lr_scheduler=None):
66
+ model.train()
67
+ for epoch in range(epochs):
68
+ optimizer.zero_grad()
69
+ predictions = model(inputs)
70
+ loss = torch.mean(torch.square(predictions - outputs))
71
+ loss.backward()
72
+ optimizer.step()
73
+
74
+ if lr_scheduler:
75
+ lr_scheduler.step()
76
+
77
+ if epoch % 100 == 0:
78
+ print(f'Epoch {epoch}, Loss: {loss.item()}, Learning Rate: {optimizer.param_groups[0]["lr"]}')
79
+
80
+
81
+ def load_model(model_path):
82
+ checkpoint = torch.load(model_path, map_location=DEVICE)
83
+ model_config = checkpoint['model_config']
84
+ model = NeuralNetwork(model_config['layer_sizes'], dropout_rate=model_config['dropout_rate'])
85
+ model.load_state_dict(checkpoint['model_state_dict'])
86
+ print(f"Model loaded from {model_path}")
87
+
88
+ model.to(DEVICE)
89
+ model.eval()
90
+ return model
91
+
92
+ def inverse_design(ply_number, fiber_vf, y_target, n_restarts=10, epochs=100, use_lbfgs=False):
93
+ model = load_model('./model_checkpoint.pth')
94
+
95
+ data = Dataset()
96
+ y_target_norm = data.normalize_output(y_target) # (A1, B1, C1, Stress)
97
+ y_target_tensor = torch.tensor(y_target, dtype=torch.float32)
98
+ input_mean = torch.tensor(data.input_mean)
99
+ input_std = torch.tensor(data.input_std)
100
+ output_mean = torch.tensor(data.output_mean)
101
+ output_std = torch.tensor(data.output_std)
102
+
103
+
104
+ weights = torch.tensor([1.0, 1.0, 1.0, 0.5], dtype=torch.float32)
105
+ bounds = torch.tensor([[50., 600.], [50., 600.], [50., 600.]], dtype=torch.float32) # Initial_Temp, Punch_Velocity, Cooling_Time
106
+ best = {"loss": float('inf'), "input": None, "output": None}
107
+
108
+ for restart in range(n_restarts):
109
+ z = torch.randn(3, requires_grad=True)
110
+
111
+ if use_lbfgs:
112
+ optimizer = torch.optim.LBFGS([z], lr=0.1, max_iter=epochs, line_search_fn="strong_wolfe")
113
+ steps = 1
114
+ else:
115
+ optimizer = torch.optim.Adam([z], lr=0.001)
116
+ steps = epochs
117
+
118
+ for step in range(steps):
119
+ def closure():
120
+ var = bounds[:, 0] + (bounds[:, 1] - bounds[:, 0]) * torch.sigmoid(z)
121
+ optimizer.zero_grad()
122
+ input_raw = torch.cat([torch.tensor([ply_number, fiber_vf]), var]).unsqueeze(0)
123
+ input_norm = (input_raw - input_mean) / input_std
124
+ output_pred = model(input_norm, train=False)
125
+ output_pred = (output_pred * output_std) + output_mean
126
+ loss = torch.sum(weights * (output_pred - y_target_tensor) ** 2)
127
+ loss.backward()
128
+ return loss
129
+
130
+ if use_lbfgs:
131
+ loss = optimizer.step(closure)
132
+ else:
133
+ loss = closure()
134
+ optimizer.step()
135
+
136
+ if (step + 1) % 200 == 0:
137
+ print(f'Restart {restart + 1}, Step {step + 1}, Loss: {loss.item():.6f}, grad: {z.grad.norm().item():.6f}')
138
+
139
+ with torch.no_grad():
140
+ var = bounds[:, 0] + (bounds[:, 1] - bounds[:, 0]) * torch.sigmoid(z)
141
+ input_raw = torch.cat([torch.tensor([ply_number, fiber_vf]), var])
142
+ input_norm = (input_raw - input_mean) / input_std
143
+ output_pred = model.predict(input_norm)
144
+ output_pred = data.denormalize_output(output_pred.numpy())
145
+ final_loss = np.sum(weights.numpy() * (output_pred - y_target) ** 2).item()
146
+ if final_loss < best["loss"]:
147
+ best["loss"] = final_loss
148
+ best["input"] = var.detach().cpu().numpy()
149
+ best["output"] = output_pred
150
+
151
+ return best
152
+
153
+
154
+ def inverse_model():
155
+ set_seed(5324)
156
+ dataset = Dataset(inverse=True)
157
+ inputs, outputs = dataset.get_input(normalize=True), dataset.get_output(normalize=True)
158
+
159
+ idx_train = np.random.choice(len(inputs), size=int(0.85 * len(inputs)), replace=False)
160
+ idx_test = np.setdiff1d(np.arange(len(inputs)), idx_train)
161
+ # idx_test = np.array([1, 14+1, 18+1, 20+1, 23+1])
162
+ # idx_train = np.setdiff1d(np.arange(len(inputs)), idx_test)
163
+
164
+ inputs_train = torch.tensor(inputs[idx_train], dtype=torch.float32).to(DEVICE)
165
+ outputs_train = torch.tensor(outputs[idx_train], dtype=torch.float32).to(DEVICE)
166
+
167
+ inputs_test = torch.tensor(inputs[idx_test], dtype=torch.float32).to(DEVICE)
168
+ outputs_test = torch.tensor(outputs[idx_test], dtype=torch.float32).to(DEVICE)
169
+
170
+ layer_sizes = [inputs.shape[1]] + [64] * 4 + [outputs.shape[1]]
171
+ dropout_rate =0.05
172
+ model = NeuralNetwork(layer_sizes, dropout_rate=dropout_rate, activation=torch.nn.ReLU).to(DEVICE)
173
+ optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
174
+ lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5000, gamma=0.9)
175
+
176
+ # Create a proper dataset that keeps input-output pairs together
177
+ train_dataset = torch.utils.data.TensorDataset(inputs_train, outputs_train)
178
+ train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, shuffle=True)
179
+
180
+ # Train the model
181
+ epochs = 20000
182
+ for epoch in range(epochs):
183
+ model.train()
184
+ for inputs_batch, outputs_batch in train_loader:
185
+ inputs_batch = inputs_batch.to(DEVICE)
186
+ outputs_batch = outputs_batch.to(DEVICE)
187
+ optimizer.zero_grad()
188
+ predictions = model(inputs_batch)
189
+ loss = torch.mean(torch.square(predictions - outputs_batch))
190
+ loss.backward()
191
+ optimizer.step()
192
+
193
+ if lr_scheduler:
194
+ lr_scheduler.step()
195
+
196
+ if epoch % 500 == 0:
197
+ train_pred = model(inputs_train)
198
+ train_loss = torch.mean(torch.square(train_pred - outputs_train))
199
+ test_pred = model(inputs_test)
200
+ test_loss = torch.mean(torch.square(test_pred - outputs_test))
201
+ print(f'Epoch {epoch}, Train Loss: {train_loss.item():.6f}, Test Loss: {test_loss.item():.6f}')
202
+ # print(f'Learning Rate: {optimizer.param_groups[0]["lr"]}')
203
+
204
+
205
+ predictions = model.predict(inputs_test)
206
+ test_loss = torch.mean(torch.square(predictions - outputs_test))
207
+ print(f'Test Loss: {test_loss.item()}. Samples: {idx_test}')
208
+
209
+ x = np.arange(0, len(idx_test))
210
+
211
+ outputs_test = dataset.denormalize_output(outputs_test.cpu().numpy())
212
+ predictions = dataset.denormalize_output(predictions.cpu().numpy())
213
+ # for sample in outputs_test:
214
+ # print(f'Test samples: {sample}')
215
+ plt.figure(figsize=(10, 6))
216
+ plt.plot(x, outputs_test[:, 0], color='b', linestyle='--', label='True Initial Temp')
217
+ plt.plot(x, predictions[:, 0], color='b', linestyle='-', label='Predicted Initial Temp')
218
+ plt.plot(x, outputs_test[:, 1], color='r', linestyle='--', label='True Punch Velocity')
219
+ plt.plot(x, predictions[:, 1], color='r', linestyle='-', label='Predicted Punch Velocity')
220
+ plt.plot(x, outputs_test[:, 2], color='g', linestyle='--', label='True Cooling Time')
221
+ plt.plot(x, predictions[:, 2], color='g', linestyle='-', label='Predicted Cooling Time')
222
+ plt.gca().xaxis.set_major_locator(plt.MaxNLocator(integer=True))
223
+ plt.xlabel('Sample Index')
224
+ plt.xticks(ticks=range(len(idx_test)),labels=idx_test + 1)
225
+ plt.ylabel('Processing Parameters')
226
+ plt.legend(loc='upper right')
227
+ plt.savefig('inverse_design.png')
228
+
229
+ # MSE
230
+ mse = np.mean((predictions - outputs_test) ** 2, axis=0)
231
+ print(f'Mean Squared Error for Initial Temp: {mse[0]:.6f}, Punch Velocity: {mse[1]:.6f}, Cooling Time: {mse[2]:.6f}')
232
+
233
+ # R 2 score
234
+ ss_ress = np.sum((outputs_test - predictions) ** 2, axis=0)
235
+ ss_tots = np.sum((outputs_test - np.mean(outputs_test, axis=0)) ** 2, axis=0)
236
+ r2_scores = 1 - ss_ress / ss_tots
237
+ print(f'R² Score for Initial Temp: {r2_scores[0]:.6f}, Punch Velocity: {r2_scores[1]:.6f}, Cooling Time: {r2_scores[2]:.6f}')
238
+
239
+ # Error
240
+
241
+ # Save the model
242
+ model_save_path = './model_inverse_ckpt.pth'
243
+ model_config = {'layer_sizes': layer_sizes,
244
+ 'dropout_rate': dropout_rate
245
+ }
246
+ checkpoint = {
247
+ 'model_state_dict': model.state_dict(),
248
+ 'model_config': model_config
249
+ }
250
+ torch.save(checkpoint, model_save_path)
251
+
252
+
253
+ if __name__ == "__main__":
254
+ # train the inverse model over springback data
255
+ # inverse_model()
256
+
257
+ # perform inverse design
258
+ import time
259
+ start_time = time.time()
260
+ # best = inverse_design(ply_number=2, fiber_vf=0.6, y_target=np.array([0.89, 0.83, 0.12, 180.2]), n_restarts=50, epochs=100, use_lbfgs=True, feasibility_samples=2000)
261
+ best = inverse_design(ply_number=2, fiber_vf=0.4, y_target=np.array([0.45, 9.03, 1.87, 187.4]), n_restarts=50, epochs=100, use_lbfgs=True)
262
+ end_time = time.time()
263
+ time_elapsed = (end_time - start_time)
264
+ print(f"Inverse design completed in {time_elapsed:.2f} seconds.")
265
+ print("Best Input (Initial Temp, Punch Velocity, Cooling Time):", best["input"])
266
+ print("Best Output (A1, B1, C1, Stress):", best["output"])
267
+
requirements.txt CHANGED
@@ -5,3 +5,5 @@ plotly
5
  pathlib
6
  numpy
7
  matplotlib
 
 
 
5
  pathlib
6
  numpy
7
  matplotlib
8
+ openpyxl
9
+ torch