From b55a165b5d20350a3c7137713d556c031fe2b7a7 Mon Sep 17 00:00:00 2001
From: Lionel GUEZ <guez@lmd.ens.fr>
Date: Mon, 27 May 2019 19:16:42 +0200
Subject: [PATCH] Add TAGS rule in `CMakeLists.txt`. Do not rely on external
 `settings.mk` in GNUmakefile.

Bug fix in procedure `dispatch_snapshot`: an isolated eddy is a valid eddy.

In script `plot_snapshot.py`, in function snaphot, replace argument ax
with argument `new_figure` so the user does not have to call figure
and `axes(projection = projection)`. Add argument light to function
snapshot so we can call snapshot with light set to False for a second
snapshot. Add a test to only read `h.nc` when we need it.

In script `read_overlap.py`, print more information: number of nodes,
number of edges, number of nodes with at least one successor, number
of nodes with at least one predecessor, splitting events, merging
events.

In script `stat.py`, use the convention that shapefiles are grouped in
a directory instead of identifying a set of shapefiles by the end of
their basename.

In main program `test_successive_overlap`, we know that `delta_in` for
snapshot 1 and `delta_out` for snapshot 2 are `huge(0)` so do not test
this. Print eddy identifiers in a single line. Add the printing of
identifiers of isolated eddies.
---
 Analysis/plot_snapshot.py              | 41 +++++++++------
 Analysis/read_overlap.py               | 21 +++++++-
 Analysis/stat.py                       | 11 ++--
 CMakeLists.txt                         |  1 +
 Documentation_texfol/documentation.tex | 14 ++---
 GNUmakefile                            | 72 +++++++++++++++++++++++++-
 Tests/test_successive_overlap.f90      | 45 +++++++++++-----
 candidate_overlap.f90                  |  3 ++
 dispatch_snapshot.f90                  |  5 +-
 9 files changed, 166 insertions(+), 47 deletions(-)

diff --git a/Analysis/plot_snapshot.py b/Analysis/plot_snapshot.py
index 9d678d8c..aea29cd0 100755
--- a/Analysis/plot_snapshot.py
+++ b/Analysis/plot_snapshot.py
@@ -29,7 +29,7 @@ import itertools
 import cartopy.crs as ccrs
 from os import path
 
-def snaphot(directory, ax, dashed = False):
+def snaphot(directory, dashed = False, light = False, new_figure = False):
     """Plots extrema, outermost contours and max-speed contours.
 
     directory: containing the three shapefiles.
@@ -47,6 +47,12 @@ def snaphot(directory, ax, dashed = False):
     else:
         m_s_iterShapes = reader_m_s.iterShapes()
 
+    if new_figure:
+        plt.figure()
+        ax = plt.axes(projection = projection)
+    else:
+        ax = plt.gca()
+
     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]
@@ -68,27 +74,27 @@ def snaphot(directory, ax, dashed = False):
                             transform = src_crs)
 
             if shape_rec_extr.record.valid == 1:
-                if args.light:
+                if light:
                     lines[0].set_marker("+")
                 else:
                     lines[0].set_marker("o")
-            elif not args.light:
+            elif not light:
                 # Invalid outermost contour
                 lines[0].set_marker("s")
 
-            if not args.light:
+            if not light:
                 ax.annotate(str(shape_rec_extr.record.eddy_index),
                             projection.transform_point(points[0], points[1],
                                                        src_crs),
                             xytext = (3, 3), textcoords = "offset points")
 
-            if shape_outer.shapeType != shapefile.NULL and not args.light \
+            if shape_outer.shapeType != shapefile.NULL and not light \
                or shape_rec_extr.record.valid:
                 points = np.array(shape_outer.points)
                 lines = ax.plot(points[:, 0], points[:, 1], color = color,
                                 transform = src_crs)
 
-                if not args.light:
+                if not light:
                     if shape_rec_extr.record.valid == 0:
                         # Invalid outermost contour
                         lines[0].set_marker("s")
@@ -112,7 +118,7 @@ def snaphot(directory, ax, dashed = False):
 
                     lines = ax.plot(points[:, 0], points[:, 1], color = color,
                                     transform = src_crs)
-                    if not args.light: lines[0].set_marker("o")
+                    if not light: lines[0].set_marker("o")
                     if dashed: lines[0].set_linestyle("dashed")
 
     ax.gridlines(draw_labels = True)
@@ -138,9 +144,10 @@ if __name__ == "__main__":
     parser.add_argument("directory", help = "containing the three shapefiles")
     args = parser.parse_args()
 
-    with netCDF4.Dataset("h.nc") as f:
-        longitude = f.variables["longitude"][:]
-        latitude = f.variables["latitude"][:]
+    if args.grid or args.velocity:
+        with netCDF4.Dataset("h.nc") as f:
+            longitude = f.variables["longitude"][:]
+            latitude = f.variables["latitude"][:]
     
     if args.window:
         l = input("llcrnrlon, llcrnrlat, urcrnrlon, "
@@ -150,12 +157,14 @@ if __name__ == "__main__":
         if urcrnrlon - llcrnrlon > 360:
             sys.exit("bad values of urcrnrlon and llcrnrlon")
 
-        longitude += np.ceil((llcrnrlon - longitude) / 360) * 360
-        # (in [llcrnrlon, llcrnrlon + 2 pi[)
+        if args.grid or args.velocity:
+            longitude += np.ceil((llcrnrlon - longitude) / 360) * 360
+            # (in [llcrnrlon, llcrnrlon + 2 pi[)
 
-        lon_mask = longitude <= urcrnrlon
-        lat_mask = np.logical_and(latitude >= llcrnrlat, latitude <= urcrnrlat)
-    else:
+            lon_mask = longitude <= urcrnrlon
+            lat_mask = np.logical_and(latitude >= llcrnrlat,
+                                      latitude <= urcrnrlat)
+    elif args.grid or args.velocity:
         lon_mask = np.ones(len(longitude), dtype = bool)
         lat_mask = np.ones(len(latitude), dtype = bool)
 
@@ -170,7 +179,7 @@ if __name__ == "__main__":
         ax.plot(lon_2d.reshape(-1), lat_2d.reshape(-1), transform = src_crs,
                 marker = "+", color = "gray", linestyle = "None")
 
-    snaphot(args.directory, ax, args.dashed)
+    snaphot(args.directory, args.dashed, args.light)
 
     if args.velocity:
         with netCDF4.Dataset("uv.nc") as f:
diff --git a/Analysis/read_overlap.py b/Analysis/read_overlap.py
index 7027422b..b36f8ed1 100755
--- a/Analysis/read_overlap.py
+++ b/Analysis/read_overlap.py
@@ -18,8 +18,27 @@ with open("edgelist.csv") as f:
                                  int(row[3]), float(row[4])
         G.add_edge((k1, i1), (k2, i2), weight = weight)
 
-pos = nx.nx_agraph.graphviz_layout(G, prog="dot")
+print("Number of nodes:", len(G))
+print("Number of edges:", G.number_of_edges())
+n1 = 0
+n2 = 0
+print("Merging:")
 
+for n, d in G.in_degree():
+    if d >= 1:
+        n2 += 1
+        if d >= 2: print(list(G.predecessors(n)), "->", n)
+
+print("Splitting:")
+
+for n, d in G.out_degree():
+    if d >= 1:
+        n1 += 1
+        if d >= 2: print(n, "->", list(G.successors(n)))
+
+print("number of nodes with at least one successor =", n1)
+print("number of nodes with at least one predecessor =", n2)
+pos = nx.nx_agraph.graphviz_layout(G, prog = "dot")
 plt.figure()
 nx.draw(G, pos, with_labels=True)
 plt.show()
diff --git a/Analysis/stat.py b/Analysis/stat.py
index e32e4edb..6ff5fa29 100755
--- a/Analysis/stat.py
+++ b/Analysis/stat.py
@@ -3,11 +3,10 @@
 import argparse
 import shapefile
 import numpy as np
+from os import path
 
 parser = argparse.ArgumentParser()
-parser.add_argument("-e", "--end_filename", help = "add character string after "
-                    "extremum, outermost contour and max_speed_contour",
-                    default = "")
+parser.add_argument("directory", help = "containing the three shapefiles")
 args = parser.parse_args()
 
 n_valid_cycl = 0
@@ -20,8 +19,8 @@ n_points = 0
 n_missing_speed = np.zeros(3, dtype = int)
 n_valid_speed = 0
 
-with shapefile.Reader("extremum" + args.end_filename) as extremum, \
-     shapefile.Reader("outermost_contour" + args.end_filename) \
+with shapefile.Reader(path.join(args.directory, "extremum")) as extremum, \
+     shapefile.Reader(path.join(args.directory, "outermost_contour")) \
      as outermost_contour:
     n_extr = len(extremum)
     
@@ -94,7 +93,7 @@ n_max_speed = 0
 n_points_valid = 0
 n_points = 0
 
-with shapefile.Reader("max_speed_contour" + args.end_filename) \
+with shapefile.Reader(path.join(args.directory, "max_speed_contour")) \
      as max_speed_contour:
     for shape_rec in max_speed_contour:
         l = len(shape_rec.shape.points)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7de64de9..f60a728b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,3 +56,4 @@ target_link_libraries(extraction_eddies ${contour_531_LIBRARY}
 
 include(Tests/CMakeLists.txt)
 include(ConfigureCompilerFlags)
+include(TAGS)
diff --git a/Documentation_texfol/documentation.tex b/Documentation_texfol/documentation.tex
index ba58f28a..df34d88e 100644
--- a/Documentation_texfol/documentation.tex
+++ b/Documentation_texfol/documentation.tex
@@ -49,7 +49,7 @@
 \maketitle
 \tableofcontents
 
-\section{Extraction des tourbillons}
+\section{Extraction des tourbillons instantanés}
 
 \subsection{Divers}
 
@@ -1004,12 +1004,12 @@ comptant 10 caractères par entier, 13 caractères par réel, \np{5e7}
 sommets et deux arcs par sommet en moyenne, est de 6 GiB.
 
 Problème des sommets isolés. L'information est déjà présente, de façon
-implicite, dans les fichiers évoqués ci-dessus. En effet, on a le
-nombre de tourbillons à chaque date d'après la liste des identifiants
-de tourbillons dans les shapefiles, et on a la liste des tourbillons
-non isolés d'après la liste des arcs. Mais la liste des sommets isolés
-n'est donc pas très facilement trouvée. On ajoute par conséquent les
-fichiers de sortie suivants :
+implicite, dans les shapefiles et les fichiers edgelist. En effet, on
+a le nombre de tourbillons valides à chaque date d'après les
+shapefiles, et on a la liste des tourbillons non isolés d'après la
+liste des arcs. Mais la liste des sommets isolés n'est donc pas très
+facilement trouvée. On ajoute par conséquent les fichiers de sortie
+suivants :
 \begin{description}
 \item[number\_eddies\_\$m.csv] Une colonne indice de date, une colonne
   nombre de tourbillons visibles, une colonne nombre de tourbillons
diff --git a/GNUmakefile b/GNUmakefile
index 8aa63336..f6144413 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -31,8 +31,7 @@ sources := $(sort ${src_test_local_extrema} ${src_test_get_1_outerm} ${src_test_
 lib_list = GPC_F contour_531 numer_rec_95 GPC shapelib_03 netcdf95 geometry jumble netcdff fortrangis shp fortranc nr_util
 
 makefile_dir = .
-include ${general_compiler_options_dir}/settings.mk
-VPATH += ${makefile_dir}/Tests
+VPATH := ${makefile_dir}
 
 # 2. Objects and executable files
 
@@ -55,6 +54,12 @@ execut = test_good_contour test_inside_4 test_get_1_outerm test_local_extrema te
 
 # 3. Compiler-dependent part
 
+# Default mode:
+mode = debug
+
+##include ${general_compiler_options_dir}/${FC}_${mode}.mk
+# general_compiler_options_dir should be defined in the environment.
+
 ifeq (${FC},gfortran)
 # gfortran bug:
 FFLAGS := $(subst invalid${comma},,${FFLAGS})
@@ -62,6 +67,69 @@ endif
 
 # 4. Rules
 
+SHELL = bash
+# for echo in log commands
+
+%: %.o
+	@echo "Linking $@..."
+	$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
+# (must be before the other "%:" rules)
+
+%.o: %.f
+	@echo "Building $@..."
+	$(COMPILE.f) $(OUTPUT_OPTION) $<
+
+%: %.f
+	@echo "Compiling and linking $@..."
+	$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+# Extend known suffixes:
+# (if the suffix is not ".f" or ".F")
+
+%.o: %.f90
+	@echo "Building $@..."
+	$(COMPILE.f) $(OUTPUT_OPTION) $<
+
+%.o: %.F90
+	@echo "Building $@..."
+	$(COMPILE.F) $(OUTPUT_OPTION) $<
+
+%: %.f90
+	@echo "Linking $@..."
+	$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+%: %.F90
+	@echo "Linking $@..."
+	$(LINK.F) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+# Use the Fortran compiler for linking:
+LINK.o = $(FC) $(LDFLAGS) $(TARGET_ARCH)
+
+%.so:
+	$(FC) $(LDFLAGS) ${ldflags_lib_dyn} $^ -o $@
+
+%.csv: %.ods
+	unoconv --format=csv --output=$@ $<
+
+.DELETE_ON_ERROR:
+.PHONY: all clean clobber depend
+all: log
+
+TAGS: ${sources}
+	ctags -e --language-force=fortran $^
+# Or, on barren machines:
+##	etags --language=fortran $^
+
+log:
+	hostname >$@
+	${FC} ${version_flag} >>$@ 2>&1
+	@echo -e "\nFC = ${FC}\n\nCPPFLAGS = ${CPPFLAGS}\n\nFFLAGS = ${FFLAGS}\n\nLDLIBS = ${LDLIBS}\n\nLDFLAGS = ${LDFLAGS}\n\nldflags_lib_dyn = ${ldflags_lib_dyn}" >>$@
+
+clobber: clean
+	rm -f *.mod ${makefile_dir}/depend.mk TAGS
+
+VPATH += ${makefile_dir}/Tests
+
 all: ${execut} log
 test_get_1_outerm: ${obj_test_get_1_outerm}
 test_set_max_speed: ${obj_test_set_max_speed}
diff --git a/Tests/test_successive_overlap.f90 b/Tests/test_successive_overlap.f90
index 446da5b9..614c59a0 100644
--- a/Tests/test_successive_overlap.f90
+++ b/Tests/test_successive_overlap.f90
@@ -15,7 +15,7 @@ program test_successive_overlap
 
   implicit none
 
-  integer unit_edgelist, i, j, k
+  integer unit_edgelist, i, k
   type(snapshot) flow(2)
   TYPE(shpfileobject) hshp_extremum ! shapefile extremum
   TYPE(shpfileobject) hshp_outermost ! shapefile outermost_contour
@@ -27,7 +27,7 @@ program test_successive_overlap
 
   real:: step_deg(2) = [0.25, 0.25], step(2)
   ! longitude and latitude steps, in degrees and in rad
-  
+
   logical periodic ! grid is periodic in longitude
   integer:: nlon = - 1, nlat = - 1
 
@@ -66,6 +66,7 @@ program test_successive_overlap
   CALL shpclose(hshp_extremum)
   CALL shpclose(hshp_outermost)
   CALL shpclose(hshp_max_speed)
+  print *, "Snapshot 1, k = ", k
 
   call shp_open_03("Snapshot_2/extremum", pszaccess = "rb", &
        hshp = hshp_extremum)
@@ -79,6 +80,7 @@ program test_successive_overlap
   CALL shpclose(hshp_extremum)
   CALL shpclose(hshp_outermost)
   CALL shpclose(hshp_max_speed)
+  print *, "Snapshot 2, k = ", k
 
   call new_unit(unit_edgelist)
   open(unit_edgelist, file = "edgelist.csv", status = "replace", &
@@ -95,20 +97,37 @@ program test_successive_overlap
   close(unit_edgelist)
   print *, 'Created file "edgelist.csv".'
 
-  do j = 1, 2
-     print *, "i, flow(", j, ")%list_vis%delta_in:"
+  print *, "Snapshot 1:"
+  print *, "Eddies for which delta_out == 1:"
+
+  do i = 1, flow(1)%number_vis_extr
+     if (flow(1)%list_vis(i)%delta_out /= huge(0)) &
+          write(unit = *, fmt = "(i0, 1x)",  advance = "no") i
+  end do
 
-     do i = 1, flow(j)%number_vis_extr
-        if (flow(j)%list_vis(i)%delta_in /= huge(0)) &
-             print *, i, flow(j)%list_vis(i)%delta_in
-     end do
+  print *
+  print *, "Valid isolated eddies:"
+  do i = 1, flow(1)%number_vis_extr
+     if (flow(1)%list_vis(i)%valid .and. flow(1)%list_vis(i)%delta_out &
+          == huge(0)) write(unit = *, fmt = "(i0, 1x)", advance = "no") i
+  end do
+
+  print *
+  print *, "Snapshot 2:"
+  print *, "Eddies for which delta_in == 1:"
+
+  do i = 1, flow(2)%number_vis_extr
+     if (flow(2)%list_vis(i)%delta_in /= huge(0)) &
+          write(unit = *, fmt = "(i0, 1x)", advance = "no") i
+  end do
 
-     print *, "i, flow(", j, ")%list_vis%delta_out:"
+  print *
+  print *, "Valid isolated eddies:"
 
-     do i = 1, flow(j)%number_vis_extr
-        if (flow(j)%list_vis(i)%delta_out /= huge(0)) &
-             print *, i, flow(j)%list_vis(i)%delta_out
-     end do
+  do i = 1, flow(2)%number_vis_extr
+     if (flow(2)%list_vis(i)%valid .and. flow(2)%list_vis(i)%delta_in &
+          == huge(0)) write(unit = *, fmt = "(i0, 1x)", advance = "no") i
   end do
+  print *
 
 end program test_successive_overlap
diff --git a/candidate_overlap.f90 b/candidate_overlap.f90
index 8f6c02a8..ec33a985 100644
--- a/candidate_overlap.f90
+++ b/candidate_overlap.f90
@@ -6,6 +6,9 @@ contains
 
   function candidate_overlap(extr_map, list_vis, cyclone)
 
+    ! Find the eddies in extr_map that are valid and have a given
+    ! cyclonicity.
+
     use derived_types, only: eddy
 
     integer, allocatable:: candidate_overlap(:)
diff --git a/dispatch_snapshot.f90 b/dispatch_snapshot.f90
index 759ffaec..4201d06a 100644
--- a/dispatch_snapshot.f90
+++ b/dispatch_snapshot.f90
@@ -28,8 +28,9 @@ contains
 
     if (m == 1 .or. k >= k_begin + max_delta) then
        do i = 1, s%number_vis_extr
-          if (s%list_vis(i)%delta_in == huge(0) .and. s%list_vis(i)%delta_out &
-               == huge(0)) write(unit_isolated, fmt = *) k, i
+          if (s%list_vis(i)%valid .and. s%list_vis(i)%delta_in == huge(0) &
+               .and. s%list_vis(i)%delta_out == huge(0)) &
+               write(unit_isolated, fmt = *) k, i
        end do
 
        write(unit_number_eddies, fmt = *) k, s%number_vis_extr, &
-- 
GitLab