Source code for compmech.stiffpanelbay.stiffpanelbay

import gc
import pickle
from multiprocessing import cpu_count

import numpy as np
from scipy.sparse import csr_matrix
from numpy import linspace, reshape

from compmech.logger import msg
from compmech.constants import DOUBLE
from compmech.sparse import (finalize_symmetric_matrix, make_skew_symmetric)
from compmech.panel import Panel, modelDB as panelmDB
from compmech.stiffener import (BladeStiff1D, BladeStiff2D, TStiff2D)


def load(name):
    if '.StiffPanelBay' in name:
        return pickle.load(open(name, 'rb'))
    else:
        return pickle.load(open(name + '.StiffPanelBay', 'rb'))


[docs] class StiffPanelBay(object): r"""Stiffened Panel Bay Can be used for supersonic Aeroelastic studies with the Piston Theory. Stiffeners are modeled either with 1D or 2D formulations. Main characteristics: - Supports both airflows along x (axis) or y (circumferential). Controlled by the parameter ``flow`` - ``bladestiff1ds`` contains the :class:`.BladeStiff1D` stiffeners - ``bladestiff2ds`` contains the :class:`.BladeStiff2D` stiffeners - ``tstiff2ds`` contains the :class:`.TStiff2D` stiffeners """ def __init__(self): self.name = '' # boundary conditions # "inf" is used to define the high stiffnesses (removed dofs) # a high value will cause numerical instabilities #TODO use a marker number for self.inf and self.maxinf if the # normalization of edge stiffnesses is adopted # now it is already independent of self.inf and more robust self.forces_skin = [] self.flow = 'x' self.bc = None self.model = None self.stack = [] self.laminaprop = None self.laminaprops = [] self.plyt = None self.plyts = [] self.mu = None # approximation series self.m = 11 self.n = 12 # panels self.panels = [] # stiffeners self.stiffeners = [] self.bladestiff1ds = [] self.bladestiff2ds = [] self.tstiff2ds = [] # geometry self.a = None self.b = None self.r = None self.alphadeg = None # boundary conditions self.u1tx = 0. self.u1rx = 0. self.u2tx = 0. self.u2rx = 0. self.v1tx = 0. self.v1rx = 0. self.v2tx = 0. self.v2rx = 0. self.w1tx = 0. self.w1rx = 1. self.w2tx = 0. self.w2rx = 1. self.u1ty = 0. self.u1ry = 0. self.u2ty = 0. self.u2ry = 0. self.v1ty = 0. self.v1ry = 0. self.v2ty = 0. self.v2ry = 0. self.w1ty = 0. self.w1ry = 1. self.w2ty = 0. self.w2ry = 1. # aerodynamic properties for the Piston theory self.beta = None self.gamma = None self.aeromu = None self.rho_air = None self.speed_sound = None self.Mach = None self.V = None # output queries self.out_num_cores = cpu_count() self._clear_matrices() def _clear_matrices(self): self.k0 = None self.kT = None self.kM = None self.kA = None self.cA = None self.u = None self.v = None self.w = None self.phix = None self.phiy = None self.Xs = None self.Ys = None for panel in self.panels: panel.k0 = None panel.kM = None panel.kG0 = None for s in self.bladestiff1ds: s.k0 = None s.kM = None s.kG0 = None for s in self.bladestiff2ds: s.k0 = None s.kM = None s.kG0 = None for s in self.tstiff2ds: s.k0 = None s.kM = None s.kG0 = None gc.collect() def _rebuild(self): if self.a is None: raise ValueError('The length a must be specified') if self.b is None: raise ValueError('The width b must be specified') for p in self.panels: p._rebuild() if self.model is not None: assert self.model == p.model else: self.model = p.model for s in self.bladestiff1ds: s._rebuild() for s in self.bladestiff2ds: s._rebuild() for s in self.tstiff2ds: s._rebuild() def _default_field(self, xs, a, ys, b, gridx, gridy): if xs is None or ys is None: xs = linspace(0., a, gridx) ys = linspace(0., b, gridy) xs, ys = np.meshgrid(xs, ys, copy=False) xs = np.atleast_1d(np.array(xs, dtype=DOUBLE)) ys = np.atleast_1d(np.array(ys, dtype=DOUBLE)) xshape = xs.shape yshape = ys.shape if xshape != yshape: raise ValueError('Arrays xs and ys must have the same shape') self.Xs = xs self.Ys = ys xs = xs.ravel() ys = ys.ravel() return xs, ys, xshape, yshape
[docs] def get_size(self): r"""Calculate the size of the stiffness matrices The size of the stiffness matrices can be interpreted as the number of rows or columns, recalling that this will be the size of the Ritz constants' vector `\{c\}`, the internal force vector `\{F_{int}\}` and the external force vector `\{F_{ext}\}`. It takes into account the independent degrees of freedom from each of the `.Stiffener2D` objects that belong to the current assembly. Returns ------- size : int The size of the stiffness matrices. """ num = panelmDB.db[self.model]['num'] self.size = num*self.m*self.n for s in self.bladestiff2ds: self.size += s.flange.get_size() for s in self.tstiff2ds: self.size += s.base.get_size() + s.flange.get_size() return self.size
[docs] def add_bladestiff1d(self, ys, mu=None, bb=None, bstack=None, bplyts=None, bplyt=None, blaminaprops=None, blaminaprop=None, bf=None, fstack=None, fplyts=None, fplyt=None, flaminaprops=None, flaminaprop=None, **kwargs): r"""Add a new BladeStiff1D to the current panel bay Parameters ---------- ys : float Stiffener position. mu : float, optional Stiffener's material density. If not given the bay density will be used. bb : float, optional Stiffener base width. bstack : list, optional Stacking sequence for the stiffener base laminate. bplyts : list, optional Thicknesses for each stiffener base ply. bplyt : float, optional Unique thickness for all stiffener base plies. blaminaprops : list, optional Lamina properties for each stiffener base ply. blaminaprop : float, optional Unique lamina properties for all stiffener base plies. bf : float Stiffener flange width. fstack : list, optional Stacking sequence for the stiffener flange laminate. fplyts : list, optional Thicknesses for each stiffener flange ply. fplyt : float, optional Unique thickness for all stiffener flange plies. flaminaprops : list, optional Lamina properties for each stiffener flange ply. flaminaprop : float, optional Unique lamina properties for all stiffener flange plies. Returns ------- s : :class:`.BladeStiff1D` object Notes ----- Additional parameters can be passed using the ``kwargs``. """ if mu is None: mu = self.mu if bstack is None and fstack is None: raise ValueError('bstack or fstack must be defined!') if bstack is not None: if bplyts is None: if bplyt is None: raise ValueError('bplyts or bplyt must be defined!') else: bplyts = [bplyt for _ in bstack] if blaminaprops is None: if blaminaprop is None: raise ValueError('blaminaprops or blaminaprop must be defined!') else: blaminaprops = [blaminaprop for _ in bstack] if fstack is not None: if fplyts is None: if fplyt is None: raise ValueError('fplyts or fplyt must be defined!') else: fplyts = [fplyt for _ in fstack] if flaminaprops is None: if flaminaprop is None: raise ValueError('flaminaprops or flaminaprop must be defined!') else: flaminaprops = [flaminaprop for _ in fstack] if len(self.panels) == 0: raise RuntimeError('The panels must be added before the stiffeners') # finding panel1 and panel2 panel1 = None panel2 = None for p in self.panels: if p.y2 == ys: panel1 = p if p.y1 == ys: panel2 = p if np.isclose(ys, 0): if np.isclose(p.y1, ys): panel1 = panel2 = p if np.isclose(ys, self.b): if np.isclose(p.y2, ys): panel1 = panel2 = p if panel1 is None or panel2 is None: raise RuntimeError('panel1 and panel2 could not be found!') s = BladeStiff1D(bay=self, mu=mu, panel1=panel1, panel2=panel2, ys=ys, bb=bb, bf=bf, bstack=bstack, bplyts=bplyts, blaminaprops=blaminaprops, fstack=fstack, fplyts=fplyts, flaminaprops=flaminaprops) for k, v in kwargs.items(): setattr(s, k, v) self.bladestiff1ds.append(s) self.stiffeners.append(s) return s
[docs] def add_bladestiff2d(self, ys, mu=None, bb=None, bstack=None, bplyts=None, bplyt=None, blaminaprops=None, blaminaprop=None, bf=None, fstack=None, fplyts=None, fplyt=None, flaminaprops=None, flaminaprop=None, mf=14, nf=11, **kwargs): r"""Add a new BladeStiff2D to the current panel bay Parameters ---------- ys : float Stiffener position. mu : float, optional Stiffener's material density. If not given the bay density will be used. bb : float, optional Stiffener base width. bstack : list, optional Stacking sequence for the stiffener base laminate. bplyts : list, optional Thicknesses for each stiffener base ply. bplyt : float, optional Unique thickness for all stiffener base plies. blaminaprops : list, optional Lamina properties for each stiffener base ply. blaminaprop : float, optional Unique lamina properties for all stiffener base plies. bf : float Stiffener flange width. fstack : list, optional Stacking sequence for the stiffener flange laminate. fplyts : list, optional Thicknesses for each stiffener flange ply. fplyt : float, optional Unique thickness for all stiffener flange plies. flaminaprops : list, optional Lamina properties for each stiffener flange ply. flaminaprop : float, optional Unique lamina properties for all stiffener flange plies. mf : int, optional Number of approximation terms for flange, along `x`. nf : int, optional Number of approximation terms for flange, along `y`. Returns ------- s : :class:`.BladeStiff2D` object Notes ----- Additional parameters can be passed using the ``kwargs``. """ if mu is None: mu = self.mu if bstack is None and fstack is None: raise ValueError('bstack or fstack must be defined!') if bstack is not None: if bplyts is None: if bplyt is None: raise ValueError('bplyts or bplyt must be defined!') else: bplyts = [bplyt for _ in bstack] if blaminaprops is None: if blaminaprop is None: raise ValueError('blaminaprops or blaminaprop must be defined!') else: blaminaprops = [blaminaprop for _ in bstack] if fstack is not None: if fplyts is None: if fplyt is None: raise ValueError('fplyts or fplyt must be defined!') else: fplyts = [fplyt for _ in fstack] if flaminaprops is None: if flaminaprop is None: raise ValueError('flaminaprops or flaminaprop must be defined!') else: flaminaprops = [flaminaprop for _ in fstack] if len(self.panels) == 0: raise RuntimeError('The panels must be added before the stiffeners') # finding panel1 and panel2 panel1 = None panel2 = None for p in self.panels: if p.y2 == ys: panel1 = p if p.y1 == ys: panel2 = p if np.isclose(ys, 0): if np.isclose(p.y1, ys): panel1 = panel2 = p if np.isclose(ys, self.b): if np.isclose(p.y2, ys): panel1 = panel2 = p if panel1 is None or panel2 is None: raise RuntimeError('panel1 and panel2 could not be found!') s = BladeStiff2D(bay=self, mu=mu, panel1=panel1, panel2=panel2, ys=ys, bb=bb, bf=bf, bstack=bstack, bplyts=bplyts, blaminaprops=blaminaprops, fstack=fstack, fplyts=fplyts, flaminaprops=flaminaprops, mf=mf, nf=nf) for k, v in kwargs.items(): setattr(s, k, v) self.bladestiff2ds.append(s) self.stiffeners.append(s) return s
[docs] def add_tstiff2d(self, ys, mu=None, bb=None, bstack=None, bplyts=None, bplyt=None, blaminaprops=None, blaminaprop=None, bf=None, fstack=None, fplyts=None, fplyt=None, flaminaprops=None, flaminaprop=None, mb=12, nb=13, mf=11, nf=12, Nxxf=0., **kwargs): r"""Add a new TStiff2D to the current panel bay Parameters ---------- ys : float Stiffener position. mu : float, optional Stiffener's material density. If not given the bay density will be used. bb : float, optional Stiffener base width. bstack : list, optional Stacking sequence for the stiffener base laminate. bplyts : list, optional Thicknesses for each stiffener base ply. bplyt : float, optional Unique thickness for all stiffener base plies. blaminaprops : list, optional Lamina properties for each stiffener base ply. blaminaprop : float, optional Unique lamina properties for all stiffener base plies. bf : float Stiffener flange width. fstack : list, optional Stacking sequence for the stiffener flange laminate. fplyts : list, optional Thicknesses for each stiffener flange ply. fplyt : float, optional Unique thickness for all stiffener flange plies. flaminaprops : list, optional Lamina properties for each stiffener flange ply. flaminaprop : float, optional Unique lamina properties for all stiffener flange plies. mb : int, optional Number of approximation terms for base, along `x`. nb : int, optional Number of approximation terms for base, along `y`. mf : int, optional Number of approximation terms for flange, along `x`. nf : int, optional Number of approximation terms for flange, along `y`. Returns ------- s : :class:`.TStiff2D` object Notes ----- Additional parameters can be passed using the ``kwargs``. """ if mu is None: mu = self.mu if bstack is None or fstack is None: raise ValueError('bstack and fstack must be defined!') if bplyts is None: if bplyt is None: raise ValueError('bplyts or bplyt must be defined!') else: bplyts = [bplyt for _ in bstack] if blaminaprops is None: if blaminaprop is None: raise ValueError('blaminaprops or blaminaprop must be defined!') else: blaminaprops = [blaminaprop for _ in bstack] if fplyts is None: if fplyt is None: raise ValueError('fplyts or fplyt must be defined!') else: fplyts = [fplyt for _ in fstack] if flaminaprops is None: if flaminaprop is None: raise ValueError('flaminaprops or flaminaprop must be defined!') else: flaminaprops = [flaminaprop for _ in fstack] if len(self.panels) == 0: raise RuntimeError('The panels must be added before the stiffeners') # finding panel1 and panel2 panel1 = None panel2 = None for p in self.panels: if p.y2 == ys: panel1 = p if p.y1 == ys: panel2 = p if np.isclose(ys, 0): if np.isclose(p.y1, ys): panel1 = panel2 = p if np.isclose(ys, self.b): if np.isclose(p.y2, ys): panel1 = panel2 = p if panel1 is None or panel2 is None: raise RuntimeError('panel1 and panel2 could not be found!') s = TStiff2D(bay=self, mu=mu, panel1=panel1, panel2=panel2, ys=ys, bb=bb, bf=bf, bstack=bstack, bplyts=bplyts, blaminaprops=blaminaprops, fstack=fstack, fplyts=fplyts, flaminaprops=flaminaprops, mb=mb, nb=nb, mf=mf, nf=nf) s.flange.Nxx = Nxxf for k, v in kwargs.items(): setattr(s, k, v) self.tstiff2ds.append(s) self.stiffeners.append(s) return s
[docs] def add_panel(self, y1, y2, stack=None, plyts=None, plyt=None, laminaprops=None, laminaprop=None, model=None, mu=None, **kwargs): r"""Add a new panel to the current panel bay Parameters ---------- y1 : float Position of the first panel edge along `y`. y2 : float Position of the second panel edge along `y`. stack : list, optional Panel stacking sequence. If not given the stacking sequence of the bay will be used. plyts : list, optional Thicknesses for each panel ply. If not supplied the bay ``plyts`` attribute will be used. plyt : float, optional Unique thickness to be used for all panel plies. If not supplied the bay ``plyt`` attribute will be used. laminaprops : list, optional Lamina properties for each panel ply. laminaprop : list, optional Unique lamina properties for all panel plies. model : str, optional Not recommended to pass this parameter, but the user can use a different model for each panel. It is recommended to defined ``model`` for the bay object. mu : float, optional Panel material density. If not given the bay density will be used. Notes ----- Additional parameters can be passed using the ``kwargs``. """ p = Panel(a=self.a, b=self.b, m=self.m, n=self.n, r=self.r, alphadeg=self.alphadeg, y1=y1, y2=y2, u1tx=self.u1tx, u1rx=self.u1rx, u2tx=self.u2tx, u2rx=self.u2rx, v1tx=self.v1tx, v1rx=self.v1rx, v2tx=self.v2tx, v2rx=self.v2rx, w1tx=self.w1tx, w1rx=self.w1rx, w2tx=self.w2tx, w2rx=self.w2rx, u1ty=self.u1ty, u1ry=self.u1ry, u2ty=self.u2ty, u2ry=self.u2ry, v1ty=self.v1ty, v1ry=self.v1ry, v2ty=self.v2ty, v2ry=self.v2ry, w1ty=self.w1ty, w1ry=self.w1ry, w2ty=self.w2ty, w2ry=self.w2ry) p.model = model if model is not None else self.model p.stack = stack if stack is not None else self.stack p.plyt = plyt if plyt is not None else self.plyt p.plyts = plyts if plyts is not None else self.plyts p.laminaprop = laminaprop if laminaprop is not None else self.laminaprop p.laminaprops = laminaprops if laminaprops is not None else self.laminaprops p.mu = mu if mu is not None else self.mu for k, v in kwargs.items(): setattr(p, k, v) self.panels.append(p) return p
def calc_k0(self, silent=False): self._rebuild() msg('Calculating k0... ', level=2, silent=silent) size = self.get_size() k0 = 0. for p in self.panels: p.calc_k0(size=size, row0=0, col0=0, silent=True, finalize=False) #TODO summing up coo_matrix objects may be slow! k0 += p.k0 for s in self.bladestiff1ds: s.calc_k0(size=size, row0=0, col0=0, silent=True, finalize=False) #TODO summing up coo_matrix objects may be slow! k0 += s.k0 num = panelmDB.db[self.model]['num'] m = self.m n = self.n row0 = num*m*n col0 = num*m*n for i, s in enumerate(self.bladestiff2ds): if i > 0: s_1 = self.bladestiff2ds[i-1] s.calc_k0(size=size, row0=row0, col0=col0, silent=True, finalize=False) if s.flange is not None: row0 += s.flange.get_size() col0 += s.flange.get_size() #TODO summing up coo_matrix objects may be slow! k0 += s.k0 for i, s in enumerate(self.tstiff2ds): if i > 0: s_1 = self.tstiff2ds[i-1] s.calc_k0(size=size, row0=row0, col0=col0, silent=True, finalize=False) row0 += s.base.get_size() + s.flange.get_size() col0 += s.base.get_size() + s.flange.get_size() #TODO summing up coo_matrix objects may be slow! k0 += s.k0 k0 = finalize_symmetric_matrix(k0) self.k0 = k0 #NOTE forcing Python garbage collector to clean the memory # it DOES make a difference! There is a memory leak not # identified, probably in the csr_matrix process gc.collect() msg('finished!', level=2, silent=silent) return k0 def calc_kG0(self, silent=False, c=None): self._rebuild() msg('Calculating kG0... ', level=2, silent=silent) size = self.get_size() kG0 = 0. for p in self.panels: p.calc_kG0(size=size, row0=0, col0=0, silent=True, finalize=False, c=c) #TODO summing up coo_matrix objects may be slow! kG0 += p.kG0 for s in self.bladestiff1ds: s.calc_kG0(size=size, row0=0, col0=0, silent=True, finalize=False, c=c) #TODO summing up coo_matrix objects may be slow! kG0 += s.kG0 num = panelmDB.db[self.model]['num'] m = self.m n = self.n row0 = num*m*n col0 = num*m*n for i, s in enumerate(self.bladestiff2ds): if i > 0: s_1 = self.bladestiff2ds[i-1] s.calc_kG0(size=size, row0=row0, col0=col0, silent=True, finalize=False, c=c) if s.flange is not None: row0 += s.flange.get_size() col0 += s.flange.get_size() #TODO summing up coo_matrix objects may be slow! kG0 += s.kG0 for i, s in enumerate(self.tstiff2ds): if i > 0: s_1 = self.tstiff2ds[i-1] s.calc_kG0(size=size, row0=row0, col0=col0, silent=True, finalize=False, c=c) row0 += s.base.get_size() + s.flange.get_size() col0 += s.base.get_size() + s.flange.get_size() #TODO summing up coo_matrix objects may be slow! kG0 += s.kG0 kG0 = finalize_symmetric_matrix(kG0) self.kG0 = kG0 #NOTE forcing Python garbage collector to clean the memory # it DOES make a difference! There is a memory leak not # identified, probably in the csr_matrix process gc.collect() msg('finished!', level=2, silent=silent) return kG0 def calc_kM(self, silent=False): self._rebuild() msg('Calculating kM... ', level=2, silent=silent) size = self.get_size() kM = 0. for p in self.panels: p.calc_kM(size=size, row0=0, col0=0, silent=True, finalize=False) #TODO summing up coo_matrix objects may be slow! kM += p.kM for s in self.bladestiff1ds: s.calc_kM(size=size, row0=0, col0=0, silent=True, finalize=False) #TODO summing up coo_matrix objects may be slow! kM += s.kM num = panelmDB.db[self.model]['num'] m = self.m n = self.n row0 = num*m*n col0 = num*m*n for i, s in enumerate(self.bladestiff2ds): if i > 0: s_1 = self.bladestiff2ds[i-1] s.calc_kM(size=size, row0=row0, col0=col0, silent=True, finalize=False) if s.flange is not None: row0 += s.flange.get_size() col0 += s.flange.get_size() #TODO summing up coo_matrix objects may be slow! kM += s.kM for i, s in enumerate(self.tstiff2ds): if i > 0: s_1 = self.tstiff2ds[i-1] s.calc_kM(size=size, row0=row0, col0=col0, silent=True, finalize=False) row0 += s.base.get_size() + s.flange.get_size() col0 += s.base.get_size() + s.flange.get_size() #TODO summing up coo_matrix objects may be slow! kM += s.kM kM = finalize_symmetric_matrix(kM) self.kM = kM #NOTE forcing Python garbage collector to clean the memory # it DOES make a difference! There is a memory leak not # identified, probably in the csr_matrix process gc.collect() msg('finished!', level=2, silent=silent) return kM def calc_kA(self, silent=False): self._rebuild() msg('Calculating kA... ', level=2, silent=silent) a = self.a b = self.b r = self.r if self.r is not None else 0. m = self.m n = self.n if self.beta is None: if self.Mach < 1: raise ValueError('Mach number must be >= 1') elif self.Mach == 1: self.Mach = 1.0001 Mach = self.Mach beta = self.rho_air * self.V**2 / (Mach**2 - 1)**0.5 if r != 0.: gamma = beta*1./(2.*self.r*(Mach**2 - 1)**0.5) else: gamma = 0. ainf = self.speed_sound aeromu = beta/(Mach*ainf)*(Mach**2 - 2)/(Mach**2 - 1) else: beta = self.beta gamma = self.gamma if self.gamma is not None else 0. aeromu = self.aeromu if self.aeromu is not None else 0. # contributions from panels #TODO summing up coo_matrix objects may be slow! #FIXME this only works if the first panel represent the full # stiffpanelbay domain (mainly integration interval, boundary # conditions) p = self.panels[0] #FIXME the initialization below looks terrible # we should move as quick as possible to the strategy of using # classes more to carry data, avoiding these intrincated methods # shared among classes... (calc_k0, calc_kG0 etc) p.flow = self.flow p.Mach = self.Mach p.rho_air = self.rho_air p.speed_sound = self.speed_sound p.size = self.size p.V = self.V p.r = self.r p.calc_kA(silent=True, finalize=False) kA = p.kA assert np.any(np.isnan(kA.data)) == False assert np.any(np.isinf(kA.data)) == False kA = csr_matrix(make_skew_symmetric(kA)) self.kA = kA #NOTE forcing Python garbage collector to clean the memory # it DOES make a difference! There is a memory leak not # identified, probably in the csr_matrix process gc.collect() msg('finished!', level=2, silent=silent) return kA def calc_cA(self, silent=False): self._rebuild() msg('Calculating cA... ', level=2, silent=silent) a = self.a b = self.b r = self.r m = self.m n = self.n size = self.get_size() if self.beta is None: if self.Mach < 1: raise ValueError('Mach number must be >= 1') elif self.Mach == 1: self.Mach = 1.0001 Mach = self.Mach beta = self.rho_air * self.V**2 / (Mach**2 - 1)**0.5 gamma = beta*1./(2.*self.r*(Mach**2 - 1)**0.5) ainf = self.speed_sound aeromu = beta/(Mach*ainf)*(Mach**2 - 2)/(Mach**2 - 1) else: beta = self.beta gamma = self.gamma if self.gamma is not None else 0. aeromu = self.aeromu if self.aeromu is not None else 0. # contributions from panels p = self.panels[0] p.calc_cA(size=size, row0=0, col0=0, silent=silent) cA = p.cA cA = finalize_symmetric_matrix(cA) self.cA = cA #NOTE forcing Python garbage collector to clean the memory # it DOES make a difference! There is a memory leak not # identified, probably in the csr_matrix process gc.collect() msg('finished!', level=2, silent=silent) return cA
[docs] def uvw_skin(self, c, xs=None, ys=None, gridx=300, gridy=300): r"""Calculate the displacement field For a given full set of Ritz constants ``c``, the displacement field is calculated and stored in the parameters ``u``, ``v``, ``w``, ``phix``, ``phiy`` of the :class:`.StiffPanelBay` object. Parameters ---------- c : float The full set of Ritz constants xs : np.ndarray The `x` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. ys : np.ndarray The ``y`` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. gridx : int Number of points along the `x` axis where to calculate the displacement field. gridy : int Number of points along the `y` where to calculate the displacement field. Returns ------- out : tuple A tuple of ``np.ndarrays`` containing ``(u, v, w, phix, phiy)``. Notes ----- The returned values ``u```, ``v``, ``w``, ``phix``, ``phiy`` are stored as parameters with the same name in the :class:`.StiffPanelBay` object. """ c = np.ascontiguousarray(c, dtype=DOUBLE) m = self.m n = self.n a = self.a b = self.b if xs is None or ys is None: xs, ys, xshape, yshape = self._default_field(xs, a, ys, b, gridx, gridy) else: xshape = xs.shape if c.shape[0] == self.get_size(): num = panelmDB.db[self.model]['num'] c = c[:num*self.m*self.n] else: raise ValueError('c must be the full vector of Ritz constants') fuvw = panelmDB.db[self.model]['field'].fuvw us, vs, ws, phixs, phiys = fuvw(c, self, xs, ys, self.out_num_cores) self.u = reshape(us, xshape) self.v = reshape(vs, xshape) self.w = reshape(ws, xshape) self.phix = reshape(phixs, xshape) self.phiy = reshape(phiys, xshape) return self.u, self.v, self.w, self.phix, self.phiy
[docs] def uvw_stiffener(self, c, si, region='flange', xs=None, ys=None, gridx=300, gridy=300): r"""Calculate the displacement field on a stiffener For a given full set of Ritz constants ``c``, the displacement field is calculated and stored in the parameters ``u``, ``v``, ``w``, ``phix``, ``phiy`` of the :class:`StiffPanelBay` object. Parameters ---------- c : float The full set of Ritz constants si : int Stiffener index. region : str, optional Stiffener region ('base', 'flange' etc). xs : np.ndarray The `x` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. ys : np.ndarray The ``y`` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. gridx : int Number of points along the `x` axis where to calculate the displacement field. gridy : int Number of points along the `y` where to calculate the displacement field. Returns ------- out : tuple A tuple of ``np.ndarrays`` containing ``(u, v, w, phix, phiy)``. Notes ----- The returned values ``u```, ``v``, ``w``, ``phix``, ``phiy`` are stored as parameters with the same name in the :class:`StiffPanelBay` object. """ c = np.ascontiguousarray(c, dtype=DOUBLE) stiff = self.stiffeners[si] if isinstance(stiff, BladeStiff1D): raise RuntimeError('Use plot_skin for BladeStiff1D') if region.lower() == 'base' and isinstance(stiff, BladeStiff2D): #TODO why this case isn't working? raise RuntimeError('Use plot_skin for the base of BladeStiff2D') num = panelmDB.db[self.model]['num'] row_init = num*self.m*self.n # getting array position for i, s in enumerate(self.stiffeners): if i > 0: s_1 = self.stiffeners[i-1] if isinstance(s, BladeStiff2D): row_init += s_1.get_size() elif isinstance(s, TStiff2D): row_init += s_1.base.get_size() + s_1.flange.get_size() if i == si: break if region.lower() == 'base': bstiff = stiff.base.b if isinstance(stiff, TStiff2D): row_init = row_init row_final = row_init + s.base.get_size() else: raise elif region.lower() == 'flange': bstiff = stiff.flange.b if isinstance(stiff, TStiff2D): row_init += s.base.get_size() row_final = row_init + s.flange.get_size() elif isinstance(s, BladeStiff2D): row_final = row_init + s.flange.get_size() else: raise ValueError('Invalid region') if c.shape[0] == self.get_size(): c = c[row_init: row_final] else: raise ValueError('c must be the full vector of Ritz constants') if xs is None or ys is None: xs, ys, xshape, yshape = self._default_field(xs, self.a, ys, bstiff, gridx, gridy) else: xshape = xs.shape if region.lower() == 'flange': fuvw = panelmDB.db[s.flange.model]['field'].fuvw us, vs, ws, phixs, phiys = fuvw(c, s.flange, xs, ys, self.out_num_cores) elif region.lower() == 'base': fuvw = panelmDB.db[s.base.model]['field'].fuvw us, vs, ws, phixs, phiys = fuvw(c, s.base, xs, ys, self.out_num_cores) self.u = reshape(us, xshape) self.v = reshape(vs, xshape) self.w = reshape(ws, xshape) self.phix = reshape(phixs, xshape) self.phiy = reshape(phiys, xshape) return self.u, self.v, self.w, self.phix, self.phiy
[docs] def plot_skin(self, c, invert_y=False, plot_type=1, vec='w', deform_u=False, deform_u_sf=100., filename='', ax=None, figsize=(3.5, 2.), save=True, title='', colorbar=False, cbar_nticks=2, cbar_format=None, cbar_title='', cbar_fontsize=10, aspect='equal', clean=True, dpi=400, texts=[], xs=None, ys=None, gridx=300, gridy=300, num_levels=400, vecmin=None, vecmax=None, silent=False): r"""Contour plot for a Ritz constants vector. Parameters ---------- c : np.ndarray The Ritz constants that will be used to compute the field contour. vec : str, optional Can be one of the components: - Displacement: ``'u'``, ``'v'``, ``'w'``, ``'phix'``, ``'phiy'`` - Strain: ``'exx'``, ``'eyy'``, ``'gxy'``, ``'kxx'``, ``'kyy'``, ``'kxy'``, ``'gyz'``, ``'gxz'`` - Stress: ``'Nxx'``, ``'Nyy'``, ``'Nxy'``, ``'Mxx'``, ``'Myy'``, ``'Mxy'``, ``'Qy'``, ``'Qx'`` deform_u : bool, optional If ``True`` the contour plot will look deformed. deform_u_sf : float, optional The scaling factor used to deform the contour. invert_y : bool, optional Inverts the `y` axis of the plot. It may be used to match the coordinate system of the finite element models created using the ``desicos.abaqus`` module. plot_type : int, optional For cylinders only ``4`` and ``5`` are valid. For cones all the following types can be used: - ``1``: concave up (with ``invert_y=False``) (default) - ``2``: concave down (with ``invert_y=False``) - ``3``: stretched closed - ``4``: stretched opened (`r \times y` vs. `a`) - ``5``: stretched opened (`y` vs. `a`) save : bool, optional Flag telling whether the contour should be saved to an image file. dpi : int, optional Resolution of the saved file in dots per inch. filename : str, optional The file name for the generated image file. If no value is given, the `name` parameter of the :class:`StiffPanelBay` object will be used. ax : AxesSubplot, optional When ``ax`` is given, the contour plot will be created inside it. figsize : tuple, optional The figure size given by ``(width, height)``. title : str, optional If any string is given it is added as title to the contour plot. colorbar : bool, optional If a colorbar should be added to the contour plot. cbar_nticks : int, optional Number of ticks added to the colorbar. cbar_format : [ None | format string | Formatter object ], optional See the ``matplotlib.pyplot.colorbar`` documentation. cbar_fontsize : int, optional Fontsize of the colorbar labels. cbar_title : str, optional Colorbar title. If ``cbar_title == ''`` no title is added. aspect : str, optional String that will be passed to the ``AxesSubplot.set_aspect()`` method. clean : bool, optional Clean axes ticks, grids, spines etc. xs : np.ndarray, optional The `x` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. ys : np.ndarray, optional The ``y`` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. gridx : int, optional Number of points along the `x` axis where to calculate the displacement field. gridy : int, optional Number of points along the `y` where to calculate the displacement field. num_levels : int, optional Number of contour levels (higher values make the contour smoother). vecmin : float, optional Minimum value for the contour scale (useful to compare with other results). If not specified it will be taken from the calculated field. vecmax : float, optional Maximum value for the contour scale. silent : bool, optional A boolean to tell whether the msg messages should be printed. Returns ------- ax : matplotlib.axes.Axes The Matplotlib object that can be used to modify the current plot if needed. """ msg('Plotting contour...', silent=silent) ubkp, vbkp, wbkp, phixbkp, phiybkp = (self.u, self.v, self.w, self.phix, self.phiy) import matplotlib import matplotlib.pyplot as plt msg('Computing field variables...', level=1, silent=silent) displs = ['u', 'v', 'w', 'phix', 'phiy'] if vec in displs: self.uvw_skin(c, xs=xs, ys=ys, gridx=gridx, gridy=gridy) field = getattr(self, vec) else: raise ValueError( '{0} is not a valid vec parameter value!'.format(vec)) msg('Finished!', level=1, silent=silent) Xs = self.Xs Ys = self.Ys if vecmin is None: vecmin = field.min() if vecmax is None: vecmax = field.max() levels = linspace(vecmin, vecmax, num_levels) if ax is None: fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) else: if isinstance(ax, matplotlib.axes.Axes): ax = ax fig = ax.figure save = False else: raise ValueError('ax must be an Axes object') x = Ys y = Xs if deform_u: if vec in displs: pass else: self.uvw_skin(c, xs=xs, ys=ys, gridx=gridx, gridy=gridy) field_u = self.u field_v = self.v y -= deform_u_sf*field_u x += deform_u_sf*field_v contour = ax.contourf(x, y, field, levels=levels) if colorbar: from mpl_toolkits.axes_grid1 import make_axes_locatable fsize = cbar_fontsize divider = make_axes_locatable(ax) cax = divider.append_axes('right', size='5%', pad=0.05) cbarticks = linspace(vecmin, vecmax, cbar_nticks) cbar = plt.colorbar(contour, ticks=cbarticks, format=cbar_format, cax=cax) if cbar_title: cax.text(0.5, 1.05, cbar_title, horizontalalignment='center', verticalalignment='bottom', fontsize=fsize) try: cbar.outline.remove() except NotImplementedError: pass cbar.ax.tick_params(labelsize=fsize, pad=0., tick2On=False) if invert_y == True: ax.invert_yaxis() ax.invert_xaxis() if title != '': ax.set_title(str(title)) fig.tight_layout() ax.set_aspect(aspect) ax.grid(False) ax.set_frame_on(False) if clean: ax.xaxis.set_ticks_position('none') ax.yaxis.set_ticks_position('none') ax.xaxis.set_ticklabels([]) ax.yaxis.set_ticklabels([]) else: ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') for kwargs in texts: ax.text(transform=ax.transAxes, **kwargs) if save: if not filename: filename = 'test.png' fig.savefig(filename, transparent=True, bbox_inches='tight', pad_inches=0.05, dpi=dpi) plt.close() if ubkp is not None: self.u = ubkp if vbkp is not None: self.v = vbkp if wbkp is not None: self.w = wbkp if phixbkp is not None: self.phix = phixbkp if phiybkp is not None: self.phiy = phiybkp msg('finished!', silent=silent) return ax
[docs] def plot_stiffener(self, c, si, region='flange', invert_y=False, plot_type=1, vec='w', deform_u=False, deform_u_sf=100., filename='', ax=None, figsize=(3.5, 2.), save=True, title='', colorbar=False, cbar_nticks=2, cbar_format=None, cbar_title='', cbar_fontsize=10, aspect='equal', clean=True, dpi=400, texts=[], xs=None, ys=None, gridx=300, gridy=300, num_levels=400, vecmin=None, vecmax=None, silent=False): r"""Contour plot for a Ritz constants vector. Parameters ---------- c : np.ndarray The Ritz constants that will be used to compute the field contour. si : int Stiffener index. region : str, optional Stiffener region ('base', 'flange' etc). vec : str, optional Can be one of the components: - Displacement: ``'u'``, ``'v'``, ``'w'``, ``'phix'``, ``'phiy'`` - Strain: ``'exx'``, ``'eyy'``, ``'gxy'``, ``'kxx'``, ``'kyy'``, ``'kxy'``, ``'gyz'``, ``'gxz'`` - Stress: ``'Nxx'``, ``'Nyy'``, ``'Nxy'``, ``'Mxx'``, ``'Myy'``, ``'Mxy'``, ``'Qy'``, ``'Qx'`` deform_u : bool, optional If ``True`` the contour plot will look deformed. deform_u_sf : float, optional The scaling factor used to deform the contour. invert_y : bool, optional Inverts the `y` axis of the plot. It may be used to match the coordinate system of the finite element models created using the ``desicos.abaqus`` module. plot_type : int, optional For cylinders only ``4`` and ``5`` are valid. For cones all the following types can be used: - ``1``: concave up (with ``invert_y=False``) (default) - ``2``: concave down (with ``invert_y=False``) - ``3``: stretched closed - ``4``: stretched opened (`r \times y` vs. `a`) - ``5``: stretched opened (`y` vs. `a`) save : bool, optional Flag telling whether the contour should be saved to an image file. dpi : int, optional Resolution of the saved file in dots per inch. filename : str, optional The file name for the generated image file. If no value is given, the `name` parameter of the :class:`StiffPanelBay` object will be used. ax : AxesSubplot, optional When ``ax`` is given, the contour plot will be created inside it. figsize : tuple, optional The figure size given by ``(width, height)``. title : str, optional If any string is given it is added as title to the contour plot. colorbar : bool, optional If a colorbar should be added to the contour plot. cbar_nticks : int, optional Number of ticks added to the colorbar. cbar_format : [ None | format string | Formatter object ], optional See the ``matplotlib.pyplot.colorbar`` documentation. cbar_fontsize : int, optional Fontsize of the colorbar labels. cbar_title : str, optional Colorbar title. If ``cbar_title == ''`` no title is added. aspect : str, optional String that will be passed to the ``AxesSubplot.set_aspect()`` method. clean : bool, optional Clean axes ticks, grids, spines etc. xs : np.ndarray, optional The `x` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. ys : np.ndarray, optional The ``y`` positions where to calculate the displacement field. Default is ``None`` and the method ``_default_field`` is used. gridx : int, optional Number of points along the `x` axis where to calculate the displacement field. gridy : int, optional Number of points along the `y` where to calculate the displacement field. num_levels : int, optional Number of contour levels (higher values make the contour smoother). vecmin : float, optional Minimum value for the contour scale (useful to compare with other results). If not specified it will be taken from the calculated field. vecmax : float, optional Maximum value for the contour scale. silent : bool, optional A boolean to tell whether the msg messages should be printed. Returns ------- ax : matplotlib.axes.Axes The Matplotlib object that can be used to modify the current plot if needed. """ msg('Plotting contour...', silent=silent) ubkp, vbkp, wbkp, phixbkp, phiybkp = (self.u, self.v, self.w, self.phix, self.phiy) import matplotlib.pyplot as plt import matplotlib msg('Computing field variables...', level=1, silent=silent) displs = ['u', 'v', 'w', 'phix', 'phiy'] if vec in displs: self.uvw_stiffener(c, si=si, region=region, xs=xs, ys=ys, gridx=gridx, gridy=gridy) field = getattr(self, vec) else: raise ValueError( '{0} is not a valid vec parameter value!'.format(vec)) msg('Finished!', level=1, silent=silent) Xs = self.Xs Ys = self.Ys if vecmin is None: vecmin = field.min() if vecmax is None: vecmax = field.max() levels = linspace(vecmin, vecmax, num_levels) if ax is None: fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) else: if isinstance(ax, matplotlib.axes.Axes): ax = ax fig = ax.figure save = False else: raise ValueError('ax must be an Axes object') x = Ys y = Xs if deform_u: if vec in displs: pass else: self.uvw_stiffener(c, si=si, region=region, xs=xs, ys=ys, gridx=gridx, gridy=gridy) field_u = self.u field_v = self.v y -= deform_u_sf*field_u x += deform_u_sf*field_v contour = ax.contourf(x, y, field, levels=levels) if colorbar: from mpl_toolkits.axes_grid1 import make_axes_locatable fsize = cbar_fontsize divider = make_axes_locatable(ax) cax = divider.append_axes('right', size='5%', pad=0.05) cbarticks = linspace(vecmin, vecmax, cbar_nticks) cbar = plt.colorbar(contour, ticks=cbarticks, format=cbar_format, cax=cax) if cbar_title: cax.text(0.5, 1.05, cbar_title, horizontalalignment='center', verticalalignment='bottom', fontsize=fsize) try: cbar.outline.remove() except NotImplementedError: pass cbar.ax.tick_params(labelsize=fsize, pad=0., tick2On=False) if invert_y == True: ax.invert_yaxis() ax.invert_xaxis() if title != '': ax.set_title(str(title)) fig.tight_layout() ax.set_aspect(aspect) ax.grid(False) ax.set_frame_on(False) if clean: ax.xaxis.set_ticks_position('none') ax.yaxis.set_ticks_position('none') ax.xaxis.set_ticklabels([]) ax.yaxis.set_ticklabels([]) else: ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') for kwargs in texts: ax.text(transform=ax.transAxes, **kwargs) if save: if not filename: filename = 'test.png' fig.savefig(filename, transparent=True, bbox_inches='tight', pad_inches=0.05, dpi=dpi) plt.close() if ubkp is not None: self.u = ubkp if vbkp is not None: self.v = vbkp if wbkp is not None: self.w = wbkp if phixbkp is not None: self.phix = phixbkp if phiybkp is not None: self.phiy = phiybkp msg('finished!', silent=silent) return ax
[docs] def save(self): r"""Save the :class:`StiffPanelBay` object using ``pickle`` Notes ----- The pickled file will have the name stored in :property:`.StiffPanelBay.name` followed by a ``'.StiffPanelBay'`` extension. """ name = self.name + '.StiffPanelBay' msg('Saving StiffPanelBay to {0}'.format(name)) self._clear_matrices() with open(name, 'wb') as f: pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL)
[docs] def calc_fext(self, silent=False): r"""Calculates the external force vector `\{F_{ext}\}` Parameters ---------- silent : bool, optional A boolean to tell whether the msg messages should be printed. Returns ------- fext : np.ndarray The external force vector """ msg('Calculating external forces...', level=2, silent=silent) num = panelmDB.db[self.model]['num'] fg = panelmDB.db[self.model]['field'].fg # punctual forces on skin size = num*self.m*self.n g = np.zeros((3, size), dtype=DOUBLE) fext_skin = np.zeros(size, dtype=DOUBLE) for i, force in enumerate(self.forces_skin): x, y, fx, fy, fz = force fg(g, x, y, self) fpt = np.array([[fx, fy, fz]]) fext_skin += fpt.dot(g).ravel() fext = fext_skin # punctual forces on bladestiff2ds # flange for s in self.bladestiff2ds: fg_flange = panelmDB.db[s.flange.model]['field'].fg size = s.flange.get_size() g_flange = np.zeros((3, size), dtype=DOUBLE) fext_stiffener = np.zeros(size, dtype=DOUBLE) for i, force in enumerate(s.flange.forces): xf, yf, fx, fy, fz = force fg_flange(g_flange, xf, yf, s.flange) fpt = np.array([[fx, fy, fz]]) fext_stiffener += fpt.dot(g_flange).ravel() fext = np.concatenate((fext, fext_stiffener)) # punctual forces on tstiff2ds for s in self.tstiff2ds: # base size = s.base.get_size() g_base = np.zeros((3, size), dtype=DOUBLE) fext_base = np.zeros(size, dtype=DOUBLE) fg_base = panelmDB.db[s.base.model]['field'].fg for i, force in enumerate(s.base.forces): xb, yb, fx, fy, fz = force fg_base(g_base, xb, yb, s.base) fpt = np.array([[fx, fy, fz]]) fext_base += fpt.dot(g_base).ravel() # flange size = s.flange.get_size() g_flange = np.zeros((3, size), dtype=DOUBLE) fext_flange = np.zeros(size, dtype=DOUBLE) fg_flange = panelmDB.db[s.flange.model]['field'].fg for i, force in enumerate(s.flange.forces): xf, yf, fx, fy, fz = force fg_flange(g_flange, xf, yf, s.flange) fpt = np.array([[fx, fy, fz]]) fext_flange += fpt.dot(g_flange).ravel() fext = np.concatenate((fext, fext_base, fext_flange)) msg('finished!', level=2, silent=silent) return fext