def ComputeNeumannForces(self, mesh, materials, function_spaces, compute_traction_forces=True, compute_body_forces=False): """Compute/assemble traction and body forces""" if self.neumann_flags is None: return np.zeros((mesh.points.shape[0]*materials[0].nvar,1),dtype=np.float64) nvar = materials[0].nvar ndim = mesh.InferSpatialDimension() if self.neumann_flags.shape[0] == mesh.points.shape[0]: self.neumann_data_applied_at = "node" else: if ndim==3: if self.neumann_flags.shape[0] == mesh.faces.shape[0]: self.neumann_data_applied_at = "face" elif ndim==2: if self.neumann_flags.shape[0] == mesh.edges.shape[0]: self.neumann_data_applied_at = "face" if self.neumann_data_applied_at == 'face': from Kuru.FiniteElements.Assembly import AssembleForces if not isinstance(function_spaces,tuple): raise ValueError("Boundary functional spaces not available for computing Neumman and body forces") else: # CHECK IF A FUNCTION SPACE FOR BOUNDARY EXISTS - SAFEGAURDS AGAINST FORMULATIONS THAT DO NO PROVIDE ONE has_boundary_spaces = False for fs in function_spaces: if ndim == 3 and fs.ndim == 2: has_boundary_spaces = True break elif ndim == 2 and fs.ndim == 1: has_boundary_spaces = True break if not has_boundary_spaces: from Kuru import QuadratureRule, FunctionSpace # COMPUTE BOUNDARY FUNCTIONAL SPACES p = mesh.InferPolynomialDegree() bquadrature = QuadratureRule(optimal=3, norder=2*p+1, mesh_type=mesh.boundary_element_type, is_flattened=False) bfunction_space = FunctionSpace(mesh.CreateDummyLowerDimensionalMesh(), bquadrature, p=p, equally_spaced=mesh.IsEquallySpaced, use_optimal_quadrature=False) function_spaces = (function_spaces[0],bfunction_space) # raise ValueError("Boundary functional spaces not available for computing Neumman and body forces") t_tassembly = time() if self.analysis_type == "static": F = AssembleForces(self, mesh, materials, function_spaces, compute_traction_forces=compute_traction_forces, compute_body_forces=compute_body_forces) elif self.analysis_type == "dynamic": if self.neumann_flags.ndim==2: # THE POSITION OF NEUMANN DATA APPLIED AT FACES CAN CHANGE DYNAMICALLY tmp_flags = np.copy(self.neumann_flags) tmp_data = np.copy(self.applied_neumann) F = np.zeros((mesh.points.shape[0]*nvar,self.neumann_flags.shape[1])) for step in range(self.neumann_flags.shape[1]): self.neumann_flags = tmp_flags[:,step] self.applied_neumann = tmp_data[:,:,step] F[:,step] = AssembleForces(self, mesh, materials, function_spaces, compute_traction_forces=compute_traction_forces, compute_body_forces=compute_body_forces).flatten() self.neumann_flags = tmp_flags self.applied_neumann = tmp_data else: # THE POSITION OF NEUMANN DATA APPLIED AT FACES CAN CHANGE DYNAMICALLY F = AssembleForces(self, mesh, materials, function_spaces, compute_traction_forces=compute_traction_forces, compute_body_forces=compute_body_forces).flatten() print("Assembled external traction forces. Time elapsed is {} seconds".format(time()-t_tassembly)) elif self.neumann_data_applied_at == 'node': # A DIRICHLET TYPE METHODOLGY FOR APPLYING NEUMANN BOUNDARY CONDITONS (i.e. AT NODES) if self.analysis_type == "dynamic": if self.neumann_flags.ndim ==3: # FOR DYNAMIC ANALYSIS IT IS ASSUMED THAT # to_apply DOOES NOT CHANGE DURING THE ANALYSIS flat_neu = self.neumann_flags[:,:,0].ravel() to_apply = np.arange(self.neumann_flags[:,:,0].size)[~np.isnan(flat_neu)] F = np.zeros((mesh.points.shape[0]*nvar,self.neumann_flags.shape[2])) for step in range(self.neumann_flags.shape[2]): flat_neu = self.neumann_flags[:,:,step].ravel() to_apply = np.arange(self.neumann_flags[:,:,step].size)[~np.isnan(flat_neu)] F[to_apply,step] = flat_neu[~np.isnan(flat_neu)] else: F = np.zeros((mesh.points.shape[0]*nvar,1)) flat_neu = self.neumann_flags.ravel() to_apply = np.arange(self.neumann_flags.size)[~np.isnan(flat_neu)] applied_neumann = flat_neu[~np.isnan(flat_neu)] F[to_apply,0] = applied_neumann else: F = np.zeros((mesh.points.shape[0]*nvar,1)) flat_neu = self.neumann_flags.ravel() to_apply = np.arange(self.neumann_flags.size)[~np.isnan(flat_neu)] applied_neumann = flat_neu[~np.isnan(flat_neu)] F[to_apply,0] = applied_neumann return F
def AssembleRobinForces(boundary_condition, mesh, material, function_spaces, fem_solver, Eulerx, type_load): """Compute/assemble traction (follower)""" ndim = mesh.InferSpatialDimension() nvar = material.nvar if type_load == 'pressure': if boundary_condition.pressure_flags.shape[0] == mesh.points.shape[0]: #boundary_condition.robin_data_applied_at = "node" raise ValueError("Robin boundary forces (pressure) applied at nodes") elif type_load == 'spring': if boundary_condition.spring_flags.shape[0] == mesh.points.shape[0]: #boundary_condition.robin_data_applied_at = "node" raise ValueError("Robin boundary forces (spring) applied at nodes") elif type_load == 'connector': if ndim == 2: if boundary_condition.connector_elements.shape[1] != 2*mesh.edges.shape[1]: raise ValueError("Robin boundary connector should be compose by two boundary elements") else: if boundary_condition.connector_elements.shape[1] != 2*mesh.faces.shape[1]: raise ValueError("Robin boundary connector should be compose by two boundary elements") else: raise ValueError("Load {} not unserstood. Just spring or pressure.".format(type_load)) if not isinstance(function_spaces,tuple): raise ValueError("Boundary functional spaces not available for computing pressure stiffness") else: # CHECK IF A FUNCTION SPACE FOR BOUNDARY EXISTS - SAFEGAURDS AGAINST FORMULATIONS THAT DO NO PROVIDE ONE has_boundary_spaces = False for fs in function_spaces: if ndim == 3 and fs.ndim == 2: has_boundary_spaces = True break elif ndim == 2 and fs.ndim == 1: has_boundary_spaces = True break if not has_boundary_spaces: from Kuru import QuadratureRule, FunctionSpace # COMPUTE BOUNDARY FUNCTIONAL SPACES p = mesh.InferPolynomialDegree() bquadrature = QuadratureRule(optimal=3, norder=2*p+1, mesh_type=mesh.boundary_element_type, is_flattened=False) bfunction_space = FunctionSpace(mesh.CreateDummyLowerDimensionalMesh(), bquadrature, p=p, equally_spaced=mesh.IsEquallySpaced, use_optimal_quadrature=False) function_spaces = (function_spaces[0],bfunction_space) if type_load == 'pressure': from .RobinForces import StaticPressureForces if boundary_condition.analysis_type == "static": if fem_solver.recompute_sparsity_pattern: I_robin, J_robin, V_robin, F_robin = StaticPressureForces(boundary_condition, mesh, material, function_spaces[-1], fem_solver, Eulerx) K_robin = coo_matrix((V_robin,(I_robin,J_robin)), shape=((nvar*mesh.points.shape[0],nvar*mesh.points.shape[0])),dtype=np.float64).tocsr() else: V_robin, F_robin = StaticPressureForces(boundary_condition, mesh, material, function_spaces[-1], fem_solver, Eulerx) K_robin = csr_matrix((V_robin,fem_solver.indices,fem_solver.indptr), shape=((nvar*mesh.points.shape[0],nvar*mesh.points.shape[0]))) elif boundary_condition.analysis_type == "dynamic": raise ValueError("Not implemented yet") return K_robin, F_robin if type_load == 'spring': from .RobinForces import StaticSpringForces if boundary_condition.analysis_type == "static": if fem_solver.recompute_sparsity_pattern: I_robin, J_robin, V_robin, F_robin = StaticSpringForces(boundary_condition, mesh, material, function_spaces[-1], fem_solver, Eulerx) K_robin = coo_matrix((V_robin,(I_robin,J_robin)), shape=((nvar*mesh.points.shape[0],nvar*mesh.points.shape[0])),dtype=np.float64).tocsr() else: V_robin, F_robin = StaticSpringForces(boundary_condition, mesh, material, function_spaces[-1], fem_solver, Eulerx) K_robin = csr_matrix((V_robin,fem_solver.indices,fem_solver.indptr), shape=((nvar*mesh.points.shape[0],nvar*mesh.points.shape[0]))) elif boundary_condition.analysis_type == "dynamic": raise ValueError("Not implemented yet") return K_robin, F_robin if type_load == 'connector': from .RobinForces import StaticConnectorForces if boundary_condition.analysis_type == "static": if fem_solver.recompute_sparsity_pattern: I_robin, J_robin, V_robin, F_robin = StaticConnectorForces(boundary_condition, mesh, material, function_spaces[-1], fem_solver, Eulerx) K_robin = coo_matrix((V_robin,(I_robin,J_robin)), shape=((nvar*mesh.points.shape[0],nvar*mesh.points.shape[0])),dtype=np.float64).tocsr() else: V_robin, F_robin = StaticConnectorForces(boundary_condition, mesh, material, function_spaces[-1], fem_solver, Eulerx) K_robin = csr_matrix((V_robin,fem_solver.indices,fem_solver.indptr), shape=((nvar*mesh.points.shape[0],nvar*mesh.points.shape[0]))) elif boundary_condition.analysis_type == "dynamic": raise ValueError("Not implemented yet") return K_robin, F_robin
def GetFibreStressAndSoftness(self, mesh, formulation, material, fem_solver, Eulerx, average_derived_quantities=True): """ steps: [list,np.1darray] for which time steps/increments the data should be recovered """ det = np.linalg.det inv = np.linalg.inv # GET THE UNDERLYING LINEAR MESH # lmesh = mesh.GetLinearMesh() C = mesh.InferPolynomialDegree() - 1 ndim = mesh.InferSpatialDimension() nelem = mesh.elements.shape[0] npoint = mesh.points.shape[0] nodeperelem = mesh.elements.shape[1] # GET QUADRATURE norder = 2 * C if norder == 0: norder = 1 # quadrature = QuadratureRule(qtype="gauss", norder=norder, mesh_type=mesh.element_type, optimal=3) # Domain = FunctionSpace(mesh, quadrature, p=C+1) Domain = FunctionSpace(mesh, p=C + 1, evaluate_at_nodes=True) Jm = Domain.Jm AllGauss = Domain.AllGauss Bases = Domain.Bases # requires_geometry_update = fem_solver.requires_geometry_update requires_geometry_update = True # ALWAYS TRUE FOR THIS ROUTINE F = np.zeros((material.element_set.shape[0], nodeperelem, ndim, ndim)) # DEFINE CONSTITUENT STRESSES FOR GROWTH-REMODELING PROBLEM ElemFibreStress = np.zeros( (material.element_set.shape[0], nodeperelem, 5)) # 5-fibres ElemSoftness = np.zeros( (material.element_set.shape[0], nodeperelem, 5)) # 5-fibres FibreStress = np.zeros((material.node_set.shape[0], 5)) Softness = np.zeros((material.node_set.shape[0], 5)) # LOOP OVER ELEMENTS for ielem in range(material.element_set.shape[0]): elem = material.element_set[ielem] # GET THE FIELDS AT THE ELEMENT LEVEL LagrangeElemCoords = mesh.points[mesh.elements[elem, :], :] EulerElemCoords = Eulerx[mesh.elements[elem, :], :] # GROWTH-REMODELING VALUES FOR THIS ELEMENT material.MappingStateVariables(mesh, Domain, elem) if material.has_low_level_dispatcher: # GET LOCAL KINEMATICS SpatialGradient, F[ ielem, :, :, :], detJ, dV = _KinematicMeasures_( Jm, AllGauss[:, 0], LagrangeElemCoords, EulerElemCoords, requires_geometry_update) # PARAMETERS FOR INCOMPRESSIBILITY (MEAN DILATATION METHOD HU-WASHIZU) if material.is_incompressible: MaterialVolume = np.sum(dV) if fem_solver.has_growth_remodeling: dve = np.true_divide(detJ, material.StateVariables[:, 20]) CurrentVolume = np.sum(dve) else: CurrentVolume = np.sum(detJ) material.pressure = material.kappa * ( CurrentVolume - MaterialVolume) / MaterialVolume # COMPUTE FIBRE STRESS AND SOFTNESS ElemFibreStress[ielem, :, :], ElemSoftness[ ielem, :, :] = material._ConstituentMeasures_( F[ielem, :, :, :], elem) else: # GAUSS LOOP IN VECTORISED FORM ParentGradientX = np.einsum('ijk,jl->kil', Jm, LagrangeElemCoords) # MATERIAL GRADIENT TENSOR IN PHYSICAL ELEMENT [\nabla_0 (N)] MaterialGradient = np.einsum('ijk,kli->ijl', inv(ParentGradientX), Jm) # DEFORMATION GRADIENT TENSOR [\vec{x} \otimes \nabla_0 (N)] F[ielem, :, :, :] = np.einsum('ij,kli->kjl', EulerElemCoords, MaterialGradient) # COMPUTE REMAINING KINEMATIC MEASURES StrainTensors = KinematicMeasures(F[ielem, :, :, :], fem_solver.analysis_nature) # GEOMETRY UPDATE IS A MUST # MAPPING TENSOR [\partial\vec{X}/ \partial\vec{\varepsilon} (ndim x ndim)] ParentGradientx = np.einsum('ijk,jl->kil', Jm, EulerElemCoords) # SPATIAL GRADIENT TENSOR IN PHYSICAL ELEMENT [\nabla (N)] SpatialGradient = np.einsum('ijk,kli->ilj', inv(ParentGradientx), Jm) # COMPUTE ONCE detJ (GOOD SPEEDUP COMPARED TO COMPUTING TWICE) detJ = np.einsum('i,i,i->i', AllGauss[:, 0], np.abs(det(ParentGradientX)), np.abs(StrainTensors['J'])) # COMPUTE PARAMETERS FOR MEAN DILATATION METHOD, IT NEEDS TO BE BEFORE COMPUTE HESSIAN AND STRESS if material.is_incompressible: dV = np.einsum('i,i->i', AllGauss[:, 0], np.abs(det(ParentGradientX))) MaterialVolume = np.sum(dV) if fem_solver.has_growth_remodeling: dve = np.true_divide(detJ, material.StateVariables[:, 20]) CurrentVolume = np.sum(dve) else: CurrentVolume = np.sum(detJ) material.pressure = material.kappa * ( CurrentVolume - MaterialVolume) / MaterialVolume # LOOP OVER GAUSS POINTS for counter in range(AllGauss.shape[0]): # COMPUTE FIBRE STRESS AND SOFTNESS ElemFibreStress[ielem, counter, :], ElemSoftness[ ielem, counter, :] = material.ConstituentMeasures( StrainTensors, elem, counter) # COMPUTE THE COMMON/NEIGHBOUR NODES ONCE Elss, Poss = material.GetNodeCommonality()[:2] for inode in range(material.node_set.shape[0]): Els, Pos = Elss[inode], Poss[inode] ncommon_nodes = Els.shape[0] for uelem in range(ncommon_nodes): FibreStress[inode, :] += ElemFibreStress[Els[uelem], Pos[uelem], :] Softness[inode, :] += ElemSoftness[Els[uelem], Pos[uelem], :] # AVERAGE OUT FibreStress[inode, :] /= ncommon_nodes Softness[inode, :] /= ncommon_nodes return FibreStress, Softness
def GetQuadraturesAndFunctionSpaces(self, mesh, variables_order=(1, ), quadrature_rules=None, quadrature_type=None, function_spaces=None, compute_post_quadrature=True, equally_spaced_bases=False, quadrature_degree=None): """"The default function for computing quadrature rules and function spaces for equall order single and multi-physics/fields problems""" C = mesh.InferPolynomialDegree() - 1 mesh.InferBoundaryElementType() if quadrature_rules == None and self.quadrature_rules == None: # OPTION FOR QUADRATURE TECHNIQUE FOR TRIS AND TETS optimal_quadrature = 3 if mesh.element_type == "quad" or mesh.element_type == "hex": if quadrature_type == "wv": optimal_quadrature = 4 norder, norder_post = self.GetQuadratureOrder( C, mesh.element_type, quadrature_degree=quadrature_degree) # GET QUADRATURE quadrature = QuadratureRule(optimal=optimal_quadrature, norder=norder, mesh_type=mesh.element_type) if self.compute_post_quadrature: # COMPUTE INTERPOLATION FUNCTIONS AT ALL INTEGRATION POINTS FOR POST-PROCESSING post_quadrature = QuadratureRule(optimal=optimal_quadrature, norder=norder_post, mesh_type=mesh.element_type) else: post_quadrature = None # BOUNDARY QUADRATURE bquadrature = QuadratureRule(optimal=optimal_quadrature, norder=C + 2, mesh_type=mesh.boundary_element_type) self.quadrature_rules = (quadrature, post_quadrature, bquadrature) else: self.quadrature_rules = quadrature_rules if function_spaces == None and self.function_spaces == None: # CREATE FUNCTIONAL SPACES function_space = FunctionSpace(mesh, self.quadrature_rules[0], p=C + 1, equally_spaced=equally_spaced_bases) if self.compute_post_quadrature: post_function_space = FunctionSpace( mesh, self.quadrature_rules[1], p=C + 1, equally_spaced=equally_spaced_bases) else: post_function_space = None # CREATE BOUNDARY FUNCTIONAL SPACES bfunction_space = FunctionSpace( mesh.CreateDummyLowerDimensionalMesh(), self.quadrature_rules[2], p=C + 1, equally_spaced=equally_spaced_bases) self.function_spaces = (function_space, post_function_space, bfunction_space) else: self.function_spaces = function_spaces local_size = self.function_spaces[0].Bases.shape[0] * self.nvar self.local_rows = np.repeat(np.arange(0, local_size), local_size, axis=0) self.local_columns = np.tile(np.arange(0, local_size), local_size) self.local_size = local_size # FOR MASS local_size_m = self.function_spaces[0].Bases.shape[0] * self.ndim self.local_rows_mass = np.repeat(np.arange(0, local_size_m), local_size_m, axis=0) self.local_columns_mass = np.tile(np.arange(0, local_size_m), local_size_m) self.local_size_m = local_size_m