Skip to content

Feature request: Initialize PTO with transmission matrix #400

Description

@dtgaebe

Feature description.
There are some cases where it would be beneficial to initialize wecopttool.pto.PTO with a matrix in transmission (or "transfer") form instead of pto_impedance.

Issue addressed.
If you want to model a PTO without gyrating effect, the PTO impedance matrix is not defined, see
Coe_2024. This could be the case when combining some transformative effect in the PTO with a loss map.
In the wecopttool.pto module we also only use the impedance matrix to compute

impedance_abcd = _make_abcd(impedance, ndof)
self._transfer_mat = _make_mimo_transfer_mat(impedance_abcd, ndof)

This _transfer_mat is the $[\boldsymbol{b}]$ in Coe_2024.

Describe the solution you'd like
We could make a PTO subclass with new __init__ method.
Something like:

class PTO_abcd(PTO):
    """A power take-off (PTO) object defined with transmission instead of impedance."""
    def __init__(self,
                 ndof: int,
                 kinematics: Union[TStateFunction, np.ndarray],
                 controller: Optional[TStateFunction] = None,
                 transmission: Optional[np.ndarray] = None,  # Changed from impedance to transmission
                 loss: Optional[TLOSS] = None,
                 names: Optional[list[str]] = None) -> None:
        """Create a PTO_abcd object.

        The :py:class:`wecopttool.pto.PTO_abcd` class describes the
        kinematics, control logic, transmission and/or non-linear power
        loss of a power take-off system.
        """
        self._ndof = ndof
        # names
        if names is None:
            names = [f'PTO_{i}' for i in range(ndof)]
        elif ndof == 1 and isinstance(names, str):
            names = [names]
        self._names = names
        # kinematics
        if callable(kinematics):
            def kinematics_fun(wec, x_wec, x_opt, waves, nsubsteps=1):
                pos_wec = wec.vec_to_dofmat(x_wec)
                tmat = self._tmat(wec, nsubsteps)
                pos_wec_td = np.dot(tmat, pos_wec)
                return kinematics(pos_wec_td)
        else:
            def kinematics_fun(wec, x_wec, x_opt, waves, nsubsteps=1):
                n = wec.nt*nsubsteps
                return np.repeat(kinematics[:, :, np.newaxis], n, axis=-1)
        self._kinematics = kinematics_fun
        # controller
        if controller is None:
            controller = controller_unstructured

        def force(wec, x_wec, x_opt, waves, nsubsteps=1):
            return controller(self, wec, x_wec, x_opt, waves, nsubsteps)

        self._force = force

        # power
        if transmission is not None:
            check_1 = transmission.shape[0] == transmission.shape[1] == 2*self.ndof
            check_2 = len(transmission.shape) == 3
            if not (check_1 and check_2):
                raise TypeError(
                    "Transmission should have size [2*ndof, 2*ndof, nfreq]"
                )
            # self._transfer_mat = transmission
            self._transfer_mat = _make_mimo_transfer_mat(transmission, ndof)
        else:
            self._transfer_mat = None
        self._impedance = 1 #should never be used in actual calcs, but is argument in if statement for post_porcessing
        self._loss = loss

In the quick and dirty solution above the

        self._impedance = 1 #should never be used in actual calcs, but is argument in if statement for post_porcessing

is not very clean, but required for post processing

if self.impedance is not None:

So we want to consider changing the wecopttool.pto as well.

Interest in leading this feature development?
I can lead, but I think we need to discuss the usefulness and general applicability

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requesthelp wantedExtra attention is needed

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions