def load_real_maneuver(input_cache_fish_label, maneuver_index, use_total_cost=False, disable_wait_time=True): cache = pickle.load( open( os.path.join(MANEUVER_INPUT_CACHE_PATH, input_cache_fish_label + ".pickle"), "rb")) fish = maneuveringfish.ManeuveringFish( fork_length=cache['fish']['fork_length_cm'], mean_water_velocity=cache['fish']['mean_focal_current_speed_cm_per_s'], base_mass=cache['fish']['mass_g'], temperature=cache['fish']['temperature'], SMR=cache['fish']['SMR'], max_thrust=cache['fish']['max_thrust'], NREI=cache['fish']['NREI'], use_total_cost=use_total_cost, disable_wait_time=disable_wait_time) maneuver = cache['maneuvers'][maneuver_index] prey_velocity = maneuver['prey_speed_cm_per_s'] fish.focal_velocity = maneuver[ 'focal_current_speed_cm_per_s'] # todo just make this a maneuver attribute to avoid confusion xd, yd = maneuver['detection_point_2D_cm'] return { 'fish': fish, 'fv': fish.focal_velocity, 'pv': prey_velocity, 'xd': xd, 'yd': yd }
def create_typical_fish(species, **kwargs): # typical fish return maneuveringfish.ManeuveringFish( fork_length=typical[species]['fork_length'], mean_water_velocity=kwargs.get( 'mean_water_velocity', typical[species]['mean_water_velocity']), base_mass=typical[species]['mass'], temperature=typical[species]['temperature'], SMR=typical[species]['SMR'], max_thrust=typical[species]['max_thrust'], NREI=typical[species]['NREI'], use_total_cost=kwargs.get('use_total_cost', False), disable_wait_time=False)
def check_johansen_et_al(display=False, suppress_output=False): # Checks maneuver cost predictions against empirical measurements from Johansen et al # It looks like they had costs per maneuver of 1.28 mg O2 / kg for freestream swimming and 0.78 for refuge swimming, # both at a temperature of 15 C and velocity of 68 cm/s, with rainbow trout of size 33 cm and 423 g. With the width x # depth of the tube being 25 x 26 cm, we might assume the average detected prey's lateral distance was around 15 cm or # so, and I'll assume prey were detected fairly early (say 30 cm upstream) on average. fork_length = 33 mean_water_velocity = 68 xd = -30 yd = 15 max_thrust = 2.4 * fork_length + 40 fish_mass = 423 # this input defaults the model to a rainbow trout length-mass regression fish_SMR = 0.0 # default to sockeye SMR for given temperature fish_NREI = 0.0 # unused in this case temperature = 15 # only for SMR use_total_cost = False disable_wait_time = False fish = maneuveringfish.ManeuveringFish(fork_length, mean_water_velocity, fish_mass, temperature, fish_SMR, max_thrust, fish_NREI, use_total_cost, disable_wait_time) detection_point_3D = (xd, yd, 0.0) maneuver = optimize.optimal_maneuver(fish, detection_point_3D=detection_point_3D) visualize.summarize_solution(maneuver, display=display, title='Cost Table Check', export_path=None, detailed=True) joulesPerMgO2 = 3.36 * 4.184 costMgO2 = maneuver.activity_cost / joulesPerMgO2 costMgO2PerKG = costMgO2 / (fish_mass / 1000) print("Maneuver cost of locomotion alone is ", costMgO2PerKG, "mg O2 / kg") print("Focal swimming cost including SMR in is ", 3600 * (fish.focal_swimming_cost_including_SMR / joulesPerMgO2), " mg O2 / kg / hr" ) # convert from focal_swimming_cost_including_SMR in J/s SMR_per_hour = 3600 * (fish.SMR_J_per_s / joulesPerMgO2) / (fish_mass / 1000) print("SMR per hour is ", SMR_per_hour, " mg O2 / kg / hr" ) # convert from focal_swimming_cost_including_SMR in J/s print("Cost of SMR during maneuver is ", SMR_per_hour * maneuver.duration / 3600) print("Locomotion cost for focal swimming for duration of maneuver is ", (maneuver.duration * fish.focal_swimming_cost_of_locomotion / joulesPerMgO2) / (fish_mass / 1000), " mg O2 / kg") total_cost_during_maneuver = costMgO2PerKG + SMR_per_hour * maneuver.duration / 3600 print( f"MAIN COMPARISON: estimated cost (locomotion + SMR) of maneuver is {total_cost_during_maneuver} mg O2 / kg, compared to Johansen's 1.28 mg O2 / kg" ) return maneuver, fish
def fix_broken_files(fork_length, focal_velocity, prey_velocity, energy_cost_file, pursuit_duration_file): """ I initially use the file_contents variables below to read in the values from the input files, but then I also use the same arrays to build up the corrected output files. """ # Load the files and prepare NaN indices print("Fixing NaNs for fl={0:.1f}, fv={1:.1f}, pv={2:.1f}.".format( fork_length, focal_velocity, prey_velocity)) ec_file_contents = np.genfromtxt( energy_cost_file, delimiter=',')[:, :-1] # odd indexing avoids adding an extra NaN from pd_file_contents = np.genfromtxt( pursuit_duration_file, delimiter=',')[:, :-1] # the rightmost delimiter in each row xs = ec_file_contents[1:, 0] ys = ec_file_contents[0, 1:] ec_data = ec_file_contents[1:, 1:] nan_indices = np.argwhere(np.isnan(ec_data)) # nan_indices = np.argwhere(ec_data) # if I wanted this function to rebuild the whole table # Prepare the fish for calculations max_thrust = 2.4 * fork_length + 40 fish_mass = 0.0 # this input defaults the model to a rainbow trout length-mass regression fish_SMR = 0.0 # unused in this case fish_NREI = 0.0 # also unused in this case temperature = 10 # also unused in this case use_total_cost = False disable_wait_time = False fish = maneuveringfish.ManeuveringFish(fork_length, focal_velocity, fish_mass, temperature, fish_SMR, max_thrust, fish_NREI, use_total_cost, disable_wait_time) for x_ind, y_ind in nan_indices: # sol = optimize_cuckoo.optimal_maneuver_CS(fish, detection_point_3D=(xs[x_ind], ys[y_ind], 0.0), prey_velocity=prey_velocity, n=25, iterations=1500, p_a=0.25, suppress_output=False) # TEMP sol = optimize_cuckoo.optimal_maneuver_CS( fish, detection_point_3D=(xs[x_ind], ys[y_ind], 0.0), prey_velocity=prey_velocity, n=50, iterations=3000, p_a=0.25, suppress_output=False) assert (sol.energy_cost != CONVERGENCE_FAILURE_COST) ec_file_contents[x_ind + 1, y_ind + 1] = sol.energy_cost pd_file_contents[x_ind + 1, y_ind + 1] = sol.pursuit_duration np.savetxt("/Users/Jason/Desktop/ManeuverTemp/ec_results.csv", ec_file_contents, fmt='%.7f,') np.savetxt("/Users/Jason/Desktop/ManeuverTemp/pd_results.csv", pd_file_contents, fmt='%.7f,')
def check_cost_table_value(fork_length, mean_water_velocity, xd, yd, display=False, suppress_output=False): max_thrust = 2.4 * fork_length + 40 fish_mass = 0.0 # this input defaults the model to a rainbow trout length-mass regression fish_SMR = 0.0 # unused in this case fish_NREI = 0.0 # also unused in this case temperature = 10 # also unused in this case use_total_cost = False disable_wait_time = False fish = maneuveringfish.ManeuveringFish(fork_length, mean_water_velocity, fish_mass, temperature, fish_SMR, max_thrust, fish_NREI, use_total_cost, disable_wait_time) detection_point_3D = (xd, yd, 0.0) maneuver = optimize.optimal_maneuver(fish, detection_point_3D=detection_point_3D) # visualize.summarize_solution(maneuver, display=display, title='Cost Table Check', export_path=None, detailed=True) return maneuver
'focal_velocity': 42, 'prey_velocity': 48, 'mass': 920, 'temperature': 6, 'max_thrust': 159, 'NREI': 0.017, 'detection_distance': 35, 'SMR': 40 } } species = 'Dolly Varden' fish = maneuveringfish.ManeuveringFish( fork_length=typical[species]['fork_length'], mean_water_velocity=typical[species]['focal_velocity'], mass=typical[species]['mass'], temperature=typical[species]['temperature'], SMR=typical[species]['SMR'], max_thrust=typical[species]['max_thrust'], NREI=typical[species]['NREI'], use_total_cost=False, disable_wait_time=False) xd, yd = (-typical[species]['detection_distance'] / 1.414, typical[species]['detection_distance'] / 1.414) prey_velocity = typical[species]['prey_velocity'] def objective_function(p): return maneuver.maneuver_from_proportions(fish, prey_velocity, xd, yd, p).fitness def problem_description(verbose=True):
# # opt = optimize.run_convergence_test(typical_fish[species], detection_point_3D=detection_point_3D) # opt, opt_model = optimize.optimal_maneuver(test_fish, n=DEFAULT_OPT_N, max_iterations=DEFAULT_OPT_ITERATIONS, detection_point_3D=detection_point_3D, tracked=True, return_optimization_model=True) # best solution is 0.159198 # visualize.summarize_solution(opt, display = True, title = 'Typical Dolly', export_path = None, detailed=True, add_text_panel=True) test_x = -135.3565092 test_y = 1.2 fork_length = 3.0 velocity = 1.0 fish = maneuveringfish.ManeuveringFish( fork_length=fork_length, mean_water_velocity=velocity, base_mass=0, # defaults to regression from length temperature=10, # irrelevant in this case SMR=0, # irrelevant in this case max_thrust=250, NREI=0, # irrelevant in this case use_total_cost=False, disable_wait_time=False) opt, opt_model = optimize.optimal_maneuver( fish, detection_point_3D=(test_x, test_y, 0.0), max_iterations=(DEFAULT_OPT_ITERATIONS), max_n=(DEFAULT_OPT_N), tracked=True, return_optimization_model=True, suppress_output=False) print( f"Had {opt_model.nfe_nonconvergent} nonconvergent evaluations out of {opt_model.nfe}" )
def calculate_cost_tables(fork_length, velocity, taskid): fish = maneuveringfish.ManeuveringFish(fork_length=fork_length, mean_water_velocity=velocity, base_mass=0, # defaults to regression from length temperature=10, # irrelevant in this case SMR=0, # irrelevant in this case max_thrust=250, NREI=0, # irrelevant in this case use_total_cost=False, disable_wait_time=False) prey_length_mm_for_max_distance = 25 # mm, set the max distance considered to the max at which 25-mm prey could be resolved (asymptotically approaches 3 m for large fish) max_visible_distance = 12 * prey_length_mm_for_max_distance * (1. - np.exp(-0.2 * fork_length)) # max visible distance of prey in cm # the minimum lateral (y) distance we'll consider for maneuver is 0.2 cm, basically just a head-snap maneuver for the smallest drift foragers # We also insert a couple of manual values in there to get consistent coverage at short distances before the scaled values kick in for all fish. (xmin, xmax, ymin, ymax) = (-max_visible_distance, max_visible_distance, 0.2, max_visible_distance) sp = 4 # sp = spacing power used to emphasize points closer to the fish while still covering much more distant points adequately def scale(x): return (x)**sp def scale_inv(x): return abs(abs(x)**(1.0/sp)) xs = np.concatenate([-abs(np.flip(scale(np.linspace(scale_inv(1), scale_inv(-xmin), 24))[1:], axis=0)), [-0.1], scale(np.linspace(scale_inv(1), scale_inv(xmax), 14)[1:])]) ys = np.concatenate([[ymin, 0.7, 1.2, 1.8], scale(np.linspace(scale_inv(1), scale_inv(xmax), 25)[2:])]) if FAST_TEST: xs = xs[::5] ys = ys[::5] # This grid has 999 values per sheet. # for x in xs: print("x = {0:.5f}".format(x)) # print statements that can be used to check grid spacing # for y in ys: print("y = {0:.5f}".format(y)) ac = np.zeros(shape=(len(xs),len(ys))) pd = np.zeros(shape=(len(xs),len(ys))) rd = np.zeros(shape=(len(xs),len(ys))) count = 1 final_count = float(len(xs) * len(ys)) for i in range(len(xs)): for j in range(len(ys)): print("Calculating optimal maneuver for detection point ", xs[i], ", ", ys[j], ", 0") sol = optimize.optimal_maneuver(fish, detection_point_3D=(xs[i], ys[j], 0.0), max_iterations=(100 if FAST_TEST else DEFAULT_OPT_ITERATIONS), max_n=(30 if FAST_TEST else DEFAULT_OPT_N), suppress_output=True) if sol.activity_cost != CONVERGENCE_FAILURE_COST: ac[i,j] = sol.activity_cost pd[i,j] = sol.pursuit_duration rd[i,j] = sol.return_duration else: ac[i,j] = np.nan pd[i,j] = np.nan rd[i, j] = np.nan if IS_MAC: print(f"Solution {count} of {final_count:.0f}: For fl={fork_length:.1f} cm, v={velocity:.1f} cm/s, at x={xs[i]:.2f} and y={ys[j]:.2f}, energy cost is {sol.activity_cost:.5f} J and pursuit duration is {sol.pursuit_duration:.3f} and return duration is {sol.return_duration:.3f} s.") if count % 5 == 0 and not IS_MAC: db_execute("UPDATE maneuver_model_tasks SET progress={0} WHERE taskid={1}".format(count/final_count, taskid)) count += 1 # Now, run quality control check on the ec and pd values, redoing calculation if they're too far off from their neighbors or came out np.nan the first time for table in [ac]: # base the check only on ac imax = table.shape[0] jmax = table.shape[1] for i in range(imax): for j in range(jmax): i_min = 0 if i == 0 else i-1 i_max = imax+1 if i == imax else i+2 j_min = 0 if j == 0 else j-1 j_max = jmax+1 if j == jmax else j+2 neighbors = table[i_min:i_max, j_min:j_max] notnan_neighbors = neighbors[~np.isnan(neighbors)] if len(notnan_neighbors) >= 3 or np.isnan(table[i,j]): # only do the neighbor-based QC check if there are enough "neighbors" (2 + current number) to check against the median neighbor_median = np.median(notnan_neighbors) # median of 4 (corner), 6 (edge), or 9-value (center) block around present cell ratio_to_median = table[i,j] / neighbor_median worst_allowable_ratio = 3.0 # assume optimal solution wasn't found if solution differs from neighbors by factor of 3 if ratio_to_median < 1/worst_allowable_ratio or ratio_to_median > worst_allowable_ratio or np.isnan(table[i,j]): for retry in (2,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4): # If we didn't get reasonable values the first time, try again with more rigorous but time-consuming algorithm parameters if not IS_MAC: db_execute("UPDATE maneuver_model_tasks SET retries=retries+1 WHERE taskid={0}".format(taskid)) sol = optimize.optimal_maneuver(fish, detection_point_3D=(xs[i], ys[j], 0.0), max_iterations=(retry*DEFAULT_OPT_ITERATIONS), max_n=(retry*DEFAULT_OPT_N), suppress_output=True) if sol.activity_cost < ac[i, j]: ac[i,j] = sol.activity_cost pd[i,j] = sol.pursuit_duration rd[i,j] = sol.return_duration ratio_to_median = table[i,j] / neighbor_median if 1/worst_allowable_ratio <= ratio_to_median <= worst_allowable_ratio: break if np.isnan(table[i,j]): print(f"Retries still produced NaN activity cost for x={xs[i]}, y={ys[j]} with fl={fork_length}, velocity={velocity}.") if ratio_to_median < 1/worst_allowable_ratio or ratio_to_median > worst_allowable_ratio: print(f"Retries to match neighbors failed for x={xs[i]}, y={ys[j]} with fl={fork_length}, velocity={velocity}, ratio_to_median={ratio_to_median}.") if not IS_MAC: db_execute(f"UPDATE maneuver_model_tasks SET has_failed_retries=1 WHERE taskid={taskid}") # Count up final number of NaNs if any, using the activity cost table nan_count = np.count_nonzero(np.isnan(ac)) if nan_count > 0 and not IS_MAC: db_execute(f"UPDATE maneuver_model_tasks SET nan_count={nan_count} WHERE taskid={taskid}") # Now add on the mirror image of the first 4 columns to each extrapolation, with negative y values, to facilitate smooth interpolation near y=0 ys = np.concatenate([np.flip(-ys[:4], axis=0), ys]) ac = np.concatenate([np.flip(ac[:,:4], axis=1), ac], axis=1) pd = np.concatenate([np.flip(pd[:,:4], axis=1), pd], axis=1) rd = np.concatenate([np.flip(rd[:,:4], axis=1), rd], axis=1) return ac, pd, rd, xs, ys