def create(self): """Actually create the imperfection This modifies all composite layups to replace their existing (constant) ply orientations with values that are defined by a discrete field. .. note:: Must be called from Abaqus. """ from abaqus import mdb from abaqusConstants import ANGLE_0, ROTATION_FIELD from desicos.abaqus.abaqus_functions import modify_composite_layup cc = self.impconf.conecyl mod = mdb.models[cc.model_name] part = mod.parts[cc.part_name_shell] fields = self._create_orientation_fields(mod, part.elements) def modify_orientation(index, kwargs): kwargs['orientationType'] = ANGLE_0 kwargs['additionalRotationType'] = ROTATION_FIELD kwargs['additionalRotationField'] = fields[index].name kwargs.pop('orientation', None) kwargs.pop('orientationValue', None) kwargs.pop('angle', None) return kwargs for layup_name, layup in part.compositeLayups.items(): if not layup.suppressed: modify_composite_layup(part, layup_name, modify_orientation)
def update_after_tis(self): """Call this function after the thickness imperfection(s) are applied, to modify the material properties as well, if needed. """ if not self.use_ti: return from abaqus import mdb from desicos.abaqus.abaqus_functions import modify_composite_layup cc = self.impconf.conecyl part = mdb.models[cc.model_name].parts[cc.part_name_shell] for layup_name in part.compositeLayups.keys(): if layup_name.startswith('CLayup_'): layup = part.compositeLayups[layup_name] thickness = sum(p.thickness for p in layup.plies.values()) scaling_factor = thickness / sum(cc.plyts) suffix = layup_name[-6:] self._update_material(suffix, scaling_factor) def modify_material(index, kwargs): kwargs['material'] = cc.laminapropKeys[index] + suffix return kwargs modify_composite_layup(part, layup_name, modify_material)
def create(self): """Actually create the imperfection .. note:: Must be called from Abaqus. """ from abaqus import mdb from desicos.abaqus.abaqus_functions import modify_composite_layup cc = self.impconf.conecyl part = mdb.models[cc.model_name].parts[cc.part_name_shell] if self.global_sf is not None: MAT_SUFFIX = '_scaled' self._update_material(MAT_SUFFIX, self.global_sf) def modify_mat_thick(index, kwargs): kwargs['thickness'] = cc.plyts[index] * self.global_sf kwargs['material'] = cc.laminapropKeys[index] + MAT_SUFFIX return kwargs modify_composite_layup(part, 'CompositePlate', modify_mat_thick) self.update_after_tis() self.created = True
def change_thickness_ABAQUS(imperfection_file_name, model_name, part_name, stack, t_model, t_measured, H_model, H_measured, R_model, R_best_fit=None, number_of_sets=None, semi_angle=0., stretch_H=False, z_offset_bot=None, scaling_factor=1., num_closest_points=5, power_parameter=2, elems_t=None, t_set=None, use_theta_z_format=False): r"""Applies a given thickness imperfection to the finite element model Assumes that a percentage variation of the laminate thickness can be represented by the same percentage veriation of each ply, i.e., each ply thickness is varied in order to reflect a given measured thickness imperfection field. Parameters ---------- imperfection_file_name : str Full path to the imperfection file. model_name : str Model name. part_name : str Part name. stack : list The stacking sequence of the current model with each angle given in degrees. t_model : float The nominal shell thickness of the current model. t_measured : float The nominal thickness of the measured specimen. H_model : float Total height of the model where the imperfections will be applied to, considering also eventual resin rings. H_measured : float The total height of the measured test specimen, including eventual resin rings at the edges. R_model : float Radius **at the bottom edge** of the model where the imperfections will be applied to. R_best_fit : float, optional Best fit radius obtained with functions :func:`.best_fit_cylinder` or :func:`.best_fit_cone`. number_of_sets : int, optional Defines in how many levels the thicknesses should be divided. If ``None`` it will be based on the input file, and if the threshold of ``100`` is exceeded, ``10`` sections are used. semi_angle : float, optional Cone semi-vertex angle in degrees, when applicable. stretch_H : bool, optional If the measured imperfection data should be stretched to the current model (which may happen when ``H_model!=H_measured``. z_offset_bot : float, optional It is common to have the measured data not covering the whole test specimen, and therefore it will be centralized, if a non-centralized position is desired this parameter can be used for the adjustment. scaling_factor : float, optional A scaling factor that can be used to study the imperfection sensitivity. num_closest_points : int, optional See :func:`the inverse-weighted interpolation algorithm <.inv_weighted>`. power_parameter : float, optional See :func:`the inverse-weighted interpolation algorithm <.inv_weighted>`. elems_t : np.ndarray, optional Interpolated thickness for each element. Can be used to avoid the same interpolation to be performed twice. t_set : set, optional A ``set`` object containing the unique thicknesses that will be used to create the new properties. use_theta_z_format : bool, optional If the new format `\theta, Z, imp` should be used instead of the old `X, Y, Z`. """ from abaqus import mdb import desicos.abaqus.abaqus_functions as abaqus_functions mod = mdb.models[model_name] part = mod.parts[part_name] part_cyl_csys = part.features['part_cyl_csys'] part_cyl_csys = part.datums[part_cyl_csys.id] if use_theta_z_format: if elems_t is None or t_set is None: log('Reading coordinates for elements...') elements = vec_calc_elem_cg(part.elements) log('Coordinates for elements read!') d, d, data = read_theta_z_imp(path=imperfection_file_name, H_measured=H_measured, stretch_H=stretch_H, z_offset_bot=z_offset_bot) data3D = np.zeros((data.shape[0], 4), dtype=FLOAT) z = data[:, 1] z *= H_model alpharad = deg2rad(semi_angle) tana = tan(alpharad) def r_local(z): return R_model - z * tana data3D[:, 0] = r_local(z) * cos(data[:, 0]) data3D[:, 1] = r_local(z) * sin(data[:, 0]) data3D[:, 2] = z data3D[:, 3] = data[:, 2] dist, ans = inv_weighted(data3D, elements[:, :3], ncp=num_closest_points, power_parameter=power_parameter) t_set = set(ans) t_set.discard(0.) #TODO why inv_weighted returns an array with 0. elems_t = np.zeros((elements.shape[0], 2), dtype=FLOAT) elems_t[:, 0] = elements[:, 3] elems_t[:, 1] = ans else: log('Thickness differences already calculated!') else: if elems_t is None or t_set is None: # reading elements data log('Reading coordinates for elements...') elements = vec_calc_elem_cg(part.elements) log('Coordinates for elements read!') # calling translate_nodes function elems_t, t_set = calc_elems_t( imperfection_file_name, nodes=elements, t_model=t_model, t_measured=t_measured, H_model=H_model, H_measured=H_measured, R_model=R_model, R_best_fit=R_best_fit, semi_angle=semi_angle, stretch_H=stretch_H, z_offset_bot=z_offset_bot, num_closest_points=num_closest_points, power_parameter=power_parameter) else: log('Thickness differences already calculated!') # creating sets t_list = [] max_len_t_set = 100 if len(t_set) >= max_len_t_set and number_of_sets in (None, 0): number_of_sets = 10 log('More than {0:d} different thicknesses measured!'.format( max_len_t_set)) log('Forcing a number_of_sets = {0:d}'.format(number_of_sets)) if number_of_sets is None or number_of_sets == 0: number_of_sets = len(t_set) t_list = list(t_set) t_list.sort() else: t_min = min(t_set) t_max = max(t_set) t_list = list(np.linspace(t_min, t_max, number_of_sets + 1)) # grouping elements sets_ids = [[] for i in range(len(t_list))] for entry in elems_t: elem_id, t = entry index = index_within_linspace(t_list, t) sets_ids[index].append(int(elem_id)) # putting elements in sets original_layup = part.compositeLayups['CompositePlate'] for i, set_ids in enumerate(sets_ids): if len(set_ids) == 0: # since t_set_norm * t_model <> t_set originally measured # there may be empty set_ids at the end continue elements = part.elements.sequenceFromLabels(labels=set_ids) suffix = 'measured_imp_t_{0:03d}'.format(i) set_name = 'Set_' + suffix log('Creating set ({0: 7d} elements): {1}'.format( len(set_ids), set_name)) part.Set(name=set_name, elements=elements) region = part.sets[set_name] layup_name = 'CLayup_' + suffix t_diff = (float(t_list[i]) - t_model) * scaling_factor t_scaling_factor = (t_model + t_diff) / t_model def modify_ply(index, kwargs): kwargs['thickness'] *= t_scaling_factor kwargs['region'] = region return kwargs layup = part.CompositeLayup(name=layup_name, objectToCopy=original_layup) layup.resume() abaqus_functions.modify_composite_layup(part=part, layup_name=layup_name, modify_func=modify_ply) # suppress needed to put the new properties to the input file original_layup.suppress() return elems_t, t_set
def change_thickness_ABAQUS(imperfection_file_name, model_name, part_name, stack, t_model, t_measured, H_model, H_measured, R_model, R_best_fit = None, number_of_sets = None, semi_angle = 0., stretch_H = False, z_offset_bot = None, scaling_factor = 1., num_closest_points = 5, power_parameter = 2, num_sec_z = 100, elems_t = None, t_set = None, use_theta_z_format = False): r"""Applies a given thickness imperfection to the finite element model Assumes that a percentage variation of the laminate thickness can be represented by the same percentage veriation of each ply, i.e., each ply thickness is varied in order to reflect a given measured thickness imperfection field. Parameters ---------- imperfection_file_name : str Full path to the imperfection file. model_name : str Model name. part_name : str Part name. stack : list The stacking sequence of the current model with each angle given in degrees. t_model : float The nominal shell thickness of the current model. t_measured : float The nominal thickness of the measured specimen. H_model : float Total height of the model where the imperfections will be applied to, considering also eventual resin rings. H_measured : float The total height of the measured test specimen, including eventual resin rings at the edges. R_model : float Radius **at the bottom edge** of the model where the imperfections will be applied to. R_best_fit : float, optional Best fit radius obtained with functions :func:`.best_fit_cylinder` or :func:`.best_fit_cone`. number_of_sets : int, optional Defines in how many levels the thicknesses should be divided. If ``None`` it will be based on the input file, and if the threshold of ``100`` is exceeded, ``10`` sections are used. semi_angle : float, optional Cone semi-vertex angle in degrees, when applicable. stretch_H : bool, optional If the measured imperfection data should be stretched to the current model (which may happen when ``H_model!=H_measured``. z_offset_bot : float, optional It is common to have the measured data not covering the whole test specimen, and therefore it will be centralized, if a non-centralized position is desired this parameter can be used for the adjustment. scaling_factor : float, optional A scaling factor that can be used to study the imperfection sensitivity. num_closest_points : int, optional See :func:`the inverse-weighted interpolation algorithm <.inv_weighted>`. power_parameter : float, optional See :func:`the inverse-weighted interpolation algorithm <.inv_weighted>`. num_sec_z : int, optional Number of spatial sections used to classify the measured data in order to accelerate the searching algorithms elems_t : np.ndarray, optional Interpolated thickness for each element. Can be used to avoid the same interpolation to be performed twice. t_set : set, optional A ``set`` object containing the unique thicknesses that will be used to create the new properties. use_theta_z_format : bool, optional If the new format `\theta, Z, imp` should be used instead of the old `X, Y, Z`. """ from abaqus import mdb import desicos.abaqus.abaqus_functions as abaqus_functions mod = mdb.models[model_name] part = mod.parts[part_name] part_cyl_csys = part.features['part_cyl_csys'] part_cyl_csys = part.datums[part_cyl_csys.id] if use_theta_z_format: if elems_t is None or t_set is None: log('Reading coordinates for elements...') elements = vec_calc_elem_cg(part.elements) log('Coordinates for elements read!') d, d, data = read_theta_z_imp(path = imperfection_file_name, H_measured = H_measured, stretch_H = stretch_H, z_offset_bot = z_offset_bot) data3D = np.zeros((data.shape[0], 4), dtype=FLOAT) z = data[:, 1] z *= H_model alpharad = deg2rad(semi_angle) tana = tan(alpharad) def r_local(z): return R_model - z*tana data3D[:, 0] = r_local(z)*cos(data[:, 0]) data3D[:, 1] = r_local(z)*sin(data[:, 0]) data3D[:, 2] = z data3D[:, 3] = data[:, 2] ans = inv_weighted(data3D, elements[:, :3], num_sub = num_sec_z, col = 2, ncp = num_closest_points, power_parameter = power_parameter) t_set = set(ans) t_set.discard(0.) #TODO why inv_weighted returns an array with 0. elems_t = np.zeros((elements.shape[0], 2), dtype=FLOAT) elems_t[:, 0] = elements[:, 3] elems_t[:, 1] = ans else: log('Thickness differences already calculated!') else: if elems_t is None or t_set is None: # reading elements data log('Reading coordinates for elements...') elements = vec_calc_elem_cg(part.elements) log('Coordinates for elements read!') # calling translate_nodes function elems_t, t_set = calc_elems_t( imperfection_file_name, nodes = elements, t_model = t_model, t_measured = t_measured, H_model = H_model, H_measured = H_measured, R_model = R_model, R_best_fit = R_best_fit, semi_angle = semi_angle, stretch_H = stretch_H, z_offset_bot = z_offset_bot, num_closest_points = num_closest_points, power_parameter = power_parameter, num_sec_z = num_sec_z) else: log('Thickness differences already calculated!') # creating sets t_list = [] max_len_t_set = 100 if len(t_set) >= max_len_t_set and number_of_sets in (None, 0): number_of_sets = 10 log('More than {0:d} different thicknesses measured!'.format( max_len_t_set)) log('Forcing a number_of_sets = {0:d}'.format(number_of_sets)) if number_of_sets is None or number_of_sets == 0: number_of_sets = len(t_set) t_list = list(t_set) t_list.sort() else: t_min = min(t_set) t_max = max(t_set) t_list = list(np.linspace(t_min, t_max, number_of_sets+1)) # grouping elements sets_ids = [[] for i in range(len(t_list))] for entry in elems_t: elem_id, t = entry index = index_within_linspace(t_list, t) sets_ids[index].append(int(elem_id)) # putting elements in sets original_layup = part.compositeLayups['CompositePlate'] for i, set_ids in enumerate(sets_ids): if len(set_ids) == 0: # since t_set_norm * t_model <> t_set originally measured # there may be empty set_ids at the end continue elements = part.elements.sequenceFromLabels(labels=set_ids) suffix = 'measured_imp_t_{0:03d}'.format(i) set_name = 'Set_' + suffix log('Creating set ({0: 7d} elements): {1}'.format( len(set_ids), set_name)) part.Set(name = set_name, elements = elements) region = part.sets[set_name] layup_name = 'CLayup_' + suffix t_diff = (float(t_list[i]) - t_model) * scaling_factor t_scaling_factor = (t_model + t_diff)/t_model def modify_ply(index, kwargs): kwargs['thickness'] *= t_scaling_factor kwargs['region'] = region return kwargs layup = part.CompositeLayup(name=layup_name, objectToCopy=original_layup) layup.resume() abaqus_functions.modify_composite_layup(part=part, layup_name=layup_name, modify_func=modify_ply) # suppress needed to put the new properties to the input file original_layup.suppress() return elems_t, t_set