def CurvedPlate(ncirc=2, nlong=20, show_plot=False): """Creates custom mesh for plate with curved edges ncirc discretisation around circular fillets nlong discretisation along the length - X """ mesh_arc = Mesh() mesh_arc.Arc(element_type="quad", nrad=ncirc, ncirc=ncirc, radius=5) mesh_arc1 = deepcopy(mesh_arc) mesh_arc1.points[:, 1] += 15 mesh_arc1.points[:, 0] += 95 mesh_arc2 = deepcopy(mesh_arc) mesh_arc2.points[:, 1] += 15 mesh_arc2.points[:, 0] *= -1. mesh_arc2.points[:, 0] += 5. mesh_plate1 = Mesh() mesh_plate1.Rectangle(element_type="quad", lower_left_point=(5, 15), upper_right_point=(95, 20), ny=ncirc, nx=nlong) mesh_plate2 = deepcopy(mesh_plate1) mesh_plate2.points[:, 1] -= 5. mesh_square1 = Mesh() mesh_square1.Square(element_type="quad", lower_left_point=(0, 10), side_length=5, nx=ncirc, ny=ncirc) mesh_square2 = deepcopy(mesh_square1) mesh_square2.points[:, 0] += 95 mesh = mesh_plate1 + mesh_plate2 + mesh_arc1 + mesh_arc2 + mesh_square1 + mesh_square2 mesh.Extrude(length=0.5, nlong=1) mesh2 = deepcopy(mesh) mesh2.points[:, 2] += 0.5 mesh += mesh2 if show_plot: mesh.SimplePlot() return mesh
def LogSave(self, fem_solver, formulation, U, Eulerp, Increment): if fem_solver.print_incremental_log: dmesh = Mesh() dmesh.points = U dmesh_bounds = dmesh.Bounds if formulation.fields == "electro_mechanics": _bounds = np.zeros((2,formulation.nvar)) _bounds[:,:formulation.ndim] = dmesh_bounds _bounds[:,-1] = [Eulerp.min(),Eulerp.max()] print("\nMinimum and maximum incremental solution values at increment {} are \n".format(Increment),_bounds) else: print("\nMinimum and maximum incremental solution values at increment {} are \n".format(Increment),dmesh_bounds) # SAVE INCREMENTAL SOLUTION IF ASKED FOR if fem_solver.save_incremental_solution: # FOR BIG MESHES if Increment % fem_solver.incremental_solution_save_frequency !=0: return from scipy.io import savemat filename = fem_solver.incremental_solution_filename if filename is not None: if ".mat" in filename: filename = filename.split(".")[0] savemat(filename+"_"+str(Increment), {'solution':np.hstack((U,Eulerp[:,None]))},do_compression=True) else: raise ValueError("No file name provided to save incremental solution")
def HighOrderMeshTet_SEMISTABLE(C, mesh, Decimals=10, equally_spaced=False, check_duplicates=True, parallelise=False, nCPU=1, compute_boundary_info=True): from Florence.FunctionSpace import Tet from Florence.QuadratureRules.FeketePointsTet import FeketePointsTet from Florence.MeshGeneration.NodeArrangement import NodeArrangementTet # SWITCH OFF MULTI-PROCESSING FOR SMALLER PROBLEMS WITHOUT GIVING A MESSAGE Parallel = parallelise if (mesh.elements.shape[0] < 500) and (C < 5): Parallel = False nCPU = 1 if not equally_spaced: eps = FeketePointsTet(C) # COMPUTE BASES FUNCTIONS AT ALL NODAL POINTS Neval = np.zeros((4, eps.shape[0]), dtype=np.float64) hpBases = Tet.hpNodal.hpBases for i in range(4, eps.shape[0]): Neval[:, i] = hpBases(0, eps[i, 0], eps[i, 1], eps[i, 2], Transform=1, EvalOpt=1)[0] else: from Florence.QuadratureRules.EquallySpacedPoints import EquallySpacedPointsTet eps = EquallySpacedPointsTet(C) # COMPUTE BASES FUNCTIONS AT ALL NODAL POINTS hpBases = Tet.hpNodal.hpBases Neval = np.zeros((4, eps.shape[0]), dtype=np.float64) for i in range(4, eps.shape[0]): Neval[:, i] = hpBases(0, eps[i, 0], eps[i, 1], eps[i, 2], Transform=1, EvalOpt=1, equally_spaced=True)[0] # THIS IS NECESSARY FOR REMOVING DUPLICATES makezero(Neval, tol=1e-12) nodeperelem = mesh.elements.shape[1] renodeperelem = int((C + 2.) * (C + 3.) * (C + 4.) / 6.) left_over_nodes = renodeperelem - nodeperelem reelements = -1 * np.ones( (mesh.elements.shape[0], renodeperelem), dtype=np.int64) reelements[:, :4] = mesh.elements # TOTAL NUMBER OF (INTERIOR+EDGE+FACE) NODES iesize = np.int64(C * (C - 1) * (C - 2) / 6. + 6. * C + 2 * C * (C - 1)) repoints = np.zeros( (mesh.points.shape[0] + iesize * mesh.elements.shape[0], 3), dtype=np.float64) repoints[:mesh.points.shape[0], :] = mesh.points telements = time() xycoord_higher = [] ParallelTuple1 = [] if Parallel: # GET HIGHER ORDER COORDINATES - PARALLEL ParallelTuple1 = parmap.map(ElementLoopTet, np.arange(0, mesh.elements.shape[0]), mesh.elements, mesh.points, 'tet', eps, Neval, pool=MP.Pool(processes=nCPU)) maxNode = np.max(reelements) for elem in range(0, mesh.elements.shape[0]): # maxNode = np.max(reelements) # BIG BOTTLENECK if Parallel: xycoord_higher = ParallelTuple1[elem] else: xycoord = mesh.points[mesh.elements[elem, :], :] # GET HIGHER ORDER COORDINATES xycoord_higher = GetInteriorNodesCoordinates( xycoord, 'tet', elem, eps, Neval) # EXPAND THE ELEMENT CONNECTIVITY newElements = np.arange(maxNode + 1, maxNode + 1 + left_over_nodes) reelements[elem, 4:] = newElements # INSTEAD COMPUTE maxNode BY INDEXING maxNode = newElements[-1] repoints[mesh.points.shape[0] + elem * iesize:mesh.points.shape[0] + (elem + 1) * iesize] = xycoord_higher[4:, :] if Parallel: del ParallelTuple1 telements = time() - telements #-------------------------------------------------------------------------------------- # NOW REMOVE DUPLICATED POINTS tnodes = time() nnode_linear = mesh.points.shape[0] # KEEP ZEROFY ON, OTHERWISE YOU GET STRANGE BEHVAIOUR # rounded_repoints = makezero(repoints[nnode_linear:,:].copy()) rounded_repoints = repoints[nnode_linear:, :].copy() makezero(rounded_repoints) rounded_repoints = np.round(rounded_repoints, decimals=Decimals) _, idx_repoints, inv_repoints = unique2d(rounded_repoints, order=False, consider_sort=False, return_index=True, return_inverse=True) # idx_repoints.sort() del rounded_repoints idx_repoints = np.concatenate( (np.arange(nnode_linear), idx_repoints + nnode_linear)) repoints = repoints[idx_repoints, :] unique_reelements, inv_reelements = np.unique(reelements[:, 4:], return_inverse=True) unique_reelements = unique_reelements[inv_repoints] reelements = unique_reelements[inv_reelements] reelements = reelements.reshape(mesh.elements.shape[0], renodeperelem - 4) reelements = np.concatenate((mesh.elements, reelements), axis=1) # SANITY CHECK fOR DUPLICATES #---------------------------------------------------------------------# # NOTE THAT THIS REMAPS THE ELEMENT CONNECTIVITY FOR THE WHOLE MESH # AND AS A RESULT THE FIRST FEW COLUMNS WOULD NO LONGER CORRESPOND TO # LINEAR CONNECTIVITY if check_duplicates: last_shape = repoints.shape[0] deci = int(Decimals) - 2 if Decimals < 6: deci = Decimals repoints, idx_repoints, inv_repoints = remove_duplicates_2D( repoints, decimals=deci) unique_reelements, inv_reelements = np.unique(reelements, return_inverse=True) unique_reelements = unique_reelements[inv_repoints] reelements = unique_reelements[inv_reelements] reelements = reelements.reshape(mesh.elements.shape[0], renodeperelem) if last_shape != repoints.shape[0]: warn( 'Duplicated points generated in high order mesh. Lower the "Decimals". I have fixed it for now' ) #---------------------------------------------------------------------# tnodes = time() - tnodes #------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------ if compute_boundary_info: # BUILD FACES NOW tfaces = time() # GET MESH EDGES AND FACES fsize = int((C + 2.) * (C + 3.) / 2.) refaces = np.zeros((mesh.faces.shape[0], fsize), dtype=mesh.faces.dtype) refaces = np.zeros((mesh.faces.shape[0], fsize)) # DO NOT CHANGE THE FACES, BY RECOMPUTING THEM, AS THE LINEAR FACES CAN COME FROM # AN EXTERNAL MESH GENERATOR, WHOSE ORDERING MAY NOT BE THE SAME, SO JUST FIND WHICH # ELEMENTS CONTAIN THESE FACES face_to_elements = mesh.GetElementsWithBoundaryFacesTet() node_arranger = NodeArrangementTet(C)[0] refaces = reelements[face_to_elements[:, 0][:, None], node_arranger[face_to_elements[:, 1], :]].astype( mesh.faces.dtype) tfaces = time() - tfaces #------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------ # BUILD EDGES NOW tedges = time() # BUILD A 2D MESH from Florence import Mesh tmesh = Mesh() tmesh.element_type = "tri" tmesh.elements = refaces tmesh.nelem = tmesh.elements.shape[0] # GET BOUNDARY EDGES reedges = tmesh.GetEdgesTri() del tmesh tedges = time() - tedges #------------------------------------------------------------------------------------------ class nmesh(object): # """Construct pMesh""" points = repoints elements = reelements edges = np.array([[], []]) faces = np.array([[], []]) nnode = repoints.shape[0] nelem = reelements.shape[0] info = 'tet' if compute_boundary_info: nmesh.edges = reedges nmesh.faces = refaces gc.collect() # print '\nHigh order meshing timing:\n\t\tElement loop:\t '+str(telements)+' seconds\n\t\tNode loop:\t\t '+str(tnodes)+\ # ' seconds'+'\n\t\tEdge loop:\t\t '+str(tedges)+' seconds'+\ # '\n\t\tFace loop:\t\t '+str(tfaces)+' seconds\n' return nmesh
def Solver(self, function_spaces, formulation, solver, K, M, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition, fem_solver): # COMPUTE DAMPING MATRIX BASED ON MASS D = 0.0 if fem_solver.include_physical_damping: D = fem_solver.damping_factor*M # GET BOUNDARY CONDITIONS INFROMATION if formulation.fields == "electro_mechanics": self.GetBoundaryInfo(mesh, formulation,boundary_condition) M_mech = M[self.mechanical_dofs,:][:,self.mechanical_dofs] if fem_solver.include_physical_damping: D_mech = D[self.mechanical_dofs,:][:,self.mechanical_dofs] # INITIALISE VELOCITY AND ACCELERATION velocities = np.zeros((mesh.points.shape[0],formulation.ndim)) accelerations = np.zeros((mesh.points.shape[0],formulation.ndim)) # COMPUTE INITIAL ACCELERATION FOR TIME STEP 0 InitResidual = Residual - NeumannForces[:,0][:,None] if formulation.fields == "electro_mechanics": accelerations[:,:] = solver.Solve(M_mech, -InitResidual[self.mechanical_dofs].ravel() ).reshape(mesh.points.shape[0],formulation.ndim) else: accelerations[:,:] = solver.Solve(M, -InitResidual.ravel() ).reshape(mesh.points.shape[0],formulation.ndim) self.NRConvergence = fem_solver.NRConvergence LoadIncrement = fem_solver.number_of_load_increments LoadFactor = fem_solver.total_time/LoadIncrement AppliedDirichletInc = np.zeros(boundary_condition.applied_dirichlet.shape[0],dtype=np.float64) if NeumannForces.ndim == 2 and NeumannForces.shape[1]==1: tmp = np.zeros((NeumannForces.shape[0],LoadIncrement)) tmp[:,0] = NeumannForces[:,0] NeumannForces = tmp # TIME LOOP for Increment in range(1,LoadIncrement): t_increment = time() # APPLY NEUMANN BOUNDARY CONDITIONS DeltaF = NeumannForces[:,Increment][:,None] NodalForces = DeltaF # OBRTAIN INCREMENTAL RESIDUAL - CONTRIBUTION FROM BOTH NEUMANN AND DIRICHLET Residual = -boundary_condition.ApplyDirichletGetReducedMatrices(K,Residual, boundary_condition.applied_dirichlet[:,Increment],LoadFactor=1.0,mass=M,only_residual=True) Residual -= DeltaF # Residual = -DeltaF # GET THE INCREMENTAL DIRICHLET AppliedDirichletInc = boundary_condition.applied_dirichlet[:,Increment] # COMPUTE INITIAL ACCELERATION - ONLY NEEDED IN CASES OF PRESTRETCHED CONFIGURATIONS # accelerations[:,:] = solver.Solve(M, Residual.ravel() - \ # K.dot(TotalDisp[:,:,Increment].ravel())).reshape(mesh.points.shape[0],formulation.nvar) # LET NORM OF THE FIRST RESIDUAL BE THE NORM WITH RESPECT TO WHICH WE # HAVE TO CHECK THE CONVERGENCE OF NEWTON RAPHSON. TYPICALLY THIS IS # NORM OF NODAL FORCES if Increment==1: self.NormForces = np.linalg.norm(Residual) # AVOID DIVISION BY ZERO if np.isclose(self.NormForces,0.0): self.NormForces = 1e-14 self.norm_residual = np.linalg.norm(Residual)/self.NormForces Eulerx, Eulerp, K, Residual, velocities, accelerations = self.NewtonRaphson(function_spaces, formulation, solver, Increment, K, D, M, NodalForces, Residual, mesh, Eulerx, Eulerp, material,boundary_condition,AppliedDirichletInc, fem_solver, velocities, accelerations) # UPDATE DISPLACEMENTS FOR THE CURRENT LOAD INCREMENT TotalDisp[:,:formulation.ndim,Increment] = Eulerx - mesh.points if formulation.fields == "electro_mechanics": TotalDisp[:,-1,Increment] = Eulerp # COMPUTE DISSIPATION OF ENERGY THROUGH TIME if fem_solver.compute_energy_dissipation: energy_info = self.ComputeEnergyDissipation(function_spaces[0],mesh,material,formulation,fem_solver, Eulerx, TotalDisp, NeumannForces, M, velocities, Increment) formulation.energy_dissipation.append(energy_info[0]) formulation.internal_energy.append(energy_info[1]) formulation.kinetic_energy.append(energy_info[2]) formulation.external_energy.append(energy_info[3]) # COMPUTE DISSIPATION OF LINEAR MOMENTUM THROUGH TIME if fem_solver.compute_linear_momentum_dissipation: power_info = self.ComputePowerDissipation(function_spaces[0],mesh,material,formulation,fem_solver, Eulerx, TotalDisp, NeumannForces, M, velocities, accelerations, Increment) formulation.power_dissipation.append(power_info[0]) formulation.internal_power.append(power_info[1]) formulation.kinetic_power.append(power_info[2]) formulation.external_power.append(power_info[3]) # PRINT LOG IF ASKED FOR if fem_solver.print_incremental_log: dmesh = Mesh() dmesh.points = TotalDisp[:,:formulation.ndim,Increment] dmesh_bounds = dmesh.Bounds if formulation.fields == "electro_mechanics": _bounds = np.zeros((2,formulation.nvar)) _bounds[:,:formulation.ndim] = dmesh_bounds _bounds[:,-1] = [TotalDisp[:,-1,Increment].min(),TotalDisp[:,-1,Increment].max()] print("\nMinimum and maximum incremental solution values at increment {} are \n".format(Increment),_bounds) else: print("\nMinimum and maximum incremental solution values at increment {} are \n".format(Increment),dmesh_bounds) # SAVE INCREMENTAL SOLUTION IF ASKED FOR if fem_solver.save_incremental_solution: from scipy.io import savemat if fem_solver.incremental_solution_filename is not None: savemat(fem_solver.incremental_solution_filename+"_"+str(Increment),{'solution':TotalDisp[:,:,Increment]},do_compression=True) else: raise ValueError("No file name provided to save incremental solution") print('\nFinished Load increment', Increment, 'in', time()-t_increment, 'seconds') try: print('Norm of Residual is', np.abs(la.norm(Residual[boundary_condition.columns_in])/self.NormForces), '\n') except RuntimeWarning: print("Invalid value encountered in norm of Newton-Raphson residual") # STORE THE INFORMATION IF NEWTON-RAPHSON FAILS if fem_solver.newton_raphson_failed_to_converge: solver.condA = np.NAN TotalDisp = TotalDisp[:,:,:Increment-1] fem_solver.number_of_load_increments = Increment - 1 break # BREAK AT A SPECIFICED LOAD INCREMENT IF ASKED FOR if fem_solver.break_at_increment != -1 and fem_solver.break_at_increment is not None: if fem_solver.break_at_increment == Increment: if fem_solver.break_at_increment < LoadIncrement - 1: print("\nStopping at increment {} as specified\n\n".format(Increment)) TotalDisp = TotalDisp[:,:,:Increment] fem_solver.number_of_load_increments = Increment break return TotalDisp
def QuadBallSphericalArc(center=(0., 0., 0.), inner_radius=9., outer_radius=10., n=10, nthick=1, element_type="hex", cut_threshold=None, portion=1. / 8.): """Similar to QuadBall but hollow and creates only 1/8th or 1/4th or 1/2th of the sphere. Starting and ending angles are not supported. Radial division (nthick: to be consistent with SphericalArc method of Mesh class) is supported input: cut_threshold [float] cutting threshold for element removal since this function is based QuadBall. Ideal value is zero, so prescribe a value as close to zero as possible, however that might not always be possible as the cut might take remove some wanted elements [default = -0.01] portion [float] portion of the sphere to take. Can only be 1/8., 1/4., 1/2. """ assert inner_radius < outer_radius mm = QuadBallSurface(n=n, element_type=element_type) offset = outer_radius * 2. if cut_threshold is None: cut_threshold = -0.01 if portion == 1. / 8.: mm.RemoveElements( np.array([[cut_threshold, cut_threshold, cut_threshold], [offset, offset, offset]])) elif portion == 1. / 4.: mm.RemoveElements( np.array([[cut_threshold, cut_threshold, -offset], [offset, offset, offset]])) elif portion == 1. / 2.: mm.RemoveElements( np.array([[cut_threshold, -offset, -offset], [offset, offset, offset]])) else: raise ValueError("The value of portion can only be 1/8., 1/4. or 1/2.") radii = np.linspace(inner_radius, outer_radius, nthick + 1) mesh = Mesh() mesh.element_type = "hex" mesh.nelem = 0 mesh.nnode = 0 for i in range(nthick): mm1, mm2 = deepcopy(mm), deepcopy(mm) if not np.isclose(radii[i], 1): mm1.points *= radii[i] if not np.isclose(radii[i + 1], 1): mm2.points *= radii[i + 1] if i == 0: elements = np.hstack( (mm1.elements, mm1.nnode + mm2.elements)).astype(np.int64) mesh.elements = np.copy(elements) mesh.points = np.vstack((mm1.points, mm2.points)) else: elements = np.hstack( (mesh.elements[(i - 1) * mm2.nelem:i * mm2.nelem, 4:], mesh.nnode + mm2.elements)).astype(np.int64) mesh.elements = np.vstack((mesh.elements, elements)) mesh.points = np.vstack((mesh.points, mm2.points)) mesh.nelem = mesh.elements.shape[0] mesh.nnode = mesh.points.shape[0] mesh.elements = np.ascontiguousarray(mesh.elements, dtype=np.int64) mesh.nelem = mesh.elements.shape[0] mesh.nnode = mesh.points.shape[0] mesh.GetBoundaryFaces() mesh.GetBoundaryEdges() mesh.points[:, 0] += center[0] mesh.points[:, 1] += center[1] mesh.points[:, 2] += center[2] return mesh
def Torus(show_plot=False): """Custom mesh for torus """ raise NotImplementedError("Not fully implemented yet") # MAKE TORUS WORK from copy import deepcopy from numpy.linalg import norm mesh = Mesh() mesh.Circle(element_type="quad", ncirc=2, nrad=2) tmesh = deepcopy(mesh) arc = GeometricArc(start=(10, 10, 8), end=(10, 10, -8)) # arc.GeometricArc() nlong = 10 points = mesh.Extrude(path=arc, nlong=nlong) # mesh.SimplePlot() # print points # elem_nodes = tmesh.elements[0,:] # p1 = tmesh.points[elem_nodes[0],:] # p2 = tmesh.points[elem_nodes[1],:] # p3 = tmesh.points[elem_nodes[2],:] # p4 = tmesh.points[elem_nodes[3],:] # E1 = np.append(p2 - p1, 0.0) # E2 = np.append(p4 - p1, 0.0) # E3 = np.array([0,0,1.]) # E1 /= norm(E1) # E2 /= norm(E2) # # print E1,E2,E3 # elem_nodes = mesh.elements[0,:] # p1 = mesh.points[elem_nodes[0],:] # p2 = mesh.points[elem_nodes[1],:] # p3 = mesh.points[elem_nodes[2],:] # p4 = mesh.points[elem_nodes[3],:] # p5 = mesh.points[elem_nodes[4],:] # e1 = p2 - p1 # e2 = p4 - p1 # e3 = p5 - p1 # e1 /= norm(e1) # e2 /= norm(e2) # e3 /= norm(e3) # # print e1,e2,e3 # # TRANSFORMATION MATRIX # Q = np.array([ # [np.einsum('i,i',e1,E1), np.einsum('i,i',e1,E2), np.einsum('i,i',e1,E3)], # [np.einsum('i,i',e2,E1), np.einsum('i,i',e2,E2), np.einsum('i,i',e2,E3)], # [np.einsum('i,i',e3,E1), np.einsum('i,i',e3,E2), np.einsum('i,i',e3,E3)] # ]) # mesh.points = np.dot(mesh.points,Q.T) # points = np.dot(points,Q) # E1 = np.array([1,0,0.]) E3 = np.array([0., 0., 1.]) nnode_2D = tmesh.points.shape[0] for i in range(nlong + 1): # e1 = points[i,:][None,:]/norm(points[i,:]) # Q = np.dot(E1[:,None],e1) # vpoints = np.dot(points,Q) e3 = points[i + 1, :] - points[i, :] e3 /= norm(e3) Q = np.dot(e3[:, None], E3[None, :]) # print Q # print np.dot(Q,points[i,:][:,None]) vpoints = np.dot(points, Q) # print current_points mesh.points[nnode_2D * i:nnode_2D * (i + 1), :2] = tmesh.points + points[i, :2] mesh.points[nnode_2D * i:nnode_2D * (i + 1), 2] = vpoints[i, 2] # print Q # print tmesh.points # mesh = Mesh.HexahedralProjection() if show_plot: mesh.SimplePlot() return mesh
def QuadBall(center=(0., 0., 0.), radius=1., n=10, element_type="hex"): """Creates a fully hexahedral mesh on sphere using midpoint subdivision algorithm by creating a cube and spherifying it using PostMesh's projection schemes inputs: n: [int] number of divsion in every direction. Given that this implementation is based on high order bases different divisions in different directions is not possible """ try: from Florence import Mesh, BoundaryCondition, DisplacementFormulation, FEMSolver, LinearSolver from Florence import LinearElastic, NeoHookean from Florence.Tensor import prime_number_factorisation except ImportError: raise ImportError("This function needs Florence's core support") n = int(n) if n > 50: # Values beyond this result in >1M DoFs due to internal prime factoristaion splitting raise ValueError( "The value of n={} (division in each direction) is too high". format(str(n))) if not isinstance(center, tuple): raise ValueError( "The center of the circle should be given in a tuple with two elements (x,y,z)" ) if len(center) != 3: raise ValueError( "The center of the circle should be given in a tuple with two elements (x,y,z)" ) if n == 2 or n == 3 or n == 5 or n == 7: ps = [n] else: def factorise_all(n): if n < 2: n = 2 factors = prime_number_factorisation(n) if len(factors) == 1 and n > 2: n += 1 factors = prime_number_factorisation(n) return factors factors = factorise_all(n) ps = [] for factor in factors: ps += factorise_all(factor) # Do high ps first ps = np.sort(ps)[::-1].tolist() niter = len(ps) # IGS file for sphere with radius 1000. sphere_igs_file_content = SphereIGS() with open("sphere_cad_file.igs", "w") as f: f.write(sphere_igs_file_content) sys.stdout = open(os.devnull, "w") ndim = 3 scale = 1000. condition = 1.e020 mesh = Mesh() material = LinearElastic(ndim, mu=1., lamb=4.) # Keep the solver iterative for low memory consumption. All boundary points are Dirichlet BCs # so they will be exact anyway solver = LinearSolver(linear_solver="iterative", linear_solver_type="cg2", dont_switch_solver=True, iterative_solver_tolerance=1e-9) for it in range(niter): if it == 0: mesh.Parallelepiped(element_type="hex", nx=1, ny=1, nz=1, lower_left_rear_point=(-0.5, -0.5, -0.5), upper_right_front_point=(0.5, 0.5, 0.5)) mesh.GetHighOrderMesh(p=ps[it], equally_spaced=True) boundary_condition = BoundaryCondition() boundary_condition.SetCADProjectionParameters( "sphere_cad_file.igs", scale=scale, condition=condition, project_on_curves=True, solve_for_planar_faces=True, modify_linear_mesh_on_projection=True, fix_dof_elsewhere=False) boundary_condition.GetProjectionCriteria(mesh) formulation = DisplacementFormulation(mesh) fem_solver = FEMSolver(number_of_load_increments=1, analysis_nature="linear", force_not_computing_mesh_qualities=True, report_log_level=0, optimise=True) solution = fem_solver.Solve(formulation=formulation, mesh=mesh, material=material, boundary_condition=boundary_condition, solver=solver) mesh.points += solution.sol[:, :, -1] mesh = mesh.ConvertToLinearMesh() os.remove("sphere_cad_file.igs") if not np.isclose(radius, 1): mesh.points *= radius mesh.points[:, 0] += center[0] mesh.points[:, 1] += center[1] mesh.points[:, 2] += center[2] if element_type == "tet": mesh.ConvertHexesToTets() sys.stdout = sys.__stdout__ return mesh
def QuadBallSurface(center=(0., 0., 0.), radius=1., n=10, element_type="quad"): """Creates a surface quad mesh on sphere using midpoint subdivision algorithm by creating a cube and spherifying it using PostMesh's projection schemes. Unlike the volume QuadBall method there is no restriction on number of divisions here as no system of equations is solved inputs: n: [int] number of divsion in every direction. Given that this implementation is based on high order bases different divisions in different directions is not possible """ try: from Florence import Mesh, BoundaryCondition, DisplacementFormulation, FEMSolver, LinearSolver from Florence import LinearElastic, NeoHookean from Florence.Tensor import prime_number_factorisation except ImportError: raise ImportError("This function needs Florence's core support") n = int(n) if not isinstance(center, tuple): raise ValueError( "The center of the circle should be given in a tuple with two elements (x,y,z)" ) if len(center) != 3: raise ValueError( "The center of the circle should be given in a tuple with two elements (x,y,z)" ) if n == 2 or n == 3 or n == 5 or n == 7: ps = [n] else: def factorise_all(n): if n < 2: n = 2 factors = prime_number_factorisation(n) if len(factors) == 1 and n > 2: n += 1 factors = prime_number_factorisation(n) return factors factors = factorise_all(n) ps = [] for factor in factors: ps += factorise_all(factor) # Do high ps first ps = np.sort(ps)[::-1].tolist() niter = len(ps) sphere_igs_file_content = SphereIGS() with open("sphere_cad_file.igs", "w") as f: f.write(sphere_igs_file_content) sys.stdout = open(os.devnull, "w") ndim = 3 scale = 1000. condition = 1.e020 mesh = Mesh() material = LinearElastic(ndim, mu=1., lamb=4.) for it in range(niter): if it == 0: mesh.Parallelepiped(element_type="hex", nx=1, ny=1, nz=1, lower_left_rear_point=(-0.5, -0.5, -0.5), upper_right_front_point=(0.5, 0.5, 0.5)) mesh = mesh.CreateSurface2DMeshfrom3DMesh() mesh.GetHighOrderMesh(p=ps[it], equally_spaced=True) mesh = mesh.CreateDummy3DMeshfrom2DMesh() formulation = DisplacementFormulation(mesh) else: mesh.GetHighOrderMesh(p=ps[it], equally_spaced=True) mesh = mesh.CreateDummy3DMeshfrom2DMesh() boundary_condition = BoundaryCondition() boundary_condition.SetCADProjectionParameters( "sphere_cad_file.igs", scale=scale, condition=condition, project_on_curves=True, solve_for_planar_faces=True, modify_linear_mesh_on_projection=True, fix_dof_elsewhere=False) boundary_condition.GetProjectionCriteria(mesh) nodesDBC, Dirichlet = boundary_condition.PostMeshWrapper( formulation, mesh, None, None, FEMSolver()) mesh.points[nodesDBC.ravel(), :] += Dirichlet mesh = mesh.CreateSurface2DMeshfrom3DMesh() mesh = mesh.ConvertToLinearMesh() os.remove("sphere_cad_file.igs") if not np.isclose(radius, 1): mesh.points *= radius mesh.points[:, 0] += center[0] mesh.points[:, 1] += center[1] mesh.points[:, 2] += center[2] if element_type == "tri": mesh.ConvertQuadsToTris() sys.stdout = sys.__stdout__ return mesh
def HarvesterPatch(ndisc=20, nradial=4, show_plot=False): """Creates a custom mesh for an energy harvester patch. [Not to be modified] ndisc: [int] number of discretisation in c ndradial: [int] number of discretisation in radial directions for different components of harevester """ center = np.array([30.6979, 20.5]) p1 = np.array([30., 20.]) p2 = np.array([30., 21.]) p1line = p1 - center p2line = p2 - center radius = np.linalg.norm(p1line) pp = np.array([center[0], center[1] + radius]) y_line = pp - center start_angle = -np.pi / 2. - np.arccos( np.linalg.norm(y_line * p1line) / np.linalg.norm(y_line) / np.linalg.norm(p1line)) end_angle = np.pi / 2. + np.arccos( np.linalg.norm(y_line * p1line) / np.linalg.norm(y_line) / np.linalg.norm(p1line)) points = np.array([p1, p2, center]) # nradial = 4 mesh = Mesh() mesh.Arc(element_type="quad", radius=radius, start_angle=start_angle, end_angle=end_angle, nrad=nradial, ncirc=ndisc, center=(center[0], center[1]), refinement=True) mesh1 = Mesh() mesh1.Triangle(element_type="quad", npoints=nradial, c1=totuple(center), c2=totuple(p1), c3=totuple(p2)) mesh += mesh1 mesh_patch = Mesh() mesh_patch.HollowArc(ncirc=ndisc, nrad=nradial, center=(-7.818181, 44.22727272), start_angle=np.arctan(44.22727272 / -7.818181), end_angle=np.arctan(-24.22727272 / 37.818181), element_type="quad", inner_radius=43.9129782, outer_radius=44.9129782) mesh3 = Mesh() mesh3.Triangle(element_type="quad", npoints=nradial, c2=totuple(p1), c3=totuple(p2), c1=(mesh_patch.points[0, 0], mesh_patch.points[0, 1])) mesh += mesh3 mesh += mesh_patch mesh.Extrude(nlong=ndisc, length=40) if show_plot: mesh.SimplePlot() return mesh
def SubdivisionCircle(center=(0., 0.), radius=1., nrad=16, ncirc=40, element_type="tri", refinement=False, refinement_level=2): """Creates a mesh on circle using midpoint subdivision. This function is internally called from Mesh.Circle if 'midpoint_subdivision' algorithm is selected """ r = float(radius) h_r = float(radius) / 2. nx = int(ncirc / 4.) ny = int(nrad / 2.) if nx < 3: warn("Number of division in circumferential direction too low") mesh = Mesh() mesh.Rectangle(element_type="quad", lower_left_point=(-1., -1.), upper_right_point=(1., 1.), nx=nx, ny=ny) uv = np.array([ [-1., -1], [1., -1], [1., 1], [-1., 1], ]) t = np.pi / 4 end_points = np.array([ [-h_r * np.cos(t), h_r * np.sin(t)], [h_r * np.cos(t), h_r * np.sin(t)], [r * np.cos(t), r * np.sin(t)], [-r * np.cos(t), r * np.sin(t)], ]) edge_points = mesh.points[np.unique(mesh.edges), :] new_end_points = [] new_end_points.append(end_points[0, :]) new_end_points.append(end_points[1, :]) new_end_points.append(end_points[2, :]) tt = np.linspace(np.pi / 4, 3 * np.pi / 4, nx) x = r * np.cos(tt) y = r * np.sin(tt) interp_p = np.vstack((x, y)).T for i in range(1, len(x) - 1): new_end_points.append([x[i], y[i]]) new_end_points.append(end_points[3, :]) new_end_points = np.array(new_end_points) new_uv = [] new_uv.append(uv[0, :]) new_uv.append(uv[1, :]) new_uv.append(uv[2, :]) L = 0. for i in range(1, interp_p.shape[0]): L += np.linalg.norm(interp_p[i, :] - interp_p[i - 1, :]) interp_uv = [] last_uv = uv[2, :] for i in range(1, interp_p.shape[0] - 1): val = (uv[3, :] - uv[2, :]) * np.linalg.norm( interp_p[i, :] - interp_p[i - 1, :]) / L + last_uv last_uv = np.copy(val) interp_uv.append(val) interp_uv = np.array(interp_uv) new_uv = np.array(new_uv) if interp_uv.shape[0] != 0: new_uv = np.vstack((new_uv, interp_uv)) new_uv = np.vstack((new_uv, uv[3, :])) from Florence.FunctionSpace import MeanValueCoordinateMapping new_points = np.zeros_like(mesh.points) for i in range(mesh.nnode): point = MeanValueCoordinateMapping(mesh.points[i, :], new_uv, new_end_points) new_points[i, :] = point mesh.points = new_points rmesh = deepcopy(mesh) rmesh.points = mesh.Rotate(angle=np.pi / 2., copy=True) mesh += rmesh rmesh.points = rmesh.Rotate(angle=np.pi / 2., copy=True) mesh += rmesh rmesh.points = rmesh.Rotate(angle=np.pi / 2., copy=True) mesh += rmesh mesh.LaplacianSmoothing(niter=10) qmesh = Mesh() qmesh.Rectangle(element_type="quad", lower_left_point=(-h_r * np.cos(t), -h_r * np.sin(t)), upper_right_point=(h_r * np.cos(t), h_r * np.sin(t)), nx=nx, ny=nx) mesh += qmesh mesh.LaplacianSmoothing(niter=20) mesh.points[:, 0] += center[0] mesh.points[:, 1] += center[1] if refinement: mesh.Refine(level=refinement_level) if element_type == "tri": sys.stdout = open(os.devnull, "w") mesh.ConvertQuadsToTris() sys.stdout = sys.__stdout__ return mesh
def SubdivisionArc(center=(0., 0.), radius=1., nrad=16, ncirc=40, start_angle=0., end_angle=np.pi / 2., element_type="tri", refinement=False, refinement_level=2): """Creates a mesh on circle using midpoint subdivision. This function is internally called from Mesh.Circle if 'midpoint_subdivision' algorithm is selected """ if start_angle != 0. and end_angle != np.pi / 2.: raise ValueError( "Subdivision based arc only produces meshes for a quarter-circle arc for now" ) r = float(radius) h_r = float(radius) / 2. nx = int(ncirc / 4.) ny = int(nrad / 2.) if nx < 3: warn("Number of division in circumferential direction too low") mesh = Mesh() mesh.Rectangle(element_type="quad", lower_left_point=(-1., -1.), upper_right_point=(1., 1.), nx=nx, ny=ny) uv = np.array([ [-1., -1], [1., -1], [1., 1], [-1., 1], ]) t = np.pi / 4. end_points = np.array([ [0., h_r * np.sin(t)], [h_r * np.cos(t), h_r * np.sin(t)], [r * np.cos(t), r * np.sin(t)], [0., radius], ]) edge_points = mesh.points[np.unique(mesh.edges), :] new_end_points = [] new_end_points.append(end_points[0, :]) new_end_points.append(end_points[1, :]) new_end_points.append(end_points[2, :]) tt = np.linspace(np.pi / 4, np.pi / 2, nx) x = r * np.cos(tt) y = r * np.sin(tt) interp_p = np.vstack((x, y)).T for i in range(1, len(x) - 1): new_end_points.append([x[i], y[i]]) new_end_points.append(end_points[3, :]) new_end_points = np.array(new_end_points) new_uv = [] new_uv.append(uv[0, :]) new_uv.append(uv[1, :]) new_uv.append(uv[2, :]) L = 0. for i in range(1, interp_p.shape[0]): L += np.linalg.norm(interp_p[i, :] - interp_p[i - 1, :]) interp_uv = [] last_uv = uv[2, :] for i in range(1, interp_p.shape[0] - 1): val = (uv[3, :] - uv[2, :]) * np.linalg.norm( interp_p[i, :] - interp_p[i - 1, :]) / L + last_uv last_uv = np.copy(val) interp_uv.append(val) interp_uv = np.array(interp_uv) new_uv = np.array(new_uv) if interp_uv.shape[0] != 0: new_uv = np.vstack((new_uv, interp_uv)) new_uv = np.vstack((new_uv, uv[3, :])) from Florence.FunctionSpace import MeanValueCoordinateMapping new_points = np.zeros_like(mesh.points) # All nodes barring the ones lying on the arc for i in range(mesh.nnode - nx - 1): point = MeanValueCoordinateMapping(mesh.points[i, :], new_uv, new_end_points) new_points[i, :] = point # The nodes on the arc are not exactly on the arc # so they need to be snapped/clipped tt = np.linspace(np.pi / 4, np.pi / 2, nx + 1)[::-1] x = r * np.cos(tt) y = r * np.sin(tt) new_points[mesh.nnode - nx - 1:, :] = np.vstack((x, y)).T mesh.points = new_points rmesh = deepcopy(mesh) rmesh.points = mesh.Rotate(angle=-np.pi / 2., copy=True) rmesh.points[:, 1] *= -1. mesh += rmesh mesh.LaplacianSmoothing(niter=10) qmesh = Mesh() qmesh.Rectangle(element_type="quad", lower_left_point=(0.0, 0.0), upper_right_point=(h_r * np.cos(t), h_r * np.sin(t)), nx=nx, ny=nx) mesh += qmesh # mesh.LaplacianSmoothing(niter=20) NodeSliderSmootherArc(mesh, niter=20) mesh.points[:, 0] += center[0] mesh.points[:, 1] += center[1] if refinement: mesh.Refine(level=refinement_level) if element_type == "tri": sys.stdout = open(os.devnull, "w") mesh.ConvertQuadsToTris() sys.stdout = sys.__stdout__ return mesh
def StaticSolver(self, function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition): LoadIncrement = self.number_of_load_increments LoadFactor = 1. / LoadIncrement AppliedDirichletInc = np.zeros( boundary_condition.applied_dirichlet.shape[0], dtype=np.float64) for Increment in range(LoadIncrement): # CHECK ADAPTIVE LOAD FACTOR if self.load_factor is not None: LoadFactor = self.load_factor[Increment] # APPLY NEUMANN BOUNDARY CONDITIONS DeltaF = LoadFactor * NeumannForces NodalForces += DeltaF # OBRTAIN INCREMENTAL RESIDUAL - CONTRIBUTION FROM BOTH NEUMANN AND DIRICHLET Residual = -boundary_condition.ApplyDirichletGetReducedMatrices( K, Residual, boundary_condition.applied_dirichlet, LoadFactor=LoadFactor, only_residual=True) Residual -= DeltaF # GET THE INCREMENTAL DISPLACEMENT AppliedDirichletInc = LoadFactor * boundary_condition.applied_dirichlet t_increment = time() # LET NORM OF THE FIRST RESIDUAL BE THE NORM WITH RESPECT TO WHICH WE # HAVE TO CHECK THE CONVERGENCE OF NEWTON RAPHSON. TYPICALLY THIS IS # NORM OF NODAL FORCES if Increment == 0: self.NormForces = np.linalg.norm(Residual) # AVOID DIVISION BY ZERO if np.isclose(self.NormForces, 0.0): self.NormForces = 1e-14 self.norm_residual = np.linalg.norm(Residual) / self.NormForces if self.iterative_technique == "newton_raphson": Eulerx, Eulerp, K, Residual = self.NewtonRaphson( function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc) elif self.iterative_technique == "modified_newton_raphson": Eulerx, Eulerp, K, Residual = self.ModifiedNewtonRaphson( function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc) # UPDATE DISPLACEMENTS FOR THE CURRENT LOAD INCREMENT TotalDisp[:, Increment] = Eulerp # PRINT LOG IF ASKED FOR if self.print_incremental_log: dmesh = Mesh() dmesh.points = TotalDisp[:, :formulation.ndim, Increment] dmesh_bounds = dmesh.Bounds if formulation.fields == "electro_mechanics": _bounds = np.zeros((2, formulation.nvar)) _bounds[:, :formulation.ndim] = dmesh_bounds _bounds[:, -1] = [ TotalDisp[:, -1, Increment].min(), TotalDisp[:, -1, Increment].max() ] print( "\nMinimum and maximum incremental solution values at increment {} are \n" .format(Increment), _bounds) else: print( "\nMinimum and maximum incremental solution values at increment {} are \n" .format(Increment), dmesh_bounds) # SAVE INCREMENTAL SOLUTION IF ASKED FOR if self.save_incremental_solution: from scipy.io import savemat if self.incremental_solution_filename is not None: savemat(self.incremental_solution_filename + "_" + str(Increment), {'solution': TotalDisp[:, :, Increment]}, do_compression=True) else: raise ValueError( "No file name provided to save incremental solution") print('\nFinished Load increment', Increment, 'in', time() - t_increment, 'seconds') try: print( 'Norm of Residual is', np.abs( la.norm(Residual[boundary_condition.columns_in]) / self.NormForces), '\n') except RuntimeWarning: print( "Invalid value encountered in norm of Newton-Raphson residual" ) # STORE THE INFORMATION IF NEWTON-RAPHSON FAILS if self.newton_raphson_failed_to_converge: solver.condA = np.NAN if Increment == 0: TotalDisp = TotalDisp.ravel() else: TotalDisp = TotalDisp[:, :Increment] self.number_of_load_increments = Increment break # BREAK AT A SPECIFICED LOAD INCREMENT IF ASKED FOR if self.break_at_increment != -1 and self.break_at_increment is not None: if self.break_at_increment == Increment: if self.break_at_increment < LoadIncrement - 1: print("\nStopping at increment {} as specified\n\n". format(Increment)) TotalDisp = TotalDisp[:, :, :Increment] self.number_of_load_increments = Increment break # print(TotalDisp.shape) return TotalDisp
def test_BEM(): """Unnecessary test for the ugly and non-working and legacy BEM for the sake of coverage """ from Florence.BoundaryElements import GetBasesBEM2D from Florence.BoundaryElements import GenerateCoordinates from Florence.BoundaryElements import CoordsJacobianRadiusatGaussPoints, CoordsJacobianRadiusatGaussPoints_LM from Florence.BoundaryElements import AssemblyBEM2D from Florence.BoundaryElements.Assembly import AssemblyBEM2D_Sparse from Florence.BoundaryElements import Sort_BEM from Florence import QuadratureRule, FunctionSpace, Mesh # Unnecessary loop for i in range(10): mesh = Mesh() mesh.element_type = "line" mesh.points = np.array([ [0.,0.], [1.,0.], [1.,1.], [0.,1.], ]) mesh.elements = np.array([ [0,1], [1,2], [2,3], [3,0], ]) mesh.nelem = 4 q = QuadratureRule(mesh_type="line") for C in range(10): N, dN = GetBasesBEM2D(C,q.points) N, dN = GetBasesBEM2D(2,q.points) global_coord = np.zeros((mesh.points.shape[0],3)) global_coord[:,:2] = mesh.points Jacobian = 2*np.ones((q.weights.shape[0],mesh.nelem)) nx = 4*np.ones((q.weights.shape[0],mesh.nelem)) ny = 3*np.ones((q.weights.shape[0],mesh.nelem)) XCO = 2*np.ones((q.weights.shape[0],mesh.nelem)) YCO = np.ones((q.weights.shape[0],mesh.nelem)) N = np.ones((mesh.elements.shape[1],q.weights.shape[0])) dN = 0.5*np.ones((mesh.elements.shape[1],q.weights.shape[0])) GenerateCoordinates(mesh.elements,mesh.points,0,q.points) CoordsJacobianRadiusatGaussPoints(mesh.elements,global_coord,0,N,dN,q.weights) # Not working # CoordsJacobianRadiusatGaussPoints_LM(mesh.elements,global_coord[:,:3],0,N,dN,q.weights,mesh.elements) class GeoArgs(object): Lagrange_Multipliers = "activated" def __init__(self): Lagrange_Multipliers = "activated" geo_args = GeoArgs() K1, K2 = AssemblyBEM2D(0,global_coord,mesh.elements,mesh.elements,dN,N, q.weights,q.points,Jacobian, nx, ny, XCO, YCO, geo_args) AssemblyBEM2D_Sparse(0,global_coord,mesh.elements,mesh.elements,dN,N, q.weights,q.points,Jacobian, nx, ny, XCO, YCO, geo_args) bdata = np.zeros((2*mesh.points.shape[0],2)) bdata[:4,1] = -1 bdata[4:,0] = -1 Sort_BEM(bdata,K1, K2)
E1 = [1.,0.,0.] E2 = [0.,1.,0.] E3 = [0.,0.,1.] # MAKE A SINGLE INSTANCE OF MATERIAL AND UPDATE IF NECESSARY import Florence.MaterialLibrary pmaterial_func = getattr(Florence.MaterialLibrary,material.mtype,None) pmaterial_dict = deepcopy(material.__dict__) del pmaterial_dict['ndim'], pmaterial_dict['mtype'] pmaterial = pmaterial_func(2,**pmaterial_dict) print("The problem requires 2D analyses. Solving", number_of_planar_surfaces, "2D problems") for niter in range(number_of_planar_surfaces): pmesh = Mesh() if mesh.element_type == "tet": pmesh.element_type = "tri" no_face_vertices = 3 elif mesh.element_type == "hex": pmesh.element_type = "quad" no_face_vertices = 4 else: raise ValueError("Curvilinear mesher for element type {} not yet implemented".format(mesh.element_type)) pmesh.elements = mesh.faces[planar_mesh_faces[planar_mesh_faces[:,1]==surface_flags[niter,0],0],:] pmesh.nelem = np.int64(surface_flags[niter,1]) pmesh.GetBoundaryEdges() unique_edges = np.unique(pmesh.edges).astype(nodesDBC.dtype) # Dirichlet2D = np.zeros((unique_edges.shape[0],3)) # nodesDBC2D = np.zeros(unique_edges.shape[0]).astype(np.int64)
def StaticSolverDisplacementControl(self, function_spaces, formulation, solver, K, NeumannForces, NodalForces, Residual, mesh, TotalDisp, Eulerx, Eulerp, material, boundary_condition): LoadIncrement = self.number_of_load_increments self.accumulated_load_factor = 0.0 AppliedDirichletInc = np.zeros( boundary_condition.applied_dirichlet.shape[0], dtype=np.float64) # GET TOTAL FORCE TotalForce = np.copy(NeumannForces) # TotalForce = boundary_condition.ApplyDirichletGetReducedMatrices(K,TotalForce, # boundary_condition.applied_dirichlet,LoadFactor=1.0,only_residual=True) # self.max_prescribed_displacement = np.max(np.abs(boundary_condition.applied_dirichlet)) self.max_prescribed_displacement = 20. self.incremental_displacement = self.max_prescribed_displacement / self.number_of_load_increments # print(self.incremental_displacement) # exit() for Increment in range(LoadIncrement): # CHECK ADAPTIVE LOAD FACTOR # if self.load_factor is not None: # self.local_load_factor = self.load_factor[Increment] # else: # if Increment <= 1: # self.local_load_factor = 1./LoadIncrement # else: # # GET THE REDUCED SYSTEM OF EQUATIONS # K_b, F_bb = boundary_condition.GetReducedMatrices(K,TotalForce)[:2] # # SOLVE THE SYSTEM # sol_b = solver.Solve(K_b,F_bb) # # GET ITERATIVE SOLUTION # dU_b = boundary_condition.UpdateFreeDoFs(sol_b,K.shape[0],formulation.nvar) # # max_occured_displacement = np.max(np.abs((TotalDisp[:,:,Increment-1] - TotalDisp[:,:,Increment-2]))) # max_occured_displacement = np.max(np.abs(dU_b)) # self.local_load_factor = max_occured_displacement/self.max_prescribed_displacement # # print(self.local_load_factor) # # exit() # self.accumulated_load_factor += self.local_load_factor # # print(self.accumulated_load_factor) # GET THE REDUCED SYSTEM OF EQUATIONS K_b, F_bb = boundary_condition.GetReducedMatrices(K, TotalForce)[:2] # SOLVE THE SYSTEM sol_b = solver.Solve(K_b, F_bb) # GET ITERATIVE SOLUTION dU_b = boundary_condition.UpdateFreeDoFs(sol_b, K.shape[0], formulation.nvar) max_occured_displacement = np.max(np.abs(dU_b)) self.local_load_factor = self.incremental_displacement / max_occured_displacement if self.local_load_factor > 1.0: raise ValueError("Raise max displacements") # print(self.local_load_factor,max_occured_displacement) # exit() self.accumulated_load_factor += self.local_load_factor # print(self.accumulated_load_factor) # APPLY NEUMANN BOUNDARY CONDITIONS DeltaF = self.local_load_factor * NeumannForces NodalForces += DeltaF # OBRTAIN INCREMENTAL RESIDUAL - CONTRIBUTION FROM BOTH NEUMANN AND DIRICHLET Residual = -boundary_condition.ApplyDirichletGetReducedMatrices( K, Residual, boundary_condition.applied_dirichlet, LoadFactor=self.local_load_factor, only_residual=True) Residual -= DeltaF # GET THE INCREMENTAL DISPLACEMENT AppliedDirichletInc = self.local_load_factor * boundary_condition.applied_dirichlet t_increment = time() # LET NORM OF THE FIRST RESIDUAL BE THE NORM WITH RESPECT TO WHICH WE # HAVE TO CHECK THE CONVERGENCE OF NEWTON RAPHSON. TYPICALLY THIS IS # NORM OF NODAL FORCES if Increment == 0: self.NormForces = np.linalg.norm(Residual) # AVOID DIVISION BY ZERO if np.isclose(self.NormForces, 0.0): self.NormForces = 1e-14 self.norm_residual = np.linalg.norm(Residual) / self.NormForces Eulerx, Eulerp, K, Residual = NewtonRaphsonDisplacementControl( self, function_spaces, formulation, solver, Increment, K, NodalForces, Residual, mesh, Eulerx, Eulerp, material, boundary_condition, AppliedDirichletInc, NeumannForces, TotalForce, TotalDisp) # UPDATE DISPLACEMENTS FOR THE CURRENT LOAD INCREMENT TotalDisp[:, :formulation.ndim, Increment] = Eulerx - mesh.points if formulation.fields == "electro_mechanics": TotalDisp[:, -1, Increment] = Eulerp # PRINT LOG IF ASKED FOR if self.print_incremental_log: dmesh = Mesh() dmesh.points = TotalDisp[:, :formulation.ndim, Increment] dmesh_bounds = dmesh.Bounds if formulation.fields == "electro_mechanics": _bounds = np.zeros((2, formulation.nvar)) _bounds[:, :formulation.ndim] = dmesh_bounds _bounds[:, -1] = [ TotalDisp[:, -1, Increment].min(), TotalDisp[:, -1, Increment].max() ] print( "\nMinimum and maximum incremental solution values at increment {} are \n" .format(Increment), _bounds) else: print( "\nMinimum and maximum incremental solution values at increment {} are \n" .format(Increment), dmesh_bounds) # SAVE INCREMENTAL SOLUTION IF ASKED FOR if self.save_incremental_solution: from scipy.io import savemat if self.incremental_solution_filename is not None: savemat(self.incremental_solution_filename + "_" + str(Increment), {'solution': TotalDisp[:, :, Increment]}, do_compression=True) else: raise ValueError( "No file name provided to save incremental solution") print('\nFinished Load increment', Increment, 'in', time() - t_increment, 'seconds') try: print( 'Norm of Residual is', np.abs( la.norm(Residual[boundary_condition.columns_in]) / self.NormForces), '\n') except RuntimeWarning: print( "Invalid value encountered in norm of Newton-Raphson residual") # STORE THE INFORMATION IF NEWTON-RAPHSON FAILS if self.newton_raphson_failed_to_converge: solver.condA = np.NAN TotalDisp = TotalDisp[:, :, :Increment] self.number_of_load_increments = Increment break # BREAK AT A SPECIFICED LOAD INCREMENT IF ASKED FOR if self.break_at_increment != -1 and self.break_at_increment is not None: if self.break_at_increment == Increment: if self.break_at_increment < LoadIncrement - 1: print("\nStopping at increment {} as specified\n\n".format( Increment)) TotalDisp = TotalDisp[:, :, :Increment] break return TotalDisp