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 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 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 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 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)
# counter = 0 # for i in unique_edges: # nodesDBC2D[counter] = np.where(nodesDBC==i)[0][0] # Dirichlet2D[counter,:] = Dirichlet[nodesDBC2D[counter],:] # counter += 1 # nodesDBC2D = nodesDBC2D.astype(np.int64) # temp_dict = [] # for i in nodesDBC[nodesDBC2D].flatten(): # temp_dict.append(np.where(unique_elements==i)[0][0]) # nodesDBC2D = np.array(temp_dict,copy=False) dirichlet_edges = in2d_unsorted(nodesDBC,unique_edges[:,None]).flatten() nodesDBC2D = in2d_unsorted(unique_elements.astype(nodesDBC.dtype)[:,None],nodesDBC[dirichlet_edges]).flatten() Dirichlet2D = Dirichlet[dirichlet_edges,:] pmesh.points = mesh.points[unique_elements,:] one_element_coord = pmesh.points[pmesh.elements[0,:no_face_vertices],:] # FOR COORDINATE TRANSFORMATION AB = one_element_coord[0,:] - one_element_coord[1,:] AC = one_element_coord[0,:] - one_element_coord[2,:] normal = np.cross(AB,AC) unit_normal = normal/np.linalg.norm(normal) e1 = AB/np.linalg.norm(AB) e2 = np.cross(normal,AB)/np.linalg.norm(np.cross(normal,AB)) e3 = unit_normal # TRANSFORMATION MATRIX
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