1# ============================================================================= 2# PROJECT CHRONO - http:#projectchrono.org 3# 4# Copyright (c) 2014 projectchrono.org 5# All rights reserved. 6# 7# Use of this source code is governed by a BSD-style license that can be found 8# in the LICENSE file at the top level of the distribution and at 9# http:#projectchrono.org/license-chrono.txt. 10# 11# ============================================================================= 12# Authors: Simone Benatti 13# ============================================================================= 14# 15# FEA demo on applying loads to beams 16# 17# ============================================================================= 18 19import pychrono as chrono 20import pychrono.fea as fea 21import copy 22 23print("Copyright (c) 2017 projectchrono.org ") 24 25# Create the physical system 26my_system = chrono.ChSystemSMC() 27 28# Create a mesh: 29my_mesh = fea.ChMesh() 30my_system.Add(my_mesh) 31 32# Create some nodes. 33mnodeA = fea.ChNodeFEAxyzrot(chrono.ChFrameD(chrono.ChVectorD(0, 0, 0))) 34mnodeB = fea.ChNodeFEAxyzrot(chrono.ChFrameD(chrono.ChVectorD(2, 0, 0))) 35 36# Default mass for FEM nodes is zero 37mnodeA.SetMass(0.0) 38mnodeB.SetMass(0.0) 39 40my_mesh.AddNode(mnodeA) 41my_mesh.AddNode(mnodeB) 42 43# Create beam section & material 44msection = fea.ChBeamSectionEulerAdvanced() 45beam_wy = 0.1 46beam_wz = 0.2 47msection.SetAsRectangularSection(beam_wy, beam_wz) 48msection.SetYoungModulus(0.01e9) 49msection.SetGshearModulus(0.01e9 * 0.3) 50msection.SetBeamRaleyghDamping(0.200) 51msection.SetDensity(1500) 52 53# Create a beam of Eulero-Bernoulli type: 54melementA = fea.ChElementBeamEuler() 55melementA.SetNodes(mnodeA, mnodeB) 56melementA.SetSection(msection) 57my_mesh.AddElement(melementA) 58 59# Create also a truss 60truss = chrono.ChBody() 61truss.SetBodyFixed(True) 62my_system.Add(truss) 63 64# Create a constraat the end of the beam 65constr_a = chrono.ChLinkMateGeneric() 66constr_a.Initialize(mnodeA, truss, False, mnodeA.Frame(), mnodeA.Frame()) 67my_system.Add(constr_a) 68constr_a.SetConstrainedCoords(True, True, True, # x, y, z 69 True, True, True) # Rx, Ry, Rz 70 71# APPLY SOME LOADS! 72 73# First: loads must be added to "load containers", 74# and load containers must be added to your system 75mloadcontainer = chrono.ChLoadContainer() 76my_system.Add(mloadcontainer) 77 78# Example 1: 79 80# Add a vertical load to the end of the beam element: 81mwrench = fea.ChLoadBeamWrench(melementA) 82mwrench.loader.SetApplication(1.0) # in -1..+1 range, -1: end A, 0: mid, +1: end B 83mwrench.loader.SetForce(chrono.ChVectorD(0, -0.2, 0)) 84mloadcontainer.Add(mwrench) # do not forget to add the load to the load container. 85 86# Example 2: 87 88# Add a distributed load along the beam element: 89mwrenchdis = fea.ChLoadBeamWrenchDistributed(melementA) 90mwrenchdis.loader.SetForcePerUnit(chrono.ChVectorD(0, -0.1, 0)) # load per unit length 91mloadcontainer.Add(mwrenchdis) 92 93# Example 3: 94 95# Add gravity (constant volumetric load) 96mgravity = chrono.LoadLoaderGravity(melementA) 97mloadcontainer.Add(mgravity) 98 99# note that by default all solid elements in the mesh will already 100# get gravitational force, if you want to bypass this automatic gravity, do: 101my_mesh.SetAutomaticGravity(False) 102 103""" 104CANNOT INSTANTIATE TEMPLATE 105Example 4 and 5 in C++ equivalent demo require respectively ChLoad<MyLoaderTriangular> and ChLoad<MyLoaderPointStiff>(mnodeC) 106 107""" 108 109# Example 6: 110 111# As before, create a custom load with stiff force, acting on a single node, but 112# this time we inherit directly from ChLoadCustom, i.e. a load that does not require ChLoader features. 113# This is mostly used in case one does not need the automatic surface/volume quadrature of ChLoader. 114# As a stiff load, this will automatically generate a jacobian (tangent stiffness matrix K) 115# that will be used in statics, implicit integrators, etc. 116 117mnodeD = fea.ChNodeFEAxyz(chrono.ChVectorD(2, 10, 3)) 118my_mesh.AddNode(mnodeD) 119 120class MyLoadCustom(chrono.ChLoadCustom): 121 def __init__(self, mloadable): 122 chrono.ChLoadCustom.__init__(self, mloadable) 123 #/ "Virtual" copy constructor (covariant return type). 124 def Clone(self): 125 newinst = copy.deepcopy(self) 126 return newinst 127 128 # Compute Q=Q(x,v) 129 # This is the function that you have to implement. It should return the generalized Q load 130 # (i.e.the force in generalized lagrangian coordinates). 131 # For ChNodeFEAxyz, Q loads are expected as 3-rows vectors, containing absolute force x,y,z. 132 # As this is a stiff force field, dependency from state_x and state_y must be considered. 133 def ComputeQ(self,state_x, #/< state position to evaluate Q 134 state_w): #/< state speed to evaluate Q 135 if not state_x==None and not state_w==None : 136 node_pos = chrono.ChVectorD(state_x[0], state_x[1], state_x[2]) 137 node_vel = chrono.ChVectorD(state_w[0], state_w[1], state_w[2]) 138 else: 139 mynode = fea.CastToChNodeFEAxyz( fea.CastToChNodeFEAbase( chrono.CastToChNodeBase(self.loadable) )) 140 node_pos = mynode.GetPos() 141 node_vel = mynode.GetPos_dt() 142 # Just implement a simple force+spring+damper in xy plane, 143 # for spring&damper connected to absolute reference: 144 Kx = 100 145 Ky = 400 146 Dx = 0.6 147 Dy = 0.9 148 x_offset = 2 149 y_offset = 10 150 x_force = 50 151 y_force = 0 152 # Store the computed generalized forces in this.load_Q, same x,y,z order as in state_w 153 self.load_Q[0] = x_force - Kx * (node_pos.x - x_offset) - Dx * node_vel.x 154 self.load_Q[1] = y_force - Ky * (node_pos.y - y_offset) - Dy * node_vel.y 155 self.load_Q[2] = 0 156 157 # Compute jacobian not available from Python 158 159 # Remember to set this as stiff, to enable the jacobians 160 def IsStiff(self) : 161 return True 162 163# Instance load object, applying to a node, as in previous example, and add to container: 164mloadcustom = MyLoadCustom(mnodeD) 165mloadcontainer.Add(mloadcustom) 166 167# Example 7: 168 169# As before, create a custom load with stiff force, acting on MULTIPLE nodes at once. 170# This time we will need the ChLoadCustomMultiple as base class. 171# Those nodes (ie.e ChLoadable objects) can be added in my_mesh in whatever order, 172# not necessarily contiguous, because the bookkeeping is automated. 173# Being a stiff load, a jacobian will be automatically generated 174# by default using numerical differentiation but if you want you 175# can override ComputeJacobian() and compute mK, mR analytically - see prev.example. 176 177mnodeE = fea.ChNodeFEAxyz(chrono.ChVectorD(2, 10, 3)) 178my_mesh.AddNode(mnodeE) 179mnodeF = fea.ChNodeFEAxyz(chrono.ChVectorD(2, 11, 3)) 180my_mesh.AddNode(mnodeF) 181 182class MyLoadCustomMultiple(chrono.ChLoadCustomMultiple): 183 def __init__(self, mloadables): 184 chrono.ChLoadCustomMultiple.__init__(self, mloadables) 185 #/ "Virtual" copy constructor (covariant return type). 186 def Clone(self): 187 print('I am there!') 188 newinst = copy.deepcopy(self) 189 return newinst 190 # Compute Q=Q(x,v) 191 # This is the function that you have to implement. It should return the generalized Q load 192 # (i.e.the force in generalized lagrangian coordinates). 193 # Since here we have multiple connected ChLoadable objects (the two nodes), the rule is that 194 # all the vectors (load_Q, state_x, state_w) are split in the same order that the loadable objects 195 # are added to MyLoadCustomMultiple in this case for instance Q={Efx,Efy,Efz,Ffx,Ffy,Ffz}. 196 # As this is a stiff force field, dependency from state_x and state_y must be considered. 197 def ComputeQ(self,state_x, #/< state position to evaluate Q 198 state_w): #/< state speed to evaluate Q 199 #chrono.ChVectorD Enode_pos 200 #chrono.ChVectorD Enode_vel 201 #chrono.ChVectorD Fnode_pos 202 #chrono.ChVectorD Fnode_vel 203 if not state_x==None and not state_w==None : 204 Enode_pos = chrono.ChVectorD(state_x[0], state_x[1], state_x[2]) 205 Enode_vel = chrono.ChVectorD(state_w[0], state_w[1], state_w[2]) 206 Fnode_pos = chrono.ChVectorD(state_x[3], state_x[4], state_x[5]) 207 Fnode_vel = chrono.ChVectorD(state_w[3], state_w[4], state_w[5]) 208 else: 209 # explicit integrators might call ComputeQ(0,0), null pointers mean 210 # that we assume current state, without passing state_x for efficiency 211 Enode = fea.CastToChNodeFEAxyz( fea.CastToChNodeFEAbase( chrono.CastToChNodeBase(self.loadables[0]))) 212 Fnode = fea.CastToChNodeFEAxyz( fea.CastToChNodeFEAbase( chrono.CastToChNodeBase(self.loadables[1]))) 213 Enode_pos = Enode.GetPos() 214 Enode_vel = Enode.GetPos_dt() 215 Fnode_pos = Fnode.GetPos() 216 Fnode_vel = Fnode.GetPos_dt() 217 # Just implement two simple force+spring+dampers in xy plane: 218 # ... from node E to ground, 219 Kx1 = 60 220 Ky1 = 50 221 Dx1 = 0.3 222 Dy1 = 0.2 223 E_x_offset = 2 224 E_y_offset = 10 225 spring1 = chrono.ChVectorD(-Kx1 * (Enode_pos.x - E_x_offset) - Dx1 * Enode_vel.x, -Ky1 * (Enode_pos.y - E_y_offset) - Dy1 * Enode_vel.y, 0) 226 # ... from node F to node E, 227 Ky2 = 10 228 Dy2 = 0.2 229 EF_dist = 1 230 spring2 = chrono.ChVectorD (0, -Ky2 * (Fnode_pos.y - Enode_pos.y - EF_dist) - Dy2 * (Enode_vel.y - Fnode_vel.y), 0) 231 Fforcey = 2 232 # store generalized forces as a contiguous vector in this.load_Q, with same order of state_w 233 self.load_Q[0] = spring1.x - spring2.x # Fx component of force on 1st node 234 self.load_Q[1] = spring1.y - spring2.y # Fy component of force on 1st node 235 self.load_Q[2] = spring1.z - spring2.z # Fz component of force on 1st node 236 self.load_Q[3] = spring2.x # Fx component of force on 2nd node 237 self.load_Q[4] = spring2.y + Fforcey # Fy component of force on 2nd node 238 self.load_Q[5] = spring2.z # Fz component of force on 2nd node 239 240 # OPTIONAL: if you want to provide an analytical jacobian, just implement the following: 241 # virtual void ComputeJacobian(...) 242 243 # Remember to set this as stiff, to enable the jacobians 244 def IsStiff(self) : 245 return True 246 247 248# Instance load object. This require a list of ChLoadable objects 249# (these are our two nodes,pay attention to the sequence order), and add to container. 250mnodelist = chrono.vector_ChLoadable() 251mnodelist.append(mnodeE) 252mnodelist.append(mnodeF) 253mloadcustommultiple = MyLoadCustomMultiple(mnodelist) 254mloadcontainer.Add(mloadcustommultiple) 255 256###################/ 257 258# Setup a MINRES solver. For FEA one cannot use the default PSOR type solver. 259 260solver = chrono.ChSolverMINRES() 261my_system.SetSolver(solver) 262solver.SetMaxIterations(100) 263solver.SetTolerance(1e-10) 264solver.EnableDiagonalPreconditioner(True) 265solver.SetVerbose(True) 266 267# Perform a static analysis: 268my_system.DoStaticLinear() 269 270print(" constr_a reaction force F= " + str(constr_a.Get_react_force()) ) 271print( " constr_a reaction torque T= " + str(constr_a.Get_react_torque())) 272 273#print("mnodeC position = " + mnodeC.GetPos() ) 274#print("mloadstiff K jacobian=" + mloadstiff.GetJacobians().K ) 275 276print("mnodeD position = ") 277print(mnodeD.GetPos() ) 278print("mloadcustom K jacobian=" ) 279print( mloadcustom.GetJacobians().K.GetMatr() ) 280 281print(" mnodeE position = " ) 282print( mnodeE.GetPos() ) 283print(" mnodeF position = " ) 284print( mnodeF.GetPos() ) 285print(" mloadcustommultiple K jacobian=" ) 286print( mloadcustommultiple.GetJacobians().K.GetMatr() ) 287