Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r.surf.fractal: Added seed option and -s flag to module #3480

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
27 changes: 27 additions & 0 deletions raster/r.surf.fractal/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ int main(int argc, char *argv[])
struct Option *rast_out; /* Structure for output raster */
struct Option *frac_dim; /* Fractal dimension of surface. */
struct Option *num_images; /* Number of images to produce. */
struct Option *seed;

long seed_value;
char *seedptr;

G_gisinit(argv[0]); /* Link with GRASS interface. */

Expand All @@ -54,9 +58,32 @@ int main(int argc, char *argv[])
num_images->required = NO;
num_images->answer = "0";

seed = G_define_option();
seed->key = "seed";
seed->type = TYPE_INTEGER;
seed->required = NO;
seed->label = _("Seed for random number generator");
seed->description = _("The same seed can be used to obtain same results"
" or random seed can be generated by other means.");

if (G_parser(argc, argv)) /* Performs the prompting for */
exit(EXIT_FAILURE); /* keyboard input. */

/****** INITIALISE RANDOM NUMBER GENERATOR ******/
if (seed->answer) {
seed_value = atol(seed->answer);
G_srand48(seed_value);
G_verbose_message(_("Read random seed from %s option: %ld"), seed->key,
seed_value);
}
else {
/* default as it used to be */
seed_value = G_srand48_auto();
G_verbose_message(_("Warning set option seed. Generated "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the first sentence and the "-s"

"random seed (-s): %ld"),
seed_value);
}

rast_out_name = rast_out->answer;
sscanf(frac_dim->answer, "%lf", &H);
H = 3.0 - H;
Expand Down
3 changes: 0 additions & 3 deletions raster/r.surf.fractal/spec_syn.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ int specsyn(double *data[2], /* Array holding complex data to transform. */
double phase, rad, /* polar coordinates of Fourier coeff. */
*temp[2];

/* You can set GRASS_RANDOM_SEED for repeatability */
G_math_srand_auto(); /* Reset random number generator. */

temp[0] = (double *)G_malloc(nn * nn * sizeof(double));
temp[1] = (double *)G_malloc(nn * nn * sizeof(double));

Expand Down
110 changes: 110 additions & 0 deletions raster/r.surf.fractal/testsuite/test_r_surf_fractal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env python3

"""
MODULE: Test of r.surf.fractal

AUTHOR(S): Shashank Shekhar Singh <shashankshekharsingh1205 gmail com>

PURPOSE: Test fractal surface generation with r.surf.fractal module

COPYRIGHT: (C) 2024 by Shashank Shekhar Singh and the GRASS Development Team

This program is free software under the GNU General Public
License (>=v2). Read the file COPYING that comes with GRASS
for details.
"""

import os

Check failure on line 17 in raster/r.surf.fractal/testsuite/test_r_surf_fractal.py

View workflow job for this annotation

GitHub Actions / Python Code Quality Checks (ubuntu-22.04)

Ruff (F401)

raster/r.surf.fractal/testsuite/test_r_surf_fractal.py:17:8: F401 `os` imported but unused

import grass.script as gs

Check failure on line 19 in raster/r.surf.fractal/testsuite/test_r_surf_fractal.py

View workflow job for this annotation

GitHub Actions / Python Code Quality Checks (ubuntu-22.04)

Ruff (F401)

raster/r.surf.fractal/testsuite/test_r_surf_fractal.py:19:24: F401 `grass.script` imported but unused
Shashankss1205 marked this conversation as resolved.
Show resolved Hide resolved

from grass.gunittest.case import TestCase
from grass.gunittest.main import test


class FractalTestCase(TestCase):
"""Test case for r.surf.fractal"""

# Raster map name to be used as output
output = "fractal_result"
marisn marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def setUpClass(cls):
"""Set up necessary environment"""
# Set up temporary computational region
cls.use_temp_region()
# Only 100,000,000 seem to reasonably (not 100%) ensure that all values
# are generated, so exceeding of ranges actually shows up.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of ranges you are referring to? I didn't see any range check. Usually a tiny computational region suffices to save on CI execution time.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind helping me with the test suite please, because I haven't previously worked with them and therefore I was struggling with how to write them.

Thanks @marisn

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See for example test for r.random.cells and test documentation. Run r.surf.fractal first with certain fixed seed and save the raster statistics (computed with r.univar) for reference in the test. You can then use assertRasterFitsUnivar to ensure the test-generated raster with the same fixed seed will always give the same values. This type of test is not necessarily testing the output is correct, but it helps detect any regressions in the tool in the future and also that with a fixed seed you always get the same results.

cls.runModule("g.region", rows=10000, cols=10000)

@classmethod
def tearDownClass(cls):
"""Clean up temporary environment"""
cls.del_temp_region()

def tearDown(self):
"""Remove the output created from the module after each test"""
self.runModule("g.remove", flags="f", type="raster", name=[self.output])

def test_default_settings(self):
"""Test with default settings"""
self.assertModule("r.surf.fractal", output=self.output)

def test_fractal_dimension(self):
"""Test with specified fractal dimension"""
fractal_dim = 2.05 # Example fractal dimension value
self.assertModule(
"r.surf.fractal",
dimension=fractal_dim,
output=self.output,
)

def test_num_images(self):
"""Test with specified number of intermediate images"""
num_images = 5 # Example number of intermediate images
self.assertModule(
"r.surf.fractal",
number=num_images,
output=self.output,
)

def test_random_seed(self):
"""Test with specified random seed"""
seed_value = 12345 # Example random seed value
self.assertModule(
"r.surf.fractal",
seed=seed_value,
output=self.output,
)

def test_generate_random_seed(self):
"""Test with flag to generate random seed"""
self.assertModule(
"r.surf.fractal",
flags="s",
output=self.output,
)

def test_invalid_fractal_dimension(self):
"""Test with invalid fractal dimension"""
invalid_dim = 1.5 # Example invalid fractal dimension value
# Ensure that the module fails with an error message
self.assertModuleFail(
"r.surf.fractal",
dimension=invalid_dim,
output=self.output,
)

def test_invalid_seed_value(self):
"""Test with invalid random seed value"""
invalid_seed = "abc" # Example invalid random seed value
# Ensure that the module fails with an error message
self.assertModuleFail(
"r.surf.fractal",
seed=invalid_seed,
output=self.output,
)


if __name__ == "__main__":
test()
Loading