Source code for desicos.abaqus.imperfections.cutout

from __future__ import absolute_import

import numpy as np
from numpy import sin, cos

from desicos.abaqus.utils import cyl2rec
from desicos.logger import warn
from desicos.abaqus import abaqus_functions


[docs]class Cutout(object): r"""Cutout Parameters ---------- thetadeg : float Circumferential position of the dimple. pt : float Normalized meridional position. d : float Diameter of the drilling machine. drill_offset_deg : float, optional Angular offset when the drilling is not normal to the shell surface. A positive offset means a positive rotation about the `\theta` axis, along the meridional plane. clearance_factor : float, optional Fraction of the diameter to apply as clearance around the cutout. This clearance is partitoned and meshed separately from the rest of the cone / cylinder. numel_radial_edge : int, optional Number of elements along the radial edges about the cutout center. This parameter affects the aspect ratio of the elements inside the cutout area. prop_around_cutout : dict, optional Dictionary with keys: - 'mode' : str ('radius' or 'partition') - 'radius' : float - 'stack': list of floats - 'plyts': list of floats - 'mat_names': list of strings . Examples: - Defining a property with ``'mode'='radius'``:: prop_around_cutout = { 'mode': 'radius', 'radius': 10., 'stack': [0, 90, 0], 'plyts': [0.125, 0.125, 0.125], 'mat_names': ['Alum', 'Alum', 'Alum'], } - Defining a property with ``'mode'='partition'``:: prop_around_cutout = { 'mode': 'partition', 'stack': [0, 90, 0], 'plyts': [0.125, 0.125, 0.125], 'mat_names': ['Alum', 'Alum', 'Alum'], } .. note:: ``mat_names`` must be a list of materials already created in the current model in Abaqus """ def __init__(self, thetadeg, pt, d, drill_offset_deg=0., clearance_factor=0.75, numel_radial_edge=4, prop_around_cutout=None): self.thetadeg = thetadeg self.pt = pt self.d = d self.index = None self.drill_offset_deg = drill_offset_deg self.clearance_factor = clearance_factor self.numel_radial_edge = numel_radial_edge self.prop_around_cutout = prop_around_cutout self.impconf = None self.name = '' self.thetadeg1 = None self.thetadeg2 = None self.offsetdeg = None # plotting options self.xaxis = 'd' self.xaxis_label = 'Cutout diameter, mm' self.x = None self.y = None self.z = None def rebuild(self): cc = self.impconf.conecyl H = cc.H r, z = cc.r_z_from_pt(self.pt) self.x, self.y, self.z = cyl2rec(r, self.thetadeg, z) clearance = self.clearance_factor*self.d self.offsetdeg = np.rad2deg((clearance + self.d/2.)/r) self.thetadeg1 = self.thetadeg - self.offsetdeg self.thetadeg2 = self.thetadeg + self.offsetdeg zoffs = clearance + self.d/2. / cos(np.deg2rad(self.drill_offset_deg)) zoffs *= cos(cc.alpharad) self.ptlow = (z - zoffs)/H self.ptup = (z + zoffs)/H self.name = 'cutout' self.thetadegs = [self.thetadeg1, self.thetadeg, self.thetadeg2] self.pts = [self.ptlow, self.pt, self.ptup] def calc_amplitude(self): # Label the cutout diameter as 'amplitude', at least for now. self.amplitude = self.d return self.amplitude def create(self): from abaqus import mdb from abaqusConstants import (SIDE1, SUPERIMPOSE, COPLANAR_EDGES, MIDDLE, XZPLANE, SWEEP, FIXED) from regionToolset import Region cc = self.impconf.conecyl mod = mdb.models[cc.model_name] p = mod.parts[cc.part_name_shell] ra = mod.rootAssembly datums = p.datums d = self.d r, z = cc.r_z_from_pt(self.pt) x, y, z = self.x, self.y, self.z alpharad = cc.alpharad drill_offset_rad = np.deg2rad(self.drill_offset_deg) thetarad = np.deg2rad(self.thetadeg) thetadeg = self.thetadeg thetadeg1 = self.thetadeg1 thetadeg2 = self.thetadeg2 # line defining the z axis _p1 = p.DatumPointByCoordinate(coords=(0, 0, 0)) _p2 = p.DatumPointByCoordinate(coords=(0, 0, 1)) zaxis = p.DatumAxisByTwoPoint(point1=datums[_p1.id], point2=datums[_p2.id]) # line defining the cutting axis self.p1coord = np.array((x, y, z)) dx = d*cos(alpharad - drill_offset_rad)*cos(thetarad) dy = d*cos(alpharad - drill_offset_rad)*sin(thetarad) dz = d*sin(alpharad - drill_offset_rad) self.p0coord = np.array((x-dx, y-dy, z-dz)) self.p2coord = np.array((x+dx, y+dy, z+dz)) p1 = p.DatumPointByCoordinate(coords=self.p1coord) p2 = p.DatumPointByCoordinate(coords=self.p2coord) drillaxis = p.DatumAxisByTwoPoint(point1=datums[p1.id], point2=datums[p2.id]) #TODO get vertices where to pass the cutting plane plow = self.p1coord.copy() pup = self.p1coord.copy() rlow, zlow = cc.r_z_from_pt(self.ptlow) plow[2] = zlow rup, zup = cc.r_z_from_pt(self.ptup) pup[2] = zup diag1pt = p.DatumPointByCoordinate( coords=cyl2rec(rup, thetadeg2, zup)) diag2pt = p.DatumPointByCoordinate( coords=cyl2rec(rup, thetadeg1, zup)) diag3pt = p.DatumPointByCoordinate( coords=cyl2rec(rlow, thetadeg1, zlow)) diag4pt = p.DatumPointByCoordinate( coords=cyl2rec(rlow, thetadeg2, zlow)) diag1 = p.DatumPlaneByThreePoints(point1=datums[p1.id], point2=datums[p2.id], point3=datums[diag1pt.id]) diag2 = p.DatumPlaneByThreePoints(point1=datums[p1.id], point2=datums[p2.id], point3=datums[diag2pt.id]) diag3 = p.DatumPlaneByThreePoints(point1=datums[p1.id], point2=datums[p2.id], point3=datums[diag3pt.id]) diag4 = p.DatumPlaneByThreePoints(point1=datums[p1.id], point2=datums[p2.id], point3=datums[diag4pt.id]) c1 = cyl2rec(0.5*(rup + r), thetadeg + 0.5*self.offsetdeg, 0.5*(z + zup)) c2 = cyl2rec(0.5*(rup + r), thetadeg - 0.5*self.offsetdeg, 0.5*(z + zup)) c3 = cyl2rec(0.5*(rlow + r), thetadeg - 0.5*self.offsetdeg, 0.5*(z + zlow)) c4 = cyl2rec(0.5*(rlow + r), thetadeg + 0.5*self.offsetdeg, 0.5*(z + zlow)) #TODO try / except blocks needed due to an Abaqus bug try: face1 = p.faces.findAt(c1) p.PartitionFaceByDatumPlane(datumPlane=datums[diag1.id], faces=face1) except: pass try: face2 = p.faces.findAt(c2) p.PartitionFaceByDatumPlane(datumPlane=datums[diag2.id], faces=face2) except: pass try: face3 = p.faces.findAt(c3) p.PartitionFaceByDatumPlane(datumPlane=datums[diag3.id], faces=face3) except: pass try: face4 = p.faces.findAt(c4) p.PartitionFaceByDatumPlane(datumPlane=datums[diag4.id], faces=face4) except: pass sketchplane = p.DatumPlaneByPointNormal(point=datums[p2.id], normal=datums[drillaxis.id]) sketchstrans = p.MakeSketchTransform( sketchPlane=datums[sketchplane.id], sketchUpEdge=datums[zaxis.id], sketchPlaneSide=SIDE1, origin=self.p2coord) sketch = mod.ConstrainedSketch(name='__profile__', sheetSize=10.*d, gridSpacing=d/10., transform=sketchstrans) sketch.setPrimaryObject(option=SUPERIMPOSE) p.projectReferencesOntoSketch(sketch=sketch, filter=COPLANAR_EDGES) sketch.CircleByCenterPerimeter(center=(0.0, 0), point1=(0.0, d/2.)) #TODO try / except blocks needed due to an Abaqus bug try: p.PartitionFaceBySketchDistance(sketchPlane=datums[sketchplane.id], sketchUpEdge=datums[zaxis.id], faces=p.faces, sketchPlaneSide=SIDE1, sketch=sketch, distance=1.5*d) except: pass sketch.unsetPrimaryObject() del mod.sketches['__profile__'] while True: faceList = [f[0] for f in p.faces.getClosest(coordinates=((x, y, z),), searchTolerance=(d/2. - 0.5)).values()] if not faceList: break #TODO try / except blocks needed due to an Abaqus bug try: p.RemoveFaces(faceList=faceList, deleteCells=False) except: pass # Seed edges around cutout area numel_per_edge = int(np.ceil(self.offsetdeg / 360.0 * cc.numel_r)) edge_coords = [ (rup, 0.5*(thetadeg1 + thetadeg), zup), (rup, 0.5*(thetadeg2 + thetadeg), zup), (rlow, 0.5*(thetadeg1 + thetadeg), zlow), (rlow, 0.5*(thetadeg2 + thetadeg), zlow), (0.5*(rlow + r), thetadeg1, 0.5*(zlow + z)), (0.5*(rup + r), thetadeg1, 0.5*(zup + z)), (0.5*(rlow + r), thetadeg2, 0.5*(zlow + z)), (0.5*(rup + r), thetadeg2, 0.5*(zup + z)), ] edge_coords = [cyl2rec(*c) for c in edge_coords] edgeList = [e[0] for e in p.edges.getClosest(coordinates=edge_coords, searchTolerance=1.).values()] p.seedEdgeByNumber(edges=edgeList, number=numel_per_edge, constraint=FIXED) # Seed radial edges about the cutout edge_coords = [ (r, 0.5*(thetadeg2 + thetadeg), z), (0.5*(rup + r), 0.5*(thetadeg2 + thetadeg), 0.5*(zup + z)), (0.5*(rup + r), thetadeg, 0.5*(zup + z)), (0.5*(rup + r), 0.5*(thetadeg1 + thetadeg), 0.5*(zup + z)), (r, 0.5*(thetadeg1 + thetadeg), z), (0.5*(rlow + r), 0.5*(thetadeg1 + thetadeg), 0.5*(zlow + z)), (0.5*(rlow + r), thetadeg, 0.5*(zlow + z)), (0.5*(rlow + r), 0.5*(thetadeg2 + thetadeg), 0.5*(zlow + z)), ] edge_coords = [cyl2rec(*c) for c in edge_coords] edgeList = [e[0] for e in p.edges.getClosest(coordinates=edge_coords, searchTolerance=1.).values()] p.seedEdgeByNumber(edges=edgeList, number=self.numel_radial_edge, constraint=FIXED) # Mesh control for cutout faces try: p.setMeshControls(regions=p.faces, technique=SWEEP) except: warn("Unable to set mesh control to 'SWEEP', please check the mesh around your cutout(s)") p.generateMesh() for pload in cc.impconf.ploads: if (pload.pt == self.pt and pload.thetadeg == self.thetadeg and pload.name in mod.loads.keys()): warn("Cutout is in the same location as perturbation load, moving PL to cutout edge") inst_shell = ra.instances['INST_SHELL'] coords = cyl2rec(r, thetadeg + np.rad2deg(self.d/2./r), z) new_vertex = inst_shell.vertices.getClosest( coordinates=[coords], searchTolerance=1.).values()[0][0] #TODO Unfortunately you cannot simply pass a vertex or list of vertices # It has to be some internal abaqus sequence type... work around that: index = inst_shell.vertices.index(new_vertex) region = Region(vertices=inst_shell.vertices[index:index+1]) mod.loads[pload.name].setValues(region=region) if self.prop_around_cutout is not None: self.create_prop_around_cutout() def create_prop_around_cutout(self): if self.prop_around_cutout is not None: from abaqus import mdb from abaqusConstants import CYLINDRICAL cc = self.impconf.conecyl mod = mdb.models[cc.model_name] p = mod.parts[cc.part_name_shell] if not isinstance(self.prop_around_cutout, dict): raise ValueError('prop_around_cutout must be a dictionary') mode = self.prop_around_cutout['mode'] stack = self.prop_around_cutout['stack'] plyts = self.prop_around_cutout['plyts'] mat_names = self.prop_around_cutout['mat_names'] if mode == 'radius': radius = self.prop_around_cutout['radius'] elem_set = p.Set(name='elems_around_cutout_%02d' % self.index, elements=p.elements.getByBoundingCylinder( center1=self.p0coord, center2=self.p2coord, radius=radius)) elif mode == 'partition': selection_radius = (1+self.clearance_factor)*self.d*1.05*2**0.5 elem_set = p.Set(name='elems_around_cutout_%02d' % self.index, faces=p.faces.getByBoundingCylinder( center1=self.p0coord, center2=self.p2coord, radius=selection_radius)) else: raise ValueError('%s is an invalid options for "mode"' % mode) #TODO could get the Csys that already exists... part_csys = p.DatumCsysByThreePoints(name='part_cyl_csys', coordSysType=CYLINDRICAL, origin=(0.0, 0.0, 0.0), point1=(1.0, 0.0, 0.0), point2=(0.0, 1.0, 0.0)) #FIXME create a circular boundary region around the cutout to # guarantee a better mesh for these cases with a new property # around the cutout part_csys = p.datums[part_csys.id] abaqus_functions.create_composite_layup( name='prop_around_cutout_%02d' % self.index, stack=stack, plyts=plyts, mat_names=mat_names, part=p, part_csys=part_csys, region=elem_set) else: raise RuntimeError('prop_around_cutout not defined!')
if __name__ == '__main__': from desicos.abaqus.conecyl import ConeCyl cc = ConeCyl() cc.from_DB('desicos_2014_c17') cc.impconf.ploads = [] cc.impconf.add_cutout(0, 0.5, 100, 45) cc.create_model()