def parse_traction_bc(tractions, ssets, coords, connect, elements): """Parse the traction bc. """ trax = [] for (func, scale, sideset_id) in tractions: # now get the actual sideset try: sideset = [x[1] for x in ssets if sideset_id == x[0]][0] except IndexError: raise UserInputError( "Sideset {0} not defined".format(sideset_id)) trax.extend( Mesh.format_traction_bc(sideset, func, scale, connect, coords, elements)) continue return trax
def parse_nodal_forces(self, prforces, nsets): nfrcs = [] for prforce in prforces: fcn = prforce[0] scale = prforce[1] nodeset = prforce[2] key = prforce[3].upper() dofs = default_dofs(self.dim, key) # get the nodes in the nodeset try: nodes = [x[1] for x in nsets if nodeset == x[0]][0] except IndexError: raise UserInputError("Nodeset {0} not defined".format(nodeset)) for dof in dofs: nfrcs.extend(Mesh.format_nodal_force(nodes, dof, fcn, scale)) continue return nfrcs
def parse_displacement_bc(self, prdisps, nsets): dbcs = [] for prdisp in prdisps: fcn = prdisp[0] scale = prdisp[1] nodeset = prdisp[2] key = prdisp[3].upper() dofs = default_dofs(self.dim, key) # get the nodes in the nodeset try: nodes = [x[1] for x in nsets if nodeset == x[0]][0] except IndexError: raise UserInputError("Nodeset {0} not defined".format(nodeset)) for dof in dofs: dbcs.extend(Mesh.format_displacement_bc( nodes, dof, fcn, scale)) continue return dbcs
def pMesh(self, element_list): """Parse the Mesh block and set defaults """ if not element_list: raise UserInputError("Mesh: input not found") if len(element_list) > 1: raise UserInputError("Mesh: expected 1 block, found: {0}".format( len(element_list))) mesh = element_list[0] mesh_type = mesh.attributes.get("type") if mesh_type is None: raise UserInputError("Mesh: mesh type not found") mesh_type = mesh_type.value.strip().lower() node_sets = [] side_sets = [] el_blocks = [] for s in mesh.getElementsByTagName("AssignGroups"): node_sets.extend(self.get_node_sets(s.getElementsByTagName("Nodeset"))) side_sets.extend(self.get_side_sets(s.getElementsByTagName("Sideset"))) el_blocks.extend(self.get_el_blocks(s.getElementsByTagName("Block"))) if not el_blocks: raise UserInputError("Mesh.AssignGroups: no element block assignments " "found") if mesh_type == "inline": eltype, dim, coords, connect = self.parse_inline_mesh(mesh) for el_block in el_blocks: if eltype.lower()[:3] != el_block[1][:3]: raise UserInputError("Mesh.inline: {0}: inconsistent element " "type for assigned block {1}".format( el_block[1], el_block[0])) elif mesh_type == "ascii": dim, coords, connect = self.parse_ascii_mesh(mesh) else: raise UserInputError("{0}: invalid mesh type".format(mesh_type)) return dim, coords, connect, el_blocks, side_sets, node_sets
def format_el_blocks(coords, conn, el_blocks): """Format element sets to be passed to exodusii Parameters ---------- coords : ndarray Nodal coordinates conn : ndarray Element connectivity el_blocks : array_like User given element sets *** Returns ------- formatted_el_blocks :array_like Sorted list of element sets formatted_el_blocks[i][j] -> jth element of the ith set Notes ----- el_blocks can be given in one of two ways: 1) Explicitly give each element of a set. [[1, [23, 24, 25]]] would assign element 23, 24, 25 to element set 1 2) Define ranges to find elements [[1, ((xi, xl), (yi, yl), (zi, zl))]] would assign to element set 1 all elements in the ranges. """ formatted_el_blocks, explicit, used = [], [], [] unassigned = None for i, (j, eltype, members, elopts) in enumerate(el_blocks): if members == "all": members = range(len(conn)) if members == "unassigned": unassigned = j continue fromrange = len(members) == 3 and all( [len(x) == 2 for x in members]) # find all element sets that are given explicitly if not fromrange: members = np.array(members, dtype=np.int) formatted_el_blocks.append([j, eltype, members, elopts]) used.extend(members) explicit.append(j) continue # now find elements within given ranges hold = {} for (iel, el) in enumerate(conn): ecoords = coords[el] for i, eltype, members, elopts in el_blocks: if i in explicit or i == unassigned: continue # look for all elements in the given range if all([ np.amin(ecoords[:, 0]) >= members[0][0], np.amax(ecoords[:, 0]) <= members[0][1], np.amin(ecoords[:, 1]) >= members[1][0], np.amax(ecoords[:, 1]) <= members[1][1], np.amin(ecoords[:, 2]) >= members[2][0], np.amax(ecoords[:, 2]) <= members[2][1] ]): hold.setdefault(i, []).append(iel) for iset, members in hold.items(): # get eltype for this set el_block = [x for x in el_blocks if x[0] == iset][0] eltype = el_block[1] elopts = el_block[3] formatted_el_blocks.append([iset, eltype, members, elopts]) used.extend(iset) # put all elements not already assigned in set 'unassigned' used = np.unique(used) orphans = [x for x in range(len(conn)) if x not in used] if orphans: if unassigned is None: raise UserInputError("nothing to do for unassigned elements") el_block = [x for x in el_blocks if x[0] == unassigned][0] eltype = el_block[1] elopts = el_block[3] formatted_el_blocks.append([unassigned, eltype, orphans, elopts]) return sorted(formatted_el_blocks, key=lambda x: x[0])
def form_elements(runid, el_blocks, connect, coords, block_options, materials): elements = [None] * len(connect) for key, val in block_options.items(): mtlid = val[0] el_block = [x for x in el_blocks if x[0] == key][0] i, etype, els, elopts = el_block elopts["RUNID"] = runid mtl_name, mtl_params = materials.get(mtlid, (None, None)) # Check if element is an RVE if mtl_name.lower() == "rve": etype = "RVE" elopts.update(mtl_params) # give default elastic material mtl_name = "elastic" mtl_params = {"E": 10.0E+09, "NU": .333333333} if mtl_name is None: raise UserInputError( "Material {0} not defined in input file".format(mtlid)) # instantiate the material model for element material = create_material(mtl_name) # get the element class for elements in this block ecls = element_class_from_name(etype) # determine volumes of each element v_els = None # Check for any perturbed parameters matparams, perturbed = {}, {} seed(12) for key, val in mtl_params.items(): idx = material.parameter_index(key) if idx is None: recognized = ", ".join(material.param_map) raise UserInputError( "{0}: {1}: unrecognized parameter. " "Recognized parameters are: {2}".format( material.name, key, recognized)) weibull = False if not weibull: matparams[key] = val continue # weibull perturbation requested if v_els is None: v_els = np.array( [ecls.volume(coords[connect[eid]]) for eid in els]) v_ave = np.average(v_els) p0 = val.get("MEDIAN") if p0 is None: raise UserInputError( "{0}: no median value given".format(key)) k = float(val.get("VOLUME EXPONENT", 1.)) m = float(val.get("WEIBULL MODULUS", 10.)) msf = float(val.get("MODULUS SCALE FACTOR", 1.)) p = [ p0 * (v_ave / v_els[i])**(k / m) * (np.log(rand()) / np.log(.5))**(1. / (m * msf)) for i, el in enumerate(els) ] perturbed[key] = (idx, np.array(p)) # Fill parameter array for iel, eid in enumerate(els): p = {} for key, (i, val) in perturbed.items(): p[key] = (i, val[iel]) nodes = connect[eid] nodal_coords = coords[nodes] elements[eid] = ecls(eid, nodes, nodal_coords, material, matparams, p, **elopts) del matparams del perturbed return elements
def __init__(self, runid, dim, coords, connect, el_blocks, ssets, nsets, prdisps, prforces, tractions, block_options, materials, periodic_masters_slaves): self.dim = dim self._sidesets = {} self._elem_blocks = {} self._nodesets = {} self._displacement_bcs = [] self._nodal_forces = [] self._traction_bcs = [] self._elements = None self._coords = [] self._conn = [] coords, connect = self.check_coords_and_conn(dim, coords, connect) # assign element blocks # Do this first because element ordering changes formatted_el_blocks = self.format_el_blocks(coords, connect, el_blocks) # elements are put in to the exodus output by element set. As a # result, the element numbering in the exodus file will differ from # the element numbering in the fea code. exo_emap maps the number # in the exodus file to the number in the fea code as # exodus_element_number = exo_map[i] # where i is the internal element number i = 0 self.exo_emap = np.empty(len(connect), dtype=int) for (ielset, eltype, els, elopts) in formatted_el_blocks: for el in els: self.exo_emap[el] = i i += 1 # instantiate element classes elements = self.form_elements(runid, formatted_el_blocks, connect, coords, block_options, materials) # assign sets # Sidesets are a dict made up of ID: DOMAIN pairs, where ID is the # integer for the sideset and DOMAIN is the domain - one of {ILO, IHI, # JLO, JHI}. We first get the nodes that lie on the DOMAIN and the # connected elements sidesets = self.format_sidesets(coords, connect, elements, ssets) nodesets = self.format_nodesets(coords, connect, nsets) # Now that sets have been assigned, assign the displacement and # traction boundary conditions to appropriate sets. displacement_bcs = self.parse_displacement_bc(prdisps, nodesets) # Nodal forces nodal_forces = self.parse_nodal_forces(prforces, nodesets) # tractions tractions = self.parse_traction_bc(tractions, sidesets, coords, connect, elements) # assign class members self._coords = coords self._conn = connect self.assign_sidesets(sidesets) self.assign_nodesets(nodesets) self.assign_elem_blocks(formatted_el_blocks) self.assign_displacement_bcs(displacement_bcs) self.assign_nodal_forces(nodal_forces) self.assign_traction_bcs(tractions) self._elements = np.array(elements) self._node_el_map = self.generate_node_el_map(self._coords.shape[0], self._conn) if periodic_masters_slaves and len(periodic_masters_slaves) > 1: raise UserInputError("Only one periodic bc currently supported") pass
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 __init__(self, lmnid, nodes, coords, material, mat_params, perturbed, *args, **kwargs): if len(nodes) != self.nnodes: raise UserInputError("{0}: {1} nodes required, got {2}".format( self.name, self.nnodes, len(nodes))) # set up the model, perturbing parameters if necessary self.material = material for key, (i, val) in perturbed.items(): mat_params[key] = val self.material.setup(mat_params) self.material.initialize_state() self.rve_model = None self._variables = [] self.reducedint = "REDUCED_INTEGRATION" in kwargs self.planestress = "PLANE_STRESS" in kwargs if self.reducedint and not self.canreduce: raise UserInputError( "Element {0} cannot use reduced integration".format(self.name)) if self.planestress and not self.hasplanestress: raise UserInputError( "Element {0} not plane stress compatible".format(self.name)) ro.reducedint = self.reducedint self.lmnid = lmnid self.ievar = 0 self.register_variable("CAUCHY-STRESS", vtype="SYMTENS") self.register_variable("LEFT-STRETCH", vtype="SYMTENS") self.register_variable("ROTATION", vtype="TENS") self.register_variable("SYMM-L", vtype="SYMTENS") self.register_variable("SKEW-L", vtype="SKEWTENS") self.register_variable("GREEN-STRAIN", vtype="SYMTENS") for var in self.material.variables(): self.register_variable(var, vtype="SCALAR") # Element data array. See comments above. self.nxtra = self.material.nxtra self.ndat = XTRA + self.nxtra + len(perturbed) self._p = XTRA + self.nxtra self.data = np.zeros((2, self.ngauss + 1, self.ndat)) # register perturbed parameters as variables self._pidx = [] for i, (key, (idx, val)) in enumerate(perturbed.items()): self._pidx.append(idx) self.register_variable("{0}_STAT".format(key)) self.data[:, :, self._p + i] = val # Element volume self._volume = self.volume(coords) # initialize nonzero data self.data[:, :, LEFTV:LEFTV + NSYMM] = I6 self.data[:, :, ROTATE:ROTATE + NTENS] = I9 self.data[:, :, XTRA:XTRA + self.nxtra] = self.material.initial_state() # Initialize the stiffness self.kel = np.zeros((self.ndof * self.nnodes, self.ndof * self.nnodes)) pass
def get_el_blocks(el_blocks_element): """Parse Mesh.Blocks.Block element trees """ el_blocks = [] unassigned, allassigned = 0, 0 for el_block in el_blocks_element: el_block_id = el_block.attributes.get("id") if el_block_id is None: raise UserInputError("Blocks: Block: 'id' not found") el_block_id = int(el_block_id.value.strip()) # element block elements elements = el_block.attributes.get("elements") if elements is None: raise UserInputError("AssignGroups.Block({0}): no elements " "assigned".format(el_block_id)) elements = elements.value.strip().lower() # check special cases first if elements == "all": allassigned += 1 elif elements == "unassigned": unassigned += 1 if elements not in ("all", "unassigned"): elements = str2list(elements, dtype=int) # element type for block eltype = el_block.attributes.get("eltype") if eltype is None: raise UserInputError("AssignGroups.Block({0}): no eltype " "assigned".format(el_block_id)) eltype = eltype.value.strip().lower() # element options for block elopts = el_block.attributes.get("elopts", {}) if elopts: S = re.findall(r".*?=\s*[\w\.]+.*?", elopts.value) for (i, item) in enumerate(S): item = item.split("=") if len(item) != 2: raise UserInputError("elopts must be of form key=val") try: val = int(item[1]) except ValueError: val = str(item[1]) key = str("_".join(item[0].split()).upper()) S[i] = (key, val) elopts = dict(S) el_blocks.append([el_block_id, eltype, elements, elopts]) if unassigned and allassigned: raise UserInputError("AssignGroups: elements='all' inconsistent with " "elements='unassigned'") if allassigned and len(el_blocks) > 1: raise UserInputError("AssignGroups: elements='all' inconsistent with " "mutliple assigned blocks") if unassigned and len(el_blocks) == 1: el_blocks[0][2] = "all" return el_blocks
def parse_traction_block(elements): """Parse the Boundary.Traction element Tractions are given as either constant or function. Alternatively each force component can be specified as components="x=xval y=yval z=zval" """ tractions = [] attributes = ("components", "constant", "sideset", "function", "scale") for (i, element) in enumerate(elements): components = element.attributes.get("components") sideset = element.attributes.get("sideset") const = element.attributes.get("constant") function = element.attributes.get("function") scale = element.attributes.get("scale") # check for required arguments and conflicts if sideset is None: raise UserInputError("Boundary.Traction: sideset not specified") sideset = int(sideset.value) one_rqd = [x for x in (const, function, components) if x is not None] if not one_rqd: raise UserInputError("Boundary.Traction: expected one of " "(constant, function, components) attribute") if len(one_rqd) > 1: raise UserInputError("Boundary.Traction: expected only one of " "(constant, function, components) attribute") if components is not None: # components specified, find out which scale = np.zeros(3) components = " ".join(components.value.upper().split()) for i, label in enumerate("XYZ"): S = re.search("{0}\s*=\s*(?P<val>{1})".format( label, NUMREGEX), components) if S is not None: scale[i] = float(S.group("val")) # assign the constant function function = [W_CONST_FCN] * 3 elif const is not None: const = float(const.value) if scale is not None: raise UserInputError("Boundary.PrescribedForce: incompatible " "attributes: 'scale, constant'") function = W_CONST_FCN scale = const else: function = int(function.value) if scale is None: scale = 1. else: scale = float(scale.value) assert sideset is not None, "sideset is None" tractions.append([function, scale, sideset]) continue return tractions
def parse_inline_mesh(inline_mesh_element): lmn_types = ("Quad",) lmn = {} for lmn_type in lmn_types: tags = inline_mesh_element.getElementsByTagName(lmn_type) if not tags: continue if len(tags) > 1: raise UserInputError( "Mesh.inline: expected 1 {0}, got {1}".format( lmn_type, len(tags))) lmn.setdefault(lmn_type, []).append(tags[0]) if not lmn: raise UserInputError( "Mesh.inline: expected one of {0} block types".format( ", ".join(lmn_types))) if len(lmn) > 1: raise UserInputError( "Mesh.inline: expected only one of {0} block types".format( ", ".join(lmn_types))) lmn_type = lmn.keys()[0] lmn = lmn[lmn_type][0] # get [xyz]mins mins = [] for item in ("xmin", "ymin", "zmin"): val = lmn.attributes.get("xmin", 0.) if val: val = float(val.value.strip()) mins.append(val) xyzblocks = [None] * 3 attributes = ("order", "length", "interval") blk_types = ("XBlock", "YBlock", "ZBlock") for i, tag in enumerate(blk_types): o = 0 blks = [] for xyzblock in lmn.getElementsByTagName(tag): blk = [] for item in attributes: attr = xyzblock.attributes.get(item) if not attr: raise UserInputError( "Mesh.inline.{0}: expected {1} attribute".format( tag, item)) attr = float(attr.value.strip()) if item == "order": o += 1 if attr != o: raise UserInputError("Mesh.inline.{0}s must be ordered " "contiguously".format(tag)) continue blk.append(attr) blks.append(blk) xyzblocks[i] = blks mesh = gen_coords_conn_from_inline(lmn_type, mins, xyzblocks) dim, coords, connect = mesh return lmn_type, dim, coords, connect
def pFunctions(self, element_list): """Parse the functions block """ if len(element_list) > 1: raise UserInputError("Functions: expected 1 block, found: {0}".format( len(element_list))) functions = {W_CONST_FCN: lambda x: 1.} if not element_list: return functions for function in element_list[0].getElementsByTagName("Function"): fid = function.attributes.get("id") if fid is None: raise UserInputError("Functions.Function: id not found") fid = int(fid.value) if fid == W_CONST_FCN: raise UserInputError("Function id {0} is reserved".format(fid)) if fid in functions: raise UserInputError("{0}: function already defined".format(fid)) ftype = function.attributes.get("type") if ftype is None: raise UserInputError("Functions.Function: type not found") ftype = " ".join(ftype.value.split()).upper() if ftype not in (AE, PWL): raise UserInputError("{0}: invalid function type".format(ftype)) expr = function.firstChild.data.strip() if ftype == AE: func, err = build_lambda(expr, disp=1) if err: raise UserInputError("{0}: in analytic expression in " "function {1}".format(err, fid)) elif ftype == PWL: # parse the table in expr try: columns = str2list(function.attributes.get("columns").value, dtype=str) except AttributeError: columns = ["x", "y"] except TypeError: columns = ["x", "y"] table = [] ncol = len(columns) for line in expr.split("\n"): line = [float(x) for x in line.split()] if not line: continue if len(line) != ncol: nl = len(line) raise UserInputError("Expected {0} columns in function " "{1}, got {2}".format(ncol, fid, nl)) table.append(line) func, err = build_interpolating_function(np.array(table), disp=1) if err: raise UserInputError("{0}: in piecwise linear table in " "function {1}".format(err, fid)) functions[fid] = func continue return functions
def __init__(self, user_input): """Parse the xml input file Parameters ---------- file_name : str path to input file """ user_input = fill_in_includes(user_input) dom = xdom.parseString(user_input) # Get the root element (Should always be "MaterialModel") model_input = dom.getElementsByTagName("WasatchModel") if not model_input: raise UserInputError("Expected Root Element 'WasatchModel'") # ------------------------------------------ get and parse blocks --- # input_blocks = {} recognized_blocks = ("SolutionControl", "Mesh", "Boundary", "Materials", "Functions", "Blocks") for block in recognized_blocks: elements = model_input[0].getElementsByTagName(block) try: parse_function = getattr(self, "p{0}".format(block)) except AttributeError: sys.exit("{0}: not finished parsing".format(block)) input_blocks[block] = parse_function(elements) for element in elements: p = element.parentNode p.removeChild(element) # --------------------------------------------------- check input --- # # 0) pop needed data from input_blocks dict and save # 1) check that functions are defined for entities requiring function # 2) replace function ids with actual function self.control = input_blocks.pop("SolutionControl") self.materials = input_blocks.pop("Materials") self.blk_options = input_blocks.pop("Blocks") self.periodic_masters_slaves = None boundary = input_blocks.pop("Boundary") functions = input_blocks.pop("Functions") (self.dim, self.coords, self.connect, self.el_blocks, self.ssets, self.nsets) = input_blocks.pop("Mesh") # element blocks for blk, items in self.blk_options.items(): # make sure block is defined in mesh if blk not in [x[0] for x in self.el_blocks]: raise UserInputError("Blocks.Block: Block {0} not defined in " "mesh".format(blk)) material = items[0] try: self.materials[material] except KeyError: raise UserInputError("Blocks.Block({0}): material {1} " "not defined".format(blk, material)) # --- boundary self.prdisps = [] for prdisp in boundary.get("PrescribedDisplacement", []): (fid, scale, nodeset, dof) = prdisp func = functions.get(fid) if func is None: raise UserInputError("Boundary.PrescribedDisplacement function " "{0} not defined".format(fid)) if nodeset not in [x[0] for x in self.nsets]: raise UserInputError("Boundary.PrescribedDisplacement nodeset " "{0} not defined".format(nodeset)) self.prdisps.append([func, scale, nodeset, dof]) self.prforces = [] for (fid, scale, nodeset, dof) in boundary.get("PrescribedForce", []): func = functions.get(fid) if func is None: raise UserInputError("Boundary.PrescribedForce function {0} " "not defined".format(fid)) self.prforces.append([func, scale, nodeset, dof]) self.tractions = [] for (fid, scale, sideset) in boundary.get("Traction", []): # test if fid is a function ID or IDs func = None try: func = [functions[x] for x in fid] except TypeError: # must be an integer func = functions.get(fid) if func is None: raise UserInputError("Boundary.Traction function {0} not " "defined".format(fid)) self.tractions.append([func, scale, sideset]) self.distloads = [] for (fid, scale, blx) in boundary.get("DistributedLoad", []): func = functions.get(fid) if func is None: raise UserInputError("Boundary.DistributedLoad function {0} " "not defined".format(fid)) self.distloads.append([func, scale, blx])
def create_material(matname): model = models.get(matname.upper()) if model is None: raise UserInputError("model {0} not recognized".format(matname)) return model()