changeset 3:331d0776abb4 draft

planemo upload for repository https://github.com/muon-spectroscopy-computational-project/muon-galaxy-tools/main/muspinsim_config commit 4f06b404d8b7fb83995f3052faa7e2ec7811f507
author muon-spectroscopy-computational-project
date Fri, 03 Feb 2023 15:39:07 +0000
parents 52d3a28902e8
children e1e338f56656
files build_file.py config_macros.xml muspinsim_config.xml test-data/test_1.in test-data/test_2.in test-data/test_3.in test-data/test_4.in test-data/test_5.in test-data/test_6.in test-data/test_7.in
diffstat 10 files changed, 490 insertions(+), 358 deletions(-) [+]
line wrap: on
line diff
--- a/build_file.py	Thu Sep 15 10:23:52 2022 +0000
+++ b/build_file.py	Fri Feb 03 15:39:07 2023 +0000
@@ -11,15 +11,15 @@
     :param file_name: name of file
     :param content: list of strings containing blocks to write
     """
-    with open(file_name, "w") as f:
-        f.write(
+    with open(file_name, "w", encoding="utf-8") as file:
+        file.write(
             """
 #######################################################
-#Muspinsim Input File
-#Generated using Muon Galaxy Tool Muspinsim_Input
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
 #######################################################\n\n"""
         )
-        f.write("".join(content))
+        file.write("".join(content))
 
 
 def build_block(title, vals):
@@ -47,9 +47,8 @@
         elif char == ")":
             if len(stck) == 0:
                 raise ValueError(
-                    "Could not parse entry {0}"
-                    "brackets mismatch - unexpected ')' "
-                    "found on char {1}".format(entry, i)
+                    f"Could not parse entry {entry} brackets mismatch - "
+                    f"unexpected ')' found on char {i}"
                 )
             stck.pop()
         elif char == " " and len(stck) > 0:
@@ -63,10 +62,8 @@
 
     if len(stck) != 0:
         raise ValueError(
-            "Could not parse entry {0}"
-            "brackets mismatch - unclosed '(' found on char(s): {1}".format(
-                entry, stck
-            )
+            f"Could not parse entry {entry} brackets mismatch - unclosed '(' "
+            f"found on char(s): {stck}"
         )
     return new_str
 
@@ -92,11 +89,8 @@
     chars = [elem.strip() for elem in content if elem != ""]
     if len(chars) != nargs:
         raise ValueError(
-            "Could not parse entry {0}"
-            " incorrect number of args"
-            " found {1}:\n({2})\nBut expected {3}".format(
-                entry, len(chars), chars, nargs
-            )
+            f"Could not parse entry {entry} incorrect number of args found "
+            f"{len(chars)}:\n({chars})\nBut expected {nargs}"
         )
     return chars
 
@@ -104,7 +98,7 @@
 def parse_matrix(entry_string, size):
     """
     Helper function to parse and format matrix/vector
-    to be readable by Muspinsim
+    to be readable by MuSpinSim
     :param entry_string: a user input string for a matrix/vector
     :param size: (x, y) integer tuple: dimensions of matrix
     :return: a list of strings of length y, each string
@@ -136,31 +130,30 @@
                 parse_matrix(options["zeeman_vector"], (3, 1)),
             ),
             "hyperfine": lambda options: build_block(
-                "hyperfine {0} {1}".format(
-                    options["hfine_index"],
-                    options["hfine_e_index"]
-                    if options["hfine_e_index"]
-                    else "",
+                (
+                    f"hyperfine {options['hfine_index']} "
+                    f"""{
+                        options['hfine_e_index']
+                        if options['hfine_e_index'] else ''
+                    }"""
                 ).strip(),
                 parse_matrix(options["hfine_matrix"], (3, 3)),
             ),
             "dipolar": lambda options: build_block(
-                "dipolar {0} {1}".format(
-                    options["di_index"], options["di_index_2"]
-                ),
+                f"dipolar {options['di_index']} {options['di_index_2']}",
                 parse_matrix(options["di_vector"], (3, 1)),
             ),
             "quadrupolar": lambda options: build_block(
-                "quadrupolar {0}".format(options["quad_index"]),
+                f"quadrupolar {options['quad_index']}",
                 parse_matrix(options["quad_matrix"], (3, 3)),
             ),
             "dissipation": lambda options: build_block(
-                "dissipation {0}".format(options["dis_index"]),
+                f"dissipation {options['dis_index']}",
                 [options["dis_val"]],
             ),
         }.get(interaction_type)(options)
-    except ValueError as e:
-        raise ValueError("Error occurred when parsing {0}".format(e))
+    except ValueError as exc:
+        raise ValueError(f"Error occurred when parsing {exc}") from exc
 
 
 def parse_orientation(orientation):
@@ -217,16 +210,18 @@
             return " ".join(split_into_args(options["polarization"], 3))
 
 
-def parse_field(field):
+def parse_field(field, field_type):
     """
     Helper function to parse field keyword arguments
     :param field: a dictionary containing one set of field arguments
+    :param field_type: a string giving the type of field, either field or
+                       intrinsic_field
     :return: a formatted string
     """
     try:
-        return " ".join(split_into_args(field["field"], 1))
+        return " ".join(split_into_args(field[field_type], 1))
     except ValueError:
-        return " ".join(split_into_args(field["field"], 3))
+        return " ".join(split_into_args(field[field_type], 3))
 
 
 def parse_fitting_variables(fitting_variables):
@@ -251,41 +246,62 @@
 
 
 def parse_spin(spin):
+    """
+    Helper function for parsing a spin
+    :param spin: a dictionary containing a spin object from the config either
+                 just a spin_preset or a custom value with a name and
+                 atomic_mass
+    """
     if spin["spin_preset"] != "custom":
         return spin["spin_preset"]
     else:
         elem_name = spin["spin"].strip()
-        if elem_name not in ['e', 'mu']:
+        if elem_name not in ["e", "mu"]:
             elem_name = elem_name.capitalize()
-        return "{0}{1}".format(
-            int(spin["atomic_mass"]) if spin["atomic_mass"] else "",
-            elem_name
+        return (
+            f"{int(spin['atomic_mass']) if spin['atomic_mass'] else ''}"
+            f"{elem_name}"
         ).strip()
 
 
+def parse_celio(celio_params):
+    """
+    Helper function for parsing Celio's method parameters
+    :param celio_params: a dictionary containing the parameters for Celio's
+                         method
+    """
+    options = celio_params["celio_options"]
+    if not options["celio_enabled"]:
+        return ""
+    else:
+        # Now have celio_k and potentially celio_averages
+        celio_k = options["celio_k"]
+        celio_averages = options["celio_averages"]
+
+        # As celio_averages is optional so may be None
+        if celio_averages is None:
+            celio_averages = ""
+
+        return build_block("celio", [f"{celio_k} {celio_averages}".strip()])
+
+
 parse_func_dict = {
     "spins": lambda values: build_block(
         "spins",
-        [
-            " ".join(
-                [
-                    parse_spin(entry["spin_options"])
-                    for entry in values
-                ]
-            )
-        ],
+        [" ".join([parse_spin(entry["spin_options"]) for entry in values])],
     ),
     # either 1x3 vector or scalar or function
     "fields": lambda values: build_block(
-        "field", [parse_field(entry) for entry in values]
+        "field", [parse_field(entry, "field") for entry in values]
+    ),
+    "intrinsic_fields": lambda values: build_block(
+        "intrinsic_field",
+        [parse_field(entry, "intrinsic_field") for entry in values],
     ),
     # either scalar or single function
     "times": lambda values: build_block(
         "time",
-        [
-            " ".join(split_into_args(entry["time"], 1))
-            for entry in values
-        ],
+        [" ".join(split_into_args(entry["time"], 1)) for entry in values],
     ),
     # either scalar or single function
     "temperatures": lambda values: build_block(
@@ -295,16 +311,21 @@
             for entry in values
         ],
     ),
-    "x_axis": lambda value: build_block("x_axis", [value]),
-    "y_axis": lambda value: build_block("y_axis", [value]),
-    "average_axes": lambda values: build_block(
-        "average_axes", values
-    ),
-    "experiment_preset": lambda value: build_block(
-        "experiment", [value]
-    ),
+    "axes_options": {
+        "x_axis_options": {
+            "x_axis": lambda value: build_block("x_axis", [value]),
+            "average_axes": lambda values: build_block(
+                "average_axes", values),
+        },
+        "x_axis": lambda value: build_block("x_axis", [value]),
+        "y_axis": lambda value: build_block("y_axis", [value]),
+        "average_axes": lambda values: build_block(
+            "average_axes", values)
+    },
+    "average_axes": lambda values: build_block("average_axes", values),
+    "experiment_preset": lambda value: build_block("experiment", [value]),
     "orientations": lambda values: build_block(
-        "orientation {0}".format(euler_convention),
+        f"orientation {EULER_CONVENTION}",
         [parse_orientation(entry) for entry in values],
     ),
     "interactions": lambda values: "".join(
@@ -314,12 +335,11 @@
         "polarization",
         [parse_polarization(entry) for entry in values],
     ),
+    "celio_params": parse_celio,
     "fitting": lambda value: build_block(
         "fitting_data", ['load("fitting_data.dat")']
     ),
-    "fitting_method": lambda value: build_block(
-        "fitting_method", [value]
-    ),
+    "fitting_method": lambda value: build_block("fitting_method", [value]),
     "fitting_variables": lambda values: build_block(
         "fitting_variables",
         [parse_fitting_variables(entry) for entry in values],
@@ -329,12 +349,58 @@
         [str(value)],
     ),
 }
-euler_convention = 'ZYZ'
+EULER_CONVENTION = "ZYZ"
+
+# Gives replacement values in the case a parameter is unassigned
+parse_none_dict = {
+    # Allow average_axis to be None as by default is orientation in
+    # muspinsim but letting the UI present this here instead
+    "average_axes": ["none"],
+}
+
+
+def parse_dict(dictionary, params, file_contents) -> bool:
+    """
+    Helper function for parsing nested dictionaries defined above
+    containing parse functions
+    :returns: Whether an error occurred
+    """
+
+    err_found = False
+    for keyword, val in params.items():
+
+        # Either don't allow the value to be None or replace
+        # with value in the parse_none_dict above
+        should_assign = val and val not in ["None"]
+        if not should_assign and keyword in parse_none_dict:
+            should_assign = keyword in parse_none_dict
+            val = parse_none_dict[keyword]
+
+        if should_assign:
+            try:
+                keyword_func = dictionary.get(keyword)
+                # Check for nested dict, and add that contents as well if found
+                if isinstance(keyword_func, dict):
+                    err_found = err_found or parse_dict(
+                        keyword_func, val, file_contents)
+                else:
+                    if keyword_func:
+                        file_contents.append(keyword_func(val))
+
+            except ValueError as exc:
+                sys.stderr.write(
+                    f"Error occurred when parsing {keyword}\n{str(exc)}"
+                )
+                err_found = True
+    return err_found
 
 
 def main():
+    """
+    Entry point
+    """
     input_json_path = sys.argv[1]
-    mu_params = json.load(open(input_json_path, "r"))
+    mu_params = json.load(open(input_json_path, "r", encoding="utf-8"))
 
     out_file_name = mu_params["out_file_prefix"].strip().replace(" ", "_")
 
@@ -354,41 +420,24 @@
         del mu_params["experiment_preset"]
     del mu_params["experiment"]
 
-    global euler_convention
-    euler_convention = mu_params["euler_convention"]
+    global EULER_CONVENTION
+    EULER_CONVENTION = mu_params["euler_convention"]
 
-    err_found = False
     file_contents = [
         build_block("name", [out_file_name.strip().replace(" ", "_")])
     ]
-    for keyword, val in mu_params.items():
-        if val and val not in ["None"]:
-            try:
-                keyword_func = parse_func_dict.get(keyword)
-                if keyword_func:
-                    file_contents.append(keyword_func(val))
 
-            except ValueError as e:
-                sys.stderr.write(
-                    "Error occurred when parsing {0}\n{1}".format(
-                        keyword, str(e)
-                    )
-                )
-                err_found = True
-
-    if err_found:
+    if parse_dict(parse_func_dict, mu_params, file_contents):
         sys.exit(1)
 
     write_file("outfile.in", file_contents)
 
     try:
-        MuSpinInput(open("outfile.in"))
-    except Exception as e:
+        MuSpinInput(open("outfile.in", encoding="utf-8"))
+    except Exception as exc:  # pylint: disable=broad-except
         sys.stdout.write(
-            "Warning, This created file may not work properly. "
-            "Error(s) encountered when trying to parse the file : {0}".format(
-                str(e)
-            )
+            "Warning, This created file may not work properly. Error(s) "
+            f"encountered when trying to parse the file : {str(exc)}"
         )
         sys.exit(1)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config_macros.xml	Fri Feb 03 15:39:07 2023 +0000
@@ -0,0 +1,145 @@
+<macros>
+    <xml name="celio_options">
+        <section name="celio_params" expanded="true" title="Celio's Method" help="Celio's method is an approximate method for computing asymmetry values over a range of times. It is particularly useful for larger systems of higher spins.">
+            <conditional name="celio_options">
+                <param name="celio_enabled" type="select" label="Use Celio's method?" display="radio">
+                    <option value="true">Yes</option>
+                    <option selected="true" value="">No</option>
+                </param>
+                <when value="true">
+                    <param name="celio_k" type="integer" value="10" label="Trotter number, k" help="Value used in the Trotter expansion."/>
+                    <param name="celio_averages" type="integer" optional="true" label="Number of averages" help="When given a value MuSpinSim will randomise initial polarisation states and perform an average. This method is more approximate but much faster and reduces memory usage. It will also assume T-&gt;inf and zero magnetic fields. See the MuSpinSim docs for more info."/>
+                </when>
+                <when value=""/>
+            </conditional>
+        </section>
+    </xml>
+    <xml name="times">
+        <repeat name="times" title="Time" help="A time or range of times (μs)">
+            <param name="time" type="text" label="Time" help="Either a single time value or `range` function - allows default expressions, default constants, and special 'range()' function (see help section)" optional="true" value="range(0, 10, 101)"/>
+        </repeat>
+    </xml>
+    <xml name="fields">
+        <repeat name="fields" title="Fields" help="Magnetic fields">
+            <param name="field" type="text" value="0" label="Field" optional="true" help="Optional, (default is 0). Accepts 1X3 vector, or scalar value. Scalar value means field is assumed to be aligned with z axis. Allows default expressions, constants and functions, plus MHz, muon_gyr constants and special 'range()' function (see help section)"/>
+        </repeat>
+    </xml>
+    <xml name="intrinsic_fields">
+        <repeat name="intrinsic_fields" title="Intrinsic Fields" help="Intrinsic magnetic fields (these are unaffected by rotations when using angular averaging via the orientations parameter).">
+            <param name="intrinsic_field" type="text" value="0" label="Intrinsic Field (T)" optional="true" help="Optional, (default is 0). Accepts 1X3 vector, or scalar value. Scalar value means field is assumed to be aligned with z axis. Allows default expressions, constants and functions, plus MHz, muon_gyr constants and special 'range()' function (see help section)"/>
+        </repeat>
+    </xml>
+    <xml name="polarizations">
+        <repeat name="polarizations" title="Polarizations" help="The direction along which the muon should be polarized when starting, as well as the one in which it will be measured. Each entry will generate a separate calculation when MuSpinSim is run">
+            <conditional name="polarization_options">
+                <param name="polarization_preset" value="custom" type="select" display="radio" label="Polarization" help="transverse: along x-axis, longitudinal: along z-axis, custom: define vector">
+                    <option value="longitudinal">longitudinal</option>
+                    <option value="transverse">transverse</option>
+                    <option selected="true" value="custom">custom</option>
+                </param>
+                <when value="custom">
+                    <param name="polarization" type="text" value="" label="Enter custom vector for polarization" help="Accepts 1X3 vector. Allows default expressions, constants and functions (see help section)"/>
+                </when>
+                <when value="longitudinal"/>
+                <when value="transverse"/>
+            </conditional>
+        </repeat>
+    </xml>
+    <xml name="orientations">
+        <repeat name="orientations" title="Orientations" help="Orientations to use for crystallites - (define powder averages)">
+            <conditional name="orientation_options">
+                <param name="orientation_preset" type="select" display="radio" label="Orientation" optional="false" help="Polar angles: Define two polar angles θ and ϕ, defining only the direction of the z-axis (recommended for powder averages). Euler angles: Define 3 Euler angles defining a new frame, convention used is ZYZ by default. Euler angles with weight: define 3 Euler angles and a weight (will be normalized automatically). Euler angles helper function: eulrange(n). Zaremba-Conroy-Wolfsberg helper function: zcw(n)">
+                    <option value="2_polar">Polar angles</option>
+                    <option value="3_euler">Euler angles</option>
+                    <option value="4_euler">Euler angles with weight</option>
+                    <option value="eulrange">Euler angles helper function (eulrange(n))</option>
+                    <option selected="true" value="zcw">Zaremba-Conroy-Wolfsberg helper function (zcw(n))</option>
+                </param>
+                <when value="eulrange">
+                    <param name="eul_n" type="text" value="0" label="n" help="Value for n for eulrange(n) - allows default expressions, constants and functions. WARNING: large values are more computationally expensive."/>
+                </when>
+                <when value="zcw">
+                    <param name="zcw_n" type="text" value="0" label="n" help="Value for n for zcw(n) - Allows default expressions, constants and functions. WARNING: large values are more computationally expensive."/>
+                </when>
+                <when value="2_polar">
+                    <param name="theta" type="text" value="" label="θ (theta/inclination) angle" help="All polar angle entries allow default expressions, constants and functions"/>
+                    <param name="phi" type="text" value="" label="ϕ (phi/azimuth) angle" help="All polar angle entries allow default expressions, constants and functions"/>
+                </when>
+                <when value="3_euler">
+                    <param name="eul_1" type="text" value="" label="Euler angle 1" help="All Euler angle entries allow default expressions, constants and functions"/>
+                    <param name="eul_2" type="text" value="" label="Euler angle 2" help="All Euler angle entries allow default expressions, constants and functions"/>
+                    <param name="eul_3" type="text" value="" label="Euler angle 3" help="All Euler angle entries allow default expressions, constants and functions"/>
+                </when>
+                <when value="4_euler">
+                    <param name="eul_1" type="text" value="" label="Euler angle 1" help="All Euler angle entries allow default expressions and constants"/>
+                    <param name="eul_2" type="text" value="" label="Euler angle 2" help="All Euler angle entries allow default expressions and constants"/>
+                    <param name="eul_3" type="text" value="" label="Euler angle 3" help="All Euler angle entries allow default expressions and constants"/>
+                    <param name="weight" type="float" value="0" label="Weight" help="Allows only floating point value, weights will automatically be normalised"/>
+                </when>
+            </conditional>
+        </repeat>
+    </xml>
+    <xml name="euler_convention">
+        <param name="euler_convention" type="select" display="radio" label="Euler Convention" help="Euler angle convention to use for orientation definitions (ignored if Euler angles not defined)">
+            <option selected="true" value="zyz">ZYZ</option>
+            <option value="zxz">ZXZ</option>
+        </param>
+    </xml>
+    <xml name="temperatures">
+        <repeat name="temperatures" title="Temperature" help="Temperature or range of temperatures (K)">
+            <param name="temperature" type="text" help="Either single value or 'range()' function - allows default expressions, default constants, and special 'range()' function (see help section). Warning: both density matrices and dissipative couplings for finite temperatures are only calculated approximatively, see MuSpinSim docs." optional="true" value="inf"/>
+        </repeat>
+    </xml>
+    <xml name="average_axis_options_no_time">
+        <param name="average_axes" type="select" display="checkboxes" multiple="true" optional="true" value="orientation" label="Average axes" help="Keywords that should have an average carried out over them. Each keyword ticked should have a range specified. Keywords not ticked or set as X axis, but which have a range set, will generate separate calculations for each value in the range when muspinsim is run.">
+            <option value="field">field</option>
+            <option value="intrinsic_field">intrinsic_field</option>
+            <option value="temperature">temperature</option>
+            <option value="polarization">polarization</option>
+            <option selected="true" value="orientation">orientation</option>
+        </param>
+    </xml>
+    <xml name="average_axis_options_no_field">
+        <param name="average_axes" type="select" display="checkboxes" multiple="true" optional="true" value="orientation" label="Average axes" help="Keywords that should have an average carried out over them. Each keyword ticked should have a range specified. Keywords not ticked or set as X axis, but which have a range set, will generate separate calculations for each value in the range when muspinsim is run.">
+            <option value="time">time</option>
+            <option value="intrinsic_field">intrinsic_field</option>
+            <option value="temperature">temperature</option>
+            <option value="polarization">polarization</option>
+            <option selected="true" value="orientation">orientation</option>
+        </param>
+    </xml>
+    <xml name="restricted_axes_options">
+        <param name="average_axes" type="select" display="checkboxes" multiple="true" optional="true" value="orientation" label="Average axes" help="Keywords that should have an average carried out over them. Each keyword ticked should have a range specified. Keywords not ticked or set as X axis, but which have a range set, will generate separate calculations for each value in the range when MuSpinSim is run.">
+            <option selected="true" value="orientation">orientation</option>
+            <option value="temperature">temperature</option>
+        </param>
+    </xml>
+    <xml name="axes_options">
+        <conditional name="axes_options">
+            <param name="y_axis" type="select" value="asymmetry" display="radio" label="Y axis" help="Range to use as y axis for output file(s). Asymmetry: use muon's polarization. Integral: use muon's polarization integral over time.">
+                <option selected="true" value="asymmetry">asymmetry</option>
+                <option value="integral">integral</option>
+            </param>
+            <when value="asymmetry">
+                <conditional name="x_axis_options">
+                    <param name="x_axis" type="select" value="time" display="radio" label="X axis" help="Range to use as X axis for output file(s)">
+                        <option selected="true" value="time">time</option>
+                        <option value="field">field</option>
+                    </param>
+                    <when value="time">
+                        <expand macro="average_axis_options_no_time"/>
+                    </when>
+                    <when value="field">
+                        <expand macro="average_axis_options_no_field"/>
+                    </when>
+                </conditional>
+            </when>
+            <when value="integral">
+                <param name="x_axis" type="select" value="time" display="radio" label="X axis" help="Range to use as X axis for output file(s)">
+                    <option selected="true" value="field">field</option>
+                </param>
+                <expand macro="average_axis_options_no_field"/>
+            </when>
+        </conditional>
+    </xml>
+</macros>
--- a/muspinsim_config.xml	Thu Sep 15 10:23:52 2022 +0000
+++ b/muspinsim_config.xml	Fri Feb 03 15:39:07 2023 +0000
@@ -1,28 +1,30 @@
-<tool id="muspinsim_config" name="MuSpinSim Configure" version="@TOOL_VERSION@+galaxy@WRAPPER_VERSION@" python_template_version="3.5" profile="22.01">
+<tool id="muspinsim_config" name="MuSpinSim Configure" version="@TOOL_VERSION@+galaxy@WRAPPER_VERSION@" python_template_version="3.5" profile="22.05" license="MIT">
     <description>define simulation parameters</description>
-       <macros>
+    <macros>
         <!-- version of underlying tool (PEP 440) -->
-        <token name="@TOOL_VERSION@">1.1.0</token>
+        <token name="@TOOL_VERSION@">2.0.2</token>
         <!-- version of this tool wrapper (integer) -->
         <token name="@WRAPPER_VERSION@">0</token>
         <!-- citation should be updated with every underlying tool version -->
         <!-- typical fields to update are version, month, year, and doi -->
         <token name="@TOOL_CITATION@">
             @software{muspinsim,
-                author = {Sturniolo, Simone and Liborio, Leandro and Owen, Josh and Mudaraddi, Anish and {Muon Spectroscopy Computational Project}},
+                author = {Sturniolo, Simone and Liborio, Leandro and Owen, Josh and Mudaraddi, Anish and Davies, Joel and Wilkinson, John and {Muon Spectroscopy Computational Project}},
                 license = {MIT},
                 title = {{muspinsim}},
                 url = {https://github.com/muon-spectroscopy-computational-project/muspinsim},
-                version = {v1.1.0},
-                month = {5},
-                year = {2022},
-                doi = {10.5281/zenodo.6563074}
+                version = {v2.0.2},
+                month = {1},
+                year = {2023}
+                doi = {10.5281/zenodo.7568830}
             }
         </token>
+        <import>config_macros.xml</import>
     </macros>
     <creator>
         <person givenName="Anish" familyName="Mudaraddi" identifier="https://orcid.org/0000-0002-2135-2705"/>
         <person givenName="Eli" familyName="Chadwick" url="https://github.com/elichad" identifier="https://orcid.org/0000-0002-0035-6475"/>
+        <person givenName="Joel" familyName="Davies" url="https://github.com/joelvdavies" identifier="https://orcid.org/0000-0002-4153-6819"/>
         <organization url="https://muon-spectroscopy-computational-project.github.io/index.html" name="The Muon Spectroscopy Computational Project"/>
     </creator>
     <requirements>
@@ -33,14 +35,14 @@
         <include type="literal" path="build_file.py"/>
     </required_files>
     <command detect_errors="exit_code"><![CDATA[
-        cp ${__tool_directory__}/sample_fitting_data.dat ./fitting_data.dat &&
-        python ${__tool_directory__}/build_file.py inputs.json
+        cp '${__tool_directory__}/sample_fitting_data.dat' ./fitting_data.dat &&
+        python '${__tool_directory__}/build_file.py' inputs.json
     ]]></command>
     <configfiles>
-        <inputs name="inputs" filename="inputs.json" />
+        <inputs name="inputs" filename="inputs.json"/>
     </configfiles>
     <inputs>
-        <param type="text" name="out_file_prefix" label="Name" help="A name with which to label this configuration" optional="true" value="muspinsim" />
+        <param type="text" name="out_file_prefix" label="Name" help="A name with which to label this configuration" optional="true" value="muspinsim"/>
         <section name="spins" expanded="true" title="Spins">
             <repeat name="spins" title="Spins to simulate" min="1" help="Specify the spins to be used in the system. This should include a muon (mu) and one or more electrons (e)">
                 <conditional name="spin_options">
@@ -59,7 +61,7 @@
             </repeat>
         </section>
         <section name="interaction_params" expanded="true" title="Spin Interactions" help="">
-            <repeat name="interactions" title="Interactions to simulate" help="Add couplings between spins, and/or dissipation terms. Interaction terms available: Zeeman, hyperfine, dipolar, quadrupolar or dissipation. See muspinsim docs for more info">
+            <repeat name="interactions" title="Interactions to simulate" help="Add couplings between spins, and/or dissipation terms. Interaction terms available: Zeeman, hyperfine, dipolar, quadrupolar or dissipation. See MuSpinSim docs for more info">
                 <conditional name="interaction_options">
                     <param name="interaction" type="select" label="Choose interaction type">
                         <option value="zeeman">Zeeman</option>
@@ -69,21 +71,13 @@
                         <option value="dissipation">dissipation</option>
                     </param>
                     <when value="zeeman">
-                        <param name="zeeman_index" type="integer" value="" label="Index of coupled spin" min="1"
-                               help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
-                        <param name="zeeman_vector" type="text" value="" label="Zeeman coupling vector"
-                            help="Define 1X3 vector for local magnetic field coupling (T).
-                            Allows default expressions, constants and functions (see help)"/>
+                        <param name="zeeman_index" type="integer" value="" label="Index of coupled spin" min="1" help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
+                        <param name="zeeman_vector" type="text" value="" label="Zeeman coupling vector" help="Define 1X3 vector for local magnetic field coupling (T). Allows default expressions, constants and functions (see help)"/>
                     </when>
                     <when value="hyperfine">
-                        <param name="hfine_index" type="integer" value="" label="Index of nuclear coupled spin" min="1"
-                               help="Non-electronic spin - muon or otherwise.
-                               Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
-                        <param name="hfine_e_index" type="integer" value="" optional="true" min="1" label="Index of electronic coupled spin"
-                               help="Optional, will use first defined electronic spin if unspecified"/>
-                        <param name="hfine_matrix" area="true" type="text" value="" label="Hyperfine coupling tensor"
-                               help="Define 3X3 tensor for coupling between electron and non-electron spins (in MHz).
-                               Allows default expressions, constants and functions (see help)">
+                        <param name="hfine_index" type="integer" value="" label="Index of nuclear coupled spin" min="1" help="Non-electronic spin - muon or otherwise. Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
+                        <param name="hfine_e_index" type="integer" value="" optional="true" min="1" label="Index of electronic coupled spin" help="Optional, will use first defined electronic spin if unspecified"/>
+                        <param name="hfine_matrix" area="true" type="text" value="" label="Hyperfine coupling tensor" help="Define 3X3 tensor for coupling between electron and non-electron spins (in MHz). Allows default expressions, constants and functions (see help)">
                             <sanitizer>
                                 <valid initial="string.printable">
                                 </valid>
@@ -91,21 +85,13 @@
                         </param>
                     </when>
                     <when value="dipolar">
-                        <param name="di_index" type="integer" value="" min="1" label="Index of 1st coupled spin"
-                               help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
-                        <param name="di_index_2" type="integer" value="" label="Index of 2nd coupled spin"
-                               help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
-                        <param name="di_vector" type="text" value="" label="Dipole coupling vector"
-                               help="Define 1X3 vector for coupling between two spins (Angstrom).
-                               Allows default expressions, constants and functions (see help)"/>
+                        <param name="di_index" type="integer" value="" min="1" label="Index of 1st coupled spin" help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
+                        <param name="di_index_2" type="integer" value="" label="Index of 2nd coupled spin" help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
+                        <param name="di_vector" type="text" value="" label="Dipole coupling vector" help="Define 1X3 vector for coupling between two spins (Angstrom). Allows default expressions, constants and functions (see help)"/>
                     </when>
                     <when value="quadrupolar">
-                        <param name="quad_index" type="integer" value="" label="Index of coupled spin"
-                               help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
-                        <param name="quad_matrix" area="true" type="text" value="" label="Electric Field Gradient tensor"
-                               help="Define 3X3 tensor (in atomic units) for quadrupolar coupling.
-                               Allows default expressions, constants and functions (see help).
-                               Warning: spins with zero quadrupole moment will have zero coupling regardless of the input">
+                        <param name="quad_index" type="integer" value="" label="Index of coupled spin" help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
+                        <param name="quad_matrix" area="true" type="text" value="" label="Electric Field Gradient tensor" help="Define 3X3 tensor (in atomic units) for quadrupolar coupling. Allows default expressions, constants and functions (see help). Warning: spins with zero quadrupole moment will have zero coupling regardless of the input">
                             <sanitizer>
                                 <valid initial="string.printable">
                                 </valid>
@@ -113,204 +99,72 @@
                         </param>
                     </when>
                     <when value="dissipation">
-                        <param name="dis_index" type="integer" value="" label="Index of spin with dissipation"
-                               help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
-                        <param name="dis_val" type="text" value="" label="Dissipation"
-                               help="Define dissipation term (MHz).
-                               Allows default expressions, constants and functions (see help)"/>
+                        <param name="dis_index" type="integer" value="" label="Index of spin with dissipation" help="Index refers to the order of the spins listed in 'spins' section.  The first spin in the list has index 1, the second has index 2, and so on."/>
+                        <param name="dis_val" type="text" value="" label="Dissipation" help="Define dissipation term (MHz). Allows default expressions, constants and functions (see help)"/>
                     </when>
                 </conditional>
             </repeat>
         </section>
         <section name="experiment_params" expanded="true" title="Experiment Parameters">
             <conditional name="experiment">
-                <param name="experiment_preset" type="select" value='custom' display="radio" label="Experiment type"
-                       help="Experiment preset to use. Avoided Level Crossing (ALC): sets polarization to longitudinal, x-axis to field and y-axis to integral.
-                             Zero field: sets polarization to transverse and field to 0. Choose custom for no preset">
+                <param name="experiment_preset" type="select" value="custom" display="radio" label="Experiment type" help="Experiment preset to use. Avoided Level Crossing (ALC): sets polarization to longitudinal, x-axis to field and y-axis to integral. Zero field: sets polarization to transverse, field to 0, x-axis to time and y-axis to asymmetry. Choose custom for no preset">
                     <option value="alc">Avoided Level Crossing (ALC)</option>
                     <option value="zero_field">Zero Field</option>
                     <option selected="true" value="custom">Custom</option>
                 </param>
                 <when value="alc">
-                    <repeat name="fields" title="Fields" help="Magnetic fields">
-                        <param name="field" type="text" value="0" label="Field"
-                            optional="true" help="Optional, (default is 0).
-                            Accepts 1X3 vector, or scalar value. Scalar value means field is assumed to be aligned with z axis.
-                            Allows default expressions, constants and functions, plus MHz, muon_gyr constants and special 'range()' function (see help section)"/>
-                    </repeat>
+                    <expand macro="restricted_axes_options"/>
+                    <expand macro="fields"/>
+                    <expand macro="temperatures"/>
+                    <expand macro="orientations"/>
+                    <expand macro="euler_convention"/>
                 </when>
                 <when value="zero_field">
-                    <param name="x_axis" type="select" value="time" display="radio" label="X axis"
-                           help="Range to use as X axis for output file(s)">
-                        <option selected="true" value="time">time</option>
-                        <option value="field">field</option>
-                    </param>
-                    <param name="y_axis" type="select" value="asymmetry" display="radio" label="Y axis"
-                           help="Range to use as y axis for output file(s)
-                                Asymmetry: use muon's polarization.
-                                Integral: use muon's polarization integral over time.
-                                WARNING: if integral chosen, 'time' parameter is ignored, and cannot be used as x-axis parameter ">
-                        <option value="integral">integral</option>
-                        <option selected="true" value="asymmetry">asymmetry</option>
-                    </param>
+                    <expand macro="restricted_axes_options"/>
+                    <expand macro="times"/>
+                    <expand macro="temperatures"/>
+                    <expand macro="orientations"/>
+                    <expand macro="euler_convention"/>
+                    <expand macro="celio_options"/>
                 </when>
                 <when value="custom">
-                    <param name="x_axis" type="select" value="time" display="radio" label="X axis"
-                           help="Range to use as X axis for output file(s)">
-                        <option selected="true" value="time">time</option>
-                        <option value="field">field</option>
-                    </param>
-                    <param name="y_axis" type="select" value="asymmetry" display="radio" label="Y axis"
-                           help="Range to use as y axis for output file(s)
-                                Asymmetry: use muon's polarization.
-                                Integral: use muon's polarization integral over time.
-                                WARNING: if integral chosen, 'time' parameter is ignored, and cannot be used as x-axis parameter ">
-                        <option value="integral">integral</option>
-                        <option selected="true" value="asymmetry">asymmetry</option>
-                    </param>
-                    <repeat name="fields" title="Fields" help="Magnetic fields">
-                         <param name="field" type="text" value="0" label="Field (T)"
-                            optional="true" help="Optional, (default is 0).
-                            Accepts 1X3 vector, or scalar value. Scalar value means field is assumed to be aligned with z axis.
-                            Allows default expressions, constants and functions, plus MHz, muon_gyr constants and special 'range()' function (see help section)"/>
-                    </repeat>
-                    <repeat name="polarizations" title="Polarizations" help="The direction along which the muon
-                    should be polarized when starting, as well as the one in which it will be measured.
-                    Each entry will generate a separate calculation when muspinsim is run">
-                        <conditional name="polarization_options">
-                            <param name="polarization_preset" value="custom" type="select" display="radio" label="Polarization"
-                                   help="transverse: along x-axis, longitudinal: along z-axis, custom: define vector">
-                                <option value="longitudinal">longditudinal</option>
-                                <option value="transverse">transverse</option>
-                                <option selected="true" value="custom">custom</option>
-                            </param>
-                            <when value="custom">
-                                <param name="polarization" type="text" value="" label="Enter custom vector for polarization"
-                                       help="Accepts 1X3 vector.
-                                       Allows default expressions, constants and functions (see help section)"/>
-                            </when>
-                            <when value="longitudinal" />
-                            <when value="transverse" />
-                        </conditional>
-                    </repeat>
+                    <expand macro="axes_options"/>
+                    <expand macro="times"/>
+                    <expand macro="fields"/>
+                    <expand macro="intrinsic_fields"/>
+                    <expand macro="temperatures"/>
+                    <expand macro="polarizations"/>
+                    <expand macro="orientations"/>
+                    <expand macro="euler_convention"/>
+                    <expand macro="celio_options"/>
                 </when>
             </conditional>
-            <param name="average_axes" type="select" display="checkboxes" multiple="true" optional="true" value="orientation" label="Average axes"
-                   help="Keywords that should have an average carried out over them. Each keyword ticked should have a range specified.
-                   Keywords not ticked or set as X axis, but which have a range set, will generate separate calculations for each value in the range when muspinsim is run.">
-                <option selected="true" value="orientation">orientation</option>
-                <option value="polarization">polarization</option>
-                <option value="field">field</option>
-                <option value="time">time</option>
-                <option value="temperature">temperature</option>
-            </param>
-            <repeat name="orientations" title="Orientations" help="Orientations to use for crystallites - (define powder averages)">
-                <conditional name="orientation_options">
-                    <param name="orientation_preset" type="select" display="radio" label="Orientation" optional="false"
-                           help="Polar angles: Define two polar angles θ and ϕ, defining only the direction of the z-axis (recommended for powder averages).
-                                Euler angles: Define 3 Euler angles defining a new frame, convention used is ZYZ by default.
-                                Euler angles with weight: define 3 Euler angles and a weight (will be normalized automatically).
-                                Euler angles helper function: eulrange(n).
-                                Zaremba-Conroy-Wolfsberg helper function: zcw(n)">
-                        <option value="2_polar">Polar angles</option>
-                        <option value="3_euler">Euler angles</option>
-                        <option value="4_euler">Euler angles with weight</option>
-                        <option value="eulrange">Euler angles helper function (eulrange(n))</option>
-                        <option selected="true" value="zcw">Zaremba-Conroy-Wolfsberg helper function (zcw(n))</option>
-                    </param>
-                    <when value="eulrange">
-                        <param name="eul_n" type="text" value="0" label="n"
-                               help="value for n for eulrange(n),
-                               Allows default expressions, constants and functions.
-                               WARNING: large values are more computationally expensive."
-                        />
-                    </when>
-                    <when value="zcw">
-                        <param name="zcw_n" type="text" value="0" label="n"
-                               help="value for n for zcw(n)
-                               Allows default expressions, constants and functions.
-                               WARNING: large values are more computationally expensive."
-                        />
-                    </when>
-                    <when value="2_polar">
-                        <param name="theta" type="text" value="" label="θ (theta/inclination) angle"
-                               help="All polar angle entries allow default expressions, constants and functions"/>
-                        <param name="phi" type="text" value="" label="ϕ (phi/azimuth) angle"
-                               help="All polar angle entries allow default expressions, constants and functions"/>
-                    </when>
-                    <when value="3_euler">
-                        <param name="eul_1" type="text" value="" label="Euler angle 1"
-                               help="All Euler angle entries allow default expressions, constants and functions"/>
-                        <param name="eul_2" type="text" value="" label="Euler angle 2"
-                               help="All Euler angle entries allow default expressions, constants and functions"/>
-                        <param name="eul_3" type="text" value="" label="Euler angle 3"
-                               help="All Euler angle entries allow default expressions, constants and functions"/>
-
-                    </when>
-                    <when value="4_euler">
-                        <param name="eul_1" type="text" value="" label="Euler angle 1"
-                               help="All Euler angle entries allow default expressions and constants"/>
-                        <param name="eul_2" type="text" value="" label="Euler angle 2"
-                               help="All Euler angle entries allow default expressions and constants"/>
-                        <param name="eul_3" type="text" value="" label="Euler angle 3"
-                               help="All Euler angle entries allow default expressions and constants"/>
-                        <param name="weight" type="float" value="0" label="Weight"
-                               help="Allows only floating point value, weights will automatically be normalised"/>
-                    </when>
-                </conditional>
-            </repeat>
-            <param name="euler_convention" type="select" display="radio" label="Euler Convention"
-                   help="Euler angle convention to use for orientation definitions (ignored if Euler angles not defined)">
-                <option selected="true" value="zyz">ZYZ</option>
-                <option value="zxz">ZXZ</option>
-            </param>
-            <repeat name="times" title="Time" help="A time or range of times (μs)">
-                <param name="time" type="text" label="Time"
-                       help="Either a single time value or `range` function
-                       Allows default expressions, default constants, and special 'range()' function (see help section)"
-                       optional="true" value="range(0, 10, 101)"/>
-            </repeat>
-            <repeat name="temperatures" title="Temperature" help="Temperature or range of temperatures (K)">
-                <param name="temperature" type="text"
-                       help="Either single value or 'range()' function
-                       Allows default expressions, default constants, and special 'range()' function (see help section).
-                       Warning: both density matrices and dissipative couplings for finite temperatures are only calculated approximatively, see muspinsim docs."
-                       optional="true" value="inf"/>
-            </repeat>
         </section>
         <section name="fitting_params" expanded="true" title="Fitting Parameters">
             <conditional name="fitting_options">
-                <param name="fitting" type="select" display="radio" optional="false" value="" label="Fit experimental data with simulations"
-                       help="Fitting requires a file with data to fit. File must be given in muspinsim tool, or by manually setting filepath for keyword 'fitting_data' if running muspinsim externally">
+                <param name="fitting" type="select" display="radio" optional="false" value="" label="Fit experimental data with simulations" help="Fitting requires a file with data to fit. File must be given in MuSpinSim tool, or by manually setting filepath for keyword 'fitting_data' if running MuSpinSim externally">
                     <option value="true">Yes</option>
                     <option selected="true" value="">No</option>
                 </param>
                 <when value="true">
-                    <param name="fitting_method" type="select" display="radio" optional="false" value="nelder-mead" label="Method to use to fit the data"
-                           help="See the help section for a description of each method">
+                    <param name="fitting_method" type="select" display="radio" optional="false" value="nelder-mead" label="Method to use to fit the data" help="See the help section for a description of each method">
                         <option selected="true" value="nelder-mead">Nelder-Mead</option>
                         <option value="lbfgs">L-BFGS</option>
                     </param>
                     <repeat name="fitting_variables" title="Variable to fit to the experimental data">
                         <param name="var_name" type="text" optional="false" label="Name of the variable"/>
-                        <param name="start_val" type="text" value="0" label="Starting value"
-                               help="Allows default expressions, constants and functions, plus MHz, muon_gyr constants. Cannot contain names of other variables"/>
-                        <param name="min_bound" type="text" value="-inf" label="minimum bound"
-                               help="Allows default expressions, constants and functions, plus MHz, muon_gyr constants"/>
-                        <param name="max_bound" type="text" value="inf" label="maximum bound"
-                               help="Allows default expressions, constants and functions, plus MHz, muon_gyr constants"/>
+                        <param name="start_val" type="text" value="0" label="Starting value" help="Allows default expressions, constants and functions, plus MHz, muon_gyr constants. Cannot contain names of other variables"/>
+                        <param name="min_bound" type="text" value="-inf" label="minimum bound" help="Allows default expressions, constants and functions, plus MHz, muon_gyr constants"/>
+                        <param name="max_bound" type="text" value="inf" label="maximum bound" help="Allows default expressions, constants and functions, plus MHz, muon_gyr constants"/>
                     </repeat>
-                    <param name="fitting_tolerance" type="float" optional='true' value="" label="Fitting Tolerance"
-                           help="Used as the tol parameter in Scipy's scipy.optimize.minimize method. Will use scipy defaults if left blank.
-                           Does not accept expressions/functions/constants"
-                    />
+                    <param name="fitting_tolerance" type="float" optional="true" value="" label="Fitting Tolerance" help="Used as the tol parameter in Scipy's scipy.optimize.minimize method. Will use scipy defaults if left blank. Does not accept expressions/functions/constants"/>
                 </when>
                 <when value=""/>
             </conditional>
         </section>
     </inputs>
     <outputs>
-        <data format="txt" label="muspinsim input file $out_file_prefix" name="out_file" from_work_dir="outfile.in" />
+        <data format="txt" label="muspinsim input file $out_file_prefix" name="out_file" from_work_dir="outfile.in"/>
     </outputs>
     <tests>
         <test expect_num_outputs="1">
@@ -319,24 +173,29 @@
             <param name="spin" value="H"/>
             <param name="spin_preset" value="mu"/>
             <param name="interaction" value="zeeman"/>
-            <param name="zeeman_index" value="1" />
-            <param name="zeeman_vector" value="1 0 0" />
-            <param name="zeeman_index" value="2" />
-            <param name="zeeman_vector" value="2 0 0" />
-            <output name="out_file" file="test_1.in" ftype="txt" compare="diff" />
+            <param name="zeeman_index" value="1"/>
+            <param name="zeeman_vector" value="1 0 0"/>
+            <param name="zeeman_index" value="2"/>
+            <param name="zeeman_vector" value="2 0 0"/>
+            <param name="y_axis" value="asymmetry"/>
+            <param name="x_axis" value="time"/>
+            <param name="average_axes" value="orientation"/>
+            <output name="out_file" file="test_1.in" ftype="txt" compare="diff"/>
         </test>
         <test expect_num_outputs="1">
             <param name="out_file_prefix" value="test_2"/>
+            <param name="experiment_preset" value="custom"/>
             <param name="spin_preset" value="e"/>
             <param name="spin_preset" value="mu"/>
             <param name="interaction" value="hyperfine"/>
-            <param name="hfine_index" value="1" />
+            <param name="hfine_index" value="1"/>
             <param name="hfine_matrix" value="[1 0 0 sin(10) (5*2) 0 10*pi 5 cos(20)]"/>
-            <param name="time" value="range(0, 0.1)" />
-            <param name="y_axis" value="asymmetry" />
-            <param name="field" value="1.0" />
-            <param name="temperature" value="1.0" />
-            <output name="out_file" file="test_2.in" ftype="txt" compare="diff" />
+            <param name="time" value="range(0, 0.1)"/>
+            <param name="y_axis" value="asymmetry"/>
+            <param name="x_axis" value="time"/>
+            <param name="field" value="1.0"/>
+            <param name="temperature" value="1.0"/>
+            <output name="out_file" file="test_2.in" ftype="txt" compare="diff"/>
         </test>
         <test expect_num_outputs="1">
             <param name="out_file_prefix" value="test_3"/>
@@ -345,17 +204,17 @@
             <param name="spin_preset" value="mu"/>
             <param name="spin_preset" value="e"/>
             <param name="interaction" value="hyperfine"/>
-            <param name="hfine_index" value="2" />
+            <param name="hfine_index" value="2"/>
             <param name="hfine_matrix" value="[580 5  10 5   580 9 10  9   580]"/>
             <param name="interaction" value="hyperfine"/>
-            <param name="hfine_index" value="3" />
+            <param name="hfine_index" value="3"/>
             <param name="hfine_matrix" value="[(300/2)  3   4*10 ], [3  15*10 6-3+2]  ,[4  5   15 ]"/>
-            <param name="average_axes" value="polarization,temperature" />
-            <param name="experiment_preset" value="alc" />
-            <param name="field" value="range(1.8, 2.6, 100)" />
-            <param name="orientation_preset" value="zcw" />
-            <param name="zcw_n" value="20" />
-            <output name="out_file" file="test_3.in" ftype="txt" compare="diff" />
+            <param name="average_axes" value="orientation,temperature"/>
+            <param name="experiment_preset" value="alc"/>
+            <param name="field" value="range(1.8, 2.6, 100)"/>
+            <param name="orientation_preset" value="zcw"/>
+            <param name="zcw_n" value="20"/>
+            <output name="out_file" file="test_3.in" ftype="txt" compare="diff"/>
         </test>
         <test expect_num_outputs="1">
             <param name="out_file_prefix" value="test_4"/>
@@ -375,17 +234,21 @@
             <param name="interaction" value="dissipation"/>
             <param name="dis_index" value="1"/>
             <param name="dis_val" value="0.5"/>
-            <param name="average_axes" value="" />
-            <param name="experiment_preset" value="custom" />
+            <param name="average_axes" value=""/>
+            <param name="experiment_preset" value="custom"/>
             <param name="field" value="1.5e-2 1.0e-2 1.0e-2"/>
             <param name="field" value="0.01"/>
+            <param name="intrinsic_field" value="1.6e-2 1.1e-2 1.1e-2"/>
+            <param name="intrinsic_field" value="0.02"/>
             <param name="polarization_preset" value="custom"/>
             <param name="polarization" value="1 0 0"/>
             <param name="time" value="range(0,8.0,1000)"/>
             <param name="time" value="range(0,1.0)"/>
             <param name="orientation_preset" value="eulrange"/>
             <param name="eul_n" value="10"/>
-            <output name="out_file" file="test_4.in" ftype="txt" compare="diff" />
+            <param name="celio_enabled" value="true"/>
+            <param name="celio_k" value="10"/>
+            <output name="out_file" file="test_4.in" ftype="txt" compare="diff"/>
         </test>
         <test expect_num_outputs="1">
             <param name="out_file_prefix" value="test_5"/>
@@ -393,24 +256,58 @@
             <param name="interaction" value="dissipation"/>
             <param name="dis_index" value="1"/>
             <param name="dis_val" value="g"/>
-            <param name="experiment_preset" value="custom" />
-            <param name="field" value="1.0/muon_gyr" />
+            <param name="experiment_preset" value="custom"/>
+            <param name="field" value="1.0/muon_gyr"/>
+            <param name="intrinsic_field" value="2.0/muon_gyr"/>
             <param name="fitting" value="true"/>
-            <param name="var_name" value="g" />
-            <param name="min_bound" value="0.0" />
-            <param name="fitting_tolerance" value="1.0" />
-            <output name="out_file" file="test_5.in" ftype="txt" compare="diff" />
+            <param name="var_name" value="g"/>
+            <param name="min_bound" value="0.0"/>
+            <param name="fitting_tolerance" value="1.0"/>
+            <param name="celio_enabled" value="true"/>
+            <param name="celio_k" value="10"/>
+            <param name="celio_averages" value="4"/>
+            <output name="out_file" file="test_5.in" ftype="txt" compare="diff"/>
+        </test>
+        <test expect_num_outputs="1">
+            <param name="out_file_prefix" value="test_6"/>
+            <param name="experiment_preset" value="zero_field"/>
+            <param name="spin_preset" value="custom"/>
+            <param name="spin" value="H"/>
+            <param name="spin_preset" value="mu"/>
+            <param name="interaction" value="zeeman"/>
+            <param name="zeeman_index" value="1"/>
+            <param name="zeeman_vector" value="1 0 0"/>
+            <param name="zeeman_index" value="2"/>
+            <param name="zeeman_vector" value="2 0 0"/>
+            <param name="average_axes" value="orientation,temperature"/>
+            <output name="out_file" file="test_6.in" ftype="txt" compare="diff"/>
+        </test>
+        <test expect_num_outputs="1">
+            <param name="out_file_prefix" value="test_7"/>
+            <param name="experiment_preset" value="custom"/>
+            <param name="spin_preset" value="custom"/>
+            <param name="spin" value="H"/>
+            <param name="spin_preset" value="mu"/>
+            <param name="interaction" value="zeeman"/>
+            <param name="zeeman_index" value="1"/>
+            <param name="zeeman_vector" value="1 0 0"/>
+            <param name="zeeman_index" value="2"/>
+            <param name="zeeman_vector" value="2 0 0"/>
+            <param name="y_axis" value="integral"/>
+            <param name="x_axis" value="field"/>
+            <param name="average_axes" value="orientation,temperature"/>
+            <output name="out_file" file="test_7.in" ftype="txt" compare="diff"/>
         </test>
     </tests>
     <help><![CDATA[
-    Tool to create input parameter file for Muspinsim.
+    Tool to create input parameter file for MuSpinSim.
 
 
-    This tool creates a structured text file with keywords and values which describe the system to model for Muspinsim.
-    See muspinsim docs for more information https://muon-spectroscopy-computational-project.github.io/muspinsim/input/.
+    This tool creates a structured text file with keywords and values which describe the system to model for MuSpinSim.
+    See MuSpinSim docs for more information https://muon-spectroscopy-computational-project.github.io/muspinsim/input/.
 
 
-    Muspinsim allows expressions and special functions to be used when defining certain keywords. This tool also allows this.
+    MuSpinSim allows expressions and special functions to be used when defining certain keywords. This tool also allows this.
     Check the hint at the bottom of each input to see what, if any, special function or expressions can be used.
 
 
@@ -469,18 +366,7 @@
         - Scipy default tolerance is :code:`1e-5`
     ]]></help>
     <citations>
-        <citation type="bibtex">
-            @article{nelder_mead_1965,
-                title={A Simplex Method for Function Minimization},
-                volume={7},
-                doi={10.1093/comjnl/7.4.308},
-                number={4},
-                journal={The Computer Journal},
-                author={Nelder, J. A. and Mead, R.},
-                year={1965},
-                pages={308-313}
-            }
-        </citation>
+        <citation type="doi">10.1093/comjnl/7.4.308</citation>
         <citation type="bibtex">
             @book{nocedal_wright_2006,
                 place={New York (NY)},
@@ -490,8 +376,9 @@
                 year={2006}
             }
         </citation>
+        <citation type="doi">10.1103/PhysRevLett.56.2720</citation>
         <citation type="bibtex">
             @TOOL_CITATION@
         </citation>
     </citations>
-</tool>
\ No newline at end of file
+</tool>
--- a/test-data/test_1.in	Thu Sep 15 10:23:52 2022 +0000
+++ b/test-data/test_1.in	Fri Feb 03 15:39:07 2023 +0000
@@ -1,7 +1,7 @@
 
 #######################################################
-#Muspinsim Input File
-#Generated using Muon Galaxy Tool Muspinsim_Input
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
 #######################################################
 
 name
--- a/test-data/test_2.in	Thu Sep 15 10:23:52 2022 +0000
+++ b/test-data/test_2.in	Fri Feb 03 15:39:07 2023 +0000
@@ -1,7 +1,7 @@
 
 #######################################################
-#Muspinsim Input File
-#Generated using Muon Galaxy Tool Muspinsim_Input
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
 #######################################################
 
 name
@@ -14,13 +14,13 @@
     10*pi 5 cos(20)
 average_axes
     orientation
-time
-    range(0,0.1)
-temperature
-    1.0
 x_axis
     time
 y_axis
     asymmetry
+time
+    range(0,0.1)
 field
     1.0
+temperature
+    1.0
--- a/test-data/test_3.in	Thu Sep 15 10:23:52 2022 +0000
+++ b/test-data/test_3.in	Fri Feb 03 15:39:07 2023 +0000
@@ -1,7 +1,7 @@
 
 #######################################################
-#Muspinsim Input File
-#Generated using Muon Galaxy Tool Muspinsim_Input
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
 #######################################################
 
 name
@@ -17,11 +17,11 @@
     5 580 9
     10 9 580
 average_axes
-    polarization
+    orientation
     temperature
+field
+    range(1.8,2.6,100)
 orientation zyz
     zcw(20)
-field
-    range(1.8,2.6,100)
 experiment
     alc
--- a/test-data/test_4.in	Thu Sep 15 10:23:52 2022 +0000
+++ b/test-data/test_4.in	Fri Feb 03 15:39:07 2023 +0000
@@ -1,7 +1,7 @@
 
 #######################################################
-#Muspinsim Input File
-#Generated using Muon Galaxy Tool Muspinsim_Input
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
 #######################################################
 
 name
@@ -14,17 +14,24 @@
     -0.9 -0.9 0
 dipolar 1 2
     0.9 0.9 0
-orientation zyz
-    eulrange(10)
-time
-    range(0,1.0)
-    range(0,8.0,1000)
+average_axes
+    none
 x_axis
     time
 y_axis
     asymmetry
+time
+    range(0,1.0)
+    range(0,8.0,1000)
 field
     0.01
     1.5e-2 1.0e-2 1.0e-2
+intrinsic_field
+    0.02
+    1.6e-2 1.1e-2 1.1e-2
 polarization
     1 0 0
+orientation zyz
+    eulrange(10)
+celio
+    10
--- a/test-data/test_5.in	Thu Sep 15 10:23:52 2022 +0000
+++ b/test-data/test_5.in	Fri Feb 03 15:39:07 2023 +0000
@@ -1,7 +1,7 @@
 
 #######################################################
-#Muspinsim Input File
-#Generated using Muon Galaxy Tool Muspinsim_Input
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
 #######################################################
 
 name
@@ -10,8 +10,6 @@
     mu
 dissipation 1
     g
-average_axes
-    orientation
 fitting_method
     nelder-mead
 fitting_variables
@@ -20,9 +18,15 @@
     1.0
 fitting_data
     load("fitting_data.dat")
+average_axes
+    orientation
 x_axis
     time
 y_axis
     asymmetry
 field
     1.0/muon_gyr
+intrinsic_field
+    2.0/muon_gyr
+celio
+    10 4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/test_6.in	Fri Feb 03 15:39:07 2023 +0000
@@ -0,0 +1,19 @@
+
+#######################################################
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
+#######################################################
+
+name
+    test_6
+spins
+    mu H
+zeeman 2
+    2 0 0
+zeeman 1
+    1 0 0
+average_axes
+    orientation
+    temperature
+experiment
+    zero_field
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/test_7.in	Fri Feb 03 15:39:07 2023 +0000
@@ -0,0 +1,21 @@
+
+#######################################################
+# Muspinsim Input File
+# Generated using Muon Galaxy Tool Muspinsim_Input
+#######################################################
+
+name
+    test_7
+spins
+    mu H
+zeeman 2
+    2 0 0
+zeeman 1
+    1 0 0
+x_axis
+    field
+average_axes
+    orientation
+    temperature
+y_axis
+    integral