diff --git a/lib/ants/cli/ancil_2anc.py b/lib/ants/cli/ancil_2anc.py index 60deaa1..48a6f05 100755 --- a/lib/ants/cli/ancil_2anc.py +++ b/lib/ants/cli/ancil_2anc.py @@ -37,16 +37,16 @@ import iris -def load_data(source): +def load_data(source, ignore_metadata_files): with warnings.catch_warnings(): warnings.filterwarnings( "ignore", "NetCDF default loading", iris._deprecation.IrisDeprecation ) - cubes = ants.io.load.load(source) + cubes = ants.io.load.load(source, ignore_metadata_files=ignore_metadata_files) return cubes -def main(source_path, output_path, grid_staggering, netcdf_only): +def main(source_path, output_path, grid_staggering, netcdf_only, ignore_metadata_files): """ Convert specified source file to an ancillary. @@ -66,7 +66,7 @@ def main(source_path, output_path, grid_staggering, netcdf_only): written to an ancillary. """ - source_cubes = load_data(source_path) + source_cubes = load_data(source_path, ignore_metadata_files) if grid_staggering is not None: for source_cube in source_cubes: source_cube.attributes["grid_staggering"] = grid_staggering @@ -98,6 +98,7 @@ def cli_interface(): args.output, args.grid_staggering, args.netcdf_only, + args.ignore_metadata_files, ) diff --git a/lib/ants/cli/ancil_fill_n_merge.py b/lib/ants/cli/ancil_fill_n_merge.py index 9bb7cea..4776891 100755 --- a/lib/ants/cli/ancil_fill_n_merge.py +++ b/lib/ants/cli/ancil_fill_n_merge.py @@ -25,6 +25,7 @@ def load_data( land_fraction_threshold=None, begin=None, end=None, + ignore_metadata_files=None, ): """ Load the necessary data for performing a merge and fill operation. @@ -56,12 +57,16 @@ def load_data( respectively. """ - primary_cubes = ants.io.load.load(primary_source) + primary_cubes = ants.io.load.load( + primary_source, ignore_metadata_files=ignore_metadata_files + ) if begin is not None: primary_cubes = create_time_constrained_cubes(primary_cubes, begin, end) alternate_cubes = None if alternate_source: - alternate_cubes = ants.io.load.load(alternate_source) + alternate_cubes = ants.io.load.load( + alternate_source, ignore_metadata_files=ignore_metadata_files + ) if begin is not None: alternate_cubes = create_time_constrained_cubes(alternate_cubes, begin, end) @@ -96,6 +101,7 @@ def main( end, netcdf_only, search_method, + ignore_metadata_files, ): """ Perform merge and fill operation on the provided sources. @@ -159,6 +165,7 @@ def main( land_fraction_threshold, begin, end, + ignore_metadata_files, ) result = primary_cubes @@ -251,6 +258,7 @@ def cli_interface(): end=args.end, netcdf_only=args.netcdf_only, search_method=args.search_method, + ignore_metadata_files=args.ignore_metadata_files, ) diff --git a/lib/ants/cli/ancil_general_regrid.py b/lib/ants/cli/ancil_general_regrid.py index 7f6bd86..dd84790 100755 --- a/lib/ants/cli/ancil_general_regrid.py +++ b/lib/ants/cli/ancil_general_regrid.py @@ -42,12 +42,17 @@ def load_data( land_fraction_threshold=None, begin=None, end=None, + ignore_metadata_files=None, ): - source_cubes = ants.io.load.load(source) + source_cubes = ants.io.load.load( + source, ignore_metadata_files=ignore_metadata_files + ) if begin is not None: source_cubes = create_time_constrained_cubes(source_cubes, begin, end) if target_grid: - target_cube = ants.io.load.load_grid(target_grid) + target_cube = ants.io.load.load_grid( + target_grid, ignore_metadata_files=ignore_metadata_files + ) else: target_cube = ants.io.load.load_landsea_mask( target_landseamask, land_fraction_threshold @@ -76,6 +81,7 @@ def main( save_ukca, netcdf_only, search_method, + ignore_metadata_files, ): """ General regrid application top level call function. @@ -118,6 +124,9 @@ def main( provided source(s) consistent with the provided land sea mask. This should only be provided if a target land sea mask is also provided via target_lsm_path. + ignore_metadata_files : :obj:`bool`, optional + When set to True, files containing metadata will not be loaded alongside data + and added as attributes to the cube. Returns ------- @@ -132,6 +141,7 @@ def main( land_fraction_threshold, begin, end, + ignore_metadata_files, ) if ants.utils.cube._is_ugrid(target_cube): raise ValueError( @@ -210,6 +220,7 @@ def cli_interface(): args.save_ukca, args.netcdf_only, args.search_method, + args.ignore_metadata_files, ) diff --git a/lib/ants/command_parse.py b/lib/ants/command_parse.py index 2a3a01c..3a58ed3 100644 --- a/lib/ants/command_parse.py +++ b/lib/ants/command_parse.py @@ -33,6 +33,11 @@ def __init__(self, target_lsm=False, target_grid=False, time_constraints=False): sources ... Source data path(s). --output , -o Output filepath --ants-config Configuration path. + --ignore-metadata-files Stops the application from + searching for metadata files + with the same name as the + source and loading them + alongside the source. Additionally, there are standardised optional arguments and these are activated by passing the relevant keyword argument. See 'Parameters' @@ -171,6 +176,13 @@ def __init__(self, target_lsm=False, target_grid=False, time_constraints=False): help="Only write out a netCDF file.", required=False, ) + self.add_argument( + "--ignore-metadata-files", + action="store_true", + help="Turn off the automatic loading of metadata files alongside source " + "files", + required=False, + ) if time_constraints: self.add_argument( "--begin", diff --git a/lib/ants/io/load.py b/lib/ants/io/load.py index 9c53786..b2a4107 100644 --- a/lib/ants/io/load.py +++ b/lib/ants/io/load.py @@ -56,6 +56,7 @@ """ import copy +import glob import warnings from contextlib import contextmanager from functools import wraps @@ -231,18 +232,22 @@ def load_landsea_mask(filename, land_threshold=None): """ try: # Is it a landsea mask field? - lbm = ants.io.load.load_cube(filename, "land_binary_mask") + lbm = ants.io.load.load_cube( + filename, "land_binary_mask", ignore_metadata_files=True + ) lbm = lbm.copy(lbm.data.astype("bool", copy=False)) except iris.exceptions.ConstraintMismatchError: try: # Is it a land fraction field? - land_fraction = ants.io.load.load_cube(filename, "vegetation_area_fraction") + land_fraction = ants.io.load.load_cube( + filename, "vegetation_area_fraction", ignore_metadata_files=True + ) lbm = land_fraction.copy(land_fraction.data > land_threshold) lbm.rename("land_binary_mask") except iris.exceptions.ConstraintMismatchError: # It looks like we are wanting to extract a landsea mask from some # other field. - cube = ants.io.load.load(filename)[0] + cube = ants.io.load.load(filename, ignore_metadata_files=True)[0] y = cube.coord(axis="y") x = cube.coord(axis="x") cube = cube.slices((y, x)).next() @@ -355,6 +360,21 @@ def load_function(*args, **kwargs): "iris.FUTURE.datum_support flag.", FutureWarning, ) + ignore_metadata_files = False + if "ignore_metadata_files" in kwargs: + ignore_metadata_files = kwargs.pop("ignore_metadata_files") + if not ignore_metadata_files: + # Do the handling for each way a user callback can be passed in through + # iris + user_callback = None + if len(args) == 3: + user_callback = args[2] + else: + if "callback" in kwargs: + user_callback = kwargs.pop("callback") + args, kwargs = _add_callback( + _CallbackMetadata(user_callback), *args, **kwargs + ) # Use context manager to avoid permanently modifying iris behaviour. with ants_format_agent(): cubes = func(*args, **kwargs) @@ -370,6 +390,92 @@ def load_function(*args, **kwargs): return load_function +def _add_callback(callback, *args, **kwargs): + """ + Adds both the ants callback and the user provided callback (if any) to the + load. + """ + args = list(args) + if len(args) == 1: + kwargs["callback"] = callback + elif len(args) == 3: + args[2] = callback + args = tuple(args) + return args, kwargs + + +class _CallbackMetadata(object): + """Callback for collecting metadata from sidecar files. + + This callback will load additional metadata files with the naming convention: + filename. and append the contents of those files to the cube + attributes. + """ + + def __init__(self, user_callback): + self._user_callback = user_callback + + def __call__(self, cube, field, filename): + """ + The method that runs when iris runs the callback. Collects the filenames and + will run the user callback + """ + if type(filename) is list: + filename = filename[0] + metadata_filenames = "".join([filename, ".*"]) + metadata_files = glob.glob(metadata_filenames) + if metadata_files != []: + self._retrieve_metadata(metadata_files, cube) + if self._user_callback is not None: + self._user_callback(cube, field, filename) + + def _retrieve_metadata(self, metadata_files, cube): + """ + Reads in the contents of each file and adds an attribute to the cube with the + name of the file. + + Parameters + ---------- + metadata_files: list + The list of filenames that contain metadata for the cube. + cube : :class:`iris.cube.Cube` + The cube being loaded. + + """ + valid_metadata_names = ["license", "attribution", "restrictions"] + other_license = ["lisense", "licence", "lisence"] + for metadata_file in metadata_files: + file_name_splits = str(metadata_file).split(".") + attribute_name = file_name_splits[-1] + if attribute_name in other_license: + warnings.warn( + f"The attribute name {attribute_name} has been changed to " + "license, in line with ANTS working practices.", + category=UserWarning, + ) + attribute_name = "license" + if attribute_name not in valid_metadata_names: + warnings.warn( + f"Attribute {attribute_name} is not a valid metadata file " + "name. Accepted metadata names are license, attribution " + "and restrictions.", + category=UserWarning, + ) + else: + if attribute_name in cube.attributes: + raise AttributeError( + f"The {attribute_name} is already an attribute on the " + "cube. To ignore metadata files, use the " + "--ignore-metadata-files flag." + ) + open_file = open(metadata_file, "r") + metadata = open_file.readlines() + open_file.close() + cube.attributes[attribute_name] = metadata + with open("written_license.txt", "a") as file: + file.write("".join(metadata)) + + def load_cube(*args, **kwargs): """ Loads a single cube. diff --git a/lib/ants/tests/command_parse/test_integration.py b/lib/ants/tests/command_parse/test_integration.py index 31105ab..50c11e0 100644 --- a/lib/ants/tests/command_parse/test_integration.py +++ b/lib/ants/tests/command_parse/test_integration.py @@ -30,6 +30,7 @@ def setUp(self): self.addCleanup(patch.stop) def test_default_args(self): + """Tests that the default arguments are as expected.""" new = [ "program", "/path/to/source", @@ -53,6 +54,7 @@ def path_check(filename, **kwargs): output="/path/to/output", sources=["/path/to/source"], netcdf_only=False, + ignore_metadata_files=False, ) self.assertFalse(self.mock_config.called) self.assertEqual(args, target_args) @@ -69,6 +71,7 @@ def test_add_args(self): output="/path/to/output", ants_config=None, netcdf_only=False, + ignore_metadata_files=False, new_arg="new_arg_value", ) self.assertEqual(args, target_args) @@ -150,6 +153,7 @@ def test_invalid_output_same_file(self): self.mock_dirpath_writeable.assert_called_once() def test_time_constraint_args(self): + """Tests that using time constraint arguments will be correctly parsed.""" new = [ "program", "/path/to/source", @@ -176,11 +180,13 @@ def test_time_constraint_args(self): begin=2016, end=2021, netcdf_only=False, + ignore_metadata_files=False, ) self.assertFalse(self.mock_config.called) self.assertEqual(args, target_args) def test_time_constraint_flags(self): + """Tests that using time constraint flags will be correctly parsed.""" new = [ "program", "/path/to/source", @@ -207,6 +213,7 @@ def test_time_constraint_flags(self): begin=1990, end=1996, netcdf_only=False, + ignore_metadata_files=False, ) self.assertFalse(self.mock_config.called) self.assertEqual(args, target_args) @@ -265,3 +272,30 @@ def test_both_time_constraints_exist(self): parser = AntsArgParser(target_lsm=True, time_constraints=True) with self.assertRaises(exceptions.TimeConstraintMissingException): parser.parse_args() + + def test_set_ignore_metadata_files_flag(self): + "Tests that ignore_metadata_files will be set to True when passed in." + new = [ + "program", + "/path/to/source", + "--target-lsm", + "/path/to/lsm", + "-o", + "/path/to/output", + "--ignore-metadata-files", + ] + with mock.patch("sys.argv", new=new): + parser = AntsArgParser(target_lsm=True) + args = parser.parse_args() + + target_args = argparse.Namespace( + ants_config=None, + land_threshold=None, + target_lsm="/path/to/lsm", + output="/path/to/output", + sources=["/path/to/source"], + netcdf_only=False, + ignore_metadata_files=True, + ) + self.assertFalse(self.mock_config.called) + self.assertEqual(args, target_args) diff --git a/lib/ants/tests/io/load/test_CallbackMetadata.py b/lib/ants/tests/io/load/test_CallbackMetadata.py new file mode 100644 index 0000000..5bc3d5d --- /dev/null +++ b/lib/ants/tests/io/load/test_CallbackMetadata.py @@ -0,0 +1,159 @@ +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of ANTS and is released under the BSD 3-Clause license. +# See LICENSE.txt in the root of the repository for full licensing details. +""" +Includes tests for end-to-end functionality of CallbackMetadata as well as calling the +class directly. +""" + +import unittest.mock as mock +import warnings + +import ants.io.load +import iris +import pytest + + +def test_metadata_files_added_to_attributes(tmp_path): + """Tests that metadata files are found and added to the cube's attributes.""" + # The text that would be in a license file + license_text = """This is the license of the cube. + + It should be preserved and added to the cube when loaded. + """ + # How the text should look while stored in an array + loaded_license = [ + "This is the license of the cube.\n", + "\n", + " It should be preserved and added to the cube when loaded.\n", + " ", + ] + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.pp" + iris.save(test_cube, str(temporary_cube_path)) + temporary_license_path = tmp_path / "cube_attribute.pp.license" + temporary_license_path.write_text(license_text, encoding="utf-8") + loaded_test_cube = ants.io.load.load_cube(temporary_cube_path) + assert loaded_test_cube.attributes["license"] == loaded_license + + +def test_no_metadata_loaded(tmp_path): + """Tests that metadata files are not loaded when the option is turned off.""" + # The text that would be in a license file + license_text = """This is the license of the cube. + + It should be preserved and added to the cube when loaded. + """ + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.pp" + iris.save(test_cube, str(temporary_cube_path)) + temporary_license_path = tmp_path / "cube_attribute.pp.license" + temporary_license_path.write_text(license_text, encoding="utf-8") + loaded_test_cube = ants.io.load.load_cube( + temporary_cube_path, ignore_metadata_files=True + ) + with pytest.raises(KeyError): + loaded_test_cube.attributes["license"] + + +def test_user_callback_added(): + """Test that on ititialisation, the user's function will be set.""" + + def user_callback(cube, field, filename): + print("a user's callback, passed in") + + class_instance = ants.io.load._CallbackMetadata(user_callback) + assert class_instance._user_callback == user_callback + + +def test_args_parsed_correctly_with_kwargs(tmp_path): + """Tests that when passed a callback using a keyword argument, + the callback is parsed correctly.""" + mock_callback = mock.Mock() + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.pp" + iris.save(test_cube, str(temporary_cube_path)) + ants.io.load.load_cube(temporary_cube_path, callback=mock_callback) + mock_callback.assert_called() + + +def test_args_parsed_correctly_with_positional_args(tmp_path): + """Tests that when passed a callback using a keyword argument, + the callback is parsed correctly.""" + mock_callback = mock.Mock() + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.pp" + iris.save(test_cube, str(temporary_cube_path)) + ants.io.load.load_cube(temporary_cube_path, None, mock_callback) + mock_callback.assert_called() + + +def test_existing_metadata_error(tmp_path): + """Tests that an error is raised when a cube has existing metadata attributes.""" + license_text = "a license" + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + test_cube.attributes["license"] = "an existing license" + temporary_cube_path = tmp_path / "cube_attribute.nc" + iris.save(test_cube, str(temporary_cube_path)) + temporary_license_path = tmp_path / "cube_attribute.nc.license" + temporary_license_path.write_text(license_text, encoding="utf-8") + error_message = ( + "The license is already an attribute on the cube. To ignore " + "metadata files, use the --ignore-metadata-files flag." + ) + + with pytest.raises(AttributeError, match=error_message): + ants.io.load.load_cube(temporary_cube_path) + + +def test_misspelt_license_warning(tmp_path): + """Tests that different spellings for license will raise a warning.""" + license_text = "a license" + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.nc" + iris.save(test_cube, str(temporary_cube_path)) + temporary_license_path = tmp_path / "cube_attribute.nc.lisense" + temporary_license_path.write_text(license_text, encoding="utf-8") + + warning_message = ( + "The attribute name lisense has been changed to license, in " + "line with ANTS working practices." + ) + with pytest.raises(UserWarning, match=warning_message): + ants.io.load.load_cube(temporary_cube_path) + + +def test_misspelt_license_added(tmp_path): + """Tests that a different spelling of license will add a license attribute.""" + license_text = "a license" + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.nc" + iris.save(test_cube, str(temporary_cube_path)) + temporary_license_path = tmp_path / "cube_attribute.nc.lisense" + temporary_license_path.write_text(license_text, encoding="utf-8") + # ignore warning that will be raised + warning_message = ( + "The attribute name lisense has been changed to license, in " + "line with ANTS working practices." + ) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message=warning_message, category=UserWarning) + loaded_cube = ants.io.load.load_cube(temporary_cube_path) + assert loaded_cube.attributes["license"] == ["a license"] + + +def test_invalid_metadata_name(tmp_path): + """Tests that an invalid metadata name will not be added as an attribute.""" + test_cube = ants.tests.stock.geodetic(shape=(2, 2)) + temporary_cube_path = tmp_path / "cube_attribute.nc" + iris.save(test_cube, str(temporary_cube_path)) + temporary_license_path = tmp_path / "cube_attribute.nc.invalid-name" + temporary_license_path.write_text(" ", encoding="utf-8") + + warning_message = ( + "Attribute invalid-name is not a valid metadata file name. " + "Accepted metadata names are license, attribution and restrictions." + ) + with pytest.raises(UserWarning, match=warning_message): + ants.io.load.load_cube(temporary_cube_path) diff --git a/pyproject.toml b/pyproject.toml index a0d1632..76d8a7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ skip = ".git" # Configure pytest. [tool.pytest.ini_options] addopts = "--durations=5" +tmp_path_retention_policy = "none" filterwarnings = [ # During unittesting only, treat all warnings as errors by default: 'error', diff --git a/rose-stem/app/ancil_2anc/rose-app.conf b/rose-stem/app/ancil_2anc/rose-app.conf index 7ab4ed0..60f9ee4 100644 --- a/rose-stem/app/ancil_2anc/rose-app.conf +++ b/rose-stem/app/ancil_2anc/rose-app.conf @@ -11,7 +11,7 @@ history=${CYLC_WORKFLOW_ID}@${ROSE_SUITE_REVISION}:${ROSE_TASK_NAME} [command] default=ants-launch ancil_2anc.py \ =${source} --grid-staggering ${grid_staggering} -o ${output} \ - =--ants-config ${ANTS_CONFIG} + =--ants-config ${ANTS_CONFIG} --ignore-metadata-files [env] ANTS_CONFIG=rose-app-run.conf diff --git a/rose-stem/app/ancil_fill_n_merge/opt/rose-app-invert_mask.conf b/rose-stem/app/ancil_fill_n_merge/opt/rose-app-invert_mask.conf index 5d8e792..88b6cc2 100644 --- a/rose-stem/app/ancil_fill_n_merge/opt/rose-app-invert_mask.conf +++ b/rose-stem/app/ancil_fill_n_merge/opt/rose-app-invert_mask.conf @@ -2,7 +2,7 @@ default=ants-launch ancil_fill_n_merge.py \ =${source} --target-lsm ${target} -o ${output} \ =--ants-config ${ANTS_CONFIG} --invert-mask \ - =--search-method ${search_method} + =--search-method ${search_method} --ignore-metadata-files [env] search_method=${CYLC_TASK_PARAM_fill} diff --git a/rose-stem/app/ancil_fill_n_merge/opt/rose-app-land_cover.conf b/rose-stem/app/ancil_fill_n_merge/opt/rose-app-land_cover.conf index 4db75a3..bd71de6 100644 --- a/rose-stem/app/ancil_fill_n_merge/opt/rose-app-land_cover.conf +++ b/rose-stem/app/ancil_fill_n_merge/opt/rose-app-land_cover.conf @@ -1,7 +1,7 @@ [command] default=ants-launch ancil_fill_n_merge.py \ =${source} --target-lsm ${target} --polygon ${polygon} -o ${output} \ - =--ants-config ${ANTS_CONFIG} --search-method ${search_method} + =--ants-config ${ANTS_CONFIG} --search-method ${search_method} --ignore-metadata-files [env] polygon=${ROSE_DATA}/ite.shp diff --git a/rose-stem/app/ancil_general_regrid/opt/rose-app-grid_to_grid.conf b/rose-stem/app/ancil_general_regrid/opt/rose-app-grid_to_grid.conf index 23bc985..78e6087 100644 --- a/rose-stem/app/ancil_general_regrid/opt/rose-app-grid_to_grid.conf +++ b/rose-stem/app/ancil_general_regrid/opt/rose-app-grid_to_grid.conf @@ -1,7 +1,7 @@ [command] default=ants-launch ancil_general_regrid.py \ =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ - =${begin} ${end} --search-method ${search_method} + =${begin} ${end} --search-method ${search_method} --ignore-metadata-files [env] output=${ROSE_DATA}/${ROSE_TASK_NAME} diff --git a/rose-stem/app/ancil_general_regrid/opt/rose-app-invert_mask.conf b/rose-stem/app/ancil_general_regrid/opt/rose-app-invert_mask.conf index 9a4c4ba..5a7fd0e 100644 --- a/rose-stem/app/ancil_general_regrid/opt/rose-app-invert_mask.conf +++ b/rose-stem/app/ancil_general_regrid/opt/rose-app-invert_mask.conf @@ -1,7 +1,7 @@ [command] default=ants-launch ancil_general_regrid.py \ =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ - =--invert-mask ${begin} ${end} --search-method ${search_method} + =--invert-mask ${begin} ${end} --search-method ${search_method} --ignore-metadata-files [env] output=${ROSE_DATA}/${ROSE_TASK_NAME} diff --git a/rose-stem/app/ancil_general_regrid/rose-app.conf b/rose-stem/app/ancil_general_regrid/rose-app.conf index fb1a949..778e84f 100644 --- a/rose-stem/app/ancil_general_regrid/rose-app.conf +++ b/rose-stem/app/ancil_general_regrid/rose-app.conf @@ -14,7 +14,7 @@ scheme=TwoStage [command] default=ants-launch ancil_general_regrid.py \ =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ - =${begin} ${end} + =${begin} ${end} --ignore-metadata-files [env] ANTS_CONFIG=rose-app-run.conf diff --git a/rose-stem/app/ancil_general_regrid_with_time_constraint/rose-app.conf b/rose-stem/app/ancil_general_regrid_with_time_constraint/rose-app.conf index 6f166bd..ad44222 100644 --- a/rose-stem/app/ancil_general_regrid_with_time_constraint/rose-app.conf +++ b/rose-stem/app/ancil_general_regrid_with_time_constraint/rose-app.conf @@ -14,7 +14,7 @@ scheme=TwoStage [command] default=ants-launch ancil_general_regrid.py \ =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ - =--begin ${begin} --end ${end} --search-method ${search_method} + =--begin ${begin} --end ${end} --search-method ${search_method} --ignore-metadata-files [env] ANTS_CONFIG=rose-app-run.conf