def coupled_dip_mags_focused_beam(
    mol_angle,
    plas_angle,
    d_col,
    p0_position,
    beam_x_positions,
    E_d_angle=None,
    drive_hbar_w=None,
    alpha0_diag=None,
    alpha1_diag=None,
    n_b=None,
    drive_amp=None,
    return_polarizabilities=False,
    ):
    """ Calculate dipole magnitudes with generalized dyadic
        polarizabilities driven by a focused dipole PSF beam.

        Returns dipole moment vecotrs as rows in array of shape
        (# of seperations, # of beam positions, 3 cart. coords).

        As with the rest of this module, 'd_col' is expected in shape
            (number of seperations, 3)

        'p0_positions' is expected in shape
            (3)

        beam_x_positions is currelty just assuming a 1d slice, so it
        should be shape
            (number of points) on the x axis.

        To be consistent with super-res notation, 'mol' dipole is
        placed at 'p0_position' and seperation vectrs point towards
        this location from the 'plas' location,
            p0_location - d_col = p1_location
        so if we want p0 on the left, d has to be negative.
        """

    # Initialize unit vector for molecule dipole in lab frame
    phi_0 = mol_angle ## angle of bf_p0 in lab frame

    # Initialize unit vecotr for molecule dipole in lab frame
    phi_1 = plas_angle ## angle of bf_p1 in lab frame

    k = (drive_hbar_w*n_b/hbar) / c

    ## Define positions with shape (num_seperations, 3)
    if p0_position.ndim is 1:
        p0_position = p0_position[None, :]
    p1_position = p0_position - d_col

    ## Buld focused beam profile
    E_0 = aff.E_field(
        dipole_orientation_angle=E_d_angle,
        xi=beam_x_positions - p0_position[...,0],
        y=0,
        k=k
        ).T*drive_amp
    E_1 = aff.E_field(
        dipole_orientation_angle=E_d_angle,
        xi=beam_x_positions - p1_position[...,0],
        y=0,
        k=k
        ).T*drive_amp

    ## Normalize fields to correct beam intensity
    spot_size = 2*np.pi/k
    spot_space = np.linspace(-spot_size, spot_size, 500)
    spot_mesh = np.meshgrid(spot_space, spot_space)
    focal_spot_field = aff.E_field(
        dipole_orientation_angle=E_d_angle,
        xi=spot_mesh[0],
        y=spot_mesh[1],
        k=k
        ).T
    intensity_ofx = c/(8*np.pi) * np.sum(
        focal_spot_field*np.conj(focal_spot_field), axis=-1)

    area_image = (spot_space.max() - spot_space.min())**2.
    num_pixels = len(spot_space)**2.
    area_per_pixel = area_image / num_pixels

    beam_power = np.sum(intensity_ofx)*area_per_pixel
    ## integral of (c/8pi)|E|^2 dA = beam_power
    E_0 /= (beam_power)**0.5
    E_1 /= (beam_power)**0.5

    # print(f'np.sum(intensity_ofx) = {np.sum(intensity_ofx)}')
    # print(f'(2*spot_size)**2. = {(2*spot_size)**2.}')
    # print(f'beam_power = {beam_power}')

    ## Rotate polarizabilities into connecting vector frame
    alpha_0_p0 = alpha0_diag
    alpha_0 = rotation_by(-phi_0) @ alpha_0_p0 @ rotation_by(phi_0)

    alpha_1_p1 = alpha1_diag
    alpha_1 = rotation_by(-phi_1) @ alpha_1_p1 @ rotation_by(phi_1)

    G_d = G(drive_hbar_w, d_col, n_b)

    geometric_coupling_01 = np.linalg.inv(
        np.identity(3) - alpha_0 @ G_d @ alpha_1 @ G_d
        )

    # p0 = (
    #     np.einsum(
    #         '...ij,...j->...i',
    #         geometric_coupling_01 @ alpha_0,
    #         E_0
    #         )
    #     +
    #     np.einsum(
    #         '...ij,...j->...i',
    #         geometric_coupling_01 @ alpha_0 @ G_d @ alpha_1,
    #         E_1
    #         )
    #     )
    # p1 = (
    #     np.einsum(
    #         '...ij,...j->...i',
    #         geometric_coupling_01 @ alpha_1,
    #         E_1
    #         )
    #     +
    #     np.einsum(
    #         '...ij,...j->...i',
    #         geometric_coupling_01 @ alpha_1 @ G_d @ alpha_0,
    #         E_0
    #         )
    #     )
    p0 = np.einsum(
        '...ij,...j->...i',
        geometric_coupling_01 @ alpha_0 @ (
            np.identity(3) + G_d @ alpha_1
            ),
        E_0
        )
    p1 = np.einsum(
        '...ij,...j->...i',
        geometric_coupling_01 @ alpha_1 @ (
            np.identity(3) + G_d @ alpha_0
            ),
        E_0
        )

    if not return_polarizabilities:
        return [p0, p1]
    ## If using to compute absorption spectrum,
    elif return_polarizabilities:
        return [p0, p1, alpha_0, alpha_1]
def single_dip_mag_focused_beam(
    angle,
    p0_position,
    beam_x_positions,
    E_d_angle=None,
    drive_hbar_w=None,
    alpha0_diag=None,
    n_b=None,
    drive_amp=None,
    return_polarizabilities=False,
    ):
    """ Calculate dipole magnitude with generalized dyadic
        polarizabilities driven by a focused dipole PSF beam.

        Returns dipole moment vector as rows in array of shape
        (# of seperations, # of beam positions, 3 cart. coords).

        'p0_positions' is expected in shape
            (3)

        beam_x_positions is currelty just assuming a 1d slice, so it
        should be shape
            (number of points) on the x axis.

        """

    # Initialize unit vector for molecule dipole in lab frame
    phi_0 = angle ## angle of bf_p0 in lab frame


    k = (drive_hbar_w*n_b/hbar) / c

    ## Define positions with shape (num_seperations, 3)
    if p0_position.ndim is 1:
        p0_position = p0_position[None, :]

    ## Buld focused beam profile
    E_0 = aff.E_field(
        dipole_orientation_angle=E_d_angle,
        xi=beam_x_positions - p0_position[...,0],
        y=0,
        k=k
        ).T*drive_amp

    ## Normalize fields to correct beam intensity
    spot_size = 2*np.pi/k
    spot_space = np.linspace(-spot_size, spot_size, 500)
    spot_mesh = np.meshgrid(spot_space, spot_space)
    focal_spot_field = aff.E_field(
        dipole_orientation_angle=E_d_angle,
        xi=spot_mesh[0],
        y=spot_mesh[1],
        k=k
        ).T
    intensity_ofx = c/(8*np.pi) * np.sum(
        focal_spot_field*np.conj(focal_spot_field), axis=-1)

    area_image = (spot_space.max() - spot_space.min())**2.
    num_pixels = len(spot_space)**2.
    area_per_pixel = area_image / num_pixels

    beam_power = np.sum(intensity_ofx)*area_per_pixel
    ## integral of (c/8pi)|E|^2 dA = beam_power
    E_0 /= (beam_power)**0.5

    ## Rotate polarizabilities into connecting vector frame
    alpha_0_p0 = alpha0_diag
    alpha_0 = rotation_by(-phi_0) @ alpha_0_p0 @ rotation_by(phi_0)

    ## Dipole mmoment
    p0 = np.einsum(
        '...ij,...j->...i',
        alpha_0,
        E_0
        )

    if not return_polarizabilities:
        return [p0]
    ## If using to compute absorption spectrum,
    elif return_polarizabilities:
        return [p0, alpha_0]
    def foc_field(self, dip_angle, x, y, k):
        """ Dipole field of an x oriented dipole"""

        E = aff.E_field(dipole_orientation_angle=dip_angle, xi=x, y=y, k=k)

        return E