From f88b44a4f239b1edcc650e3cb735ad5f2d5638a8 Mon Sep 17 00:00:00 2001 From: Lionel GUEZ <guez@lmd.ens.fr> Date: Tue, 8 Jan 2019 16:45:43 +0100 Subject: [PATCH] Add default value of argument end_filename of function def read in script distribution_function.py. Return dictionary instead of tuple: easier to use. In script plot_snapshot.py, move definition of readers to the beginning so there is no risk of entering the window for nothing if we forgot the end_filename option. Also, enter the four values defining the window in a single input: easier to copy and paste. --- Analysis/distribution_function.py | 43 ++++++++++++---------- Analysis/plot_snapshot.py | 45 ++++++++++++----------- Documentation_texfol/documentation.tex | 51 ++++++++++++++++++++++++++ spherical_polygon_area.f | 6 +-- spherical_polyline_area.f | 8 ++-- 5 files changed, 104 insertions(+), 49 deletions(-) diff --git a/Analysis/distribution_function.py b/Analysis/distribution_function.py index c1b7e5b3..68797f53 100755 --- a/Analysis/distribution_function.py +++ b/Analysis/distribution_function.py @@ -15,9 +15,10 @@ def plot_distr_funct(X, label = None, ax = None): print("maximum value:", X[-1]) print("number of values:", nx, "\n") -def read(end_filename): - """Return speed, radius of outermost contour and radius of maximum speed - contour from three dbf files, as Numpy arrays. +def read(end_filename = ""): + """Read the three dbf files and return speed, radius and amplitude of + outermost contour, radius and amplitude of maximum speed contour, + as Numpy arrays. Select valid speed values and valid outermost contour (with sufficient area). @@ -46,8 +47,9 @@ def read(end_filename): rad_speed.append(rec_max.r_eq_area) amp_speed.append(rec_extr.ssh - rec_max.ssh) - return np.array(speed), np.array(rad_outer), np.array(rad_speed), \ - np.array(amp_outer), np.array(amp_speed) + return {"speed": np.array(speed), "rad_outer": np.array(rad_outer), + "rad_speed": np.array(rad_speed), "amp_outer": np.array(amp_outer), + "amp_speed": np.array(amp_speed)} if __name__ == "__main__": import argparse @@ -58,62 +60,62 @@ if __name__ == "__main__": "extremum, outermost_contour and max_speed_contour", default = "") args = parser.parse_args() - - speed, rad_outer, rad_speed, amp_outer, amp_speed = read(args.end_filename) + d = read(args.end_filename) xlabel = "speed, in m s-1" plt.figure() - plt.hist(speed, bins = "auto") + plt.hist(d["speed"], bins = "auto") plt.xlabel(xlabel) plt.ylabel("number of occurrences") plt.figure() print("speed, when positive:") - plot_distr_funct(speed[speed >= 0], label = "positive") + plot_distr_funct(d["speed"][d["speed"] >= 0], label = "positive") print("- speed, when negative:") - plot_distr_funct(- speed[speed < 0], label = "negative") + plot_distr_funct(- d["speed"][d["speed"] < 0], label = "negative") plt.xlabel(xlabel) plt.legend() xlabel = "equivalent radius of outermost contour, in km" plt.figure() - plt.hist(rad_outer, bins = "auto") + plt.hist(d["rad_outer"], bins = "auto") plt.xlabel(xlabel) plt.ylabel("number of occurrences") plt.figure() print(xlabel + ":") - plot_distr_funct(rad_outer) + plot_distr_funct(d["rad_outer"]) plt.xlabel(xlabel) plt.ylabel("distribution function") xlabel = "equivalent radius of max-speed contour, in km" plt.figure() - plt.hist(rad_speed, bins = "auto") + plt.hist(d["rad_speed"], bins = "auto") plt.xlabel(xlabel) plt.ylabel("number of occurrences") plt.figure() print(xlabel + ":") - plot_distr_funct(rad_speed) + plot_distr_funct(d["rad_speed"]) plt.xlabel(xlabel) plt.ylabel("distribution function") xlabel = "amplitude of outermost contour, in m" plt.figure() - plt.hist(amp_outer, bins = "auto") + plt.hist(d["amp_outer"], bins = "auto") plt.xlabel(xlabel) plt.ylabel("number of occurrences") plt.figure() print("amplitude of outermost contour, anticyclones:") - plot_distr_funct(amp_outer[amp_outer >= 0], label = "anticyclones") + plot_distr_funct(d["amp_outer"][d["amp_outer"] >= 0], + label = "anticyclones") print("amplitude of outermost contour, cyclones:") - plot_distr_funct(- amp_outer[amp_outer < 0], label = "cyclones") + plot_distr_funct(- d["amp_outer"][d["amp_outer"] < 0], label = "cyclones") plt.xlabel(xlabel) plt.ylabel("distribution function") plt.legend() @@ -121,15 +123,16 @@ if __name__ == "__main__": xlabel = "amplitude of max-speed contour, in m" plt.figure() - plt.hist(amp_speed, bins = "auto") + plt.hist(d["amp_speed"], bins = "auto") plt.xlabel(xlabel) plt.ylabel("number of occurrences") plt.figure() print("amplitude of max-speed contour, anticyclones:") - plot_distr_funct(amp_speed[amp_speed >= 0], label = "anticyclones") + plot_distr_funct(d["amp_speed"][d["amp_speed"] >= 0], + label = "anticyclones") print("amplitude of max-speed contour, cyclones:") - plot_distr_funct(- amp_speed[amp_speed < 0], label = "cyclones") + plot_distr_funct(- d["amp_speed"][d["amp_speed"] < 0], label = "cyclones") plt.xlabel(xlabel) plt.ylabel("distribution function") plt.legend() diff --git a/Analysis/plot_snapshot.py b/Analysis/plot_snapshot.py index ec5ded35..8ef3ff0e 100755 --- a/Analysis/plot_snapshot.py +++ b/Analysis/plot_snapshot.py @@ -4,10 +4,11 @@ Red for anti-cyclones, blue for cyclones. Empty circles for extrema with a valid outermost contour, empty squares for other -extrema. Squares for a well-defined but invalid outermost -contour. Crosses for a valid outermost contour but with no distinct -max-speed contour. Filled circles for a valid outermost contour with a -distinct max-speed contour. +extrema. Squares on outermost contour for a well-defined but invalid +outermost contour. Crosses on outermost contour for a valid outermost +contour but with no distinct max-speed contour. Filled circles on +outermost contour and max-speed contour for a valid outermost contour +with a distinct max-speed contour. This scripts takes about 30 s of CPU for a 90° by 90° window, about 4 mn for a 180° by 180° window. @@ -39,15 +40,27 @@ parser.add_argument("-e", "--end_filename", help = "add character string after " default = "") args = parser.parse_args() +reader_extr = shapefile.Reader("extremum" + args.end_filename) +reader_outer = shapefile.Reader("outermost_contour" + args.end_filename) + +try: + reader_m_s = shapefile.Reader("max_speed_contour" + args.end_filename) +except shapefile.ShapefileException: + print("Shapefile max_speed_contour_1 not found. " + "Max-speed contours will not be plotted.") + w = shapefile.Writer(shapeType = shapefile.NULL) + m_s_iterShapes = itertools.repeat(w) +else: + m_s_iterShapes = reader_m_s.iterShapes() + with netCDF4.Dataset("h.nc") as f: longitude = f.variables["lon"][:] latitude = f.variables["lat"][:] if args.window: - llcrnrlon = float(input("llcrnrlon = ? ")) - llcrnrlat = float(input("llcrnrlat = ? ")) - urcrnrlon = float(input("urcrnrlon = ? ")) - urcrnrlat = float(input("urcrnrlat = ? ")) + l = input("llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat = ? ")\ + .split(sep = ",") + llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat = [float(v) for v in l] if urcrnrlon - llcrnrlon > 360: sys.exit("bad values of urcrnrlon and llcrnrlon") @@ -64,6 +77,7 @@ else: plt.figure() src_crs = ccrs.PlateCarree() projection = ccrs.PlateCarree() +##projection = ccrs.NorthPolarStereo() ax = plt.axes(projection = projection) if args.grid: @@ -71,19 +85,6 @@ if args.grid: ax.plot(lon_2d.reshape(-1), lat_2d.reshape(-1), transform = src_crs, marker = "+", color = "gray", linestyle = "None") -reader_extr = shapefile.Reader("extremum" + args.end_filename) -reader_outer = shapefile.Reader("outermost_contour" + args.end_filename) - -try: - reader_m_s = shapefile.Reader("max_speed_contour" + args.end_filename) -except shapefile.ShapefileException: - print("Shapefile max_speed_contour_1 not found. " - "Max-speed contours will not be plotted.") - w = shapefile.Writer(shapeType = shapefile.NULL) - m_s_iterShapes = itertools.repeat(w) -else: - m_s_iterShapes = reader_m_s.iterShapes() - for shape_rec_extr, shape_outer, shape_m_s \ in zip(reader_extr, reader_outer.iterShapes(), m_s_iterShapes): points = shape_rec_extr.shape.points[0] @@ -103,7 +104,7 @@ for shape_rec_extr, shape_outer, shape_m_s \ lines = ax.plot(points[0], points[1], markersize = 10, color = color, fillstyle = "none", transform = src_crs) - if shape_rec_extr.record.valid != 0: + if shape_rec_extr.record.valid == 1: if args.light: lines[0].set_marker("+") else: diff --git a/Documentation_texfol/documentation.tex b/Documentation_texfol/documentation.tex index 3917c53f..5d86f94a 100644 --- a/Documentation_texfol/documentation.tex +++ b/Documentation_texfol/documentation.tex @@ -20,6 +20,8 @@ \usepackage{algorithm} +\newcommand{\ud}{\mathrm{d}} + \renewcommand{\algorithmicfor}{\textbf{pour}} \renewcommand{\algorithmicend}{\textbf{fin}} \renewcommand{\algorithmicdo}{\textbf{faire}} @@ -1277,6 +1279,55 @@ weight à u et v, c'est-à -dire qu'il faudrait conserver u, v à plusieurs dates en dehors de get\_snpashot. Cela remettrait en question tout l'algorithme. +\subsection{spherical\_polyline\_area} + +La procédure de calcul des contours donne une suite de sommets. Les +courbes reliant ces sommets ne sont en fait pas imposés +intrinsèquement par cette procédure. Par exemple, il n'est ni plus ni +moins juste de supposer que ces courbes sont des portions de grands +cercles ou de supposer qu'elles donnent des segments en projection +plate carrée. Néanmoins, dans le reste du programme, pour décider si +un contour entoure un extremum, ou pour calculer l'intersection de +deux tourbillons, nous supposons que les contours sont des polygones +en $(\lambda, \phi)$ (c'est-à -dire que les courbes reliant les sommets +donnent des segments en projection plate carrée). + +Un domaine $S$ quelconque à la surface de la Terre a pour aire : +\begin{equation*} + a^2 \iint_S \cos \phi\ \ud \phi\ \ud \lambda + = a^2 \iint_S \ud \lambda\ \ud(\sin \phi) +\end{equation*} +Nous avons une procédure qui calcule la surface $\iint \ud x\ \ud y$ +d'un polygone en $(x, y)$. Nous calculons la surface du polygone en +$(\lambda, \sin \phi)$ passant par les sommets du +contour. C'est-à -dire que nous supposons que les courbes reliant les +somments donnent des segments en projection conforme cylindrique. + +Rémi fait un autre calcul : la surface du polygone en +$(\lambda \cos \phi, \phi)$ passant par les sommets du contour. Le +changement de variable : +\begin{align*} + & x = \lambda \cos \phi \\ + & y = \phi +\end{align*} +a pour jacobien $\cos \phi$ donc on a bien : +\begin{equation*} + \iint \cos \phi\ \ud \phi\ \ud \lambda = \iint \ud x\ \ud y +\end{equation*} +Les deux surfaces obtenues, celle du polygone en +$(\lambda, \sin \phi)$ et celle du polygone en +$(\lambda \cos \phi, \phi)$ sont très proches si les sommets sont très +proches. C'est le cas puisque les sommets sont séparés au maximum de +\np{0.25}° en longitude et latitude. On peut aussi le voir en +considérant la formule de calcul de surface du polygone : +\begin{align*} + S & = \frac{1}{2} \sum_i x_i (y_{i + 1} - y_{i - 1}) \\ + & = + \frac{1}{2} \sum_i \lambda_i \cos \phi_i (\phi_{i + 1} - \phi_{i - 1}) \\ + & \approx \frac{1}{2} \sum_i \lambda_i (\sin \phi_{i + 1} - \sin \phi_{i - 1}) +\end{align*} +si les valeurs successives des $\phi_i$ sont proches. + \subsection{successive\_overlap} j, k, scalaires entiers, données. flow : donnée-résultat. Cherche les diff --git a/spherical_polygon_area.f b/spherical_polygon_area.f index d10b3e74..9fe5c848 100644 --- a/spherical_polygon_area.f +++ b/spherical_polygon_area.f @@ -6,8 +6,8 @@ contains pure real function spherical_polygon_area(p) - ! Assuming p is a polygon in longitude, latitude, in rad, compute - ! the area of a polygon in longitude, sin(latitude) with the same + ! Assuming p is a polygon in longitude, latitude, compute the area + ! of a polygon in longitude, sin(latitude) with the same ! vertices. Result in m2. ! Libraries: @@ -15,7 +15,7 @@ contains use spherical_polyline_area_m, only: spherical_polyline_area - type(polygon), intent(in):: p + type(polygon), intent(in):: p ! in rad ! Local: diff --git a/spherical_polyline_area.f b/spherical_polyline_area.f index b9d5449b..5c00b84b 100644 --- a/spherical_polyline_area.f +++ b/spherical_polyline_area.f @@ -6,15 +6,15 @@ contains pure real function spherical_polyline_area(p) - ! Assuming p is a polyline in longitude, latitude, in rad, compute - ! the area (positive) of a polyline in longitude, sin(latitude) - ! with the same vertices. Result in m2. + ! Assuming p is a polyline in longitude, latitude, compute the + ! area (positive) of a polyline in longitude, sin(latitude) with + ! the same vertices. Result in m2. ! Libraries: use contour_531, only: polyline use geometry, only: polygon_area_2d - type(polyline), intent(in):: p ! should be closed + type(polyline), intent(in):: p ! Should be closed. In rad. ! Local: -- GitLab