diff --git a/news/wavelength-workflow.rst b/news/wavelength-workflow.rst new file mode 100644 index 0000000..09891c6 --- /dev/null +++ b/news/wavelength-workflow.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* Workflow for loading wavelength - raise an error when both wavelength and anode type are specified. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/labpdfproc/labpdfprocapp.py b/src/diffpy/labpdfproc/labpdfprocapp.py index 1d6581f..3f741f1 100644 --- a/src/diffpy/labpdfproc/labpdfprocapp.py +++ b/src/diffpy/labpdfproc/labpdfprocapp.py @@ -30,8 +30,7 @@ def define_arguments(): "in a file with name file_list.txt. " "If one or more directory is provided, all valid " "data-files in that directory will be processed. " - "Examples of valid " - "inputs are 'file.xy', 'data/file.xy', " + "Examples of valid inputs are 'file.xy', 'data/file.xy', " "'file.xy, data/file.xy', " "'.' (load everything in the current directory), " "'data' (load everything in the folder ./data), " @@ -48,18 +47,16 @@ def define_arguments(): "name": ["-a", "--anode-type"], "help": ( f"The type of the x-ray source. " - f"Allowed values are {*[known_sources], }. " + f"Allowed values are {*known_sources, }. " f"Either specify a known x-ray source or specify wavelength." ), - "default": "Mo", + "default": None, }, { "name": ["-w", "--wavelength"], "help": ( "X-ray source wavelength in angstroms. " - "Not needed if the anode-type is specified. " - "This wavelength will override the anode wavelength " - "if both are specified." + "Not needed if the anode-type is specified." ), "type": float, }, @@ -104,7 +101,7 @@ def define_arguments(): f"The method for computing absorption correction. " f"Allowed methods: {*CVE_METHODS, }. " f"Default method is polynomial interpolation " - f"if not specified. " + f"if not specified." ), "default": "polynomial_interpolation", }, @@ -121,7 +118,7 @@ def define_arguments(): "For example, facility='NSLS II', " "'facility=NSLS II', beamline=28ID-2, " "'beamline'='28ID-2', 'favorite color'=blue, " - "are all valid key=value items. " + "are all valid key=value items." ), "nargs": "+", "metavar": "KEY=VALUE", @@ -131,7 +128,7 @@ def define_arguments(): "help": ( "Username will be loaded from config files. " "Specify here only if you want to " - "override that behavior at runtime. " + "override that behavior at runtime." ), "default": None, }, @@ -140,7 +137,7 @@ def define_arguments(): "help": ( "Email will be loaded from config files. " "Specify here only if you want to " - "override that behavior at runtime. " + "override that behavior at runtime." ), "default": None, }, @@ -149,7 +146,7 @@ def define_arguments(): "help": ( "ORCID will be loaded from config files. " "Specify here only if you want to " - "override that behavior at runtime. " + "override that behavior at runtime." ), "default": None, }, diff --git a/src/diffpy/labpdfproc/tools.py b/src/diffpy/labpdfproc/tools.py index 21cb90d..3850673 100644 --- a/src/diffpy/labpdfproc/tools.py +++ b/src/diffpy/labpdfproc/tools.py @@ -160,9 +160,10 @@ def set_input_lists(args): def set_wavelength(args): - """Set the wavelength based on the given anode_type. - If a wavelength is provided, - it will be used, and the anode_type argument will be removed. + """Set the wavelength based on the given anode_type or wavelength. + + First checks from args. If neither is provided, + it attempts to load from local and then global config file. Parameters ---------- @@ -172,15 +173,32 @@ def set_wavelength(args): Raises ------ ValueError - Raised when input wavelength is non-positive - or if input anode_type is not one of the known sources. + Raised if: + (1) neither wavelength or anode type is provided, + and xtype is not the two-theta grid, + (2) both are provided, + (3) anode_type is not one of the known sources, + (4) wavelength is non-positive. Returns ------- args : argparse.Namespace The updated arguments with the wavelength. """ - if args.wavelength is None: + # first load values from config file + if args.wavelength is None and args.anode_type is None: + if args.xtype not in ANGLEQUANTITIES: + raise ValueError( + f"Please provide a wavelength or anode type " + f"because the independent variable axis is not on two-theta. " + f"Allowed anode types are {*known_sources, }." + ) + elif args.wavelength is not None and args.anode_type is not None: + raise ValueError( + f"Please provide either a wavelength or an anode type, not both. " + f"Allowed anode types are {*known_sources, }." + ) + elif args.anode_type is not None: matched_anode_type = next( ( key @@ -197,15 +215,12 @@ def set_wavelength(args): ) args.anode_type = matched_anode_type args.wavelength = WAVELENGTHS[args.anode_type] - else: - if args.wavelength <= 0: - raise ValueError( - "No valid wavelength. " - "Please rerun specifying a known anode_type " - "or a positive wavelength." - ) - else: - delattr(args, "anode_type") + elif args.wavelength is not None and args.wavelength <= 0: + raise ValueError( + "No valid wavelength. " + "Please rerun specifying a known anode_type " + "or a positive wavelength." + ) return args @@ -362,7 +377,8 @@ def load_package_info(args): def preprocessing_args(args): """Perform preprocessing on the provided args. The process includes loading package and user information, - setting input, output, wavelength, xtype, mu*D, and loading user metadata. + setting input, output, wavelength, anode type, xtype, mu*D, + and loading user metadata. Parameters ---------- diff --git a/tests/test_tools.py b/tests/test_tools.py index a0c8292..e6f2269 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -198,9 +198,7 @@ def test_set_output_directory_bad(user_filesystem): @pytest.mark.parametrize( "inputs, expected", [ - # C1: nothing passed in, expect default is Mo - ([], {"wavelength": 0.71073, "anode_type": "Mo"}), - # C2: only a valid anode type was entered (case independent), + # C1: only a valid anode type was entered (case independent), # expect to match the corresponding wavelength # and preserve the correct case anode type (["--anode-type", "Mo"], {"wavelength": 0.71073, "anode_type": "Mo"}), @@ -239,15 +237,13 @@ def test_set_output_directory_bad(user_filesystem): ["--anode-type", "cuka1"], {"wavelength": 1.54056, "anode_type": "CuKa1"}, ), - # C3: only a valid wavelength was entered, + # C2: a valid wavelength was entered, # expect to include the wavelength only and anode type is None (["--wavelength", "0.25"], {"wavelength": 0.25, "anode_type": None}), - # C4: both valid anode type and wavelength were entered, - # expect to remove the anode type and preserve wavelength only - ( - ["--wavelength", "0.25", "--anode-type", "Ag"], - {"wavelength": 0.25, "anode_type": None}, - ), + # C3: nothing passed in, but mu*D was provided and xtype is on tth + # expect wavelength and anode type to be None + # and program proceeds without error + ([], {"wavelength": None, "anode_type": None}), ], ) def test_set_wavelength(inputs, expected): @@ -255,29 +251,38 @@ def test_set_wavelength(inputs, expected): actual_args = get_args(cli_inputs) actual_args = set_wavelength(actual_args) assert actual_args.wavelength == expected["wavelength"] - assert getattr(actual_args, "anode_type", None) == expected["anode_type"] + assert actual_args.anode_type == expected["anode_type"] @pytest.mark.parametrize( "inputs, expected_error_msg", [ - ( + ( # C1: nothing passed in, xtype is not on tth + # expect error asking for either wavelength or anode type + ["--xtype", "q"], + f"Please provide a wavelength or anode type " + f"because the independent variable axis is not on two-theta. " + f"Allowed anode types are {*known_sources, }.", + ), + ( # C2: both wavelength and anode type were specified + # expect error asking not to specify both + ["--wavelength", "0.7", "--anode-type", "Mo"], + f"Please provide either a wavelength or an anode type, not both. " + f"Allowed anode types are {*known_sources, }.", + ), + ( # C3: invalid anode type + # expect error asking to specify a valid anode type ["--anode-type", "invalid"], f"Anode type not recognized. " f"Please rerun specifying an anode_type from {*known_sources, }.", ), - ( + ( # C4: invalid wavelength + # expect error asking to specify a valid wavelength or anode type ["--wavelength", "0"], "No valid wavelength. " "Please rerun specifying a known anode_type " "or a positive wavelength.", ), - ( - ["--wavelength", "-1", "--anode-type", "Mo"], - "No valid wavelength. " - "Please rerun specifying a known anode_type " - "or a positive wavelength.", - ), ], ) def test_set_wavelength_bad(inputs, expected_error_msg): @@ -502,6 +507,11 @@ def test_load_package_info(mocker): def test_load_metadata(mocker, user_filesystem): + # Test if the function loads args + # (which will be loaded into the header file). + # Expect to include mu*D, anode type, xtype, cve method, + # user-specified metadata, user info, package info, z-scan file, + # and full paths for current input and output directories. cwd = Path(user_filesystem) home_dir = cwd / "home_dir" mocker.patch("pathlib.Path.home", lambda _: home_dir) @@ -515,6 +525,8 @@ def test_load_metadata(mocker, user_filesystem): cli_inputs = [ "2.5", ".", + "--anode-type", + "Mo", "--user-metadata", "key=value", "--username",