def yield_test(self, pointset, total_strain, plastic_strain, stress, modulus): # Locally-defined tolerances -- these should maybe be # parameters for the property itself. When these # tolerances are met, the Newton-Raphson iterations are over. yield_tolerance = 1.E-06 resid_tolerance = 1.E-06 iteration_max = 100 for (p, e, ep, sigma, cijkl) in zip(pointset, total_strain, plastic_strain, stress, modulus): yld = self.yield_func(sigma) print "Yield function gives ", yld if yld > 0.0: # "Initial guess" for plastic increment amounts. gamma = 0.0 delta_ep = SymmMatrix(3) # Allocate "work" matrices. mtx = smallmatrix.SmallMatrix(7, 7) rhs = smallmatrix.SmallMatrix(7, 1) # Compute matrix r, measure norm. r_matrix = SymmMatrix(3) r_norm = 0.0 dyld = self.d_yield_func(sigma) for kl in SymTensorIndex(0, 0): klr = kl.row() klc = kl.col() r_matrix[klr, klc] = delta_ep[klr, klc] - gamma * dyld[klr, klc] r_norm += r_matrix[klr, klc]**2 r_norm = math.sqrt(r_norm) # # Repeat until converged.... iteration_count = 0 # On entry to the loop, you must have a valid yield # function value yld, derivative dyld, and both # r_matrix and r_norm computed for the current stress, # and gamma and delta_ep must be valid. while ((math.fabs(yld)>yield_tolerance) or \ (r_norm > resid_tolerance)) and \ iteration_count<iteration_max: iteration_count += 1 # # RHS is easy. for kl in SymTensorIndex(0, 0): rhs[kl.integer(), 0] = -r_matrix[kl.row(), kl.col()] rhs[6, 0] = -yld mtx.clear() # # MTX is slightly more involved. for kl in SymTensorIndex(0, 0): klr = kl.row() klc = kl.col() mtx[kl.integer(), 6] = -dyld[klr, klc] for mn in SymTensorIndex(0, 0): mnr = mn.row() mnc = mn.col() # if (klr == mnr) and (klc == mnc): mtx[kl.integer(), mn.integer()] += 1.0 mtx[6, mn.integer()] -= ( dyld[klr, klc] * cijkl[kl.integer(), mn.integer()]) if gamma != 0.0: for op in SymTensorIndex(0, 0): opr = op.row() opc = op.col() # mtx[kl.integer(), mn.integer()] += ( gamma * cijkl[op.integer(), mn.integer()]*\ self.d_d_yield_func(klr,klc, opr,opc, sigma)) # Mtx is now built. Whee. rcode = mtx.solve(rhs) if rcode != 0: raise ErrUserError( "Nonzero return code in matrix solver.") # Increment gamma and delta_ep, then use them to # recompute sigma, yld, dyld, r_matrix and r_norm. # then go around again. for kl in SymTensorIndex(0, 0): kli = kl.integer() klr = kl.row() klc = kl.col() delta_ep[klr, klc] += rhs[kli, 0] gamma += rhs[6, 0] # Modify stress according to the increment of # delta_ep (*not* delta_ep itself!) for kl in SymTensorIndex(0, 0): for mn in SymTensorIndex(0, 0): sigma[kl.integer()] -= cijkl[kl.integer(), mn.integer()]*\ rhs[mn.integer(),0] yld = self.yield_func(sigma) dyld = self.d_yield_func(sigma) r_norm = 0.0 for kl in SymTensorIndex(0, 0): klr = kl.row() klc = kl.col() r_matrix[klr,klc]=delta_ep[klr,klc] -\ gamma*dyld[klr,klc] r_norm += r_matrix[klr, klc]**2 r_norm = math.sqrt(r_norm) # Exit the convergence while loop. if iteration_count == 1: print "Exiting while loop after 1 iteration." else: print "Exiting while loop after %d iterations." %\ iteration_count print "Yield, resid are ", yld, r_norm for ij in SymTensorIndex(0, 0): ijr = ij.row() ijc = ij.col() ep[ijr, ijc] += delta_ep[ijr, ijc] print "Plastic strain modified."
def _undisplaced_from_displaced_with_element(self, mesh, elem, pos): # Search master space for an x such that f(x)=pos, and return x. delta = 0.001 # Small compared to master space. if config.dimension() == 2: mtx = smallmatrix.SmallMatrix(2, 2) rhs = smallmatrix.SmallMatrix(2, 1) delta_x = mastercoord.MasterCoord(delta, 0.0) delta_y = mastercoord.MasterCoord(0.0, delta) elif config.dimension() == 3: mtx = smallmatrix.SmallMatrix(3, 3) rhs = smallmatrix.SmallMatrix(3, 1) delta_x = mastercoord.MasterCoord(delta, 0.0, 0.0) delta_y = mastercoord.MasterCoord(0.0, delta, 0.0) delta_z = mastercoord.MasterCoord(0.0, 0.0, delta) res = elem.center() done = False # Magic numbers. The function can actually fail, because for # higher-order elements, the displacement mapping is # parabolic, and the range of the parabolic mapping might not # include the point pos, if it's too far away from the # element. So, if we've gone for more than maxiters # iterations, raise an exception. tolerance = 1.0e-10 maxiters = 50 count = 0 while not done: count += 1 if count > maxiters: raise IterationMaxExceeded() fwd = self.where.evaluate(mesh, [elem], [[res]])[0] fwddx = self.where.evaluate(mesh, [elem], [[res + delta_x]])[0] fwddy = self.where.evaluate(mesh, [elem], [[res + delta_y]])[0] if config.dimension() == 3: fwddz = self.where.evaluate(mesh, [elem], [[res + delta_z]])[0] dfdx = (fwddx - fwd) / delta dfdy = (fwddy - fwd) / delta if config.dimension() == 3: dfdz = (fwddz - fwd) / delta diff = pos - fwd rhs.setitem(0, 0, diff[0]) rhs.setitem(1, 0, diff[1]) if config.dimension() == 3: rhs.setitem(2, 0, diff[2]) mtx.setitem(0, 0, dfdx[0]) mtx.setitem(0, 1, dfdy[0]) mtx.setitem(1, 0, dfdx[1]) mtx.setitem(1, 1, dfdy[1]) if config.dimension() == 3: mtx.setitem(0, 2, dfdz[0]) mtx.setitem(1, 2, dfdz[1]) mtx.setitem(2, 0, dfdx[2]) mtx.setitem(2, 1, dfdy[2]) mtx.setitem(2, 2, dfdz[2]) ## TODO OPT: For a 2x2 matrix, is it faster to write out ## the solution, rather than using a general purpose ## routine? r = mtx.solve(rhs) if config.dimension() == 2: resid = (rhs[0, 0]**2 + rhs[1, 0]**2) res = mastercoord.MasterCoord(res[0] + rhs[0, 0], res[1] + rhs[1, 0]) elif config.dimension() == 3: resid = (rhs[0, 0]**2 + rhs[1, 0]**2 + rhs[2, 0]**2) res = mastercoord.MasterCoord(res[0] + rhs[0, 0], res[1] + rhs[1, 0], res[2] + rhs[2, 0]) if resid < tolerance: done = True return res
def undisplaced(self, mesh, elem, pos): # 2D only # Search master space for an x such that f(x)=pos, and return # the real space location of x. 'pos' is a position in real # space. delta = 0.001 # Small compared to master space. if config.dimension() == 2: mtx = smallmatrix.SmallMatrix(2, 2) rhs = smallmatrix.SmallMatrix(2, 1) delta_x = mastercoord.MasterCoord(delta, 0.0) delta_y = mastercoord.MasterCoord(0.0, delta) elif config.dimension() == 3: mtx = smallmatrix.SmallMatrix(3, 3) rhs = smallmatrix.SmallMatrix(3, 1) delta_x = mastercoord.MasterCoord(delta, 0.0, 0.0) delta_y = mastercoord.MasterCoord(0.0, delta, 0.0) delta_z = mastercoord.MasterCoord(0.0, 0.0, delta) res = elem.center() # master space position done = False # Magic numbers. The function can actually fail, because for # higher-order elements, the displacement mapping is # parabolic, and the range of the parabolic mapping might not # include the point pos, if it's too far away from the # element. So, if we've gone for more than maxiters # iterations, raise an exception. tolerance = 1.0e-10 maxiters = 50 count = 0 while not done: count += 1 if count > maxiters: raise ooferror2.ErrConvergenceFailure("Displacement inversion", maxiters) fwd = self._displaced(mesh, elem, masterpos=res) fwddx = self._displaced(mesh, elem, masterpos=res + delta_x) fwddy = self._displaced(mesh, elem, masterpos=res + delta_y) dfdx = (fwddx - fwd) / delta dfdy = (fwddy - fwd) / delta if config.dimension() == 3: fwddz = self._displaced(mesh, elem, masterpos=res + delta_z) dfdz = (fwddz - fwd) / delta diff = pos - fwd rhs.setitem(0, 0, diff[0]) rhs.setitem(1, 0, diff[1]) if config.dimension() == 3: rhs.setitem(2, 0, diff[2]) mtx.setitem(0, 0, dfdx[0]) mtx.setitem(0, 1, dfdy[0]) mtx.setitem(1, 0, dfdx[1]) mtx.setitem(1, 1, dfdy[1]) if config.dimension() == 3: mtx.setitem(0, 2, dfdz[0]) mtx.setitem(1, 2, dfdz[1]) mtx.setitem(2, 0, dfdx[2]) mtx.setitem(2, 1, dfdy[2]) mtx.setitem(2, 2, dfdz[2]) ## TODO OPT: For a 2x2 matrix, is it faster to write out the ## solution, rather than using a general purpose routine? r = mtx.solve(rhs) if config.dimension() == 2: resid = (rhs[0, 0]**2 + rhs[1, 0]**2) res = mastercoord.MasterCoord(res[0] + rhs[0, 0], res[1] + rhs[1, 0]) elif config.dimension() == 3: resid = (rhs[0, 0]**2 + rhs[1, 0]**2 + rhs[2, 0]**2) res = mastercoord.MasterCoord(res[0] + rhs[0, 0], res[1] + rhs[1, 0], res[2] + rhs[2, 0]) if resid < tolerance: done = True return elem.from_master(res)