def fe_solve(exo_io, runid, control, X, connect, elements, fixnodes, tractions, nforces, nproc=1): """ 2D and 3D Finite Element Code Currently configured to run either plane strain in 2D or general 3D but could easily be modified for plane stress or axisymmetry. Parameters ---------- control : array_like, (i,) control[0] -> time integration scheme control[1] -> number of steps control[2] -> Newton tolerance control[3] -> maximum Newton iterations control[4] -> relax control[5] -> starting time control[6] -> termination time control[7] -> time step multiplier X : array like, (i, j,) Nodal coordinates X[i, j] -> jth coordinate of ith node for i=1...nnode, j=1...ncoord connect : array_like, (i, j,) Nodal connections connect[i, j] -> jth node on on the ith element elements : array_like, (i,) Element class for each element fixnodes : array_like, (i, j,) List of prescribed displacements at nodes fixnodes[i, 0] -> Node number fixnodes[i, 1] -> Displacement component (x: 0, y: 1, or z: 2) fixnodes[i, 2] -> Value of the displacement tractions : array_like, (i, j,) List of element tractions tractions[i, 0] -> Element number tractions[i, 1] -> face number tractions[i, 2:] -> Components of traction as a function of time Returns ------- retval : init 0 on completion failure otherwise Notes ----- Original code was adapted from [1]. References ---------- 1. solidmechanics.org """ # Local Variables # --------------- # du : array_like, (i,) # Nodal displacements. # Let wij be jth displacement component at ith node. Then du # contains [w00, w01, w10, w11, ...] for 2D # and [w00, w01, w02, w10, w11, w12, ...) for 3D # dw : array_like, (i,) # Correction to nodal displacements. # K : array_like, (i, j,) # Global stiffness matrix. Stored as # [K_1111 K_1112 K_1121 K_1122... # K_1211 K_1212 K_1221 K_1222... # K_2111 K_2112 K_2121 K_2122...] # for 2D problems and similarly for 3D problems # F : array_like, (i, ) # Force vector. # Currently only includes contribution from tractions acting on # element faces (body forces are neglected) # R : array_like, (i, ) # Volume contribution to residual # b : array_like (i, ) # RHS of equation system # number of processors nproc = np.amin([mp.cpu_count(), nproc, elements.size]) # Set up timing and logging t0 = time.time() logger = Logger(runid) # Problem dimensions dim = elements[0].ndof nelems = elements.shape[0] nnode = X.shape[0] ndof = elements[0].ndof ncoord = elements[0].ncoord # Setup kinematic variables u = np.zeros((2, nnode * ndof)) v = np.zeros((2, nnode * ndof)) a = np.zeros((2, nnode * ndof)) # Simulation setup tint, nsteps, tol, maxit, relax, tstart, tterm, dtmult = control nsteps, maxit = int(nsteps), int(maxit) t = tstart dt = (tterm - tstart) / float(nsteps) * dtmult # Newmark parameters b = [.5, 0.] # Global mass, find only once M = global_mass(X, elements, connect, nproc) # Determine initial accelerations du = np.diff(u, axis=0)[0] K = global_stiffness(0., 1., X, elements, connect, du, nproc) findstiff = False F = global_traction( 0., 1., X, elements, connect, tractions, nforces, du) apply_dirichlet_bcs(ndof, nnode, 0., fixnodes, u[0], du, 1., K, F, M) a[0] = np.linalg.solve(M, -np.dot(K, u[0]) + F) logger.write_intro("Explicit", runid, nsteps, tol, maxit, relax, tstart, tterm, ndof, nelems, nnode)) for step in range(nsteps): err1 = 1. t += dt logger.write( "Step {0:.5f}, Time: {1}, Time step: {2}".format(step + 1, t, dt)) # --- Update the state of each element to end of step du = np.diff(u, axis=0)[0] update_element_states(dt, X, elements, connect, du) # --- Get global quantities K = global_stiffness( t, dt, X, elements, connect, du, nproc)
def solve(self, nproc=1, disp=0): """ 2D and 3D Finite Element Code Currently configured to run either plane strain in 2D or general 3D but could easily be modified for plane stress or axisymmetry. """ # Local Variables # --------------- # du : array_like, (i,) # Nodal displacements. # Let wij be jth displacement component at ith node. Then du # contains [w00, w01, w10, w11, ...] for 2D # and [w00, w01, w02, w10, w11, w12, ...) for 3D # dw : array_like, (i,) # Correction to nodal displacements. # K : array_like, (i, j,) # Global stiffness matrix. Stored as # [K_1111 K_1112 K_1121 K_1122... # K_1211 K_1212 K_1221 K_1222... # K_2111 K_2112 K_2121 K_2122...] # for 2D problems and similarly for 3D problems # F : array_like, (i, ) # Force vector. # Currently only includes contribution from tractions acting on # element faces (body forces are neglected) # R : array_like, (i, ) # Volume contribution to residual # b : array_like (i, ) # RHS of equation system runid = self.runid control = self.control_params() X = self.mesh.nodes() connect = self.mesh.connect() elements = self.mesh.elements() fixnodes = self.mesh.displacement_bcs() nforces = self.mesh.nodal_forces() tractions = self.mesh.traction_bcs() t0 = time.time() dim = elements[0].ndof nelems = elements.shape[0] nnode = X.shape[0] ndof = elements[0].ndof ncoord = elements[0].ncoord u = np.zeros((nnode * ndof)) du = np.zeros((nnode * ndof)) nodal_stresses = np.zeros((nnode, 6)) # tjf: nodal_state will have to be adjusted for multi-material where # each node may be connected to elements of different material. # nodal_state = np.zeros((nnode, max(el.material.nxtra for el in elements))) nproc = 1. # Simulation setup (tint, nsteps, tol, maxit, relax, tstart, tterm, dtmult, verbosity) = control nsteps, maxit = int(nsteps), int(maxit) t = tstart dt = (tterm - tstart) / float(nsteps) * dtmult global_data.set_var("TIME", t) global_data.set_var("TIME_STEP", dt) nodal_data.set_var("DISPL", u) findstiff = True logger.write_intro("Implicit", runid, nsteps, tol, maxit, relax, tstart, tterm, ndof, nelems, nnode, elements) logger.write(HEAD) for step in range(nsteps): loadfactor = float(step + 1) / float(nsteps) err1 = 1. t += dt # Newton-Raphson loop mult = 10 if step == 0 and ro.reducedint else 1 for nit in range(mult * maxit): # --- Update the state of each element to end of Newton step update_element_states(t, dt, X, elements, connect, u, du) # --- Update nodal stresses for (inode, els) in enumerate(self.mesh._node_el_map): sig = np.zeros(6) # nx = elements[els[0]].material.nxtra # xtra = np.zeros(nx) acc_volume = 0. for iel in els: if iel == -1: break el = elements[iel] vol = el._volume sig += el.element_data(ave=True, item="STRESS") * vol # xtra += el.element_data(ave=True, item="XTRA") * vol acc_volume += vol nodal_stresses[inode][:] = sig / acc_volume # nodal_state[inode][0:nx] = xtra / acc_volume continue # --- Get global quantities if findstiff: gK = global_stiffness(t, dt, X, elements, connect, du, nproc) findstiff = False K = np.array(gK) F = global_traction(t, dt, X, elements, connect, tractions, nforces, du) R = global_residual(t, dt, X, elements, connect, du) b = loadfactor * F - R apply_dirichlet_bcs(ndof, nnode, t, fixnodes, u, du, loadfactor, K, b) # --- Solve for the correction c, dw, info = linsolve(K, b) if info > 0: logger.write("using least squares to solve system", beg="*** ") dw = np.linalg.lstsq(K, b)[0] elif info < 0: raise WasatchError( "illegal value in %d-th argument of internal dposv" % -info) # --- update displacement increment du += relax * dw # --- Check convergence wnorm = np.dot(du, du) err1 = np.dot(dw, dw) if err1 > 1.E-10: findstiff = True if wnorm != 0.: err1 = np.sqrt(err1 / wnorm) err2 = np.sqrt(np.dot(b, b)) / float(ndof * nnode) logger.write_formatted(step+1, nit+1, loadfactor, t, dt, err1, err2, tol, fmt=ITER_FMT) if err1 < tol: break continue else: raise WasatchError("Problem did not converge") # Update the total displacecment u += du # Update the nodal coordinates x = np.zeros((nnode, ndof)) for i in range(nnode): for j in range(ndof): x[i, j] = X[i, j] + u[ndof * i + j] continue continue # Advance the state of each element for element in elements: element.advance_state() global_data.set_var("TIME", t) global_data.set_var("TIME_STEP", dt) nodal_data.set_var("DISPL", u) self.io.write_data_to_db() continue self.io.finish() tf = time.time() logger.write("\n{0}: execution finished".format(self.runid)) logger.write("{0}: total execution time: {1:8.6f}s".format( runid, tf - t0)) retval = 0 if disp: retval = { "DISPLACEMENT": u, "TIME": t, "DT": dt, "ELEMENT DATA": np.array([el.element_data() for el in elements]), "NODAL STRESSES": nodal_stresses, # "NODAL STATES": nodal_state, "NODAL COORDINATES": x} return retval
def solve(self, nproc=1, disp=0): """ 2D and 3D Finite Element Code Currently configured to run either plane strain in 2D or general 3D but could easily be modified for plane stress or axisymmetry. """ # Local Variables # --------------- # du : array_like, (i,) # Nodal displacements. # Let wij be jth displacement component at ith node. Then du # contains [w00, w01, w10, w11, ...] for 2D # and [w00, w01, w02, w10, w11, w12, ...) for 3D # dw : array_like, (i,) # Correction to nodal displacements. # K : array_like, (i, j,) # Global stiffness matrix. Stored as # [K_1111 K_1112 K_1121 K_1122... # K_1211 K_1212 K_1221 K_1222... # K_2111 K_2112 K_2121 K_2122...] # for 2D problems and similarly for 3D problems # F : array_like, (i, ) # Force vector. # Currently only includes contribution from tractions acting on # element faces (body forces are neglected) # R : array_like, (i, ) # Volume contribution to residual # b : array_like (i, ) # RHS of equation system runid = self.runid control = self.control_params() X = self.mesh.nodes() connect = self.mesh.connect() elements = self.mesh.elements() fixnodes = self.mesh.displacement_bcs() nforces = self.mesh.nodal_forces() tractions = self.mesh.traction_bcs() t0 = time.time() dim = elements[0].ndof nelems = elements.shape[0] nnode = X.shape[0] ndof = elements[0].ndof ncoord = elements[0].ncoord u = np.zeros((nnode * ndof)) du = np.zeros((nnode * ndof)) nodal_stresses = np.zeros((nnode, 6)) # tjf: nodal_state will have to be adjusted for multi-material where # each node may be connected to elements of different material. # nodal_state = np.zeros((nnode, max(el.material.nxtra for el in elements))) nproc = 1. # Simulation setup (tint, nsteps, tol, maxit, relax, tstart, tterm, dtmult, verbosity) = control nsteps, maxit = int(nsteps), int(maxit) t = tstart dt = (tterm - tstart) / float(nsteps) * dtmult global_data.set_var("TIME", t) global_data.set_var("TIME_STEP", dt) nodal_data.set_var("DISPL", u) findstiff = True logger.write_intro("Implicit", runid, nsteps, tol, maxit, relax, tstart, tterm, ndof, nelems, nnode, elements) logger.write(HEAD) for step in range(nsteps): loadfactor = float(step + 1) / float(nsteps) err1 = 1. t += dt # Newton-Raphson loop mult = 10 if step == 0 and ro.reducedint else 1 for nit in range(mult * maxit): # --- Update the state of each element to end of Newton step update_element_states(t, dt, X, elements, connect, u, du) # --- Update nodal stresses for (inode, els) in enumerate(self.mesh._node_el_map): sig = np.zeros(6) # nx = elements[els[0]].material.nxtra # xtra = np.zeros(nx) acc_volume = 0. for iel in els: if iel == -1: break el = elements[iel] vol = el._volume sig += el.element_data(ave=True, item="STRESS") * vol # xtra += el.element_data(ave=True, item="XTRA") * vol acc_volume += vol nodal_stresses[inode][:] = sig / acc_volume # nodal_state[inode][0:nx] = xtra / acc_volume continue # --- Get global quantities if findstiff: gK = global_stiffness(t, dt, X, elements, connect, du, nproc) findstiff = False K = np.array(gK) F = global_traction(t, dt, X, elements, connect, tractions, nforces, du) R = global_residual(t, dt, X, elements, connect, du) b = loadfactor * F - R apply_dirichlet_bcs(ndof, nnode, t, fixnodes, u, du, loadfactor, K, b) # --- Solve for the correction c, dw, info = linsolve(K, b) if info > 0: logger.write("using least squares to solve system", beg="*** ") dw = np.linalg.lstsq(K, b)[0] elif info < 0: raise WasatchError( "illegal value in %d-th argument of internal dposv" % -info) # --- update displacement increment du += relax * dw # --- Check convergence wnorm = np.dot(du, du) err1 = np.dot(dw, dw) if err1 > 1.E-10: findstiff = True if wnorm != 0.: err1 = np.sqrt(err1 / wnorm) err2 = np.sqrt(np.dot(b, b)) / float(ndof * nnode) logger.write_formatted(step + 1, nit + 1, loadfactor, t, dt, err1, err2, tol, fmt=ITER_FMT) if err1 < tol: break continue else: raise WasatchError("Problem did not converge") # Update the total displacecment u += du # Update the nodal coordinates x = np.zeros((nnode, ndof)) for i in range(nnode): for j in range(ndof): x[i, j] = X[i, j] + u[ndof * i + j] continue continue # Advance the state of each element for element in elements: element.advance_state() global_data.set_var("TIME", t) global_data.set_var("TIME_STEP", dt) nodal_data.set_var("DISPL", u) self.io.write_data_to_db() continue self.io.finish() tf = time.time() logger.write("\n{0}: execution finished".format(self.runid)) logger.write("{0}: total execution time: {1:8.6f}s".format( runid, tf - t0)) retval = 0 if disp: retval = { "DISPLACEMENT": u, "TIME": t, "DT": dt, "ELEMENT DATA": np.array([el.element_data() for el in elements]), "NODAL STRESSES": nodal_stresses, # "NODAL STATES": nodal_state, "NODAL COORDINATES": x } return retval