示例#1
0
def test_solution_auto_equilibrate(consolidated_data):
    """ assert that solutions are equilibrated with Na or Cl depending
        on what their concentrations are """
    df = consolidated_data
    df = df.iloc[:3, :]
    # make sure that equilibrating with Na will fail because
    # it will needs a concentration below 0 to match the
    # charge in balance
    df.loc[0, 'Fe'] = 2*df.loc[0, 'Na']
    sol = df.hgc.get_phreeqpython_solutions(equilibrate_with='auto', inplace=False)
    Na_in_sol = [s.total_element('Na') * mw('Na') for s in sol]
    Cl_in_sol = [s.total_element('Cl') * mw('Cl') for s in sol]
    # first one equilibrates with Cl
    np.testing.assert_allclose(
        Na_in_sol[:1], df.loc[:0, 'Na'].values, rtol=1.e-1)
    with pytest.raises(AssertionError):
        np.testing.assert_allclose(Na_in_sol[1:],
                                   df.loc[1:, 'Na'].values,
                                   rtol=1.e-1)
    # others one equilibrates with Na
    np.testing.assert_allclose(
        Cl_in_sol[1:], df.loc[1:, 'Cl'].values, rtol=1.e-1)
示例#2
0
def test_add_oxygen(consolidated_data, phreeqpython_solutions_excel):
    ''' Test oxygen is added and returned correctly to and from
    phreeqpython with or without. Test it in pure phreeqpython (because there
    it is not working as expected)
    - test that no O(0) added yields no O in solution
    - test that adding `O(0)` `as O2` yields wrong result
    - test that adding `O(0)` without `as O2` yields correct result
    - test that adding in the SamplesFrame O2 concentration yields correct results '''
    pp = PhreeqPython()
    test_solution_dict = {'units': 'mg/L',
                          'Cl': 8.0,
                          'Na': '2.0 charge',
                          'Alkalinity': '2.0 as HCO3',
                          'Ca': 1.0,
                          'pH': 7,
                          'temp': 11
                          }
    sol = pp.add_solution(test_solution_dict)
    assert sol.total_element('O') == 0.0

    test_solution_dict['O(0)'] = '3 as O2'
    sol1 = pp.add_solution(test_solution_dict)
    assert sol1.species['O2'] * 2 * mw('O') * 1000. != pytest.approx(3., 1.e-4)

    test_solution_dict['O(0)'] = 3
    sol1 = pp.add_solution(test_solution_dict)
    assert sol1.species['O2'] * 2 * mw('O') * 1000. == pytest.approx(3., 1.e-4)

    test_data = pd.DataFrame({'Cl': 8, 'Na': 2, 'alkalinity': 2,
                              'Ca': 1, 'O2': 3, 'ph': 7,
                              'temp': 11}, index=[0])
    test_data.hgc.make_valid()
    test_data.hgc.consolidate(
        use_so4=None, use_ph=None, use_ec=None, use_temp=None)
    sols = test_data.hgc.get_phreeqpython_solutions(inplace=False)
    assert sols[0].species['O2'] * 2 * \
        mw('O') * 1000. == pytest.approx(3., 1.e-4)
示例#3
0
def test_solution_equilibrate_with(consolidated_data):
    ''' Assert phreeqpython solutions are returned as series'''
    df = consolidated_data

    solutions_default = df.hgc.get_phreeqpython_solutions(inplace=False)
    solutions_Na = df.hgc.get_phreeqpython_solutions(equilibrate_with='Na', inplace=False)
    solutions_Cl = df.hgc.get_phreeqpython_solutions(equilibrate_with='Cl', inplace=False)
    solutions_none = df.hgc.get_phreeqpython_solutions(equilibrate_with=None, inplace=False)
    solutions_auto = df.hgc.get_phreeqpython_solutions(equilibrate_with='auto', inplace=False)

    # get the list of Na-concentrations in the phreeqpython-solutions (from mmol/L to
    # mg/L)
    Na_in_sol_default = [s.total_element('Na') * mw('Na')
                         for s in solutions_default]
    Na_in_sol_Na = [s.total_element('Na') * mw('Na') for s in solutions_Na]
    Na_in_sol_Cl = [s.total_element('Na') * mw('Na') for s in solutions_Cl]
    Na_in_sol_none = [s.total_element('Na') * mw('Na') for s in solutions_none]
    Na_in_sol_auto = [s.total_element('Na') * mw('Na') for s in solutions_auto]

    # get the list of Fe-concentrations in the phreeqpython-solutions (from mmol/L to
    # mg/L)
    Fe_in_sol_default = [s.total_element('Fe') * mw('Fe')
                         for s in solutions_default]
    Fe_in_sol_none = [s.total_element('Fe') * mw('Fe')
                      for s in solutions_none]
    Fe_in_sol_auto = [s.total_element('Fe') * mw('Fe')
                      for s in solutions_auto]

    # test that default equilibrate with is None by returning the same array
    np.testing.assert_array_equal(Na_in_sol_default, Na_in_sol_none)
    # assert that indeed Na-concentration is altered by comparing it
    # to the Na concentration when Cl is used for equilibration
    assert all(np.not_equal(Na_in_sol_Cl, Na_in_sol_Na))
    # assert that auto equals with Na for this case
    assert all(np.not_equal(Na_in_sol_none, Na_in_sol_Na))
    # assert Na-concentration is not altered by using equilibrate with
    # Cl. Na concentration should be the same as in the original dataframe
    # to some extent of accuracy (this is generally not closer than 10% in my experience).
    np.testing.assert_allclose(Na_in_sol_Cl, df.Na.values, rtol=1.e-1)
    np.testing.assert_allclose(Na_in_sol_none, df.Na.values, rtol=1.e-1)
    # same assertion but now for Fe
    np.testing.assert_allclose(Fe_in_sol_default, df.Fe.values, rtol=1.e-1)
    np.testing.assert_allclose(Fe_in_sol_none, df.Fe.values, rtol=1.e-1)
    np.testing.assert_allclose(Fe_in_sol_auto, df.Fe.values, rtol=1.e-1)
示例#4
0
    def _get_dominant_anions_of_df(self, df_in):
        """  calculates the dominant anions of the dataframe df_in """
        s_sum_cations = self.get_sum_cations(inplace=False)

        cols_req = ('ph', 'Na', 'K', 'Ca', 'Mg', 'Fe', 'Mn', 'NH4', 'Al', 'Ba',
                    'Co', 'Cu', 'Li', 'Ni', 'Pb', 'Sr', 'Zn')
        df_in = df_in.hgc._make_input_df(cols_req)

        na_mmol = df_in.Na / mw('Na')
        k_mmol = df_in.K / mw('K')
        nh4_mmol = df_in.NH4 / (mw('N') + 4 * mw('H'))
        ca_mmol = df_in.Ca / mw('Ca')
        mg_mmol = df_in.Mg / mw('Mg')
        fe_mmol = df_in.Fe / mw('Fe')
        mn_mmol = df_in.Mn / mw('Mn')
        h_mmol = (10**-df_in.ph) / 1000  # ph -> mol/L -> mmol/L
        al_mmol = 1000. * df_in.Al / mw('Al')  # ug/L ->mg/L -> mmol/L

        # - Na, K, NH4
        # select rows that do not have Na, K or NH4 as dominant cation
        is_no_domcat_na_nh4_k = (na_mmol + k_mmol +
                                 nh4_mmol) < (s_sum_cations / 2)

        is_domcat_nh4 = ~is_no_domcat_na_nh4_k & (nh4_mmol >
                                                  (na_mmol + k_mmol))

        is_domcat_na = ~is_no_domcat_na_nh4_k & ~is_domcat_nh4 & (na_mmol >
                                                                  k_mmol)

        is_domcat_k = ~is_no_domcat_na_nh4_k & ~is_domcat_nh4 & ~is_domcat_na

        # abbreviation
        is_domcat_na_nh4_k = is_domcat_na | is_domcat_nh4 | is_domcat_k

        # - Ca, Mg
        is_domcat_ca_mg = (
            # not na or nh4 or k dominant
            ~is_domcat_na_nh4_k & (
                # should be any of Ca or Mg available
                ((ca_mmol > 0) | (mg_mmol > 0)) |
                # should be more of Ca or Mg then sum of H, Fe, Al, Mn
                # (compensated for charge)
                (2 * ca_mmol + 2 * mg_mmol <
                 h_mmol + 3 * al_mmol + 2 * fe_mmol + 2 * mn_mmol)))

        is_domcat_ca = is_domcat_ca_mg & (ca_mmol >= mg_mmol)
        is_domcat_mg = is_domcat_ca_mg & (ca_mmol < mg_mmol)

        # - H, Al, Fe, Mn
        # IF(IF(h_mmol+3*IF(al_mmol)>2*(fe_mol+mn_mol),IF(h_mmol>3*al_mmol,"H","Al"),IF(fe_mol>mn_mol,"Fe","Mn")))
        is_domcat_fe_mn_al_h = (
            # not na, nh4, k, ca or Mg dominant
            ~is_domcat_na_nh4_k & ~is_domcat_ca & ~is_domcat_mg & (
                # should be any of Fe, Mn, Al or H available
                (fe_mmol > 0) | (mn_mmol > 0) | (h_mmol > 0) |
                (al_mmol > 0)  # |
                # # should be more of Ca or Mg then sum of H, Fe, Al, Mn
                # # (compensated for charge)
                # (2*ca_mmol+2*mg_mmol < h_mmol+3*al_mmol+2*fe_mmol+2*mn_mmol)
            ))

        is_domcat_h_al = is_domcat_fe_mn_al_h & ((h_mmol + 3 * al_mmol) >
                                                 (2 * fe_mmol + 2 * mn_mmol))
        is_domcat_h = is_domcat_h_al & (h_mmol > al_mmol)
        is_domcat_al = is_domcat_h_al & (al_mmol > h_mmol)

        is_domcat_fe_mn = is_domcat_fe_mn_al_h & ~is_domcat_h_al
        is_domcat_fe = is_domcat_fe_mn & (fe_mmol > mn_mmol)
        is_domcat_mn = is_domcat_fe_mn & (mn_mmol > fe_mmol)

        sr_out = pd.Series(index=df_in.index, dtype='object')
        sr_out[:] = ""
        sr_out[is_domcat_nh4] = "NH4"
        sr_out[is_domcat_na] = "Na"
        sr_out[is_domcat_k] = "K"
        sr_out[is_domcat_ca] = 'Ca'
        sr_out[is_domcat_mg] = 'Mg'
        sr_out[is_domcat_fe] = 'Fe'
        sr_out[is_domcat_mn] = 'Mn'
        sr_out[is_domcat_al] = 'Al'
        sr_out[is_domcat_h] = 'H'

        return sr_out
示例#5
0
    def get_stuyfzand_water_type(self, inplace=True):
        """
        Get Stuyfzand water type. This water type classification contains
        5 components: Salinity, Alkalinity, Dominant Cation, Dominant Anion and Base Exchange Index.
        This results in a classification such as for example 'F3CaMix+'.

        It is assumed that only HCO<sub>3</sub><sup>-</sup> contributes to
        the alkalinity.

        Returns
        -------
        pandas.Series
            Series with Stuyfzand water type of each row in original SamplesFrame.
        """
        if not self.is_valid:
            raise ValueError(
                "Method can only be used on validated HGC frames, use 'make_valid' to validate"
            )

        # Create input dataframe containing all required columns
        # Inherit column values from HGC frame, assume 0 if column
        # is not present
        cols_req = ('Al', 'Ba', 'Br', 'Ca', 'Cl', 'Co', 'Cu', 'doc', 'F', 'Fe',
                    'alkalinity', 'K', 'Li', 'Mg', 'Mn', 'Na', 'Ni', 'NH4',
                    'NO2', 'NO3', 'Pb', 'PO4', 'ph', 'SO4', 'Sr', 'Zn')
        df_in = self._make_input_df(cols_req)
        df_out = pd.DataFrame(index=df_in.index)

        # Salinity
        df_out['swt_s'] = 'G'
        df_out.loc[df_in['Cl'] > 5, 'swt_s'] = 'g'
        df_out.loc[df_in['Cl'] > 30, 'swt_s'] = 'F'
        df_out.loc[df_in['Cl'] > 150, 'swt_s'] = 'f'
        df_out.loc[df_in['Cl'] > 300, 'swt_s'] = 'B'
        df_out.loc[df_in['Cl'] > 1000, 'swt_s'] = 'b'
        df_out.loc[df_in['Cl'] > 10000, 'swt_s'] = 'S'
        df_out.loc[df_in['Cl'] > 20000, 'swt_s'] = 'H'

        #Alkalinity
        df_out['swt_a'] = '*'
        df_out.loc[df_in['alkalinity'] > 31, 'swt_a'] = '0'
        df_out.loc[df_in['alkalinity'] > 61, 'swt_a'] = '1'
        df_out.loc[df_in['alkalinity'] > 122, 'swt_a'] = '2'
        df_out.loc[df_in['alkalinity'] > 244, 'swt_a'] = '3'
        df_out.loc[df_in['alkalinity'] > 488, 'swt_a'] = '4'
        df_out.loc[df_in['alkalinity'] > 976, 'swt_a'] = '5'
        df_out.loc[df_in['alkalinity'] > 1953, 'swt_a'] = '6'
        df_out.loc[df_in['alkalinity'] > 3905, 'swt_a'] = '7'

        #Dominant cation
        s_sum_cations = self.get_sum_cations(inplace=False)

        df_out['swt_domcat'] = self._get_dominant_anions_of_df(df_in)

        # Dominant anion
        s_sum_anions = self.get_sum_anions(inplace=False)
        cl_mmol = df_in.Cl / mw('Cl')
        hco3_mmol = df_in.alkalinity / (mw('H') + mw('C') + 3 * mw('O'))
        no3_mmol = df_in.NO3 / (mw('N') + 3 * mw('O'))
        so4_mmol = df_in.SO4 / (mw('S') + 4 * mw('O'))

        # TODO: consider renaming doman to dom_an or dom_anion
        is_doman_cl = (cl_mmol > s_sum_anions / 2)
        df_out.loc[is_doman_cl, 'swt_doman'] = "Cl"

        is_doman_hco3 = ~is_doman_cl & (hco3_mmol > s_sum_anions / 2)
        df_out.loc[is_doman_hco3, 'swt_doman'] = "HCO3"

        is_doman_so4_or_no3 = ~is_doman_cl & ~is_doman_hco3 & (
            2 * so4_mmol + no3_mmol > s_sum_anions / 2)
        is_doman_so4 = (2 * so4_mmol > no3_mmol)
        df_out.loc[is_doman_so4_or_no3 & is_doman_so4, 'swt_doman'] = "SO4"
        df_out.loc[is_doman_so4_or_no3 & ~is_doman_so4, 'swt_doman'] = "NO3"

        is_mix = ~is_doman_cl & ~is_doman_hco3 & ~is_doman_so4_or_no3
        df_out.loc[is_mix, 'swt_doman'] = "Mix"

        # Base Exchange Index
        s_bex = self.get_bex(inplace=False)
        threshold1 = 0.5 + 0.02 * cl_mmol
        threshold2 = -0.5 - 0.02 * cl_mmol
        is_plus = (s_bex > threshold1) & (s_bex > 1.5 *
                                          (s_sum_cations - s_sum_anions))

        is_minus = ~is_plus & (s_bex < threshold2) & (
            s_bex < 1.5 * (s_sum_cations - s_sum_anions))

        is_neutral = (~is_plus & ~is_minus & (s_bex > threshold2) &
                      (s_bex < threshold1) &
                      ((s_sum_cations == s_sum_anions) |
                       ((abs(s_bex + threshold1 *
                             (s_sum_cations - s_sum_anions)) /
                         abs(s_sum_cations - s_sum_anions)) > abs(
                             1.5 * (s_sum_cations - s_sum_anions)))))

        is_none = ~is_plus & ~is_minus & ~is_neutral

        df_out.loc[is_plus, 'swt_bex'] = '+'
        df_out.loc[is_minus, 'swt_bex'] = '-'
        df_out.loc[is_neutral, 'swt_bex'] = 'o'
        df_out.loc[is_none, 'swt_bex'] = ''

        #Putting it all together
        df_out['swt'] = df_out['swt_s'].str.cat(
            df_out[['swt_a', 'swt_domcat', 'swt_doman', 'swt_bex']])

        if inplace:
            self._obj['water_type'] = df_out['swt']
        else:
            return df_out['swt']