def add_ffi(self, nominal_vf, E_matrix, nu_matrix, use_ti, global_sf=None): """Adds Fiber Fraction Imperfection (FFI) There can be only one of these, so calling this function overrides the previous imperfection, if any. Parameters ---------- nominal_vf : float Nominal fiber volume fraction of the material E_matrix : float Young's modulus of the matrix material nu_matrix : float Poisson's ratio of the matrix material use_ti : bool If ``True``, create varying material properties according to the thickness imperfection data (if present). global_sf : float or ``None`` Global scaling factor to apply to the material thickness. Set to ``None`` to disable. The global scaling may be overridden by a thickness imperfection, if ``use_ti`` (see above) is ``True``. Returns ------- ffi : :class:`.FFI` object. """ if self.ffi is not None: warn('FFI object already set, overriding...') self.ffi = FFI(nominal_vf, E_matrix, nu_matrix, use_ti, global_sf) self.ffi.impconf = self return self.ffi
def calc_msi_amplitude(cc, force=False): """Calculates the mid-surface imperfection of a ConeCyl model .. note:: Must be called from Abaqus. Parameters ---------- cc : ConeCyl object The :class:`.ConeCyl` object already force : bool, optional Does not the check if the finite element model is already created. Returns ------- max_amp : float The maximum absolute amplitude. """ if not force and not cc.created_model: warn('The finite element for the input ConeCyl object is not created') return from abaqus import mdb part = mdb.models[cc.model_name].parts[cc.part_name_shell] coords = np.array([n.coordinates for n in part.nodes]) xs, ys, zs = coords.T node_rs = (xs**2 + ys**2)**0.5 pts = zs/cc.H rs, zs = cc.r_z_from_pt(pts) amps = (node_rs - rs)/np.cos(cc.alpharad) max_amp = max(np.absolute(amps)) return max_amp
def add_ppi(self, info, extra_height): """Adds Ply Piece Imperfection (PPI) There can be only one of these, so calling this function overrides the previous imperfection, if any. Note: Applicable for cones only! Parameters ---------- info : list List of dictionaries with info about the layup of this cone. See :class:`.PPI` for more details extra_height : float Extra height above and below the cone height (`cc.H`) to consider in the ply placement model. Returns ------- ppi : :class:`.PPI` object. """ if self.ppi is not None: warn('PPI object already set, overriding...') self.ppi = PPI(info, extra_height) self.ppi.impconf = self return self.ppi
def calc_msi_amplitude(cc, force=False): """Calculates the mid-surface imperfection of a ConeCyl model .. note:: Must be called from Abaqus. Parameters ---------- cc : ConeCyl object The :class:`.ConeCyl` object already force : bool, optional Does not the check if the finite element model is already created. Returns ------- max_amp : float The maximum absolute amplitude. """ if not force and not cc.created_model: warn('The finite element for the input ConeCyl object is not created') return from abaqus import mdb part = mdb.models[cc.model_name].parts[cc.part_name_shell] coords = np.array([n.coordinates for n in part.nodes]) xs, ys, zs = coords.T node_rs = (xs**2 + ys**2)**0.5 pts = zs/cc.H rs, zs = cc.r_z_from_pt(pts) amps = (node_rs - rs) * np.cos(cc.alpharad) max_amp = max(np.absolute(amps)) return max_amp
def calc_amplitude(self): """Calculate the imperfection amplitude. The odb must be available and it will be used to extract the last frame of the first analysis step, corresponding to the constant loads. """ from abaqus import mdb cc = self.impconf.conecyl mod = mdb.models[cc.model_name] nodes = mod.parts[cc.part_name_shell].nodes # calculate unit normal vector w.r.t. the surface ux = cos(cc.alpharad) * cos(np.deg2rad(self.thetadeg)) uy = cos(cc.alpharad) * sin(np.deg2rad(self.thetadeg)) uz = sin(cc.alpharad) # It would be nicer to calculate this based on e.g. MSI amplitude max_imp = 10 r_TOL = 0.1 # Radius of cylinder to search pt1 = (self.x + max_imp * ux, self.y + max_imp * uy, self.z + max_imp * uz) pt2 = (self.x - max_imp * ux, self.y - max_imp * uy, self.z - max_imp * uz) # Search for our node in a cylinder normal to the surface, because # 'our' node may be moved by a MSI nodes = nodes.getByBoundingCylinder(pt1, pt2, r_TOL) if len(nodes) != 1: warn("Unable to locate node where perturbation load" + "'{0}' is applied. ".format(self.name) + "Cannot calculate imperfection amplitude.") self.amplitude = 0. return 0. odb = cc.attach_results() fo = odb.steps[cc.step1Name].frames[-1].fieldOutputs if not 'U' in fo.keys(): raise RuntimeError('Field output ' U' not available to calculate amplitude') #TODO not sure if this is robust: node.label-1 u, v, w = fo['U'].values[nodes[0].label - 1].data cc.detach_results(odb) alpha = cc.alpharad theta = np.deg2rad(self.thetadeg) amp = -( (u * cos(theta) + v * sin(theta)) * cos(alpha) + w * sin(alpha)) self.amplitude = amp return amp
def rebuild(self): cc = self.impconf.conecyl alpharad = np.deg2rad(cc.alphadeg) self.cbradial = self.cbtotal*cos(alpharad) self.cbmeridional = self.cbtotal*sin(alpharad) self.x, self.y, self.z = self.get_xyz() self.r, z = cc.r_z_from_pt(self.pt) self.thetadeg = self.thetadeg % 360. self.thetadegs = [self.thetadeg] self.pts = [self.pt] self.name = 'CB_pt_{0:03d}_theta_{1:03d}'.format( int(self.pt*100), int(self.thetadeg)) if abs(self.cbtotal) < 0.1*TOL: warn('Ignoring perturbation buckle: {0}'.format(self.name))
def calc_amplitude(self): """Calculates the geometric imperfection of the finite element model .. note:: Must be called from Abaqus. Returns ------- max_amp : float The maximum absolute amplitude. """ if self.created: return calc_msi_amplitude(self.impconf.conecyl, force=True) else: warn('Mid-surface imperfection not created')
def rebuild(self): cc = self.impconf.conecyl alpharad = cc.alpharad self.plradial = self.pltotal*cos(alpharad) self.plz = -self.pltotal*sin(alpharad) self.plx = -self.plradial*cos(np.deg2rad(self.thetadeg)) self.ply = -self.plradial*sin(np.deg2rad(self.thetadeg)) self.x, self.y, self.z = self.get_xyz() self.r, z = cc.r_z_from_pt(self.pt) self.thetadeg = self.thetadeg % 360. self.thetadegs = [self.thetadeg] self.pts = [self.pt] self.name = 'PL_pt_{0:03d}_theta_{1:03d}'.format( int(self.pt*100), int(self.thetadeg)) if abs(self.pltotal) < 0.1*TOL: warn('Ignoring perturbation load: {0}'.format(self.name))
def calc_amplitude(self): """Calculate the imperfection amplitude. The odb must be available and it will be used to extract the last frame of the first analysis step, corresponding to the constant loads. """ from abaqus import mdb cc = self.impconf.conecyl mod = mdb.models[cc.model_name] nodes = mod.parts[cc.part_name_shell].nodes # calculate unit normal vector w.r.t. the surface ux = cos(cc.alpharad)*cos(np.deg2rad(self.thetadeg)) uy = cos(cc.alpharad)*sin(np.deg2rad(self.thetadeg)) uz = sin(cc.alpharad) # It would be nicer to calculate this based on e.g. MSI amplitude max_imp = 10 r_TOL = 0.1 # Radius of cylinder to search pt1 = (self.x + max_imp*ux, self.y + max_imp*uy, self.z + max_imp*uz) pt2 = (self.x - max_imp*ux, self.y - max_imp*uy, self.z - max_imp*uz) # Search for our node in a cylinder normal to the surface, because # 'our' node may be moved by a MSI nodes = nodes.getByBoundingCylinder(pt1, pt2, r_TOL) if len(nodes) != 1: warn("Unable to locate node where perturbation load" + "'{0}' is applied. ".format(self.name) + "Cannot calculate imperfection amplitude.") self.amplitude = 0. return 0. odb = cc.attach_results() fo = odb.steps[cc.step1Name].frames[-1].fieldOutputs if not 'U' in fo.keys(): raise RuntimeError( 'Field output 'U' not available to calculate amplitude') #TODO not sure if this is robust: node.label-1 u, v, w = fo['U'].values[nodes[0].label-1].data cc.detach_results(odb) alpha = cc.alpharad theta = np.deg2rad(self.thetadeg) amp = -((u*cos(theta) + v*sin(theta))*cos(alpha) + w*sin(alpha)) self.amplitude = amp return amp
def create_model(self, force=False): """Triggers the routines to create the model in Abaqus The auxiliary module ``_create_model.py`` is used, from where the functions ``_create_mesh()``, ``_create_load_steps()`` and ``_create_loads_bcs()`` are executed in this order. .. note:: Must be called from Abaqus .. note:: When new functionalities have to be implemented or for any debugging purposes, one can conveniently change file ``_create_model.py`` directly, and using the ``__main__`` section at the end of this file makes it easy to test whatever necessary methods. The tests can be repeatedly run doing:: import os from desicos.abaqus.constants import DAHOME os.chdir(os.path.join(DAHOME, 'conecyl')) execfile('_create_model.py') Parameters ---------- force : bool, optional Forces the model creation even if the finite element model corresponding to this :class:`ConeCyl` object already exists. """ if self.created_model and not force: warn('Finite element model already created') warn('use "force=True" in order to proceed', level=1) return self.rebuild() from _create_model import (_create_mesh, _create_load_steps, _create_loads_bcs) _create_mesh(self) _create_load_steps(self) _create_loads_bcs(self) self.created_model = True
def _update_material(self, suffix, scaling_factor): from abaqus import mdb cc = self.impconf.conecyl mod = mdb.models[cc.model_name] warned = set() for name, laminaprop in zip(cc.laminapropKeys, cc.laminaprops): if len(laminaprop) == 6: new_laminaprop = self.calc_scaled_laminaprop(laminaprop, scaling_factor) else: if name not in warned: warn(('Invalid number of lamina properties for material' + '{0}, or material is isotropic. Fiber Fraction' + ' Imperfection is not applied.').format(name)) warned.add(name) new_laminaprop = laminaprop new_mat = mod.Material(name=(name + suffix), objectToCopy=mod.materials[name]) new_mat.elastic.setValues(table=(new_laminaprop,))
def _update_material(self, suffix, scaling_factor): from abaqus import mdb cc = self.impconf.conecyl mod = mdb.models[cc.model_name] warned = set() for name, laminaprop in zip(cc.laminapropKeys, cc.laminaprops): if len(laminaprop) == 6: new_laminaprop = self.calc_scaled_laminaprop( laminaprop, scaling_factor) else: if name not in warned: warn(('Invalid number of lamina properties for material' + '{0}, or material is isotropic. Fiber Fraction' + ' Imperfection is not applied.').format(name)) warned.add(name) new_laminaprop = laminaprop new_mat = mod.Material(name=(name + suffix), objectToCopy=mod.materials[name]) new_mat.elastic.setValues(table=(new_laminaprop, ))
def attach_results(self): """Attach the odb file into Abaqus If the odb file exists it will be attached in ``session.odbs``, in Abaqus. .. note:: Must be called from Abaqus """ import abaqus odbname = self.model_name + '.odb' odbpath = os.path.join(self.output_dir, odbname) if not os.path.isfile(odbpath): warn('result was not found') return False session = abaqus.session if not odbpath in session.odbs.keys(): return session.openOdb(name=odbname, path=odbpath, readOnly=True) else: return session.odbs[odbname]
def check_completed(self, wait=False, print_found=False): if not self.rebuilt: self.rebuild() tmp = os.path.join(self.output_dir, self.model_name + '.log') if wait == True: log('Waiting for job completion...') #TODO a general function to check the log file while True: if os.path.isfile(tmp): tmpfile = open(tmp, 'r') lines = tmpfile.readlines() tmpfile.close() if len(lines) == 0: continue if len(lines) < 2: continue if lines[-2].find('End Abaqus/Standard Analysis') > -1: if print_found: log('RUN COMPLETED for model {0}'.format( self.model_name)) return True elif lines[-1].find('Abaqus/Analysis exited with errors') > -1: if print_found: log('RUN COMPLETED WITH ERRORS for model {0}'.format( self.model_name)) return True else: if not wait: warn('RUN NOT COMPLETED for model {0}'.format( self.model_name)) return False else: if not wait: warn('RUN NOT STARTED for model {0}'.format( self.model_name)) return False if wait: import time time.sleep(5)
def calc_partitions(self, thetadegs=[], pts=[]): """Updates all circumferential and axial positions to partition This method reads all the imperfections and collects the circumferential positions ``thetadegs`` and the normalized meridional positions ``pts`` where partitions should be created. These two lists will be used in the routines to create an Abaqus model. Parameter --------- thetadegs : list, optional Additional positions where circumferential partitions are desired pts : list, optional Additional positions where meridional partitions are desired """ thetadegs += [0] for imp in self.impconf.imperfections: valid = True for pt in imp.pts: if pt<0 or pt>1.: error('Invalid imperfection: {0}, {1}'.format(imp.index, imp.name)) warn('Ignored imperfection: {0}, {1}'.format(imp.index, imp.name)) valid = False break if valid: thetadegs += imp.thetadegs pts += imp.pts for stringer in self.stringerconf.stringers: thetadegs += stringer.thetadegs self.thetadegs = sorted(list(set(thetadegs))) self.pts = sorted(list(set(pts))) return self.thetadegs, self.pts
def calc_partitions(self, thetadegs=[], pts=[]): """Updates all circumferential and axial positions to partition This method reads all the imperfections and collects the circumferential positions ``thetadegs`` and the normalized meridional positions ``pts`` where partitions should be created. These two lists will be used in the routines to create an Abaqus model. Parameter --------- thetadegs : list, optional Additional positions where circumferential partitions are desired pts : list, optional Additional positions where meridional partitions are desired """ thetadegs += [0] for imp in self.impconf.imperfections: valid = True for pt in imp.pts: if pt < 0 or pt > 1.: error('Invalid imperfection: {0}, {1}'.format( imp.index, imp.name)) warn('Ignored imperfection: {0}, {1}'.format( imp.index, imp.name)) valid = False break if valid: thetadegs += imp.thetadegs pts += imp.pts for stringer in self.stringerconf.stringers: thetadegs += stringer.thetadegs self.thetadegs = sorted(list(set(thetadegs))) self.pts = sorted(list(set(pts))) return self.thetadegs, self.pts
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 read_file(file_name, frequency=1, forced_average_radius=None, H_measured=None, R_best_fit=None, stretch_H=False, z_offset_bot=None, r_TOL=1.): log('Reading imperfection file: {0} ...'.format(file_name)) # user warnings if stretch_H: if z_offset_bot: warn('Because of the stretch_H option, ' + 'consider setting z_offset_bot to None') # reading the imperfection file ignore = False mps = np.loadtxt(file_name, dtype=FLOAT) r = np.sqrt(mps[:, 0]**2 + mps[:, 1]**2) # measuring model dimensions if R_best_fit is None: R_best_fit = np.average(r) warn('The cylinder average radius of the measured points ' + 'assumed to be {0:1.2f}'.format(R_best_fit)) z_min = mps[:, 2].min() z_max = mps[:, 2].max() z_center = (z_max + z_min) / 2. H_points = (z_max - z_min) log('R_best_fit : {0}'.format(R_best_fit)) # applying user inputs R = R_best_fit if forced_average_radius: R = forced_average_radius log('Forced measured radius: {0}'.format(forced_average_radius)) H_model = H_points if not H_measured: H_measured = H_points warn('The cylinder height of the measured points assumed ' + 'to be {0:1.2f}'.format(H_measured)) # calculating default z_offset_bot if not z_offset_bot: if stretch_H: z_offset_bot = 0. else: z_offset_bot = (H_measured - H_points) / 2. offset_z = z_offset_bot - z_min log('H_points : {0}'.format(H_points)) log('H_measured : {0}'.format(H_measured)) log('z_min : {0}'.format(z_min)) log('z_max : {0}'.format(z_max)) log('offset_z : {0}'.format(offset_z)) r_TOL_min = (R * (1 - r_TOL / 100.)) r_TOL_max = (R * (1 + r_TOL / 100.)) cond = np.all(np.array((r > r_TOL_max, r < r_TOL_min)), axis=0) skept = mps[cond] log('Skipping {0} points'.format(len(skept))) mps = mps[np.logical_not(cond)] offset_mps = mps.copy() offset_mps[:, 2] += offset_z norm_mps = offset_mps.copy() norm_mps[:, 0] /= R norm_mps[:, 1] /= R if stretch_H: norm_mps[:, 2] /= H_points else: norm_mps[:, 2] /= H_measured return mps, offset_mps, norm_mps
def calc_nodal_translations(imperfection_file_name, nodes, H_model, H_measured, R_model, R_best_fit, semi_angle, stretch_H, z_offset_bot, rotatedeg, r_TOL, num_closest_points, power_parameter, num_sec_z, sample_size): # reading imperfection file m, o, mps = read_file(file_name=imperfection_file_name, H_measured=H_measured, R_best_fit=R_best_fit, forced_average_radius=R_best_fit, stretch_H=stretch_H, z_offset_bot=z_offset_bot, r_TOL=r_TOL) log('Calculating nodal translations!') if sample_size: num = mps.shape[0] if sample_size < num: log('Using sample_size={0}'.format(sample_size), level=1) mps = mps[sample(range(num), int(sample_size)), :] num_nodes = nodes.shape[0] R_top = R_model - np.tan(np.deg2rad(semi_angle)) * H_model semi_angle = abs(semi_angle) def local_radius(z): return R_model + (R_top - R_model) * z / H_model mps[:, 2] *= H_model if semi_angle < 1.e-6: R_local = R_model else: R_local = local_radius(mps[:, 2]) thetarads = np.arctan2(mps[:, 1], mps[:, 0]) if rotatedeg: thetarads += np.deg2rad(rotatedeg) mps[:, 0] = R_local * np.cos(thetarads) mps[:, 1] = R_local * np.sin(thetarads) num_sec_z = int(num_sec_z) mem_limit = 1024 * 1024 * 1024 * 8 * 2 # 2 GB mem_entries = int(mem_limit / 64) # if float64 is used sec_size = int(num_nodes / num_sec_z) #TODO better memory control... if sec_size**2 * 10 > mem_entries: while True: num_sec_z += 1 sec_size = int(num_nodes / num_sec_z) if sec_size**2 * 10 <= mem_entries: warn('New sec_size {0}'.format(sec_size)) break ncp = num_closest_points nodes = nodes[np.argsort(nodes[:, 2])] mps = mps[np.argsort(mps[:, 2])] nodal_t = np.zeros(nodes.shape, dtype=nodes.dtype) limit = int(num_sec_z / 5) for i in xrange(num_sec_z + 1): i_inf = sec_size * i i_sup = sec_size * (i + 1) if i % limit == 0: log('processed {0:7d} out of {1:7d} entries'.format( min(i_sup, num_nodes), num_nodes), level=1) sub_nodes = nodes[i_inf:i_sup] if not np.any(sub_nodes): continue inf_z = sub_nodes[:, 2].min() sup_z = sub_nodes[:, 2].max() tol = 0.01 if i == 0 or i == num_sec_z: tol = 0.05 while True: cond1 = mps[:, 2] >= inf_z - tol * H_model cond2 = mps[:, 2] <= sup_z + tol * H_model cond = np.all(np.array((cond1, cond2)), axis=0) sub_mps = mps[cond] if not np.any(sub_mps): tol += 0.01 else: break dist = np.subtract.outer(sub_nodes[:, 0], sub_mps[:, 0])**2 dist += np.subtract.outer(sub_nodes[:, 1], sub_mps[:, 1])**2 dist += np.subtract.outer(sub_nodes[:, 2], sub_mps[:, 2])**2 asort = np.argsort(dist, axis=1) lenn = sub_nodes.shape[0] lenp = sub_mps.shape[0] asort_mesh = asort + np.meshgrid( np.arange(lenn) * lenp, np.arange(lenp))[0].transpose() # getting the z coordinate of the closest points sub_mps_z = np.take(sub_mps[:, 2], asort[:, :ncp]) # getting the distance of the closest points dist_ncp = np.take(dist, asort_mesh[:, :ncp]) # avoiding division by zero dist_ncp[(dist_ncp == 0)] == 1.e-12 # calculating the radius of the sub-group of measured points radius = np.sqrt(sub_mps[:, 0]**2 + sub_mps[:, 1]**2) # taking only the radius of the closest points radius_ncp = np.take(radius, asort[:, :ncp]) # weight calculation total_weight = np.sum(1. / (dist_ncp**power_parameter), axis=1) weight = 1. / (dist_ncp**power_parameter) # calculating the local radius for the closest points r_local_ncp = local_radius(sub_mps_z) # computing the new radius r_new = np.sum(radius_ncp * weight / r_local_ncp, axis=1) / total_weight r_new *= local_radius(sub_nodes[:, 2]) #NOTE modified after Regina, Mariano and Saullo decided to use # the imperfection amplitude constant along the whole cone # surface, which represents better the real manufacturing # conditions. In that case the amplitude will be re-scaled # using only the bottom radius # calculating the local radius for the nodes for the new assumption r_local_nodes = np.sqrt(sub_nodes[:, 0]**2 + sub_nodes[:, 1]**2) # calculating the scaling factor required for the new assumption sf = R_model / r_local_nodes theta = np.arctan2(sub_nodes[:, 1], sub_nodes[:, 0]) nodal_t[i_inf : i_sup][:, 0] = \ (r_new*np.cos(theta) - sub_nodes[:, 0])*sf nodal_t[i_inf : i_sup][:, 1] = \ (r_new*np.sin(theta) - sub_nodes[:, 1])*sf nodal_t[i_inf:i_sup][:, 3] = sub_nodes[:, 3] nodal_t = nodal_t[np.argsort(nodal_t[:, 3])] log('Nodal translations calculated!') return nodal_t
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 self.part = p 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.0 * d, gridSpacing=d / 10.0, transform=sketchstrans ) sketch.setPrimaryObject(option=SUPERIMPOSE) p.projectReferencesOntoSketch(sketch=sketch, filter=COPLANAR_EDGES) sketch.CircleByCenterPerimeter(center=(0.0, 0), point1=(0.0, d / 2.0)) # 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 - 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.0).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.0).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.0 / r), z) new_vertex = inst_shell.vertices.getClosest(coordinates=[coords], searchTolerance=1.0).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)
def read_file(file_name, frequency = 1, forced_average_radius = None, H_measured = None, R_best_fit = None, stretch_H = False, z_offset_bot = None, r_TOL = 1.): log('Reading imperfection file: {0} ...'.format(file_name)) # user warnings if stretch_H: if z_offset_bot: warn('Because of the stretch_H option, '+ 'consider setting z_offset_bot to None') # reading the imperfection file ignore = False mps = np.loadtxt(file_name, dtype=FLOAT) r = np.sqrt(mps[:, 0]**2 + mps[:, 1]**2) # measuring model dimensions if R_best_fit is None: R_best_fit = np.average(r) warn('The cylinder average radius of the measured points ' + 'assumed to be {0:1.2f}'.format(R_best_fit)) z_min = mps[:, 2].min() z_max = mps[:, 2].max() z_center = (z_max + z_min)/2. H_points = (z_max - z_min) log('R_best_fit : {0}'.format(R_best_fit)) # applying user inputs R = R_best_fit if forced_average_radius: R = forced_average_radius log('Forced measured radius: {0}'.format(forced_average_radius)) H_model = H_points if not H_measured: H_measured = H_points warn('The cylinder height of the measured points assumed ' + 'to be {0:1.2f}'.format(H_measured)) # calculating default z_offset_bot if not z_offset_bot: if stretch_H: z_offset_bot = 0. else: z_offset_bot = (H_measured - H_points) / 2. offset_z = z_offset_bot - z_min log('H_points : {0}'.format(H_points)) log('H_measured : {0}'.format(H_measured)) log('z_min : {0}'.format(z_min)) log('z_max : {0}'.format(z_max)) log('offset_z : {0}'.format(offset_z)) r_TOL_min = (R * (1-r_TOL/100.)) r_TOL_max = (R * (1+r_TOL/100.)) cond = np.all(np.array((r > r_TOL_max, r < r_TOL_min)), axis=0) skept = mps[cond] log('Skipping {0} points'.format(len(skept))) mps = mps[np.logical_not(cond)] offset_mps = mps.copy() offset_mps[:, 2] += offset_z norm_mps = offset_mps.copy() norm_mps[:, 0] /= R norm_mps[:, 1] /= R if stretch_H: norm_mps[:, 2] /= H_points else: norm_mps[:, 2] /= H_measured return mps, offset_mps, norm_mps
def calc_nodal_translations(imperfection_file_name, nodes, H_model, H_measured, R_model, R_best_fit, semi_angle, stretch_H, z_offset_bot, rotatedeg, r_TOL, num_closest_points, power_parameter, num_sec_z, sample_size): # reading imperfection file m, o, mps = read_file(file_name = imperfection_file_name, H_measured = H_measured, R_best_fit = R_best_fit, forced_average_radius = R_best_fit, stretch_H = stretch_H, z_offset_bot = z_offset_bot, r_TOL = r_TOL) log('Calculating nodal translations!') if sample_size: num = mps.shape[0] if sample_size < num: log('Using sample_size={0}'.format(sample_size), level=1) mps = mps[sample(range(num), int(sample_size)), :] num_nodes = nodes.shape[0] R_top = R_model - np.tan(np.deg2rad(semi_angle)) * H_model semi_angle = abs(semi_angle) def local_radius(z): return R_model + (R_top - R_model) * z / H_model mps[:, 2] *= H_model if semi_angle < 1.e-6: R_local = R_model else: R_local = local_radius(mps[:, 2]) thetarads = np.arctan2(mps[:, 1], mps[:, 0]) if rotatedeg: thetarads += np.deg2rad(rotatedeg) mps[:, 0] = R_local*np.cos(thetarads) mps[:, 1] = R_local*np.sin(thetarads) num_sec_z = int(num_sec_z) mem_limit = 1024*1024*1024*8*2 # 2 GB mem_entries = int(mem_limit / 64) # if float64 is used sec_size = int(num_nodes/num_sec_z) #TODO better memory control... if sec_size**2*10 > mem_entries: while True: num_sec_z +=1 sec_size = int(num_nodes/num_sec_z) if sec_size**2*10 <= mem_entries: warn('New sec_size {0}'.format(sec_size)) break ncp = num_closest_points nodes = nodes[np.argsort(nodes[:, 2])] mps = mps[np.argsort(mps[:, 2])] nodal_t = np.zeros(nodes.shape, dtype=nodes.dtype) limit = int(num_sec_z/5) for i in xrange(num_sec_z+1): i_inf = sec_size*i i_sup = sec_size*(i+1) if i % limit == 0: log('processed {0:7d} out of {1:7d} entries'.format( min(i_sup, num_nodes), num_nodes), level=1) sub_nodes = nodes[i_inf : i_sup] if not np.any(sub_nodes): continue inf_z = sub_nodes[:, 2].min() sup_z = sub_nodes[:, 2].max() tol = 0.01 if i == 0 or i == num_sec_z: tol = 0.05 while True: cond1 = mps[:, 2] >= inf_z - tol*H_model cond2 = mps[:, 2] <= sup_z + tol*H_model cond = np.all(np.array((cond1, cond2)), axis=0) sub_mps = mps[cond] if not np.any(sub_mps): tol += 0.01 else: break dist = np.subtract.outer(sub_nodes[:, 0], sub_mps[:, 0])**2 dist += np.subtract.outer(sub_nodes[:, 1], sub_mps[:, 1])**2 dist += np.subtract.outer(sub_nodes[:, 2], sub_mps[:, 2])**2 asort = np.argsort(dist, axis=1) lenn = sub_nodes.shape[0] lenp = sub_mps.shape[0] asort_mesh = asort + np.meshgrid(np.arange(lenn)*lenp, np.arange(lenp))[0].transpose() # getting the z coordinate of the closest points sub_mps_z = np.take(sub_mps[:, 2], asort[:, :ncp]) # getting the distance of the closest points dist_ncp = np.take(dist, asort_mesh[:, :ncp]) # avoiding division by zero dist_ncp[(dist_ncp==0)] == 1.e-12 # calculating the radius of the sub-group of measured points radius = np.sqrt(sub_mps[:, 0]**2 + sub_mps[:, 1]**2) # taking only the radius of the closest points radius_ncp = np.take(radius, asort[:, :ncp]) # weight calculation total_weight = np.sum(1./(dist_ncp**power_parameter), axis=1) weight = 1./(dist_ncp**power_parameter) # calculating the local radius for the closest points r_local_ncp = local_radius(sub_mps_z) # computing the new radius r_new = np.sum(radius_ncp*weight/r_local_ncp, axis=1)/total_weight r_new *= local_radius(sub_nodes[:, 2]) #NOTE modified after Regina, Mariano and Saullo decided to use # the imperfection amplitude constant along the whole cone # surface, which represents better the real manufacturing # conditions. In that case the amplitude will be re-scaled # using only the bottom radius # calculating the local radius for the nodes for the new assumption r_local_nodes = np.sqrt(sub_nodes[:,0]**2 + sub_nodes[:, 1]**2) # calculating the scaling factor required for the new assumption sf = R_model/r_local_nodes theta = np.arctan2(sub_nodes[:, 1], sub_nodes[:, 0]) nodal_t[i_inf : i_sup][:, 0] = \ (r_new*np.cos(theta) - sub_nodes[:, 0])*sf nodal_t[i_inf : i_sup][:, 1] = \ (r_new*np.sin(theta) - sub_nodes[:, 1])*sf nodal_t[i_inf : i_sup][:, 3] = sub_nodes[:, 3] nodal_t = nodal_t[np.argsort(nodal_t[:, 3])] log('Nodal translations calculated!') return nodal_t
def create(self, force=False): """Applies the mid-surface imperfection in the finite element model .. note:: Must be called from Abaqus. Parameters ---------- force : bool, optional Creates the imperfection even when it is already created """ from abaqus import mdb if self.created: if force: self.created = False self.create() else: return cc = self.impconf.conecyl if self.c0 is None: self.nodal_translations = translate_nodes_ABAQUS( imperfection_file_name = self.path, model_name = cc.model_name, part_name = cc.part_name_shell, H_model = cc.H, H_measured = self.H_measured, R_model = cc.rbot, R_best_fit = self.R_best_fit, semi_angle = cc.alphadeg, stretch_H = self.stretch_H, rotatedeg = self.rotatedeg, scaling_factor = self.scaling_factor, r_TOL = self.r_TOL, num_closest_points = self.ncp, power_parameter = self.power_parameter, num_sec_z = self.num_sec_z, nodal_translations = self.nodal_translations, use_theta_z_format = self.use_theta_z_format, ignore_bot_h = self.ignore_bot_h, ignore_top_h = self.ignore_top_h, sample_size = self.sample_size) else: if self.rotatedeg: warn('"rotatedeg != 0", be sure you included this effect ' + 'when calculating "c0"') self.nodal_translations = translate_nodes_ABAQUS_c0( m0 = self.m0, n0 = self.n0, c0 = self.c0, funcnum = self.funcnum, model_name = cc.model_name, part_name = cc.part_name_shell, H_model = cc.H, semi_angle = cc.alphadeg, scaling_factor = self.scaling_factor, fem_meridian_bot2top = True, ignore_bot_h = self.ignore_bot_h, ignore_top_h = self.ignore_top_h) self.created = True print '%s amplitude = %f' % (self.name, self.calc_amplitude()) return self.nodal_translations
def rebuild(self, force=False, save_rebuild=True): """Updates the properties of the current :class:`.ConeCyl` object Parameters ---------- force : bool Force the update even if it is already rebuilt (even if the ``rebuilt`` attribute is ``True``). save_rebuild : bool Tells if the ``rebuilt`` attribute should be ``True`` after the update. """ if self.rebuilt and not force: return if self.model_name: self.rename = False if self.force_output: self.output_requests += ['SF'] if save_rebuild: #if len(self.impconf.ploads) == 0: #print 'WARNING - separate_load_steps changed to False' #self.separate_load_steps = False if not self.separate_load_steps: self.num_of_steps = 1 if self.separate_load_steps: self.num_of_steps = 2 # angle in radians if self.alphadeg is not None: self.alpharad = np.deg2rad(self.alphadeg) self.rtop = self.rbot if self.rbot is not None and self.H is not None and self.alpharad is not None: self.rtop = self.rbot - np.tan(self.alpharad) * self.H self.L = self.H/np.cos(self.alpharad) # mesh self.rmesh = ((self.rbot - self.rtop)*self.rtop/self.rbot + self.rtop) self.mesh_size = 2*np.pi*self.rmesh/self.numel_r # cutouts for cutout in self.cutouts: cutout.rebuild() # allowables if self.allowable and not self.allowables: self.allowables = [tuple(self.allowable) for i in self.stack] # laminapropKeys if not self.laminapropKeys: self.laminapropKeys = [self.laminapropKey for i in self.stack] # laminaprops if not self.laminaprops: laminaprops = fetch('laminaprops') if self.laminaprop: self.laminaprops = [tuple(self.laminaprop) for i in self.stack] else: self.laminaprops = [laminaprops[k] for k in self.laminapropKeys] # ply thicknesses if not self.plyts: self.plyts = [self.plyt for i in self.stack] else: if isinstance(self.plyts, list): if len(self.plyts) != len(self.stack): self.plyts = [plyts[0] for i in self.stack] else: self.plyts = [plyts for i in self.stack] # calculating ABD matrix if self.direct_ABD_input: self.calc_ABD_matrix() # imperfections if self.betadeg: self.impconf.uneven_top_edge.betadeg = self.betadeg self.impconf.uneven_top_edge.omegadeg = self.omegadeg self.impconf.rebuild() # model_name if self.rename: if not self.study: tmp = [self.name_DB] + [self.impconf.name] self.model_name = '_'.join(tmp) else: self.model_name = (self.study.name + '_model_{0:02d}'.format(self.index+1)) # defining if bottom edge will have GAP elements if self.impconf.uneven_bottom_edge: self.bc_gaps_bottom_edge = True else: self.bc_gaps_bottom_edge = False # defining if top edge will have GAP elements # This is the case if displacment ctrl is used, and either: # - the top edge is uneven # - the relevant faces are not constrained fully (both uR and v), # so a pin-type MPC cannot be used. top_needs_gap = not (self.bc_fix_top_uR and self.bc_fix_top_v) side_needs_gap = self.bc_fix_top_side_u3 and not ( self.bc_fix_top_side_uR and self.bc_fix_top_side_v) if not (top_needs_gap or side_needs_gap): if self.displ_controlled: if self.impconf.uneven_top_edge: self.bc_gaps_top_edge = True else: self.bc_gaps_top_edge = False else: self.bc_gaps_top_edge = False else: if self.displ_controlled: self.bc_gaps_top_edge = True else: self.bc_gaps_top_edge = False if self.linear_buckling: self.bc_gaps_bottom_edge = False self.bc_gaps_top_edge = False if (not self.bc_fix_top_uR or not self.bc_fix_top_v or self.Nxxtop): self.distr_load_top = True if (not self.linear_buckling and self.impconf.uneven_top_edge and self.distr_load_top): warn('Distributed load is not compatible with uneven top edge ' + 'conditions!') warn('Using a concentrated load at the reference point!') self.distr_load_top = False # Apply DLR boundary conditions if self.use_DLR_bc: self.resin_add_BIR = True self.resin_add_BOR = True self.resin_add_TIR = True self.resin_add_TOR = True self.bc_fix_bottom_side_uR = True self.bc_fix_bottom_side_v = False self.bc_fix_bottom_side_u3 = False self.bc_fix_top_side_uR = True self.bc_fix_top_side_v = False self.bc_fix_top_side_u3 = False if save_rebuild: self.rebuilt = True
def rebuild(self, force=False, save_rebuild=True): """Updates the properties of the current :class:`.ConeCyl` object Parameters ---------- force : bool Force the update even if it is already rebuilt (even if the ``rebuilt`` attribute is ``True``). save_rebuild : bool Tells if the ``rebuilt`` attribute should be ``True`` after the update. """ if self.rebuilt and not force: return if self.model_name: self.rename = False if self.force_output: self.output_requests += ['SF'] if save_rebuild: #if len(self.impconf.ploads) == 0: #print 'WARNING - separate_load_steps changed to False' #self.separate_load_steps = False if not self.separate_load_steps: self.num_of_steps = 1 if self.separate_load_steps: self.num_of_steps = 2 # angle in radians if self.alphadeg is not None: self.alpharad = np.deg2rad(self.alphadeg) self.rtop = self.rbot if self.rbot is not None and self.H is not None and self.alpharad is not None: self.rtop = self.rbot - np.tan(self.alpharad) * self.H self.L = self.H / np.cos(self.alpharad) # mesh self.rmesh = ((self.rbot - self.rtop) * self.rtop / self.rbot + self.rtop) self.mesh_size = 2 * np.pi * self.rmesh / self.numel_r # cutouts for cutout in self.cutouts: cutout.rebuild() # allowables if self.allowable and not self.allowables: self.allowables = [tuple(self.allowable) for i in self.stack] # laminapropKeys if not self.laminapropKeys: self.laminapropKeys = [self.laminapropKey for i in self.stack] # laminaprops if not self.laminaprops: laminaprops = fetch('laminaprops') if self.laminaprop: self.laminaprops = [tuple(self.laminaprop) for i in self.stack] else: self.laminaprops = [ laminaprops[k] for k in self.laminapropKeys ] # ply thicknesses if not self.plyts: self.plyts = [self.plyt for i in self.stack] else: if isinstance(self.plyts, list): if len(self.plyts) != len(self.stack): self.plyts = [plyts[0] for i in self.stack] else: self.plyts = [plyts for i in self.stack] # calculating ABD matrix if self.direct_ABD_input: self.calc_ABD_matrix() # imperfections if self.betadeg: self.impconf.uneven_top_edge.betadeg = self.betadeg self.impconf.uneven_top_edge.omegadeg = self.omegadeg self.impconf.rebuild() # model_name if self.rename: if not self.study: tmp = [self.name_DB] + [self.impconf.name] self.model_name = '_'.join(tmp) else: self.model_name = (self.study.name + '_model_{0:02d}'.format(self.index + 1)) # defining if bottom edge will have GAP elements if self.impconf.uneven_bottom_edge: self.bc_gaps_bottom_edge = True else: self.bc_gaps_bottom_edge = False # defining if top edge will have GAP elements # This is the case if displacment ctrl is used, and either: # - the top edge is uneven # - the relevant faces are not constrained fully (both uR and v), # so a pin-type MPC cannot be used. top_needs_gap = not (self.bc_fix_top_uR and self.bc_fix_top_v) side_needs_gap = self.bc_fix_top_side_u3 and not ( self.bc_fix_top_side_uR and self.bc_fix_top_side_v) if not (top_needs_gap or side_needs_gap): if self.displ_controlled: if self.impconf.uneven_top_edge: self.bc_gaps_top_edge = True else: self.bc_gaps_top_edge = False else: self.bc_gaps_top_edge = False else: if self.displ_controlled: self.bc_gaps_top_edge = True else: self.bc_gaps_top_edge = False if self.linear_buckling: self.bc_gaps_bottom_edge = False self.bc_gaps_top_edge = False if (not self.bc_fix_top_uR or not self.bc_fix_top_v or self.Nxxtop): self.distr_load_top = True if (not self.linear_buckling and self.impconf.uneven_top_edge and self.distr_load_top): warn('Distributed load is not compatible with uneven top edge ' + 'conditions!') warn('Using a concentrated load at the reference point!') self.distr_load_top = False # Apply DLR boundary conditions if self.use_DLR_bc: self.resin_add_BIR = True self.resin_add_BOR = True self.resin_add_TIR = True self.resin_add_TOR = True self.bc_fix_bottom_side_uR = True self.bc_fix_bottom_side_v = False self.bc_fix_bottom_side_u3 = False self.bc_fix_top_side_uR = True self.bc_fix_top_side_v = False self.bc_fix_top_side_u3 = False if save_rebuild: self.rebuilt = True