From 025f62ca5b2fefb2eb2d2f0d5d43f99b6e8dbce6 Mon Sep 17 00:00:00 2001 From: Malo Rosemeier <malo.rosemeier@iwes.fraunhofer.de> Date: Thu, 20 Jul 2017 17:22:24 +0200 Subject: [PATCH] wrapper works, verification of cross section props required. --- finstrip_wrapper/core/blade.py | 330 ++++- finstrip_wrapper/core/main.py | 24 +- finstrip_wrapper/omdao/finstrip_blade.py | 1143 +++++++++-------- .../omdao/test/test_finstrip_blade.py | 130 +- 4 files changed, 1020 insertions(+), 607 deletions(-) diff --git a/finstrip_wrapper/core/blade.py b/finstrip_wrapper/core/blade.py index c2fcc38..6a15e97 100644 --- a/finstrip_wrapper/core/blade.py +++ b/finstrip_wrapper/core/blade.py @@ -1,5 +1,6 @@ import numpy as np from finstrip_wrapper.core.main import FINSTRIPWrapper, status +from PGL.components.airfoil import AirfoilShape class FINSTRIPWrapperBlade(FINSTRIPWrapper): @@ -12,18 +13,10 @@ class FINSTRIPWrapperBlade(FINSTRIPWrapper): # data base output name self.name = 'blade' - # loads dummy - self.loads = np.array([]) - #self.aligns = np.array([]) + self.core_mats = [] # list of materials to be identified as core + self.spanpos = 0 # position along span of analysed cross-section - # flags - #self.post_rst = True - - # standard buckling analysis variables - self.buckle_modes = 10 # number of bucling modes - self.buckle_load_start = 0.0 # start boundary load - self.buckle_load_end = 10.0 # end boundary load - # self.buckle_method = 'LANB' # 'SUBSP' + self.use_becas_stiffness = False for k, w in kwargs.iteritems(): try: @@ -31,56 +24,248 @@ class FINSTRIPWrapperBlade(FINSTRIPWrapper): except: pass + self._init_half_wavelength_steps() self._init_finstrip_input_files_names() - def init_dicts(self, pf): + def init_dicts(self, cs2d): # dictionaries written by FINSTRIPBladeStructure according to FUSED-Wind format # NOTE: absolut dimensions are required! - self.pf = pf - - def init_full_scale_load_case(self, loads, aligns): - # TODO: here we need the conversion from load distro to point loads - # and the calculation of the load introduction positions - # By now it takes only external point loads - - # TODO: remove hardcoded loads - # position of master nodes and force vector,and actuator positions - # x,y,z,rotx,roty,rotz,Fx,Fy,Fz,Mx,My,Mz,ax,ay,az - # actuator positions in the test_tig locinate system - self.loads = loads - self.nloads = self.loads.shape[0] - - # tilt, pitch, x_orig, y_orig, z_orig - # tilt_angle rotation about root x-axis in deg - # pitch_angle rotation about root z-axis in deg - # test_rig_origin use distance in y-direction for vertical pull - self.aligns = aligns + self.cs2d = cs2d + + def init_becas_stiffness(self, csprops, csprops_ref): + # stiffness properties as described in BECASWrapper + self.csprops = csprops + self.csprops_ref = csprops_ref + + def init_cs_load_cases(self, csloads_rcs, twist): + ''' csload is assumed to be provided in root cs + :param csloads: cross section loads (Fx,Fy,Fz,Mx,My,Mz) size ((ncase,6)) + :param twist: geometric twist angle of cross section w.r.t root coordinate system size ((1)) + note: so far only bending moments mx and my are used + ''' + + Mx_rcs = csloads_rcs[:, 3] + My_rcs = csloads_rcs[:, 4] + + # transformation matrix from root cs to cross section cs + sin_twist = np.sin(np.deg2rad(twist)) + cos_twist = np.cos(np.deg2rad(twist)) + self.T_twist = np.matrix( + [[cos_twist, sin_twist], [-sin_twist, cos_twist]]) + + # transform from root cs to cross section cs + v_Mrcs = np.array([[Mx_rcs], [My_rcs]]) + v_Mcs = self.T_twist * v_Mrcs + + M_x = np.squeeze(np.asarray(v_Mcs[:, 0])) + M_y = np.squeeze(np.asarray(v_Mcs[:, 1])) + + self.csloads = np.zeros_like(csloads_rcs) + + self.csloads[:, 3] = M_x + self.csloads[:, 4] = M_y + + self.csloads[:, 3:] = self.csloads[:, 3:] * 1E3 # convert to Nmm + self.ncase = len(self.csloads[:, 0]) def setup(self): ''' Sets up the FINSTRIP execution. ''' super(FINSTRIPWrapperBlade, self).setup() + self._det_maxlength_element() + self._write_finstrip_input_files() self._write_finstrip_analysis_files() + def _det_maxlength_element(self): + # calculate arc length of airfoil surface + + af = AirfoilShape() + af.initialize(points=self.cs2d['coords']) + + # maximum size of generated element in m + self.maxlength_element = af.smax * self.elsize_factor + def _write_finstrip_input_files(self): super(FINSTRIPWrapperBlade, self)._write_finstrip_input_files() self._write_file_buc() def _write_file_buc(self): - pass + out_str = [] + n = '\n' + # write header + out_str.append( + '// Finstrip input file, created by FINSTRIPWrapper' + n) + + # write vars converted to mm + for var_name, var_value in zip(['maxlength_element', + 'lambda_steps', + 'lambda_min', + 'lambda_max'], + [self.maxlength_element * 1E3, + self.lambda_steps, + self.lambda_min * 1E3, + self.lambda_max * 1E3, + ]): + out_str.append('%s, %0.2f' % (var_name, var_value) + n) + + # write materials + mat_names = self.cs2d['materials'].keys() + matprops = self.cs2d['matprops'] + + def _write_iso(out_str, n, label, E1, nu12, rho): + out_str.append('iso, %s, %g, %g, %g' % + (label, E1, nu12, rho) + n) + + def _write_ortho(out_str, n, label, E1, E2, nu12, G12, rho): + out_str.append('ortho, %s, %g, %g, %g, %g, %g' % ( + label, E1, E2, nu12, G12, rho) + n) + + def _write_core(out_str, n, label, E1, E2, nu12, G12, G13, G23, rho): + out_str.append('core, %s, %g, %g, %g, %g, %g,%g, %g' % ( + label, E1, E2, nu12, G12, G13, G23, rho) + n) + + for i, label in enumerate(mat_names): + E1 = matprops[i, 0] * 1E-6 # to N/mm*2 + E2 = matprops[i, 1] * 1E-6 # to N/mm*2 + E3 = matprops[i, 2] * 1E-6 # to N/mm*2 + nu12 = matprops[i, 3] + nu13 = matprops[i, 4] + nu23 = matprops[i, 5] + G12 = matprops[i, 6] * 1E-6 # to N/mm*2 + G13 = matprops[i, 7] * 1E-6 # to N/mm*2 + G23 = matprops[i, 8] * 1E-6 # to N/mm*2 + rho = matprops[i, 9] * 1E-9 # to kg/mm*3 + # deterimne material type + if label in self.core_mats: + # mat_type = 'core' + _write_core( + out_str, n, label, E1, E2, nu12, G12, G13, G23, rho) + elif E1 == E2 and nu12 == nu13: + # mat_type = 'iso' + _write_iso(out_str, n, label, E1, nu12, rho) + else: + # mat_type = 'ortho' + _write_ortho(out_str, n, label, E1, E2, nu12, G12, rho) + + # write blade name and span pos + self.spanpos = self.cs2d['s'] * 1E3 # to mm + out_str.append('blade, %s, %g' % (self.name, self.spanpos) + n) + + # write first (and only) section + out_str.append('Section, %g' % (self.spanpos) + n) + + # write coords + coords = self.cs2d['coords'] * 1E3 # to mm + for i, c in enumerate(coords): + out_str.append('k, %g, %g' % (c[0], c[1]) + n) + + # write region layup and dps + for ireg, _ in enumerate(self.cs2d['regions']): + layers = self.cs2d['regions'][ireg]['layers'] + thicknesses = self.cs2d['regions'][ + ireg]['thicknesses'] * 1E3 # to mm + angles = self.cs2d['regions'][ireg]['angles'] + + dp0 = self.cs2d['DPs'][ireg] + dp1 = self.cs2d['DPs'][ireg + 1] + for l, t, a in zip(layers, thicknesses, angles): + out_str.append('Layer, %s, %g, %g, %g, %g' % + (l[:-2], t, a, dp0, dp1) + n) + + # write web layup and dps + for ireg, _ in enumerate(self.cs2d['webs']): + layers = self.cs2d['webs'][ireg]['layers'] + thicknesses = self.cs2d['webs'][ + ireg]['thicknesses'] * 1E3 # to mm + angles = self.cs2d['regions'][ireg]['angles'] + # check if web exists at this cross section + if not np.sum(thicknesses) == 0.0: + dp0 = self.cs2d['DPs'][self.cs2d['web_def'][ireg][0]] + dp1 = self.cs2d['DPs'][self.cs2d['web_def'][ireg][1]] + out_str.append('Shearweb, %g, %g' % (dp0, dp1) + n) + + wdp0 = 0.0 # web layup does not change along width + wdp1 = 1.0 + for l, t, a in zip(layers, thicknesses, angles): + out_str.append('Layer, %s, %g, %g, %g, %g' % + (l[:-2], t, a, wdp0, wdp1) + n) + + if self.use_becas_stiffness: + x_e = self.csprops_ref[2] * 1E3 # to mm + y_e = self.csprops_ref[3] * 1E3 # to mm + I_xx = self.csprops_ref[7] + I_yy = self.csprops_ref[8] + I_xy = self.csprops_ref[9] + nu = self.csprops_ref[17] + np.pi / 2 + # TODO: check if sign is correct in I_xy! + # A = self.csprops_ref[15] + + # Gere 12-30 + theta = 0.5 * np.arctan(-2 * I_xy / (I_xx - I_yy)) + + # Gere 12-32 + R = np.sqrt((0.5 * (I_xx - I_yy))**2 + I_xy**2) + + # Gere 12-31a + theta1 = 0.5 * np.arccos((I_xx - I_yy) / (2 * R)) + + # Gere 12-31b + theta2 = 0.5 * np.arcsin(-I_xy / R) + + # if not np.sign(theta) == np.sign(nu): + # raise RuntimeError('BECAS input not consistent') + + config = {} + config['BECASWrapper'] = {} + config['BECASWrapper']['hawc2_FPM'] = True + from becas_wrapper.becas_bladestructure import set_cs_size + cs_size, _ = set_cs_size(config) + if cs_size == len(self.csprops): + #hawc2_FPM = True + AE = self.csprops[20] * 1E-3 # to N/mm + else: + #hawc2_FPM = False + E = self.csprops[8] + An = self.csprops[15] + AE = E * An * 1E-3 # to N/mm + + # AE = A * E # to N/mm + EI_xx = E * I_xx * 1E3 # to Nmm**2/mm + EI_yy = E * I_yy * 1E3 # to Nmm**2/mm + EI_xy = E * I_xy * 1E3 # to Nmm**2/mm + + out_str.append('farob, %g, %g, %g, %g, %g, %g' % + (x_e, y_e, AE, EI_xx, EI_yy, EI_xy) + n) + + fid = open(self.file_input, 'w') + for i, line in enumerate(out_str): + fid.write(line) + fid.close() def _write_finstrip_analysis_files(self): - self._write_file_loads() + self._write_file_lcd() def _write_file_lcd(self): - ''' - np.savetxt(self.file_loads, self.loads, fmt='%.10e', delimiter=',') - np.savetxt( - self.file_aligns, self.aligns, fmt='%.10e', delimiter=',') - ''' + out_str = [] + for case in range(self.ncase): + fx = self.csloads[case, 0] + fy = self.csloads[case, 1] + fz = self.csloads[case, 2] + mx = self.csloads[case, 3] + my = self.csloads[case, 4] + mz = self.csloads[case, 5] + n = '\n' + # write header + out_str.append( + 'loads, %s, %i, %g, %g, %g, %g, %g' % (self.spanpos, fx, fy, fz, mx, my, mz) + n) + + fid = open(self.file_load, 'w') + for i, line in enumerate(out_str): + fid.write(line) + fid.close() def execute(self): super(FINSTRIPWrapperBlade, self).execute() @@ -91,16 +276,19 @@ class FINSTRIPWrapperBlade(FINSTRIPWrapper): self._read_rst_mode_shape() def _read_rst_eigenvalue(self): - # proces_nr, section_nr, load_nr, wavelength, eigenvalue - self.f_eig = np.loadtxt(self.file_rst_eigen, skiprows=1) - # convert wavelength to m - self.f_eig[:, -2] = self.f_eig[:, -2] * 1E-3 + self.f_eig = np.zeros((self.ncase, self.nmode, 2)) + for case in range(self.ncase): + # proces_nr, section_nr, load_nr, wavelength, eigenvalue + self.f_eig[case, :, :] = np.loadtxt( + self.file_rst_eigen, skiprows=1)[case * self.nmode:(case + 1) * self.nmode, -2:] + # convert wavelength to m + self.f_eig[:, :, 0] = self.f_eig[:, :, 0] * 1E-3 def _read_rst_mode_shape(self): # determine number of elements self.nelm = len(np.loadtxt('R' + - '%i' % (self.spanpos) + + '%05i' % (self.spanpos) + '_LC' + '%03d' % 0 + '_mode' @@ -108,41 +296,43 @@ class FINSTRIPWrapperBlade(FINSTRIPWrapper): self.txt_sfx, skiprows=2)[:, 0]) # get mode shapes - self.mode_shape = np.zeros((self.nmode, self.nelm, 8)) + self.mode_shape = np.zeros((self.ncase, self.nmode, self.nelm, 8)) # x-loc p1, y-loc p1, x-loc p2, y-loc p2, # x-u p1, y-u p1, x-u p2, y-u p2 for case in range(self.ncase): for mode in range(self.nmode): - # transform to m - self.mode_shape[mode, :, :] = 1E-3 * np.loadtxt('R' + - '%i' % (self.spanpos) + - '_LC' + - '%03d' % (case) + - '_mode' - '%03d' % (mode) + - self.txt_sfx, - skiprows=2) + self.mode_shape[case, mode, :, :] = np.loadtxt('R' + + '%05i' % (self.spanpos) + + '_LC' + + '%03d' % (case) + + '_mode' + '%03d' % (mode) + + self.txt_sfx, + skiprows=2) idxs = [] # identify surface and web nodes for elm in range(self.nelm - 1): - elm1_node2 = self.mode_shape[0, elm, 2:4] - elm2_node1 = self.mode_shape[0, elm + 1, :2] + elm1_node2 = self.mode_shape[0, 0, elm, 2:4] + elm2_node1 = self.mode_shape[0, 0, elm + 1, :2] if elm1_node2[0] != elm2_node1[0] and elm1_node2[1] != elm2_node1[1]: idxs.append(elm) # append last elm index - idxs.append(len(self.mode_shape[0, :, 0]) - 1) + idxs.append(len(self.mode_shape[0, 0, :, 0]) - 1) # obtain surface nodes - self.nnode_loc_surf = idxs[0] + 1 + 1 - self.node_loc_surf = np.zeros((self.nnode_loc_surf, 2)) + self.nnode_surf = idxs[0] + 1 + 1 + self.node_loc_surf = np.zeros((self.nnode_surf, 2)) self.node_loc_surf[ - :-1, :] = self.mode_shape[0, :idxs[0] + 1, :2] + :-1, :] = self.mode_shape[0, 0, :idxs[0] + 1, :2] # add second node of last element self.node_loc_surf[-1:, - :] = self.mode_shape[0, idxs[0], 2:4] + :] = self.mode_shape[0, 0, idxs[0], 2:4] # obtain web nodes + # actual amount of webs in post processing files can differ from input + # NOTE: if the flatback web is oriented such that it closes the cross section, + # then it is identified as if the web was part of the surface self.nweb = len(idxs[1:]) self.nnode_web = [] @@ -154,29 +344,29 @@ class FINSTRIPWrapperBlade(FINSTRIPWrapper): (self.nweb, self.nnode_web_max, 2)) for w in range(self.nweb): self.node_loc_web[ - w, :self.nnode_web[w] - 1, :] = self.mode_shape[0, idxs[w] + 1:idxs[w + 1] + 1, :2] + w, :self.nnode_web[w] - 1, :] = self.mode_shape[0, 0, idxs[w] + 1:idxs[w + 1] + 1, :2] # add second node of last element self.node_loc_web[w, self.nnode_web[w] - 1, :] = self.mode_shape[ - 0, idxs[w + 1], 2:4] + 0, 0, idxs[w + 1], 2:4] # obtain surface displacements self.node_u_surf = np.zeros( - (self.nmode, self.nnode_loc_surf, 2)) + (self.ncase, self.nmode, self.nnode_surf, 2)) self.node_u_surf[ - :, :-1, :] = self.mode_shape[:, :idxs[0] + 1, 4:6] + :, :, :-1, :] = self.mode_shape[:, :, :idxs[0] + 1, 4:6] # add second node of last element self.node_u_surf[ - :, -1, :] = self.mode_shape[:, idxs[0], 6:] + :, :, -1, :] = self.mode_shape[:, :, idxs[0], 6:] # obtain web displacements self.node_u_web = np.zeros( - (self.nweb, self.nmode, self.nnode_web_max, 2)) + (self.ncase, self.nweb, self.nmode, self.nnode_web_max, 2)) for w in range(self.nweb): self.node_u_web[ - w, :, :self.nnode_web[w] - 1, :] = self.mode_shape[:, idxs[w] + 1:idxs[w + 1] + 1, 4:6] + :, w, :, :self.nnode_web[w] - 1, :] = self.mode_shape[:, :, idxs[w] + 1:idxs[w + 1] + 1, 4:6] # add second node of last element - self.node_u_web[w, :, self.nnode_web[w] - 1, :] = self.mode_shape[ - :, idxs[w + 1], 6:] + self.node_u_web[:, w, :, self.nnode_web[w] - 1, :] = self.mode_shape[ + :, :, idxs[w + 1], 6:] plot = False if plot: @@ -199,5 +389,3 @@ class FINSTRIPWrapperBlade(FINSTRIPWrapper): self.node_loc_web[ w, :self.nnode_web[w], 0] + sf * self.node_u_web[w, mode, :self.nnode_web[w], 0], self.node_loc_web[w, :self.nnode_web[w], 1] + sf * self.node_u_web[w, mode, :self.nnode_web[w], 1], '--') - - plt.show() diff --git a/finstrip_wrapper/core/main.py b/finstrip_wrapper/core/main.py index ee632ae..7275aec 100644 --- a/finstrip_wrapper/core/main.py +++ b/finstrip_wrapper/core/main.py @@ -34,23 +34,24 @@ class FINSTRIPWrapper(object): def __init__(self, **kwargs): super(FINSTRIPWrapper, self).__init__() - # finstrip program parameters - # maximum size of generated element in mm - self.maxlength_element = 0.252E+03 - self.lambda_steps = 20 # number of half wavelength steps - self.lambda_min = 0.01E+03 # minimum half wavelength in mm - self.lambda_max = 15.126E+03 # maximum half wavelength in mm + # element length as fraction of arc length of airfoil surface + self.elsize_factor = 0.015 - self.spanpos = 0 # position along span of analysed cross-section + # finstrip program parameters + self.lambda_min = 0.01 # minimum half wavelength in m + self.lambda_max = 15.126 # maximum half wavelength in m - self.nmode = self.lambda_steps + 1 - self.ncoord = 79 - self.ncase = 1 + self.nmode = 21 + #self.ncoord = 79 + #self.ncase = 1 # flags self.debug_mode = True self.dry_run = False + def _init_half_wavelength_steps(self): + self.lambda_steps = self.nmode - 1 # number of half wavelength steps + def _init_finstrip_input_files_names(self): ''' Initializes standard input file names or extension ''' @@ -59,10 +60,11 @@ class FINSTRIPWrapper(object): # input file names self.buc_sfx = '.buc' - self.lcd_sfx = '.lcd' + self.lcd_sfx = '.ldc' # self.fdb_prfx = 'db' # why no "." ?? self.txt_sfx = '.txt' self.file_input = self.name + self.buc_sfx + self.file_load = self.name + self.lcd_sfx #self.file_output = self.name + self.fcdb_prfx #self.file_mat = 'MAT' + self.fd_sfx #self.file_failmat = 'FAILMAT' + self.fd_sfx diff --git a/finstrip_wrapper/omdao/finstrip_blade.py b/finstrip_wrapper/omdao/finstrip_blade.py index 43005f4..ce2c380 100644 --- a/finstrip_wrapper/omdao/finstrip_blade.py +++ b/finstrip_wrapper/omdao/finstrip_blade.py @@ -1,549 +1,678 @@ import numpy as np import os -import shutil +from openmdao.api import Component, Group, ParallelGroup, ExecComp +from finstrip_wrapper.core.blade import FINSTRIPWrapperBlade from PGL.components.airfoil import AirfoilShape - -from openmdao.api import Component, Group, ParallelGroup from becas_wrapper.becas_bladestructure import set_cs_size -from feproc_wrapper.omdao.blade import set_ncsnodes_nsets -class FINSTRIPCrossSectionsCaseBase(Component): +class FINSTRIPCSStructure(Component): + """ + Component for computing beam structural properties + using the cross-sectional structure code FINSTRIP. + + The code firstly calls CS2DtoFINSTRIP which is a wrapper around + shellexpander that comes with FINSTRIP, and second + calls FINSTRIP using a file interface. + + parameters + ---------- + config: dict + dictionary of model specific inputs + coords: array + cross-sectional shape. size ((ni_chord, 3)) + matprops: array + material stiffness properties. Size ((10, nmat)). + DPs: array + vector of DPs. Size: (nDP) + r<xx><lname>T: float + layer thicknesses, e.g. r01triaxT. + r<xx><lname>A: float + layer angles, e.g. r01triaxA. + w<xx><lname>T: float + web thicknesses, e.g. r01triaxT. + w<xx><lname>A: float + web angles, e.g. r01triaxA. + + returns + ------- + + """ - def __init__(self, name, config, caseid, sdim, ncase, nload): - super(FINSTRIPCrossSectionsCaseBase, self).__init__() + def __init__(self, name, finstrip_hash, config, st3d, s, ni_chord, ncase): + """ + parameters + ---------- + config: dict + dictionary with inputs to CS2DtoFINSTRIP and FINSTRIPWrapper + st3d: dict + dictionary with blade structural definition + s: array + spanwise location of the cross-section + ni_chord: int + number of points defining the cross-section shape + """ + super(FINSTRIPCSStructure, self).__init__() + + self.basedir = os.getcwd() + self.finstrip_hash = finstrip_hash + self.nr = len(st3d['regions']) + self.ni_chord = ni_chord + # threshold for minimum layer thickness + try: + self.min_thickness = config['FINSTRIPWrapper']['min_thickness'] + except: + self.min_thickness = 1.e-7 - self.name = name - self.caseid = caseid + self.nmode = config['FINSTRIPWrapper']['nmode'] + self.nnode_surf_max = nnode_surf_max = 150 + self.nnode_web_max = nnode_web_max = 50 + self.nweb = len(st3d['webs']) self.ncase = ncase - self.nsec = sdim[1] - self.config = config + try: + self.use_becas_stiffness = config[ + 'FINSTRIPWrapper']['use_becas_stiffness'] + except: + self.use_becas_stiffness = False + + # add materials properties array ((10, nmat)) + self.add_param('matprops', st3d['matprops']) + + # add materials strength properties array ((18, nmat)) + # self.add_param('failmat', st3d['failmat']) + + # add DPs array + self.add_param('stab:' + '%s:DPs' % name, np.zeros(self.nr + 1)) + self.add_param('stab:' + '%s:rot_z_st' % name, 0.) + + # add coords coords + self._varnames = [] + self.add_param('stab:' + '%s:coords' % name, np.zeros((ni_chord, 3))) + + self.cs2di = {} + self.cs2di['materials'] = st3d['materials'] + self.cs2di['matprops'] = st3d['matprops'] + #self.cs2di['failcrit'] = st3d['failcrit'] + #self.cs2di['failmat'] = st3d['failmat'] + self.cs2di['web_def'] = st3d['web_def'] + #self.cs2di['dominant_regions'] = st3d['dominant_regions'] + #self.cs2di['cap_DPs'] = st3d['cap_DPs'] + #self.cs2di['le_DPs'] = st3d['le_DPs'] + #self.cs2di['te_DPs'] = st3d['te_DPs'] + self.cs2di['s'] = s + self.cs2di['DPs'] = np.zeros(self.nr + 1) + self.cs2di['regions'] = [] + self.cs2di['webs'] = [] + for ireg, reg in enumerate(st3d['regions']): + r = {} + r['layers'] = reg['layers'] + nl = len(reg['layers']) + r['thicknesses'] = np.zeros(nl) + r['angles'] = np.zeros(nl) + self.cs2di['regions'].append(r) + for i, lname in enumerate(reg['layers']): + varname = '%s:r%02d%s' % (name, ireg, lname) + self._varnames.append(varname) + for ireg, reg in enumerate(st3d['webs']): + r = {} + r['layers'] = reg['layers'] + nl = len(reg['layers']) + r['thicknesses'] = np.zeros(nl) + r['angles'] = np.zeros(nl) + self.cs2di['webs'].append(r) + for i, lname in enumerate(reg['layers']): + varname = '%s:w%02d%s' % (name, ireg, lname) + self._varnames.append(varname) + self.add_param( + 'stab:' + name + ':tvec', np.zeros(len(self._varnames) * 2)) + + if self.use_becas_stiffness: + cs_size, cs_size_ref = set_cs_size(config) + self.add_param('stab:' + '%s:csprops' % name, np.zeros((cs_size))) + self.add_param('stab:' + '%s:csprops_ref' % + name, np.zeros((cs_size_ref))) + + self.add_param('load_cases_%s' % name, np.zeros((ncase, 6))) + + # add outputs + self.add_output('%s:f_eig' % + name, np.zeros((self.ncase, self.nmode, 2))) + self.add_output('%s:nnode_surf' % name, 0) + self.add_output('%s:nnode_web' % name, np.zeros(self.nweb).astype(int)) + self.add_output('%s:node_loc_surf' % + name, np.zeros((self.nnode_surf_max, 2))) + self.add_output('%s:node_loc_web' % + name, np.zeros((self.nweb, self.nnode_web_max, 2))) + self.add_output('%s:node_u_surf' % + name, np.zeros((self.ncase, self.nmode, self.nnode_surf_max, 2))) + self.add_output('%s:node_u_web' % + name, np.zeros((self.ncase, self.nweb, self.nmode, self.nnode_web_max, 2))) + + #self.add_output('%s:cs_props' % name, np.zeros(cs_size)) + #self.add_output('%s:csprops_ref' % name, np.zeros(cs_size_ref)) + #self.add_output('%s:k_matrix' % name, shape=(6, 6)) + #self.add_output('%s:m_matrix' % name, shape=(6, 6)) + #self.cs_props_m1 = np.zeros(cs_size) + #self.csprops_ref_m1 = np.zeros(cs_size_ref) + #self.k_matrix_m1 = np.zeros((6, 6)) + #self.m_matrix_m1 = np.zeros((6, 6)) + + #self.add_output('%s:DPcoords' % name, np.zeros((self.nr + 1, 3))) + + self.workdir = 'finstrip_%s_%i' % (name, self.finstrip_hash) + # not so nice hack to ensure unique directory names when + # running parallel FD + self.add_output('stab:' + name + ':hash', float(self.finstrip_hash)) + + self.finstrip = FINSTRIPWrapperBlade(**config['FINSTRIPWrapper']) + + def _params2dict(self, params): + """ + convert the OpenMDAO params dictionary into + the dictionary format used in FINSTRIP. + """ + tvec = params['stab:' + self.name + ':tvec'] + + self.cs2d = {} + # constants + self.cs2d['s'] = self.cs2di['s'] + self.cs2d['web_def'] = self.cs2di['web_def'] + #self.cs2d['failcrit'] = self.cs2di['failcrit'] + self.cs2d['materials'] = self.cs2di['materials'] + #self.cs2d['dominant_regions'] = self.cs2di['dominant_regions'] + #self.cs2d['cap_DPs'] = self.cs2di['cap_DPs'] + #self.cs2d['le_DPs'] = self.cs2di['le_DPs'] + #self.cs2d['te_DPs'] = self.cs2di['te_DPs'] + + # params + self.cs2d['coords'] = params['stab:' + '%s:coords' % self.name][:, :2] + self.cs2d['matprops'] = params['matprops'] + #self.cs2d['failmat'] = params['failmat'] + + self.cs2d['DPs'] = params['stab:' + '%s:DPs' % self.name] + self.cs2d['regions'] = [] + self.cs2d['webs'] = [] + counter = 0 + nvar = len(self._varnames) + for ireg, reg in enumerate(self.cs2di['regions']): + self.cs2d['regions'].append({}) + Ts = [] + As = [] + layers = [] + for i, lname in enumerate(reg['layers']): + if tvec[counter] > self.min_thickness: + Ts.append(tvec[counter]) + As.append(tvec[nvar + counter]) + layers.append(lname) + counter += 1 + self.cs2d['regions'][ireg]['thicknesses'] = np.asarray(Ts) + self.cs2d['regions'][ireg]['angles'] = np.asarray(As) + self.cs2d['regions'][ireg]['layers'] = layers + for ireg, reg in enumerate(self.cs2di['webs']): + self.cs2d['webs'].append({}) + Ts = [] + As = [] + layers = [] + for i, lname in enumerate(reg['layers']): + if tvec[counter] > self.min_thickness: + Ts.append(tvec[counter]) + As.append(tvec[nvar + counter]) + layers.append(lname) + counter += 1 + self.cs2d['webs'][ireg]['thicknesses'] = np.asarray(Ts) + self.cs2d['webs'][ireg]['angles'] = np.asarray(As) + self.cs2d['webs'][ireg]['layers'] = layers + + def solve_nonlinear(self, params, unknowns, resids): + """ + calls FINSTRIP to compute the eigenvalues and mode shapes + """ + + try: + os.mkdir(self.workdir) + except: + pass + os.chdir(self.workdir) + + self._params2dict(params) - self.cs_size, self.cs_size_ref = set_cs_size(config) - self.ncsnodes, _, _ = set_ncsnodes_nsets(config) + self.finstrip.init_dicts(self.cs2d) - self.add_param('blade_length', 0., units='m', desc='Blade length') - self.add_param('z_st', np.zeros(self.nsec), units='m', - desc='non-dimensionalised y-coordinate of blade axis in structural grid') - self.add_param('rot_z_st', np.zeros(self.nsec), - desc='rotation about blade axis') - self.add_param('chord_st', np.zeros(self.nsec), units='m', - desc='blade chord distribution in structural grid') - self.add_param('p_le_st', np.zeros(self.nsec), units='m', - desc='blade pitch axis aft leading edge in structural grid') - self.add_param('blade_beam_structure', np.zeros((self.nsec, self.cs_size)), - desc='Beam properties of the blade') - self.add_param('blade_beam_csprops_ref', np.zeros((self.nsec, self.cs_size_ref)), - desc='Cross section properties of the blade') + twist = params['stab:' + '%s:rot_z_st' % self.name] # deg + cs_loads_rcs = params['load_cases_%s' % self.name] + self.finstrip.init_cs_load_cases(cs_loads_rcs, twist) - #case_name = 'case_00' - self.add_param('loc_cs_nodes', np.zeros((self.ncsnodes, 6, self.nsec))) + if self.use_becas_stiffness: + self.finstrip.init_becas_stiffness(params['stab:' + '%s:csprops' % + self.name], + params['stab:' + '%s:csprops_ref' % + self.name]) - self.add_param('n_cs_nodes', np.zeros((self.nsec)).astype(int)) + self.finstrip.compute() - self.add_output('%s:cs_strains' % name, np.zeros((self.ncsnodes, 4, self.nsec)), - desc='Cross section strains of the blade') - self.add_output('%s:loc_cs_nodes_local' % name, np.zeros((self.ncsnodes, 6, self.nsec)), - desc='Local coordinates of cs nodes') - self.add_output('%s:blade_beam_csprops_ref_global' % name, np.zeros((self.nsec, self.cs_size_ref)), - desc='Global coordinates of cs nodes') + self.unknowns['%s:f_eig' % self.name] = self.finstrip.f_eig + nnode_surf = self.finstrip.nnode_surf + nnode_web_max = np.max(self.finstrip.nnode_web) + nweb_actual = self.finstrip.nweb + self.unknowns['%s:nnode_surf' % self.name] = nnode_surf + self.unknowns['%s:nnode_web' % self.name] = self.finstrip.nnode_web + self.unknowns['%s:node_loc_surf' % self.name][ + :nnode_surf, :] = self.finstrip.node_loc_surf + self.unknowns['%s:node_loc_web' % self.name][ + :nweb_actual, :nnode_web_max, :] = self.finstrip.node_loc_web + self.unknowns['%s:node_u_surf' % self.name][ + :, :, :nnode_surf, :] = self.finstrip.node_u_surf + self.unknowns['%s:node_u_web' % self.name][ + :, :nweb_actual, :, :nnode_web_max, :] = self.finstrip.node_u_web + os.chdir(self.basedir) + + +class FINSTRIPCSStructureLoader(FINSTRIPCSStructure): + + def __init__(self, name, finstrip_hash, config, st3d, s, ni_chord, cs_size, cs_size_ref, pickled_unkn, + grp_name=''): + super(FINSTRIPCSStructureLoader, self).__init__( + name, finstrip_hash, config, st3d, s, ni_chord, cs_size, cs_size_ref) + + self.pickled_unkn = pickled_unkn + self.grp_name = grp_name + self.promoted = [] + + def solve_nonlinear(self, params, unknowns, resids): + # write data base pickled dict content into unknowns + for kp, vp in self.pickled_unkn.iteritems(): + for ku in unknowns.iterkeys(): + if kp.endswith(ku) and kp.startswith(self.grp_name): + unknowns[ku] = vp + elif kp.endswith(ku) and self.promoted: + unknowns[ku] = vp -class FINSTRIPBladeCrossSectionsCase(FINSTRIPCrossSectionsCaseBase): - """ - Component for analytical calculations of a blade cross section. - Returns cross section angles and strains due to a full-scale blade load case. +class Slice(Component): """ + simple component for slicing arrays into vectors + for passing to sub-comps computing the eig values + + parameters + ---------- + DP<xx>: array + arrays of DPs along span. Size: (nsec) + blade_surface_norm_st: array + blade surface with structural discretization, no twist and prebend. + Size: ((ni_chord, nsec, 3)) + + returns + ------- + sec<xxx>DPs: array + Vector of DPs along chord for each section. Size (nDP) + sec<xxx>coords: array + Array of cross section coords shapes. Size ((ni_chord, 3)) + """ + + def __init__(self, st3d, sdim, config): + """ + parameters + ---------- + DPs: array + DPs array, size: ((nsec, nDP)) + sdim: array + blade surface. Size: ((ni_chord, nsec, 3)) + """ + super(Slice, self).__init__() - def __init__(self, name, config, caseid, sdim, ncase, nload): - super(FINSTRIPBladeCrossSectionsCase, self).__init__( - name, config, caseid, sdim, ncase, nload) + self.nsec = sdim[1] + DPs = st3d['DPs'] + self.nDP = DPs.shape[1] try: - self.load_case_moment_distro = config[ - 'FINSTRIP']['load_case_moment_distro'] + self.use_becas_stiffness = config[ + 'FINSTRIPWrapper']['use_becas_stiffness'] except: - self.load_case_moment_distro = False + self.use_becas_stiffness = False + + for i in range(self.nDP): + self.add_param('DP%02d' % i, DPs[:, i]) + + self.add_param('blade_surface_norm_st', np.zeros(sdim)) + #self.add_param('blade_surface_st', np.zeros(sdim)) + self.add_param('blade_length', 0.) + self.add_param('rot_z_st', np.zeros(self.nsec)) + + if self.use_becas_stiffness: + cs_size, cs_size_ref = set_cs_size(config) + self.add_param('blade_beam_structure', np.zeros((self.nsec, cs_size)), + desc='Beam properties of the blade') + self.add_param('blade_beam_csprops_ref', np.zeros((self.nsec, cs_size_ref)), + desc='Cross section properties of the blade') + + vsize = 0 + self._varnames = [] + for ireg, reg in enumerate(st3d['regions']): + for i, lname in enumerate(reg['layers']): + varname = 'r%02d%s' % (ireg, lname) + self.add_param(varname + 'T', np.zeros(self.nsec)) + self.add_param(varname + 'A', np.zeros(self.nsec)) + self._varnames.append(varname) + for ireg, reg in enumerate(st3d['webs']): + for i, lname in enumerate(reg['layers']): + varname = 'w%02d%s' % (ireg, lname) + self.add_param(varname + 'T', np.zeros(self.nsec)) + self.add_param(varname + 'A', np.zeros(self.nsec)) + self._varnames.append(varname) + + for i in range(self.nsec): + self.add_output('stab:sec%03d:DPs' % i, DPs[i, :]) + self.add_output('stab:sec%03d:coords' % + i, np.zeros((sdim[0], sdim[2]))) + self.add_output('stab:sec%03d:tvec' % + i, np.zeros(len(self._varnames) * 2)) + self.add_output('stab:sec%03d:rot_z_st' % i, 0.) + + if self.use_becas_stiffness: + self.add_output('stab:sec%03d:csprops' % + i, np.zeros((cs_size))) + self.add_output('stab:sec%03d:csprops_ref' % + i, np.zeros((cs_size_ref))) - self.add_param('load_cases', np.zeros((ncase, nload, 15))) - - self.add_output('%s:cs_angles' % name, np.zeros(self.nsec), - desc='Cross section angles of the blade') + def solve_nonlinear(self, params, unknowns, resids): - self.add_output('%s:reactions' % name, np.zeros((self.nsec, 6)), - desc='Cross section reactions') + nvar = len(self._varnames) + for i in range(self.nsec): + #DPs = np.zeros(self.nDP) + # for j in range(self.nDP): + # DPs[j] = params['DP%02d' % j][i] + + # convert DPs from s11 to s01 notation + af = AirfoilShape() + af.initialize(points=params['blade_surface_norm_st'][:, i, :] * + params['blade_length']) + DPs01 = np.zeros(self.nDP) + DPs11 = np.zeros(self.nDP) + for j in range(self.nDP): + DPs11[j] = params['DP%02d' % j][i] + if j == 0: + # fix to omit very small negative values (e.g.-1.0E-16) + DPs01[j] = 0.0 + else: + DPs01[j] = af.s_to_01(DPs11[j]) + + unknowns['stab:sec%03d:DPs' % i] = DPs01 + unknowns['stab:sec%03d:rot_z_st' % i] = params['rot_z_st'][i] + unknowns['stab:sec%03d:coords' % i] = params['blade_surface_norm_st'][:, i, :] * \ + params['blade_length'] + # unknowns['sec%03d:coords' % i] = params['blade_surface_st'][:, i, :] * \ + # params['blade_length'] + + for ii, name in enumerate(self._varnames): + unknowns['stab:sec%03d:tvec' % i][ii] = params[name + 'T'][i] + unknowns['stab:sec%03d:tvec' % i][ + nvar + ii] = params[name + 'A'][i] + + if self.use_becas_stiffness: + unknowns['stab:sec%03d:csprops' % i] = params[ + 'blade_beam_structure'][i, :] + unknowns['stab:sec%03d:csprops_ref' % i] = params[ + 'blade_beam_csprops_ref'][i, :] + + +class SliceLoader(Slice): + + def __init__(self, st3d, sdim, pickled_unkn, grp_name=''): + super(SliceLoader, self).__init__(st3d, sdim) + + self.pickled_unkn = pickled_unkn + self.grp_name = grp_name + self.promoted = [] def solve_nonlinear(self, params, unknowns, resids): + # write data base pickled dict content into unknowns + for kp, vp in self.pickled_unkn.iteritems(): + for ku in unknowns.iterkeys(): + if kp.endswith(ku) and kp.startswith(self.grp_name): + unknowns[ku] = vp + elif kp.endswith(ku) and self.promoted: + unknowns[ku] = vp - nnodes = params['n_cs_nodes'] - - # determine forces and moments - # x,y,z, rotx,roty,rotz, Fx,Fy,Fz, Mx,My,Mz - z_positions = params['load_cases'][self.caseid, :, 2] - if self.load_case_moment_distro: - moments = params['load_cases'][self.caseid, :, 9:12] - else: - forces = params['load_cases'][self.caseid, :, 6:9] - #force_y = params['load_cases'][self.ncase, :, 7] - #force_z = params['load_cases'][self.ncase, :, 8] - - for nsec in range(self.nsec): - # center rotated along principal axis - AE = params['blade_beam_structure'][nsec, 20] - EI_x_prime = params['blade_beam_structure'][nsec, 24] - EI_y_prime = params['blade_beam_structure'][nsec, 27] - elastic_x = params['blade_beam_csprops_ref'][nsec, 2] - elastic_y = params['blade_beam_csprops_ref'][nsec, 3] - nu = - np.degrees( - params['blade_beam_csprops_ref'][nsec, 17]) # deg (clock-wise positive) - twist = params['rot_z_st'][nsec] # deg - chord = params['chord_st'][nsec] * \ - params['blade_length'] # tip end - le_x = params['p_le_st'][nsec] * chord - te_x = -chord + le_x - te_y = 0 - - if self.load_case_moment_distro: - Mx = moments[nsec, 0] - My = moments[nsec, 1] - - else: - # transform point loads to moment distro - moment_position = params['z_st'][nsec] * params['blade_length'] - moments = convert_point_forces_to_moment_distro( - moment_position, z_positions, forces) - - Mx = moments[0] - My = moments[1] - - unknowns['%s:reactions' % self.name][nsec, 3] = Mx - unknowns['%s:reactions' % self.name][nsec, 4] = My - - #My = -1.0E+06 - #Mx = 0.0 - - locx = params['loc_cs_nodes'][:nnodes[nsec], 0, nsec] - locy = params['loc_cs_nodes'][:nnodes[nsec], 1, nsec] - - # create component - blade_an = CrossSection(AE, - EI_x_prime, - EI_y_prime, - elastic_x, - elastic_y, - nu, - twist, - te_x, - te_y) - blade_an.apply_bending_moment(Mx, My) - - # neutral axis angle - gamma = blade_an.gamma - unknowns['%s:cs_angles' % self.name][nsec] = gamma - - # transform elastic center to rcs - elastic_x_rcs_, elastic_y_rcs_ = blade_an.cs2root( - [elastic_x], [elastic_y]) - unknowns['%s:blade_beam_csprops_ref_global' % - self.name][nsec, 2] = elastic_x_rcs_ - unknowns['%s:blade_beam_csprops_ref_global' % - self.name][nsec, 3] = elastic_y_rcs_ - - # coordinates transformed to cross section cs - loccsx, loccsy = blade_an.root2cs(locx, locy) - unknowns['%s:loc_cs_nodes_local' % - self.name][:nnodes[nsec], 0, nsec] = loccsx - unknowns['%s:loc_cs_nodes_local' % - self.name][:nnodes[nsec], 1, nsec] = loccsy - - # strains - epto_blade_an, epto_blade_an_ax, epto_blade_an_rotx, epto_blade_an_roty = blade_an.recover_strain( - locx, locy) - unknowns['%s:cs_strains' % self.name][ - :nnodes[nsec], 0, nsec] = epto_blade_an - unknowns['%s:cs_strains' % self.name][ - :nnodes[nsec], 1, nsec] = epto_blade_an_ax - unknowns['%s:cs_strains' % self.name][ - :nnodes[nsec], 2, nsec] = epto_blade_an_rotx - unknowns['%s:cs_strains' % self.name][ - :nnodes[nsec], 3, nsec] = epto_blade_an_roty - - -class FINSTRIPComponentCrossSectionsCase(FINSTRIPCrossSectionsCaseBase): - """ - Component for analytical calculations of a blade component cross section. - Returns cross section angles and strains due to a full-scale blade load case. +class PostprocessCS(Component): """ + component for gathering cross section eigen values and wave lengths + into array as function of span + + parameters + ---------- + f_eig<xxx>: array + array of cross section eigen values. size (nmode,2) + + returns + ------- + blade_f_eig: array + array of buckling resistances. Size ((nsec,nmode,2)). + """ + + def __init__(self, nsec, nmode, ncase): + """ + parameters + ---------- + nsec: int + number of blade sections. + """ + super(PostprocessCS, self).__init__() + + self.nsec = nsec - def __init__(self, name, config, caseid, sdim, ncase, nload, nsec_b): + for i in range(nsec): + self.add_param('f_eig%03d' % i, np.zeros((ncase, nmode, 2))) - super(FINSTRIPComponentCrossSectionsCase, self).__init__( - name, config, caseid, sdim, ncase, nload) + self.add_output('blade_f_eig', np.zeros((ncase, nsec, nmode, 2))) + + def solve_nonlinear(self, params, unknowns, resids): + """ + aggregate results + """ + for i in range(self.nsec): + cname = 'f_eig%03d' % i + cs = params[cname] + unknowns['blade_f_eig'][:, i, :, :] = cs - self.nsec_b = nsec_b - # assume same initial number of nodes for blade and component - ncsnodes_b = self.ncsnodes - self.add_param('%s:fullblade_z_st' % name, np.zeros(self.nsec_b), units='m', - desc='non-dimensionalised y-coordinate of blade axis in structural grid') - self.add_param('%s:fullblade_cs_angles' % name, np.zeros(nsec_b), - desc='Cross section angles of the blade') - self.add_param('%s:fullblade_beam_csprops_ref' % name, np.zeros((nsec_b, self.cs_size_ref)), - desc='Cross section properties of the blade') - self.add_param('%s:fullblade_cs_strains' % name, np.zeros((ncsnodes_b, 4, self.nsec_b)), - desc='Cross section strains of the blade') - self.add_param('%s:fullblade_loc_cs_nodes' % - name, np.zeros((ncsnodes_b, 6, self.nsec_b))) - self.add_param('%s:fullblade_n_cs_nodes' % - name, np.zeros((self.nsec_b)).astype(int)) +class PostprocessCSLoader(PostprocessCS): - # x,y,z, rotx,roty,rotz, Fx,Fy,Fz, Mx,My,Mz - self.add_output('%s:comp_load_case' % name, np.zeros((2, 12)), - desc='Load case for component') + def __init__(self, nsec, cs_size, cs_size_ref, pickled_unkn, grp_name='', promoted=[]): + super(PostprocessCSLoader, self).__init__(nsec, cs_size, cs_size_ref) + + self.pickled_unkn = pickled_unkn + self.grp_name = grp_name + self.promoted = promoted def solve_nonlinear(self, params, unknowns, resids): + # write data base pickled dict content into unknowns + for kp, vp in self.pickled_unkn.iteritems(): + for ku in unknowns.iterkeys(): + if kp.endswith(ku) and kp.startswith(self.grp_name): + unknowns[ku] = vp + elif kp.endswith(ku) and self.promoted: + unknowns[ku] = vp + - nnodes_c = params['n_cs_nodes'] - nnodes_b = params['%s:fullblade_n_cs_nodes' % self.name] - - # component z coordinate - comp_z = params['z_st'] * params['blade_length'] - - # find nearest index in blade z - blade_z = params['%s:fullblade_z_st' % - self.name] * params['blade_length'] - - # collect fullscale strains at TE - node_idx = -1 # node idx on SS (TESS) at mid te? - ref_blade_epto_an_te = [] - ref_te_x_rcs = [] - ref_te_y_rcs = [] - for nsec_bi in range(self.nsec_b): - ref_blade_epto_an_ = params['%s:fullblade_cs_strains' % self.name][ - :nnodes_b[nsec_bi], 0, nsec_bi][node_idx] - ref_te_x_ = params['%s:fullblade_loc_cs_nodes' % self.name][ - :nnodes_b[nsec_bi], 0, nsec_bi][node_idx] - ref_te_y_ = params['%s:fullblade_loc_cs_nodes' % self.name][ - :nnodes_b[nsec_bi], 1, nsec_bi][node_idx] - - ref_blade_epto_an_te.append(ref_blade_epto_an_) - ref_te_x_rcs.append(ref_te_x_) - ref_te_y_rcs.append(ref_te_y_) - ref_blade_epto_an_te = np.array(ref_blade_epto_an_te) - ref_te_x_rcs = np.array(ref_te_x_rcs) - ref_te_y_rcs = np.array(ref_te_y_rcs) - - blade_an_elastic_x = params[ - '%s:fullblade_beam_csprops_ref' % self.name][:, 2] - blade_an_elastic_y = params[ - '%s:fullblade_beam_csprops_ref' % self.name][:, 3] - - blade_an_gamma = params['%s:fullblade_cs_angles' % self.name][:] - - # interpolate fullscale strains at TE - ref_blade_epto_an_te_comp_z = np.interp( - comp_z, blade_z, ref_blade_epto_an_te) - ref_te_x_comp_z_rcs = np.interp(comp_z, blade_z, ref_te_x_rcs) - ref_te_y_comp_z_rcs = np.interp(comp_z, blade_z, ref_te_y_rcs) - blade_an_elastic_x_comp_z = np.interp( - comp_z, blade_z, blade_an_elastic_x) - blade_an_elastic_y_comp_z = np.interp( - comp_z, blade_z, blade_an_elastic_y) - blade_an_gamma_comp_z = np.interp( - comp_z, blade_z, blade_an_gamma) - - # get component data - AE_c = params['blade_beam_structure'][:, 20] # 9.9E+15 - EI_x_prime_c = params['blade_beam_structure'][:, 24] - EI_y_prime_c = params['blade_beam_structure'][:, 27] - elastic_x_c = params['blade_beam_csprops_ref'][:, 2] - elastic_y_c = params['blade_beam_csprops_ref'][:, 3] - nu_c = - np.degrees( - params['blade_beam_csprops_ref'][:, 17]) # deg (clock-wise positive) - twist_c = params['rot_z_st'][:] # deg - - # strain at the TE reference point at trailing edge - - beta = [] - l_fx = [] - l_fy = [] - Fz = [] - f_x = [] - f_y = [] - f_x_rcs = [] - f_y_rcs = [] - epto_an_te = [] - elastic_x_c_rcs = [] - elastic_y_c_rcs = [] - blade_an_elastic_x_comp_z_rcs = [] - blade_an_elastic_y_comp_z_rcs = [] - m_x = [] - m_y = [] - - for nsec_c in range(self.nsec): # [nsec_cm]: # - - chord = params['chord_st'][nsec_c] * \ - params['blade_length'] # tip end - le_x = params['p_le_st'][nsec_c] * chord - te_x_c = -chord + le_x - te_y_c = 0 - - # TODO: remove dependency from trailing edge - comp_an = CrossSection(AE_c[nsec_c], - EI_x_prime_c[nsec_c], - EI_y_prime_c[nsec_c], - elastic_x_c[nsec_c], - elastic_y_c[nsec_c], - nu_c[nsec_c], - twist_c[nsec_c], - te_x_c, te_y_c) - - # convert elastic center from cs to root - elastic_x_c_rcs_, elastic_y_c_rcs_ = comp_an.cs2root( - [elastic_x_c[nsec_c]], [elastic_y_c[nsec_c]]) - elastic_x_c_rcs.append(elastic_x_c_rcs_[0]) - elastic_y_c_rcs.append(elastic_y_c_rcs_[0]) - - unknowns['%s:blade_beam_csprops_ref_global' % - self.name][nsec_c, 2] = elastic_x_c_rcs_ - unknowns['%s:blade_beam_csprops_ref_global' % - self.name][nsec_c, 3] = elastic_y_c_rcs_ - - # get coordinates from a "dummy" feproc mesh run - # root coordinates - locx_ = params['loc_cs_nodes'][:nnodes_c[nsec_c], 0, nsec_c] - locy_ = params['loc_cs_nodes'][:nnodes_c[nsec_c], 1, nsec_c] - # cs coordinates - loccsx_, loccsy_ = comp_an.root2cs(locx_, locy_) - #loccsx_, loccsy_ = comp_an.cs2root(locx_, locy_) - unknowns['%s:loc_cs_nodes_local' % - self.name][:nnodes_c[nsec_c], 0, nsec_c] = loccsx_ - unknowns['%s:loc_cs_nodes_local' % - self.name][:nnodes_c[nsec_c], 1, nsec_c] = loccsy_ - - # get blade gamma and elastic center - beta_ = comp_an.required_load_angle(blade_an_gamma_comp_z[nsec_c]) - beta.append(beta_) - - # determine vector components of load intro point (from centroid in - # cs cs) - l_fx_, l_fy_ = comp_an.required_arm2( - beta_, blade_an_elastic_x_comp_z[nsec_c], blade_an_elastic_y_comp_z[nsec_c]) - l_fx.append(l_fx_) - l_fy.append(l_fy_) - - # transform blade elastic center from cs to root - blade_an_elastic_x_comp_z_rcs_, blade_an_elastic_y_comp_z_rcs_ = comp_an.cs2root( - [blade_an_elastic_x_comp_z[nsec_c]], [blade_an_elastic_y_comp_z[nsec_c]]) - blade_an_elastic_x_comp_z_rcs.append( - blade_an_elastic_x_comp_z_rcs_[0]) - blade_an_elastic_y_comp_z_rcs.append( - blade_an_elastic_y_comp_z_rcs_[0]) - - # transform full cs coordinates from root to cs - ref_te_x_comp_z_, ref_te_y_comp_z_ = comp_an.root2cs( - [ref_te_x_comp_z_rcs[nsec_c]], [ref_te_y_comp_z_rcs[nsec_c]]) - - # determine required load (if blade was prismatic) - Fz_ = comp_an.required_load( - l_fx_, beta_, ref_te_x_comp_z_[0], ref_te_y_comp_z_[0], - ref_blade_epto_an_te_comp_z[nsec_c]) - Fz.append(Fz_) - - # determine required load introduction point in cs cs - f_x_, f_y_ = comp_an.load_intro_intro_point2(beta_, l_fx_) - f_x.append(f_x_) - f_y.append(f_y_) - - # convert load intro point from cs to root - f_x_rcs_, f_y_rcs_ = comp_an.cs2root( - [f_x_], [f_y_]) - f_x_rcs.append(f_x_rcs_[0]) - f_y_rcs.append(f_y_rcs_[0]) - - # cantilever arm for bending moment - a_x = f_x_rcs_ - elastic_x_c_rcs_ - a_y = f_y_rcs_ - elastic_y_c_rcs_ - m_y_ = Fz_ * -a_x - m_x_ = Fz_ * -a_y - m_x.append(float(m_x_)) - m_y.append(float(m_y_)) - - # apply required load - comp_an.apply_axial_load2(Fz_, f_x_, f_y_) - - # recover strains of applied load - _epto_an, _epto_an_ax, _epto_an_rotx, _epto_an_roty = comp_an.recover_strain( - loccsx_, loccsy_, rcs=False) - - # get strains at node idx - epto_an_te_ = _epto_an[node_idx] - epto_an_te.append(epto_an_te_) - - # return strains of cross section - unknowns['%s:cs_strains' % self.name][ - :nnodes_c[nsec_c], 0, nsec_c] = _epto_an - unknowns['%s:cs_strains' % self.name][ - :nnodes_c[nsec_c], 1, nsec_c] = _epto_an_ax - unknowns['%s:cs_strains' % self.name][ - :nnodes_c[nsec_c], 2, nsec_c] = _epto_an_rotx - unknowns['%s:cs_strains' % self.name][ - :nnodes_c[nsec_c], 3, nsec_c] = _epto_an_roty - - # choose applied bending moment at mid section - mean_z = comp_z[0] + 0.5 * (comp_z[-1] - comp_z[0]) - Fz_mean = np.interp(mean_z, comp_z, Fz) - #Fz_applied = np.mean(Fz[4:6]) - - m_x = np.array(m_x) - m_y = np.array(m_y) - - # determine load intro positions with constant force - a_y_applied = - m_x / Fz_mean - a_x_applied = - m_y / Fz_mean - - # rcs = root coordinate system - f_x_rcs_applied = a_x_applied + elastic_x_c_rcs - f_y_rcs_applied = a_y_applied + elastic_y_c_rcs - - # fit load intro positions along specimen - # cs_idx = range(self.nsec) - # alternatively use two neighbouring plus mid cs to fit load intro - # positions by linear extrapolation - fit_mid = False - if fit_mid: - mid_idx = self.nsec / 2 - cs_idx = [mid_idx - 1, mid_idx, mid_idx + 1] - else: - cs_idx = range(len(comp_z)) - - f_x_rcs_fit = np.polyfit(comp_z[cs_idx], f_x_rcs_applied[cs_idx], 1) - f_x_rcs_fit_fn = np.poly1d(f_x_rcs_fit) - - f_y_rcs_fit = np.polyfit(comp_z[cs_idx], f_y_rcs_applied[cs_idx], 1) - f_y_rcs_fit_fn = np.poly1d(f_y_rcs_fit) - - # TODO: remove zhinge hardcoding - zhinge = 0.1 - f_z_tip = comp_z[-1] + zhinge - f_z_root = comp_z[0] - zhinge - - f_x_tip = f_x_rcs_fit_fn(f_z_tip) - f_y_tip = f_y_rcs_fit_fn(f_z_tip) - - # determine required load intro at root end - f_x_root = f_x_rcs_fit_fn(f_z_root) - f_y_root = f_y_rcs_fit_fn(f_z_root) - - # applied load (double the full-scale test load) - # TODO: remove factor 2 hardcoding - Fz_applied = 2 * Fz_mean - - # angles - #unknowns['%s:cs_angles' % self.name][0, :] = beta - - import matplotlib.pylab as plt - plt.figure() - plt.plot(comp_z, beta) - plt.plot(comp_z, nu_c) - plt.plot(comp_z, twist_c) - plt.plot(comp_z, beta + twist_c) - - plt.figure() - plt.plot(comp_z, blade_an_elastic_x_comp_z_rcs) - plt.plot(comp_z, elastic_x_c_rcs) - plt.plot(comp_z, f_x_rcs) - plt.plot(comp_z, f_x_rcs_fit_fn(comp_z), 'k-o') - plt.plot(comp_z, f_x_rcs_applied) - - plt.figure() - plt.plot(comp_z, blade_an_elastic_y_comp_z_rcs) - plt.plot(comp_z, elastic_y_c_rcs) - plt.plot(comp_z, f_y_rcs) - plt.plot(comp_z, f_y_rcs_fit_fn(comp_z), 'k-o') - plt.plot(comp_z, f_y_rcs_applied) - - plt.figure() - plt.plot(blade_an_elastic_x_comp_z_rcs, - blade_an_elastic_y_comp_z_rcs, 'bo') - plt.plot( - blade_an_elastic_x_comp_z_rcs[0], blade_an_elastic_y_comp_z_rcs[0], 'bs') - plt.plot(elastic_x_c_rcs, elastic_y_c_rcs, 'go') - plt.plot(elastic_x_c_rcs[0], elastic_y_c_rcs[0], 'gs') - plt.plot(f_x_rcs, f_y_rcs, 'ro') - plt.plot(f_x_rcs[0], f_y_rcs[0], 'rs') - plt.plot(ref_te_x_comp_z_rcs, ref_te_y_comp_z_rcs, 'mo') - plt.plot(ref_te_x_comp_z_rcs[0], ref_te_y_comp_z_rcs[0], 'ms') - plt.plot(blade_an_elastic_x_comp_z[ - 0], blade_an_elastic_y_comp_z[0], 'b+') - plt.plot(elastic_x_c, elastic_y_c, 'g+') - plt.plot(elastic_x_c[0], elastic_y_c[0], 'gs') - plt.plot(f_x, f_y, 'r+') - plt.plot(f_x[0], f_y[0], 'rs') - plt.plot(ref_te_x_comp_z_, ref_te_y_comp_z_, 'm+') - plt.plot(ref_te_x_comp_z_[0], ref_te_y_comp_z_[0], 'ms') - plt.axis('equal') - - plt.figure() - plt.plot(comp_z, ref_blade_epto_an_te_comp_z) - plt.plot(comp_z, epto_an_te) - - plt.figure() - plt.plot(ref_te_x_comp_z_rcs[-1], ref_te_y_comp_z_rcs[-1], 'ro') - plt.plot(ref_te_x_comp_z_, ref_te_y_comp_z_, 'bo') - plt.plot(locx_, locy_, 'r') - plt.plot(loccsx_, loccsy_, 'b') - - plt.figure() - for nsec_c in range(self.nsec): - plt.plot(params['loc_cs_nodes'][:nnodes_c[nsec_c], 0, nsec_c], - params['loc_cs_nodes'][:nnodes_c[nsec_c], 1, nsec_c]) - plt.axis('equal') - - plt.figure() - plt.plot(comp_z, m_x) - plt.plot(comp_z, m_y) - plt.plot(comp_z, Fz) - # plt.show() - - unknowns['%s:comp_load_case' % self.name][0, 8] = Fz_applied - unknowns['%s:comp_load_case' % self.name][0, 0] = f_x_tip - unknowns['%s:comp_load_case' % self.name][0, 1] = f_y_tip - unknowns['%s:comp_load_case' % self.name][0, 2] = f_z_tip - unknowns['%s:comp_load_case' % self.name][1, 0] = f_x_root - unknowns['%s:comp_load_case' % self.name][1, 1] = f_y_root - unknowns['%s:comp_load_case' % self.name][1, 2] = f_z_root - - -class FINSTRIPCrossSectionsBase(Group): - - def __init__(self): - super(FINSTRIPCrossSectionsBase, self).__init__() - #self.nsec = sdim[1] - #self.ncase = ncase - - self.par = self.add('par', ParallelGroup(), promotes=['*']) - - def add_full_scale_analysis_cases(self, config, sdim, ncase, nload): - for caseid in range(ncase): - self.par.add('case_%02d' % caseid, FINSTRIPBladeCrossSectionsCase( - 'case_%02d' % caseid, config, caseid, sdim, ncase, nload), promotes=['*']) - - def add_component_analysis_cases(self, config, sdim, ncase, nload, nsec_b): - for caseid in range(ncase): - self.par.add('case_%02d' % caseid, FINSTRIPComponentCrossSectionsCase( - 'case_%02d' % caseid, config, caseid, sdim, ncase, nload, nsec_b), promotes=['*']) - - -class FINSTRIPBladeCrossSections(FINSTRIPCrossSectionsBase): +class FINSTRIPBeamStructure(Group): + """ + Group for computing blade buckling resistance using FINSTRIP. + + The geometric and structural inputs used are defined + in detail in FUSED-Wind. + + parameters + ---------- + blade_surface_norm_st: array + blade surface with structural discretization, no twist and prebend. + Size: ((ni_chord, nsec, 3)) + matprops: array + material stiffness properties. Size (10, nmat). + sec<xx>DPs: array + 2D array of DPs. Size: ((nsec, nDP)) + sec<xx>coords: array + blade surface. Size: ((ni_chord, nsec, 3)) + sec<xx>r<yy><lname>T: array + region layer thicknesses, e.g. r01triaxT. Size (nsec) + sec<xx>r<yy><lname>A: array + region layer angles, e.g. r01triaxA. Size (nsec) + sec<xx>w<yy><lname>T: array + web layer thicknesses, e.g. r01triaxT. Size (nsec) + sec<xx>w<yy><lname>A: array + web layer angles, e.g. r01triaxA. Size (nsec) + + returns + ------- + blade_f_eig: array + array of buckling resistances. Size ((nsec,nmode,2)). """ - Group for analysing cross sections of different load cases in parallel. + + def __init__(self, group, config, st3d, sdim, ncase): + """ + initializes parameters and adds a csprops component + for each section + + parameters + ---------- + config: dict + dictionary of inputs for the cs_code class + st3d: dict + dictionary of blade structure properties + surface: array + blade surface with structural discretization. + Size: ((ni_chord, nsec, 3)) + """ + super(FINSTRIPBeamStructure, self).__init__() + + # check that the config is ok + if not 'FINSTRIPWrapper' in config.keys(): + raise RuntimeError('You need to supply a config dict', + 'for FINSTRIPWrapper') + + self.st3d = st3d + nsec = st3d['s'].shape[0] + try: + nmode = config['FINSTRIPWrapper']['nmode'] + except: + nmode = 10 + + # create a unique ID for this group so that FD's are not overwritten + self.add('hash_c', ExecComp('finstrip_hash=%f' % + float(self.__hash__())), promotes=['*']) + + # add comp to slice the 2D arrays DPs and surface + promotions = [] + self.add('slice', Slice(st3d, sdim, config), promotes=['*']) + + self._varnames = [] + for ireg, reg in enumerate(st3d['regions']): + for i, lname in enumerate(reg['layers']): + varname = 'r%02d%s' % (ireg, lname) + self._varnames.append(varname) + for ireg, reg in enumerate(st3d['webs']): + for i, lname in enumerate(reg['layers']): + varname = 'w%02d%s' % (ireg, lname) + self._varnames.append(varname) + + # # now add a component for each section + par = self.add('par', ParallelGroup(), promotes=['*']) + + for i in range(nsec): + secname = 'sec%03d' % i + par.add(secname, FINSTRIPCSStructure(secname, self.__hash__(), config, st3d, + st3d['s'][i], sdim[0], ncase), promotes=['*']) + + promotions = ['blade_f_eig'] + self.add('postpro', PostprocessCS( + nsec, nmode, ncase), promotes=promotions) + for i in range(nsec): + secname = 'sec%03d' % i + self.connect('%s:f_eig' % secname, 'postpro.f_eig%03d' % i) + + +class FINSTRIPBeamStructureLoader(Group): """ + Component for loading eigen values stored in a database by a + previous run. + + This component can be replaced by FINSTRIPBeamStructure group in case the FINSTRIP + run shall be skipped, i.e. when the structure or load case does not change during + iterations. - def __init__(self, config, sdim, ncase, nload): - super(FINSTRIPBladeCrossSections, self).__init__() + Note: If parameters are promoted on the level higher than the group + FINSTRIPBeamStructure belongs to, they need to be listed in the promoted list. + """ - self.add_full_scale_analysis_cases(config, sdim, ncase, nload) + def __init__(self, group, config, st3d, sdim, pickled_unkn, grp_name=''): + """ + initializes parameters + + parameters + ---------- + config: dict + dictionary of inputs for the cs_code class + st3d: dict + dictionary of blade structure properties + pickled_unkn: dict + unknowns dict read in via SqliteDict() + grp_name: str + name of the upper group + promoted: list + list of promoted variables in group above upper group + + Note: + load pickled unkowns from data base + >>> import sqlitedict + >>> db = sqlitedict.SqliteDict('some.db', 'iterations') + >>> data = db['rank0:Driver|1'] + >>> pickled_unkn = data['Unknowns'] + + """ + super(FINSTRIPBeamStructureLoader, self).__init__() + + # check that the config is ok + if not 'FINSTRIPWrapper' in config.keys(): + raise RuntimeError('You need to supply a config dict', + 'for FINSTRIPWrapper') + + self.st3d = st3d + nsec = st3d['s'].shape[0] + try: + nmode = config['FINSTRIPWrapper']['nmode'] + except: + nmode = 10 + + # create a unique ID for this group so that FD's are not overwritten + self.add('hash_c', ExecComp('finstrip_hash=%f' % + float(self.__hash__())), promotes=['*']) + + # add comp to slice the 2D arrays DPs and surface + self.add('slice', SliceLoader( + st3d, sdim, pickled_unkn, grp_name), promotes=['*']) + + self._varnames = [] + for ireg, reg in enumerate(st3d['regions']): + for i, lname in enumerate(reg['layers']): + varname = 'r%02d%s' % (ireg, lname) + self._varnames.append(varname) + for ireg, reg in enumerate(st3d['webs']): + for i, lname in enumerate(reg['layers']): + varname = 'w%02d%s' % (ireg, lname) + self._varnames.append(varname) + + # # now add a component for each section + par = self.add('par', ParallelGroup(), promotes=['*']) + + for i in range(nsec): + secname = 'sec%03d' % i + par.add(secname, FINSTRIPCSStructureLoader(secname, self.__hash__(), config, st3d, + st3d['s'][i], sdim[0], pickled_unkn, grp_name), promotes=['*']) + + promotions = ['blade_f_eig'] + self.add('postpro', PostprocessCS(nsec, nmode), promotes=promotions) + for i in range(nsec): + secname = 'sec%03d' % i + self.connect('%s:f_eig' % secname, 'postpro.f_eig%03d' % i) diff --git a/finstrip_wrapper/omdao/test/test_finstrip_blade.py b/finstrip_wrapper/omdao/test/test_finstrip_blade.py index 6cd0155..f01b045 100644 --- a/finstrip_wrapper/omdao/test/test_finstrip_blade.py +++ b/finstrip_wrapper/omdao/test/test_finstrip_blade.py @@ -23,6 +23,8 @@ from becas_wrapper.becas_bladestructure import BECASBeamStructureKM,\ from becas_wrapper.becas_stressrecovery import BECASStressRecovery from distutils.spawn import find_executable +from finstrip_wrapper.omdao.finstrip_blade import FINSTRIPBeamStructure +from robe.lib.load import convert_point_forces_to_moment_distro _matlab_installed = find_executable('matlab') @@ -43,12 +45,12 @@ else: DRYRUN = False -def configure_BECASBeamStructure(nsec, exec_mode, path_data, dry_run=False, FPM=False, with_sr=False, param2=False): +def configure_BECASBeamStructure(nsec, exec_mode, path_data, dry_run=False, FPM=False, with_sr=False, param2=False, with_becas=False): p = Problem(impl=impl, root=Group()) p.root.add('blade_length_c', IndepVarComp( - 'blade_length', 86.366), promotes=['*']) + 'blade_length', 86.366, units='m'), promotes=['*']) pf = read_blade_planform( os.path.join(path_data, 'DTU_10MW_RWT_blade_axis_prebend.dat')) @@ -116,30 +118,79 @@ def configure_BECASBeamStructure(nsec, exec_mode, path_data, dry_run=False, FPM= cfg['debug_mode'] = False config['BECASWrapper'] = cfg - p.root.add('stiffness', BECASBeamStructure(p.root, config, st3dn, - (200, nsec_st, 3)), promotes=['*']) - if with_sr: - p.root.add('stress_recovery', BECASStressRecovery( - config, s_st, 2), promotes=['*']) + if with_becas: + p.root.add('stiffness', BECASBeamStructure(p.root, config, st3dn, + (200, nsec_st, 3)), promotes=['*']) + # if with_sr: + # p.root.add('stress_recovery', BECASStressRecovery( + # config, s_st, 2), promotes=['*']) + + cfg = {} + cfg['nmode'] = 20 + cfg['core_mats'] = ['balsa'] + cfg['elsize_factor'] = 0.015 + cfg['lambda_min'] = 0.01 # minimum half wavelength in m + cfg['lambda_max'] = 15.126 # maximum half wavelength in m + cfg['use_becas_stiffness'] = with_becas + config['FINSTRIPWrapper'] = cfg + + ncase = 2 + p.root.add('stability', FINSTRIPBeamStructure(p.root, config, st3dn, + (200, nsec_st, 3), ncase), promotes=['*']) p.setup() - p['hub_radius'] = 2.8 for k, v in pf.iteritems(): if k in p.root.pf_splines.params.keys(): p.root.pf_splines.params[k] = v - if with_sr: - # set some arbitrary values in the load vectors used to compute strains - for i, x in enumerate(s_st): - try: - p['load_cases_sec%03d' % i] = np.ones((2, 6)) - except: - pass - + # assuming we have an external shear force loading along the blade, which + # need to be transformed to internal section load case + nload = 9 + cfg = {} + cfg['load_cases'] = np.zeros((ncase, nload, 15)) + hr = 2.8 + cfg['load_cases'][0, :, :] = np.array( + [[0.26658496, -0.064059925, 20.073 - hr, 0.0, 0.0, 0.0, + 0.0, 230000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.49336469, -0.04793707, 30.437 - hr, 0.0, 0.0, + 0.0, 0.0, 270000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.47505332, -0.053173575, 33.028 - hr, 0.0, + 0.0, 0.0, 290000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.37001989, -0.01194097, 47.71 - hr, 0.0, 0.0, 0.0, + 180000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.33257294, 0.00242491, 52.892 - hr, 0.0, 0.0, 0.0, + 0.0, 250000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.26347361, 0.01553866, 62.393 - hr, 0.0, 0.0, 0.0, + 130000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.23826286, 0.01686549, 65.847 - hr, 0.0, 0.0, 0.0, + 0.0, 220000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.16251535, 0.017204025, 76.211 - hr, 0.0, 0.0, 0.0, + 18000, 190000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.09939912, 0.014232575, 84.848 - hr, 0.0, 0.0, 0.0, + 25000, 165000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]) + csmoments = np.zeros((len(s_st), 3)) + moment_positions = s_st * p['blade_length'] + for cs, _ in enumerate(csmoments): + csmoments[cs, :] = convert_point_forces_to_moment_distro( + moment_position=moment_positions[cs], + force_positions=cfg['load_cases'][0, :, 2], + forces=cfg['load_cases'][0, :, 6:9]) + for i, _ in enumerate(s_st): + p['load_cases_sec%03d' % i] = np.zeros((ncase, 6)) + # Fx,Fy,Fz,Mx,My,Mz + p['load_cases_sec%03d' % i][0, 3] = csmoments[i, 0] + p['load_cases_sec%03d' % i][0, 4] = csmoments[i, 1] + + # make second dummy load case + p['load_cases_sec%03d' % i][1, 3] = -2 * csmoments[i, 0] + p['load_cases_sec%03d' % i][1, 4] = -2 * csmoments[i, 1] + + if with_becas: + p['hub_radius'] = hr return p -class BECASBeamStructureTestCase(unittest.TestCase): +class FINSTRIPBeamStructureTestCase(unittest.TestCase): def setUp(self): pass @@ -147,6 +198,7 @@ class BECASBeamStructureTestCase(unittest.TestCase): def tearDown(self): pass + #@unittest.skip("skipping") @unittest.skipIf(not _matlab_installed, "Matlab not available on this system") def test_standard_matlab(self): @@ -155,8 +207,50 @@ class BECASBeamStructureTestCase(unittest.TestCase): dry_run=DRYRUN, FPM=False, with_sr=True, - param2=False) + param2=False, + with_becas=True) + p.run() + + ncase = 2 + nsec = len(p['s_st']) + f_eig_min = np.zeros((ncase, nsec)) + for case in range(ncase): + for sec in range(nsec): + f_eig_min[case, sec] = np.min( + p['blade_f_eig'][case, sec, :, 1]) + + f_eig_min_exp = np.array([[0.727, 0.833, 1.385, 1.], + [4.734, 3.066, 1.694, 1.]]) + for case in range(ncase): + for i, f in enumerate(f_eig_min[case, :]): + self.assertAlmostEqual( + f, f_eig_min_exp[case, i], places=3) + + #@unittest.skip("skipping") + def test_finstrip(self): + + p = configure_BECASBeamStructure(4, 'matlab', 'data', + dry_run=DRYRUN, + FPM=False, + with_sr=True, + param2=False, + with_becas=False) + p.run() + ncase = 2 + nsec = len(p['s_st']) + f_eig_min = np.zeros((ncase, nsec)) + for case in range(ncase): + for sec in range(nsec): + f_eig_min[case, sec] = np.min( + p['blade_f_eig'][case, sec, :, 1]) + + f_eig_min_exp = np.array([[0.996, 1.877, 2.09, 1.], + [6.484, 5.243, 2.011, 1.]]) + for case in range(ncase): + for i, f in enumerate(f_eig_min[case, :]): + self.assertAlmostEqual( + f, f_eig_min_exp[case, i], places=3) if __name__ == "__main__": -- GitLab