# This file was auto generated by Styx.
# Do not edit this file directly.

import typing
import pathlib
from styxdefs import *

VOLUME_DISTORTION_METADATA = Metadata(
    id="71b5b8e30a3f86c052e4af8965fbf9041a247504.workbench",
    name="volume-distortion",
    package="workbench",
    container_image_tag="nx10x/workbench:2.1.0",
)


_VolumeDistortionParamsDictNoTag = typing.TypedDict('_VolumeDistortionParamsDictNoTag', {
    "warpfield": str,
    "volume-out": str,
    "source-volume": typing.NotRequired[str | None],
    "log2": bool,
    "circular": bool,
})
VolumeDistortionParamsDictTagged = typing.TypedDict('VolumeDistortionParamsDictTagged', {
    "@type": typing.Literal["workbench/volume-distortion"],
    "warpfield": str,
    "volume-out": str,
    "source-volume": typing.NotRequired[str | None],
    "log2": bool,
    "circular": bool,
})
VolumeDistortionParamsDict = _VolumeDistortionParamsDictNoTag | VolumeDistortionParamsDictTagged


class VolumeDistortionOutputs(typing.NamedTuple):
    """
    Output object returned when calling `VolumeDistortionParamsDict(...)`.
    """
    root: OutputPathType
    """Output root folder. This is the root folder for all outputs."""
    volume_out: OutputPathType
    """the output distortion measures"""


def volume_distortion_params(
    warpfield: str,
    volume_out: str,
    source_volume: str | None = None,
    log2: bool = False,
    circular: bool = False,
) -> VolumeDistortionParamsDictTagged:
    """
    Build parameters.
    
    Args:
        warpfield: the warpfield to compute the distortion of.
        volume_out: the output distortion measures.
        source_volume: MUST be used if using a fnirt warpfield\
            \
            the source volume used when generating the warpfield.
        log2: apply base-2 log transform.
        circular: use the circle-based formula for the anisotropic measure.
    Returns:
        Parameter dictionary
    """
    params = {
        "@type": "workbench/volume-distortion",
        "warpfield": warpfield,
        "volume-out": volume_out,
        "log2": log2,
        "circular": circular,
    }
    if source_volume is not None:
        params["source-volume"] = source_volume
    return params


def volume_distortion_validate(
    params: typing.Any,
) -> None:
    """
    Validate parameters. Throws an error if `params` is not a valid
    `VolumeDistortionParamsDict` object.
    
    Args:
        params: The parameters object to validate.
    """
    if params is None or not isinstance(params, dict):
        raise StyxValidationError(f'Params object has the wrong type \'{type(params)}\'')
    if params.get("warpfield", None) is None:
        raise StyxValidationError("`warpfield` must not be None")
    if not isinstance(params["warpfield"], str):
        raise StyxValidationError(f'`warpfield` has the wrong type: Received `{type(params.get("warpfield", None))}` expected `str`')
    if params.get("volume-out", None) is None:
        raise StyxValidationError("`volume-out` must not be None")
    if not isinstance(params["volume-out"], str):
        raise StyxValidationError(f'`volume-out` has the wrong type: Received `{type(params.get("volume-out", None))}` expected `str`')
    if params.get("source-volume", None) is not None:
        if not isinstance(params["source-volume"], str):
            raise StyxValidationError(f'`source-volume` has the wrong type: Received `{type(params.get("source-volume", None))}` expected `str | None`')
    if params.get("log2", False) is None:
        raise StyxValidationError("`log2` must not be None")
    if not isinstance(params["log2"], bool):
        raise StyxValidationError(f'`log2` has the wrong type: Received `{type(params.get("log2", False))}` expected `bool`')
    if params.get("circular", False) is None:
        raise StyxValidationError("`circular` must not be None")
    if not isinstance(params["circular"], bool):
        raise StyxValidationError(f'`circular` has the wrong type: Received `{type(params.get("circular", False))}` expected `bool`')


def volume_distortion_cargs(
    params: VolumeDistortionParamsDict,
    execution: Execution,
) -> list[str]:
    """
    Build command-line arguments from parameters.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Command-line arguments.
    """
    cargs = []
    cargs.extend([
        "wb_command",
        "-volume-distortion"
    ])
    cargs.append(params.get("warpfield", None))
    cargs.append(params.get("volume-out", None))
    if params.get("source-volume", None) is not None:
        cargs.extend([
            "-fnirt",
            params.get("source-volume", None)
        ])
    if params.get("log2", False):
        cargs.append("-log2")
    if params.get("circular", False):
        cargs.append("-circular")
    return cargs


def volume_distortion_outputs(
    params: VolumeDistortionParamsDict,
    execution: Execution,
) -> VolumeDistortionOutputs:
    """
    Build outputs object containing output file paths and possibly stdout/stderr.
    
    Args:
        params: The parameters.
        execution: The execution object for resolving input paths.
    Returns:
        Outputs object.
    """
    ret = VolumeDistortionOutputs(
        root=execution.output_file("."),
        volume_out=execution.output_file(params.get("volume-out", None)),
    )
    return ret


def volume_distortion_execute(
    params: VolumeDistortionParamsDict,
    runner: Runner | None = None,
) -> VolumeDistortionOutputs:
    """
    CALCULATE VOLUME WARPFIELD DISTORTION.
    
    Calculates isotropic and anisotropic distortions in the volume warpfield. At
    each voxel, the gradient of the absolute warpfield is computed to obtain the
    local affine transforms for each voxel (jacobian matrices), and strain
    tensors are derived from them. The isotropic component (volumetric expansion
    ratio) is the product of the three principal strains. The default measure
    ('elongation') for the anisotropic component is the largest principal strain
    divided by the smallest.
    
    The -circular option instead calculates the anisotropic component by
    transforming the principal strains into log space, considering them as
    x-values of points on a circle 120 degrees apart, finds the circle's
    diameter, and transforms that back to a ratio.
    
    Args:
        params: The parameters.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `VolumeDistortionOutputs`).
    """
    volume_distortion_validate(params)
    runner = runner or get_global_runner()
    execution = runner.start_execution(VOLUME_DISTORTION_METADATA)
    params = execution.params(params)
    cargs = volume_distortion_cargs(params, execution)
    ret = volume_distortion_outputs(params, execution)
    execution.run(cargs)
    return ret


def volume_distortion(
    warpfield: str,
    volume_out: str,
    source_volume: str | None = None,
    log2: bool = False,
    circular: bool = False,
    runner: Runner | None = None,
) -> VolumeDistortionOutputs:
    """
    CALCULATE VOLUME WARPFIELD DISTORTION.
    
    Calculates isotropic and anisotropic distortions in the volume warpfield. At
    each voxel, the gradient of the absolute warpfield is computed to obtain the
    local affine transforms for each voxel (jacobian matrices), and strain
    tensors are derived from them. The isotropic component (volumetric expansion
    ratio) is the product of the three principal strains. The default measure
    ('elongation') for the anisotropic component is the largest principal strain
    divided by the smallest.
    
    The -circular option instead calculates the anisotropic component by
    transforming the principal strains into log space, considering them as
    x-values of points on a circle 120 degrees apart, finds the circle's
    diameter, and transforms that back to a ratio.
    
    Args:
        warpfield: the warpfield to compute the distortion of.
        volume_out: the output distortion measures.
        source_volume: MUST be used if using a fnirt warpfield\
            \
            the source volume used when generating the warpfield.
        log2: apply base-2 log transform.
        circular: use the circle-based formula for the anisotropic measure.
        runner: Command runner.
    Returns:
        NamedTuple of outputs (described in `VolumeDistortionOutputs`).
    """
    params = volume_distortion_params(
        warpfield=warpfield,
        volume_out=volume_out,
        source_volume=source_volume,
        log2=log2,
        circular=circular,
    )
    return volume_distortion_execute(params, runner)


__all__ = [
    "VOLUME_DISTORTION_METADATA",
    "VolumeDistortionOutputs",
    "VolumeDistortionParamsDict",
    "VolumeDistortionParamsDictTagged",
    "volume_distortion",
    "volume_distortion_execute",
    "volume_distortion_params",
]
