def smallestDirection(x,method='inertia',return_size=False): """Return the direction of the smallest dimension of a Coords - `x`: a Coords-like array - `method`: one of 'inertia' or 'random' - return_size: if True and `method` is 'inertia', a tuple of a direction vector and the size along that direction and the cross directions; else, only return the direction vector. """ x = x.reshape(-1,3) if method == 'inertia': # The idea is to take the smallest dimension in a coordinate # system aligned with the global axes. C,r,Ip,I = x.inertia() X = x.trl(-C).rot(r) sizes = X.sizes() i = sizes.argmin() # r gives the directions as column vectors! # TODO: maybe we should change that N = r[:,i] if return_size: return N,sizes[i] else: return N elif method == 'random': # Take the mean of the normals on randomly created triangles from plugins.trisurface import TriSurface n = x.shape[0] m = 3 * (n // 3) e = arange(m) random.shuffle(e) if n > m: e = concatenate([e,[0,1,n-1]]) el = e[-3:] S = TriSurface(x,e.reshape(-1,3)) A,N = S.areaNormals() ok = where(isnan(N).sum(axis=1) == 0)[0] N = N[ok] N = N*N N = N.mean(axis=0) N = sqrt(N) N = normalize(N) return N
def createShellModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces()) nodes = S.coords elems = S.elems # the triangles clear() draw(F) # Shell section and material properties # VALUES SHOULD BE SET CORRECTLY glass_plate = { 'name': 'glass_plate', 'sectiontype': 'shell', 'thickness': 18, 'material': 'glass', } glass = { 'name': 'glass', 'young_modulus': 72000, 'shear_modulus': 26200, 'density': 2.5e-9, # T/mm**3 } print glass_plate print glass glasssection = ElemSection(section=glass_plate,material=glass) PDB = PropertyDB() # All elements have same property: PDB.elemProp(set=arange(len(elems)),section=glasssection,eltype='STRI3') # Calculate the nodal loads # Area of triangles area,normals = S.areaNormals() print "Area:\n%s" % area ### DEFINE LOAD CASE (ask user) ### res = askItems([('Glass',True),('Snow',False)]) if not res: return step = 0 if res['Glass']: step += 1 NODLoad = zeros((S.ncoords(),3)) # add the GLASS weight wgt = 450e-6 # N/mm**2 # Or, calculate weight from density: # wgt = glass_plate['thickness'] * glass['density'] * 9810 # assemble uniform glass load for e,a in zip(S.elems,area): NODLoad[e] += [ 0., 0., - a * wgt / 3 ] # Put the nodal loads in the properties database for i,P in enumerate(NODLoad): PDB.nodeProp(tag=step,set=i,cload=[P[0],P[1],P[2],0.,0.,0.]) if res['Snow']: step += 1 NODLoad = zeros((S.ncoords(),3)) # add NON UNIFORM SNOW fn = 'hesperia-nieve.prop' snowp = fromfile(fn,sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 } # assemble non-uniform snow load for e,a,p in zip(S.elems,area,snowp): NODLoad[e] += [ 0., 0., - a * snow_non_uniform[p] / 3] # Put the nodal loads in the properties database for i,P in enumerate(NODLoad): PDB.nodeProp(tag=step,set=[i],cload=[P[0],P[1],P[2],0.,0.,0.]) # Get support nodes botnodes = where(isClose(nodes[:,2], 0.0))[0] bot = nodes[botnodes].reshape((-1,1,3)) pf.message("There are %s support nodes." % bot.shape[0]) botofs = bot + [ 0.,0.,-0.2] bbot2 = concatenate([bot,botofs],axis=1) print bbot2.shape S = Formex(bbot2) draw(S) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) # Since we left out the ring beam, we enforce no movement at the botnodes bc = PDB.nodeProp(set=botnodes,bound=[1,1,1,0,0,0],csys=CoordSystem('C',[0,0,0,0,0,1])) # And we record the name of the bottom nodes set botnodeset = Nset(bc.nr) fe_model = Dict(dict(nodes=nodes,elems=elems,prop=PDB,botnodeset=botnodeset,nsteps=step)) export({'fe_model':fe_model}) smooth() lights(False)
def createFrameModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ wireframe() lights(False) # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) nodes = S.coords elems = S.elems # the triangles # Create edges and faces from edges print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces()) # Create the steel structure E = Formex(nodes[S.getEdges()]) clear() draw(E) # Get the tri elements that are part of a quadrilateral: prop = F.prop quadtri = S.getFaceEdges()[prop==6] nquadtri = quadtri.shape[0] print "%s triangles are part of quadrilateral faces" % nquadtri if nquadtri > 0: # Create triangle definitions of the quadtri faces tri = Connectivity.tangle(quadtri,S.getEdges()) D = Formex(nodes[tri]) clear() flatwire() draw(D,color='yellow') conn = connections(quadtri) print conn # Filter out the single connection edges internal = [ c[0] for c in conn if len(c[1]) > 1 ] print "Internal edges in quadrilaterals: %s" % internal E = Formex(nodes[S.getEdges()],1) E.prop[internal] = 6 wireframe() clear() draw(E) # Remove internal edges tubes = S.getEdges()[E.prop != 6] print "Number of tube elements after removing %s internals: %s" % (len(internal),tubes.shape[0]) D = Formex(nodes[tubes],1) clear() draw(D) # Beam section and material properties b = 60 h = 100 t = 4 b1 = b-2*t h1 = h-2*t A = b*h - b1*h1 print b*h**3 I1 = (b*h**3 - b1*h1**3) / 12 I2 = (h*b**3 - h1*b1**3) / 12 I12 = 0 J = 4 * A**2 / (2*(b+h)/t) tube = { 'name':'tube', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J } steel = { 'name':'steel', 'young_modulus' : 206000, 'shear_modulus' : 81500, 'density' : 7.85e-9, } print tube print steel tubesection = ElemSection(section=tube,material=steel) # Calculate the nodal loads # Area of triangles area,normals = S.areaNormals() print "Area:\n%s" % area # compute bar lengths bars = nodes[tubes] barV = bars[:,1,:] - bars[:,0,:] barL = sqrt((barV*barV).sum(axis=-1)) print "Member length:\n%s" % barL ### DEFINE LOAD CASE (ask user) ### res = askItems([('Steel',True), ('Glass',True), ('Snow',False), ('Solver',None,'select',['Calpy','Abaqus']), ]) if not res: return nlc = 0 for lc in [ 'Steel','Glass','Snow' ]: if res[lc]: nlc += 1 NODLoad = zeros((nlc,S.ncoords(),3)) nlc = 0 if res['Steel']: # the STEEL weight lwgt = steel['density'] * tube['cross_section'] * 9810 # mm/s**2 print "Weight per length %s" % lwgt # assemble steel weight load for e,L in zip(tubes,barL): NODLoad[nlc,e] += [ 0., 0., - L * lwgt / 2 ] nlc += 1 if res['Glass']: # the GLASS weight wgt = 450e-6 # N/mm**2 # assemble uniform glass load for e,a in zip(S.elems,area): NODLoad[nlc,e] += [ 0., 0., - a * wgt / 3 ] nlc += 1 if res['Snow']: # NON UNIFORM SNOW fn = 'hesperia-nieve.prop' snowp = fromfile(fn,sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 } # assemble non-uniform snow load for e,a,p in zip(S.elems,area,snowp): NODLoad[nlc,e] += [ 0., 0., - a * snow_non_uniform[p] / 3] nlc += 1 # For Abaqus: put the nodal loads in the properties database print NODLoad PDB = PropertyDB() for lc in range(nlc): for i,P in enumerate(NODLoad[lc]): PDB.nodeProp(tag=lc,set=i,cload=[P[0],P[1],P[2],0.,0.,0.]) # Get support nodes botnodes = where(isClose(nodes[:,2], 0.0))[0] bot = nodes[botnodes] pf.message("There are %s support nodes." % bot.shape[0]) # Upper structure nnodes = nodes.shape[0] # node number offset ntubes = tubes.shape[0] # element number offset PDB.elemProp(set=arange(ntubes),section=tubesection,eltype='FRAME3D') # Create support systems (vertical beams) bot2 = bot + [ 0.,0.,-200.] # new nodes 200mm below bot botnodes2 = arange(botnodes.shape[0]) + nnodes # node numbers nodes = concatenate([nodes,bot2]) supports = column_stack([botnodes,botnodes2]) elems = concatenate([tubes,supports]) ## !!! ## THIS SHOULD BE FIXED !!! supportsection = ElemSection(material=steel,section={ 'name':'support', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J }) PDB.elemProp(set=arange(ntubes,elems.shape[0]),section=supportsection,eltype='FRAME3D') # Finally, the botnodes2 get the support conditions botnodes = botnodes2 ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # No movement, since we left out the ring beam ## for i in botnodes: ## NodeProperty(i,bound=[1,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) # Draw the supports S = connect([Formex(bot),Formex(bot2)]) draw(S,color='black') if res['Solver'] == 'Calpy': fe_model = Dict(dict(solver='Calpy',nodes=nodes,elems=elems,prop=PDB,loads=NODLoad,botnodes=botnodes,nsteps=nlc)) else: fe_model = Dict(dict(solver='Abaqus',nodes=nodes,elems=elems,prop=PDB,botnodes=botnodes,nsteps=nlc)) export({'fe_model':fe_model}) print "FE model created and exported as 'fe_model'"
def createShellModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) print("The structure has %s nodes, %s edges and %s faces" % (S.ncoords(), S.nedges(), S.nfaces())) nodes = S.coords elems = S.elems # the triangles clear() draw(F) # Shell section and material properties # VALUES SHOULD BE SET CORRECTLY glass_plate = { 'name': 'glass_plate', 'sectiontype': 'shell', 'thickness': 18, 'material': 'glass', } glass = { 'name': 'glass', 'young_modulus': 72000, 'shear_modulus': 26200, 'density': 2.5e-9, # T/mm**3 } print(glass_plate) print(glass) glasssection = ElemSection(section=glass_plate, material=glass) PDB = PropertyDB() # All elements have same property: PDB.elemProp(set=arange(len(elems)), section=glasssection, eltype='STRI3') # Calculate the nodal loads # Area of triangles area, normals = S.areaNormals() print("Area:\n%s" % area) ### DEFINE LOAD CASE (ask user) ### res = askItems([('Glass', True), ('Snow', False)]) if not res: return step = 0 if res['Glass']: step += 1 NODLoad = zeros((S.ncoords(), 3)) # add the GLASS weight wgt = 450e-6 # N/mm**2 # Or, calculate weight from density: # wgt = glass_plate['thickness'] * glass['density'] * 9810 # assemble uniform glass load for e, a in zip(S.elems, area): NODLoad[e] += [0., 0., -a * wgt / 3] # Put the nodal loads in the properties database for i, P in enumerate(NODLoad): PDB.nodeProp(tag=step, set=i, cload=[P[0], P[1], P[2], 0., 0., 0.]) if res['Snow']: step += 1 NODLoad = zeros((S.ncoords(), 3)) # add NON UNIFORM SNOW fn = '../data/hesperia-nieve.prop' snowp = fromfile(fn, sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1: 333e-6, 2: 133e-6, 3: 133e-6, 4: 266e-6, 5: 266e-6, 6: 667e-6 } # assemble non-uniform snow load for e, a, p in zip(S.elems, area, snowp): NODLoad[e] += [0., 0., -a * snow_non_uniform[p] / 3] # Put the nodal loads in the properties database for i, P in enumerate(NODLoad): PDB.nodeProp(tag=step, set=[i], cload=[P[0], P[1], P[2], 0., 0., 0.]) # Get support nodes botnodes = where(isClose(nodes[:, 2], 0.0))[0] bot = nodes[botnodes].reshape((-1, 1, 3)) pf.message("There are %s support nodes." % bot.shape[0]) botofs = bot + [0., 0., -0.2] bbot2 = concatenate([bot, botofs], axis=1) print(bbot2.shape) S = Formex(bbot2) draw(S) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) # Since we left out the ring beam, we enforce no movement at the botnodes bc = PDB.nodeProp(set=botnodes, bound=[1, 1, 1, 0, 0, 0], csys=CoordSystem('C', [0, 0, 0, 0, 0, 1])) # And we record the name of the bottom nodes set botnodeset = Nset(bc.nr) fe_model = Dict( dict(nodes=nodes, elems=elems, prop=PDB, botnodeset=botnodeset, nsteps=step)) export({'fe_model': fe_model}) smooth() lights(False)
def createFrameModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ wireframe() lights(False) # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) nodes = S.coords elems = S.elems # the triangles # Create edges and faces from edges print("The structure has %s nodes, %s edges and %s faces" % (S.ncoords(), S.nedges(), S.nfaces())) # Remove the edges between to quad triangles drawNumbers(S.coords) quadtri = where(S.prop == 6)[0] nquadtri = quadtri.shape[0] print("%s triangles are part of quadrilateral faces" % nquadtri) faces = S.getElemEdges()[quadtri] cnt, ind, xbin = histogram2(faces.reshape(-1), arange(faces.max() + 1)) rem = where(cnt == 2)[0] print("Total edges %s" % len(S.edges)) print("Removing %s edges" % len(rem)) edges = S.edges[complement(rem, n=len(S.edges))] print("Remaining edges %s" % len(edges)) # Create the steel structure E = Formex(nodes[edges]) clear() draw(E) warning("Beware! This script is currently under revision.") conn = connections(quadtri) print(conn) # Filter out the single connection edges internal = [c[0] for c in conn if len(c[1]) > 1] print("Internal edges in quadrilaterals: %s" % internal) E = Formex(nodes[edges], 1) E.prop[internal] = 6 wireframe() clear() draw(E) # Remove internal edges tubes = edges[E.prop != 6] print("Number of tube elements after removing %s internals: %s" % (len(internal), tubes.shape[0])) D = Formex(nodes[tubes], 1) clear() draw(D) # Beam section and material properties b = 60 h = 100 t = 4 b1 = b - 2 * t h1 = h - 2 * t A = b * h - b1 * h1 print(b * h**3) I1 = (b * h**3 - b1 * h1**3) / 12 I2 = (h * b**3 - h1 * b1**3) / 12 I12 = 0 J = 4 * A**2 / (2 * (b + h) / t) tube = { 'name': 'tube', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J } steel = { 'name': 'steel', 'young_modulus': 206000, 'shear_modulus': 81500, 'density': 7.85e-9, } print(tube) print(steel) tubesection = ElemSection(section=tube, material=steel) # Calculate the nodal loads # Area of triangles area, normals = S.areaNormals() print("Area:\n%s" % area) # compute bar lengths bars = nodes[tubes] barV = bars[:, 1, :] - bars[:, 0, :] barL = sqrt((barV * barV).sum(axis=-1)) print("Member length:\n%s" % barL) ### DEFINE LOAD CASE (ask user) ### res = askItems([ _I('Steel', True), _I('Glass', True), _I('Snow', False), _I('Solver', choices=['Calpy', 'Abaqus']), ]) if not res: return nlc = 0 for lc in ['Steel', 'Glass', 'Snow']: if res[lc]: nlc += 1 NODLoad = zeros((nlc, S.ncoords(), 3)) nlc = 0 if res['Steel']: # the STEEL weight lwgt = steel['density'] * tube['cross_section'] * 9810 # mm/s**2 print("Weight per length %s" % lwgt) # assemble steel weight load for e, L in zip(tubes, barL): NODLoad[nlc, e] += [0., 0., -L * lwgt / 2] nlc += 1 if res['Glass']: # the GLASS weight wgt = 450e-6 # N/mm**2 # assemble uniform glass load for e, a in zip(S.elems, area): NODLoad[nlc, e] += [0., 0., -a * wgt / 3] nlc += 1 if res['Snow']: # NON UNIFORM SNOW fn = '../data/hesperia-nieve.prop' snowp = fromfile(fn, sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1: 333e-6, 2: 133e-6, 3: 133e-6, 4: 266e-6, 5: 266e-6, 6: 667e-6 } # assemble non-uniform snow load for e, a, p in zip(S.elems, area, snowp): NODLoad[nlc, e] += [0., 0., -a * snow_non_uniform[p] / 3] nlc += 1 # For Abaqus: put the nodal loads in the properties database print(NODLoad) PDB = PropertyDB() for lc in range(nlc): for i, P in enumerate(NODLoad[lc]): PDB.nodeProp(tag=lc, set=i, cload=[P[0], P[1], P[2], 0., 0., 0.]) # Get support nodes botnodes = where(isClose(nodes[:, 2], 0.0))[0] bot = nodes[botnodes] pf.message("There are %s support nodes." % bot.shape[0]) # Upper structure nnodes = nodes.shape[0] # node number offset ntubes = tubes.shape[0] # element number offset PDB.elemProp(set=arange(ntubes), section=tubesection, eltype='FRAME3D') # Create support systems (vertical beams) bot2 = bot + [0., 0., -200.] # new nodes 200mm below bot botnodes2 = arange(botnodes.shape[0]) + nnodes # node numbers nodes = concatenate([nodes, bot2]) supports = column_stack([botnodes, botnodes2]) elems = concatenate([tubes, supports]) ## !!! ## THIS SHOULD BE FIXED !!! supportsection = ElemSection(material=steel, section={ 'name': 'support', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J }) PDB.elemProp(set=arange(ntubes, elems.shape[0]), section=supportsection, eltype='FRAME3D') # Finally, the botnodes2 get the support conditions botnodes = botnodes2 ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # No movement, since we left out the ring beam ## for i in botnodes: ## NodeProperty(i,bound=[1,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) # Draw the supports S = connect([Formex(bot), Formex(bot2)]) draw(S, color='black') if res['Solver'] == 'Calpy': fe_model = Dict( dict(solver='Calpy', nodes=nodes, elems=elems, prop=PDB, loads=NODLoad, botnodes=botnodes, nsteps=nlc)) else: fe_model = Dict( dict(solver='Abaqus', nodes=nodes, elems=elems, prop=PDB, botnodes=botnodes, nsteps=nlc)) export({'fe_model': fe_model}) print("FE model created and exported as 'fe_model'")