def cp2bhp(Cp, rpm, density, dia, power_units='hp', density_units='lb/ft**3', dia_units='in'): """ Returns the bhp, given propeller power coefficient (Cp), revolutions per minute (rpm), and propeller diameter. The power units may be specified as "hp", "ft-lb/mn", "ft-lb/s", "W" (watts) or "kW" (kilowatts), but default to "hp" if not specified. The density units may be specified as "lb/ft**3", "slug/ft**3" or "kg/m**3", but default to "lb/ft**3" if not specified. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ # bhp = U.power_conv(bhp, from_units = power_units, to_units = 'W') density = U.density_conv(density, from_units=density_units, to_units='kg/m**3') dia = U.length_conv(dia, from_units=dia_units, to_units='m') bhp = Cp * (density * ((rpm / 60.)**3) * dia**5) bhp = U.power_conv(bhp, from_units='W', to_units=power_units) return bhp
def tip_mach(tas, rpm, temperature, dia, speed_units='kt', temp_units='C', dia_units='in'): """ Returns the mach number of the propeller blade tip, given the true airspeed (tas), revolutions per minute (rpm), temperature and propeller diameter. The speed units may be specified as "kt", "mph", "km/h" or "ft/s", but default to "kt" if not specified. The temperature units may be specified as "C", "F", "K" or "R", but default to deg C if not specified. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ dia = U.length_conv(dia, from_units=dia_units, to_units='m') tas = U.speed_conv(tas, from_units=speed_units, to_units='m/s') speed_of_sound = SA.temp2speed_of_sound(temperature, temp_units=temp_units, speed_units='m/s') rotation_speed = dia * rpm * np.pi / 60 tip_speed = np.sqrt(tas**2 + rotation_speed**2) tip_mach = tip_speed / speed_of_sound return tip_mach
def pwr(rpm, MP, altitude, temp='std', alt_units='ft', temp_units='C'): """ Returns horsepower for Lycoming IO-360-A series engines, given: rpm - engine speed in revolutions per minute MP - manifold pressure (" HG) altitude - pressure altitude temp - ambient temperature (optional - std temperature is used if no temperature is input). alt_units - (optional) - units for altitude, ft, m, or km (default is ft) temp_units - (optional) - units for temperature, C, F, K or R (default is deg C) The function replicates Lycoming curve 12700-A, and is valid at mixture for maximum power. Examples: Determine power at 2620 rpm, 28 inches HG manifold pressure, 0 ft, and -10 deg C: >>> pwr(2620, 28, 0, -10) 197.71751932574702 Determine power at 2500 rpm, 25" MP, 5000 ft and 0 deg F: >>> pwr(2500, 25, 5000, 0, temp_units = 'F') 171.87810350172663 Determine power at 2200 rpm, 20" MP, 2000 metres and -5 deg C >>> pwr(2200, 20, 2000, -5, alt_units = 'm') 108.60284092217333 Determine power at 2200 rpm, 20" MP, 2000 metres and standard temperature: >>> pwr(2200, 20, 2000, alt_units = 'm') 107.2124765533882 """ # convert units altitude = unit_conversion.length_conv(altitude, from_units=alt_units, to_units='ft') if temp == 'std': temp = std_atm.alt2temp(altitude, temp_units=temp_units) temp = unit_conversion.temp_conv(temp, from_units=temp_units, to_units='K') # get standard temperature temp_std = std_atm.alt2temp(altitude, temp_units='K') # get power at standard temperature pwr_std = _pwr_std_temp(rpm, MP, altitude) # correct power for non-standard temperature pwr = pwr_std * np.sqrt(temp_std / temp) return pwr
def pp(rpm, MP, altitude, temp='std', alt_units='ft', temp_units='C'): """ Returns percent power for Lycoming IO-360-A series engines, given: rpm - engine speed in revolutions per minute MP - manifold pressure (" HG) altitude - pressure altitude temp - ambient temperature (optional - std temperature is used if no temperature is input). alt_units - (optional) - units for altitude, ft, m, or km (default is ft) temp_units - (optional) - units for temperature, C, F, K or R (default is deg C) The function replicates Lycoming curve 12700-A, and is valid at mixture for maximum power. Note: the output is rounded off to two decimal places. Examples: Determine power at 2620 rpm, 28 inches HG manifold pressure, 0 ft, and -10 deg C: >>> pp(2620, 28, 0, -10) '98.86%' Determine power at 2500 rpm, 25" MP, 5000 ft and 0 deg F: >>> pp(2500, 25, 5000, 0, temp_units = 'F') '85.94%' Determine power at 2200 rpm, 20" MP, 2000 metres and -5 deg C >>> pp(2200, 20, 2000, -5, alt_units = 'm') '54.30%' Determine power at 2200 rpm, 20" MP, 2000 metres and standard temperature: >>> pp(2200, 20, 2000, alt_units = 'm') '53.61%' """ altitude = unit_conversion.length_conv(altitude, from_units=alt_units, to_units='ft') if temp == 'std': temp = std_atm.alt2temp(altitude, temp_units=temp_units) temp = unit_conversion.temp_conv(temp, from_units=temp_units, to_units='C') pp = pwr(rpm, MP, altitude, temp) / 2 # return pp return '%.2f' % (pp) + '%'
def pp2mp(percent_power, rpm, altitude, temp='std', alt_units='ft', temp_units='C'): """ Returns manifold pressure in inches of mercury for a given percent power, rpm, altitude and temperature (temperature input is optional - standard temperature is used if no temperature is input). Note: the output is rounded off to two decimal places. Examples: Determine manifold pressure required for 62.5% power at 2550 rpm at 8000 ft and 10 deg C: >>> pp2mp(62.5, 2550, 8000, 10) '19.45' Determine manifold pressure required for 75% power at 2500 rpm at 7500 ft at 10 deg F: >>> pp2mp(75, 2500, 7500, 10, temp_units = 'F') '22.25' Determine manifold pressure required for 55% power at 2400 rpm at 9,500 ft at standard temperature: >>> pp2mp(55, 2400, 9500) '18.18' """ if percent_power <= 0: raise ValueError('Power input must be positive.') # convert units altitude = unit_conversion.length_conv(altitude, from_units=alt_units, to_units='ft') if temp == 'std': temp = std_atm.alt2temp(altitude, temp_units=temp_units) temp = unit_conversion.temp_conv(temp, from_units=temp_units, to_units='C') pwr_seek = percent_power * 2 mp = pwr2mp(pwr_seek, rpm, altitude, temp) return mp
def advance_ratio(tas, rpm, dia, speed_units='kt', dia_units='in'): """ Returns the propeller advance ratio, J, given the revolutions per minute (rpm), true airspeed (tas), temperature and propeller diameter. The advance ratio is the forward distance that the propeller advances during one revolution, divided by the diameter of the propeller. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ tas = U.speed_conv(tas, from_units=speed_units, to_units='m/s') dia = U.length_conv(dia, from_units=dia_units, to_units='m') advance_ratio = tas * 60. / (rpm * dia) return advance_ratio
def ct2thrust(Ct, Rho, rpm, dia, thrust_units='lb', density_units='lb/ft**3', dia_units='in'): """ Returns the thrust, given thrust coefficient, Ct, density, rpm and prop diameter. """ Rho = U.density_conv(Rho, from_units=density_units, to_units='kg/m**3') dia = U.length_conv(dia, from_units=dia_units, to_units='m') # convert rpm to revolutions / s n = rpm / 60. thrust = Ct * Rho * n**2. * dia**4. return U.force_conv(thrust, from_units='N', to_units=thrust_units)
def pp2rpm(percent_power, mp, altitude, temp='std', alt_units='ft', temp_units='C'): """ Returns rpm for a given percent power, manifold pressure in inches of mercury, altitude and temperature (temperature input is optional - standard temperature is used if no temperature is input). Examples: Determine rpm required for 125 hp at 20 inches HG manifold pressure at 8000 ft and 10 deg C: >>> pp2rpm(62.5, 20, 8000, 10) 2477 Determine rpm required for 75% power at 22 inches HG manifold pressure at 6500 ft and 10 deg F: >>> pp2rpm(75, 22, 6500, 10, temp_units = 'F') 2547 Determine rpm required for 55% power at at 18 inches HG manifold pressure at 9,500 ft at standard temperature: >>> pp2rpm(55, 18, 9500) 2423 """ if percent_power <= 0: raise ValueError('Power input must be positive.') # convert units altitude = unit_conversion.length_conv(altitude, from_units=alt_units, to_units='ft') if temp == 'std': temp = std_atm.alt2temp(altitude, temp_units=temp_units) temp = unit_conversion.temp_conv(temp, from_units=temp_units, to_units='C') pwr_seek = percent_power * 2 rpm = pwr2rpm(pwr_seek, mp, altitude, temp) return rpm
def __init__(self, base_name, base_path=''): """Returns a propeller object prop = Prop('7666-2RV') prop = Prop('MTV*183-59B') prop = Prop('MTV*402') """ self.prop = base_name # create temp lists, to hold the data before it is read into arrays temp_data_storage1 = [] temp_data_storage2 = [] temp_data_storage3 = [] if '7666' in base_name: self.manufacturer = 'Hartzell' self.model = base_name if base_path == '': node_name = node() if node_name[:9] == 'PowerBook': base_path = '/Users/kwh/RV/Engine_prop/Hartzell_prop_maps/' elif node_name == 'Kevins-MacBook-Air.local': base_path = '/Users/kwh/Documents/RV/Engine_prop/Hartzell_prop_maps/' elif node_name == 'Kevins-MBPr.local': base_path = '/Users/kwh/Documents/RV/Engine_prop/Hartzell_prop_maps/' elif node_name == 'MacMini.local': base_path = '/Users/kwh/Documents/Flying/RV/Engine_prop/Hartzell_prop_maps/' elif node_name == 'ncrnbhortonk2': base_path = 'C:\\Documents and Settings\\hortonk\\My Documents\\rv\\Prop\\Hartzell_prop_maps\\' elif node_name == 'eeepc': base_path = '/home/kwh/RV/Hartzell_prop_maps/' elif node_name[:4] == 'sage': base_path = '/home/kwh/python/prop_maps/Hartzell_prop_maps/' elif node_name == 'sage-ubuntu-1404': base_path = '/home/kwh/python/prop_maps/Hartzell_prop_maps/' else: raise ValueError('Unknown computer') # confirm the path to the data files exists if os.path.exists(base_path): pass else: raise ValueError( 'The path specified for the prop map files does not exist') file_glob = base_path + base_name + '*.csv' file_names = glob.glob(file_glob) # confirm that at least one data file was found if len(file_names) < 1: raise ValueError('No prop data files were found.') # need names in sorted order, so the array ends up in order of mach # sorting seems to not be needed on OS X, as glob.glob() returns a sorted list # but, on Linux, the list is not sorted file_names.sort() for file_name in file_names: FILE = open(file_name) raw_data = csv.reader(FILE) # parse mach number for line in raw_data: mach_search = re.search(r'Mach\s0\.\d+', line[0]) if mach_search: mach_group = re.search(r'0\.\d+', line[0]) mach = mach_group.group() break # find start of CT data for line in raw_data: try: header_search = re.search('THRUST COEFFICIENT', line[0]) if header_search: break except IndexError: pass # load block of CT values into temp storage temp_lines1 = [] while True: value_line = raw_data.next() try: value_line_search = re.match(r'[0\.\d+,CP]', value_line[0]) except IndexError: # at the end of the data block - move on break if value_line_search: # strip spurious zeros from right end of data while value_line[-1] == '0': value_line.pop() temp_lines1.append(value_line) # convert values to float for i, item in enumerate(value_line): try: value_line[i] = float(item) except ValueError: value_line[i] = 0 else: # at the end of the data block - move on break # strip of "CP / J" from first line, and replace by mach, temp_lines1[0][0] = float(mach) # find start of blade angle data for line in raw_data: try: header_search = re.search('BLADE ANGLE', line[0]) if header_search: break except IndexError: pass # load block of blade angle values into temp storage temp_lines3 = [] while True: try: value_line = raw_data.next() except StopIteration: break try: value_line_search = re.match(r'[0\.\d+,CP]', value_line[0]) except IndexError: # at the end of the data block - move on break if value_line_search: # strip spurious zeros from right end of data while value_line[-1] == '0': value_line.pop() temp_lines3.append(value_line) # convert values to float for i, item in enumerate(value_line): try: value_line[i] = float(item) except ValueError: value_line[i] = 0 else: # at the end of the data block - move on break # strip of "CP / J" from first line, and replace by mach, temp_lines3[0][0] = float(mach) # find start of efficiency data for line in raw_data: try: header_search = re.search('EFFICIENCY', line[0]) if header_search: break except IndexError: pass # load block of efficiency values into temp storage temp_lines2 = [] while True: try: value_line = raw_data.next() except StopIteration: break try: value_line_search = re.match(r'[0\.\d+,CP]', value_line[0]) except IndexError: # at the end of the data block - move on break if value_line_search: # strip spurious zeros from right end of data while value_line[-1] == '0': value_line.pop() temp_lines2.append(value_line) # convert values to float for i, item in enumerate(value_line): try: value_line[i] = float(item) except ValueError: value_line[i] = 0 else: # at the end of the data block - move on break # strip of "CP / J" from first line, and replace by mach, temp_lines2[0][0] = float(mach) # determine required size for array # this is done once for each data file, but only the last # result is used self.x_size = len(temp_lines1[0]) self.y_size = len(temp_lines1) self.z_size = len(file_names) # push data blocks into temp storage temp_data_storage1.append(temp_lines1) temp_data_storage2.append(temp_lines2) temp_data_storage3.append(temp_lines3) # create array for CT data, and populate it self.prop_CT_map = N.zeros((self.z_size, self.y_size, self.x_size)) # trim length of temp data to be sure it will fit in array for i, layer in enumerate(temp_data_storage1): for i, line in enumerate(layer): while len(line) > self.x_size: if line[-1] == 0: line.pop(-1) else: print('Problem line =', line) raise ValueError( 'Problem - Trying to remove real data from end of line' ) for i, item in enumerate(temp_data_storage1): self.prop_CT_map[i] = temp_data_storage1[i] # determine range of mach, advance ratio and power coefficient in # the CT data self.Ct_mach_min = min(self.prop_CT_map[:, 0, 0]) self.Ct_mach_max = max(self.prop_CT_map[:, 0, 0]) self.Ct_Cp_min = min(self.prop_CT_map[0, 1:, 0]) self.Ct_Cp_max = max(self.prop_CT_map[0, 1:, 0]) self.Ct_J_min = min(self.prop_CT_map[0, 0, :]) self.Ct_J_max = max(self.prop_CT_map[0, 0, :]) # create array for blade angle data, and populate it self.blade_angle_map = N.zeros( (self.z_size, self.y_size, self.x_size)) # trim length of temp data to be sure it will fit in array for i, layer in enumerate(temp_data_storage3): for i, line in enumerate(layer): while len(line) > self.x_size: if line[-1] == 0: line.pop(-1) else: raise ValueError( 'Problem - Trying to remove real data from end of line' ) for i, item in enumerate(temp_data_storage1): self.blade_angle_map[i] = temp_data_storage3[i] # determine range of mach, advance ratio and power coefficient in # the blade angle data self.blade_angle_mach_min = min(self.blade_angle_map[:, 0, 0]) self.blade_angle_mach_max = max(self.blade_angle_map[:, 0, 0]) self.blade_angle_Cp_min = min(self.blade_angle_map[0, 1:, 0]) self.blade_angle_Cp_max = max(self.blade_angle_map[0, 1:, 0]) self.blade_angle_J_min = min(self.blade_angle_map[0, 0, :]) self.blade_angle_J_max = max(self.blade_angle_map[0, 0, :]) # create array for efficiency data, and populate it self.prop_eff_map = N.zeros( (self.z_size, self.y_size, self.x_size)) # trim length of temp data to be sure it will fit in array for i, layer in enumerate(temp_data_storage2): for i, line in enumerate(layer): while len(line) > self.x_size: if line[-1] == 0: line.pop(-1) else: raise ValueError( 'Problem - Trying to remove real data from end of line' ) for i, item in enumerate(temp_data_storage1): self.prop_eff_map[i] = temp_data_storage2[i] # determine range of mach, advance ratio and power coefficient in # the efficiency data self.eff_mach_min = min(self.prop_eff_map[:, 0, 0]) self.eff_mach_max = max(self.prop_eff_map[:, 0, 0]) self.eff_Cp_min = min(self.prop_eff_map[0, 1:, 0]) self.eff_Cp_max = max(self.prop_eff_map[0, 1:, 0]) self.eff_J_min = min(self.prop_eff_map[0, 0, :]) self.eff_J_max = max(self.prop_eff_map[0, 0, :]) # wipe temp storage, to be sure to start with a clean slate for the # next type of data temp_data_storage1 = [] temp_data_storage2 = [] temp_data_storage3 = [] if base_name == '7666-4RV': self.dia = 72 elif base_name == '7666-2RV': self.dia = 74 else: raise ValueError('Invalid prop') elif 'MTV' in base_name: if base_path == '': node_name = node() if node_name[:9] == 'PowerBook': base_path = '/Users/kwh/Documents/RV/MT_Prop/' elif node_name == 'Kevins-MacBook-Air.local': base_path = '/Users/kwh/Documents/RV/Engine_prop/MT_Prop/' elif node_name == 'Kevins-MBPr.local': base_path = '/Users/kwh/Documents/RV/Engine_prop/MT_Prop/' elif node_name == 'MacMini.local': base_path = '/Users/kwh/Documents/Flying/RV/Engine_prop/MT_Prop/' elif node_name == 'ncrnbhortonk2': base_path = 'C:\\Documents and Settings\\hortonk\\My Documents\\rv\\Prop\\MT_Prop\\' elif node_name == 'eeepc': base_path = '/home/kwh/RV/MT_Prop/' elif node_name[:4] == 'sage': base_path = '/home/kwh/python/prop_maps/' elif node_name == 'sage-ubuntu-1204': base_path = '/home/kwh/python//prop_maps/' else: raise ValueError('Unknown computer') # confirm the path to the data files exists if os.path.exists(base_path): pass else: raise ValueError( 'The path specified for the prop map files does not exist') file_glob = base_path + base_name + '*.csv' # file_glob = base_path + '*' + base_name + '*.csv' file_names = glob.glob(file_glob) # confirm that at least one data file was found if len(file_names) < 1: raise ValueError('No prop data files were found.') for file_name in file_names: temp_lines = [] FILE = open(file_name) raw_data = csv.reader(FILE) header_lines = 2 line1 = raw_data.next() model_match = re.search(r'(\S+)\s+(\S+)\s.*', line1[0]) self.manufacturer = model_match.group(1) self.model = model_match.group(2) self.dia = float( re.search(r'[^-]+-[^-]+-[^-]+-(\d+).*', line1[0]).group(1)) self.dia = U.length_conv(self.dia, from_units='cm', to_units='in') for n in range(header_lines - 1): raw_data.next() # replace "J" with mach 0 temp_lines.append(raw_data.next()) temp_lines[0][0] = 0 # load block of efficiency values into temp storage while True: try: temp_lines.append(raw_data.next()) except StopIteration: break for l, line in enumerate(temp_lines): for i, item in enumerate(line): try: temp_lines[l][i] = float(item) except ValueError: temp_lines[l][i] = 0. # determine required size for array self.x_size = len(temp_lines[0]) self.y_size = len(temp_lines) self.z_size = 2 temp_array1 = [] for line in temp_lines: temp_array1.append(line) # MT eff map does not have different data for different tip mach numbers. # To allow using the same routines as for Hartzell props, set the original # data for Mach 0, and make a copy for Mach 1. temp_array = [temp_array1, temp_array1] self.prop_eff_map = N.array(temp_array) self.prop_eff_map[1, 0, 0] = 1 # MT eff data has Cp running left to right, and Js running # vertically, which is the reverse of the Hartzell data. # Must swapaxes to get the same orientation for both props. self.prop_eff_map = self.prop_eff_map.swapaxes(1, 2) # determine range of mach, advance ratio and power coefficient # in the efficiency data self.eff_mach_min = min(self.prop_eff_map[:, 0, 0]) self.eff_mach_max = max(self.prop_eff_map[:, 0, 0]) self.eff_Cp_min = min(self.prop_eff_map[0, :, 0]) self.eff_Cp_max = max(self.prop_eff_map[0, :, 0]) self.eff_J_min = min(self.prop_eff_map[0, 0, 1:]) self.eff_J_max = max(self.prop_eff_map[0, 0, 1:]) else: print('prop type not known')
def test_02(self): Value = U.length_conv(10, from_units='ft', to_units='in') Truth = 120 self.assertLessEqual(RE(Value, Truth), 1e-5)
def test_06(self): Value = U.length_conv(0.86897624, from_units='nm', to_units='sm') Truth = 1 self.assertLessEqual(RE(Value, Truth), 1e-5)
def test_04(self): Value = U.length_conv(10, from_units='km', to_units='m') Truth = 10000 self.assertLessEqual(RE(Value, Truth), 1e-5)
def pwr2rpm(pwr_seek, mp, altitude, temp='std', alt_units='ft', temp_units='C'): """ Returns rpm for a given power, manifold pressure in inches of mercury, altitude and temperature (temperature input is optional - standard temperature is used if no temperature is input). Note: the output is rounded off to the nearest rpm. Examples: Determine rpm required for 125 hp at 20 inches HG manifold pressure at 8000 ft and 10 deg C: >>> pwr2rpm(125, 20, 8000, 10) 2477 Determine rpm required for 75% power at 22 inches HG manifold pressure at 6500 ft and 10 deg F: >>> pwr2rpm(.75 * 200, 22, 6500, 10, temp_units = 'F') 2547 Determine rpm required for 55% power at at 18 inches HG manifold pressure at 9,500 ft at standard temperature: >>> pwr2rpm(.55 * 200, 18, 9500) 2423 """ if pwr_seek <= 0: raise ValueError('Power input must be positive.') low = 1000 # initial lower guess high = 3500 # initial upper guess # convert units altitude = unit_conversion.length_conv(altitude, from_units=alt_units, to_units='ft') if temp == 'std': temp = std_atm.alt2temp(altitude, temp_units=temp_units) temp = unit_conversion.temp_conv(temp, from_units=temp_units, to_units='C') # confirm initial low and high are OK: pwr_low = pwr(low, mp, altitude, temp) # print("pwr_low=", pwr_low) if pwr_low > pwr_seek: raise ValueError('Initial low guess too high.') pwr_high = pwr(high, mp, altitude, temp) # print("pwr_high=", pwr_high) if pwr_high < pwr_seek: # print("pwr_high=", pwr_high) print("Function called was IO.pwr(%f, %f, %f, %f)" % (high, mp, altitude, temp)) raise ValueError('Initial high guess too low.') guess = (low + high) / 2. pwr_guess = pwr(guess, mp, altitude, temp) # keep iterating until power is within 0.1% of desired value while np.fabs(pwr_guess - pwr_seek) / pwr_seek > 1e-4: if pwr_guess > pwr_seek: high = guess else: low = guess guess = (low + high) / 2. pwr_guess = pwr(guess, mp, altitude, temp) return int(round(guess, 0))
def pwr2mp(pwr_seek, rpm, altitude, temp='std', alt_units='ft', temp_units='C'): """ Returns manifold pressure in inches of mercury for a given power, rpm, altitude and temperature (temperature input is optional - standard temperature is used if no temperature is input). Note: the output is rounded off to two decimal places. Examples: Determine manifold pressure required for 125 hp at 2550 rpm at 8000 ft and 10 deg C: >>> pwr2mp(125, 2550, 8000, 10) '19.45' Determine manifold pressure required for 75% power at 2500 rpm at 7500 ft at 10 deg F: >>> pwr2mp(.75 * 200, 2500, 7500, 10, temp_units = 'F') '22.25' Determine manifold pressure required for 55% power at 2400 rpm at 9,500 ft at standard temperature: >>> pwr2mp(.55 * 200, 2400, 9500) '18.18' """ if pwr_seek <= 0: raise ValueError('Power input must be positive.') low = 0 # initial lower guess high = 35 # initial upper guess # convert units altitude = unit_conversion.length_conv(altitude, from_units=alt_units, to_units='ft') if temp == 'std': temp = std_atm.alt2temp(altitude, temp_units=temp_units) temp = unit_conversion.temp_conv(temp, from_units=temp_units, to_units='C') # confirm initial low and high are OK: pwr_low = pwr(rpm, low, altitude, temp) if pwr_low > pwr_seek: raise ValueError('Initial low guess too high.') pwr_high = pwr(rpm, high, altitude, temp) if pwr_high < pwr_seek: raise ValueError('Initial high guess too low.') guess = (low + high) / 2. pwr_guess = pwr(rpm, guess, altitude, temp) # keep iterating until power is within 0.1% of desired value while np.fabs(pwr_guess - pwr_seek) / pwr_seek > 1e-3: if pwr_guess > pwr_seek: high = guess else: low = guess guess = (low + high) / 2. pwr_guess = pwr(rpm, guess, altitude, temp) # result = int(guess) + round(guess % 1, 2)) # return guess # return result return '%.2f' % (guess)