def _check_params(self): """Check parameters and set defaults """ if self._params[self.E] < 0.: raise WasatchError("Young's modulus E must be > 0") if not -1 < self._params[self.NU] < .5: raise WasatchError("Poisson's ratio NU out of bounds") return
def reduce_map(i, j, ncoord, ndof=None): """Mapping from higher order tensor space to lower order Parameters ---------- ij : tuple Indeces in the higher-order tensor space (1, 1) ij[0] -> ith index ij[1] -> jth index Returns ------- I : int Index in the lower-order tensor space Comments -------- Maps according to: 2D 3D 00 -> 0 00 -> 0 11 -> 1 11 -> 1 22 -> 2 01 -> 2 01 -> 3 12 -> 4 02 -> 5 """ ij = (i, j) if ndof is None: ndof = ncoord if ndof != ncoord: raise WasatchError("NDOF must equal NCOORD (for now)") # sort the indices ij = sorted(ij) if ij[0] == ij[1]: return ij[0] if ncoord == 2: if any(i > 1 for i in ij): raise WasatchError("Bad index for 2D mapping: {0}".format( repr(ij))) return 2 if ij == [0, 1]: return 3 elif ij == [1, 2]: return 4 elif ij == [0, 2]: return 5 raise WasatchError("Bad index for 3D mapping: {0}".format(repr(ij)))
def register_variable(self, var, vtype="SCALAR"): """Register element variable """ vtype = vtype.upper() var = var.upper() if vtype == "SCALAR": self._variables.append(var) elif vtype == "TENS": self._variables.extend([ "{0}-{1}".format(var, x) for x in ("XX", "XY", "XZ", "YX", "YY", "YZ", "ZX", "ZY", "ZZ") ]) elif vtype == "SYMTENS": self._variables.extend([ "{0}-{1}".format(var, x) for x in ("XX", "YY", "ZZ", "XY", "YZ", "XZ") ]) elif vtype == "SKEWTENS": self._variables.extend( ["{0}-{1}".format(var, x) for x in ("XY", "YZ", "XZ")]) else: raise WasatchError("{0}: unrecognized vtype".format(vtype))
def numberofintegrationpoints(ncoord, nelnodes, elident): """Return the number of integration points for each element type Parameters ---------- ncoord : int No. spatial coords (2 for 2D, 3 for 3D) nelnodes : int No. nodes on the element elident : int Element identifier Returns ------- n : int Number of integration points on element """ n = None if ncoord == 1: n = nelnodes elif ncoord == 2: if nelnodes == 3: n = 1 elif nelnodes == 6: n = 3 elif nelnodes == 4: n = 4 elif nelnodes == 8: n = 9 elif ncoord == 3: if nelnodes == 4: n = 1 elif nelnodes == 10: n = 4 elif nelnodes == 8: n = 8 elif nelnodes == 20: n = 27 if n is None: raise WasatchError( "error: {0}D element with {1} nodes not supported".format( ncoord, nelnodes)) return n
def parse_input_parameters(self, pdict): self.pdict = pdict for name, val in pdict.items(): i = self.param_map.get(name.upper()) if i is None: if cfg.debug: raise WasatchError( "{0}: {1}: unrecognized parameter".format( self.name, name)) continue self._params[i] = val
def element_data(self, ave=False, exo=False, item=None): """Return the current element data Returns ------- data : array_like Element data """ a, b = 0, self.ndat if item is not None: # Get the index of item try: a, b = DATMAP.get(item.upper()) except TypeError: raise WasatchError("{0}: not in element data".format(item)) if b == -1: b = self.ndat if ave or exo: return self.data[0, self.ngauss][a:b] return self.data[0, :, a:b]
def __init__(self, lmnid, nodes, coords, material, mat_params, perturbed, *args, **kwargs): import src.fem.core.fe_model as fem # Find parent and child elements parent = kwargs.get("ParentElement") if parent is None: raise UserInputError("RVE element expected a parent element") parent = DEFAULT_ELEMENTS.get(parent.upper()) if parent not in (Quad4, ): raise WasatchError("RVE requires Quad4 parent element") self.name = parent.name # The analysis driver analysis_driver = kwargs.get("AnalysisDriver") if analysis_driver is None: raise UserInputError("No rve driver specified for RVE") analysis_driver = analysis_driver.upper() if analysis_driver not in ("WASATCH", ): raise UserInputError("{0}: unsupported rve driver") self.analysis_driver = analysis_driver ki = kwargs.get("KeepIntermediateResults") if ki is not None: ki = {"FALSE": 0, "TRUE": 1}.get(ki.upper(), 1) self.wipe_intermediate = not bool(ki) template = kwargs.get("InputTemplate") if template is None: raise UserInputError("RVE element expected an input template") if not os.path.isfile(template): raise UserInputError("{0}: no such template file".format(template)) self.input_template_file = template self.template = open(template, "r").read() # RVE inherits from parent. Is there a better way to do this? self.parent = parent(lmnid, nodes, coords, material, mat_params, perturbed, *args, **kwargs) self.bndry = self.parent.bndry self.nnodes = self.parent.nnodes self.ncoord = self.parent.ncoord self.ndof = self.parent.ndof self.dim = self.parent.dim self.ngauss = self.parent.ngauss self.cornernodes = self.parent.cornernodes self.facenodes = self.parent.facenodes self.gauss_coords = self.parent.gauss_coords self.gauss_weights = self.parent.gauss_weights # Find the refinement level and form the child mesh self.refinement = kwargs.get("Refinement", 10) self.generate_child_mesh_info(coords) self.end_state = None super(RVE, self).__init__(lmnid, nodes, coords, material, mat_params, perturbed, *args, **kwargs) # setup FEModel object for the entire RVE simulation wasatch_input = self.generate_wasatch_input(0., np.zeros((self.nnodes, 3))) runid = "{0}_EID={1}_RVE".format(kwargs["RUNID"], lmnid) self.rve_model = fem.FEModel.from_input_string(runid, wasatch_input, rundir=os.getcwd())
def volume(self, coords): raise WasatchError("Element {0} must define volume".format(self.name))
def main(argv=None): if argv is None: argv = sys.argv[1:] parser = argparse.ArgumentParser() parser.add_argument("file") parser.add_argument("--chk-mesh", default=False, action="store_true", help="Stop to check mesh before running simulation [default: %(default)s]") parser.add_argument("--piecewise", default=False, action="store_true", help="""Print the piecewise solution as a function of x [default: %(default)s""") parser.add_argument("--dbg", default=False, action="store_true", help="Debug mode [default: %(default)s]") parser.add_argument("--sqa", default=False, action="store_true", help="SQA mode [default: %(default)s]") parser.add_argument("-v", default=None, type=int, help="Verbosity [default: %(default)s]") parser.add_argument("--wm", default=False, action="store_true", help="Write mesh to ascii file [default: %(default)s]") parser.add_argument("-j", default=1, type=int, help="Number of proccesses to run simultaneously [default: %(default)s]") parser.add_argument("-E", default=False, action="store_true", help="Write exodus file [default: %(default)s]") parser.add_argument("--clean", default=False, action="store_true", help="Clean simulation output [default: %(default)s]") parser.add_argument("--cleanall", default=False, action="store_true", help="Clean all simulation output [default: %(default)s]") parser.add_argument("--profile", default=False, action="store_true", help="Run the simulation in a profiler [default: %(default)s]") parser.add_argument("-d", nargs="?", default=None, const="_RUNID_", help="Directory to run analysis [default: %(default)s]") args = parser.parse_args(argv) if args.profile: raise WasatchError("Profiling must be run from __main__") # set some simulation wide configurations ro.debug = args.dbg ro.sqa = args.sqa ro.verbosity = args.v if not cfg.exodus: cfg.exodus = args.E if args.clean or args.cleanall: from src.base.utilities import clean_wasatch clean_wasatch(os.path.splitext(args.file)[0], args.cleanall) return 0 ti = time.time() infile = args.file if args.d is not None: fdir, fname = os.path.split(os.path.realpath(args.file)) if args.d == "_RUNID_": d = os.path.join(fdir, os.path.splitext(fname)[0]) else: d = os.path.realpath(args.d) try: os.makedirs(d) except OSError: pass infile = os.path.join(d, fname) try: os.remove(infile) except: pass shutil.copyfile(args.file, infile) os.chdir(d) fe_model = fem.FEModel.from_input_file(infile, verbosity=args.v) if args.wm: fe_model.mesh.write_ascii(fe_model.runid) if args.chk_mesh: fe_model.logger.write("See {0} for initial mesh".format( fe_model.runid + ".exo")) resp = raw_input("Continue with simulation? (Y/N) [N]: ") stop = not {"Y": True}.get(resp.upper().strip(), False) if stop: return 0 try: retval = fe_model.solve(nproc=args.j) except KeyboardInterrupt: sys.stderr.write("\nKeyboard interrupt\n") retval = -1 tf = time.time() fe_model.logger.write("wasatch: total simulation time: {0:.2f}s".format( tf - ti)) fe_model.logger.close() 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 exo_io = self.io 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 findstiff = True self.logger.write( intro("Implicit", runid, nsteps, tol, maxit, relax, tstart, tterm, ndof, nelems, nnode, elements)) for step in range(nsteps): loadfactor = float(step + 1) / float(nsteps) err1 = 1. t += dt self.logger.write("Step {0:.5f} Load factor {1:.5f}, " "Time: {2}, Time step: {3}".format( step + 1, loadfactor, 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 = utils.linsolve(K, b) if info > 0: self.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) self.logger.write("Iteration number {0}: " "Correction {1} " "Residual {2} " "tolerance {3}".format(nit, err1, err2, tol), beg=" ") 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() if element.rve_model: element.dump_rve_end_state() if exo_io: self.dump_time_step_data(t, dt, u) continue if exo_io: exo_io.finish() tf = time.time() self.logger.write("\n{0}: execution finished".format(self.runid)) self.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 brick_mesh(xpoints, ypoints, zpoints=None, test=False): """Generate a [2,3]D block mesh. Parameters ---------- xpoints : array_like Points defining the x-axis from x0 to xf ypoints : array_like Points defining the y-axis from y0 to yf zpoints : array_like [optional] Points defining the z-axis from z0 to zf Returns ------- coords : array_like, (i, j) Nodal coordinates coords[i, j] -> jth coordinate of ith node conn : array_like, (i, j) nodal connectivity conn[i, j] -> jth node of the ith element """ dim = 2 if zpoints is None else 3 if dim == 3: raise WasatchError("3D inline mesh not done") shape = [ xpoints.size, ypoints.size, ] if dim == 3: shape.append(zpoints.size) shape = np.array(shape, dtype=np.int) nnode = np.prod(shape) nel = np.prod(shape - 1) # Nodal coordinates if dim == 3: coords = [[x, y, z] for z in zpoints for y in ypoints for x in xpoints] else: coords = [(x, y, 0) for y in ypoints for x in xpoints] coords = np.array(coords, dtype=np.float64) # Connectivity if dim == 2: row = 0 conn = np.zeros((nel, 4), dtype=np.int) nelx = xpoints.size - 1 for lmn in range(nel): ii = lmn + row conn[lmn, :] = [ii, ii + 1, ii + nelx + 2, ii + nelx + 1] if (lmn + 1) % (nelx) == 0: row += 1 continue else: grid = np.zeros(shape, dtype=np.int) for ii, ic in enumerate(_cycle(shape)): grid[tuple(ic)] = ii conn = np.zeros((nel, 8), dtype=np.int) for ii, (ix, iy, iz) in enumerate(_cycle(shape - 1)): conn[ii, :] = [ grid[ix, iy, iz], grid[ix + 1, iy, iz], grid[ix + 1, iy + 1, iz], grid[ix, iy + 1, iz], grid[ix, iy, iz + 1], grid[ix + 1, iy, iz + 1], grid[ix + 1, iy + 1, iz + 1], grid[ix, iy + 1, iz + 1] ] return coords, conn
def plane_stress_unit(nel): import src.fem.element.element as el if nel not in (1, 2): raise WasatchError("wrong NEL") # E, nu, Y, e0, n, edot0, m materialprops = np.array([96, .333333, 1.e99, 0., 0., 0., 1.]) ncoord = 2 ndof = 2 def _mesh(n): """ 3 [2] 2 o --------------- o | | | | [3] | | [1] | | | | o --------------- o 0 [0] 1 """ if n == 1: # 1 element coords = np.array([[0., 0.], [2., 0.], [2., 1.], [0., 1.]]) connect = np.array([[0, 1, 2, 3]]) elif n == 2: # 2 elements coords = np.array([[0., 0.], [.5, 0.], [.5, 1.], [0., 1.], [.5, 0.], [1., 0.], [1., 1.], [.5, 1.]]) connect = np.array([[0, 1, 2, 3], [4, 5, 6, 7]]) return coords, connect coords, connect = _mesh(nel) nelnodes = np.array([len(x != np.nan) for x in connect]) elident = np.array([el.initialize("Quad4") for i in range(len(connect))]) # fixnodes : 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 (function) comp = lambda x: {"x": 0, "y": 1, "z": 2}.get(x.lower()) fixnodes = np.array([[0, comp("x"), lambda t: 0.], [0, comp("y"), lambda t: 0.], [3, comp("x"), lambda t: 0.], [3, comp("y"), lambda t: 0.]]) # dloads : List of element tractions # dloads[i, 0] -> Element number # dloads[i, 1] -> face number # dloads[i, 2], dloads[i, 3], dloads[i, 4] -> Components of traction n = len(elident) - 1 dloads = np.array([[n, 1, 10.e5, 0., 0.]]) # control: nsteps, tol, maxit, relax, tstart, tterm, dtmult control = np.array([10, 1.e-4, 60, .5, 0., 5., 1.]) control = np.array([10, 1.e-4, 30, 1., 0., 5., 1.]) fe_solve(sys.stdout, control, ncoord, ndof, coords, connect, nelnodes, elident, fixnodes, dloads)
def update_state(self, *args, **kwargs): raise WasatchError("update_state must be provided by model")
def setup(self, pdict): raise WasatchError("setup must be provided by model")