浏览代码

r.sun: Fix metadata update on parallel computation (#1514)

Add test cases to check for metadata consistency
Aaron Saw Min Sern 4 年之前
父节点
当前提交
8295d9506d
共有 2 个文件被更改,包括 185 次插入40 次删除
  1. 6 2
      raster/r.sun/main.c
  2. 179 38
      raster/r.sun/testsuite/test_rsun.py

+ 6 - 2
raster/r.sun/main.c

@@ -1837,9 +1837,13 @@ void calculate(double singleSlope, double singleAspect, double singleAlbedo,
 	}
 	sunVarGeom.zmax = zmax;
         shadowoffset_base = (j % (numRows)) * n * arrayNumInt;
-    #pragma omp parallel firstprivate(q1,tan_lam_l,z1,i,shadowoffset,longitTime,coslat,coslatsq,func,latitude,longitude,sin_phi_l,latid_l,sin_u,cos_u,sin_v,cos_v,lum, gridGeom,sunGeom,sunVarGeom,sunSlopeGeom,sunRadVar, elevin,aspin,slopein,civiltime,linkein,albedo,latin,coefbh,coefdh,incidout,longin,horizon,beam_rad,insol_time,diff_rad,refl_rad,glob_rad,mapset,per,decimals,str_step )
+    #pragma omp parallel firstprivate(q1,tan_lam_l,z1,i,shadowoffset,longitTime,coslat,coslatsq,func,latitude,longitude,sin_phi_l,latid_l,sin_u,cos_u,sin_v,cos_v,lum,gridGeom,elevin,aspin,slopein,civiltime,linkein,albedo,latin,coefbh,coefdh,incidout,longin,horizon,beam_rad,insol_time,diff_rad,refl_rad,glob_rad,mapset,per,decimals,str_step)
     {
-      #pragma omp for schedule(dynamic) 
+      #pragma omp for schedule(dynamic)                                                        \
+                      firstprivate(sunGeom,sunVarGeom,sunSlopeGeom,sunRadVar)                  \
+                      lastprivate(sunGeom,sunVarGeom,sunSlopeGeom,sunRadVar)                   \
+                      reduction(max : linke_max, albedo_max, lat_max, sunrise_max, sunset_max) \
+                      reduction(min : linke_min, albedo_min, lat_min, sunrise_min, sunset_min)
 	for (i = 0; i < n; i++) {
             shadowoffset = shadowoffset_base + (arrayNumInt * i);
 	    if (useCivilTime()) {

+ 179 - 38
raster/r.sun/testsuite/test_rsun.py

@@ -4,16 +4,117 @@ from grass.gunittest.main import test
 from grass.gunittest.gmodules import SimpleModule
 
 
+class TestRSunMode1Metadata(TestCase):
+    """Tests the consistency of metadata when computing instantaneous solar
+    incidence angle and irradiance.
+    """
+
+    elevation = "elevation"
+    slope = "rsun_slope"
+    aspect = "rsun_aspect"
+    incidout = "incidout"
+
+    # expected metadata with the relevant `nprocs` field
+    metadata = """\"\
+ ----------------------------------------------------------------\
+ Day [1-365]:                              172\
+ Local (solar) time (decimal hr.):         18.0000\
+ Solar constant (W/m^2):                   1367.000000\
+ Extraterrestrial irradiance (W/m^2):      1322.508495\
+ Declination (rad):                        0.409115\
+ Latitude min-max(deg):                    35.7328 - 35.7642\
+ Sunrise time (hr.):                       4.79\
+ Sunset time (hr.):                        19.21\
+ Daylight time (hr.):                      14.43\
+ Solar altitude (deg):                     13.4438\
+ Solar azimuth (deg):                      289.3828\
+ Linke turbidity factor:                   3.0\
+ Ground albedo:                            0.200\
+ -----------------------------------------------------------------\
+\
+r.sun --overwrite elevation="elevation" aspect="rsun_aspect" aspect_\\\
+value=270.0 slope="rsun_slope" slope_value=0.0 linke_value=3.0 albed\\\
+o_value=0.2 incidout="incidout" day=172 step=0.5 solar_constant=1367\\\
+.0 time=18 nprocs={nprocs} distance_step=1.0 npartitions=1"\
+"""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.use_temp_region()
+        cls.runModule("g.region", n=223500, s=220000, e=640000, w=635000, res=10)
+        cls.runModule(
+            "r.slope.aspect",
+            elevation=cls.elevation,
+            slope=cls.slope,
+            aspect=cls.aspect,
+        )
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.del_temp_region()
+        cls.runModule(
+            "g.remove",
+            type=["raster"],
+            name=[
+                cls.slope,
+                cls.aspect,
+                cls.incidout,
+            ],
+            flags="f",
+        )
+
+    def setUp(self):
+        self.rsun = SimpleModule(
+            "r.sun",
+            elevation=self.elevation,
+            slope=self.slope,
+            aspect=self.aspect,
+            incidout=self.incidout,
+            day=172,
+            time=18,
+            overwrite=True,
+        )
+
+    def test_more_threads(self):
+        """Checks metadata correctness in threaded cases."""
+        self.rsun.inputs["nprocs"].value = 4
+        self.assertModule(self.rsun)
+        self.assertRasterExists(name=self.incidout)
+        self.assertModuleKeyValue(
+            "r.info",
+            map=self.incidout,
+            flags="e",
+            reference=dict(comments=self.metadata.format(nprocs=4)),
+            precision=0,
+            sep="=",
+        )
+
+    def test_run_outputs(self):
+        """Checks metadata correctness in non-threaded cases."""
+        self.assertModule(self.rsun)
+        self.assertRasterExists(name=self.incidout)
+        self.assertModuleKeyValue(
+            "r.info",
+            map=self.incidout,
+            flags="e",
+            reference=dict(comments=self.metadata.format(nprocs=1)),
+            precision=0,
+            sep="=",
+        )
+
+
 class TestRSunMode2(TestCase):
-    elevation = 'elevation'
-    elevation_attrib = 'elevation_attrib'
-    elevation_threads = 'elevation_threads'
-    slope = 'rsun_slope'
-    aspect = 'rsun_aspect'
-    beam_rad = 'beam_rad'
-    glob_rad = 'glob_rad'
-    insol_time = 'insol_time'
-    glob_rad_threads = 'glob_rad_threads'
+    elevation = "elevation"
+    elevation_attrib = "elevation_attrib"
+    elevation_threads = "elevation_threads"
+    slope = "rsun_slope"
+    aspect = "rsun_aspect"
+    beam_rad = "beam_rad"
+    glob_rad = "glob_rad"
+    insol_time = "insol_time"
+    beam_rad_threads = "beam_rad_threads"
+    glob_rad_threads = "glob_rad_threads"
+    insol_time_threads = "insol_time_threads"
 
     @classmethod
     def setUpClass(cls):
@@ -24,8 +125,21 @@ class TestRSunMode2(TestCase):
     @classmethod
     def tearDownClass(cls):
         cls.del_temp_region()
-        cls.runModule('g.remove', type=['raster'],
-                      name=[cls.slope, cls.aspect, cls.insol_time, cls.beam_rad, cls.glob_rad, cls.glob_rad_threads], flags='f')
+        cls.runModule(
+            "g.remove",
+            type=["raster"],
+            name=[
+                cls.slope,
+                cls.aspect,
+                cls.insol_time,
+                cls.beam_rad,
+                cls.glob_rad,
+                cls.beam_rad_threads,
+                cls.glob_rad_threads,
+                cls.insol_time_threads,
+            ],
+            flags="f",
+        )
 
     def setUp(self):
         self.rsun = SimpleModule('r.sun', elevation=self.elevation, slope=self.slope, aspect=self.aspect,
@@ -34,14 +148,20 @@ class TestRSunMode2(TestCase):
 
     def test_more_threads(self):
         self.assertModule(self.rsun)
-        try:
-            self.rsun.inputs['nprocs'].value = 4
-            self.rsun.outputs.glob_rad = self.glob_rad_threads
-            self.assertModule(self.rsun)
-            self.assertRastersNoDifference(self.glob_rad, self.glob_rad_threads, precision=1e-8)
-        except KeyError:
-            # original version of r.sun without parallel processing
-            return
+        self.rsun.inputs["nprocs"].value = 4
+        self.rsun.outputs.beam_rad = self.beam_rad_threads
+        self.rsun.outputs.glob_rad = self.glob_rad_threads
+        self.rsun.outputs.insol_time = self.insol_time_threads
+        self.assertModule(self.rsun)
+        self.assertRastersNoDifference(
+            self.beam_rad, self.beam_rad_threads, precision=1e-8
+        )
+        self.assertRastersNoDifference(
+            self.glob_rad, self.glob_rad_threads, precision=1e-8
+        )
+        self.assertRastersNoDifference(
+            self.insol_time, self.insol_time_threads, precision=1e-8
+        )
 
     def test_run_outputs(self):
         self.assertModule(self.rsun)
@@ -60,15 +180,17 @@ class TestRSunMode2(TestCase):
 
 
 class TestRSunMode1(TestCase):
-    elevation = 'elevation'
-    elevation_attrib = 'elevation_attrib'
-    elevation_threads = 'elevation_threads'
-    slope = 'rsun_slope'
-    aspect = 'rsun_aspect'
-    beam_rad = 'beam_rad'
-    glob_rad = 'glob_rad'
-    incidout = 'incidout'
-    glob_rad_threads = 'glob_rad_threads'
+    elevation = "elevation"
+    elevation_attrib = "elevation_attrib"
+    elevation_threads = "elevation_threads"
+    slope = "rsun_slope"
+    aspect = "rsun_aspect"
+    beam_rad = "beam_rad"
+    glob_rad = "glob_rad"
+    incidout = "incidout"
+    beam_rad_threads = "beam_rad_threads"
+    glob_rad_threads = "glob_rad_threads"
+    incidout_threads = "incidout_threads"
 
     @classmethod
     def setUpClass(cls):
@@ -79,8 +201,21 @@ class TestRSunMode1(TestCase):
     @classmethod
     def tearDownClass(cls):
         cls.del_temp_region()
-        cls.runModule('g.remove', type=['raster'],
-                      name=[cls.slope, cls.aspect, cls.incidout, cls.beam_rad, cls.glob_rad, cls.glob_rad_threads], flags='f')
+        cls.runModule(
+            "g.remove",
+            type=["raster"],
+            name=[
+                cls.slope,
+                cls.aspect,
+                cls.incidout,
+                cls.beam_rad,
+                cls.glob_rad,
+                cls.beam_rad_threads,
+                cls.glob_rad_threads,
+                cls.incidout_threads,
+            ],
+            flags="f",
+        )
 
     def setUp(self):
         self.rsun = SimpleModule('r.sun', elevation=self.elevation, slope=self.slope, aspect=self.aspect,
@@ -89,14 +224,20 @@ class TestRSunMode1(TestCase):
 
     def test_more_threads(self):
         self.assertModule(self.rsun)
-        try:
-            self.rsun.inputs['nprocs'].value = 4
-            self.rsun.outputs.glob_rad = self.glob_rad_threads
-            self.assertModule(self.rsun)
-            self.assertRastersNoDifference(self.glob_rad, self.glob_rad_threads, precision=1e-8)
-        except KeyError:
-            # original version of r.sun without parallel processing
-            return
+        self.rsun.inputs["nprocs"].value = 4
+        self.rsun.outputs.beam_rad = self.beam_rad_threads
+        self.rsun.outputs.glob_rad = self.glob_rad_threads
+        self.rsun.outputs.incidout = self.incidout_threads
+        self.assertModule(self.rsun)
+        self.assertRastersNoDifference(
+            self.beam_rad, self.beam_rad_threads, precision=1e-8
+        )
+        self.assertRastersNoDifference(
+            self.glob_rad, self.glob_rad_threads, precision=1e-8
+        )
+        self.assertRastersNoDifference(
+            self.incidout, self.incidout_threads, precision=1e-8
+        )
 
     def test_run_outputs(self):
         self.assertModule(self.rsun)