From 769a27c5afc7c4bb2483004c95d59ce45de4852c Mon Sep 17 00:00:00 2001 From: Jayneel Shah <80264736+jayneel-shah18@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:06:05 +0000 Subject: [PATCH 1/4] Added test file for i.biomass module --- imagery/i.biomass/testsuite/test_i_biomass.py | 357 ++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 imagery/i.biomass/testsuite/test_i_biomass.py diff --git a/imagery/i.biomass/testsuite/test_i_biomass.py b/imagery/i.biomass/testsuite/test_i_biomass.py new file mode 100644 index 00000000000..df939e6d5c8 --- /dev/null +++ b/imagery/i.biomass/testsuite/test_i_biomass.py @@ -0,0 +1,357 @@ +import numpy as np +from grass.script import array +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestIBiomass(TestCase): + """Regression tests for the i.biomass GRASS GIS module.""" + + input_rasters = { + "fpar": "test_fpar", + "lightuse_eff": "test_lightuse_eff", + "latitude": "test_latitude", + "dayofyear": "test_dayofyear", + "transmissivity": "test_transmissivity", + "water": "test_water_availability", + } + output_raster = "biomass_output" + + @classmethod + def setUpClass(cls): + """Set up input rasters and configure test environment.""" + cls.use_temp_region() + cls.runModule("g.region", n=10, s=0, e=10, w=0, rows=10, cols=10) + + cls.runModule( + "r.mapcalc", + expression=f"{cls.input_rasters['fpar']} = col() * 0.1", + overwrite=True, + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.input_rasters['lightuse_eff']} = row() * 0.1", + overwrite=True, + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.input_rasters['latitude']} = 45.0", + overwrite=True, + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.input_rasters['dayofyear']} = 150", + overwrite=True, + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.input_rasters['transmissivity']} = 0.75", + overwrite=True, + ) + cls.runModule( + "r.mapcalc", + expression=f"{cls.input_rasters['water']} = 0.8", + overwrite=True, + ) + + @classmethod + def tearDownClass(cls): + """Clean up generated data and reset the region.""" + rasters_to_remove = list(cls.input_rasters.values()) + [cls.output_raster] + cls.runModule( + "g.remove", + type="raster", + name=",".join(rasters_to_remove), + flags="f", + quiet=True, + ) + cls.del_temp_region() + + def test_biomass_with_zero_inputs(self): + """Test the behavior of i.biomass when all inputs are zero.""" + for raster in self.input_rasters.values(): + self.runModule("r.mapcalc", expression=f"{raster} = 0", overwrite=True) + + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + self.assertRasterExists(self.output_raster) + + output_values = array.array(self.output_raster) + self.assertTrue( + np.allclose(output_values, 0), "Biomass raster should be all zeros" + ) + + def test_biomass_linearity(self): + """Test linearity of i.biomass by scaling inputs.""" + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['fpar']} = col() * 0.5", + overwrite=True, + ) + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + self.assertRasterExists(self.output_raster) + + scaled_output_values = array.array(self.output_raster) + + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['fpar']} = col() * 1.0", + overwrite=True, + ) + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + self.assertRasterExists(self.output_raster) + + original_output_values = array.array(self.output_raster) + self.assertTrue( + np.allclose(scaled_output_values * 2, original_output_values, atol=1e-5), + "Linearity property failed for biomass computation", + ) + + def test_biomass_with_extreme_values(self): + """Test the behavior of i.biomass with extreme input values.""" + extreme_values = { + "fpar": [0, 1], + "lightuse_eff": [0, 10], + "latitude": [-90, 90], + "dayofyear": [1, 365], + "transmissivity": [0, 1], + "water": [0, 1], + } + + for key, (min_val, max_val) in extreme_values.items(): + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters[key]} = {min_val}", + overwrite=True, + ) + + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + self.assertRasterExists(self.output_raster) + + output_values_min = array.array(self.output_raster) + self.assertTrue( + np.all(output_values_min >= 0), + "Biomass output should not contain negative values.", + ) + + for key, (min_val, max_val) in extreme_values.items(): + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters[key]} = {max_val}", + overwrite=True, + ) + + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + self.assertRasterExists(self.output_raster) + + output_values_max = array.array(self.output_raster) + self.assertTrue( + np.all(np.isfinite(output_values_max)), + "Biomass output contains non-finite values.", + ) + + def test_biomass_large_processing(self): + """Test processing large input rasters with i.biomass.""" + self.runModule("g.region", n=90, s=-90, e=180, w=-180, rows=1000, cols=1000) + + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['fpar']} = col() * 0.01", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['lightuse_eff']} = row() * 0.01", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['latitude']} = 45.0", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['dayofyear']} = 150", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['transmissivity']} = 0.75", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['water']} = 0.8", + overwrite=True, + ) + + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + + self.assertRasterExists(self.output_raster) + + output_values = array.array(self.output_raster) + self.assertTrue( + np.isfinite(output_values).all(), + "Output contains non-finite values for large input rasters.", + ) + + def test_biomass_latitude_dependency(self): + """Test that biomass values vary reasonably with latitude.""" + + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['latitude']} = 0", + overwrite=True, + ) + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + equatorial_output = array.array(self.output_raster) + + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['latitude']} = 90", + overwrite=True, + ) + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + polar_output = array.array(self.output_raster) + + self.assertTrue( + np.mean(equatorial_output) > np.mean(polar_output), + "Biomass at equatorial regions should generally exceed" + "biomass at polar regions.", + ) + + def test_biomass_ecological_range(self): + """Test that biomass values fall within an expected ecological range.""" + + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['fpar']} = col() * 0.5", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['lightuse_eff']} = row() * 0.1", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['latitude']} = 45.0", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['dayofyear']} = 150", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['transmissivity']} = 0.75", + overwrite=True, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_rasters['water']} = 0.8", + overwrite=True, + ) + + self.assertModule( + "i.biomass", + fpar=self.input_rasters["fpar"], + lightuse_efficiency=self.input_rasters["lightuse_eff"], + latitude=self.input_rasters["latitude"], + dayofyear=self.input_rasters["dayofyear"], + transmissivity_singleway=self.input_rasters["transmissivity"], + water_availability=self.input_rasters["water"], + output=self.output_raster, + overwrite=True, + ) + self.assertRasterExists(self.output_raster) + + output_values = array.array(self.output_raster) + self.assertTrue( + np.all((output_values >= 0) & (output_values <= 1000)), + "Biomass output values should be within the range [0, 1000].", + ) + + +if __name__ == "__main__": + test() From 92496572fb868a395cae929f4e194e7f5aeb7acc Mon Sep 17 00:00:00 2001 From: Jayneel Shah <80264736+jayneel-shah18@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:27:40 -0500 Subject: [PATCH 2/4] "Trigger Workflow" --- imagery/i.biomass/testsuite/test.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 imagery/i.biomass/testsuite/test.txt diff --git a/imagery/i.biomass/testsuite/test.txt b/imagery/i.biomass/testsuite/test.txt new file mode 100644 index 00000000000..8c1eb2139c7 --- /dev/null +++ b/imagery/i.biomass/testsuite/test.txt @@ -0,0 +1 @@ +To trigger workflow From 7561233ee1cc8329f1d18ccf5ab8c6513303ef46 Mon Sep 17 00:00:00 2001 From: Jayneel Shah <80264736+jayneel-shah18@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:36:15 +0000 Subject: [PATCH 3/4] Added test file for i.emissivity module --- .../testsuite/test_i_emissivity.py | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 imagery/i.emissivity/testsuite/test_i_emissivity.py diff --git a/imagery/i.emissivity/testsuite/test_i_emissivity.py b/imagery/i.emissivity/testsuite/test_i_emissivity.py new file mode 100644 index 00000000000..04e17ace1e2 --- /dev/null +++ b/imagery/i.emissivity/testsuite/test_i_emissivity.py @@ -0,0 +1,187 @@ +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestIEmissivity(TestCase): + """Test case for the i.emissivity module.""" + + input_raster = "ndvi_test_map" + output_raster = "emissivity_test_output" + reference_raster = "emissivity_reference_map" + temp_rasters = [] + + @classmethod + def setUpClass(cls): + """Set up an input raster and configure test environment.""" + cls.use_temp_region() + + cls.runModule( + "g.region", + n=10, + s=0, + e=10, + w=0, + res=1, + flags="p", + ) + + cls.runModule( + "r.mapcalc", + expression=(f"{cls.input_raster} = if(row() <= 5, 0.2, 0.7)"), + overwrite=True, + ) + cls.temp_rasters.append(cls.input_raster) + + cls.runModule( + "r.mapcalc", + expression=(f"{cls.reference_raster} = if(row() <= 5, 0.9, 0.95)"), + overwrite=True, + ) + cls.temp_rasters.append(cls.reference_raster) + + @classmethod + def tearDownClass(cls): + """Clean up generated data and reset the region.""" + cls.del_temp_region() + for raster in cls.temp_rasters + [cls.output_raster]: + cls.runModule("g.remove", type="raster", name=raster, flags="f") + + def test_emissivity_ndvi_range(self): + """Test with NDVI values in the valid range.""" + self.runModule( + "r.mapcalc", + expression=( + f"{self.input_raster} = if(col() == 1, 0.16, " + f"if(col() == 2, 0.74, 0.45))" + ), + overwrite=True, + ) + + self.assertModule( + "i.emissivity", + input=self.input_raster, + output=self.output_raster, + overwrite=True, + ) + + self.runModule( + "r.mapcalc", + expression=( + f"{self.reference_raster} = if({self.input_raster} < 0.16, 0.97, " + f"if({self.input_raster} > 0.74, 0.99, " + f"0.97 + 0.003 * {self.input_raster}))" + ), + overwrite=True, + ) + + self.runModule( + "r.mapcalc", + expression=( + f"diff_raster = abs({self.output_raster} - {self.reference_raster})" + ), + overwrite=True, + ) + self.assertModule("r.univar", map="diff_raster", flags="e") + self.assertRasterMinMax("diff_raster", 0, 0.05) + + def test_complex_mask(self): + """Test with a complex mask involving irregular shape and regions.""" + self.runModule( + "r.mapcalc", + expression=("MASK = if((row() + col()) % 3 == 0 || row() < 3, 1, null())"), + overwrite=True, + ) + self.temp_rasters.append("MASK") + + self.assertModule( + "i.emissivity", + input=self.input_raster, + output=self.output_raster, + overwrite=True, + ) + + self.runModule( + "r.mapcalc", + expression=( + f"masked_reference = if(isnull(MASK), null(), {self.reference_raster})" + ), + overwrite=True, + ) + self.temp_rasters.append("masked_reference") + + self.runModule( + "r.mapcalc", + expression=(f"diff_raster = abs({self.output_raster} - masked_reference)"), + overwrite=True, + ) + self.temp_rasters.append("diff_raster") + + self.assertModule("r.univar", map="diff_raster", flags="e") + self.assertRasterMinMax("diff_raster", 0, 0.05) + + self.runModule("r.mask", flags="r") + + def test_region_resolution(self): + """Test the module behaviour with different region resolutions.""" + for res in [1, 0.1]: + with self.subTest(res=res): + self.runModule( + "g.region", + n=10, + s=0, + e=10, + w=0, + res=res, + ) + self.runModule( + "r.mapcalc", + expression=f"{self.input_raster} = 0.6", + overwrite=True, + ) + self.assertModule( + "i.emissivity", + input=self.input_raster, + output=self.output_raster, + overwrite=True, + ) + self.assertRasterMinMax(self.output_raster, 0, 1) + + def test_large_raster_performance(self): + """Assess performance with a larger raster.""" + self.runModule("g.region", n=90, s=-90, e=180, w=-180, rows=1000, cols=1000) + self.runModule( + "r.mapcalc", + expression=f"{self.input_raster} = col()", + overwrite=True, + ) + + self.assertModule( + "i.emissivity", + input=self.input_raster, + output=self.output_raster, + overwrite=True, + ) + + self.assertRasterExists(self.output_raster) + + def test_extreme_ndvi_values(self): + """Test with extreme NDVI values beyond the valid range.""" + self.runModule( + "r.mapcalc", + expression=( + f"{self.input_raster} = if(col() == 1, -1, " + f"if(col() == 2, 1.2, 0.5))" + ), + overwrite=True, + ) + self.assertModule( + "i.emissivity", + input=self.input_raster, + output=self.output_raster, + overwrite=True, + ) + self.assertRasterMinMax(self.output_raster, 0, 1) + + +if __name__ == "__main__": + test() From ae25ae1655e82155130bf94f04ae7ac925ef5eef Mon Sep 17 00:00:00 2001 From: Jayneel Shah <80264736+jayneel-shah18@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:54:51 -0500 Subject: [PATCH 4/4] Revert "Added test file for i.emissivity module" --- .../testsuite/test_i_emissivity.py | 187 ------------------ 1 file changed, 187 deletions(-) delete mode 100644 imagery/i.emissivity/testsuite/test_i_emissivity.py diff --git a/imagery/i.emissivity/testsuite/test_i_emissivity.py b/imagery/i.emissivity/testsuite/test_i_emissivity.py deleted file mode 100644 index 04e17ace1e2..00000000000 --- a/imagery/i.emissivity/testsuite/test_i_emissivity.py +++ /dev/null @@ -1,187 +0,0 @@ -from grass.gunittest.case import TestCase -from grass.gunittest.main import test - - -class TestIEmissivity(TestCase): - """Test case for the i.emissivity module.""" - - input_raster = "ndvi_test_map" - output_raster = "emissivity_test_output" - reference_raster = "emissivity_reference_map" - temp_rasters = [] - - @classmethod - def setUpClass(cls): - """Set up an input raster and configure test environment.""" - cls.use_temp_region() - - cls.runModule( - "g.region", - n=10, - s=0, - e=10, - w=0, - res=1, - flags="p", - ) - - cls.runModule( - "r.mapcalc", - expression=(f"{cls.input_raster} = if(row() <= 5, 0.2, 0.7)"), - overwrite=True, - ) - cls.temp_rasters.append(cls.input_raster) - - cls.runModule( - "r.mapcalc", - expression=(f"{cls.reference_raster} = if(row() <= 5, 0.9, 0.95)"), - overwrite=True, - ) - cls.temp_rasters.append(cls.reference_raster) - - @classmethod - def tearDownClass(cls): - """Clean up generated data and reset the region.""" - cls.del_temp_region() - for raster in cls.temp_rasters + [cls.output_raster]: - cls.runModule("g.remove", type="raster", name=raster, flags="f") - - def test_emissivity_ndvi_range(self): - """Test with NDVI values in the valid range.""" - self.runModule( - "r.mapcalc", - expression=( - f"{self.input_raster} = if(col() == 1, 0.16, " - f"if(col() == 2, 0.74, 0.45))" - ), - overwrite=True, - ) - - self.assertModule( - "i.emissivity", - input=self.input_raster, - output=self.output_raster, - overwrite=True, - ) - - self.runModule( - "r.mapcalc", - expression=( - f"{self.reference_raster} = if({self.input_raster} < 0.16, 0.97, " - f"if({self.input_raster} > 0.74, 0.99, " - f"0.97 + 0.003 * {self.input_raster}))" - ), - overwrite=True, - ) - - self.runModule( - "r.mapcalc", - expression=( - f"diff_raster = abs({self.output_raster} - {self.reference_raster})" - ), - overwrite=True, - ) - self.assertModule("r.univar", map="diff_raster", flags="e") - self.assertRasterMinMax("diff_raster", 0, 0.05) - - def test_complex_mask(self): - """Test with a complex mask involving irregular shape and regions.""" - self.runModule( - "r.mapcalc", - expression=("MASK = if((row() + col()) % 3 == 0 || row() < 3, 1, null())"), - overwrite=True, - ) - self.temp_rasters.append("MASK") - - self.assertModule( - "i.emissivity", - input=self.input_raster, - output=self.output_raster, - overwrite=True, - ) - - self.runModule( - "r.mapcalc", - expression=( - f"masked_reference = if(isnull(MASK), null(), {self.reference_raster})" - ), - overwrite=True, - ) - self.temp_rasters.append("masked_reference") - - self.runModule( - "r.mapcalc", - expression=(f"diff_raster = abs({self.output_raster} - masked_reference)"), - overwrite=True, - ) - self.temp_rasters.append("diff_raster") - - self.assertModule("r.univar", map="diff_raster", flags="e") - self.assertRasterMinMax("diff_raster", 0, 0.05) - - self.runModule("r.mask", flags="r") - - def test_region_resolution(self): - """Test the module behaviour with different region resolutions.""" - for res in [1, 0.1]: - with self.subTest(res=res): - self.runModule( - "g.region", - n=10, - s=0, - e=10, - w=0, - res=res, - ) - self.runModule( - "r.mapcalc", - expression=f"{self.input_raster} = 0.6", - overwrite=True, - ) - self.assertModule( - "i.emissivity", - input=self.input_raster, - output=self.output_raster, - overwrite=True, - ) - self.assertRasterMinMax(self.output_raster, 0, 1) - - def test_large_raster_performance(self): - """Assess performance with a larger raster.""" - self.runModule("g.region", n=90, s=-90, e=180, w=-180, rows=1000, cols=1000) - self.runModule( - "r.mapcalc", - expression=f"{self.input_raster} = col()", - overwrite=True, - ) - - self.assertModule( - "i.emissivity", - input=self.input_raster, - output=self.output_raster, - overwrite=True, - ) - - self.assertRasterExists(self.output_raster) - - def test_extreme_ndvi_values(self): - """Test with extreme NDVI values beyond the valid range.""" - self.runModule( - "r.mapcalc", - expression=( - f"{self.input_raster} = if(col() == 1, -1, " - f"if(col() == 2, 1.2, 0.5))" - ), - overwrite=True, - ) - self.assertModule( - "i.emissivity", - input=self.input_raster, - output=self.output_raster, - overwrite=True, - ) - self.assertRasterMinMax(self.output_raster, 0, 1) - - -if __name__ == "__main__": - test()