from __future__ import absolute_import
import os
import numpy as np
from desicos.logger import warn
from .uneven_edges import Shim, UnevenBottomEdge, UnevenTopEdge
from .axisymmetric import Axisymmetric
from .dimple import Dimple
from .pload import PLoad
from .cb import CBamp
from .lbmi import LBMI
from .msi import MSI
from .ti import TI
from .cutout import Cutout
from .ppi import PPI
from .ffi import FFI
[docs]class ImpConf(object):
"""Imperfection Configuration
Created by default as one attribute of the :class:`.ConeCyl` object,
accessed through::
cc = ConeCyl()
impconf = cc.impconf
If one has the ``impconf`` object and wants to access the corresponding
:class:`ConeCyl` object, the attribute ``conecyl`` can be used as
examplified below. Note that if no :class:`.ConeCyl` is assigned to this
imperfection configuration a ``None`` value will be obtained::
cc = impconf.conecyl
The imperfections are grouped in the attributes detailed below.
================== ========================================================
Attributes Description
================== ========================================================
uneven_bottom_edge :class:`.UnevenBottomEdge` object
uneven_top_edge :class:`.UnevenTopEdge` object
ploads ``list`` of :class:`.PLoad` (Perturbation Load) objects
dimples ``list`` of :class:`.Dimple` (Dimple Imperfection)
objects
axisymmetrics ``list`` of :class:`.Axisymmetric` (Axisymmetric
Imperfection) objects
lbmis ``list`` of :class:`.LBMI` (Linear Buckling Mode-Shaped
Imperfection) objects
tis ``list`` of :class:`.TI` (Thickness Imperfection)
objects
msis ``list`` of :class:`.MSI` (Mid-Surface Imperfection)
objects
cutouts ``list`` of :class:`.Cutout` objects
ppi :class:`.PPI` (Ply Piece Imperfection) object or
``None`` if not set
ffi :class:`.FFI` (Fiber Fraction Imperfection) object or
``None`` if not set
================== ========================================================
"""
def __init__(self):
self.uneven_bottom_edge = UnevenBottomEdge()
self.uneven_bottom_edge.impconf = self
self.uneven_top_edge = UnevenTopEdge()
self.uneven_top_edge.impconf = self
self.imperfections = []
self.ploads = []
self.cb = []
self.dimples = []
self.axisymmetrics = []
self.lbmis = []
self.msis = []
self.tis = []
self.cutouts = []
self.ppi = None
self.ffi = None
self.rename = True
self.name = ''
self.conecyl = None
def __setstate__(self, attrs):
# Called during unpickling (i.e. loading)
# Calling __init__ prevents problems with missing attributes,
# when loading from older versions
self.__init__()
self.__dict__.update(attrs)
[docs] def add_axisymmetric(self, pt, b, wb):
"""Add an Axisymmetric Imperfection (AI)
Parameters
----------
pt : float
Normalized meridional position.
b : float
Half-wave length.
wb : float
Imperfection amplitude (amplitude of the half-wave).
Returns
-------
ax : :class:`.Axisymmetric` object.
"""
ax = Axisymmetric(pt, b, wb)
ax.impconf = self
self.axisymmetrics.append(ax)
return ax
[docs] def add_dimple(self, thetadeg, pt, a, b, wb):
"""Add a Dimple Imperfection (DI)
Parameters
----------
thetadeg : float
Circumferential position of the dimple.
a : float
Circumferential half-wave length of the dimple.
b : float
Meridional half-wave length of the dimple.
wb : float
Imperfection amplitude.
Returns
-------
d : :class:`.Dimple` object.
"""
d = Dimple(thetadeg, pt, a, b, wb)
d.impconf = self
self.dimples.append(d)
return d
[docs] def add_lbmi(self, mode, scaling_factor):
"""Add a Linear Buckling Mode-shaped Imperfection (LBMI)
Parameters
----------
mode : int
Mode number corresponding to this eigenvector.
scaling_factor : float
Amplitude of this eigenvector when applied as an imperfection.
Returns
-------
lbmi : :class:`.LBMI` object.
"""
lbmi = LBMI(mode, scaling_factor)
lbmi.impconf = self
self.lbmis.append(lbmi)
return lbmi
[docs] def add_measured_u3s_bottom_edge(self, thetadegs, u3s):
r"""Add a measured uneven bottom edge
Straightforward method to include measured data about the bottom edge
imperfection.
Adopts the coordinate system of :ref:`this figure <figure_conecyl>`
when defining the `u_3` displacements for each `\theta` value.
The edge imperfection that actually goes for each node is a linear
interpolation of the measured values.
Parameters
----------
thetadegs : list
The circumferential positions where the imperfect bottom edge was
measured, in degrees.
u3s : list
The measured imperfections representing displacements along the
`X_3` axis :ref:`of the adopted model <figure_conecyl>`.
"""
self.uneven_bottom_edge.add_measured_u3s(thetadegs, u3s)
[docs] def add_measured_u3s_top_edge(self, thetadegs, u3s):
r"""Add a measured uneven top edge
Straightforward method to include measured data about the top edge
imperfection.
Adopts the coordinate system of :ref:`this figure <figure_conecyl>`
when defining the `u_3` displacements for each `\theta` value.
The edge imperfection that actually goes for each node is a linear
interpolation of the measured values.
Parameters
----------
thetadegs : list
The circumferential positions where the imperfect top edge was
measured, in degrees.
u3s : list
The measured imperfections representing displacements along the
`X_3` axis :ref:`of the adopted model <figure_conecyl>`.
"""
self.uneven_top_edge.add_measured_u3s(thetadegs, u3s)
[docs] def add_msi(self, imp_ms='', scaling_factor=1., R_best_fit=None,
H_measured=None, path=None, use_theta_z_format=True,
rotatedeg=0., ignore_bot_h=True, ignore_top_h=True,
stretch_H=False, c0=None,
m0=None, n0=None, funcnum=None):
r"""Add a Mid-Surface Imperfection (MSI)
Also called geometric imperfection.
If the imperfection is :ref:`already included in the database
<tutorials_conecylDB>` only the corresponding entry ``imp_ms`` and
the scaling factor need to be specified.
If the imperfection is not in the database one can specify the
full path for the file containing the imperfection, the measured
radius and height, as detailed below.
Parameters
----------
imp_ms : str, optional
Name of the imperfection in the database.
scaling_factor : float, optional
Scaling factor applied to the original imperfection amplitude,
usually to allow imperfection sensitivity studies.
R_best_fit : float, optional
Best fit radius obtained with functions :func:`.best_fit_cylinder`
or :func:`.best_fit_cone`.
alphadeg_measured : float
The semi-vertex angle of the measured sample (it is ``0.`` for a
cylinder).
H_measured : float, optional
The total height of the measured test specimen, including eventual
resin rings at the edges.
path : str, optional
Full path to the file containing the imperfection data.
use_theta_z_format : bool, optional
If the imperfection file is in the `\theta, Z, imp` format
instead of the `X, Y, Z` format.
rotatedeg : float, optional
Rotation angle in degrees telling how much the imperfection
pattern should be rotated about the `X_3` (or `Z`) axis.
ignore_bot_h : float, optional
Used to ignore nodes from the bottom resin ring. The default value
``True`` will use data from the bottom resin ring, if it exists.
ignore_top_h : float, optional
Used to ignore nodes from the top resin ring. The default value
``True`` will use data from the top resin ring, if it exists.
stretch_H : bool, optional
If the measured imperfection does not cover the whole height it
will be stretched. If ``stretch_H==True``, ``ignore_bot_h`` and
``ignore_top_h`` are automatically set to ``False``.
c0 : str or np.ndarray, optional
The coefficients representing the imperfection pattern. If
supplied will overwrite the imperfection data passed using the
other parameters. For more details see :func:`.calc_c0`.
m0 : int, optional
Number of terms along the meridian (`z`) used to obtain ``c0``,
see :func:`.calc_c0`.
n0 : int, optional
Number of terms along the circumference (`\theta`) used to obtain
``c0``, see :func:`.calc_c0`.
funcnum : int, optional
The base function used to obtain ``c0``, see :func:`.calc_c0`.
Returns
-------
msi : :class:`.MSI` object.
"""
msi = MSI()
msi.impconf = self
msi.imp_ms = imp_ms
msi.scaling_factor = scaling_factor
msi.use_theta_z_format = use_theta_z_format
msi.R_best_fit = R_best_fit
msi.H_measured = H_measured
msi.path = path
msi.rotatedeg = rotatedeg
msi.ignore_bot_h = ignore_bot_h
msi.ignore_top_h = ignore_top_h
msi.stretch_H = stretch_H
if c0 is not None and not (m0 and n0 and funcnum):
raise ValueError('Parameter "c0" must be supplied with ' +
'"m0", "n0" and "funcnum"')
elif c0 is not None and m0 and n0 and funcnum:
if funcnum == 1:
size = 2
elif funcnum == 2:
size = 2
elif funcnum == 3:
size = 4
else:
raise ValueError('Invalid value for "funcnum"!')
if isinstance(c0, str):
if not os.path.isfile(c0):
raise ValueError('File {0} not found!'.format(c0))
else:
c0 = np.loadtxt(c0)
if c0.ndim == 1:
if c0.shape[0] != size*m0*n0:
raise ValueError(
'Invalid "c0" for the given "m0" and "n0"!')
else:
raise ValueError(
'Array for "c0" must be one-dimensional!')
msi.c0 = c0
msi.m0 = m0
msi.n0 = n0
msi.funcnum = funcnum
self.msis.append(msi)
return msi
[docs] def add_pload(self, thetadeg, pt, pltotal, step=1):
"""Add a Perturbation Load
Parameters
----------
thetadeg : float
Circumferential position.
pt : float
Normalized meridional position.
pltotal : float
The magnitude of the perturbation load (it is always applied
normally to the shell surface).
step : int
The step in which the perturbation load will be included. In
``step=1`` the load is constant along the analysis while in
``step=2`` the load is incremented.
Returns
-------
pload : :class:`.PLoad` object.
"""
pload = PLoad(thetadeg, pt, pltotal, step)
pload.impconf = self
self.ploads.append(pload)
return pload
[docs] def add_cb(self, thetadeg, pt, cbtotal, step=1):
"""Add a Constant Amplitude Perturbation Buckle Imperfection
Parameters
----------
thetadeg : float
Circumferential position.
pt : float
Normalized meridional position.
cbtotal : float
The magnitude of the constant buckle (it is always applied
normally to the shell surface).
step : int
The step in which the constant buckle will be included. In
``step=1`` the load is constant along the analysis while in
``step=2`` the load is incremented.
Returns
-------
cb : :class:`.CBamp` object.
"""
cb = CBamp(thetadeg, pt, cbtotal, step)
cb.impconf = self
self.cb.append(cb)
return cb
[docs] def add_shim_bottom_edge(self, thetadeg, thick, width):
"""Add a Shim to the bottom edge
Parameters
----------
thetadeg : float
Circumferential position where the shim starts.
thick : float
Thickness of the shim.
width : float
Perimetrical width of the shim (along the shell perimeter).
Returns
-------
shim : :class:`.Shim` object.
"""
shim = Shim(thetadeg, thick, width, self.uneven_bottom_edge)
return shim
[docs] def add_shim_top_edge(self, thetadeg, thick, width):
"""Add a Shim to the top edge
Parameters
----------
thetadeg : float
Circumferential position where the shim starts.
thick : float
Thickness of the shim.
width : float
Perimetrical width of the shim (along the shell perimeter).
Returns
-------
shim : :class:`.Shim` object.
"""
shim = Shim(thetadeg, thick, width, self.uneven_top_edge)
return shim
[docs] def add_ti(self, imp_thick, scaling_factor):
"""Add Thickness Imperfection (TI)
The imperfection must be already included in the database (:ref:`check
this tutorial <tutorials_conecylDB>`).
Parameters
----------
imp_thick : str
Name of the thickness imperfection in the database.
scaling_factor : float
Scaling factor applied to the original imperfection amplitude,
usually to allow imperfection sensitivity studies.
Returns
-------
ti : :class:`.TI` object.
"""
ti = TI()
ti.impconf = self
ti.imp_thick = imp_thick
ti.scaling_factor = scaling_factor
self.tis.append(ti)
return ti
[docs] def add_cutout(self, thetadeg, pt, d, drill_offset_deg=0.,
clearance_factor=0.75, numel_radial_edge=4,
prop_around_cutout=None):
r"""Add a 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
Returns
-------
cutout : :class:`.Cutout` object.
"""
cutout = Cutout(thetadeg, pt, d, drill_offset_deg, clearance_factor,
numel_radial_edge, prop_around_cutout)
cutout.impconf = self
self.cutouts.append(cutout)
return cutout
[docs] 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
[docs] 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 rebuild(self):
# TODO: Reduce the amount of code duplication?
self.imperfections = []
i = -1
# uneven bottom edge
ube = self.uneven_bottom_edge
i += 1
ube.index = i
ube.rebuild()
self.imperfections.append(ube)
# uneven top edge
ute = self.uneven_top_edge
i += 1
ute.index = i
ute.rebuild()
self.imperfections.append(ute)
# ploads
for pload in self.ploads:
i += 1
pload.index = i
pload.rebuild()
self.imperfections.append(pload)
# CSBI
for cb in self.cb:
i += 1
cb.index = i
cb.rebuild()
self.imperfections.append(cb)
# dimples
for sb in self.dimples:
i += 1
sb.index = i
sb.rebuild()
self.imperfections.append(sb)
# axisymmetrics
for ax in self.axisymmetrics:
i += 1
ax.index = i
ax.rebuild()
self.imperfections.append(ax)
# linear buckling mode-shaped imperfection (LBMI)
for lbmi in self.lbmis:
i += 1
lbmi.index = i
lbmi.rebuild()
self.imperfections.append(lbmi)
# cutout
for cutout in self.cutouts:
i += 1
cutout.index = i
cutout.rebuild()
self.imperfections.append(cutout)
# ply piece imperfection
if self.ppi is not None:
i += 1
self.ppi.index = i
self.ppi.rebuild()
self.imperfections.append(self.ppi)
# fiber fraction imperfection
if self.ffi is not None:
i += 1
self.ffi.index = i
self.ffi.rebuild()
self.imperfections.append(self.ffi)
# thickness imperfection (TI)
for ti in self.tis:
i += 1
ti.index = i
ti.rebuild()
self.imperfections.append(ti)
# mid-surface imperfection (MSI)
for msi in self.msis:
i += 1
msi.index = i
msi.rebuild()
self.imperfections.append(msi)
# name
if self.rename:
self.name = ('PLs_%02d_dimples_%02d_axisym_%02d' +\
'_lbmis_%02d_MSIs_%02d_TIs_%02d') % \
(len(self.ploads), len(self.dimples),
len(self.axisymmetrics), len(self.lbmis),
len(self.msis), len(self.tis))
def create(self):
for imp in self.imperfections:
valid = True
for pt in imp.pts:
if pt < 0 or pt > 1.:
valid = False
break
if imp and valid:
imp.create()