Project: s41467-024-51560-x | Function: _dormand_prince_step (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Implements the Dormand-Prince order 5(4) adaptive scheme in the interaction picture for solving nonlinear optical propagation equations, specifically designed for efficient simulation of frequency conversion processes in the nanophotonic waveguides described in the paper

**KEY TERMS:** {
  "field": "optical field amplitude A(z,t)",
  "dz": "propagation step size Δz",
  "k_1, k_2, k_3, k_4, k_5, k_6": "Runge-Kutta intermediate steps",
  "dispersion_step": "linear dispersion operator D̂",
  "nonlinear_term": "nonlinear operator N̂",
  "sol_5, sol_4": "5th and 4th order solutions for error control"
}

**CONTEXT CODE:**         self.max_dz = max_dz
        self.z_save = np.linspace(0, self.model.waveguide.length, self.z_pts)
        self.method_name = self._set_method_name(method)
        self.breakpoints = breakpoints
        self.stepsize_save = []
        self.power_prefactor = eps_0*c/2/self.model.waveguide.t_pts*self.model.waveguide.delta_t
    
    def _set_method_name(self, method):
        """
        Helper function to choose an appropriate solver. If a method is not
        provided by the user, it  will look into the model used to see if
        there is a recommended solver. Otherwise, defaults to the model that
        should be mostly used, i.e. DP5IP.

        Parameters
        ----------
        method : string
            Name of the solver.

        Returns
        -------
        string
            Name of the solver.
        """
        if method is None:
            if hasattr(self.model, '_recommended_solver'):
                return self.model._recommended_solver
            else:
                return 'DP5IP'
        else:
            return method
        
    def _compute_error(self, field_1, field_2):
        """
        Error estimation for adaptive step size

        Parameters
        ----------
        field_1 : array
            Reference field.
        field_2 : array
            Test field.

        Returns
        -------
        float
            Error estimate between the two fields.
        """
        raise NotImplementedError("Function implementation has been masked")
    
    def _dormand_prince_step(self, field, dz):
        """
        Dormand Prince order 5(4) adaptive scheme in the interaction picture. This
        new stepper uses the interaction picture in an embedded scheme of order 5.
        Tests have shown it to be up to four times faster than RK4IP.

        Parameters
        ----------
        field : array
            Current field.
        dz : float
            Current step size.

        Returns
        -------
        array
            New field estimate.
        
        float
            Error estimate.
        """
        raise NotImplementedError("Function implementation has been masked")
    
    def _rk546m(self, field, dz):
        """
        Some other Dormand-Prince method that has one less step but does not
        leverage the interaction picture and does not have the FSAL property.
        Usually worse than the other dormand-prince method.
        References:
            DOI: 10.1016/0771-050X(80)90013-3

        Parameters
        ----------
        field : array
            Current field.
        dz : float
            Current step size.

        Returns
        -------
        array
            New field estimate.
        
        float
            Error estimate.
        """
        raise NotImplementedError("Function implementation has been masked")
    
    def _adams_bashforth54_step(self, field, dz):
        """To be implemented"""
        pass

    def _rk4ip_single_step(self, field, dz):
        """
        Runge-Kutta order 4 step in the interaction picture (RK4IP)

        Parameters
        ----------
        field : array
            Current field.
        dz : float
            Current step size.

        Returns
        -------
        array
            New field estimate.
        """
        dispersion_op = numbaexp(-1j*self.model.waveguide.k*dz/2)
        a_int = dispersion_op*field
        k_1 = dz*dispersion_op*self.model.nonlinear_term(field)
        k_2 = dz*self.model.nonlinear_term(a_int + k_1/2)
        k_3 = dz*self.model.nonlinear_term(a_int + k_2/2)
        k_4 = dz*self.model.nonlinear_term(dispersion_op*(a_int + k_3))
        return dispersion_op*(a_int + k_1/6 + k_2/3 + k_3/3) + k_4/6
    
    def _rk4ip_step(self, field, dz):
        """
        Runge-Kutta order 4 adaptive scheme in the interaction picture (RK4IP)
        with step doubling and combining. Provided for comparison but slower
        than the dormand-prince method.
        References:
            DOI: 10.1109/JLT.2007.909373
            DOI: 10.1109/JLT.2009.2021538

        Parameters
        ----------
        field : array
            Current field.
        dz : float
            Current step size.

        Returns
        -------
        array
            New field estimate.
        
        float
            Error estimate.
        """


Complete the function implementation replacing "raise NotImplementedError".