Skip to content

DSLangmuir fitting problems #3

@richardjgowers

Description

@richardjgowers

The fitting method for the dual-site Langmuir isotherm doesn't seem to work very well. I've included an example of data where it fails.

df = pd.read_csv('./data.csv', sep=' ')

T1 = 293.15
T2 = 313.15
T3 = 333.15
T4 = 363.15

df.columns = pd.MultiIndex.from_tuples(list(itertools.product([T1, T2, T3, T4], ['P', 'N'])))

m = pyiast.ModelIsotherm(df[T1].dropna(), loading_key='N', pressure_key='P', model='DSLangmuir')
m.print_params()

DSLangmuir identified model parameters:
	M1 = 0.271962
	K1 = -0.014806
	M2 = 0.189937
	K2 = -0.021286
RMSE =  2.75210883186

data.csv.tar.gz

I think this is just because the model is hard to fit to, even using a simple scipy.curve_fit seems to give "bad" answers even with a relatively accurate initial guess, eg: (here changing one parameter guess from 0.01 to 0.01 makes the optimisation snap to the correct values)

from scipy.optimize import curve_fit

def dualsite(P, q1, b1, q2, b2):
    return q1 * b1 * P / (1 + b1 * P) + q2 * b2 * P / (1 + b2 * P)

popt, pcov = curve_fit(dualsite, df[T1]['P'].values, df[T1]['N'].values, p0=[5, 0.01, 5, 0.01])

q1: 0.3308547822386095
b1: -3453538.574244023
q2: 7.386229859865762
b2: 0.003215815376835452

popt, pcov = curve_fit(dualsite, df[T1]['P'].values, df[T1]['N'].values, p0=[5, 0.001, 5, 0.01])
q1: 7.890734382768765
b1: 0.0016454550275279897
q2: 1.468241084209643
b2: 0.024404438085929743

I've had some luck in making lmfit fit stuff for me, it implements boundaries on parameters, which seem to work even with quite coarse estimates for initial values (see below again).

import lmfit

def dualsite_lmfit(params, x, data):
    q1 = params['q1']
    q2 = params['q2']
    b1 = params['b1']
    b2 = params['b2']
    
    model = dualsite(x, q1, b1, q2, b2)
    
    return model - data

params = lmfit.Parameters()

params.add('q1', value=5.0, min=0.0, max=20)
params.add('q2', value=5.0, min=0.0, max=20)
params.add('b1', value=1, min=0.0)
params.add('b2', value=1, min=0.0)

minner = lmfit.Minimizer(dualsite_lmfit, params, fcn_args=(x, y))

result = minner.minimize()

lmfit.report_fit(result)

[[Fit Statistics]]
    # function evals   = 152
    # data points      = 29
    # variables        = 4
    chi-square         = 0.028
    reduced chi-square = 0.001
    Akaike info crit   = -193.707
    Bayesian info crit = -188.238
[[Variables]]
    q1:   1.46819959 +/- 0.285037 (19.41%) (init= 5)
    q2:   7.89071518 +/- 0.250531 (3.18%) (init= 5)
    b1:   0.02440523 +/- 0.005533 (22.67%) (init= 1)
    b2:   0.00164549 +/- 0.000264 (16.03%) (init= 1)
[[Correlations]] (unreported correlations are <  0.100)
    C(q1, b1)                    = -0.980 
    C(q1, b2)                    = -0.957 
    C(b1, b2)                    =  0.893 
    C(q2, b2)                    = -0.759 
    C(q1, q2)                    =  0.542 
    C(q2, b1)                    = -0.408 

I could implement this to the package to improve LangmuirDS (and other models too?) if there's interest?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions