Module ctsimu.scenario.part

Parts are objects in the scene: detector, source, stage and samples.

Expand source code
# -*- coding: UTF-8 -*-
"""
Parts are objects in the scene: detector, source, stage and samples.
"""

from ..helpers import *
from ..geometry import *
from .group import Group
from .deviation import Deviation
from .parameter import Parameter
from .scenevector import Scenevector

class Part(Group):
        """Parts are objects in the scene: detector, source, stage and samples.

        They have a coordinate system and can define deviations from their
        standard geometries (translations and rotations around given axes).
        The center, vectors and deviations can all have drifts,
        allowing for an evolution in time.

        Attributes
        ----------
        name : str
                Name of the object.

        attached_to_stage : bool
                Is the part attached to the sample stage?
                If so, it will follow the stage's movements throughout the CT scan.

        coordinate_system : ctsimu.geometry.CoordinateSystem
                The current coordinate system. This variable is automatically
                set up whenever the `set_frame` method is called.

        center : ctsimu.scenario.scenevector.Scenevector
                Coordinates of the center point.

        u : ctsimu.scenario.scenevector.Scenevector
                u vector of the part's coordinate system.

        w : ctsimu.scenario.scenevector.Scenevector
                w vector of the part's coordinate system.

        deviations : list
                All the geometric deviations for this part as
                `ctsimu.scenario.deviation.Deviation` objects.

        legacy_deviations : list
                All the legacy deviations for this part as
                `ctsimu.scenario.deviation.Deviation` objects.
                Legacy deviations are geometric deviations from
                CTSimU scenario descriptions prior to
                file format version 1.0. In general, they are incompatible
                with the new geometric deviation format and must be
                treated differently.
        """
        def __init__(self, name:str="Part", _root=None):
                """The part's name can be set upon initialization.

                Parameters
                ----------
                name : str
                        Name for the part.
                """
                Group.__init__(self, _root=_root)
                self.name = name

                self.attached_to_stage = False
                self.coordinate_system = CoordinateSystem()
                self.center = Scenevector(native_unit="mm", _root=self._root)
                self.u = Scenevector(native_unit=None, _root=self._root)
                self.w = Scenevector(native_unit=None, _root=self._root)
                self.deviations = list()
                self.legacy_deviations = list() # for deviations prior to file format 1.0

                # Internal parameters used for efficiency and
                # to track current state.
                self._static = False # non-drifting object if `True`.

                # If certain geometry deviations or drifts are unknown to the
                # reconstruction software, the part may be set up in two different ways:
                self._cs_initialized_real = False  # initialized to real coordinate system?
                self._cs_initialized_recon = False # initialized to recon coordinate system?


        def reset(self):
                """Reset to standard conditions, delete all deviations, reset
                coordinate system to standard world coordinates,
                and reset all parameters to their standard values."""
                self.attached_to_stage = False
                self._static = False
                self._cs_initialized_real = False
                self._cs_initialized_recon = False

                self.coordinate_system.reset()

                self.deviations = list()
                self.legacy_deviations = list()

                Group.reset(self)

        def is_attached_to_stage(self) -> bool:
                """Is the part attached to the sample stage?

                Returns
                -------
                attached_to_stage : bool
                        `True` is part is attached to sample stage, i.e., its reference
                        coordinate system is the stage coordinate system.
                        `False` if the part is not attached to the sample stage, i.e.,
                        its reference coordinate system is the world coordinate system.
                """
                return self.attached_to_stage

        def set_name(self, name:str):
                """Set the part's name.

                Parameters
                ----------
                name : str
                        New name for the part.
                """
                self.name = name

        def set_center(self, center:'Scenevector'):
                """Set the part's center.

                Parameters
                ----------
                center : Scenevector
                        New center for the part.
                """
                self.center = center

        def set_u(self, u:'Scenevector'):
                """Set the part's `u` vector.

                Parameters
                ----------
                u : Scenevector
                        New `u` vector for the part.
                """
                self.u = u

        def set_w(self, w:'Scenevector'):
                """Set the part's `w` vector.

                Parameters
                ----------
                w : Scenevector
                        New `w` vector for the part.
                """
                self.w = w

        def attach_to_stage(self, attached:bool=True):
                """Set the part's `attached_to_stage` property.

                Parameters
                ----------
                attached : bool
                        `True` if part is attached to the sample stage,
                        `False` if not.
                """
                self.attached_to_stage = attached

        def _set_static_if_no_drifts(self):
                """Set the object to 'static' if it does not drift,
                i.e., it is not moving.
                In this case, its coordinate system does not need to
                be re-assembled for each frame.
                """

                self._static = False

                if self.attached_to_stage is False:
                        # Count drifts:
                        if self.center.has_drifts() or self.u.has_drifts() or self.w.has_drifts():
                                return

                        for dev in self.deviations:
                                if dev.has_drifts():
                                        return

                        for ldev in self.legacy_deviations:
                                if ldev.has_drifts():
                                        return

                        self._static = True

        def set_geometry(self, json_geometry_object:dict, stage_coordinate_system:'CoordinateSystem'=None, proper_cs:str="local") -> bool:
                """
                Set up the part from a CTSimU JSON geometry definition.
                The `stage_coordinate_system` must only be provided if this
                part is attached to the stage.

                Parameters
                ----------
                json_geometry_object : dict
                        A CTSimU geometry object, as imported from a JSON structure.

                stage_coordinate_system : CoordinateSystem
                        Stage coordinate system. Only necessary for samples attached
                        to the stage.

                proper_cs : str
                        Which is the proper coordinate system of this object?
                        Either `"world"` (x, y, z), `"local"` (u, v, w) or `"sample"` (r, s, t).

                Returns
                -------
                success : bool
                        `True` on success, `False` if an error occurred.
                """
                if stage_coordinate_system is None:
                        stage_coordinate_system = ctsimu_world

                self.reset()

                geo = json_geometry_object

                # Try to set up the part from world coordinate notation (x, y, z).
                # We also have to support legacy spelling of "centre" ;-)
                if (json_exists_and_not_null(geo, ["center", "x"]) or \
                        json_exists_and_not_null(geo, ["centre", "x"])) and \
                   (json_exists_and_not_null(geo, ["center", "y"]) or \
                        json_exists_and_not_null(geo, ["centre", "y"])) and \
                   (json_exists_and_not_null(geo, ["center", "z"]) or \
                        json_exists_and_not_null(geo, ["centre", "z"])):
                        # *******************************
                        #           Part is in
                        #     WORLD COORDINATE SYSTEM
                        # *******************************
                        self.attach_to_stage(attached=False)

                        # Center
                        # ------
                        if self.center.set_from_json(json_extract_from_possible_keys(geo, [["center"], ["centre"]])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part '{self.name}': failed setting the object center from the JSON dictionary.")
                                return False

                        # Orientation
                        # -----------
                        # Vectors can be either u, w (for source, stage, detector)
                        # or r, t (for samples).
                        if self.u.set_from_json(json_extract_from_possible_keys(geo, [["vector_u"], ["vector_r"]])) and \
                           self.w.set_from_json(json_extract_from_possible_keys(geo, [["vector_w"], ["vector_t"]])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part {self.name} is placed in world coordinate system, but its vectors u and w (or r and t, for samples) are not properly defined (each with an x, y and z component).")
                                return False

                elif (json_exists_and_not_null(geo, ["center", "u"]) or \
                      json_exists_and_not_null(geo, ["centre", "u"])) and \
                     (json_exists_and_not_null(geo, ["center", "v"]) or \
                      json_exists_and_not_null(geo, ["centre", "v"])) and \
                     (json_exists_and_not_null(geo, ["center", "w"]) or \
                      json_exists_and_not_null(geo, ["centre", "w"])):
                        # *******************************
                        #           Part is in
                        #     STAGE COORDINATE SYSTEM
                        # *******************************
                        self.attach_to_stage(attached=True)

                        # Center
                        # ------
                        if self.center.set_from_json(json_extract_from_possible_keys(geo, [["center"], ["centre"]])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part '{self.name}': failed setting the object center from the JSON dictionary.")
                                return False

                        # Orientation
                        # -----------
                        # Vectors can only be r, t
                        # (because only samples can be attached to the stage).
                        if self.u.set_from_json(json_extract(geo, ["vector_r"])) and \
                           self.w.set_from_json(json_extract(geo, ["vector_t"])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part {self.name} is placed in stage system, but its vectors r and t are not properly defined (each with a u, v and w component).")
                                return False

                else:
                        raise Exception(f"Failed to set geometry for part '{self.name}'. Found no valid center definition in JSON file.")
                        return False

                # *******************************
                #     DEVIATIONS
                # *******************************
                if json_exists_and_not_null(geo, ["deviations"]):
                        devs = json_extract(geo, ["deviations"])
                        if isinstance(devs, list):
                                # Go through all elements in the deviations array
                                # and add them to this part's list of deviations.
                                for dev in devs:
                                        new_deviation = Deviation(pivot_reference=proper_cs, _root=self._root)
                                        if new_deviation.set_from_json(dev):
                                                self.deviations.append(new_deviation)
                                        else:
                                                raise Exception(f"An error occurred when setting a geometrical deviation (translation) for part '{self.name}'.")
                                                return False
                        elif isinstance(devs, dict):
                                # Only one drift defined directly as a JSON object?
                                # Actually not supported by file format,
                                # but let's be generous and try...
                                new_deviation = Deviation(pivot_reference=proper_cs, _root=self._root)
                                if new_deviation.set_from_json(devs):
                                        self.deviations.append(new_deviation)
                                else:
                                        raise Exception(f"An error occurred when setting a geometrical deviation (rotation) for part '{self.name}'.")
                                        return False
                        else:
                                raise Exception(f"Error reading geometrical deviations for part '{self.name}'")

                # Support for legacy deviations, prior to
                # file format version 0.9:
                # ------------------------------------------
                if json_exists_and_not_null(geo, ["deviation"]):
                        known_to_recon = True
                        if json_exists_and_not_null(geo, ["deviation", "known_to_reconstruction"]):
                                known_to_recon = get_value_in_native_unit("bool", geo, ["deviation", "known_to_reconstruction"])

                        for axis in ctsimu_valid_axes:
                                # Deviations in position
                                # -------------------------------------
                                # Positional deviations along sample axes r, s, t
                                # have not been part of the legacy file formats
                                # prior to version 0.9, but we still add them here
                                # because now we easily can... ;-)
                                if json_exists_and_not_null(geo, ["deviation", "position", axis]):
                                        pos_dev = Deviation(pivot_reference=proper_cs, _root=self._root)
                                        pos_dev.set_type("translation")
                                        pos_dev.set_axis(axis)
                                        pos_dev.set_known_to_reconstruction(known_to_recon)
                                        if pos_dev.amount.set_from_json(json_extract(geo, ["deviation", "position", axis])):
                                                # Legacy_deviations not necessary here
                                                # because positional translations are fully
                                                # compatible with the new file format:
                                                if not pos_dev.amount.is_zero():
                                                        self.deviations.append(pos_dev)
                                        else:
                                                raise Exception(f"An error occurred when setting a geometrical deviation (translation) for part '{self.name}'.")
                                                return False

                        for axis in ctsimu_valid_axes:
                                # Deviations in rotation
                                # -------------------------------------
                                # File formats prior to version 0.9 only supported
                                # rotations around u, v and w, in the order wv'u'',
                                # and ts'r'' for samples. We need to take care
                                # to keep this order here; it is ensured by the order
                                # of elements in the `valid_axes` list. This means we
                                # also add support for x, y, z (zy'x''),
                                # just because we can.
                                if json_exists_and_not_null(geo, ["deviation", "rotation", axis]):
                                        rot_dev = Deviation(pivot_reference=proper_cs, _root=self._root)
                                        rot_dev.set_type("rotation")

                                        # Prior to 0.9, all deviations were meant to take place
                                        # before the stage rotation. This means they need to be
                                        # stored as scene vectors to designate a constant deviation axis.
                                        rot_dev.set_axis(axis)
                                        rot_dev.set_known_to_reconstruction(known_to_recon)
                                        if rot_dev.amount.set_from_json(json_extract(geo, ["deviation", "rotation", axis])):
                                                # success
                                                if not rot_dev.amount.is_zero():
                                                        self.legacy_deviations.append(rot_dev)
                                        else:
                                                raise Exception(f"An error occurred when setting a geometrical deviation (rotation) for part '{self.name}'.")
                                                return False

                self.set_frame(frame=0, n_frames=1, w_rotation=0, stage_coordinate_system=stage_coordinate_system)
                return True

        def geometry_dict(self) -> dict:
                """Create a dictionary of the geometry for a CTSimU scenario file.

                Returns
                -------
                geometry_dict : dict
                        Dictionary with the geometry of this part.
                """
                jd = dict()
                jd["center"] = self.center.json_dict()

                if self.u.reference == "world":
                        jd["vector_u"] = self.u.json_dict()
                elif self.u.reference == "local":
                        jd["vector_r"] = self.u.json_dict()

                if self.w.reference == "world":
                        jd["vector_w"] = self.w.json_dict()
                elif self.w.reference == "local":
                        jd["vector_t"] = self.w.json_dict()

                # Assemble deviations and legacy deviations:
                devs = None
                if isinstance(self.deviations, list) and isinstance(self.legacy_deviations, list):
                        devs = self.deviations + self.legacy_deviations
                elif isinstance(self.deviations, list):
                        devs = self.deviations
                elif isinstance(self.legacy_deviations, list):
                        devs = self.legacy_deviations

                if devs is not None:
                        if len(devs) > 0:
                                jd["deviations"] = list()
                                for dev in devs:
                                        jd["deviations"].append(dev.json_dict())

                return jd

        def _set_frame_coordinate_system(self, frame:float, n_frames:int, reconstruction:bool=False, w_rotation:float=0, stage_coordinate_system:'CoordinateSystem'=None):
                """
                Set up the part's current coordinate system such that
                it complies with the `frame` number and all necessary
                drifts and deviations (assuming a total number of `n_frames`).

                This function is used by `set_frame` and `set_frame_for_reconstruction`
                and is usually not called from outside the object.

                Parameters
                ----------
                frame : float
                        Current frame number.

                n_frames : int
                        Total number of frames in scan.

                reconstruction : bool
                        Set up the coordinate system as it
                        would be presented to the reconstruction software.
                        Ignores deviations and drifts that should not
                        be considered during the reconstruction.

                w_rotation : float
                        An additional rotation around the part's w axis
                        for this frame. Used for the sample stage, which
                        rotates during a CT scan.

                stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional
                        If this part is attached to the sample stage,
                        the stage coordinate system for the given `frame`
                        must be passed.
                """

                # Set up standard coordinate system at frame zero:
                center = self.center.standard_vector()
                u      = self.u.standard_vector()
                w      = self.w.standard_vector()

                self.coordinate_system.make_from_vectors(center, u, w)
                self.coordinate_system.make_unit_coordinate_system()

                # Legacy rotational deviations (prior to file format 1.0)
                # all took place before any stage rotation:
                # ----------------------------------------------------------
                for legacy_dev in self.legacy_deviations:
                        self.coordinate_system = legacy_dev.deviate(
                                coordinate_system=self.coordinate_system,
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction,
                                attached_to_stage=self.attached_to_stage,
                                stage_coordinate_system=stage_coordinate_system,
                        )

                # Potential stage rotation:
                # ------------------------------------
                # Potential rotation around the w axis (in rad).
                if w_rotation != 0:
                        self.coordinate_system.rotate_around_w(angle=w_rotation)

                # Deviations:
                # ------------------------------------
                for dev in self.deviations:
                        self.coordinate_system = dev.deviate(
                                coordinate_system=self.coordinate_system,
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction,
                                attached_to_stage=self.attached_to_stage,
                                stage_coordinate_system=stage_coordinate_system,
                        )

                # Drifts (center and vector components):
                # -----------------------------------------------
                # Build a translation vector for the center point
                # from the total drift for this frame and apply
                # the translation:
                if self.center.has_drifts():
                         center_drift = self.center.drift_vector(
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction
                         )
                         self.coordinate_system.translate(translation_vector=center_drift)

                if self.u.has_drifts() or self.w.has_drifts():
                        new_u = self.u.vector_for_frame(frame, n_frames, reconstruction)
                        new_w = self.w.vector_for_frame(frame, n_frames, reconstruction)

                        self.coordinate_system.make_from_vectors(
                                center=coordinate_system.center,
                                u=new_u,
                                w=new_w
                        )
                        self.coordinate_system.make_unit_coordinate_system()

        def set_frame(self, frame:float, n_frames:int, w_rotation:float=0, stage_coordinate_system:'CoordinateSystem'=None, reconstruction:bool=False):
                """
                Set up the part for the given `frame` number, obeying all
                deviations and drifts.

                Parameters
                ----------
                frame : float
                        Current frame number.

                n_frames : int
                        Total number of frames in scan.

                w_rotation : float
                        An additional rotation (in rad) around the part's w axis
                        for this frame. Used for the sample stage, which
                        rotates during a CT scan.

                stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional
                        If this part is attached to the sample stage,
                        the stage coordinate system for the given `frame`
                        must be passed.

                reconstruction : bool
                        If `True`, set frame as seen by reconstruction software.
                        Default: `False`.
                """

                # Set up the current coordinate system obeying all drifts:
                if (self._cs_initialized_real is False) or (self._static is False):
                        self._set_frame_coordinate_system(
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction,
                                w_rotation=w_rotation,
                                stage_coordinate_system=stage_coordinate_system
                        )
                        self._cs_initialized_real  = not reconstruction
                        self._cs_initialized_recon = reconstruction

                Group.set_frame(self, frame, n_frames, reconstruction)

Classes

class Part (name: str = 'Part')

Parts are objects in the scene: detector, source, stage and samples.

They have a coordinate system and can define deviations from their standard geometries (translations and rotations around given axes). The center, vectors and deviations can all have drifts, allowing for an evolution in time.

Attributes

name : str
Name of the object.
attached_to_stage : bool
Is the part attached to the sample stage? If so, it will follow the stage's movements throughout the CT scan.
coordinate_system : CoordinateSystem
The current coordinate system. This variable is automatically set up whenever the set_frame method is called.
center : Scenevector
Coordinates of the center point.
u : Scenevector
u vector of the part's coordinate system.
w : Scenevector
w vector of the part's coordinate system.
deviations : list
All the geometric deviations for this part as Deviation objects.
legacy_deviations : list
All the legacy deviations for this part as Deviation objects. Legacy deviations are geometric deviations from CTSimU scenario descriptions prior to file format version 1.0. In general, they are incompatible with the new geometric deviation format and must be treated differently.

The part's name can be set upon initialization.

Parameters

name : str
Name for the part.
Expand source code
class Part(Group):
        """Parts are objects in the scene: detector, source, stage and samples.

        They have a coordinate system and can define deviations from their
        standard geometries (translations and rotations around given axes).
        The center, vectors and deviations can all have drifts,
        allowing for an evolution in time.

        Attributes
        ----------
        name : str
                Name of the object.

        attached_to_stage : bool
                Is the part attached to the sample stage?
                If so, it will follow the stage's movements throughout the CT scan.

        coordinate_system : ctsimu.geometry.CoordinateSystem
                The current coordinate system. This variable is automatically
                set up whenever the `set_frame` method is called.

        center : ctsimu.scenario.scenevector.Scenevector
                Coordinates of the center point.

        u : ctsimu.scenario.scenevector.Scenevector
                u vector of the part's coordinate system.

        w : ctsimu.scenario.scenevector.Scenevector
                w vector of the part's coordinate system.

        deviations : list
                All the geometric deviations for this part as
                `ctsimu.scenario.deviation.Deviation` objects.

        legacy_deviations : list
                All the legacy deviations for this part as
                `ctsimu.scenario.deviation.Deviation` objects.
                Legacy deviations are geometric deviations from
                CTSimU scenario descriptions prior to
                file format version 1.0. In general, they are incompatible
                with the new geometric deviation format and must be
                treated differently.
        """
        def __init__(self, name:str="Part", _root=None):
                """The part's name can be set upon initialization.

                Parameters
                ----------
                name : str
                        Name for the part.
                """
                Group.__init__(self, _root=_root)
                self.name = name

                self.attached_to_stage = False
                self.coordinate_system = CoordinateSystem()
                self.center = Scenevector(native_unit="mm", _root=self._root)
                self.u = Scenevector(native_unit=None, _root=self._root)
                self.w = Scenevector(native_unit=None, _root=self._root)
                self.deviations = list()
                self.legacy_deviations = list() # for deviations prior to file format 1.0

                # Internal parameters used for efficiency and
                # to track current state.
                self._static = False # non-drifting object if `True`.

                # If certain geometry deviations or drifts are unknown to the
                # reconstruction software, the part may be set up in two different ways:
                self._cs_initialized_real = False  # initialized to real coordinate system?
                self._cs_initialized_recon = False # initialized to recon coordinate system?


        def reset(self):
                """Reset to standard conditions, delete all deviations, reset
                coordinate system to standard world coordinates,
                and reset all parameters to their standard values."""
                self.attached_to_stage = False
                self._static = False
                self._cs_initialized_real = False
                self._cs_initialized_recon = False

                self.coordinate_system.reset()

                self.deviations = list()
                self.legacy_deviations = list()

                Group.reset(self)

        def is_attached_to_stage(self) -> bool:
                """Is the part attached to the sample stage?

                Returns
                -------
                attached_to_stage : bool
                        `True` is part is attached to sample stage, i.e., its reference
                        coordinate system is the stage coordinate system.
                        `False` if the part is not attached to the sample stage, i.e.,
                        its reference coordinate system is the world coordinate system.
                """
                return self.attached_to_stage

        def set_name(self, name:str):
                """Set the part's name.

                Parameters
                ----------
                name : str
                        New name for the part.
                """
                self.name = name

        def set_center(self, center:'Scenevector'):
                """Set the part's center.

                Parameters
                ----------
                center : Scenevector
                        New center for the part.
                """
                self.center = center

        def set_u(self, u:'Scenevector'):
                """Set the part's `u` vector.

                Parameters
                ----------
                u : Scenevector
                        New `u` vector for the part.
                """
                self.u = u

        def set_w(self, w:'Scenevector'):
                """Set the part's `w` vector.

                Parameters
                ----------
                w : Scenevector
                        New `w` vector for the part.
                """
                self.w = w

        def attach_to_stage(self, attached:bool=True):
                """Set the part's `attached_to_stage` property.

                Parameters
                ----------
                attached : bool
                        `True` if part is attached to the sample stage,
                        `False` if not.
                """
                self.attached_to_stage = attached

        def _set_static_if_no_drifts(self):
                """Set the object to 'static' if it does not drift,
                i.e., it is not moving.
                In this case, its coordinate system does not need to
                be re-assembled for each frame.
                """

                self._static = False

                if self.attached_to_stage is False:
                        # Count drifts:
                        if self.center.has_drifts() or self.u.has_drifts() or self.w.has_drifts():
                                return

                        for dev in self.deviations:
                                if dev.has_drifts():
                                        return

                        for ldev in self.legacy_deviations:
                                if ldev.has_drifts():
                                        return

                        self._static = True

        def set_geometry(self, json_geometry_object:dict, stage_coordinate_system:'CoordinateSystem'=None, proper_cs:str="local") -> bool:
                """
                Set up the part from a CTSimU JSON geometry definition.
                The `stage_coordinate_system` must only be provided if this
                part is attached to the stage.

                Parameters
                ----------
                json_geometry_object : dict
                        A CTSimU geometry object, as imported from a JSON structure.

                stage_coordinate_system : CoordinateSystem
                        Stage coordinate system. Only necessary for samples attached
                        to the stage.

                proper_cs : str
                        Which is the proper coordinate system of this object?
                        Either `"world"` (x, y, z), `"local"` (u, v, w) or `"sample"` (r, s, t).

                Returns
                -------
                success : bool
                        `True` on success, `False` if an error occurred.
                """
                if stage_coordinate_system is None:
                        stage_coordinate_system = ctsimu_world

                self.reset()

                geo = json_geometry_object

                # Try to set up the part from world coordinate notation (x, y, z).
                # We also have to support legacy spelling of "centre" ;-)
                if (json_exists_and_not_null(geo, ["center", "x"]) or \
                        json_exists_and_not_null(geo, ["centre", "x"])) and \
                   (json_exists_and_not_null(geo, ["center", "y"]) or \
                        json_exists_and_not_null(geo, ["centre", "y"])) and \
                   (json_exists_and_not_null(geo, ["center", "z"]) or \
                        json_exists_and_not_null(geo, ["centre", "z"])):
                        # *******************************
                        #           Part is in
                        #     WORLD COORDINATE SYSTEM
                        # *******************************
                        self.attach_to_stage(attached=False)

                        # Center
                        # ------
                        if self.center.set_from_json(json_extract_from_possible_keys(geo, [["center"], ["centre"]])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part '{self.name}': failed setting the object center from the JSON dictionary.")
                                return False

                        # Orientation
                        # -----------
                        # Vectors can be either u, w (for source, stage, detector)
                        # or r, t (for samples).
                        if self.u.set_from_json(json_extract_from_possible_keys(geo, [["vector_u"], ["vector_r"]])) and \
                           self.w.set_from_json(json_extract_from_possible_keys(geo, [["vector_w"], ["vector_t"]])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part {self.name} is placed in world coordinate system, but its vectors u and w (or r and t, for samples) are not properly defined (each with an x, y and z component).")
                                return False

                elif (json_exists_and_not_null(geo, ["center", "u"]) or \
                      json_exists_and_not_null(geo, ["centre", "u"])) and \
                     (json_exists_and_not_null(geo, ["center", "v"]) or \
                      json_exists_and_not_null(geo, ["centre", "v"])) and \
                     (json_exists_and_not_null(geo, ["center", "w"]) or \
                      json_exists_and_not_null(geo, ["centre", "w"])):
                        # *******************************
                        #           Part is in
                        #     STAGE COORDINATE SYSTEM
                        # *******************************
                        self.attach_to_stage(attached=True)

                        # Center
                        # ------
                        if self.center.set_from_json(json_extract_from_possible_keys(geo, [["center"], ["centre"]])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part '{self.name}': failed setting the object center from the JSON dictionary.")
                                return False

                        # Orientation
                        # -----------
                        # Vectors can only be r, t
                        # (because only samples can be attached to the stage).
                        if self.u.set_from_json(json_extract(geo, ["vector_r"])) and \
                           self.w.set_from_json(json_extract(geo, ["vector_t"])):
                                # success
                                pass
                        else:
                                raise Exception(f"Part {self.name} is placed in stage system, but its vectors r and t are not properly defined (each with a u, v and w component).")
                                return False

                else:
                        raise Exception(f"Failed to set geometry for part '{self.name}'. Found no valid center definition in JSON file.")
                        return False

                # *******************************
                #     DEVIATIONS
                # *******************************
                if json_exists_and_not_null(geo, ["deviations"]):
                        devs = json_extract(geo, ["deviations"])
                        if isinstance(devs, list):
                                # Go through all elements in the deviations array
                                # and add them to this part's list of deviations.
                                for dev in devs:
                                        new_deviation = Deviation(pivot_reference=proper_cs, _root=self._root)
                                        if new_deviation.set_from_json(dev):
                                                self.deviations.append(new_deviation)
                                        else:
                                                raise Exception(f"An error occurred when setting a geometrical deviation (translation) for part '{self.name}'.")
                                                return False
                        elif isinstance(devs, dict):
                                # Only one drift defined directly as a JSON object?
                                # Actually not supported by file format,
                                # but let's be generous and try...
                                new_deviation = Deviation(pivot_reference=proper_cs, _root=self._root)
                                if new_deviation.set_from_json(devs):
                                        self.deviations.append(new_deviation)
                                else:
                                        raise Exception(f"An error occurred when setting a geometrical deviation (rotation) for part '{self.name}'.")
                                        return False
                        else:
                                raise Exception(f"Error reading geometrical deviations for part '{self.name}'")

                # Support for legacy deviations, prior to
                # file format version 0.9:
                # ------------------------------------------
                if json_exists_and_not_null(geo, ["deviation"]):
                        known_to_recon = True
                        if json_exists_and_not_null(geo, ["deviation", "known_to_reconstruction"]):
                                known_to_recon = get_value_in_native_unit("bool", geo, ["deviation", "known_to_reconstruction"])

                        for axis in ctsimu_valid_axes:
                                # Deviations in position
                                # -------------------------------------
                                # Positional deviations along sample axes r, s, t
                                # have not been part of the legacy file formats
                                # prior to version 0.9, but we still add them here
                                # because now we easily can... ;-)
                                if json_exists_and_not_null(geo, ["deviation", "position", axis]):
                                        pos_dev = Deviation(pivot_reference=proper_cs, _root=self._root)
                                        pos_dev.set_type("translation")
                                        pos_dev.set_axis(axis)
                                        pos_dev.set_known_to_reconstruction(known_to_recon)
                                        if pos_dev.amount.set_from_json(json_extract(geo, ["deviation", "position", axis])):
                                                # Legacy_deviations not necessary here
                                                # because positional translations are fully
                                                # compatible with the new file format:
                                                if not pos_dev.amount.is_zero():
                                                        self.deviations.append(pos_dev)
                                        else:
                                                raise Exception(f"An error occurred when setting a geometrical deviation (translation) for part '{self.name}'.")
                                                return False

                        for axis in ctsimu_valid_axes:
                                # Deviations in rotation
                                # -------------------------------------
                                # File formats prior to version 0.9 only supported
                                # rotations around u, v and w, in the order wv'u'',
                                # and ts'r'' for samples. We need to take care
                                # to keep this order here; it is ensured by the order
                                # of elements in the `valid_axes` list. This means we
                                # also add support for x, y, z (zy'x''),
                                # just because we can.
                                if json_exists_and_not_null(geo, ["deviation", "rotation", axis]):
                                        rot_dev = Deviation(pivot_reference=proper_cs, _root=self._root)
                                        rot_dev.set_type("rotation")

                                        # Prior to 0.9, all deviations were meant to take place
                                        # before the stage rotation. This means they need to be
                                        # stored as scene vectors to designate a constant deviation axis.
                                        rot_dev.set_axis(axis)
                                        rot_dev.set_known_to_reconstruction(known_to_recon)
                                        if rot_dev.amount.set_from_json(json_extract(geo, ["deviation", "rotation", axis])):
                                                # success
                                                if not rot_dev.amount.is_zero():
                                                        self.legacy_deviations.append(rot_dev)
                                        else:
                                                raise Exception(f"An error occurred when setting a geometrical deviation (rotation) for part '{self.name}'.")
                                                return False

                self.set_frame(frame=0, n_frames=1, w_rotation=0, stage_coordinate_system=stage_coordinate_system)
                return True

        def geometry_dict(self) -> dict:
                """Create a dictionary of the geometry for a CTSimU scenario file.

                Returns
                -------
                geometry_dict : dict
                        Dictionary with the geometry of this part.
                """
                jd = dict()
                jd["center"] = self.center.json_dict()

                if self.u.reference == "world":
                        jd["vector_u"] = self.u.json_dict()
                elif self.u.reference == "local":
                        jd["vector_r"] = self.u.json_dict()

                if self.w.reference == "world":
                        jd["vector_w"] = self.w.json_dict()
                elif self.w.reference == "local":
                        jd["vector_t"] = self.w.json_dict()

                # Assemble deviations and legacy deviations:
                devs = None
                if isinstance(self.deviations, list) and isinstance(self.legacy_deviations, list):
                        devs = self.deviations + self.legacy_deviations
                elif isinstance(self.deviations, list):
                        devs = self.deviations
                elif isinstance(self.legacy_deviations, list):
                        devs = self.legacy_deviations

                if devs is not None:
                        if len(devs) > 0:
                                jd["deviations"] = list()
                                for dev in devs:
                                        jd["deviations"].append(dev.json_dict())

                return jd

        def _set_frame_coordinate_system(self, frame:float, n_frames:int, reconstruction:bool=False, w_rotation:float=0, stage_coordinate_system:'CoordinateSystem'=None):
                """
                Set up the part's current coordinate system such that
                it complies with the `frame` number and all necessary
                drifts and deviations (assuming a total number of `n_frames`).

                This function is used by `set_frame` and `set_frame_for_reconstruction`
                and is usually not called from outside the object.

                Parameters
                ----------
                frame : float
                        Current frame number.

                n_frames : int
                        Total number of frames in scan.

                reconstruction : bool
                        Set up the coordinate system as it
                        would be presented to the reconstruction software.
                        Ignores deviations and drifts that should not
                        be considered during the reconstruction.

                w_rotation : float
                        An additional rotation around the part's w axis
                        for this frame. Used for the sample stage, which
                        rotates during a CT scan.

                stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional
                        If this part is attached to the sample stage,
                        the stage coordinate system for the given `frame`
                        must be passed.
                """

                # Set up standard coordinate system at frame zero:
                center = self.center.standard_vector()
                u      = self.u.standard_vector()
                w      = self.w.standard_vector()

                self.coordinate_system.make_from_vectors(center, u, w)
                self.coordinate_system.make_unit_coordinate_system()

                # Legacy rotational deviations (prior to file format 1.0)
                # all took place before any stage rotation:
                # ----------------------------------------------------------
                for legacy_dev in self.legacy_deviations:
                        self.coordinate_system = legacy_dev.deviate(
                                coordinate_system=self.coordinate_system,
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction,
                                attached_to_stage=self.attached_to_stage,
                                stage_coordinate_system=stage_coordinate_system,
                        )

                # Potential stage rotation:
                # ------------------------------------
                # Potential rotation around the w axis (in rad).
                if w_rotation != 0:
                        self.coordinate_system.rotate_around_w(angle=w_rotation)

                # Deviations:
                # ------------------------------------
                for dev in self.deviations:
                        self.coordinate_system = dev.deviate(
                                coordinate_system=self.coordinate_system,
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction,
                                attached_to_stage=self.attached_to_stage,
                                stage_coordinate_system=stage_coordinate_system,
                        )

                # Drifts (center and vector components):
                # -----------------------------------------------
                # Build a translation vector for the center point
                # from the total drift for this frame and apply
                # the translation:
                if self.center.has_drifts():
                         center_drift = self.center.drift_vector(
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction
                         )
                         self.coordinate_system.translate(translation_vector=center_drift)

                if self.u.has_drifts() or self.w.has_drifts():
                        new_u = self.u.vector_for_frame(frame, n_frames, reconstruction)
                        new_w = self.w.vector_for_frame(frame, n_frames, reconstruction)

                        self.coordinate_system.make_from_vectors(
                                center=coordinate_system.center,
                                u=new_u,
                                w=new_w
                        )
                        self.coordinate_system.make_unit_coordinate_system()

        def set_frame(self, frame:float, n_frames:int, w_rotation:float=0, stage_coordinate_system:'CoordinateSystem'=None, reconstruction:bool=False):
                """
                Set up the part for the given `frame` number, obeying all
                deviations and drifts.

                Parameters
                ----------
                frame : float
                        Current frame number.

                n_frames : int
                        Total number of frames in scan.

                w_rotation : float
                        An additional rotation (in rad) around the part's w axis
                        for this frame. Used for the sample stage, which
                        rotates during a CT scan.

                stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional
                        If this part is attached to the sample stage,
                        the stage coordinate system for the given `frame`
                        must be passed.

                reconstruction : bool
                        If `True`, set frame as seen by reconstruction software.
                        Default: `False`.
                """

                # Set up the current coordinate system obeying all drifts:
                if (self._cs_initialized_real is False) or (self._static is False):
                        self._set_frame_coordinate_system(
                                frame=frame,
                                n_frames=n_frames,
                                reconstruction=reconstruction,
                                w_rotation=w_rotation,
                                stage_coordinate_system=stage_coordinate_system
                        )
                        self._cs_initialized_real  = not reconstruction
                        self._cs_initialized_recon = reconstruction

                Group.set_frame(self, frame, n_frames, reconstruction)

Ancestors

Subclasses

Methods

def attach_to_stage(self, attached: bool = True)

Set the part's attached_to_stage property.

Parameters

attached : bool
True if part is attached to the sample stage, False if not.
Expand source code
def attach_to_stage(self, attached:bool=True):
        """Set the part's `attached_to_stage` property.

        Parameters
        ----------
        attached : bool
                `True` if part is attached to the sample stage,
                `False` if not.
        """
        self.attached_to_stage = attached
def geometry_dict(self) ‑> dict

Create a dictionary of the geometry for a CTSimU scenario file.

Returns

geometry_dict : dict
Dictionary with the geometry of this part.
Expand source code
def geometry_dict(self) -> dict:
        """Create a dictionary of the geometry for a CTSimU scenario file.

        Returns
        -------
        geometry_dict : dict
                Dictionary with the geometry of this part.
        """
        jd = dict()
        jd["center"] = self.center.json_dict()

        if self.u.reference == "world":
                jd["vector_u"] = self.u.json_dict()
        elif self.u.reference == "local":
                jd["vector_r"] = self.u.json_dict()

        if self.w.reference == "world":
                jd["vector_w"] = self.w.json_dict()
        elif self.w.reference == "local":
                jd["vector_t"] = self.w.json_dict()

        # Assemble deviations and legacy deviations:
        devs = None
        if isinstance(self.deviations, list) and isinstance(self.legacy_deviations, list):
                devs = self.deviations + self.legacy_deviations
        elif isinstance(self.deviations, list):
                devs = self.deviations
        elif isinstance(self.legacy_deviations, list):
                devs = self.legacy_deviations

        if devs is not None:
                if len(devs) > 0:
                        jd["deviations"] = list()
                        for dev in devs:
                                jd["deviations"].append(dev.json_dict())

        return jd
def is_attached_to_stage(self) ‑> bool

Is the part attached to the sample stage?

Returns

attached_to_stage : bool
True is part is attached to sample stage, i.e., its reference coordinate system is the stage coordinate system. False if the part is not attached to the sample stage, i.e., its reference coordinate system is the world coordinate system.
Expand source code
def is_attached_to_stage(self) -> bool:
        """Is the part attached to the sample stage?

        Returns
        -------
        attached_to_stage : bool
                `True` is part is attached to sample stage, i.e., its reference
                coordinate system is the stage coordinate system.
                `False` if the part is not attached to the sample stage, i.e.,
                its reference coordinate system is the world coordinate system.
        """
        return self.attached_to_stage
def reset(self)

Reset to standard conditions, delete all deviations, reset coordinate system to standard world coordinates, and reset all parameters to their standard values.

Expand source code
def reset(self):
        """Reset to standard conditions, delete all deviations, reset
        coordinate system to standard world coordinates,
        and reset all parameters to their standard values."""
        self.attached_to_stage = False
        self._static = False
        self._cs_initialized_real = False
        self._cs_initialized_recon = False

        self.coordinate_system.reset()

        self.deviations = list()
        self.legacy_deviations = list()

        Group.reset(self)
def set_center(self, center: Scenevector)

Set the part's center.

Parameters

center : Scenevector
New center for the part.
Expand source code
def set_center(self, center:'Scenevector'):
        """Set the part's center.

        Parameters
        ----------
        center : Scenevector
                New center for the part.
        """
        self.center = center
def set_frame(self, frame: float, n_frames: int, w_rotation: float = 0, stage_coordinate_system: CoordinateSystem = None, reconstruction: bool = False)

Set up the part for the given frame number, obeying all deviations and drifts.

Parameters

frame : float
Current frame number.
n_frames : int
Total number of frames in scan.
w_rotation : float
An additional rotation (in rad) around the part's w axis for this frame. Used for the sample stage, which rotates during a CT scan.
stage_coordinate_system : CoordinateSystem, optional
If this part is attached to the sample stage, the stage coordinate system for the given frame must be passed.
reconstruction : bool
If True, set frame as seen by reconstruction software. Default: False.
Expand source code
def set_frame(self, frame:float, n_frames:int, w_rotation:float=0, stage_coordinate_system:'CoordinateSystem'=None, reconstruction:bool=False):
        """
        Set up the part for the given `frame` number, obeying all
        deviations and drifts.

        Parameters
        ----------
        frame : float
                Current frame number.

        n_frames : int
                Total number of frames in scan.

        w_rotation : float
                An additional rotation (in rad) around the part's w axis
                for this frame. Used for the sample stage, which
                rotates during a CT scan.

        stage_coordinate_system : ctsimu.geometry.CoordinateSystem, optional
                If this part is attached to the sample stage,
                the stage coordinate system for the given `frame`
                must be passed.

        reconstruction : bool
                If `True`, set frame as seen by reconstruction software.
                Default: `False`.
        """

        # Set up the current coordinate system obeying all drifts:
        if (self._cs_initialized_real is False) or (self._static is False):
                self._set_frame_coordinate_system(
                        frame=frame,
                        n_frames=n_frames,
                        reconstruction=reconstruction,
                        w_rotation=w_rotation,
                        stage_coordinate_system=stage_coordinate_system
                )
                self._cs_initialized_real  = not reconstruction
                self._cs_initialized_recon = reconstruction

        Group.set_frame(self, frame, n_frames, reconstruction)
def set_geometry(self, json_geometry_object: dict, stage_coordinate_system: CoordinateSystem = None, proper_cs: str = 'local') ‑> bool

Set up the part from a CTSimU JSON geometry definition. The stage_coordinate_system must only be provided if this part is attached to the stage.

Parameters

json_geometry_object : dict
A CTSimU geometry object, as imported from a JSON structure.
stage_coordinate_system : CoordinateSystem
Stage coordinate system. Only necessary for samples attached to the stage.
proper_cs : str
Which is the proper coordinate system of this object? Either "world" (x, y, z), "local" (u, v, w) or "sample" (r, s, t).

Returns

success : bool
True on success, False if an error occurred.
Expand source code
def set_geometry(self, json_geometry_object:dict, stage_coordinate_system:'CoordinateSystem'=None, proper_cs:str="local") -> bool:
        """
        Set up the part from a CTSimU JSON geometry definition.
        The `stage_coordinate_system` must only be provided if this
        part is attached to the stage.

        Parameters
        ----------
        json_geometry_object : dict
                A CTSimU geometry object, as imported from a JSON structure.

        stage_coordinate_system : CoordinateSystem
                Stage coordinate system. Only necessary for samples attached
                to the stage.

        proper_cs : str
                Which is the proper coordinate system of this object?
                Either `"world"` (x, y, z), `"local"` (u, v, w) or `"sample"` (r, s, t).

        Returns
        -------
        success : bool
                `True` on success, `False` if an error occurred.
        """
        if stage_coordinate_system is None:
                stage_coordinate_system = ctsimu_world

        self.reset()

        geo = json_geometry_object

        # Try to set up the part from world coordinate notation (x, y, z).
        # We also have to support legacy spelling of "centre" ;-)
        if (json_exists_and_not_null(geo, ["center", "x"]) or \
                json_exists_and_not_null(geo, ["centre", "x"])) and \
           (json_exists_and_not_null(geo, ["center", "y"]) or \
                json_exists_and_not_null(geo, ["centre", "y"])) and \
           (json_exists_and_not_null(geo, ["center", "z"]) or \
                json_exists_and_not_null(geo, ["centre", "z"])):
                # *******************************
                #           Part is in
                #     WORLD COORDINATE SYSTEM
                # *******************************
                self.attach_to_stage(attached=False)

                # Center
                # ------
                if self.center.set_from_json(json_extract_from_possible_keys(geo, [["center"], ["centre"]])):
                        # success
                        pass
                else:
                        raise Exception(f"Part '{self.name}': failed setting the object center from the JSON dictionary.")
                        return False

                # Orientation
                # -----------
                # Vectors can be either u, w (for source, stage, detector)
                # or r, t (for samples).
                if self.u.set_from_json(json_extract_from_possible_keys(geo, [["vector_u"], ["vector_r"]])) and \
                   self.w.set_from_json(json_extract_from_possible_keys(geo, [["vector_w"], ["vector_t"]])):
                        # success
                        pass
                else:
                        raise Exception(f"Part {self.name} is placed in world coordinate system, but its vectors u and w (or r and t, for samples) are not properly defined (each with an x, y and z component).")
                        return False

        elif (json_exists_and_not_null(geo, ["center", "u"]) or \
              json_exists_and_not_null(geo, ["centre", "u"])) and \
             (json_exists_and_not_null(geo, ["center", "v"]) or \
              json_exists_and_not_null(geo, ["centre", "v"])) and \
             (json_exists_and_not_null(geo, ["center", "w"]) or \
              json_exists_and_not_null(geo, ["centre", "w"])):
                # *******************************
                #           Part is in
                #     STAGE COORDINATE SYSTEM
                # *******************************
                self.attach_to_stage(attached=True)

                # Center
                # ------
                if self.center.set_from_json(json_extract_from_possible_keys(geo, [["center"], ["centre"]])):
                        # success
                        pass
                else:
                        raise Exception(f"Part '{self.name}': failed setting the object center from the JSON dictionary.")
                        return False

                # Orientation
                # -----------
                # Vectors can only be r, t
                # (because only samples can be attached to the stage).
                if self.u.set_from_json(json_extract(geo, ["vector_r"])) and \
                   self.w.set_from_json(json_extract(geo, ["vector_t"])):
                        # success
                        pass
                else:
                        raise Exception(f"Part {self.name} is placed in stage system, but its vectors r and t are not properly defined (each with a u, v and w component).")
                        return False

        else:
                raise Exception(f"Failed to set geometry for part '{self.name}'. Found no valid center definition in JSON file.")
                return False

        # *******************************
        #     DEVIATIONS
        # *******************************
        if json_exists_and_not_null(geo, ["deviations"]):
                devs = json_extract(geo, ["deviations"])
                if isinstance(devs, list):
                        # Go through all elements in the deviations array
                        # and add them to this part's list of deviations.
                        for dev in devs:
                                new_deviation = Deviation(pivot_reference=proper_cs, _root=self._root)
                                if new_deviation.set_from_json(dev):
                                        self.deviations.append(new_deviation)
                                else:
                                        raise Exception(f"An error occurred when setting a geometrical deviation (translation) for part '{self.name}'.")
                                        return False
                elif isinstance(devs, dict):
                        # Only one drift defined directly as a JSON object?
                        # Actually not supported by file format,
                        # but let's be generous and try...
                        new_deviation = Deviation(pivot_reference=proper_cs, _root=self._root)
                        if new_deviation.set_from_json(devs):
                                self.deviations.append(new_deviation)
                        else:
                                raise Exception(f"An error occurred when setting a geometrical deviation (rotation) for part '{self.name}'.")
                                return False
                else:
                        raise Exception(f"Error reading geometrical deviations for part '{self.name}'")

        # Support for legacy deviations, prior to
        # file format version 0.9:
        # ------------------------------------------
        if json_exists_and_not_null(geo, ["deviation"]):
                known_to_recon = True
                if json_exists_and_not_null(geo, ["deviation", "known_to_reconstruction"]):
                        known_to_recon = get_value_in_native_unit("bool", geo, ["deviation", "known_to_reconstruction"])

                for axis in ctsimu_valid_axes:
                        # Deviations in position
                        # -------------------------------------
                        # Positional deviations along sample axes r, s, t
                        # have not been part of the legacy file formats
                        # prior to version 0.9, but we still add them here
                        # because now we easily can... ;-)
                        if json_exists_and_not_null(geo, ["deviation", "position", axis]):
                                pos_dev = Deviation(pivot_reference=proper_cs, _root=self._root)
                                pos_dev.set_type("translation")
                                pos_dev.set_axis(axis)
                                pos_dev.set_known_to_reconstruction(known_to_recon)
                                if pos_dev.amount.set_from_json(json_extract(geo, ["deviation", "position", axis])):
                                        # Legacy_deviations not necessary here
                                        # because positional translations are fully
                                        # compatible with the new file format:
                                        if not pos_dev.amount.is_zero():
                                                self.deviations.append(pos_dev)
                                else:
                                        raise Exception(f"An error occurred when setting a geometrical deviation (translation) for part '{self.name}'.")
                                        return False

                for axis in ctsimu_valid_axes:
                        # Deviations in rotation
                        # -------------------------------------
                        # File formats prior to version 0.9 only supported
                        # rotations around u, v and w, in the order wv'u'',
                        # and ts'r'' for samples. We need to take care
                        # to keep this order here; it is ensured by the order
                        # of elements in the `valid_axes` list. This means we
                        # also add support for x, y, z (zy'x''),
                        # just because we can.
                        if json_exists_and_not_null(geo, ["deviation", "rotation", axis]):
                                rot_dev = Deviation(pivot_reference=proper_cs, _root=self._root)
                                rot_dev.set_type("rotation")

                                # Prior to 0.9, all deviations were meant to take place
                                # before the stage rotation. This means they need to be
                                # stored as scene vectors to designate a constant deviation axis.
                                rot_dev.set_axis(axis)
                                rot_dev.set_known_to_reconstruction(known_to_recon)
                                if rot_dev.amount.set_from_json(json_extract(geo, ["deviation", "rotation", axis])):
                                        # success
                                        if not rot_dev.amount.is_zero():
                                                self.legacy_deviations.append(rot_dev)
                                else:
                                        raise Exception(f"An error occurred when setting a geometrical deviation (rotation) for part '{self.name}'.")
                                        return False

        self.set_frame(frame=0, n_frames=1, w_rotation=0, stage_coordinate_system=stage_coordinate_system)
        return True
def set_name(self, name: str)

Set the part's name.

Parameters

name : str
New name for the part.
Expand source code
def set_name(self, name:str):
        """Set the part's name.

        Parameters
        ----------
        name : str
                New name for the part.
        """
        self.name = name
def set_u(self, u: Scenevector)

Set the part's u vector.

Parameters

u : Scenevector
New u vector for the part.
Expand source code
def set_u(self, u:'Scenevector'):
        """Set the part's `u` vector.

        Parameters
        ----------
        u : Scenevector
                New `u` vector for the part.
        """
        self.u = u
def set_w(self, w: Scenevector)

Set the part's w vector.

Parameters

w : Scenevector
New w vector for the part.
Expand source code
def set_w(self, w:'Scenevector'):
        """Set the part's `w` vector.

        Parameters
        ----------
        w : Scenevector
                New `w` vector for the part.
        """
        self.w = w

Inherited members