Newer
Older
#!/usr/bin/env python3
# A script that takes a segmented graph in the gt format and performs the
# non-local cost function calculation where each edge will have a cost
# function assigned to it.
# Inputs (to be changed within the script):
# Orientation
# node_id_param_file location and name
# gt graph name and location
# shapefiles names and location
# Output: a gt graph with cost functions and an xml graph with cost functions
import graph_tool
import time
import json
import math
from os import path
import shapefile
import datetime
from numpy import loadtxt
######################
# Utility functions: #
######################
def comp_ishape(d_init, ishape_last, days_1950, eddy_index):
k = days_1950 - d_init
if k == 0:
ishape = eddy_index - 1
else:
ishape = ishape_last[k - 1] + eddy_index
return ishape
#######################
# Necessary functions #
#######################
def calculate_radii_and_rossby(start, end, inc, segment, e_overestim, extremum,
radii = 0 #[m]
rossby = 0 #[1/s]
days_modifier = 0
for i in range(start, end, inc):
current_eddy = report_graph.node_to_date_eddy(segment[i], e_overestim)
date = datetime.date(1950, 1, 1) \
+ datetime.timedelta(current_eddy['date_index'])
year = date.year
# calculate the location in the shapefile
d_init_1 = extremum[year].record(0)[1]
location = comp_ishape(d_init_1, ishape_last[year],
# now that we have the location in the shapefiles, we need to
# get the radius and the rossby number
shapeRec = extremum[year].shapeRecord(location)
lat_in_deg = shapeRec.shape.points[0][1]
#[deg]
f = 2*2*math.pi/(24*3600)*math.sin(math.radians(lat_in_deg)) # [1/s]
V_max = shapeRec.record[4] #[m/s]
R_Vmax = max_speed[year].record(location)['r_eq_area'] * 1000 #[m]
if (V_max < 100):
# calculate Ro and Delta_Ro
Ro = V_max / (f * R_Vmax) #[]
else:
Ro = 0
days_modifier += 1
####### RADII #######
radii += R_Vmax # [m]
####### ROSSBY ######
rossby += Ro # []
return {"radii": radii, "rossby": rossby, "days_modifier": days_modifier}
###############################
with open("node_id_param.json") as f: node_id_param = json.load(f)
# assign attributes to the whole graphs
e_overestim = node_id_param["e_overestim"]
################################################
# set some values needed for the cost function #
################################################
delta_cent_mean = 3.8481 # [km]
delta_cent_std = 8.0388
delta_ro_mean = -0.0025965 # []
delta_ro_std = 5.2168
delta_r_mean = -0.0094709 * 1000 #[m]
delta_r_std = 8.6953 * 1000
orientation = input("Enter orientation (anti or cyclo): ")
##########################
# load the edgelist file #
##########################
t0 = time.perf_counter()
print('Loading gt file...')
g = graph_tool.Graph()
t1 = time.perf_counter()
print(f'Loading done: {g}')
g.set_fast_edge_removal()
##################################
# Calculating the Cost Function: #
##################################
#######################
# Load the shapefiles #
#######################
# setup the dicts for the shapefiles and the ishape_last files
extremum = {}
max_speed = {}
ishape_last = {}
shp_tr_dir = input('Directory containing %Y/SHPC_(anti|cyclo): ')
shp_dir = path.join(shp_tr_dir, f'{year}/SHPC_{orientation}')
extremum[year] = shapefile.Reader(path.join(shp_dir, 'extremum'))
max_speed[year] = shapefile.Reader(path.join(shp_dir, 'max_speed_contour'))
ishape_last[year] = loadtxt(path.join(shp_dir, 'ishape_last.txt'),
dtype = int, delimiter = "\n", unpack = False)
# change if there is a change over the number of days to average
num_of_days_to_avg = 7
# we iterate on the years because the segments are non sequential in time
for yr in range(1993, 2019):
print(f'Current year: {yr}')
# iterate on the vertices
for n in g.vertices():
# Get the segment and the number of days
segment = g.vp.segment[n]
# calculate the indexes and dates
first = report_graph.node_to_date_eddy(segment[0], e_overestim)
first_days = first['date_index']
first_date = datetime.date(1950,1,1) + datetime.timedelta(first['date_index'])
first_year = first_date.year
#print(f'Segment size: {len(segment)}, first: {first_year}, last: {last_year}, yr: {yr}')
# chech if we are in the current year
if (first_year == yr):
num_of_days = len(segment)
# start processing
first_pos = []
last_pos = []
last = report_graph.node_to_date_eddy(segment[-1], e_overestim)
last_days = last['date_index']
last_date = datetime.date(1950,1,1) + datetime.timedelta(last['date_index'])
last_year = last_date.year
print(f'Segment size: {len(segment)}, first: {first_year}, last: {last_year}, yr: {yr}')
# calculate the location in the shapefile
d_init_1 = extremum[first_year].record(0)[1]
d_init_2 = extremum[last_year].record(0)[1]
first_loc = comp_ishape(d_init_1, ishape_last[first_year], first['date_index'], first['eddy_index'])
last_loc = comp_ishape(d_init_2, ishape_last[last_year], last['date_index'], last['eddy_index'])
first_pos = extremum[first_year].shape(first_loc).points[0]
last_pos = extremum[last_year].shape(last_loc).points[0]
##### STORE POSITIONS IN THE VPS ######
g.vp.pos_first[n] = first_pos # [deg, deg]
g.vp.pos_last[n] = last_pos # [deg, deg]
# if the segments are longer than the # of days over which to avg
if (num_of_days > num_of_days_to_avg):
first_radii = 0 # [m]
last_radii = 0 # [m]
first_rossby = 0 # []
last_rossby = 0 # []
# First 7 days calculation
first_res = calculate_radii_and_rossby(0, num_of_days_to_avg,
1, segment, e_overestim,
# average and assign radii
first_radii = first_res['radii'] / num_of_days_to_avg
g.vp.first_av_rad[n] = first_radii
# grab the days modifier
modifier = first_res['days_modifier']
if (num_of_days_to_avg - modifier > 0):
# Average and assign the rossbies:
first_rossby = first_res['rossby'] / (num_of_days_to_avg - modifier)
g.vp.first_av_ros[n] = first_rossby
else:
# there is division by zero, average rossby is undefinied
pass
# Last 7 days calculation
last_res = calculate_radii_and_rossby(len(segment) - 1,
len(segment) - (num_of_days_to_avg + 1),
-1,
segment, e_overestim,
extremum,
# Average and assign the last radii
last_radii = last_res['radii'] / num_of_days_to_avg
g.vp.last_av_rad[n] = last_radii
# grab the days modifier
modifier = last_res['days_modifier']
if (num_of_days_to_avg - modifier > 0):
# Average and assign the rossbies:
last_rossby = last_res['rossby'] / (num_of_days_to_avg - modifier)
g.vp.last_av_ros[n] = last_rossby
else:
# there is division by zero, average rossby is undefinied
pass
# else, the number of eddies in a segment is lower than the # of
# days over which to average, the values will be the same except
# for the positions
else:
res = calculate_radii_and_rossby(0, num_of_days, 1, segment,
e_overestim,
extremum, max_speed, ishape_last)
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# grab the days modifier
modifier = res['days_modifier']
if (num_of_days - modifier > 0):
# Average and assign the rossbies:
rossby = res['rossby'] / (num_of_days - modifier)
g.vp.first_av_ros[n] = rossby
g.vp.last_av_ros[n] = rossby
else:
# there is division by zero, average rossby is undefinied
pass
# Average and assign the radii
radii = res['radii'] / num_of_days
g.vp.first_av_rad[n] = radii
g.vp.last_av_rad[n] = radii
###############################
# Calculate the cost function #
###############################
for edge in g.edges():
source_node = edge.source()
target_node = edge.target()
cf = -10000
lat_for_conv = (g.vp.pos_last[source_node][1] +
g.vp.pos_first[target_node][1]) / 2 # latitude needed for conversion of degrees to kilometers
lat_for_conv = math.radians(lat_for_conv) # need to convert to radians
# because of the wrapping issue (360° wrapping incorrectly to 0°), we check for that here
lon_diff = abs(g.vp.pos_last[source_node][0] - g.vp.pos_first[target_node][0])
if (lon_diff > 300):
lon_diff = 360 - lon_diff
# calculate Delta_cent: numbers used for conversion obtained from:
# https://stackoverflow.com/questions/1253499/simple-calculations-for-working-with-lat-lon-and-km-distance
Delta_Cent = math.sqrt(( lon_diff * 111.32 * math.cos(lat_for_conv) )**2 +
( (g.vp.pos_last[source_node][1] - g.vp.pos_first[target_node][1]) * 110.574 )**2)
# calculate the first term
first_term = ((Delta_Cent - delta_cent_mean)/delta_cent_std) ** 2
# Rossbies:
if (g.vp.first_av_ros[target_node] and g.vp.last_av_ros[source_node]):
Delta_Ro = g.vp.last_av_ros[source_node] - g.vp.first_av_ros[target_node]
else:
print("At least one of the rossbies is invalid.")
#Delta_Ro = delta_ro_mean
Delta_Ro = 0
# Calculate the second term
second_term = ((Delta_Ro - delta_ro_mean)/delta_ro_std ) ** 2
# R_Vmax 1 and 2 already exist, just get the delta
Delta_R_Vmax = g.vp.last_av_rad[source_node] - g.vp.first_av_rad[target_node]
# Calculate the third term
third_term = ((Delta_R_Vmax - delta_r_mean)/delta_r_std) ** 2
#############################
# calculate the cost function
#############################
cf = math.sqrt(first_term + second_term + third_term)
# assign as weight to the edge
g.ep.nl_cost_function[edge] = cf
################################
# Writing to an GT Graph File #
################################
#g.save("segments_cyclo_gl_cf.xml")
#print("Done writing xml.")
g.save(f'segmented_{orientation}_cf.gt')
print("Done writing gt.")
g.save(f'segmented_{orientation}_cf.xml')
print("Done writing xml.")
#g.save(f'{orientation}_segmented_cf.gv', fmt = "dot")
#print("Done writing dot file.")
print('All done')