├── LICENSE ├── README.md ├── main └── PINN-elasticity.py └── output ├── NN_Adam.pickle ├── NN_Adam_bfgs.pickle ├── loss.png ├── loss_history.pickle ├── sxx_PINN.png ├── sxx_anal.png ├── sxy_PINN.png ├── sxy_anal.png ├── syy_PINN.png ├── syy_anal.png ├── u_PINN.png ├── u_anal.png ├── v_PINN.png └── v_anal.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jing Hu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Physics-Informed-Neural-Networks for linear elasticity 2 | ### Jing Hu (UT Austin) 3 | 4 | In this project, the linear elasticity problem is solved with Physics Informed Neural Networks (PINN). The governing equations for isotropic linear elasticity are 5 | 6 | 7 | 8 | where is the stress tensor, is the strain tensor, u is the displacement vector, f is the body force vector, and are the Lamé parameters and Einstein summation applies. Now we consider the linear elasticity problem on a square domain . We consider the following boundary conditions: 9 | 10 | Top wall 11 | 12 | 13 | 14 | Right wall 15 | 16 | 17 | 18 | Left wall 19 | 20 | 21 | 22 | Bottom wall 23 | 24 | 25 | 26 | where is the traction prescribed on the boundaries and the Q is the load magnitude. We consider the following body force: 27 | 28 | 29 | 30 | The analytical solution to this problem is [ , ]. 31 | 32 | In the numerical implementation, we output both displacements and stresses and adopt the following loss function to prescribe boundary conditions and enforce the governing equation. 33 | 34 | 35 | 36 | For model parameters: , and , we have the following results 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | The decrease of the loss function is shown in the following figure. 49 | 50 | 51 | 52 | > **Note:** The implementations were developed and tested on colab with TensorFlow 1.9 and hardware accelerator chosen as GPU. 53 | -------------------------------------------------------------------------------- /main/PINN-elasticity.py: -------------------------------------------------------------------------------- 1 | from google.colab import drive 2 | drive.mount('/content/drive') 3 | 4 | try: 5 | import mymodule 6 | except ImportError: 7 | !pip install pyDOE 8 | 9 | %tensorflow_version 1.9 10 | import numpy as np 11 | import time 12 | from pyDOE import lhs 13 | import matplotlib 14 | import matplotlib.pyplot as plt 15 | import pickle 16 | import scipy.io 17 | import random 18 | import tensorflow as tf 19 | 20 | device_name = tf.test.gpu_device_name() 21 | if device_name != '/device:GPU:0': 22 | raise SystemError('GPU device not found') 23 | 24 | random.seed(1234) 25 | np.random.seed(1234) 26 | tf.set_random_seed(1234) 27 | 28 | class PINN_elasticity: 29 | # Initialize the class 30 | def __init__(self, lamd, mu, Q, Collo, DirichletBCxy, NeumannBCxy, NeumannBCn, NeumannBCt, bodyf, uv_layers, trained_model = 0, uvDir='./drive/MyDrive/PINN/nnwb.pickle'): 31 | 32 | # Count for callback function 33 | self.count=0 34 | 35 | # Mat. properties 36 | self.lamd = lamd 37 | self.mu = mu 38 | self.Q = Q 39 | 40 | # Collocation point 41 | self.x_c = Collo[:, 0:1] 42 | self.y_c = Collo[:, 1:2] 43 | 44 | self.x_bodyf = bodyf[:, 0:1] 45 | self.y_bodyf = bodyf[:, 1:2] 46 | 47 | self.x_Dirichlet = DirichletBCxy[:, 0:1] 48 | self.y_Dirichlet = DirichletBCxy[:, 1:2] 49 | 50 | self.x_Neumann = NeumannBCxy[:, 0:1] 51 | self.y_Neumann = NeumannBCxy[:, 1:2] 52 | 53 | self.Neumann_n1 = NeumannBCn[:, 0:1] 54 | self.Neumann_n2 = NeumannBCn[:, 1:2] 55 | 56 | self.Neumann_t1 = NeumannBCt[:, 0:1] 57 | self.Neumann_t2 = NeumannBCt[:, 1:2] 58 | 59 | # Define layers 60 | self.uv_layers = uv_layers 61 | 62 | self.loss_rec = [] 63 | self.loss_Dir = [] 64 | self.loss_Neu = [] 65 | self.loss_gov = [] 66 | 67 | # Initialize NNs 68 | if trained_model == 0 : 69 | self.uv_weights, self.uv_biases = self.initialize_NN(self.uv_layers) 70 | else: 71 | print("Loading trained NN model...") 72 | self.uv_weights, self.uv_biases = self.load_NN(uvDir, self.uv_layers) 73 | 74 | # tf placeholders 75 | self.learning_rate = tf.placeholder(tf.float32, shape=[]) 76 | self.x_tf = tf.placeholder(tf.float32, shape=[None, self.x_c.shape[1]]) 77 | self.y_tf = tf.placeholder(tf.float32, shape=[None, self.y_c.shape[1]]) 78 | 79 | self.x_bodyf_tf = tf.placeholder(tf.float32, shape=[None, self.x_bodyf.shape[1]]) 80 | self.y_bodyf_tf = tf.placeholder(tf.float32, shape=[None, self.x_bodyf.shape[1]]) 81 | 82 | self.x_Dirichlet_tf = tf.placeholder(tf.float32, shape=[None, self.x_Dirichlet.shape[1]]) 83 | self.y_Dirichlet_tf = tf.placeholder(tf.float32, shape=[None, self.y_Dirichlet.shape[1]]) 84 | 85 | self.x_Neumann_tf = tf.placeholder(tf.float32, shape=[None, self.x_Neumann.shape[1]]) 86 | self.y_Neumann_tf = tf.placeholder(tf.float32, shape=[None, self.y_Neumann.shape[1]]) 87 | self.n1_Neumann_tf = tf.placeholder(tf.float32, shape=[None, self.Neumann_n1.shape[1]]) 88 | self.n2_Neumann_tf = tf.placeholder(tf.float32, shape=[None, self.Neumann_n2.shape[1]]) 89 | self.Neumannt1_tf = tf.placeholder(tf.float32, shape=[None, self.Neumann_t1.shape[1]]) 90 | self.Neumannt2_tf = tf.placeholder(tf.float32, shape=[None, self.Neumann_t2.shape[1]]) 91 | 92 | self.x_c_tf = tf.placeholder(tf.float32, shape=[None, self.x_c.shape[1]]) 93 | self.y_c_tf = tf.placeholder(tf.float32, shape=[None, self.y_c.shape[1]]) 94 | 95 | # tf graphs 96 | self.u_pred, self.v_pred, self.s11_pred, self.s22_pred, self.s12_pred, _, _ = self.net_uv(self.x_c_tf, self.y_c_tf) 97 | # for minimization 98 | self.f_s11r_pred, self.f_s22r_pred, self.f_s12r_pred, self.f_s1_pred, self.f_s2_pred = self.net_f(self.x_c_tf, self.y_c_tf) 99 | # for Dirichlet BC minimization 100 | self.u_Dirichlet_pred, self.v_Dirichlet_pred, _, _, _, _, _ = self.net_uv(self.x_Dirichlet_tf, self.y_Dirichlet_tf) 101 | # for Neumann BC minimization 102 | self.x_Neumann_pred, self.y_Neumann_pred, _, _, _, self.Neumannt1_pred, self.Neumannt2_pred = self.net_uv(self.x_Neumann_tf, self.y_Neumann_tf, self.n1_Neumann_tf, self.n2_Neumann_tf) 103 | 104 | self.loss_f = tf.reduce_mean(tf.square(self.f_s11r_pred)) \ 105 | + tf.reduce_mean(tf.square(self.f_s22r_pred))\ 106 | + tf.reduce_mean(tf.square(self.f_s12r_pred))\ 107 | + tf.reduce_mean(tf.square(self.x_bodyf_tf+self.f_s1_pred))\ 108 | + tf.reduce_mean(tf.square(self.y_bodyf_tf+self.f_s2_pred)) 109 | 110 | self.loss_Dirichlet = tf.reduce_mean(tf.square(self.u_Dirichlet_pred)) \ 111 | + tf.reduce_mean(tf.square(self.v_Dirichlet_pred)) 112 | 113 | self.loss_Neumann = tf.reduce_mean(tf.square(self.Neumannt1_tf-self.Neumannt1_pred))\ 114 | + tf.reduce_mean(tf.square(self.Neumannt2_tf-self.Neumannt2_pred)) 115 | 116 | self.loss = self.loss_f + self.loss_Dirichlet + self.loss_Neumann 117 | 118 | # Optimizer train_bfgs 119 | self.optimizer = tf.contrib.opt.ScipyOptimizerInterface(self.loss, 120 | var_list=self.uv_weights + self.uv_biases, 121 | method='L-BFGS-B', 122 | options={'maxiter': 100000, 123 | 'maxfun': 100000, 124 | 'maxcor': 50, 125 | 'maxls': 50, 126 | 'ftol ': 2e-8*np.finfo(float).eps}) 127 | 128 | # Optimizer train_Adam 129 | self.optimizer_Adam = tf.train.AdamOptimizer(learning_rate = self.learning_rate) 130 | self.train_op_Adam = self.optimizer_Adam.minimize(self.loss, 131 | var_list=self.uv_weights + self.uv_biases) 132 | 133 | # tf session 134 | self.sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True, 135 | log_device_placement=True)) 136 | init = tf.global_variables_initializer() 137 | self.sess.run(init) 138 | 139 | def initialize_NN(self, layers): 140 | weights = [] 141 | biases = [] 142 | num_layers = len(layers) 143 | for l in range(0, num_layers - 1): 144 | W = self.xavier_init(size=[layers[l], layers[l + 1]]) 145 | b = tf.Variable(tf.zeros([1, layers[l + 1]], dtype=tf.float32), dtype=tf.float32) 146 | weights.append(W) 147 | biases.append(b) 148 | return weights, biases 149 | 150 | def xavier_init(self, size): 151 | in_dim = size[0] 152 | out_dim = size[1] 153 | xavier_stddev = np.sqrt(2 / (in_dim + out_dim)) 154 | return tf.Variable(tf.truncated_normal([in_dim, out_dim], stddev=xavier_stddev, dtype=tf.float32), dtype=tf.float32) 155 | 156 | def save_NN(self, fileDir): 157 | 158 | uv_weights = self.sess.run(self.uv_weights) 159 | uv_biases = self.sess.run(self.uv_biases) 160 | 161 | with open(fileDir, 'wb') as f: 162 | pickle.dump([uv_weights, uv_biases], f) 163 | print(" - Save neural networks parameters on Google Drive successfully...") 164 | 165 | def load_NN(self, fileDir, layers): 166 | weights = [] 167 | biases = [] 168 | num_layers = len(layers) 169 | with open(fileDir, 'rb') as f: 170 | uv_weights, uv_biases = pickle.load(f) 171 | 172 | # Stored model must has the same # of layers 173 | assert num_layers == (len(uv_weights)+1) 174 | 175 | for num in range(0, num_layers - 1): 176 | W = tf.Variable(uv_weights[num], dtype=tf.float32) 177 | b = tf.Variable(uv_biases[num], dtype=tf.float32) 178 | weights.append(W) 179 | biases.append(b) 180 | print(" - Load NN parameters on Google Drive successfully...") 181 | return weights, biases 182 | 183 | def neural_net(self, X, weights, biases): 184 | num_layers = len(weights) + 1 185 | H = X 186 | # H = 2.0 * (X - self.lb) / (self.ub - self.lb) - 1.0 187 | for l in range(0, num_layers - 2): 188 | W = weights[l] 189 | b = biases[l] 190 | H = tf.tanh(tf.add(tf.matmul(H, W), b)) 191 | W = weights[-1] 192 | b = biases[-1] 193 | Y = tf.add(tf.matmul(H, W), b) 194 | return Y 195 | 196 | def net_uv(self, x, y, n1=0, n2=0): 197 | temp = self.neural_net(tf.concat([x, y], 1), self.uv_weights, self.uv_biases) 198 | u = temp[:,0:1] 199 | v = temp[:,1:2] 200 | s11 = temp[:, 2:3] 201 | s22 = temp[:, 3:4] 202 | s12 = temp[:, 4:5] 203 | 204 | # tractions 205 | t1 = tf.math.multiply(s11,n1) + tf.math.multiply(s12,n2) 206 | t2 = tf.math.multiply(s12,n1) + tf.math.multiply(s22,n2) 207 | 208 | return u, v, s11, s22, s12, t1, t2 209 | 210 | def net_f(self, x, y): 211 | 212 | lamd=self.lamd 213 | mu=self.mu 214 | Q=self.Q 215 | Pi=np.pi 216 | 217 | u, v, s11, s22, s12, _, _ = self.net_uv(x, y) 218 | 219 | s11_1 = tf.gradients(s11, x)[0] 220 | s12_2 = tf.gradients(s12, y)[0] 221 | s22_2 = tf.gradients(s22, y)[0] 222 | s12_1 = tf.gradients(s12, x)[0] 223 | 224 | # Plane stress problem 225 | u_x = tf.gradients(u, x)[0] 226 | u_y = tf.gradients(u, y)[0] 227 | 228 | v_x = tf.gradients(v, x)[0] 229 | v_y = tf.gradients(v, y)[0] 230 | 231 | e_xx = u_x 232 | e_yy = v_y 233 | e_xy = (u_y + v_x)/2.0 234 | e_kk = e_xx + e_yy 235 | 236 | f_s11r = lamd*e_kk + 2*mu*e_xx - s11 237 | f_s22r = lamd*e_kk + 2*mu*e_yy - s22 238 | f_s12r = 2*mu*e_xy - s12 239 | 240 | f_s1r = s11_1 + s12_2 241 | f_s2r = s12_1 + s22_2 242 | 243 | return f_s11r, f_s22r, f_s12r, f_s1r, f_s2r 244 | 245 | 246 | def callback(self, loss_gov, loss_Neu, loss_Dir, loss): 247 | self.count = self.count+1 248 | self.loss_Neu.append(loss_Neu) 249 | self.loss_Dir.append(loss_Dir) 250 | self.loss_gov.append(loss_gov) 251 | self.loss_rec.append(loss) 252 | 253 | if self.count % 100 == 0: 254 | print('{} th iterations, loss_f: {}, loss_Neumann: {}, loss_Dirichlet: {}, loss_total: {}'.format(self.count, loss_gov, loss_Neu, loss_Dir, loss )) 255 | 256 | def train(self, iter, learning_rate): 257 | 258 | tf_dict = {self.x_c_tf: self.x_c, self.y_c_tf: self.y_c, 259 | self.x_bodyf_tf: self.x_bodyf, self.y_bodyf_tf: self.y_bodyf, 260 | self.x_Neumann_tf: self.x_Neumann, self.y_Neumann_tf: self.y_Neumann, 261 | self.Neumannt1_tf: self.Neumann_t1, self.Neumannt2_tf: self.Neumann_t2, 262 | self.n1_Neumann_tf: self.Neumann_n1, self.n2_Neumann_tf: self.Neumann_n2, 263 | self.x_Dirichlet_tf: self.x_Dirichlet, self.y_Dirichlet_tf: self.y_Dirichlet, 264 | self.learning_rate: learning_rate} 265 | 266 | for it in range(iter): 267 | 268 | self.sess.run(self.train_op_Adam, tf_dict) 269 | 270 | # Print 271 | if it % 100 == 0: 272 | loss_value = self.sess.run(self.loss, tf_dict) 273 | loss_valueF = self.sess.run(self.loss_f, tf_dict) 274 | loss_valueN = self.sess.run(self.loss_Neumann, tf_dict) 275 | loss_valueD = self.sess.run(self.loss_Dirichlet, tf_dict) 276 | 277 | print('It: %d, Loss: %.3e, LossF: %.3e, LossN: %.3e, LossD: %.3e' % 278 | (it, loss_value, loss_valueF, loss_valueN, loss_valueD)) 279 | 280 | self.loss_Dir.append(self.sess.run(self.loss_Dirichlet, tf_dict)) 281 | self.loss_Neu.append(self.sess.run(self.loss_Neumann, tf_dict)) 282 | self.loss_gov.append(self.sess.run(self.loss_f, tf_dict)) 283 | self.loss_rec.append(self.sess.run(self.loss, tf_dict)) 284 | 285 | return self.loss_f, self.loss_Dirichlet, self.loss_Neumann, self.loss 286 | 287 | def train_bfgs(self): 288 | 289 | tf_dict = {self.x_c_tf: self.x_c, self.y_c_tf: self.y_c, 290 | self.x_bodyf_tf: self.x_bodyf, self.y_bodyf_tf: self.y_bodyf, 291 | self.x_Neumann_tf: self.x_Neumann, self.y_Neumann_tf: self.y_Neumann, 292 | self.Neumannt1_tf: self.Neumann_t1, self.Neumannt2_tf: self.Neumann_t2, 293 | self.n1_Neumann_tf: self.Neumann_n1, self.n2_Neumann_tf: self.Neumann_n2, 294 | self.x_Dirichlet_tf: self.x_Dirichlet, self.y_Dirichlet_tf: self.y_Dirichlet} 295 | 296 | self.optimizer.minimize(self.sess, 297 | feed_dict=tf_dict, 298 | fetches=[self.loss_f, self.loss_Neumann, self.loss_Dirichlet, self.loss], 299 | loss_callback=self.callback) 300 | 301 | def predict(self, x_star, y_star): 302 | u_star = self.sess.run(self.u_pred, {self.x_c_tf: x_star, self.y_c_tf: y_star}) 303 | v_star = self.sess.run(self.v_pred, {self.x_c_tf: x_star, self.y_c_tf: y_star}) 304 | 305 | s11_star = self.sess.run(self.s11_pred, {self.x_c_tf: x_star, self.y_c_tf: y_star}) 306 | s22_star = self.sess.run(self.s22_pred, {self.x_c_tf: x_star, self.y_c_tf: y_star}) 307 | s12_star = self.sess.run(self.s12_pred, {self.x_c_tf: x_star, self.y_c_tf: y_star}) 308 | return u_star, v_star, s11_star, s22_star, s12_star 309 | 310 | def plot_loss(self,savepath="./drive/MyDrive/PINN"): 311 | 312 | fig = plt.figure() 313 | ax = fig.add_subplot(1,1,1) 314 | line1, = plt.plot(self.loss_rec, label="Total loss", linestyle='-') 315 | line2, = plt.plot(self.loss_Dir, label="Dirichlet", linestyle='-.') 316 | line3, = plt.plot(self.loss_Neu, label="Neumman", linestyle=':') 317 | line4, = plt.plot(self.loss_gov, label="Governing equation", linestyle='--') 318 | plt.xlabel("Iteration") 319 | plt.ylabel("Loss") 320 | plt.yscale('log') 321 | plt.legend() 322 | plt.savefig(savepath+'/loss.png', dpi=300) 323 | plt.show() 324 | 325 | def bodyf(lamd, mu, Q, x, y): 326 | # body force 327 | Pi = np.pi 328 | b1=lamd*(4*Pi**2*np.multiply(np.cos(2*Pi*x),np.sin(Pi*y))-Q*Pi*np.multiply(np.cos(Pi*x),np.power(y,3)))\ 329 | +mu*(9.*Pi**2*np.multiply(np.cos(2*Pi*x),np.sin(Pi*y))-Q*Pi*np.multiply(np.cos(Pi*x),np.power(y,3))) 330 | b2=lamd*(-3*Q*np.multiply(np.sin(Pi*x),np.power(y,2))+2*Pi**2.*np.multiply(np.sin(2*Pi*x),np.cos(Pi*y)))\ 331 | +mu*(-6.*Q*np.multiply(np.sin(Pi*x),np.power(y,2))+2*Pi**2.*np.multiply(np.sin(2*Pi*x),np.cos(Pi*y))\ 332 | +Q*Pi**2.*np.multiply(np.sin(Pi*x),np.power(y,4))/4.) 333 | return b1, b2 334 | 335 | def analytical_soln(lamd, mu, Q, xx, yy): 336 | # analytical solution 337 | Pi = np.pi 338 | u = np.multiply(np.cos(2*Pi*xx),np.sin(Pi*yy)) 339 | v = Q*np.multiply(np.sin(Pi*xx),np.power(yy,4))/4 340 | sxx = lamd*(Q*np.multiply(np.sin(np.pi*xx),np.power(yy,3))-2*Pi*np.multiply(np.sin(2*Pi*xx),np.sin(Pi*yy)))\ 341 | -4*mu*Pi*np.multiply(np.sin(2*Pi*xx),np.sin(Pi*yy)) 342 | syy = lamd*(Q*np.multiply(np.sin(np.pi*xx),np.power(yy,3))-2*Pi*np.multiply(np.sin(2*Pi*xx),np.sin(Pi*yy)))\ 343 | +2*mu*Q*np.multiply(np.sin(Pi*xx),np.power(yy,3)) 344 | sxy = mu*(np.multiply(np.cos(np.pi*xx),np.power(yy,4))*Pi*Q/4+Pi*np.multiply(np.cos(2*Pi*xx),np.cos(Pi*yy))) 345 | 346 | return u, v, sxx, syy, sxy 347 | 348 | def postProcess_field( field_anal, field_PINN, marksize = 2, alpha=0.8, marker='o',savepath="./drive/MyDrive/PINN"): 349 | 350 | xmin = 0 351 | xmax = 1 352 | ymin = 0 353 | ymax = 1 354 | [x_anal, y_anal, u_anal, v_anal, sxx_anal, syy_anal, sxy_anal ] = field_anal 355 | [x_PINN, y_PINN, u_PINN, v_PINN, sxx_PINN, syy_PINN, sxy_PINN ] = field_PINN 356 | normuv = matplotlib.colors.Normalize(vmin=-0.8, vmax=0.8) 357 | normsigma = matplotlib.colors.Normalize(vmin=-10, vmax=10) 358 | 359 | # plot PINN results 360 | fig11, ax11 = plt.subplots() 361 | ax11.set_aspect('equal') 362 | cp = ax11.scatter(x_PINN, y_PINN, c=u_PINN, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-0.8, vmax=0.8) 363 | ax11.set_xticks([]) 364 | ax11.set_yticks([]) 365 | ax11.set_xlim([xmin, xmax]) 366 | ax11.set_ylim([ymin, ymax]) 367 | ax11.set_xlabel("x (m)") 368 | ax11.set_ylabel("y (m)") 369 | plt.title('PINN u $(m)$') 370 | fig11.colorbar(cp) 371 | plt.savefig(savepath+'/u_PINN.png', dpi=300) 372 | plt.show() 373 | 374 | fig21, ax21 = plt.subplots() 375 | ax21.set_aspect('equal') 376 | cp = ax21.scatter(x_PINN, y_PINN, c=v_PINN, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=0, vmax=0.8) 377 | ax21.set_xticks([]) 378 | ax21.set_yticks([]) 379 | ax21.set_xlim([xmin, xmax]) 380 | ax21.set_ylim([ymin, ymax]) 381 | ax21.set_xlabel("x (m)") 382 | ax21.set_ylabel("y (m)") 383 | plt.title('PINN v $(m)$') 384 | fig21.colorbar(cp) 385 | plt.savefig(savepath+'/v_PINN.png', dpi=300) 386 | plt.show() 387 | 388 | fig31, ax31 = plt.subplots() 389 | ax31.set_aspect('equal') 390 | cp = ax31.scatter(x_PINN, y_PINN, c=sxx_PINN, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-10, vmax=10) 391 | ax31.set_xticks([]) 392 | ax31.set_yticks([]) 393 | ax31.set_xlim([xmin, xmax]) 394 | ax31.set_ylim([ymin, ymax]) 395 | ax31.set_xlabel("x (m)") 396 | ax31.set_ylabel("y (m)") 397 | plt.title('PINN $\sigma_{xx}\hspace{0.2}(N/m^2$)') 398 | fig31.colorbar(cp) 399 | plt.savefig(savepath+'/sxx_PINN.png', dpi=300) 400 | plt.show() 401 | 402 | fig41, ax41 = plt.subplots() 403 | ax41.set_aspect('equal') 404 | cp = ax41.scatter(x_PINN, y_PINN, c=syy_PINN, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-6, vmax=8) 405 | ax41.set_xticks([]) 406 | ax41.set_yticks([]) 407 | ax41.set_xlim([xmin, xmax]) 408 | ax41.set_ylim([ymin, ymax]) 409 | ax41.set_xlabel("x (m)") 410 | ax41.set_ylabel("y (m)") 411 | plt.title('PINN $\sigma_{yy}\hspace{0.2}(N/m^2$)') 412 | fig41.colorbar(cp) 413 | plt.savefig(savepath+'/syy_PINN.png', dpi=300) 414 | plt.show() 415 | 416 | fig51, ax51 = plt.subplots() 417 | ax51.set_aspect('equal') 418 | cp = ax51.scatter(x_PINN, y_PINN, c=sxy_PINN, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-3, vmax=3) 419 | ax51.set_xticks([]) 420 | ax51.set_yticks([]) 421 | ax51.set_xlim([xmin, xmax]) 422 | ax51.set_ylim([ymin, ymax]) 423 | ax51.set_xlabel("x (m)") 424 | ax51.set_ylabel("y (m)") 425 | plt.title('PINN $\sigma_{xy}\hspace{0.2}(N/m^2$)') 426 | fig51.colorbar(cp) 427 | plt.savefig(savepath+'/sxy_PINN.png', dpi=300) 428 | plt.show() 429 | 430 | # plot analytical results 431 | fig12, ax12 = plt.subplots() 432 | ax12.set_aspect('equal') 433 | cp = ax12.scatter(x_anal, y_anal, c=u_anal, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-0.8, vmax=0.8) 434 | ax12.set_xticks([]) 435 | ax12.set_yticks([]) 436 | ax12.set_xlim([xmin, xmax]) 437 | ax12.set_ylim([ymin, ymax]) 438 | ax12.set_xlabel("x (m)") 439 | ax12.set_ylabel("y (m)") 440 | plt.title('Analytical u $(m)$') 441 | fig12.colorbar(cp) 442 | plt.savefig(savepath+'/u_anal.png', dpi=300) 443 | plt.show() 444 | 445 | fig22, ax22 = plt.subplots() 446 | ax22.set_aspect('equal') 447 | cp = ax22.scatter(x_anal, y_anal, c=v_anal, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=0, vmax=0.8) 448 | ax22.set_xticks([]) 449 | ax22.set_yticks([]) 450 | ax22.set_xlim([xmin, xmax]) 451 | ax22.set_ylim([ymin, ymax]) 452 | ax22.set_xlabel("x (m)") 453 | ax22.set_ylabel("y (m)") 454 | plt.title('Analytical v $(m)$') 455 | fig22.colorbar(cp) 456 | plt.savefig(savepath+'/v_anal.png', dpi=300) 457 | plt.show() 458 | 459 | fig32, ax32 = plt.subplots() 460 | ax32.set_aspect('equal') 461 | cp = ax32.scatter(x_anal, y_anal, c=sxx_anal, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-10, vmax=10) 462 | ax32.set_xticks([]) 463 | ax32.set_yticks([]) 464 | ax32.set_xlim([xmin, xmax]) 465 | ax32.set_ylim([ymin, ymax]) 466 | ax32.set_xlabel("x (m)") 467 | ax32.set_ylabel("y (m)") 468 | plt.title('Analytical $\sigma_{xx}\hspace{0.2}(N/m^2$)') 469 | fig32.colorbar(cp) 470 | plt.savefig(savepath+'/sxx_anal.png', dpi=300) 471 | plt.show() 472 | 473 | fig42, ax42 = plt.subplots() 474 | ax42.set_aspect('equal') 475 | cp = ax42.scatter(x_anal, y_anal, c=syy_anal, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-6, vmax=8) 476 | ax42.set_xticks([]) 477 | ax42.set_yticks([]) 478 | ax42.set_xlim([xmin, xmax]) 479 | ax42.set_ylim([ymin, ymax]) 480 | ax42.set_xlabel("x (m)") 481 | ax42.set_ylabel("y (m)") 482 | plt.title('Analytical $\sigma_{yy}\hspace{0.2}(N/m^2$)') 483 | fig42.colorbar(cp) 484 | plt.savefig(savepath+'/syy_anal.png', dpi=300) 485 | plt.show() 486 | 487 | fig52, ax52 = plt.subplots() 488 | ax52.set_aspect('equal') 489 | cp = ax52.scatter(x_anal, y_anal, c=sxy_anal, alpha=alpha-0.1, edgecolors='none', cmap='rainbow', marker=marker, s=int(marksize), vmin=-3, vmax=3) 490 | ax52.set_xticks([]) 491 | ax52.set_yticks([]) 492 | ax52.set_xlim([xmin, xmax]) 493 | ax52.set_ylim([ymin, ymax]) 494 | ax52.set_xlabel("x (m)") 495 | ax52.set_ylabel("y (m)") 496 | plt.title('Analytical $\sigma_{xy}\hspace{0.2}(N/m^2$)') 497 | fig52.colorbar(cp) 498 | plt.savefig(savepath+'/sxy_anal.png', dpi=300) 499 | plt.show() 500 | 501 | plt.close('all') 502 | 503 | if __name__ == "__main__": 504 | 505 | lamd = 1 506 | mu = 0.5 507 | Q = 4 508 | 509 | # Network configuration 510 | # output u,v,sigxx,sigyy,sigxy 511 | uv_layers = [2] + 10*[40] + [5] 512 | 513 | # number of domain training samples 514 | num_dom_train_samples = 200 515 | # number of boundary training samples 516 | num_b_train_samples = 300 517 | 518 | # collocation points in the domain 519 | XY_c1 = np.linspace(0.0, 1.0, num=num_dom_train_samples) # x = 0 ~ +1 520 | XY_c2 = np.linspace(0.0, 1.0, num=num_dom_train_samples) # y = 0 ~ +1 521 | XY_c1, XY_c2 = np.meshgrid(XY_c1, XY_c2) 522 | XY_c = np.vstack((XY_c1.flatten(),XY_c2.flatten())).T 523 | 524 | # Neumman BC 525 | xy_left = np.zeros((num_b_train_samples, 2)) 526 | xy_left[..., 0] = np.zeros(num_b_train_samples) # x = 0 527 | xy_left[..., 1] = np.linspace(0.0, 1.0, num=num_b_train_samples) # y = 0 ~ +1 528 | n_left = np.zeros((num_b_train_samples, 2)) 529 | n_left[..., 0] = -1.*np.ones(num_b_train_samples) 530 | n_left[..., 1] = np.zeros(num_b_train_samples) 531 | t_left = np.zeros((num_b_train_samples, 2)) 532 | t_left[..., 0] = np.zeros(num_b_train_samples) 533 | t_left[..., 1] = -mu*np.pi*(np.cos(np.pi*xy_left[..., 1])+np.power(xy_left[..., 1],4)*Q/4) 534 | 535 | xy_top = np.zeros((num_b_train_samples, 2)) 536 | xy_top[..., 0] = np.linspace(0.0, 1.0, num=num_b_train_samples) # x = 0 ~ +1 537 | xy_top[..., 1] = np.ones(num_b_train_samples) # y = 0 538 | n_top = np.zeros((num_b_train_samples, 2)) 539 | n_top[..., 0] = np.zeros(num_b_train_samples) 540 | n_top[..., 1] = np.ones(num_b_train_samples) 541 | t_top = np.zeros((num_b_train_samples, 2)) 542 | t_top[..., 0] = mu*np.pi*(-np.cos(2*np.pi*xy_top[..., 0])+np.cos(np.pi*xy_top[..., 0])*Q/4) 543 | t_top[..., 1] = (lamd+2*mu)*Q*np.sin(np.pi*xy_top[..., 0]) 544 | 545 | xy_right = np.zeros((num_b_train_samples, 2)) 546 | xy_right[..., 0] = np.ones(num_b_train_samples) # x = 1 547 | xy_right[..., 1] = np.linspace(0.0, 1.0, num=num_b_train_samples) # y = 0 ~ +1 548 | n_right = np.zeros((num_b_train_samples, 2)) 549 | n_right[..., 0] = np.ones(num_b_train_samples) 550 | n_right[..., 1] = np.zeros(num_b_train_samples) 551 | t_right = np.zeros((num_b_train_samples, 2)) 552 | t_right[..., 0] = np.zeros(num_b_train_samples) 553 | t_right[..., 1] = mu*np.pi*(np.cos(np.pi*xy_right[..., 1])-np.power(xy_right[..., 1],4)*Q/4) 554 | 555 | xy_NeumannBC = np.concatenate((xy_left, xy_top, xy_right), 0) 556 | n_NeumannBC = np.concatenate((n_left, n_top, n_right), 0) 557 | t_NeumannBC = np.concatenate((t_left, t_top, t_right), 0) 558 | 559 | # Dirichelet BC 560 | xy_bottom = np.zeros((num_b_train_samples, 2)) 561 | xy_bottom[..., 0] = np.linspace(0.0, 1.0, num=num_b_train_samples) # x = 0 ~ +1 562 | xy_bottom[..., 1] = np.zeros(num_b_train_samples) # y = 1 563 | xy_DirichletBC = xy_bottom 564 | 565 | XY_c = np.concatenate((XY_c, xy_NeumannBC, xy_DirichletBC), 0) 566 | 567 | bf = np.zeros(XY_c.shape) 568 | bf[..., 0], bf[..., 1] = bodyf(lamd, mu, Q, XY_c[..., 0], XY_c[..., 1]) 569 | 570 | # Visualize the collocation points 571 | fig, ax = plt.subplots() 572 | ax.set_aspect('equal') 573 | plt.scatter(XY_c[:,0:1], XY_c[:,1:2], marker='o', alpha=0.1 ,color='blue') 574 | plt.scatter(xy_NeumannBC[:,0:1], xy_NeumannBC[:,1:2], marker='o', alpha=0.2 , color='black') 575 | plt.scatter(xy_DirichletBC[:, 0:1], xy_DirichletBC[:, 1:2], marker='o', alpha=0.2, color='orange') 576 | plt.show() 577 | 578 | with tf.device('/device:GPU:0'): 579 | config = tf.ConfigProto() 580 | config.gpu_options.allow_growth = True 581 | session = tf.Session(config=config) 582 | 583 | # Train nn model or load trained model 584 | model = PINN_elasticity(lamd, mu, Q, XY_c, xy_DirichletBC, xy_NeumannBC, n_NeumannBC, t_NeumannBC, bf, uv_layers) 585 | 586 | start_time = time.time() 587 | loss_f, loss_Dirichlet, loss_Neumann, loss = model.train(iter=10000, learning_rate=1e-3) 588 | # postProcess_field( field_anal, field_PINN, marksize=2, alpha=0.8, marker='o',savepath="./drive/MyDrive/PINN") 589 | 590 | # Save neural network 591 | model.save_NN('./drive/MyDrive/PINN/NN_Adam.pickle') 592 | 593 | num_PINN = 200 594 | x_PINN = np.linspace(0, 1, num_PINN) 595 | y_PINN = np.linspace(0, 1, num_PINN) 596 | x_PINN, y_PINN = np.meshgrid(x_PINN, y_PINN) 597 | x_PINN = x_PINN.flatten()[:, None] 598 | y_PINN = y_PINN.flatten()[:, None] 599 | u_PINN, v_PINN, s11_PINN, s22_PINN, s12_PINN = model.predict(x_PINN, y_PINN) 600 | field_PINN = [x_PINN, y_PINN, u_PINN, v_PINN, s11_PINN, s22_PINN, s12_PINN] 601 | 602 | u_anal, v_anal, sigxx_anal, sigyy_anal, sigxy_anal = analytical_soln(lamd, mu, Q, x_PINN, y_PINN) 603 | field_anal = [x_PINN, y_PINN, u_anal, v_anal, sigxx_anal, sigyy_anal, sigxy_anal] 604 | 605 | model.train_bfgs() 606 | u_PINN, v_PINN, s11_PINN, s22_PINN, s12_PINN = model.predict(x_PINN, y_PINN) 607 | field_PINN = [x_PINN, y_PINN, u_PINN, v_PINN, s11_PINN, s22_PINN, s12_PINN] 608 | # Save neural network after bfgs training 609 | model.save_NN('./drive/MyDrive/PINN/NN_Adam_bfgs.pickle') 610 | print("--- %s seconds ---" % (time.time() - start_time)) 611 | 612 | model.plot_loss() 613 | 614 | postProcess_field( field_anal, field_PINN, marksize=2, alpha=0.8, marker='o',savepath="./drive/MyDrive/PINN") 615 | 616 | # Save loss history 617 | with open('./drive/MyDrive/PINN/loss_history.pickle', 'wb') as f: 618 | pickle.dump([model.loss_rec, model.loss_Dir, model.loss_Neu, model.loss_gov], f) -------------------------------------------------------------------------------- /output/NN_Adam.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/NN_Adam.pickle -------------------------------------------------------------------------------- /output/NN_Adam_bfgs.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/NN_Adam_bfgs.pickle -------------------------------------------------------------------------------- /output/loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/loss.png -------------------------------------------------------------------------------- /output/loss_history.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/loss_history.pickle -------------------------------------------------------------------------------- /output/sxx_PINN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/sxx_PINN.png -------------------------------------------------------------------------------- /output/sxx_anal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/sxx_anal.png -------------------------------------------------------------------------------- /output/sxy_PINN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/sxy_PINN.png -------------------------------------------------------------------------------- /output/sxy_anal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/sxy_anal.png -------------------------------------------------------------------------------- /output/syy_PINN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/syy_PINN.png -------------------------------------------------------------------------------- /output/syy_anal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/syy_anal.png -------------------------------------------------------------------------------- /output/u_PINN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/u_PINN.png -------------------------------------------------------------------------------- /output/u_anal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/u_anal.png -------------------------------------------------------------------------------- /output/v_PINN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/v_PINN.png -------------------------------------------------------------------------------- /output/v_anal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JINGHU-UT/PINN-elasticity/1b5d3a8d3aee3a2f65129ac7161b29378aaf5d50/output/v_anal.png --------------------------------------------------------------------------------