def setup(self): n_height = self.options["n_height"] nFull = get_nfull(n_height) self.add_input("cylinder_mass", val=np.zeros(nFull - 1), units="kg") self.add_input("cylinder_cost", val=0.0, units="USD") self.add_input("cylinder_center_of_mass", val=0.0, units="m") self.add_input("cylinder_section_center_of_mass", val=np.zeros(nFull - 1), units="m") self.add_input("cylinder_I_base", np.zeros(6), units="kg*m**2") self.add_input("transition_piece_height", 0.0, units="m") self.add_input("transition_piece_mass", 0.0, units="kg") self.add_input("transition_piece_cost", 0.0, units="USD") self.add_input("gravity_foundation_mass", 0.0, units="kg") self.add_input("z_full", val=np.zeros(nFull), units="m") self.add_input("d_full", val=np.zeros(nFull), units="m") self.add_output("structural_cost", val=0.0, units="USD") self.add_output("structural_mass", val=0.0, units="kg") self.add_output("tower_cost", val=0.0, units="USD") self.add_output("tower_mass", val=0.0, units="kg") self.add_output("tower_center_of_mass", val=0.0, units="m") self.add_output("tower_section_center_of_mass", val=np.zeros(nFull - 1), units="m") self.add_output("tower_I_base", np.zeros(6), units="kg*m**2") self.add_output("monopile_mass", val=0.0, units="kg") self.add_output("monopile_cost", val=0.0, units="USD") self.add_output("transition_piece_I", np.zeros(6), units="kg*m**2") self.add_output("gravity_foundation_I", np.zeros(6), units="kg*m**2")
def setup(self): n_height = self.options["n_height"] nFull = get_nfull(n_height) self.add_input("z_full", np.zeros(nFull), units="m") # extra mass self.add_input("mass", 0.0, units="kg") self.add_input("mI", np.zeros(6), units="kg*m**2") self.add_input("mrho", np.zeros(3), units="m") self.add_input("transition_piece_mass", 0.0, units="kg") self.add_input("transition_piece_I", np.zeros(6), units="kg*m**2") self.add_input("gravity_foundation_I", np.zeros(6), units="kg*m**2") self.add_input("gravity_foundation_mass", 0.0, units="kg") self.add_input("transition_piece_height", 0.0, units="m") self.add_input("suctionpile_depth", 0.0, units="m") # point loads self.add_input("rna_F", np.zeros(3), units="N") self.add_input("rna_M", np.zeros(3), units="N*m") # Monopile handling self.add_input("z_soil", np.zeros(NPTS_SOIL), units="N/m") self.add_input("k_soil", np.zeros((NPTS_SOIL, 6)), units="N/m") # spring reaction data. nK = 4 if self.options[ "monopile"] and not self.options["gravity_foundation"] else 1 self.add_output("kidx", np.zeros(nK, dtype=np.int_)) self.add_output("kx", np.zeros(nK), units="N/m") self.add_output("ky", np.zeros(nK), units="N/m") self.add_output("kz", np.zeros(nK), units="N/m") self.add_output("ktx", np.zeros(nK), units="N/m") self.add_output("kty", np.zeros(nK), units="N/m") self.add_output("ktz", np.zeros(nK), units="N/m") # extra mass nMass = 3 self.add_output("midx", np.zeros(nMass, dtype=np.int_)) self.add_output("m", np.zeros(nMass), units="kg") self.add_output("mIxx", np.zeros(nMass), units="kg*m**2") self.add_output("mIyy", np.zeros(nMass), units="kg*m**2") self.add_output("mIzz", np.zeros(nMass), units="kg*m**2") self.add_output("mIxy", np.zeros(nMass), units="kg*m**2") self.add_output("mIxz", np.zeros(nMass), units="kg*m**2") self.add_output("mIyz", np.zeros(nMass), units="kg*m**2") self.add_output("mrhox", np.zeros(nMass), units="m") self.add_output("mrhoy", np.zeros(nMass), units="m") self.add_output("mrhoz", np.zeros(nMass), units="m") # point loads (if addGravityLoadForExtraMass=True be sure not to double count by adding those force here also) nPL = 1 self.add_output("plidx", np.zeros(nPL, dtype=np.int_)) self.add_output("Fx", np.zeros(nPL), units="N") self.add_output("Fy", np.zeros(nPL), units="N") self.add_output("Fz", np.zeros(nPL), units="N") self.add_output("Mxx", np.zeros(nPL), units="N*m") self.add_output("Myy", np.zeros(nPL), units="N*m") self.add_output("Mzz", np.zeros(nPL), units="N*m")
def setup(self): n_height = self.options["modeling_options"]["n_height"] nFull = get_nfull(n_height) # effective geometry -- used for handbook methods to estimate hoop stress, buckling, fatigue self.add_input("z_full", np.zeros(nFull), units="m") self.add_input("d_full", np.zeros(nFull), units="m") self.add_input("t_full", np.zeros(nFull - 1), units="m") self.add_input("suctionpile_depth", 0.0, units="m") self.add_input("Az", val=np.zeros(nFull - 1), units="m**2") self.add_input("Asx", val=np.zeros(nFull - 1), units="m**2") self.add_input("Asy", val=np.zeros(nFull - 1), units="m**2") self.add_input("Jz", val=np.zeros(nFull - 1), units="m**4") self.add_input("Ixx", val=np.zeros(nFull - 1), units="m**4") self.add_input("Iyy", val=np.zeros(nFull - 1), units="m**4") # Material properties self.add_input("E_full", np.zeros(nFull - 1), units="N/m**2") self.add_input("G_full", np.zeros(nFull - 1), units="Pa") self.add_input("rho_full", np.zeros(nFull - 1), units="kg/m**3") self.add_input("sigma_y_full", np.zeros(nFull - 1), units="N/m**2") # Processed Frame3DD/OpenFAST outputs self.add_input("tower_Fz", val=np.zeros(nFull - 1), units="N") self.add_input("tower_Vx", val=np.zeros(nFull - 1), units="N") self.add_input("tower_Vy", val=np.zeros(nFull - 1), units="N") self.add_input("tower_Mxx", val=np.zeros(nFull - 1), units="N*m") self.add_input("tower_Myy", val=np.zeros(nFull - 1), units="N*m") self.add_input("tower_Mzz", val=np.zeros(nFull - 1), units="N*m") self.add_input("qdyn", val=np.zeros(nFull), units="N/m**2") # fatigue parameters self.add_input("life", 20.0) # self.add_input('m_SN', 4, desc='slope of S/N curve') # self.add_input('DC', 80.0, desc='standard value of stress') # self.add_input('z_DEL', np.zeros(nDEL), units='m', desc='absolute z coordinates of corresponding fatigue parameters') # self.add_input('M_DEL', np.zeros(nDEL), desc='fatigue parameters at corresponding z coordinates') # Load analysis self.add_output("axial_stress", val=np.zeros(nFull - 1), units="N/m**2") self.add_output("shear_stress", val=np.zeros(nFull - 1), units="N/m**2") self.add_output("hoop_stress", val=np.zeros(nFull - 1), units="N/m**2") self.add_output("hoop_stress_euro", val=np.zeros(nFull - 1), units="N/m**2") self.add_output("constr_stress", np.zeros(nFull - 1)) self.add_output("constr_shell_buckling", np.zeros(nFull - 1)) self.add_output("constr_global_buckling", np.zeros(nFull - 1)) # self.add_output('constr_damage', np.zeros(nFull-1), desc='Fatigue damage at each tower section') self.add_output("turbine_F", val=np.zeros(3), units="N", desc="Total force on tower+rna") self.add_output("turbine_M", val=np.zeros(3), units="N*m", desc="Total x-moment on tower+rna measured at base")
def compute(self, inputs, outputs): n_height = self.options["n_height"] nFull = get_nfull(n_height) z = inputs["z_full"] # Prepare RNA, transition piece, and gravity foundation (if any applicable) for "extra node mass" itrans = util.find_nearest(z, inputs["transition_piece_height"]) mtrans = inputs["transition_piece_mass"] Itrans = inputs["transition_piece_I"] mgrav = inputs["gravity_foundation_mass"] Igrav = inputs["gravity_foundation_I"] # Note, need len()-1 because Frame3DD crashes if mass add at end outputs["midx"] = np.array([nFull - 1, itrans, 0], dtype=np.int_) outputs["m"] = np.array([inputs["mass"], mtrans, mgrav]).flatten() outputs["mIxx"] = np.array([inputs["mI"][0], Itrans[0], Igrav[0]]).flatten() outputs["mIyy"] = np.array([inputs["mI"][1], Itrans[1], Igrav[1]]).flatten() outputs["mIzz"] = np.array([inputs["mI"][2], Itrans[2], Igrav[2]]).flatten() outputs["mIxy"] = np.array([inputs["mI"][3], Itrans[3], Igrav[3]]).flatten() outputs["mIxz"] = np.array([inputs["mI"][4], Itrans[4], Igrav[4]]).flatten() outputs["mIyz"] = np.array([inputs["mI"][5], Itrans[5], Igrav[5]]).flatten() outputs["mrhox"] = np.array([inputs["mrho"][0], 0.0, 0.0]).flatten() outputs["mrhoy"] = np.array([inputs["mrho"][1], 0.0, 0.0]).flatten() outputs["mrhoz"] = np.array([inputs["mrho"][2], 0.0, 0.0]).flatten() # Prepare point forces at RNA node outputs["plidx"] = np.array([nFull - 1], dtype=np.int_) # -1 b/c same reason as above outputs["Fx"] = np.array([inputs["rna_F"][0]]).flatten() outputs["Fy"] = np.array([inputs["rna_F"][1]]).flatten() outputs["Fz"] = np.array([inputs["rna_F"][2]]).flatten() outputs["Mxx"] = np.array([inputs["rna_M"][0]]).flatten() outputs["Myy"] = np.array([inputs["rna_M"][1]]).flatten() outputs["Mzz"] = np.array([inputs["rna_M"][2]]).flatten() # Prepare for reactions: rigid at tower base if self.options["monopile"] and not self.options["gravity_foundation"]: if self.options["soil_springs"]: z_soil = inputs["z_soil"] k_soil = inputs["k_soil"] z_pile = z[z <= (z[0] + 1e-1 + np.abs(z_soil[0]))] if z_pile.size != 4: print(z) print(z_soil) print(z_pile) raise ValueError("Please use only one section for submerged pile for now") k_mono = np.zeros((z_pile.size, 6)) for k in range(6): k_mono[:, k] = np.interp(z_pile + np.abs(z_soil[0]), z_soil, k_soil[:, k]) outputs["kidx"] = np.arange(len(z_pile), dtype=np.int_) outputs["kx"] = np.array([k_mono[:, 0]]) outputs["ky"] = np.array([k_mono[:, 2]]) outputs["kz"] = np.zeros(k_mono.shape[0]) outputs["kz"][0] = np.array([k_mono[0, 4]]) outputs["ktx"] = np.array([k_mono[:, 1]]) outputs["kty"] = np.array([k_mono[:, 3]]) outputs["ktz"] = np.array([k_mono[:, 5]]) else: z_pile = z[z <= (z[0] + 1e-1 + inputs["suctionpile_depth"])] npile = z_pile.size if npile != 4: print(z) print(z_pile) print(inputs["suctionpile_depth"]) raise ValueError("Please use only one section for submerged pile for now") outputs["kidx"] = np.arange(npile, dtype=np.int_) outputs["kx"] = outputs["ky"] = outputs["kz"] = RIGID * np.ones(npile) outputs["ktx"] = outputs["kty"] = outputs["ktz"] = RIGID * np.ones(npile) else: outputs["kidx"] = np.array([0], dtype=np.int_) outputs["kx"] = outputs["ky"] = outputs["kz"] = np.array([RIGID]) outputs["ktx"] = outputs["kty"] = outputs["ktz"] = np.array([RIGID])
def setup(self): mod_opt = self.options["modeling_options"]["WISDEM"]["TowerSE"] n_height_tow = mod_opt["n_height_tower"] n_layers_tow = mod_opt["n_layers_tower"] n_height_mon = mod_opt["n_height_monopile"] n_layers_mon = mod_opt["n_layers_monopile"] if "n_height" in mod_opt: n_height = mod_opt["n_height"] else: n_height = mod_opt[ "n_height"] = n_height_tow if n_height_mon == 0 else n_height_tow + n_height_mon - 1 nFull = get_nfull(n_height) self.set_input_defaults("gravity_foundation_mass", 0.0, units="kg") self.set_input_defaults("transition_piece_mass", 0.0, units="kg") self.set_input_defaults("tower_outer_diameter", np.ones(n_height), units="m") self.set_input_defaults("tower_wall_thickness", np.ones(n_height), units="m") self.set_input_defaults("outfitting_factor", np.zeros(n_height - 1)) self.set_input_defaults("water_depth", 0.0, units="m") self.set_input_defaults("hub_height", 0.0, units="m") self.set_input_defaults("rho", np.zeros(n_height - 1), units="kg/m**3") self.set_input_defaults("unit_cost", np.zeros(n_height - 1), units="USD/kg") self.set_input_defaults("labor_cost_rate", 0.0, units="USD/min") self.set_input_defaults("painting_cost_rate", 0.0, units="USD/m**2") # Inputs here are the outputs from the Tower component in load_IEA_yaml # TODO: Use reference axis and curvature, s, instead of assuming everything is vertical on z self.add_subsystem( "yaml", tp.DiscretizationYAML( n_height_tower=n_height_tow, n_height_monopile=n_height_mon, n_layers_tower=n_layers_tow, n_layers_monopile=n_layers_mon, n_mat=self.options["modeling_options"]["materials"]["n_mat"], ), promotes=["*"], ) # Promote all but foundation_height so that we can override self.add_subsystem( "geometry", tp.CylinderDiscretization(nPoints=n_height), promotes=[ "z_param", "z_full", "d_full", "t_full", ("section_height", "tower_section_height"), ("diameter", "tower_outer_diameter"), ("wall_thickness", "tower_wall_thickness"), ], ) self.add_subsystem("props", CylindricalShellProperties(nFull=nFull), promotes=["Az", "Asx", "Asy", "Ixx", "Iyy", "Jz"]) self.add_subsystem("tgeometry", tp.TowerDiscretization(n_height=n_height), promotes=["*"]) self.add_subsystem( "cm", tp.CylinderMass(nPoints=nFull), promotes=[ "z_full", "d_full", "t_full", "labor_cost_rate", "painting_cost_rate" ], ) self.add_subsystem( "tm", tp.TowerMass(n_height=n_height), promotes=[ "z_full", "d_full", "tower_mass", "tower_center_of_mass", "tower_section_center_of_mass", "tower_I_base", "tower_cost", "gravity_foundation_mass", "gravity_foundation_I", "transition_piece_mass", "transition_piece_cost", "transition_piece_height", "transition_piece_I", "monopile_mass", "monopile_cost", "structural_mass", "structural_cost", ], ) self.add_subsystem( "gc", util_con.GeometricConstraints(nPoints=n_height), promotes=[ "constr_taper", "constr_d_to_t", "slope", ("d", "tower_outer_diameter"), ("t", "tower_wall_thickness"), ], ) self.add_subsystem( "turb", tp.TurbineMass(), promotes=[ "turbine_mass", "monopile_mass", "tower_mass", "tower_center_of_mass", "tower_I_base", "rna_mass", "rna_cg", "rna_I", "hub_height", ], ) # Connections for geometry and mass self.connect("z_start", "geometry.foundation_height") self.connect("d_full", "props.d") self.connect("t_full", "props.t") self.connect("rho_full", "cm.rho") self.connect("outfitting_full", "cm.outfitting_factor") self.connect("unit_cost_full", "cm.material_cost_rate") self.connect("cm.mass", "tm.cylinder_mass") self.connect("cm.cost", "tm.cylinder_cost") self.connect("cm.center_of_mass", "tm.cylinder_center_of_mass") self.connect("cm.section_center_of_mass", "tm.cylinder_section_center_of_mass") self.connect("cm.I_base", "tm.cylinder_I_base")
def setup(self): mod_opt = self.options["modeling_options"]["WISDEM"]["TowerSE"] monopile = self.options["modeling_options"]["flags"]["monopile"] nLC = mod_opt["nLC"] # not yet supported wind = mod_opt["wind"] # not yet supported frame3dd_opt = mod_opt["frame3dd"] if "n_height" in mod_opt: n_height = mod_opt["n_height"] else: n_height_tow = mod_opt["n_height_tower"] n_height_mon = mod_opt["n_height_monopile"] n_height = mod_opt[ "n_height"] = n_height_tow if n_height_mon == 0 else n_height_tow + n_height_mon - 1 nFull = get_nfull(n_height) self.set_input_defaults("E", np.zeros(n_height - 1), units="N/m**2") self.set_input_defaults("G", np.zeros(n_height - 1), units="N/m**2") if monopile and mod_opt["soil_springs"]: self.set_input_defaults("G_soil", 0.0, units="N/m**2") self.set_input_defaults("nu_soil", 0.0) self.set_input_defaults("sigma_y", np.zeros(n_height - 1), units="N/m**2") self.set_input_defaults("rna_mass", 0.0, units="kg") self.set_input_defaults("rna_cg", np.zeros(3), units="m") self.set_input_defaults("rna_I", np.zeros(6), units="kg*m**2") self.set_input_defaults("life", 0.0) # Load baseline discretization self.add_subsystem( "geom", TowerLeanSE(modeling_options=self.options["modeling_options"]), promotes=["*"]) if monopile and mod_opt["soil_springs"]: self.add_subsystem( "soil", TowerSoil(npts=NPTS_SOIL), promotes=[("G", "G_soil"), ("nu", "nu_soil"), ("depth", "suctionpile_depth")], ) self.connect("d_full", "soil.d0", src_indices=[0]) # Add in all Components that drive load cases # Note multiple load cases have to be handled by replicating components and not groups/assemblies. # Replicating Groups replicates the IndepVarComps which doesn't play nicely in OpenMDAO prom = [("zref", "wind_reference_height"), "shearExp", "z0", "cd_usr", "yaw", "beta_wind", "rho_air", "mu_air"] if monopile: prom += [ "beta_wave", "rho_water", "mu_water", "cm", "Uc", "Hsig_wave", "Tsig_wave", "water_depth", ] for iLC in range(nLC): lc = "" if nLC == 1 else str(iLC + 1) self.add_subsystem("wind" + lc, CylinderEnvironment(nPoints=nFull, water_flag=monopile, wind=wind), promotes=prom) self.add_subsystem( "pre" + lc, ts.TowerPreFrame( n_height=n_height, monopile=monopile, soil_springs=mod_opt["soil_springs"], gravity_foundation=mod_opt["gravity_foundation"], ), promotes=[ "transition_piece_mass", "transition_piece_height", "transition_piece_I", "gravity_foundation_mass", "gravity_foundation_I", "z_full", "suctionpile_depth", ("mass", "rna_mass"), ("mrho", "rna_cg"), ("mI", "rna_I"), ], ) self.add_subsystem( "tower" + lc, ts.CylinderFrame3DD( nFull=nFull, nK=4 if monopile and not mod_opt["gravity_foundation"] else 1, nMass=3, nPL=1, frame3dd_opt=frame3dd_opt, ), promotes=["Az", "Asx", "Asy", "Ixx", "Iyy", "Jz"], ) self.add_subsystem( "post" + lc, ts.TowerPostFrame(modeling_options=mod_opt), promotes=[ "life", "z_full", "d_full", "t_full", "rho_full", "E_full", "G_full", "sigma_y_full", "suctionpile_depth", "Az", "Asx", "Asy", "Ixx", "Iyy", "Jz", ], ) self.connect("z_full", ["wind" + lc + ".z", "tower" + lc + ".z"]) self.connect("d_full", ["wind" + lc + ".d", "tower" + lc + ".d"]) self.connect("t_full", "tower" + lc + ".t") self.connect("rho_full", "tower" + lc + ".rho") self.connect("E_full", "tower" + lc + ".E") self.connect("G_full", "tower" + lc + ".G") self.connect("pre" + lc + ".kidx", "tower" + lc + ".kidx") self.connect("pre" + lc + ".kx", "tower" + lc + ".kx") self.connect("pre" + lc + ".ky", "tower" + lc + ".ky") self.connect("pre" + lc + ".kz", "tower" + lc + ".kz") self.connect("pre" + lc + ".ktx", "tower" + lc + ".ktx") self.connect("pre" + lc + ".kty", "tower" + lc + ".kty") self.connect("pre" + lc + ".ktz", "tower" + lc + ".ktz") self.connect("pre" + lc + ".midx", "tower" + lc + ".midx") self.connect("pre" + lc + ".m", "tower" + lc + ".m") self.connect("pre" + lc + ".mIxx", "tower" + lc + ".mIxx") self.connect("pre" + lc + ".mIyy", "tower" + lc + ".mIyy") self.connect("pre" + lc + ".mIzz", "tower" + lc + ".mIzz") self.connect("pre" + lc + ".mIxy", "tower" + lc + ".mIxy") self.connect("pre" + lc + ".mIxz", "tower" + lc + ".mIxz") self.connect("pre" + lc + ".mIyz", "tower" + lc + ".mIyz") self.connect("pre" + lc + ".mrhox", "tower" + lc + ".mrhox") self.connect("pre" + lc + ".mrhoy", "tower" + lc + ".mrhoy") self.connect("pre" + lc + ".mrhoz", "tower" + lc + ".mrhoz") self.connect("pre" + lc + ".plidx", "tower" + lc + ".plidx") self.connect("pre" + lc + ".Fx", "tower" + lc + ".Fx") self.connect("pre" + lc + ".Fy", "tower" + lc + ".Fy") self.connect("pre" + lc + ".Fz", "tower" + lc + ".Fz") self.connect("pre" + lc + ".Mxx", "tower" + lc + ".Mxx") self.connect("pre" + lc + ".Myy", "tower" + lc + ".Myy") self.connect("pre" + lc + ".Mzz", "tower" + lc + ".Mzz") if monopile and mod_opt["soil_springs"]: self.connect("soil.z_k", "pre" + lc + ".z_soil") self.connect("soil.k", "pre" + lc + ".k_soil") self.connect("wind" + lc + ".Px", "tower" + lc + ".Px") self.connect("wind" + lc + ".Py", "tower" + lc + ".Py") self.connect("wind" + lc + ".Pz", "tower" + lc + ".Pz") self.connect("wind" + lc + ".qdyn", "post" + lc + ".qdyn") self.connect("tower" + lc + ".tower_Fz", "post" + lc + ".tower_Fz") self.connect("tower" + lc + ".tower_Vx", "post" + lc + ".tower_Vx") self.connect("tower" + lc + ".tower_Vy", "post" + lc + ".tower_Vy") self.connect("tower" + lc + ".tower_Mxx", "post" + lc + ".tower_Mxx") self.connect("tower" + lc + ".tower_Myy", "post" + lc + ".tower_Myy") self.connect("tower" + lc + ".tower_Mzz", "post" + lc + ".tower_Mzz")
def setup(self): n_height = self.options["n_height"] nFull = get_nfull(n_height) self.add_input("hub_height", val=0.0, units="m") self.add_input("z_param", np.zeros(n_height), units="m") self.add_input("z_full", val=np.zeros(nFull), units="m") self.add_input("rho", val=np.zeros(n_height - 1), units="kg/m**3") self.add_input("unit_cost", val=np.zeros(n_height - 1), units="USD/kg") self.add_input("outfitting_factor", val=np.zeros(n_height - 1)) self.add_input("E", val=np.zeros(n_height - 1), units="Pa") self.add_input("G", val=np.zeros(n_height - 1), units="Pa") self.add_input("sigma_y", val=np.zeros(n_height - 1), units="Pa") self.add_input("Az", np.zeros(nFull - 1), units="m**2") self.add_input("Jz", np.zeros(nFull - 1), units="m**4") self.add_input("Ixx", np.zeros(nFull - 1), units="m**4") self.add_input("Iyy", np.zeros(nFull - 1), units="m**4") self.add_output("height_constraint", val=0.0, units="m") self.add_output("rho_full", val=np.zeros(nFull - 1), units="kg/m**3") self.add_output("unit_cost_full", val=np.zeros(nFull - 1), units="USD/kg") self.add_output("outfitting_full", val=np.zeros(nFull - 1)) self.add_output("E_full", val=np.zeros(nFull - 1), units="Pa") self.add_output("G_full", val=np.zeros(nFull - 1), units="Pa") self.add_output("sigma_y_full", val=np.zeros(nFull - 1), units="Pa") # Tower Distributed Beam Properties (properties needed for ElastoDyn (OpenFAST) inputs or BModes inputs for verification purposes) self.add_output("sec_loc", np.zeros(n_height - 1), desc="normalized sectional location") self.add_output("str_tw", np.zeros(n_height - 1), units="deg", desc="structural twist of section") self.add_output("tw_iner", np.zeros(n_height - 1), units="deg", desc="inertial twist of section") self.add_output("mass_den", np.zeros(n_height - 1), units="kg/m", desc="sectional mass per unit length") self.add_output( "foreaft_iner", np.zeros(n_height - 1), units="kg*m", desc="sectional fore-aft intertia per unit length about the Y_G inertia axis", ) self.add_output( "sideside_iner", np.zeros(n_height - 1), units="kg*m", desc="sectional side-side intertia per unit length about the Y_G inertia axis", ) self.add_output( "foreaft_stff", np.zeros(n_height - 1), units="N*m**2", desc="sectional fore-aft bending stiffness per unit length about the Y_E elastic axis", ) self.add_output( "sideside_stff", np.zeros(n_height - 1), units="N*m**2", desc="sectional side-side bending stiffness per unit length about the Y_E elastic axis", ) self.add_output("tor_stff", np.zeros(n_height - 1), units="N*m**2", desc="sectional torsional stiffness") self.add_output("axial_stff", np.zeros(n_height - 1), units="N", desc="sectional axial stiffness") self.add_output("cg_offst", np.zeros(n_height - 1), units="m", desc="offset from the sectional center of mass") self.add_output("sc_offst", np.zeros(n_height - 1), units="m", desc="offset from the sectional shear center") self.add_output("tc_offst", np.zeros(n_height - 1), units="m", desc="offset from the sectional tension center") self.declare_partials("height_constraint", ["hub_height", "z_param"], method="fd") self.declare_partials("outfitting_full", ["outfitting_factor"], method="fd") self.declare_partials("rho_full", ["rho"], method="fd") self.declare_partials("unit_cost_full", ["unit_cost"], method="fd")