<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def spherical_pores(target, pore_diameter='pore.diameter', throat_diameter='throat.diameter', throat_centroid='throat.centroid'):
 r""" Calculate the coordinates of throat endpoints, assuming spherical pores. This model accounts for the overlapping lens between pores and throats. Parameters target : OpenPNM Object The object which this model is associated with. This controls the length of the calculated array, and also provides access to other necessary properties. pore_diameter : string Dictionary key of the pore diameter values. throat_diameter : string Dictionary key of the throat diameter values. throat_centroid : string, optional Dictionary key of the throat centroid values. See the notes. Returns ------- EP : dictionary Coordinates of throat endpoints stored in Dict form. Can be accessed via the dict keys 'head' and 'tail'. Notes ----- (1) This model should not be applied to true 2D networks. Use `circular_pores` model instead. (2) By default, this model assumes that throat centroid and pore coordinates are colinear. If that's not the case, such as in extracted networks, `throat_centroid` could be passed as an optional argument, and the model takes care of the rest. """

    network = target.project.network
    throats = network.map_throats(throats=target.Ts, origin=target)
    xyz = network['pore.coords']
    cn = network['throat.conns'][throats]
    L = _ctc(target=target, pore_diameter=pore_diameter)
    Dt = network[throat_diameter][throats]
    D1 = network[pore_diameter][cn[:, 0]]
    D2 = network[pore_diameter][cn[:, 1]]
    L1 = _sp.zeros_like(L)
    L2 = _sp.zeros_like(L)
    # Handle the case where Dt > Dp
    mask = Dt > D1
    L1[mask] = 0.5 * D1[mask]
    L1[~mask] = _sp.sqrt(D1[~mask]**2 - Dt[~mask]**2) / 2
    mask = Dt > D2
    L2[mask] = 0.5 * D2[mask]
    L2[~mask] = _sp.sqrt(D2[~mask]**2 - Dt[~mask]**2) / 2
    # Handle non-colinear pores and throat centroids
    try:
        TC = network[throat_centroid][throats]
        LP1T = _sp.linalg.norm(TC - xyz[cn[:, 0]], axis=1)
        LP2T = _sp.linalg.norm(TC - xyz[cn[:, 1]], axis=1)
        unit_vec_P1T = (TC - xyz[cn[:, 0]]) / LP1T[:, None]
        unit_vec_P2T = (TC - xyz[cn[:, 1]]) / LP2T[:, None]
    except KeyError:
        unit_vec_P1T = (xyz[cn[:, 1]] - xyz[cn[:, 0]]) / L[:, None]
        unit_vec_P2T = -1 * unit_vec_P1T
    # Find throat endpoints
    EP1 = xyz[cn[:, 0]] + L1[:, None] * unit_vec_P1T
    EP2 = xyz[cn[:, 1]] + L2[:, None] * unit_vec_P2T
    # Handle throats w/ overlapping pores
    L1 = (4*L**2 + D1**2 - D2**2) / (8*L)
    # L2 = (4*L**2 + D2**2 - D1**2) / (8*L)
    h = (2*_sp.sqrt(D1**2/4 - L1**2)).real
    overlap = L - 0.5 * (D1+D2) < 0
    mask = overlap & (Dt < h)
    EP1[mask] = EP2[mask] = (xyz[cn[:, 0]] + L1[:, None] * unit_vec_P1T)[mask]
    return {'head': EP1, 'tail': EP2}