Ejemplo n.º 1
0
def optimal_maneuver_from_saved_setup(saved_setup_file, **kwargs):
    """ Retrieves the inputs specifying the maneuver setup (velocity, detection position, etc), not the
        specific attributes (radius, thrusts, etc) of the particular maneuver object saved. Calculates those anew
        instead. Any maneuver input can be altered by passing it as a keyword argument.
         """
    with open(saved_setup_file) as f:
        loaded = json.load(f)

    fish = ManeuveringFish(
        kwargs.get('fork_length', loaded['fish_fork_length']),
        kwargs.get('mean_water_velocity', loaded['mean_water_velocity']),
        kwargs.get('base_mass', loaded['fish_base_mass']),
        kwargs.get('temperature', loaded['fish_temperature']),
        kwargs.get('SMR', loaded['fish_SMR']),
        kwargs.get('max_thrust', loaded['fish_max_thrust']),
        kwargs.get('NREI', loaded['fish_NREI']),
        kwargs.get('use_total_cost', loaded['fish_use_total_cost']),
        kwargs.get('disable_wait_time', loaded['fish_disable_wait_time']))
    return optimal_maneuver(fish,
                            n=kwargs.get('n', DEFAULT_OPT_N),
                            max_iterations=kwargs.get('max_iterations',
                                                      DEFAULT_OPT_ITERATIONS),
                            detection_point_3D=kwargs.get(
                                'detection_point_3D',
                                (loaded['det_x'], loaded['det_y'], 0)),
                            tracked=True,
                            return_optimization_model=True)
Ejemplo n.º 2
0
def typical_maneuver(species, **kwargs):
    fish = kwargs.get('modified_fish', typical_fish[species])
    default_detection_point_3D = (-typical[species]['detection_distance'] /
                                  1.414,
                                  typical[species]['detection_distance'] /
                                  1.414, 0.0)
    detection_point_3D = kwargs.get('modified_detection_point_3D',
                                    default_detection_point_3D)
    return optimize.optimal_maneuver(fish,
                                     detection_point_3D=detection_point_3D,
                                     **kwargs)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
errors_A = []
errors_B = []
for i in range(1, 300):
    # Uniform test throughout the possible reaction distances or an inner subset thereof
    #distfact=1
    #testx = random.uniform(min(xs)/distfact,max(xs)/distfact)
    #testy = random.uniform(min(ys)/distfact,max(ys)/distfact)
    # Test weighted to actual reaction distances (in bodylengths) by choosing randomly from real fish data from all_foraging_attempts in Maneuver Paper Calculations.py
    attempt = random.choice(all_foraging_attempts)
    testx = fork_length * attempt.reaction_vector[0] / attempt.fish.best_length
    testy = fork_length * attempt.lateral_reaction_distance / attempt.fish.best_length
    test_energy_model = optimize.optimal_maneuver(
        fish,
        detection_point_3D=(testx, testy, 0.0),
        popsize=4,
        variant_scale=1.5,
        mixing_ratio=3.0,
        iterations=4500,
        use_starting_iterations=True,
        num_starting_populations=12,
        num_starting_iterations=500).dynamics.activity_cost
    errors_A.append(100 *
                    abs(spl_ec_665.ev(testx, testy) - test_energy_model) /
                    test_energy_model)  # percent error in log spline model
    errors_B.append(100 *
                    abs(spl_ec_416.ev(testx, testy) - test_energy_model) /
                    test_energy_model)  # percent error in lin spline model
print(
    "Mean A (665) error is {0:.2f}, median {1:.2f}, 95th percentile {2:.2f}, max {3:.2f}. Mean B (416) error is {4:.2f}, median {5:.2f}, 95th percentile is {6:.2f}, max is {7:.2f}."
    .format(np.mean(errors_A), np.median(errors_A), percentile(errors_A, 95),
            max(errors_A), np.mean(errors_B), np.median(errors_B),
            percentile(errors_B, 95), max(errors_B)))
Ejemplo n.º 6
0
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}"
)
# failure code 8 is a loop-de-loop

# nonconvergents (thousands): 15, 31, 23, 41, 40, 47, 17, 22
# new nonconvergents: 7, 9, 11, 11, 12, 9 -- much better

# new random maneuvers have mostly 6.2 and 8 with occasional 6.1 convergence problems

# basic problem is the fish generally needs either a very long wait time to put the focal point in front or massively doubling back
# to return to the region below the focal point... and that doubling back generally involves one of the figure-8-ish maneuvers
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