validate_and_fill_chgmult

qcelemental.molparse.validate_and_fill_chgmult(zeff, fragment_separators, molecular_charge: Optional[float], fragment_charges, molecular_multiplicity: Optional[int], fragment_multiplicities, zero_ghost_fragments: bool = False, verbose: int = 1) → Dict[str, Any][source]

Forms molecular and fragment charge and multiplicity specification by completing and reconciling information from argument, supplemented by physical constraints and sensible defaults.

Parameters
  • zeff (ndarray of float) – (nat,) electron counts for neutral atoms, generally Z nuclear charge. 0 indicates ghosts such that a full fragment of 0s will be constained to 0 1 charge & multiplicity.

  • fragment_separators (ndarray of int) – (nfr - 1, ) indices splitting zeff into nfr fragments.

  • molecular_charge (float or None) – Total charge for molecular system.

  • fragment_charges (list of float or None) – (nfr,) known fragment charges with None as placeholder for unknown. Expected pre-defaulted so even if nothing known, if fragment_separators breaks zeff into nfr=2 fragments, input value should be fragment_charges=[None, None].

  • molecular_multiplicity (int or None) – Total multiplicity for molecular system.

  • fragment_multiplicities (list of int or None) – (nfr,) known fragment charges with None as placeholder for unknown. Expected pre-defaulted so even if nothing known, if fragment_separators breaks zeff into nfr=2 fragments, input value should be fragment_multiplicities=[None, None].

  • zero_ghost_fragments (bool, optional) – Fragments composed entirely of ghost atoms (Zeff=0) are required to have chgmult 0 1. When False, violations of this will cause a ValidationError. When True, treat ghost fragments indicated by zeff to contain superior information over chgmult arguments that might still correspond to full-real molecule. Clears information from molecular_charge and molecular_multiplicity and sets ghost fragments to 0 1, leaving other positions free to readjust. Unused (prefer to set up such manipulations outside function call) but works.

  • verbose (int, optional) – Amount of printing.

Returns

  • molecular_charge (float) – Total charge for molecular system.

  • fragment_charges (list of float) – (nfr,) Charge on each fragment.

  • molecular_multiplicity (int) – Total multiplicity for molecular system.

  • fragment_multiplicities (list of int) – (nfr,) Multiplicity for each fragment.

Raises

qcelemental.ValidationError – When no solution to input arguments subject to the constraints below can be found.

Notes

Returns combination of total & fragment charge & multiplicity among values of S1-7 that fulfill rules R1-9. A few derived implications in I1-3.

  • Constraints

    • R1 require all chg & mult exist

    • R2 require total charge to be the sum of frag chg

    • R3 require mult is positive int

    • R4 require sufficient tot electrons for mult: mult - 1 <= neut_el - chg

    • R5 require total parity consistent among tot electrons and mult: (mult % 2) != ((neut_el - chg) % 2)

    • R6 require chg match input argument values

    • R7 require mult match input argument values

    • R8 require that tot = sum(frag) mult follow high spin addition unless tot & frag mult fully specified

    • R9 require that ghost fragments (zeff all 0) be neutral singlet

  • Allowed values

    • S1 suggest input argument values for tot chg, frag chg, tot mult or frag mult

    • S2 suggest sum frag chg for tot chg, allowing for indiv frag chg defaulting to 0

    • S3 suggest distributing unallocated chg onto frag chg

    • S4 suggest 0 default for frag chg

    • S5 suggest range of high-spin sum frag mult for tot mult, allowing for indiv frag mult defaulting to 1 or 2

    • S6 suggest range of unallocated mult = tot - high_spin_sum(frag - 1), allowing for all indiv but self defaulting to 1 or 2.

    • S7 suggest 1 or 2 default for frag mult

  • Implications

    • I1 won’t form an ion just to be closed shell (would require choosing +1 vs. -1)

    • I2 unallocated chg or mult lands on the first unspecified fragment able to bear it (enforced by returning first match encountered; subsequent matches distribute charge to later frags)

    • I3 missing chg or mult from tot - frags will always be allocated as a block, not distributed

Examples

>>> validate_and_fill_chgmult(*sys('He'), 0, [0], 1, [1])
0, [0], 1, [1]
>>> validate_and_fill_chgmult(*sys('He'), None, [None], None, [None])
0, [0], 1, [1]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], None, [None, None])
0, [0, 0], 1, [1, 1])
>>> validate_and_fill_chgmult(*sys('He/He'), 2, [None, None], None, [None, None])
2, [2, 0], 1, [1, 1])
>>> validate_and_fill_chgmult(*sys('He/He'), None, [2, None], None, [None, None])
2, [2, 0], 1, [1, 1])
>>> validate_and_fill_chgmult(*sys('He/He'), 0, [2, None], None, [None, None])
0, [2, -2], 1, [1, 1])
>>> validate_and_fill_chgmult(*sys('Ne/He/He'), -2, [None, 2, None], None, [None, None, None])
-2, [-4, 2, 0], 1, [1, 1, 1]
>>> validate_and_fill_chgmult(*sys('Ne/He/He'), 2, [None, -2, None], None, [None, None, None])
2, [4, -2, 0], 1, [1, 1, 1]
#  9 - residual +4 distributes to first fragment able to wholly accept it (He+4 is no-go)
>>> validate_and_fill_chgmult(*sys('He/He/Ne'), 2, [None, -2, None], None, [None, None, None])
2, [0, -2, 4], 1, [1, 1, 1]
# 10 - residual +4 unsuited for only open fragment, He, so irreconcilable
>>> validate_and_fill_chgmult(*sys('He/He/Ne'), 2, [None, -2, 0], None, [None, None, None])
ValidationError
# 11 - non-positive multiplicity
>>> validate_and_fill_chgmult(*sys('He/He/Ne'), 2, [2, -2, None], None, [None, None, None])
2, [2, -2, 2], 1, [1, 1, 1])
>>> validate_and_fill_chgmult(*sys('He/He'), None, [-2, 2], None, [None, None])
0, [-2, 2], 1, [1, 1]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, -2], None, [None, None])
-2, [0, -2], 1, [1, 1]
>>> validate_and_fill_chgmult(*sys('Ne/Ne'), 0, [None, 4], None, [None, None])
0, [-4, 4], 1, [1, 1]
>>> validate_and_fill_chgmult(*sys('He/He/He'), 4, [2, None, None], None, [None, None, None])
4, [2, 2, 0], 1, [1, 1, 1]
>>> validate_and_fill_chgmult(*sys('He/He'), 0, [-2, 2], None, [None, None])
0, [-2, 2], 1, [1, 1]
>>> validate_and_fill_chgmult(*sys('He/He'), 0, [-2, -2], None, [None, None])
ValidationError
>>> validate_and_fill_chgmult(*sys('He'), None, [None], 0, [None])
ValidationError
>>> validate_and_fill_chgmult(*sys('He'), None, [None], None, [1])
0, [0], 1, [1]
# 20 - doublet non consistent with closed-shell, neutral default charge
>>> validate_and_fill_chgmult(*sys('He'), None, [None], None, [2])
ValidationError
>>> validate_and_fill_chgmult(*sys('He'), None, [None], None, [3])
0, [0], 3, [3]
# 22 - insufficient electrons for pentuplet
>>> validate_and_fill_chgmult(*sys('He'), None, [None], None, [5])
ValidationError
>>> validate_and_fill_chgmult(*sys('He'), None, [-1], None, [2])
-1, [-1], 2, [2]
# 24 - doublet not consistent with even charge
>>> validate_and_fill_chgmult(*sys('He'), None, [-2], None, [2])
ValidationError
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], None, [1, 1])
0, [0, 0], 1, [1, 1]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], None, [3, 1])
0, [0, 0], 3, [3, 1]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], None, [1, 3])
0, [0, 0], 3, [1, 3]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], None, [3, 3])
0, [0, 0], 5, [3, 3]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], 3, [3, 3])
0, [0, 0], 3, [3, 3]
# 30 - bad parity btwn mult and total # electrons
>>> validate_and_fill_chgmult(*sys('He/He'), None, [None, None], 2, [3, 3])
ValidationError
>>> validate_and_fill_chgmult(*sys('H'), None, [None], None, [None])
0, [0], 2, [2]
>>> validate_and_fill_chgmult(*sys('H'), 1, [None], None, [None])
1, [1], 1, [1]
>>> validate_and_fill_chgmult(*sys('H'), None, [-1], None, [None])
-1, [-1], 1, [1]
>>> validate_and_fill_chgmult(*sys('funnyH'), None, [None], None, [None])
0, [0], 1, [1]
# 35 - insufficient electrons
>>> validate_and_fill_chgmult(*sys('funnierH'), None, [None], None, [None])
ValidationError
>>> validate_and_fill_chgmult(*sys('H/H'), None, [None, None], None, [None, None])
0, [0, 0], 3, [2, 2]
>>> validate_and_fill_chgmult(*sys('H/He'), None, [None, None], None, [None, None])
0, [0, 0], 2, [2, 1]
>>> validate_and_fill_chgmult(*sys('H/He'), None, [1, 1], None, [None, None])
2, [1, 1], 2, [1, 2]
>>> validate_and_fill_chgmult(*sys('H/He'), -2, [-1, None], None, [None, None])
-2, [-1, -1], 2, [1, 2]
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), None, [1, None, 1, None], None, [None, None, None, None])
2, [1, 0, 1, 0], 1, [1, 1, 1, 1]
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), None, [-1, None, 1, None], None, [None, None, None, None])
0, [-1, 0, 1, 0], 1, [1, 1, 1, 1]
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), 2, [None, None, 1, None], None, [None, None, None, None])
2, [1, 0, 1, 0], 1, [1, 1, 1, 1]
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), 3, [None, None, 1, None], None, [None, None, None, None])
3, [0, 2, 1, 0], 2, [2, 1, 1, 1]
>>> validate_and_fill_chgmult(*sys('H/He'), None, [1, None], None, [2, None])
ValidationError
>>> validate_and_fill_chgmult(*sys('H/He'), None, [None, 0], None, [None, 2])
ValidationError
>>> validate_and_fill_chgmult(*sys('H/He'), None, [None, -1], None, [None, 3])
ValidationError
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), None, [None, 1, 0, 1], None, [None, None, None, None])
2, [0, 1, 0, 1], 5, [2, 2, 2, 2]
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), None, [None, 1, 0, None], None, [None, None, None, None])
1, [0, 1, 0, 0], 4, [2, 2, 2, 1]
>>> validate_and_fill_chgmult(*sys('H/He/Na/Ne'), None, [None, 1, 0, None], None, [None, None, 4, None])
1, [0, 1, 0, 0], 6, [2, 2, 4, 1]
>>> validate_and_fill_chgmult(*sys('He/He/He'), 0, [None, None, 1], None, [1, None, 2])
0, [0, -1, 1], 3, [1, 2, 2]
>>> validate_and_fill_chgmult(*sys('N/N/N'), None, [1, 1, 1], 3, [None, 3, None])
3, [1, 1, 1], 3, [1, 3, 1]
>>> validate_and_fill_chgmult(*sys('N/N/N'), None, [1, 1, 1], 3, [None, None, None])
3, [1, 1, 1], 3, [3, 1, 1]
>>> validate_and_fill_chgmult(*sys('N/N/N'), None, [None, None, None], 3, [None, None, 2])
ValidationError
>>> validate_and_fill_chgmult(*sys('N/N/N'), 1, [None, -1, None], 3, [None, None, 2])
1, [2, -1, 0], 3, [2, 1, 2]
# 55 - both (1, (1, 0.0, 0.0), 4, (1, 3, 2)) and (1, (0.0, 0.0, 1), 4, (2, 3, 1)) plausible
>>> validate_and_fill_chgmult(*sys('N/Ne/N'), 1, [None, None, None], 4, [None, 3, None])
1, [1, 0, 0], 4, [1, 3, 2]
>>> validate_and_fill_chgmult(*sys('N/Ne/N'), None, [None, None, 1], 4, [None, 3, None])
1, [0, 0, 1], 4, [2, 3, 1]
>>> validate_and_fill_chgmult(*sys('He/He'), None, [-1, 1], None, [None, None])
0, [-1, 1], 3, [2, 2]
>>> validate_and_fill_chgmult(*sys('Gh'), 1, [None], None, [None])
ValidationError
>>> validate_and_fill_chgmult(*sys('Gh'), -1, [None], None, [None])
ValidationError
>>> validate_and_fill_chgmult(*sys('Gh'), None, [None], 3, [None])
ValidationError
>>> validate_and_fill_chgmult(*sys('He/Gh'), None, [2, None], None, [None, None])
2, [2, 0], 1, [1, 1]
>>> validate_and_fill_chgmult(*sys('Gh/He'), None, [2, None], None, [None, None])
ValidationError
>>> validate_and_fill_chgmult(*sys('Gh/He/Ne'), 2, [None, -2, None], None, [None, None, None])
2, [0, -2, 4], 1, [1, 1, 1]
>>> validate_and_fill_chgmult(*sys('Gh/He/Gh'), 1, [None, None, None], None, [None, None, None])
1, [0, 1, 0], 2, [1, 2, 1]
>>> sys = {
    'He': (np.array([2]), np.array([])),
    'He/He': (np.array([2, 2]), np.array([1])),
    'Ne/He/He': (np.array([10, 2, 2]), np.array([1, 2])),
    'He/He/Ne': (np.array([2, 2, 10]), np.array([1, 2])),
    'Ne/Ne': (np.array([10, 10]), np.array([1])),
    'He/He/He': (np.array([2, 2, 2]), np.array([1, 2])),
    'H': (np.array([1]), np.array([])),
    'funnyH': (np.array([0]), np.array([])),  # has no electrons
    'funnierH': (np.array([-1]), np.array([])),  # has positron
    'H/H': (np.array([1, 1]), np.array([1])),
    'H/He': (np.array([1, 2]), np.array([1])),
    'H/He/Na/Ne': (np.array([1, 2, 11, 10]), np.array([1, 2, 3])),
    'N/N/N': (np.array([7, 7, 7]), np.array([1, 2])),
    'N/Ne/N': (np.array([7, 10, 7]), np.array([1, 2])),
    'He/Gh': (np.array([2, 0]), np.array([1])),
    'Gh/He': (np.array([0, 2]), np.array([1])),
    'Gh': (np.array([0, 0]), np.array([])),
    'Gh/He/Ne': (np.array([0, 0, 2, 10]), np.array([2, 3])),
    'Gh/He/Gh': (np.array([0, 2, 0]), np.array([1, 2]))}