def testFrustum_shell(self): # In limit of thickness approaching radius, should recover regular formulas self.assertEqual(f.frustumShellVol(rb, rb, rb, h, False), f.frustumVol(rb, rb, h, False)) self.assertEqual(f.frustumShellVol(2 * rt, 2 * rt, rt, h, True), f.frustumVol(rt, rt, h, False)) self.assertEqual(f.frustumShellCG(rb, rb, rb, h, False), f.frustumCG(rb, rb, h, False)) self.assertEqual(f.frustumShellCG(2 * rt, 2 * rt, rt, h, True), f.frustumCG(rt, rt, h, False)) self.assertEqual(f.frustumShellIzz(rb, rb, rb, h, False), f.frustumIzz(rb, rb, h, False)) self.assertEqual(f.frustumShellIzz(2 * rt, 2 * rt, rt, h, True), f.frustumIzz(rt, rt, h, False)) self.assertAlmostEqual(f.frustumShellIxx(rb, rb, rb - eps, h, False), f.frustumIxx(rb, rb, h, False)) self.assertAlmostEqual( f.frustumShellIxx(2 * rt, 2 * rt, rt - eps, h, True), f.frustumIxx(rt, rt, h, False))
def compute(self, inputs, outputs): # Unpack variables for thickness and average radius at each can interface twall = inputs["t_full"] Rb = 0.5 * inputs["d_full"][:-1] Rt = 0.5 * inputs["d_full"][1:] zz = inputs["z_full"] H = np.diff(zz) rho = inputs["rho"] coeff = inputs["outfitting_factor"] coeff = coeff + np.where(coeff < 1.0, 1.0, 0.0) # Total mass of cylinder V_shell = frustum.frustumShellVol(Rb, Rt, twall, H) mass = outputs["mass"] = coeff * rho * V_shell # Center of mass of each can/section cm_section = zz[:-1] + frustum.frustumShellCG(Rb, Rt, twall, H) outputs["section_center_of_mass"] = cm_section # Center of mass of cylinder V_shell += eps outputs["center_of_mass"] = np.dot(V_shell, cm_section) / V_shell.sum() # Moments of inertia Izz_section = coeff * rho * frustum.frustumShellIzz(Rb, Rt, twall, H) Ixx_section = Iyy_section = coeff * rho * frustum.frustumShellIxx(Rb, Rt, twall, H) # Sum up each cylinder section using parallel axis theorem I_base = np.zeros((3, 3)) for k in range(Izz_section.size): R = np.array([0.0, 0.0, cm_section[k] - zz[0]]) Icg = util.assembleI([Ixx_section[k], Iyy_section[k], Izz_section[k], 0.0, 0.0, 0.0]) I_base += Icg + mass[k] * (np.dot(R, R) * np.eye(3) - np.outer(R, R)) outputs["I_base"] = util.unassembleI(I_base) # Compute costs based on "Optimum Design of Steel Structures" by Farkas and Jarmai # All dimensions for correlations based on mm, not meters. R_ave = 0.5 * (Rb + Rt) taper = np.minimum(Rb / Rt, Rt / Rb) nsec = twall.size mshell = rho * V_shell mshell_tot = np.sum(rho * V_shell) k_m = inputs["material_cost_rate"] # 1.1 # USD / kg carbon steel plate k_f = inputs["labor_cost_rate"] # 1.0 # USD / min labor k_p = inputs["painting_cost_rate"] # USD / m^2 painting k_e = 0.064 # Industrial electricity rate $/kWh https://www.eia.gov/electricity/monthly/epm_table_grapher.php?t=epmt_5_6_a e_f = 15.9 # Electricity usage kWh/kg for steel e_fo = 26.9 # Electricity usage kWh/kg for stainless steel # Cost Step 1) Cutting flat plates for taper using plasma cutter cutLengths = 2.0 * np.sqrt((Rt - Rb) ** 2.0 + H ** 2.0) # Factor of 2 for both sides # Cost Step 2) Rolling plates # Cost Step 3) Welding rolled plates into shells (set difficulty factor based on tapering with logistic function) theta_F = 4.0 - 3.0 / (1 + np.exp(-5.0 * (taper - 0.75))) # Cost Step 4) Circumferential welds to join cans together theta_A = 2.0 # Labor-based expenses K_f = k_f * ( manufacture.steel_cutting_plasma_time(cutLengths, twall) + manufacture.steel_rolling_time(theta_F, R_ave, twall) + manufacture.steel_butt_welding_time(theta_A, nsec, mshell_tot, cutLengths, twall) + manufacture.steel_butt_welding_time(theta_A, nsec, mshell_tot, 2 * np.pi * Rb[1:], twall[1:]) ) # Cost step 5) Painting- outside and inside theta_p = 2 K_p = k_p * theta_p * 2 * (2 * np.pi * R_ave * H).sum() # Cost step 6) Outfitting with electricity usage K_o = np.sum(1.5 * k_m * (coeff - 1.0) * mshell) # Material cost with waste fraction, but without outfitting, K_m = 1.21 * np.sum(k_m * mshell) # Electricity usage K_e = np.sum(k_e * (e_f * mshell + e_fo * (coeff - 1.0) * mshell)) # Assemble all costs for now tempSum = K_m + K_e + K_o + K_p + K_f # Capital cost share from BLS MFP by NAICS K_c = 0.118 * tempSum / (1.0 - 0.118) outputs["cost"] = tempSum + K_c
def compute(self, inputs, outputs): # Unpack variables for thickness and average radius at each can interface twall = inputs['t_full'] Rb = 0.5 * inputs['d_full'][:-1] Rt = 0.5 * inputs['d_full'][1:] zz = inputs['z_full'] H = np.diff(zz) rho = inputs['material_density'] coeff = inputs['outfitting_factor'] if coeff < 1.0: coeff += 1.0 # Total mass of cylinder V_shell = frustum.frustumShellVol(Rb, Rt, twall, H) outputs['mass'] = coeff * rho * V_shell # Center of mass of each can/section cm_section = zz[:-1] + frustum.frustumShellCG(Rb, Rt, twall, H) outputs['section_center_of_mass'] = cm_section # Center of mass of cylinder V_shell += eps outputs['center_of_mass'] = np.dot(V_shell, cm_section) / V_shell.sum() # Moments of inertia Izz_section = frustum.frustumShellIzz(Rb, Rt, twall, H) Ixx_section = Iyy_section = frustum.frustumShellIxx(Rb, Rt, twall, H) # Sum up each cylinder section using parallel axis theorem I_base = np.zeros((3, 3)) for k in range(Izz_section.size): R = np.array([0.0, 0.0, cm_section[k]]) Icg = assembleI([ Ixx_section[k], Iyy_section[k], Izz_section[k], 0.0, 0.0, 0.0 ]) I_base += Icg + V_shell[k] * (np.dot(R, R) * np.eye(3) - np.outer(R, R)) # All of the mass and volume terms need to be multiplied by density I_base *= coeff * rho outputs['I_base'] = unassembleI(I_base) # Compute costs based on "Optimum Design of Steel Structures" by Farkas and Jarmai # All dimensions for correlations based on mm, not meters. R_ave = 0.5 * (Rb + Rt) taper = np.minimum(Rb / Rt, Rt / Rb) nsec = twall.size mplate = rho * V_shell.sum() k_m = inputs['material_cost_rate'] #1.1 # USD / kg carbon steel plate k_f = inputs['labor_cost_rate'] #1.0 # USD / min labor k_p = inputs['painting_cost_rate'] #USD / m^2 painting # Cost Step 1) Cutting flat plates for taper using plasma cutter cutLengths = 2.0 * np.sqrt( (Rt - Rb)**2.0 + H**2.0) # Factor of 2 for both sides # Cost Step 2) Rolling plates # Cost Step 3) Welding rolled plates into shells (set difficulty factor based on tapering with logistic function) theta_F = 4.0 - 3.0 / (1 + np.exp(-5.0 * (taper - 0.75))) # Cost Step 4) Circumferential welds to join cans together theta_A = 2.0 # Labor-based expenses K_f = k_f * (manufacture.steel_cutting_plasma_time(cutLengths, twall) + manufacture.steel_rolling_time(theta_F, R_ave, twall) + manufacture.steel_butt_welding_time( theta_A, nsec, mplate, cutLengths, twall) + manufacture.steel_butt_welding_time( theta_A, nsec, mplate, 2 * np.pi * Rb[1:], twall[1:])) # Cost step 5) Painting- outside and inside theta_p = 2 K_p = k_p * theta_p * 2 * (2 * np.pi * R_ave * H).sum() # Cost step 6) Outfitting K_o = 1.5 * k_m * (coeff - 1.0) * mplate # Material cost, without outfitting K_m = k_m * mplate # Assemble all costs outputs['cost'] = K_m + K_o + K_p + K_f