Module aqequil.dissrxn_balancer

Module for checking and balancing dissociation reactions.

This module provides functionality to check if dissociation reactions are balanced and to balance them if needed using CHNOSZ basis species.

Functions

def balance_dissrxn(species_name, species_list, basis_species, fixed_species=None)
Expand source code
def balance_dissrxn(species_name, species_list, basis_species, fixed_species=None):
    """
    Balance a dissociation reaction by adding basis species.

    Parameters
    ----------
    species_name : str
        Name of the species to balance
    species_list : list
        List of species names already in the reaction
    basis_species : list
        List of basis species names to use for balancing
    fixed_species : list, optional
        List of fixed species (H2O, H+, O2, etc.)

    Returns
    -------
    dict
        Dictionary with 'species' and 'coeffs' keys, or None if balancing fails
    """
    if fixed_species is None:
        fixed_species = ["H2O", "H+", "O2(g)", "water", "e-"]

    try:
        # Set up basis
        pychnosz.basis(basis_species, messages=False)

        # Use subcrt to get balanced reaction
        result = pychnosz.subcrt([species_name], [-1], messages=False)

        if result is None or not hasattr(result, 'reaction'):
            return None

        # Extract coefficients and species names from the reaction DataFrame
        coeffs = result.reaction['coeff']
        names = result.reaction['name']

        # Replace 'water' with 'H2O' for EQ3 compatibility
        names = ['H2O' if n == 'water' else n for n in names]

        return {
            'species': names,
            'coeffs': [float(c) for c in coeffs]
        }

    except Exception as e:
        return None

Balance a dissociation reaction by adding basis species.

Parameters

species_name : str
Name of the species to balance
species_list : list
List of species names already in the reaction
basis_species : list
List of basis species names to use for balancing
fixed_species : list, optional
List of fixed species (H2O, H+, O2, etc.)

Returns

dict
Dictionary with 'species' and 'coeffs' keys, or None if balancing fails
def check_dissrxn_balanced(dissrxn_str, species_name=None, verbose=False)
Expand source code
def check_dissrxn_balanced(dissrxn_str, species_name=None, verbose=False):
    """
    Check if a dissociation reaction is balanced.

    This uses CHNOSZ's info() and makeup() to check if the reaction balances,
    similar to the R version's subcrt_bal() function.

    Parameters
    ----------
    dissrxn_str : str
        Dissociation reaction string in format: "-1.0000 species1 1.0000 species2 ..."
    species_name : str, optional
        Name of the species being checked (for error messages)
    verbose : bool
        Whether to print detailed information

    Returns
    -------
    bool
        True if reaction is balanced, False otherwise
    """
    if not dissrxn_str or dissrxn_str.strip() == '' or dissrxn_str == 'nan':
        return False

    try:
        # Parse the dissociation reaction
        parts = dissrxn_str.strip().split()
        if len(parts) < 2:  # Need at least coeff1 species1
            return False

        coeffs = []
        species_names = []

        for i in range(0, len(parts), 2):
            try:
                coeffs.append(float(parts[i]))
                species_names.append(parts[i+1])
            except (ValueError, IndexError):
                return False

        # Get species indices from CHNOSZ
        try:
            ispecies = pychnosz.info(species_names, messages=False)
        except Exception as e:
            if verbose:
                print(f"Error getting species info: {e}")
            return False

        # Calculate mass balance using CHNOSZ's makeup
        # This is equivalent to R's makeup(ispecies, coeff, sum=TRUE)
        # Use species indices instead of names to properly look up from database
        total_makeup = {}
        for i, (idx, coeff) in enumerate(zip(ispecies, coeffs)):
            try:
                # Use index to get makeup from database
                sp_makeup = pychnosz.makeup(idx)
                for elem, count in sp_makeup.items():
                    if elem not in total_makeup:
                        total_makeup[elem] = 0.0
                    total_makeup[elem] += coeff * count
            except Exception as e:
                if verbose:
                    print(f"Error getting makeup for {species_names[i]} (index {idx}): {e}")
                return False

        # Check if all elements balance (should be ~0)
        # Take out very small numbers (matching R's abs(mss) < 1e-7)
        tolerance = 1e-7
        for elem in total_makeup:
            if abs(total_makeup[elem]) < tolerance:
                total_makeup[elem] = 0.0

        # Check if any element is non-zero (unbalanced)
        if any(abs(v) > tolerance for v in total_makeup.values()):
            if verbose:
                print(f"Unbalanced reaction for {species_name}: {total_makeup}")
            return False

        return True

    except Exception as e:
        if verbose:
            print(f"Error checking balance for {species_name}: {e}")
        return False

Check if a dissociation reaction is balanced.

This uses CHNOSZ's info() and makeup() to check if the reaction balances, similar to the R version's subcrt_bal() function.

Parameters

dissrxn_str : str
Dissociation reaction string in format: "-1.0000 species1 1.0000 species2 …"
species_name : str, optional
Name of the species being checked (for error messages)
verbose : bool
Whether to print detailed information

Returns

bool
True if reaction is balanced, False otherwise
def format_dissrxn_string(species_names, coeffs)
Expand source code
def format_dissrxn_string(species_names, coeffs):
    """
    Format a dissociation reaction as a string.

    Parameters
    ----------
    species_names : list
        List of species names
    coeffs : list
        List of coefficients

    Returns
    -------
    str
        Formatted dissociation reaction string
    """
    parts = []
    for coeff, name in zip(coeffs, species_names):
        parts.append(f"{coeff:.4f}")
        parts.append(name)

    return " ".join(parts)

Format a dissociation reaction as a string.

Parameters

species_names : list
List of species names
coeffs : list
List of coefficients

Returns

str
Formatted dissociation reaction string