Пример #1
0
def convert_IEC_angle_to_bipolar(angle):
    angle = np.copy(angle)
    if np.all(angle == 180):
        return angle

    angle[angle > 180] = angle[angle > 180] - 360

    is_180 = np.where(angle == 180)[0]
    not_180 = np.where(np.invert(angle == 180))[0]

    where_closest_left_leaning = np.argmin(np.abs(is_180[:, None] -
                                                  not_180[None, :]),
                                           axis=1)
    where_closest_right_leaning = (len(not_180) - 1 - np.argmin(
        np.abs(is_180[::-1, None] - not_180[None, ::-1]), axis=1)[::-1])

    closest_left_leaning = not_180[where_closest_left_leaning]
    closest_right_leaning = not_180[where_closest_right_leaning]

    assert np.all(
        np.sign(angle[closest_left_leaning]) == np.sign(
            angle[closest_right_leaning])
    ), "Unable to automatically determine whether angle is 180 or -180"

    angle[is_180] = np.sign(angle[closest_left_leaning]) * angle[is_180]

    return angle
Пример #2
0
def calc_logfile_mu_density_bygantry(index,
                                     config,
                                     logfile_group,
                                     gantry_angle,
                                     grid_resolution=1):
    logfile_mu_density = None

    for filehash in logfile_group:
        filepath = get_filepath(index, config, filehash)
        logfile_delivery_data = pymedphys.Delivery.from_logfile(filepath)

        a_logfile_mu_density = [
            pymedphys.mudensity.grid(grid_resolution=grid_resolution),
            logfile_delivery_data.mudensity(gantry_angle,
                                            grid_resolution=grid_resolution),
        ]

        if logfile_mu_density is None:
            logfile_mu_density = a_logfile_mu_density
        else:
            assert np.all(logfile_mu_density[0] == a_logfile_mu_density[0])
            assert np.all(logfile_mu_density[1] == a_logfile_mu_density[1])
            logfile_mu_density[2] += a_logfile_mu_density[2]

    return logfile_mu_density
Пример #3
0
def get_logfile_mu_density_bygantry(logfile_groups, mosaiq_gantry_angles,
                                    logfile_delivery_data_bygantry):
    logfile_mu_density_bygantry = dict()

    for logfile_group in logfile_groups:
        delivery_data = logfile_delivery_data_bygantry[logfile_group]
        logfile_mu_density_bygantry[logfile_group] = dict()

        for file_hash in logfile_group:
            for mosaiq_gantry_angle in mosaiq_gantry_angles:
                num_control_points = len(
                    delivery_data[file_hash][mosaiq_gantry_angle]["mu"])
                if num_control_points > 0:
                    mu_density = [
                        pymedphys.mudensity.grid(),
                        delivery_data[file_hash]
                        [mosaiq_gantry_angle].mudensity(),
                    ]

                    if (mosaiq_gantry_angle
                            not in logfile_mu_density_bygantry[logfile_group]):
                        logfile_mu_density_bygantry[logfile_group][
                            mosaiq_gantry_angle] = list(mu_density)
                    else:
                        assert np.all(
                            logfile_mu_density_bygantry[logfile_group]
                            [mosaiq_gantry_angle][0] == mu_density[0])
                        assert np.all(
                            logfile_mu_density_bygantry[logfile_group]
                            [mosaiq_gantry_angle][1] == mu_density[1])
                        logfile_mu_density_bygantry[logfile_group][
                            mosaiq_gantry_angle][2] += mu_density[2]

    return logfile_mu_density_bygantry
Пример #4
0
def create_transformed_mesh(width_data, length_data, factor_data):
    """Return factor data meshgrid."""
    x = np.arange(
        np.floor(np.min(width_data)) - 1,
        np.ceil(np.max(width_data)) + 1, 0.1)
    y = np.arange(
        np.floor(np.min(length_data)) - 1,
        np.ceil(np.max(length_data)) + 1, 0.1)

    xx, yy = np.meshgrid(x, y)

    zz = spline_model_with_deformability(
        xx,
        convert2_ratio_perim_area(xx, yy),
        width_data,
        convert2_ratio_perim_area(width_data, length_data),
        factor_data,
    )

    zz[xx > yy] = np.nan

    no_data_x = np.all(np.isnan(zz), axis=0)
    no_data_y = np.all(np.isnan(zz), axis=1)

    x = x[np.invert(no_data_x)]
    y = y[np.invert(no_data_y)]

    zz = zz[np.invert(no_data_y), :]
    zz = zz[:, np.invert(no_data_x)]

    return x, y, zz
Пример #5
0
def compare_logfile_group_bygantry(index,
                                   config,
                                   cursor,
                                   logfile_group,
                                   gantry_angle,
                                   grid_resolution=1):
    field_id = get_field_id_from_logfile_group(index, logfile_group)

    mosaiq_delivery_data = pymedphys.Delivery.from_mosaiq(cursor, field_id)

    mosaiq_mu_density = [
        pymedphys.mudensity.grid(grid_resolution=grid_resolution),
        mosaiq_delivery_data.mudensity(gantry_angle,
                                       grid_resolution=grid_resolution),
    ]

    normalisation = calc_normalisation(mosaiq_delivery_data)

    logfile_mu_density = calc_logfile_mu_density_bygantry(
        index, config, logfile_group, gantry_angle)

    grid_xx = logfile_mu_density[0]
    grid_yy = logfile_mu_density[1]
    assert np.all(grid_xx == mosaiq_mu_density[0])
    assert np.all(grid_yy == mosaiq_mu_density[1])

    comparison = calc_comparison(logfile_mu_density[2], mosaiq_mu_density[2],
                                 normalisation)

    print(comparison)
    plot_results(grid_xx, grid_yy, logfile_mu_density[2], mosaiq_mu_density[2])

    return comparison
Пример #6
0
def compare_mosaiq_fields(servers, field_ids):
    unique_servers = list(set(servers))

    with pymedphys.mosaiq.connect(unique_servers) as cursors:
        deliveries = [
            pymedphys.Delivery.from_mosaiq(cursors[server], field_id)
            for server, field_id in zip(servers, field_ids)
        ]

    mu_density_results = [
        delivery_data.mudensity() for delivery_data in deliveries
    ]

    mu_densities = [results[2] for results in mu_density_results]

    labels = [
        "Server: `{}` | Field ID: `{}`".format(server, field_id)
        for server, field_id in zip(servers, field_ids)
    ]

    plot_gantry_collimator(labels, deliveries)
    plot_mu_densities(labels, mu_density_results)

    mu_densities_match = np.all([
        np.all(np.abs(mu_density_a - mu_density_b) < 0.1)
        for mu_density_a, mu_density_b in itertools.combinations(
            mu_densities, 2)
    ])

    plt.show()
    print("MU Densities match: {}".format(mu_densities_match))

    return deliveries, mu_densities
Пример #7
0
    def from_mosaiq(cls, cursor, field_id):
        mosaiq_delivery_data = cls._from_mosaiq_base(cursor, field_id)
        reference_data = (
            mosaiq_delivery_data.monitor_units,
            mosaiq_delivery_data.mlc,
            mosaiq_delivery_data.jaw,
        )

        delivery_data = cls._from_mosaiq_base(cursor, field_id)
        test_data = (delivery_data.monitor_units, delivery_data.mlc, delivery_data.jaw)

        agreement = False

        while not agreement:
            agreements = []
            for ref, test in zip(reference_data, test_data):
                agreements.append(np.all(ref == test))

            agreement = np.all(agreements)
            if not agreement:
                print("Converted Mosaiq delivery data was conflicting.")
                print(
                    "MU agreement: {}\nMLC agreement: {}\n"
                    "Jaw agreement: {}".format(*agreements)
                )
                print("Trying again...")
                reference_data = test_data
                delivery_data = cls._from_mosaiq_base(cursor, field_id)
                test_data = (
                    delivery_data.monitor_units,
                    delivery_data.mlc,
                    delivery_data.jaw,
                )

        return delivery_data
Пример #8
0
def get_comparison_results(mosaiq_mu_density_bygantry,
                           logfile_mu_density_bygantry, normalisation):
    comparison_results = dict()
    mosaiq_gantry_angles = mosaiq_mu_density_bygantry.keys()
    logfile_groups = list(logfile_mu_density_bygantry.keys())

    for mosaiq_gantry_angle in mosaiq_gantry_angles:
        comparison_results[mosaiq_gantry_angle] = dict()
        comparison_results[mosaiq_gantry_angle]["comparisons"] = {}

        grid_xx = mosaiq_mu_density_bygantry[mosaiq_gantry_angle][0]
        grid_yy = mosaiq_mu_density_bygantry[mosaiq_gantry_angle][1]
        mosaiq_mu_density = mosaiq_mu_density_bygantry[mosaiq_gantry_angle][2]

        for logfile_group in logfile_groups:
            assert np.all(grid_xx == logfile_mu_density_bygantry[logfile_group]
                          [mosaiq_gantry_angle][0])
            assert np.all(grid_yy == logfile_mu_density_bygantry[logfile_group]
                          [mosaiq_gantry_angle][1])

            logfile_mu_density = logfile_mu_density_bygantry[logfile_group][
                mosaiq_gantry_angle][2]

            comparison = calc_comparison(logfile_mu_density, mosaiq_mu_density,
                                         normalisation)
            comparison_results[mosaiq_gantry_angle]["comparisons"][
                logfile_group] = comparison

        comparisons = np.array([
            comparison_results[mosaiq_gantry_angle]["comparisons"]
            [logfile_group] for logfile_group in logfile_groups
        ])

        comparison_results[mosaiq_gantry_angle]["median"] = np.median(
            comparisons)
        ref = np.argmin(
            np.abs(comparisons -
                   comparison_results[mosaiq_gantry_angle]["median"]))
        comparison_results[mosaiq_gantry_angle][
            "median_filehash_group"] = logfile_groups[ref]
        comparison_results[mosaiq_gantry_angle][
            "filehash_groups"] = logfile_groups

    return comparison_results
Пример #9
0
def fetch_and_verify_mosaiq_sql(cursor, field_id):
    reference_results = delivery_data_sql(cursor, field_id)
    test_results = delivery_data_sql(cursor, field_id)

    agreement = False

    while not agreement:
        agreements = []
        for ref, test in zip(reference_results, test_results):
            agreements.append(np.all(ref == test))

        agreement = np.all(agreements)
        if not agreement:
            print("Mosaiq sql query gave conflicting data.")
            print("Trying again...")
            reference_results = test_results
            test_results = delivery_data_sql(cursor, field_id)

    return test_results
Пример #10
0
def pcolormesh_grid(x, y, grid_resolution=None):
    if grid_resolution is None:
        diffs = np.hstack([np.diff(x), np.diff(y)])
        assert np.all(np.abs(diffs - diffs[0]) < 10 ** -12)

        grid_resolution = diffs[0]

    new_x = np.concatenate([x - grid_resolution / 2, [x[-1] + grid_resolution / 2]])
    new_y = np.concatenate([y - grid_resolution / 2, [y[-1] + grid_resolution / 2]])

    return new_x, new_y
Пример #11
0
def get_logfile_mosaiq_results(
    index, config, field_id_key_map, filehash, cursors, grid_resolution=1
):
    file_info = index[filehash]
    delivery_details = file_info["delivery_details"]
    field_id = delivery_details["field_id"]

    centre = get_centre(config, file_info)
    server = get_sql_servers(config)[centre]

    mosaiq_delivery_data = pymedphys.Delivery.from_mosaiq(cursors[server], field_id)

    mosaiq_results = mu_density_from_delivery_data(
        mosaiq_delivery_data, grid_resolution=grid_resolution
    )

    consecutive_keys = find_consecutive_logfiles(
        field_id_key_map, field_id, filehash, index
    )

    logfilepaths = [get_filepath(index, config, key) for key in consecutive_keys]

    logfile_results = calc_and_merge_logfile_mudensity(
        logfilepaths, grid_resolution=grid_resolution
    )

    try:
        assert np.all(logfile_results[0] == mosaiq_results[0])
        assert np.all(logfile_results[1] == mosaiq_results[1])
    except AssertionError:
        print(np.shape(logfile_results[0]))
        print(np.shape(mosaiq_results[0]))
        raise

    grid_xx = logfile_results[0]
    grid_yy = logfile_results[1]

    logfile_mu_density = logfile_results[2]
    mosaiq_mu_density = mosaiq_results[2]

    return grid_xx, grid_yy, logfile_mu_density, mosaiq_mu_density
Пример #12
0
    def _gantry_angle_masks(self,
                            gantry_angles,
                            gantry_tol,
                            allow_missing_angles=False):
        masks = [
            self._gantry_angle_mask(gantry_angle, gantry_tol)
            for gantry_angle in gantry_angles
        ]

        for mask in masks:
            if np.all(mask == 0):
                continue

            # TODO: Apply mask by more than just gantry angle to appropriately
            # extract beam index even when multiple beams have the same gantry
            # angle
            is_duplicate_gantry_angles = (np.sum(
                np.abs(np.diff(np.concatenate([[0], mask, [0]])))) != 2)

            if is_duplicate_gantry_angles:
                raise ValueError("Duplicate gantry angles not yet supported")

        try:
            assert np.all(np.sum(masks, axis=0) == 1), (
                "Not all beams were captured by the gantry tolerance of "
                " {}".format(gantry_tol))
        except AssertionError:
            if not allow_missing_angles:
                print("Allowable gantry angles = {}".format(gantry_angles))
                gantry = np.array(self.gantry, copy=False)
                out_of_tolerance = np.unique(
                    gantry[np.sum(masks, axis=0) == 0]).tolist()
                print("The gantry angles out of tolerance were {}".format(
                    out_of_tolerance))

                raise

        return masks
Пример #13
0
def _calc_blocked_t(travel_diff, grid_resolution):
    blocked_t = np.ones_like(travel_diff) * np.nan

    fully_blocked = travel_diff <= -grid_resolution / 2
    fully_open = travel_diff >= grid_resolution / 2
    blocked_t[fully_blocked] = 1
    blocked_t[fully_open] = 0

    transient = ~fully_blocked & ~fully_open

    blocked_t[transient] = (-travel_diff[transient] +
                            grid_resolution / 2) / grid_resolution

    assert np.all(~np.isnan(blocked_t))

    return blocked_t
Пример #14
0
def assert_array_agreement(unique_logfile_gantry_angles, mosaiq_gantry_angles,
                           allowed_deviation):
    difference_matrix = np.abs(unique_logfile_gantry_angles[:, None] -
                               mosaiq_gantry_angles[None, :])
    agreement_matrix = difference_matrix <= allowed_deviation
    row_agreement = np.any(agreement_matrix, axis=1)
    at_least_one_agreement = np.all(row_agreement)

    assert at_least_one_agreement, (
        "There is a logfile gantry angle that deviates by more than {} degrees"
        " from the Mosaiq control points. Unsure how to handle this.\n\n"
        "Logfile: {}\nMosaiq: {}\nDifference Matrix:\n{}\n"
        "Agreement Matrix:\n{}".format(
            allowed_deviation,
            unique_logfile_gantry_angles,
            mosaiq_gantry_angles,
            difference_matrix,
            agreement_matrix,
        ))
Пример #15
0
def calculate_min_dose_difference(options, distance, to_be_checked,
                                  distance_step_size):
    """Determine the minimum dose difference.

    Calculated for a given distance from each reference point.
    """

    min_relative_dose_difference = np.nan * np.ones_like(
        options.flat_dose_reference[to_be_checked])

    num_dimensions = np.shape(options.flat_mesh_axes_reference)[0]

    coordinates_at_distance_shell = pymedphys._utilities.createshells.calculate_coordinates_shell(  # pylint: disable = protected-access
        distance, num_dimensions, distance_step_size)

    num_points_in_shell = np.shape(coordinates_at_distance_shell)[1]

    estimated_ram_needed = (np.uint64(num_points_in_shell) *
                            np.uint64(np.count_nonzero(to_be_checked)) *
                            np.uint64(32) * np.uint64(num_dimensions) *
                            np.uint64(2))

    num_slices = np.floor(
        estimated_ram_needed / options.ram_available).astype(int) + 1

    if not options.quiet:
        sys.stdout.write(
            " | Points tested per reference point: {} | RAM split count: {}".
            format(num_points_in_shell, num_slices))
        sys.stdout.flush()

    all_checks = np.where(np.ravel(to_be_checked))[0]
    index = np.arange(len(all_checks))
    sliced = np.array_split(index, num_slices)

    sorted_sliced = [np.sort(current_slice) for current_slice in sliced]

    for current_slice in sorted_sliced:
        to_be_checked_sliced = np.full_like(to_be_checked, False, dtype=bool)
        to_be_checked_sliced[  # pylint: disable=unsupported-assignment-operation
            all_checks[current_slice]] = True

        assert np.all(to_be_checked[to_be_checked_sliced])

        axes_reference_to_be_checked = options.flat_mesh_axes_reference[:,
                                                                        to_be_checked_sliced]

        evaluation_dose = interpolate_evaluation_dose_at_distance(
            options.evaluation_interpolation,
            axes_reference_to_be_checked,
            coordinates_at_distance_shell,
        )

        if options.local_gamma:
            with np.errstate(divide="ignore"):
                relative_dose_difference = (
                    evaluation_dose -
                    options.flat_dose_reference[to_be_checked_sliced][None, :]
                ) / (
                    options.flat_dose_reference[to_be_checked_sliced][None, :])
        else:
            relative_dose_difference = (
                evaluation_dose -
                options.flat_dose_reference[to_be_checked_sliced][None, :]
            ) / options.global_normalisation

        min_relative_dose_difference[current_slice] = np.min(
            np.abs(relative_dose_difference), axis=0)

    return min_relative_dose_difference
Пример #16
0
    def _gantry_angle_mask(self, gantry_angle, gantry_angle_tol):
        near_angle = np.abs(np.array(self.gantry) -
                            gantry_angle) <= gantry_angle_tol
        assert np.all(np.diff(np.where(near_angle)[0]) == 1)

        return near_angle
Пример #17
0
def calc_single_control_point(
    mlc,
    jaw,
    delivered_mu=1,
    leaf_pair_widths=__DEFAULT_LEAF_PAIR_WIDTHS,
    grid_resolution=__DEFAULT_GRID_RESOLUTION,
    min_step_per_pixel=__DEFAULT_MIN_STEP_PER_PIXEL,
):
    """Calculate the MU Density for a single control point.

    Examples
    --------
    >>> from pymedphys._imports import numpy as np
    >>> from pymedphys._mudensity.mudensity import (
    ...     calc_single_control_point, display_mu_density)
    >>>
    >>> leaf_pair_widths = (2, 2)
    >>> mlc = np.array([
    ...     [
    ...         [1, 1],
    ...         [2, 2],
    ...     ],
    ...     [
    ...         [2, 2],
    ...         [3, 3],
    ...     ]
    ... ])
    >>> jaw = np.array([
    ...     [1.5, 1.2],
    ...     [1.5, 1.2]
    ... ])
    >>> grid, mu_density = calc_single_control_point(
    ...     mlc, jaw, leaf_pair_widths=leaf_pair_widths)
    >>> display_mu_density(grid, mu_density)
    >>>
    >>> grid['mlc']
    array([-3., -2., -1.,  0.,  1.,  2.,  3.])
    >>>
    >>> grid['jaw']
    array([-1.5, -0.5,  0.5,  1.5])
    >>>
    >>> np.round(mu_density, 2)
    array([[0.  , 0.07, 0.43, 0.5 , 0.43, 0.07, 0.  ],
           [0.  , 0.14, 0.86, 1.  , 0.86, 0.14, 0.  ],
           [0.14, 0.86, 1.  , 1.  , 1.  , 0.86, 0.14],
           [0.03, 0.17, 0.2 , 0.2 , 0.2 , 0.17, 0.03]])
    """

    leaf_pair_widths = np.array(leaf_pair_widths)
    leaf_division = leaf_pair_widths / grid_resolution

    if not np.all(leaf_division.astype(int) == leaf_division):
        raise ValueError(
            "The grid resolution needs to exactly divide every leaf pair width."
        )

    if (
        not np.max(np.abs(jaw))  # pylint: disable = unneeded-not
        <= np.sum(leaf_pair_widths) / 2
    ):
        raise ValueError(
            "The jaw should not travel further out than the maximum leaf limits. "
            f"Max travel was {np.max(np.abs(jaw))}"
        )

    (grid, grid_leaf_map, mlc) = _determine_calc_grid_and_adjustments(
        mlc, jaw, leaf_pair_widths, grid_resolution
    )

    positions = {
        "mlc": {
            1: (-mlc[0, :, 0], -mlc[1, :, 0]),  # left
            -1: (mlc[0, :, 1], mlc[1, :, 1]),  # right
        },
        "jaw": {
            1: (-jaw[0::-1, 0], -jaw[1::, 0]),  # bot
            -1: (jaw[0::-1, 1], jaw[1::, 1]),  # top
        },
    }

    time_steps = _calc_time_steps(positions, grid_resolution, min_step_per_pixel)
    blocked_by_device = _calc_blocked_by_device(
        grid, positions, grid_resolution, time_steps
    )
    device_open = _calc_device_open(blocked_by_device)
    mlc_open, jaw_open = _remap_mlc_and_jaw(device_open, grid_leaf_map)
    open_fraction = _calc_open_fraction(mlc_open, jaw_open)

    mu_density = open_fraction * delivered_mu

    return grid, mu_density