Source code for desicos.abaqus.imperfections.msi

from __future__ import absolute_import

import os

import numpy as np

from desicos.logger import warn
from desicos.conecylDB import update_imps
from desicos.abaqus.constants import *
from desicos.abaqus.apply_imperfections import (calc_translations_ABAQUS,
                                                translate_nodes_ABAQUS,
                                                translate_nodes_ABAQUS_c0)
[docs]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
[docs]class MSI(object): r"""Mid-Surface Imperfection The imperfections are applied using both an inverse-weighted interpolation algorithm, detailed in :func:`.inv_weighted`, or a continuous fitting function, detailed in :func:`.calc_c0`. The following attributes of the :class:`.MSI` object control the inverse-weighted algorithm: =================== ===================================================== Attribute Description =================== ===================================================== ``ncp`` ``int``, number of closest points ``power_parameter`` ``float``, power parameter ``r_TOL`` ``float``, percentage tolerance to ignore noisy data, for example, when ``r_TOL=1.`` the points with a radius `r > 1.1 R_{bot}` =================== ===================================================== Additional attributes are used to apply the imperfection into the finite element model when the inverse-weighted algorithm is selected. ====================== ================================================== Attribute Description ====================== ================================================== ``imp_ms`` ``str``, an entry in the imperfection database, if an entry with this string key is found, it will overwrite the parameters: ``path, R_best_fit, H_measured`` ``path`` ``str``, full path to the imperfection file ``use_theta_z_format`` ``bool``, if the imperfection file is in the `\theta, Z, imp` or in the `X, Y, Z` format ``R_measured`` ``float``, best fit radius obtained with functions :func:`.best_fit_cylinder` or :func:`.best_fit_cone` ``H_measured`` ``float``, height of the specimen for which the imperfection file corresponds to ``scaling_factor`` ``float``, a scaling factor that is applied to the imperfection amplitude ``sample_size`` Avoids a memory overflow during runtime for large imperfection files ``rotatedeg`` ``float``, rotation angle in degrees telling how much the imperfection pattern should be rotated about the `X_3` (or `Z`) axis. ====================== ================================================== The following attributes of the :class:`.MSI` object control the continuous function-based algorithm: =================== ===================================================== Attribute Description =================== ===================================================== ``c0`` ``np.ndarray``, coefficients giving the amplitude of each term in the approximation function given by ``funcnum``. If specified overwrites even ``imp_ms`` .. note:: The coefficients ``c0`` must be calculated already considering ``rotatedeg`` using function :func:`.calc_c0` ``m0`` ``int``, number of terms along the meridional coordinate ``n0`` ``int``, number of terms alog the circumferential coordinate ``funcnum`` ``int``, the base function used for the approximation, as detailed in :func:`.calc_c0` ``scaling_factor`` ``float``, a scaling factor that is applied to the imperfection amplitude =================== ===================================================== Additional parameters that govern how the imperfection pattern will look like in the finite element model: =================== ===================================================== Attribute Description =================== ===================================================== ``ignore_bot_h`` Used to ignore nodes from the bottom resin ring. The default value ``True`` will automatically obtain the resin ring dimensions. Set to ``False`` or ``None`` if an imperfection pattern "extruded" to both edges is the desired behavior ``ignore_top_h`` Similar to ``ignore_bot_h``, but for the top edge. ``stretch_H`` If the measured imperfection does not cover the whole height it will be stretched. If ``stretch_H is True``, ``ignore_bot_h`` and ``ignore_top_h`` are automatically set to ``False`` =================== ===================================================== """ def __init__(self): super(MSI, self).__init__() self.name = 'msi' self.index = None self.imp_ms = '' self.stretch_H = False self.r_TOL = 1. self.ncp = 5 self.rotatedeg = 0. self.power_parameter = 2 self.scaling_factor = 1. self.use_theta_z_format = True self.path = None self.c0 = None self.m0 = None self.n0 = None self.funcnum = None self.H_measured = None self.R_best_fit = None self.ignore_bot_h = True self.ignore_top_h = True self.sample_size = 2000000 #TODO: include z_offset_bottom to calculate ignore_bot_h and # ignore_top_h # plotting options self.xaxis = 'scaling_factor' self.xaxis_label = 'Scaling factor' self.nodal_translations = None self.created = False self.thetadegs = [] self.pts = [] def __setstate__(self, attrs): # Old versions had a bug where self.xaxis was set to 'amplitude' # Fix that during loading if attrs['xaxis'] == 'amplitude': attrs['xaxis'] = 'scaling_factor' attrs['xaxis_label'] = 'Scaling factor' self.__dict__.update(attrs) def rebuild(self): cc = self.impconf.conecyl self.name = 'MSI_{0:02d}_SF_{1:05d}'.format(self.index, int(round(100*self.scaling_factor))) self.thetadegs = [] self.pts = [] imps, imps_theta_z, t_measured, R_best_fit, H_measured = update_imps() if self.use_theta_z_format: if self.imp_ms in imps_theta_z.keys(): self.path = imps_theta_z[self.imp_ms]['msi'] else: if self.imp_ms in imps.keys(): self.path = imps[self.imp_ms]['msi'] if self.imp_ms in H_measured.keys(): self.H_measured = H_measured[self.imp_ms] if self.imp_ms in R_best_fit.keys(): self.R_best_fit = R_best_fit[self.imp_ms] if self.stretch_H: self.ignore_bot_h = False self.ignore_top_h = False if self.ignore_bot_h is True: if cc.resin_add_BIR or cc.resin_add_BOR: self.ignore_bot_h = cc.resin_bot_h else: self.ignore_bot_h = False if self.ignore_top_h is True: if cc.resin_add_TIR or cc.resin_add_TOR: self.ignore_top_h = cc.resin_top_h else: self.ignore_top_h = False
[docs] 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')
[docs] 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, 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 print_to_file(self, filename=None): if not filename: cc = self.impconf.conecyl.study_dir filename = os.path.join(cc, self.name + '.txt') print('Writing output file "%s" ...' % filename) keys = self.nodal_translations.keys() keys.sort() outfile = open(filename, 'w') for k in keys: original_coords = nodes_dict[k] translations = self.nodal_translations[k] new_coords = original_coords + translations * scaling_factor outfile.write('%d %f %f %f\n' % (k, new_coords[0], new_coords[1], new_coords[2])) outfile.close()