Skip to content

Critical-state-line calibration utility

The CSL calibration helper is available as

from ACT.utilities.csl import fit_csl

It is a standalone utility. You can run it before a full ACT optimization to obtain initial or fixed critical-state-line parameters for hypoplastic or SANISAND-family models.

What the function does

fit_csl(...) fits a critical-state-line model, returns the fitted parameters in a dictionary, prints the result to the command prompt, plots the measured/generated data against the fitted line, saves the figure in the selected result directory and writes a small csl_fit_*.dat file that is automatically inserted into the ACT PDF report.


Supported CSL equations

Bauer CSL

Use model='bauer' for the Bauer CSL used by the hypoplastic family:

\[ e_c(p) = e_{c0}\exp\left[-\left(\frac{3p}{h_s}\right)^n\right] \]

In the ACT Python interface, \(p\) is provided in kPa and the fitted hs value is returned in GPa, consistent with the hypoplastic ACT model classes.

The returned parameters are:

result['parameters']['ec0']
result['parameters']['hs']
result['parameters']['n']

Li-Wang CSL

Use model='li-wang' for the Li-Wang CSL used by the SANISAND-family models:

\[ e(p) = e_0 - \lambda_c \left(\frac{p}{p_{atm}}\right)^\xi \]

The returned parameters are:

result['parameters']['e0']
result['parameters']['lambdac']
result['parameters']['xi']

Input mode 1: direct \(p\)-\(e\) data

Use this mode when your data already contain mean effective stress \(p\) and void ratio \(e\).

from ACT.utilities.csl import fit_csl

result = fit_csl(
    model='bauer',
    p=[25., 50., 100., 200., 400.],
    e=[0.91, 0.88, 0.85, 0.82, 0.79],
    result_dir=model.out_dir,
)

params = result['parameters']

ec0 = params['ec0']
hs  = params['hs']
n   = params['n']

print(ec0, hs, n)
print(result['r2'])

Do not index result['parameters'] like a list

result['parameters'] is a dictionary. Therefore this is wrong:

ec0 = result['parameters'][0]

Use the parameter names instead:

ec0 = result['parameters']['ec0']
hs  = result['parameters']['hs']
n   = result['parameters']['n']

Input mode 2: oedometric compression data

Use this mode when your input data are vertical effective stress, \(K_0\) and void ratio, or vertical effective stress, \(K_0\), axial strain and initial void ratio. ACT converts the vertical stress to mean effective stress by

\[ p = \sigma_v\frac{1+2K_0}{3} \]

K0 can be a scalar or an array with one value per stress point.

import numpy as np
from ACT.utilities.csl import fit_csl

phic = 33.0
K0 = 1. - np.sin(phic*np.pi/180.)

result = fit_csl(
    model='bauer',
    vertical_stress=[25., 50., 100., 200., 400.],
    K0=K0,
    void_ratio=[0.91, 0.88, 0.85, 0.82, 0.79],
    result_dir=model.out_dir,
)

params = result['parameters']

ec0 = params['ec0']
hs  = params['hs']
n   = params['n']

Oedometer data from stress-strain records

The ACT Excel database stores oedometric compression tests as vertical stress and axial strain. You can therefore pass the measured strain directly. e_initial may be a scalar or an array with one initial void ratio per strain value. ACT converts the strain to void ratio by

\[ e = e_0 - (1+e_0)\varepsilon_{ax} \]

for the usual ACT sign convention where compressive oedometric strain is positive.

import numpy as np
from ACT.utilities.csl import fit_csl

phic = 37.0
K0 = 1. - np.sin(phic*np.pi/180.)

result = fit_csl(
    model='bauer',
    vertical_stress=sigma_v,       # measured oedometric stress in kPa
    K0=K0,
    strain=eps_ax,                # measured oedometric strain dh/h0
    e_initial=0.359,              # initial void ratio from the OED sheet
    result_dir=model.out_dir,
)

params = result['parameters']

model.ec0 = params['ec0']
model.hs  = params['hs']
model.n   = params['n']

strain and axial_strain are synonyms. e_initial and initial_void_ratio are synonyms.

Loading-unloading-reloading oedometer data

Loading-unloading-reloading curves should normally not be fitted point-by-point for CSL/compression-law calibration. At the same stress level, unloading and reloading points represent a different stress history and a different reversible/irreversible strain split. Including all points would over-weight unloading loops, hold phases and repeated stress levels.

For oedometric input, the default evaluation='auto' detects a non-monotonic stress path and fits the virgin loading envelope instead. Internally ACT keeps points close to the historical maximum stress path and then retains the lowest void ratio in logarithmic stress bins. This gives a monotonic compression envelope suitable for Bauer/Li-Wang CSL fitting.

result = fit_csl(
    model='bauer',
    vertical_stress=sigma_v,
    K0=K0,
    strain=eps_ax,
    e_initial=e0,
    evaluation='auto',          # default; detects LUR and uses the envelope
    envelope_bins=50,           # number of logarithmic stress bins for the envelope
    stress_tolerance=0.005,     # relative tolerance for recognising the max-stress path
    result_dir=model.out_dir,
)

If you deliberately want to fit every single point, including unloading and reloading branches, set

result = fit_csl(
    model='bauer',
    vertical_stress=sigma_v,
    K0=K0,
    strain=eps_ax,
    e_initial=e0,
    evaluation='all',
    result_dir=model.out_dir,
)

This is usually not recommended for CSL/compression-law calibration from LUR tests.

vertical_stress and sigma_v are synonyms. The following call is equivalent:

result = fit_csl(
    model='bauer',
    sigma_v=[25., 50., 100., 200., 400.],
    K0=K0,
    void_ratio=[0.91, 0.88, 0.85, 0.82, 0.79],
    result_dir=model.out_dir,
)

Directly from collected ACT oedometer tests

When the experimental database has already been collected, you can also pass the ACT oedometer test objects directly. This is useful when several oedometer tests with different initial void ratios should be evaluated together.

import numpy as np
from ACT.utilities.csl import fit_csl_oedometer_tests

phic = 37.0
K0 = 1. - np.sin(phic*np.pi/180.)

result = fit_csl_oedometer_tests(
    db.oedometer,              # list of ACT oedometer test objects
    model='bauer',
    K0=K0,
    evaluation='auto',
    envelope_bins=50,
    result_dir=model.out_dir,
)

params = result['parameters']
model.ec0 = params['ec0']
model.hs  = params['hs']
model.n   = params['n']

The wrapper concatenates test.stress and test.strain and uses the first value of test.void as the initial void ratio for each test. The actual fitting is still performed by fit_csl(...).


Input mode 3: Bolton-generated CSL points

Use this mode when no measured CSL points are available and you want to generate a first estimate from \(e_{\max}\), \(e_{\min}\), \(Q\) and \(R\):

\[ e_c = e_{\max} - \frac{R}{Q-\ln(p)}\left(e_{\max}-e_{\min}\right) \]
from ACT.utilities.csl import fit_csl

result = fit_csl(
    model='bauer',
    emax=0.977,
    emin=0.605,
    Q=9.15,
    R=0.77,
    p_min=10.,
    p_max=1000.,
    n_points=30,
    result_dir=model.out_dir,
)

params = result['parameters']

ec0 = params['ec0']
hs  = params['hs']
n   = params['n']

You can also provide your own pressure vector:

result = fit_csl(
    model='bauer',
    p=[25., 50., 100., 200., 400.],
    emax=0.977,
    emin=0.605,
    Q=9.15,
    R=0.77,
    result_dir=model.out_dir,
)

Example: Li-Wang

For SANISAND-family models, use model='li-wang' and read the Li-Wang parameter names:

from ACT.utilities.csl import fit_csl

result = fit_csl(
    model='li-wang',
    p=[25., 50., 100., 200., 400.],
    e=[0.91, 0.88, 0.85, 0.82, 0.79],
    result_dir=model.out_dir,
)

params = result['parameters']

e0       = params['e0']
lambdac  = params['lambdac']
xi       = params['xi']

print(e0, lambdac, xi)
print(result['r2'])

Updating an ACT model object directly

If the ACT model object has matching parameter names, fit_csl(...) can write the fitted parameters directly to the model instance:

from ACT.models import hypoplasticity
from ACT.utilities.csl import fit_csl

model = hypoplasticity()

result = fit_csl(
    model='bauer',
    p=[25., 50., 100., 200., 400.],
    e=[0.91, 0.88, 0.85, 0.82, 0.79],
    result_dir=model.out_dir,
    update_model=model,
)

print(model.ec0)
print(model.hs)
print(model.n)

For Li-Wang fits, lambdac is also written to lambda_c if the target model uses that SANISAND naming convention.


Returned dictionary

The returned object has the following structure:

{
    'type': 'csl_fit',
    'created': '...',
    'model': 'Bauer',
    'input_source': '...',
    'n_points': 5,
    'raw_points': 5,
    'evaluation': 'all data points',
    'parameters': {'ec0': ..., 'hs': ..., 'n': ...},
    'r2': ...,
    'patm': 100.0,
    'p': array([...]),
    'e_exp': array([...]),
    'e_fit': array([...]),
    'p_raw': array([...]),
    'e_raw': array([...]),
    'figure': './results/csl_fit_bauer.png',
    'report_file': './results/csl_fit_bauer_....dat',
}

The most commonly used entries are:

Entry Meaning
result['parameters'] dictionary with fitted CSL parameters
result['r2'] coefficient of determination of the CSL fit
result['evaluation'] evaluation mode used for the fitted data, for example all data points or virgin loading envelope
result['n_points'] number of selected points used for the fit
result['raw_points'] number of raw input points before envelope selection
result['p'] stress points actually used for the fit
result['e_exp'] input or generated void-ratio values
result['e_fit'] fitted void-ratio values at the same stress points
result['p_raw'] raw stress points before envelope selection
result['e_raw'] raw void-ratio values before envelope selection
result['figure'] saved PNG figure
result['report_file'] plain-text ACT report-data file

Report integration

Each call creates these files in result_dir:

csl_fit_*.png
csl_fit_*.pdf
csl_fit_*.dat

The .dat file is a plain ACT key-value text file. During report generation, ACT.utilities.reporting.write_pdf_report(...) searches the result directory for csl_fit_*.dat files and inserts one Critical-state-line calibration page per fit into the PDF report. The page contains the settings, the number of raw and selected data points, the \(R^2\) value, the fitted parameters and the CSL figure. The CSL figure is not repeated again in the general result-figure section.