Skip to content
Snippets Groups Projects
cost_function.py 7.84 KiB
Newer Older
#!/usr/bin/env python3

Lionel GUEZ's avatar
Lionel GUEZ committed
"""This script takes the graph of segments without cost and computes
the non-local cost applied to edges.
Lionel GUEZ's avatar
Lionel GUEZ committed

Input:

Lionel GUEZ's avatar
Lionel GUEZ committed
-- the graph of segments without cost;
Lionel GUEZ's avatar
Lionel GUEZ committed

Lionel GUEZ's avatar
Lionel GUEZ committed
-- the SHPC.
Lionel GUEZ's avatar
Lionel GUEZ committed

Lionel GUEZ's avatar
Lionel GUEZ committed
Output: the graph of segments with cost.
Lionel GUEZ's avatar
Lionel GUEZ committed

The inst_eddies property of vertices is not modified by this script.
Lionel GUEZ's avatar
Lionel GUEZ committed

"""

import graph_tool
import time
import math
import report_graph
Lionel GUEZ's avatar
Lionel GUEZ committed
import util_eddies
Lionel GUEZ's avatar
Lionel GUEZ committed
import argparse
Omega = 2 * math.pi / 86164.

def calculate_radii_rossby(properties):
    """Compute average on some instantaneous eddies of Rossby number and
    radius of maximum speed contour. The required properties for each
    eddy are position, radius and speed. "properties" is a list of
Lionel GUEZ's avatar
Lionel GUEZ committed
    dictionaries. Each dictionary in the list contains the three
    properties. If the speed is not defined for any eddy then the
    returned value of avg_Rossby is 0.
    avg_rad = 0 # in m
    avg_Rossby = 0
    n_valid_Rossby = 0
Lionel GUEZ's avatar
Lionel GUEZ committed

    for prop in properties:
        f = 2 * Omega * math.sin(math.radians(prop["pos"][1])) # in s-1
        radius = prop["radius"] * 1000 # in m
Lionel GUEZ's avatar
Lionel GUEZ committed

        if abs(prop["speed"]) < 100:
            avg_Rossby += prop["speed"] / (f * radius)
            n_valid_Rossby += 1
Lionel GUEZ's avatar
Lionel GUEZ committed

Lionel GUEZ's avatar
Lionel GUEZ committed
        avg_rad += radius # in m
Lionel GUEZ's avatar
Lionel GUEZ committed

    avg_rad /= len(properties)
    if n_valid_Rossby != 0: avg_Rossby /= n_valid_Rossby
    return avg_rad, avg_Rossby
def node_to_prop(node_list, e_overestim, SHPC, orientation):
    """node_list is a list of node identification numbers for
    instantaneous eddies. This function returns some properties of the
    eddies, read from shapefiles: position of extremum, radius of
    outermost contour or maximum speed contour, and speed. The three
    properties are in a dictionary, for each eddy.

    """

    properties = []

    for n in node_list:
        date_index, eddy_index = report_graph.node_to_date_eddy(n, e_overestim)
        i_slice = SHPC.get_slice(date_index)
        ishape = SHPC.comp_ishape(date_index, eddy_index, i_slice, orientation)
        shapeRec = SHPC.get_reader(i_slice, orientation, "extremum")\
                       .shapeRecord(ishape)
        prop = {"pos": shapeRec.shape.points[0], "speed": shapeRec.record.speed}
        prop["radius"] \
            = SHPC.get_reader(i_slice, orientation, "max_speed_contour")\
                  .record(ishape).r_eq_area

        if prop["radius"] < 0:
            prop["radius"] = SHPC.get_reader(i_slice, orientation,
                                             "outermost_contour")\
                .record(ishape).r_eq_area

        properties.append(prop)

    return properties

Lionel GUEZ's avatar
Lionel GUEZ committed
t0 = time.perf_counter()
timings = open("timings.txt", "w")
Lionel GUEZ's avatar
Lionel GUEZ committed
parser = argparse.ArgumentParser()
parser.add_argument("SHPC_dir")
parser.add_argument("orientation", choices = ["Anticyclones", "Cyclones"])
parser.add_argument("input_segments", help = "input graph of segments without "
Lionel GUEZ's avatar
Lionel GUEZ committed
                    "cost, suffix .gt (graph-tool) or .graphml")
parser.add_argument("output_segments", help = "output graph of segments with "
Lionel GUEZ's avatar
Lionel GUEZ committed
                    "cost, suffix .gt (graph-tool) or .graphml")
Lionel GUEZ's avatar
Lionel GUEZ committed
args = parser.parse_args()
Lionel GUEZ's avatar
Lionel GUEZ committed
# Set some values needed for the cost function:
Lionel GUEZ's avatar
Lionel GUEZ committed
delta_cent_mean = 3.8481 # in km
delta_cent_std = 8.0388
Lionel GUEZ's avatar
Lionel GUEZ committed
delta_ro_mean = -0.0025965
delta_ro_std = 5.2168
Lionel GUEZ's avatar
Lionel GUEZ committed
delta_r_mean = -9.4709 # in m
delta_r_std = 8.6953e3
Lionel GUEZ's avatar
Lionel GUEZ committed
# Load the graph_tool file:
Lionel GUEZ's avatar
Lionel GUEZ committed

Lionel GUEZ's avatar
Lionel GUEZ committed
print('Loading graph...')
g = graph_tool.load_graph(args.input_segments)
Lionel GUEZ's avatar
Lionel GUEZ committed
print('Loading done...')
print("Input graph:")
print("Number of vertices:", g.num_vertices())
print("Number of edges:", g.num_edges())
print("Internal properties:")
g.list_properties()
Lionel GUEZ's avatar
Lionel GUEZ committed
t1 = time.perf_counter()
Lionel GUEZ's avatar
Lionel GUEZ committed
timings.write(f"loading: {t1 - t0:.0f} s\n")
Lionel GUEZ's avatar
Lionel GUEZ committed
t0 = t1
g.vp['pos_first'] = g.new_vp('vector<double>')
g.vp['pos_last'] = g.new_vp('vector<double>')
g.vp['first_av_rad'] = g.new_vp('float')
g.vp['first_av_ros'] = g.new_vp('float')
g.vp['last_av_rad'] = g.new_vp('float')
g.vp['last_av_ros'] = g.new_vp('float')
g.ep['cost_function'] = g.new_ep('float')
SHPC = util_eddies.SHPC_class(args.SHPC_dir, args.orientation)
n_days_avg = 7 # number of days to average
Lionel GUEZ's avatar
Lionel GUEZ committed
print("Iterating on vertices...")

Lionel GUEZ's avatar
Lionel GUEZ committed
for n in g.vertices():
    if n.in_degree() != 0:
        # Define properties for beginning of the segment:
        properties = node_to_prop(g.vp.inst_eddies[n][:n_days_avg],
                                  g.gp.e_overestim, SHPC, args.orientation)
        g.vp.first_av_rad[n], g.vp.first_av_ros[n] \
            = calculate_radii_rossby(properties)
        g.vp.pos_first[n] = properties[0]["pos"] # in degrees
    if n.out_degree() != 0:
        # Define properties for end of the segment:
        len_seg = len(g.vp.inst_eddies[n])

        if n.in_degree() == 0 or len_seg > n_days_avg:
            # We have to read more from the shapefiles and redefine
            # properties.

            if n.in_degree() == 0 or len_seg > 2 * n_days_avg:
                # We cannot use part of properties from the beginning
                # of the segment.
                properties = node_to_prop(g.vp.inst_eddies[n][- n_days_avg:],
                                          g.gp.e_overestim, SHPC,
                                          args.orientation)
            else:
                # assertion: n.in_degree() != 0 and n_days_avg <
                # len_seg < 2 * n_days_avg

                # We can use part of the properties from the beginning
                # of the segment.
                properties = properties[len_seg - n_days_avg:] \
                    + node_to_prop(g.vp.inst_eddies[n][n_days_avg:],
                                   g.gp.e_overestim, SHPC, args.orientation)
            g.vp.last_av_rad[n], g.vp.last_av_ros[n] \
                = calculate_radii_rossby(properties)
Lionel GUEZ's avatar
Lionel GUEZ committed
            # The number of eddies in the segment is lower than or
            # equal to the number of days over which to average. The
            # values for the end of the segment will be the same as
            # for the begining, except for the position.
            g.vp.last_av_rad[n] = g.vp.first_av_rad[n]
Lionel GUEZ's avatar
Lionel GUEZ committed
            g.vp.last_av_ros[n] = g.vp.first_av_ros[n]

        g.vp.pos_last[n] = properties[- 1]["pos"] # in degrees
Lionel GUEZ's avatar
Lionel GUEZ committed
t1 = time.perf_counter()
Lionel GUEZ's avatar
Lionel GUEZ committed
timings.write(f"iterating on vertices: {t1 - t0:.0f} s\n")
Lionel GUEZ's avatar
Lionel GUEZ committed
t0 = t1
Lionel GUEZ's avatar
Lionel GUEZ committed
print("Iterating on edges...")

for edge in g.edges():
    source_node = edge.source()
    target_node = edge.target()
Lionel GUEZ's avatar
Lionel GUEZ committed

    latitude = math.radians((g.vp.pos_last[source_node][1] +
                             g.vp.pos_first[target_node][1]) / 2)
Lionel GUEZ's avatar
Lionel GUEZ committed
    # (latitude needed for conversion of degrees to kilometers)
Lionel GUEZ's avatar
Lionel GUEZ committed

Lionel GUEZ's avatar
Lionel GUEZ committed
    # Because of the wrapping issue (360° wrapping incorrectly to 0°),
    # we check for that here:
Lionel GUEZ's avatar
Lionel GUEZ committed
    lon_diff = abs(g.vp.pos_last[source_node][0]
Lionel GUEZ's avatar
Lionel GUEZ committed
                   - g.vp.pos_first[target_node][0])
Lionel GUEZ's avatar
Lionel GUEZ committed
    if lon_diff > 300: lon_diff = 360 - lon_diff
    Delta_Cent = math.sqrt((lon_diff * 111.32 * math.cos(latitude))**2
Lionel GUEZ's avatar
Lionel GUEZ committed
                           + ((g.vp.pos_last[source_node][1]
                               - g.vp.pos_first[target_node][1]) * 110.574)**2)
Lionel GUEZ's avatar
Lionel GUEZ committed

    # Rossbies:
    if (g.vp.first_av_ros[target_node] and g.vp.last_av_ros[source_node]):
Lionel GUEZ's avatar
Lionel GUEZ committed
        Delta_Ro = g.vp.last_av_ros[source_node] \
            - g.vp.first_av_ros[target_node]
Lionel GUEZ's avatar
Lionel GUEZ committed
        # At least one of the rossbies is invalid.
        Delta_Ro = 0
Lionel GUEZ's avatar
Lionel GUEZ committed

    # R_Vmax 1 and 2 already exist, just get the delta
Lionel GUEZ's avatar
Lionel GUEZ committed
    Delta_R_Vmax = g.vp.last_av_rad[source_node] \
        - g.vp.first_av_rad[target_node]
Lionel GUEZ's avatar
Lionel GUEZ committed

Lionel GUEZ's avatar
Lionel GUEZ committed
    # Calculate the cost and assign to the edge:
    g.ep.cost_function[edge] \
        = math.sqrt(((Delta_Cent - delta_cent_mean) / delta_cent_std)**2
                    + ((Delta_Ro - delta_ro_mean) / delta_ro_std)**2
                    + ((Delta_R_Vmax - delta_r_mean) / delta_r_std)**2)
Lionel GUEZ's avatar
Lionel GUEZ committed
t1 = time.perf_counter()
Lionel GUEZ's avatar
Lionel GUEZ committed
timings.write(f"iterating on edges: {t1 - t0:.0f} s\n")
Lionel GUEZ's avatar
Lionel GUEZ committed
t0 = t1
Lionel GUEZ's avatar
Lionel GUEZ committed
print("Saving...")
g.save(args.output_segments)
print('All done')
Lionel GUEZ's avatar
Lionel GUEZ committed
t1 = time.perf_counter()
Lionel GUEZ's avatar
Lionel GUEZ committed
timings.write(f"saving: {t1 - t0:.0f} s\n")
Lionel GUEZ's avatar
Lionel GUEZ committed
timings.close()