<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def rot_consts(geom, masses, units=_EURC.INV_INERTIA, on_tol=_DEF.ORTHONORM_TOL):
 """Rotational constants for a given molecular system. Calculates the rotational constants for the provided system with numerical value given in the units provided in `units`. The orthnormality tolerance `on_tol` is required in order to be passed through to the :func:`principals` function. If the system is linear or a single atom, the effectively-zero principal moments of inertia will be assigned values of :data:`opan.const.PRM.ZERO_MOMENT_TOL` before transformation into the appropriate rotational constant units. The moments of inertia are always sorted in increasing order as :math:`0 \\leq I_A \\leq I_B \\leq I_C`; the rotational constants calculated from these will thus always be in **decreasing** order as :math:`B_A \\geq B_B \\geq B_C`, retaining the ordering and association with the three principal ``axes[:,i]`` generated by :func:`principals`. Parameters geom length-3N |npfloat_| -- Coordinates of the atoms masses length-N OR length-3N |npfloat_| -- Atomic masses of the atoms. Length-3N option is to allow calculation of a per-coordinate perturbed value. units :class:`~opan.const.EnumUnitsRotConst`, optional -- Enum value indicating the desired units of the output rotational constants. Default is :data:`~opan.const.EnumUnitsRotConst.INV_INERTIA` :math:`\\left(1\\over \\mathrm{uB^2}\\right)` on_tol |npfloat_|, optional -- Tolerance for deviation from unity/zero for principal axis dot products, within which axes are considered orthonormal. Default is :data:`opan.const.DEF.ORTHONORM_TOL` Returns ------- rc length-3 |npfloat_| -- Vector of rotational constants in the indicated units """


    # Imports
    import numpy as np
    from ..const import EnumTopType as ETT, EnumUnitsRotConst as EURC, PRM, PHYS

    # Ensure units are valid
    if not units in EURC:
        raise ValueError("'{0}' is not a valid units value".format(units))
    ## end if

    # Retrieve the moments, axes and top type. Geom and masses are proofed
    #  internally in this call.
    mom, ax, top = principals(geom, masses, on_tol)

    # Check for special cases
    if top == ETT.ATOM:
        # All moments are zero; set to zero-moment threshold
        mom = np.repeat(PRM.ZERO_MOMENT_TOL, 3)
    elif top == ETT.LINEAR:
        # First moment is zero; set to zero-moment threshold
        mom[0] = PRM.ZERO_MOMENT_TOL
    ## end if

    # Calculate the values in the indicated units
    if units == EURC.INV_INERTIA:            # 1/(amu*B^2)
        rc = 1.0 / (2.0 * mom)
    elif units == EURC.ANGFREQ_ATOMIC:       # 1/Ta
        rc = PHYS.PLANCK_BAR / (2.0 * mom * PHYS.ME_PER_AMU)
    elif units == EURC.ANGFREQ_SECS:      # 1/s
        rc = PHYS.PLANCK_BAR / (2.0 * mom * PHYS.ME_PER_AMU) / PHYS.SEC_PER_TA
    elif units == EURC.CYCFREQ_ATOMIC:    # cyc/Ta
        rc = PHYS.PLANCK_BAR / (4.0 * np.pi * mom * PHYS.ME_PER_AMU)
    elif units == EURC.CYCFREQ_HZ:        # cyc/s
        rc = PHYS.PLANCK_BAR / (4.0 * np.pi * mom * PHYS.ME_PER_AMU) / \
                                                            PHYS.SEC_PER_TA
    elif units == EURC.CYCFREQ_MHZ:       # Mcyc/s
        rc = PHYS.PLANCK_BAR / (4.0 * np.pi * mom * PHYS.ME_PER_AMU) / \
                                                    PHYS.SEC_PER_TA / 1.0e6
    elif units == EURC.WAVENUM_ATOMIC:       # cyc/B
        rc = PHYS.PLANCK / (mom * PHYS.ME_PER_AMU) / \
            (8.0 * np.pi**2.0 * PHYS.LIGHT_SPEED)
    elif units == EURC.WAVENUM_CM:           # cyc/cm
        rc = PHYS.PLANCK / (mom * PHYS.ME_PER_AMU) / \
            (8.0 * np.pi**2.0 * PHYS.LIGHT_SPEED * PHYS.ANG_PER_BOHR) * 1.0e8
    else:               # pragma: no cover -- Valid units; not implemented
        raise NotImplementedError("Units conversion not yet implemented.")
    ## end if

    # Return the result
    return rc