Package structure

pySigmaP is written under the object-oriented paradigm and divided into six modules. One of them contains the class to load and manage the data of the compressibility curve, and the other five contain the classes to determine the preconsolidation pressure via nine different methods.

Each module is documented and basic examples are illustrated.

pysigmap.data

data.py module.

Contains the class and its methods for loading and managing the data of the compressibility curve.

Data

Data class.

When the object is instanced, the compression and recompression indices are automatically calculated with the default inputs of the methods. See the documentation of each method for more information regarding the default inputs.

Attributes:
  • rawData (pandas DataFrame) –

    Data from the incremental loading oedometer test. It includes three series with the following and strict order: effective vertical stress, axial strain and void ratio. The first row must include the on-table void ratio of the specimen.

  • sigmaV (float) –

    In situ effective vertical stress of the specimen.

  • strainPercent ((bool, optional)) –

    Boolean to specify if the axial strain of the row data is in percent or not. The default is True.

  • reloading ((bool, optional)) –

    Boolean to specify if the test included a reloading stage. The default is True.

  • secondUnloading ((bool, optional)) –

    Boolean to specify if the test included two reloading stages. The default is True.

  • use_this_UR_stage ((int, optional)) –

    Index to specify which unloading-reloading stage will be used to compute the recompression index when the test data includes more than one stage. Index notation as Python's standard (first stage → 0). The default is 0.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> urlXLSX = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                    'data4testing/main/pysigmap/testData.xlsx'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> # or
>>> data = Data(pd.read_excel(urlXLSX), sigmaV=75)
>>> data.plot()
>>> data.idxCc, data.idxCr
(0.23829647651319996, 0.04873212611656529)
>>> data.compressionIdx(range2fitCc=(1000, 8000))
>>> data.plot()
>>> data.idxCc
0.22754962258271252
>>> data.recompressionIdx(opt=2)
>>> data.plot()
>>> data.idxCr
0.049481704199255676
>>> data.recompressionIdx(opt=3)
>>> data.plot()
>>> data.idxCr
0.05398842524225007
Source code in pysigmap/data.py
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
class Data:
    """``Data`` class.

    When the object is instanced, the compression and recompression indices
    are automatically calculated with the default inputs of the methods.
    See the documentation of each method for more information regarding the
    default inputs.

    Attributes
    ----------
    rawData : pandas DataFrame
        Data from the incremental loading oedometer test. It includes three
        series with the following and strict order: effective vertical stress,
        axial strain and void ratio. The first row must include the on-table
        void ratio of the specimen.
    sigmaV : float
        In situ effective vertical stress of the specimen.
    strainPercent : bool, optional
        Boolean to specify if the axial strain of the row data is in percent or
        not. The default is True.
    reloading : bool, optional
        Boolean to specify if the test included a reloading stage. The default
        is True.
    secondUnloading : bool, optional
        Boolean to specify if the test included two reloading stages. The
        default is True.
    use_this_UR_stage : int, optional
        Index to specify which unloading-reloading stage will be used to
        compute the recompression index when the test data includes more than
        one stage.  Index notation as Python's standard (first stage → 0). The
        default is 0.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> urlXLSX = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                    'data4testing/main/pysigmap/testData.xlsx'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> # or
    >>> data = Data(pd.read_excel(urlXLSX), sigmaV=75)
    >>> data.plot()
    >>> data.idxCc, data.idxCr
    (0.23829647651319996, 0.04873212611656529)
    >>> data.compressionIdx(range2fitCc=(1000, 8000))
    >>> data.plot()
    >>> data.idxCc
    0.22754962258271252
    >>> data.recompressionIdx(opt=2)
    >>> data.plot()
    >>> data.idxCr
    0.049481704199255676
    >>> data.recompressionIdx(opt=3)
    >>> data.plot()
    >>> data.idxCr
    0.05398842524225007
    """

    def __init__(
        self,
        rawData,
        sigmaV,
        strainPercent=True,
        reloading=True,
        secondUnloading=True,
        use_this_UR_stage=0,
    ):
        """Initialize the class."""
        self.raw = rawData
        self.sigmaV = sigmaV
        self.strainPercent = strainPercent
        self.reloading = reloading
        self.secondUnloading = secondUnloading
        self.use_this_UR_stage = use_this_UR_stage
        self.preprocessing()
        self.getBreakIndices()
        self.clean()
        self.idxCc = None
        self.idxCr = None
        # self.compressionIdx()
        # self.recompressionIdx()
        return

    def preprocessing(self):
        """
        Rename the series names and create the on-table void ratio attribute.

        Returns
        -------
        None.
        """
        # Standardizing series names
        self.raw.columns = ["stress", "strain", "e"]
        # Removing percentage format to strain values
        if self.strainPercent:
            self.raw["strain"] = self.raw["strain"].divide(100)
        # On-table (initial) void ratio
        self.e_0 = self.raw["e"].iloc[0]
        return

    def getBreakIndices(self):
        """
        Find the break indices of the compressibility curve.

        The break indices are the following:

            - brkIdx1: Start of the first unloading stage
            - brkIdx2: End of the first unloading stage
            - brkIdx3: Point on the compression range after the first reloading
            - brkIdx4: Index of the last point on the compression range

        Returns
        -------
        None.
        """
        idx_unloading_init = [  # brkIdx1: Start of the first unloading
            i
            for i in self.raw.index[1:-1]
            if (
                self.raw["stress"][i] > self.raw["stress"][i - 1]
                and self.raw["stress"][i] > self.raw["stress"][i + 1]
            )
        ]
        if self.secondUnloading:
            idx_unloading_init.pop(-1)
        self.idx_unloading_init = idx_unloading_init
        brkIdx1 = idx_unloading_init[self.use_this_UR_stage]

        if self.reloading:  # Cases 2-3: Reloading and second unloading
            idx_reloading_init = [  # brkIdx2: End of the first unloading
                i
                for i in self.raw.index[1:-1]
                if (
                    self.raw["stress"][i] < self.raw["stress"][i - 1]
                    and self.raw["stress"][i] < self.raw["stress"][i + 1]
                )
            ]
            brkIdx2 = idx_reloading_init[self.use_this_UR_stage]
            self.idx_reloading_init = idx_reloading_init

            # brkIdx3: Points on the NCL after the each reloading
            idx_reloading_ncl = [
                self.raw.query(f"stress == stress[{i}]").index[1]
                for i in idx_unloading_init
            ]
            brkIdx3 = idx_reloading_ncl[self.use_this_UR_stage]
            self.idx_reloading_ncl = idx_reloading_ncl
        else:  # Case 1: No reloading - No second unloading
            brkIdx2 = self.raw.index[-1]  # last point of the unluading
            brkIdx3 = None
        # brkIdx4: index of the last point on the NCL
        brkIdx4 = self.raw.query("stress == stress.max()").index[0]

        self.brkIdx1 = brkIdx1
        self.brkIdx2 = brkIdx2
        self.brkIdx3 = brkIdx3
        self.brkIdx4 = brkIdx4
        return

    def clean(self):
        """
        Clean the raw data removing the unloading and reloading stages.

        Returns
        -------
        None.
        """
        if self.reloading:
            idx_init = [-1] + self.idx_reloading_ncl
            idx_end = self.idx_unloading_init + [self.brkIdx4]
            self.cleaned = pd.concat(
                [
                    self.raw[idx_init[i] + 1 : idx_end[i] + 1]
                    for i in range(len(idx_init))
                ]
            )
        else:
            self.cleaned = self.raw[: self.brkIdx1 + 1]
        self.cleaned.reset_index(drop=True, inplace=True)
        sigmaLog = np.log10(self.cleaned["stress"][1:])
        # cs = CubicSpline(x=sigmaLog, y=self.cleaned["e"][1:])
        # self.eSigmaV = float(cs(np.log10(self.sigmaV)))
        interpolator = interp1d(x=sigmaLog, y=self.cleaned["e"][1:])
        self.eSigmaV = interpolator(np.log10(self.sigmaV))
        return

    def findStressIdx(self, stress2find, cleanedData=True):
        """
        Return the ceiling index of a specified stress.

        Parameters
        ----------
        stress2find : float
            Stress whose ceiling index is wanted.
        cleanedData : bool, optional
            Boolean to indicate if the index must be find in the row or cleaned
            data. The default is True.

        Returns
        -------
        idx : int
            The ceiling index of the stress input.
        """
        if stress2find == 0:
            return 1
        elif stress2find > self.raw["stress"].max():
            return None
        else:
            data4finding = self.cleaned if cleanedData else self.raw
            return data4finding.query(f"stress >= {stress2find}").index[0]

    def compressionIdx(self, range2fitCc=None):
        """
        Calculate the compression index (Cc).

        Parameters
        ----------
        range2fitCc : list, tuple or array (length=2), optional
            Initial and final pressures between which the first-order
            polynomial will be fit to the data on the compression range. If
            None, the Cc index will be calculated as the
            steepest slope of the cubic spline that passes through the data.
            The default is None.

        Returns
        -------
        None.
        """
        self.range2fitCc = range2fitCc
        if range2fitCc is None:  # Using a cubic spline
            sigmaLog = np.log10(self.cleaned["stress"][1:])
            cs = CubicSpline(x=sigmaLog, y=self.cleaned["e"][1:])
            sigmaCS = np.linspace(sigmaLog.iloc[0], sigmaLog.iloc[-1], 500)
            steepestSlopeIdx = np.argmin(cs(sigmaCS, 1))
            idxCc = cs(sigmaCS, 1)[steepestSlopeIdx]
            idxCcInt = (
                cs(sigmaCS[steepestSlopeIdx])
                - idxCc * sigmaCS[steepestSlopeIdx]
            )
            self.fitCc = False
        else:  # Using a linear fit of a first-order polynomial
            idxInitCc = self.findStressIdx(
                stress2find=range2fitCc[0], cleanedData=True
            )
            idxEndCc = self.findStressIdx(
                stress2find=range2fitCc[1], cleanedData=True
            )
            maskCc = np.full(len(self.cleaned), False)
            maskCc[idxInitCc:idxEndCc] = True
            self.maskCc = maskCc
            # -- Linear regresion
            sigmaCc = self.cleaned["stress"].iloc[maskCc]
            sigmaCclog = np.log10(sigmaCc)
            eCc = self.cleaned["e"].iloc[maskCc]
            idxCcInt, idxCc = polyfit(sigmaCclog, eCc, deg=1)
            r2Cc = r2_score(
                y_true=eCc, y_pred=polyval(sigmaCclog, [idxCcInt, idxCc])
            )
            self.r2Cc = r2Cc
            self.fitCc = True
        self.idxCc = abs(idxCc)
        self.idxCcInt = abs(idxCcInt)
        return

    def recompressionIdx(self, opt=1):
        """
        Calculate the recompression index (Cr).

        Parameters
        ----------
        opt : TYPE, optional
            Integer value to indicate which method will be used. Using only
            two points, the start and end of the unluading stage (opt=1); using
            all the points of the first unloading stage (opt=2); using the
            points of the first unloading and reloading stages (opt=3). The
            default is 1.

        Returns
        -------
        None.
        """
        maskCr = np.full(len(self.raw), False)
        if opt == 1:
            maskCr[self.brkIdx1] = True
            maskCr[self.brkIdx2] = True
        elif opt == 2:
            maskCr[self.brkIdx1 : self.brkIdx2 + 1] = True
        elif opt == 3:
            maskCr[self.brkIdx1 : self.brkIdx3 + 1] = True
        # -- Linear regresion
        sigmaCr = self.raw["stress"].iloc[maskCr]
        sigmaCrlog = np.log10(sigmaCr)
        eCr = self.raw["e"].iloc[maskCr]
        idxCrInt, idxCr = polyfit(sigmaCrlog, eCr, deg=1)
        r2Cr = r2_score(
            y_true=eCr, y_pred=polyval(sigmaCrlog, [idxCrInt, idxCr])
        )
        self.maskCr = maskCr
        self.r2Cr = r2Cr
        self.idxCr = abs(idxCr)
        self.idxCrInt = idxCrInt
        return

    def plot(self):
        """
        Plot the compressibility curve, Cc and Cr indices.

        Returns
        -------
        fig : matplotlib.figure.Figure
            Figure that includes the compressibility curve, Cc, Cr and the in
            situ effective vertical stress of the specimen.
        """
        # -- plotting
        fig = plt.figure(figsize=figsize)
        ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        ax.plot(
            self.raw["stress"][1:],
            self.raw["e"][1:],
            ls=(0, (1, 1)),
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Experimental data",
        )
        ax.plot(
            self.sigmaV,
            self.eSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                ["$\sigma^\prime_\mathrm{v_0}=$ ", f"{self.sigmaV:.0f} kPa"]
            ),
        )
        # Compression index
        if self.idxCc is not None:
            x4Cc = np.linspace(
                self.cleaned["stress"].iloc[-2],
                self.cleaned["stress"].iloc[-1],
            )
            y4Cc = -self.idxCc * np.log10(x4Cc) + self.idxCcInt
            ax.axline(
                (x4Cc[0], y4Cc[0]),
                (x4Cc[-1], y4Cc[-1]),
                ls="-",
                lw=1.125,
                color=colors[1],
                label=str().join(["$C_\mathrm{c}=$", f"{self.idxCc:.3f}"]),
            )
            if self.fitCc:
                ax.plot(
                    self.cleaned["stress"].iloc[self.maskCc],
                    self.cleaned["e"].iloc[self.maskCc],
                    ls="",
                    marker="x",
                    color=colors[1],
                    label=f"Data for linear fit\n(R$^2={self.r2Cc:.3f}$)",
                )
        # Recompression index
        if self.idxCr is not None:
            x4Cr = np.linspace(
                self.raw["stress"].iloc[self.maskCr].min(),
                self.raw["stress"].iloc[self.maskCr].max(),
            )
            y4Cr = -self.idxCr * np.log10(x4Cr) + self.idxCrInt
            ax.plot(
                x4Cr,
                y4Cr,
                ls="-",
                lw=1.125,
                color=colors[2],
                label=str().join(["$C_\mathrm{r}=$", f"{self.idxCr:.3f}"]),
            )
            ax.plot(
                self.raw["stress"].iloc[self.maskCr],
                self.raw["e"].iloc[self.maskCr],
                ls="",
                marker="+",
                color=colors[2],
                label=f"Data for linear fit\n(R$^2={self.r2Cr:.3f}$)",
            )
        # other details
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.set(
            xscale="log",
            ylabel="Void ratio, $e$",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
        ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.grid(False)
        ax.legend(
            bbox_to_anchor=(1.125, 0.5),
            loc=6,
            title="$\\bf{Compressibility\ curve}$",
        )
        return fig

__init__

__init__(rawData, sigmaV, strainPercent=True, reloading=True, secondUnloading=True, use_this_UR_stage=0)

Initialize the class.

pysigmap/data.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def __init__(
    self,
    rawData,
    sigmaV,
    strainPercent=True,
    reloading=True,
    secondUnloading=True,
    use_this_UR_stage=0,
):
    """Initialize the class."""
    self.raw = rawData
    self.sigmaV = sigmaV
    self.strainPercent = strainPercent
    self.reloading = reloading
    self.secondUnloading = secondUnloading
    self.use_this_UR_stage = use_this_UR_stage
    self.preprocessing()
    self.getBreakIndices()
    self.clean()
    self.idxCc = None
    self.idxCr = None
    # self.compressionIdx()
    # self.recompressionIdx()
    return

clean

clean()

Clean the raw data removing the unloading and reloading stages.

Returns:
  • None.
pysigmap/data.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
def clean(self):
    """
    Clean the raw data removing the unloading and reloading stages.

    Returns
    -------
    None.
    """
    if self.reloading:
        idx_init = [-1] + self.idx_reloading_ncl
        idx_end = self.idx_unloading_init + [self.brkIdx4]
        self.cleaned = pd.concat(
            [
                self.raw[idx_init[i] + 1 : idx_end[i] + 1]
                for i in range(len(idx_init))
            ]
        )
    else:
        self.cleaned = self.raw[: self.brkIdx1 + 1]
    self.cleaned.reset_index(drop=True, inplace=True)
    sigmaLog = np.log10(self.cleaned["stress"][1:])
    # cs = CubicSpline(x=sigmaLog, y=self.cleaned["e"][1:])
    # self.eSigmaV = float(cs(np.log10(self.sigmaV)))
    interpolator = interp1d(x=sigmaLog, y=self.cleaned["e"][1:])
    self.eSigmaV = interpolator(np.log10(self.sigmaV))
    return

compressionIdx

compressionIdx(range2fitCc=None)

Calculate the compression index (Cc).

Parameters:
  • range2fitCc ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first-order polynomial will be fit to the data on the compression range. If None, the Cc index will be calculated as the steepest slope of the cubic spline that passes through the data. The default is None.

Returns:
  • None.
pysigmap/data.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
def compressionIdx(self, range2fitCc=None):
    """
    Calculate the compression index (Cc).

    Parameters
    ----------
    range2fitCc : list, tuple or array (length=2), optional
        Initial and final pressures between which the first-order
        polynomial will be fit to the data on the compression range. If
        None, the Cc index will be calculated as the
        steepest slope of the cubic spline that passes through the data.
        The default is None.

    Returns
    -------
    None.
    """
    self.range2fitCc = range2fitCc
    if range2fitCc is None:  # Using a cubic spline
        sigmaLog = np.log10(self.cleaned["stress"][1:])
        cs = CubicSpline(x=sigmaLog, y=self.cleaned["e"][1:])
        sigmaCS = np.linspace(sigmaLog.iloc[0], sigmaLog.iloc[-1], 500)
        steepestSlopeIdx = np.argmin(cs(sigmaCS, 1))
        idxCc = cs(sigmaCS, 1)[steepestSlopeIdx]
        idxCcInt = (
            cs(sigmaCS[steepestSlopeIdx])
            - idxCc * sigmaCS[steepestSlopeIdx]
        )
        self.fitCc = False
    else:  # Using a linear fit of a first-order polynomial
        idxInitCc = self.findStressIdx(
            stress2find=range2fitCc[0], cleanedData=True
        )
        idxEndCc = self.findStressIdx(
            stress2find=range2fitCc[1], cleanedData=True
        )
        maskCc = np.full(len(self.cleaned), False)
        maskCc[idxInitCc:idxEndCc] = True
        self.maskCc = maskCc
        # -- Linear regresion
        sigmaCc = self.cleaned["stress"].iloc[maskCc]
        sigmaCclog = np.log10(sigmaCc)
        eCc = self.cleaned["e"].iloc[maskCc]
        idxCcInt, idxCc = polyfit(sigmaCclog, eCc, deg=1)
        r2Cc = r2_score(
            y_true=eCc, y_pred=polyval(sigmaCclog, [idxCcInt, idxCc])
        )
        self.r2Cc = r2Cc
        self.fitCc = True
    self.idxCc = abs(idxCc)
    self.idxCcInt = abs(idxCcInt)
    return

findStressIdx

findStressIdx(stress2find, cleanedData=True)

Return the ceiling index of a specified stress.

Parameters:
  • stress2find (float) –

    Stress whose ceiling index is wanted.

  • cleanedData (bool, default: True ) –

    Boolean to indicate if the index must be find in the row or cleaned data. The default is True.

Returns:
  • idx( int ) –

    The ceiling index of the stress input.

pysigmap/data.py
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
def findStressIdx(self, stress2find, cleanedData=True):
    """
    Return the ceiling index of a specified stress.

    Parameters
    ----------
    stress2find : float
        Stress whose ceiling index is wanted.
    cleanedData : bool, optional
        Boolean to indicate if the index must be find in the row or cleaned
        data. The default is True.

    Returns
    -------
    idx : int
        The ceiling index of the stress input.
    """
    if stress2find == 0:
        return 1
    elif stress2find > self.raw["stress"].max():
        return None
    else:
        data4finding = self.cleaned if cleanedData else self.raw
        return data4finding.query(f"stress >= {stress2find}").index[0]

getBreakIndices

getBreakIndices()

Find the break indices of the compressibility curve.

The break indices are the following:

- brkIdx1: Start of the first unloading stage
- brkIdx2: End of the first unloading stage
- brkIdx3: Point on the compression range after the first reloading
- brkIdx4: Index of the last point on the compression range
Returns:
  • None.
pysigmap/data.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def getBreakIndices(self):
    """
    Find the break indices of the compressibility curve.

    The break indices are the following:

        - brkIdx1: Start of the first unloading stage
        - brkIdx2: End of the first unloading stage
        - brkIdx3: Point on the compression range after the first reloading
        - brkIdx4: Index of the last point on the compression range

    Returns
    -------
    None.
    """
    idx_unloading_init = [  # brkIdx1: Start of the first unloading
        i
        for i in self.raw.index[1:-1]
        if (
            self.raw["stress"][i] > self.raw["stress"][i - 1]
            and self.raw["stress"][i] > self.raw["stress"][i + 1]
        )
    ]
    if self.secondUnloading:
        idx_unloading_init.pop(-1)
    self.idx_unloading_init = idx_unloading_init
    brkIdx1 = idx_unloading_init[self.use_this_UR_stage]

    if self.reloading:  # Cases 2-3: Reloading and second unloading
        idx_reloading_init = [  # brkIdx2: End of the first unloading
            i
            for i in self.raw.index[1:-1]
            if (
                self.raw["stress"][i] < self.raw["stress"][i - 1]
                and self.raw["stress"][i] < self.raw["stress"][i + 1]
            )
        ]
        brkIdx2 = idx_reloading_init[self.use_this_UR_stage]
        self.idx_reloading_init = idx_reloading_init

        # brkIdx3: Points on the NCL after the each reloading
        idx_reloading_ncl = [
            self.raw.query(f"stress == stress[{i}]").index[1]
            for i in idx_unloading_init
        ]
        brkIdx3 = idx_reloading_ncl[self.use_this_UR_stage]
        self.idx_reloading_ncl = idx_reloading_ncl
    else:  # Case 1: No reloading - No second unloading
        brkIdx2 = self.raw.index[-1]  # last point of the unluading
        brkIdx3 = None
    # brkIdx4: index of the last point on the NCL
    brkIdx4 = self.raw.query("stress == stress.max()").index[0]

    self.brkIdx1 = brkIdx1
    self.brkIdx2 = brkIdx2
    self.brkIdx3 = brkIdx3
    self.brkIdx4 = brkIdx4
    return

plot

plot()

Plot the compressibility curve, Cc and Cr indices.

Returns:
  • fig( Figure ) –

    Figure that includes the compressibility curve, Cc, Cr and the in situ effective vertical stress of the specimen.

pysigmap/data.py
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
def plot(self):
    """
    Plot the compressibility curve, Cc and Cr indices.

    Returns
    -------
    fig : matplotlib.figure.Figure
        Figure that includes the compressibility curve, Cc, Cr and the in
        situ effective vertical stress of the specimen.
    """
    # -- plotting
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    ax.plot(
        self.raw["stress"][1:],
        self.raw["e"][1:],
        ls=(0, (1, 1)),
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Experimental data",
    )
    ax.plot(
        self.sigmaV,
        self.eSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            ["$\sigma^\prime_\mathrm{v_0}=$ ", f"{self.sigmaV:.0f} kPa"]
        ),
    )
    # Compression index
    if self.idxCc is not None:
        x4Cc = np.linspace(
            self.cleaned["stress"].iloc[-2],
            self.cleaned["stress"].iloc[-1],
        )
        y4Cc = -self.idxCc * np.log10(x4Cc) + self.idxCcInt
        ax.axline(
            (x4Cc[0], y4Cc[0]),
            (x4Cc[-1], y4Cc[-1]),
            ls="-",
            lw=1.125,
            color=colors[1],
            label=str().join(["$C_\mathrm{c}=$", f"{self.idxCc:.3f}"]),
        )
        if self.fitCc:
            ax.plot(
                self.cleaned["stress"].iloc[self.maskCc],
                self.cleaned["e"].iloc[self.maskCc],
                ls="",
                marker="x",
                color=colors[1],
                label=f"Data for linear fit\n(R$^2={self.r2Cc:.3f}$)",
            )
    # Recompression index
    if self.idxCr is not None:
        x4Cr = np.linspace(
            self.raw["stress"].iloc[self.maskCr].min(),
            self.raw["stress"].iloc[self.maskCr].max(),
        )
        y4Cr = -self.idxCr * np.log10(x4Cr) + self.idxCrInt
        ax.plot(
            x4Cr,
            y4Cr,
            ls="-",
            lw=1.125,
            color=colors[2],
            label=str().join(["$C_\mathrm{r}=$", f"{self.idxCr:.3f}"]),
        )
        ax.plot(
            self.raw["stress"].iloc[self.maskCr],
            self.raw["e"].iloc[self.maskCr],
            ls="",
            marker="+",
            color=colors[2],
            label=f"Data for linear fit\n(R$^2={self.r2Cr:.3f}$)",
        )
    # other details
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set(
        xscale="log",
        ylabel="Void ratio, $e$",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
    ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.grid(False)
    ax.legend(
        bbox_to_anchor=(1.125, 0.5),
        loc=6,
        title="$\\bf{Compressibility\ curve}$",
    )
    return fig

preprocessing

preprocessing()

Rename the series names and create the on-table void ratio attribute.

Returns:
  • None.
pysigmap/data.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def preprocessing(self):
    """
    Rename the series names and create the on-table void ratio attribute.

    Returns
    -------
    None.
    """
    # Standardizing series names
    self.raw.columns = ["stress", "strain", "e"]
    # Removing percentage format to strain values
    if self.strainPercent:
        self.raw["strain"] = self.raw["strain"].divide(100)
    # On-table (initial) void ratio
    self.e_0 = self.raw["e"].iloc[0]
    return

recompressionIdx

recompressionIdx(opt=1)

Calculate the recompression index (Cr).

Parameters:
  • opt (TYPE, default: 1 ) –

    Integer value to indicate which method will be used. Using only two points, the start and end of the unluading stage (opt=1); using all the points of the first unloading stage (opt=2); using the points of the first unloading and reloading stages (opt=3). The default is 1.

Returns:
  • None.
pysigmap/data.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
def recompressionIdx(self, opt=1):
    """
    Calculate the recompression index (Cr).

    Parameters
    ----------
    opt : TYPE, optional
        Integer value to indicate which method will be used. Using only
        two points, the start and end of the unluading stage (opt=1); using
        all the points of the first unloading stage (opt=2); using the
        points of the first unloading and reloading stages (opt=3). The
        default is 1.

    Returns
    -------
    None.
    """
    maskCr = np.full(len(self.raw), False)
    if opt == 1:
        maskCr[self.brkIdx1] = True
        maskCr[self.brkIdx2] = True
    elif opt == 2:
        maskCr[self.brkIdx1 : self.brkIdx2 + 1] = True
    elif opt == 3:
        maskCr[self.brkIdx1 : self.brkIdx3 + 1] = True
    # -- Linear regresion
    sigmaCr = self.raw["stress"].iloc[maskCr]
    sigmaCrlog = np.log10(sigmaCr)
    eCr = self.raw["e"].iloc[maskCr]
    idxCrInt, idxCr = polyfit(sigmaCrlog, eCr, deg=1)
    r2Cr = r2_score(
        y_true=eCr, y_pred=polyval(sigmaCrlog, [idxCrInt, idxCr])
    )
    self.maskCr = maskCr
    self.r2Cr = r2Cr
    self.idxCr = abs(idxCr)
    self.idxCrInt = idxCrInt
    return

pysigmap.casagrande

casagrande.py module.

Contains the class and its methods to determine the preconsolidation pressure from a compressibility curve via the method proposed by Casagrande (1963).

Casagrande

Casagrande class.

When the object is instanced, the method getSigmaP() calculates the preconsolidation pressure by the method proposed by Casagrande (1963) based on the compression index obtained with the Data class and the parameters of the method. See the method documentation for more information.

Attributes:
  • data (Object instanced from the ``Data`` class.) –

    Contains the data structure from the oedometer test. See the class documentation for more information.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> method = Casagrande(data)
>>> method.getSigmaP(range2fitFOP=None, loglog=True)
>>> method.sigmaP, method.ocr
(925.6233444375316, 12.34164459250042)
>>> method.getSigmaP(range2fitFOP=[20, 5000], loglog=True)
>>> method.sigmaP, method.ocr
(651.5675456910274, 8.687567275880365)
>>> method.getSigmaP(mcp=200)
>>> method.sigmaP, method.ocr
(498.6050168481866, 6.6480668913091545)
Source code in pysigmap/casagrande.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
class Casagrande:
    """``Casagrande`` class.

    When the object is instanced, the method ``getSigmaP()`` calculates the
    preconsolidation pressure by the method proposed by Casagrande (1963)
    based on the compression index obtained with the ``Data`` class and the
    parameters of the method. See the method documentation for more
    information.

    Attributes
    ----------
    data : Object instanced from the ``Data`` class.
        Contains the data structure from the oedometer test. See the class
        documentation for more information.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> method = Casagrande(data)
    >>> method.getSigmaP(range2fitFOP=None, loglog=True)
    >>> method.sigmaP, method.ocr
    (925.6233444375316, 12.34164459250042)
    >>> method.getSigmaP(range2fitFOP=[20, 5000], loglog=True)
    >>> method.sigmaP, method.ocr
    (651.5675456910274, 8.687567275880365)
    >>> method.getSigmaP(mcp=200)
    >>> method.sigmaP, method.ocr
    (498.6050168481866, 6.6480668913091545)
    """

    def __init__(self, data):
        """Initialize the class."""
        self.data = data
        return

    def getSigmaP(
        self, mcp=None, range2fitFOP=None, range2fitCS=None, loglog=True
    ):
        """
        Return the value of the preconsolidation pressure.

        Parameters
        ----------
        mcp : float, optional
            Variable to manually specify the maximun curvature point. If a
            value is input, the other parameters are not taken into account.
            The default is None.
        range2fitFOP : list, tuple or array (length=2), optional
            Initial and final pressures between which the fourth-order
            polynomial (FOP) will be fitted to the compressibility curve to
            calculate the maximum curvature point (MCP). If None, the MCP will
            be obtained using a cubic spline that passes through the data. The
            default is None.
        loglog : bool, optional
            Boolean to specify if the vertical effective stress will be
            transformed applying a logarithm twice to fit the FOT. The default
            is True.

        Returns
        -------
        fig : matplotlib figure
            Figure with the development of the method and the results.

        """

        def transform(x, reverse=False):
            if reverse:  # Remove a logaritmic scale
                return 10**10**x if loglog else 10**x
            else:  # Set a logaritmic scale
                return np.log10(np.log10(x)) if loglog else np.log10(x)

        # -- Indices to fit the Cubis Spline (CS)
        if range2fitCS is None:
            idxInitCS, idxEndCS = 1, len(self.data.cleaned["stress"]) + 1
        else:
            idxInitCS = self.data.findStressIdx(
                stress2find=range2fitCS[0], cleanedData=True
            )
            idxEndCS = self.data.findStressIdx(
                stress2find=range2fitCS[1], cleanedData=True
            )
        sigmaLog = np.log10(self.data.cleaned["stress"][idxInitCS:idxEndCS])
        cs = CubicSpline(
            x=sigmaLog, y=self.data.cleaned["e"][idxInitCS:idxEndCS]
        )
        sigmaCS = np.linspace(sigmaLog.iloc[0], sigmaLog.iloc[-1], 100)
        if range2fitFOP is None and mcp is None:  # Using a cubic spline
            x4FOP = 10**sigmaCS
            # -- Curvature function k(x) = f''(x)/(1+(f'(x))²)³/²
            curvature = abs(cs(sigmaCS, 2)) / (1 + cs(sigmaCS, 1) ** 2) ** (
                3 / 2
            )
            # maxCurvIdx = find_peaks(
            #     curvature, distance=self.data.cleaned["stress"].max()
            # )[0][0]
            try:
                maxCurvIdx = find_max_not_at_ends(curvature)
            except Exception:
                print(
                    "Maximun curvature not found matematicaly.",
                    "Choosing the absolute maximum value.",
                    sep="\n",
                )
                maxCurvIdx = np.argmax(curvature)
            self.sigmaMC = 10 ** sigmaCS[maxCurvIdx]  # Max. Curvature point
            self.eMC = cs(sigmaCS[maxCurvIdx])  # Void ratio at MC

        elif mcp is None:  # Using a fourth order polynomial (FOP)
            # -- Indices to fit the FOP
            idxInitFOP = self.data.findStressIdx(
                stress2find=range2fitFOP[0], cleanedData=True
            )
            idxEndFOP = self.data.findStressIdx(
                stress2find=range2fitFOP[1], cleanedData=True
            )

            # -- fittig a polynomial to data without unloads
            sigmaFOP = self.data.cleaned["stress"][idxInitFOP:idxEndFOP]
            sigmaFOPlog = transform(sigmaFOP)
            eFOP = self.data.cleaned["e"][idxInitFOP:idxEndFOP]
            p0, p1, p2, p3, p4 = polyfit(sigmaFOPlog, eFOP, deg=4)
            r2FOP = r2_score(
                y_true=eFOP, y_pred=polyval(sigmaFOPlog, [p0, p1, p2, p3, p4])
            )
            x4FOP = np.linspace(sigmaFOP.iloc[0], sigmaFOP.iloc[-1], 1000)
            x4FOPlog = transform(x4FOP)
            y4FOP = polyval(x4FOPlog, [p0, p1, p2, p3, p4])

            # -- Curvature function k(x) = f''(x)/(1+(f'(X))²)³/²
            firstDer = (
                p1
                + 2 * p2 * x4FOPlog
                + 3 * p3 * x4FOPlog**2
                + 4 * p4 * x4FOPlog**3
            )
            secondDer = 2 * p2 + 6 * p3 * x4FOPlog + 12 * p4 * x4FOPlog**2
            curvature = abs(secondDer) / (1 + firstDer**2) ** 1.5
            # maxCurvIdx = find_peaks(
            #     curvature, distance=self.data.cleaned["stress"].max()
            # )[0][0]
            try:
                maxCurvIdx = find_max_not_at_ends(curvature)
            except Exception:
                print(
                    "Maximun curvature not found matematicaly.",
                    "Choosing the absolute maximum value.",
                    sep="\n",
                )
                maxCurvIdx = np.argmax(curvature)
            self.sigmaMC = transform(x4FOPlog[maxCurvIdx], True)  # Max. Curv.
            self.eMC = y4FOP[maxCurvIdx]  # Void ratio at max. curvature

        if mcp is not None:
            self.sigmaMC = mcp  # Max. Curvature point
            self.eMC = cs(np.log10(self.sigmaMC))  # Void ratio at MC

        # -- Bisector line
        slopeMP = cs(np.log10(self.sigmaMC), nu=1)  # Slope at MC
        y1, x1 = self.eMC, np.log10(self.sigmaMC)
        x2 = np.log10(
            np.linspace(self.sigmaMC, self.data.cleaned["stress"].iloc[-1], 50)
        )
        y2 = slopeMP * (x2 - x1) + y1
        slopeBisect = np.tan(
            0.5 * np.arctan(slopeMP)
        )  # slope of bisector line
        y2bis = slopeBisect * (x2 - x1) + y1

        # -- Preconsolidation pressure
        self.sigmaP = 10 ** (
            (y1 - slopeBisect * x1 - self.data.idxCcInt)
            / (-self.data.idxCc - slopeBisect)
        )
        self.eSigmaP = slopeBisect * (np.log10(self.sigmaP) - x1) + y1
        self.ocr = self.sigmaP / self.data.sigmaV

        # -- plotting
        fig = plt.figure(figsize=figsize)
        ax1 = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        l1 = ax1.plot(
            self.data.raw["stress"][1:],
            self.data.raw["e"][1:],
            ls=(0, (1, 1)),
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Compressibility curve",
        )
        # Lines of the Casagrande's method
        ax1.plot(
            [self.sigmaMC, self.data.cleaned["stress"].iloc[-1]],
            [self.eMC, self.eMC],
            ls="--",
            lw=1.125,
            c="k",
        )  # hztl line
        ax1.plot(10**x2, y2, ls="--", lw=1.125, color="k")  # tangent line
        ax1.plot(
            10**x2, y2bis, ls="--", lw=1.125, color="k"
        )  # bisector line
        # Compression index (Cc)
        x4Cc = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
        y4Cc = -self.data.idxCc * np.log10(x4Cc) + self.data.idxCcInt
        l2 = ax1.plot(
            x4Cc,
            y4Cc,
            ls="-",
            lw=1.5,
            color=colors[1],
            label=str().join(["$C_\mathrm{c}=$", f"{self.data.idxCc:.3f}"]),
        )
        allLayers = l1 + l2
        if self.data.fitCc:
            l3 = ax1.plot(
                self.data.cleaned["stress"].iloc[self.data.maskCc],
                self.data.cleaned["e"].iloc[self.data.maskCc],
                ls="",
                marker="x",
                color=colors[1],
                label=f"Data for linear fit\n(R$^2={self.data.r2Cc:.3f}$)",
            )
            allLayers.insert(2, l3[0])
        if mcp is None:  # Curvature
            ax2 = ax1.twinx()  # second y axis for curvature function
            l6 = ax2.plot(
                x4FOP,
                curvature,
                ls="--",
                c=colors[0],
                lw=1.125,
                mfc="w",
                label="Curvature",
            )
            if range2fitCS is not None:  # Cubic spline
                l4 = ax1.plot(
                    10**sigmaCS,
                    cs(sigmaCS),
                    ls="--",
                    lw=1.125,
                    color=colors[2],
                    label="Cubic spline",
                )
                l5 = ax1.plot(
                    self.data.cleaned["stress"][idxInitCS:idxEndCS],
                    self.data.cleaned["e"][idxInitCS:idxEndCS],
                    ls="",
                    marker="+",
                    c=colors[2],
                    label="Data for cubic spline",
                )
                allLayers += l4 + l5
            # if range2fitFOP is None:  # Curvature
            if range2fitFOP is not None:  # FOP fit
                l4 = ax1.plot(
                    x4FOP,
                    y4FOP,
                    ls="--",
                    lw=1.125,
                    color=colors[2],
                    label="$4^\mathrm{th}$-order polynomial",
                )
                l5 = ax1.plot(
                    sigmaFOP,
                    eFOP,
                    ls="",
                    marker="+",
                    c=colors[2],
                    label=f"Data for linear fit\n(R$^2={r2FOP:.3f}$)",
                )
                allLayers += l4 + l5
            allLayers += l6
        # Other plots
        l7 = ax1.plot(
            self.data.sigmaV,
            self.data.eSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{v_0}=$ ",
                    f"{self.data.sigmaV:.0f} kPa",
                ]
            ),
        )
        l8 = ax1.plot(
            self.sigmaMC,
            self.eMC,
            ls="",
            marker="^",
            c=colors[0],
            mfc="w",
            mew=1.5,
            ms=7,
            label="Max. curvature point",
        )
        l9 = ax1.plot(
            self.sigmaP,
            self.eSigmaP,
            ls="",
            marker="o",
            mfc="w",
            c=colors[0],
            ms=7,
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{p}=$ ",
                    f"{self.sigmaP:.0f} kPa\n",
                    f"OCR= {self.ocr:.1f}",
                ]
            ),
        )
        allLayers += l7 + l8 + l9
        # Legend
        labs = [layer.get_label() for layer in allLayers]
        ax1.legend(
            allLayers,
            labs,
            bbox_to_anchor=(1.125, 0.5),
            loc=6,
            title="$\\bf{Casagrande\ method}$",
        )
        # Other details
        ax1.spines["top"].set_visible(False)
        ax1.spines["right"].set_visible(False)
        ax1.set(
            xscale="log",
            ylabel="Void ratio, $e$",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax1.xaxis.set_major_formatter(mtick.ScalarFormatter())
        ax1.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax1.grid(False)
        if mcp is None:  # Curvature
            ax2.yaxis.set_minor_locator(mtick.AutoMinorLocator())
            ax2.set(ylabel="Curvature, $k$")
            ax2.spines["top"].set_visible(False)
        return fig

__init__

__init__(data)

Initialize the class.

pysigmap/casagrande.py
65
66
67
68
def __init__(self, data):
    """Initialize the class."""
    self.data = data
    return

getSigmaP

getSigmaP(mcp=None, range2fitFOP=None, range2fitCS=None, loglog=True)

Return the value of the preconsolidation pressure.

Parameters:
  • mcp (float, default: None ) –

    Variable to manually specify the maximun curvature point. If a value is input, the other parameters are not taken into account. The default is None.

  • range2fitFOP ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the fourth-order polynomial (FOP) will be fitted to the compressibility curve to calculate the maximum curvature point (MCP). If None, the MCP will be obtained using a cubic spline that passes through the data. The default is None.

  • loglog (bool, default: True ) –

    Boolean to specify if the vertical effective stress will be transformed applying a logarithm twice to fit the FOT. The default is True.

Returns:
  • fig( matplotlib figure ) –

    Figure with the development of the method and the results.

pysigmap/casagrande.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
def getSigmaP(
    self, mcp=None, range2fitFOP=None, range2fitCS=None, loglog=True
):
    """
    Return the value of the preconsolidation pressure.

    Parameters
    ----------
    mcp : float, optional
        Variable to manually specify the maximun curvature point. If a
        value is input, the other parameters are not taken into account.
        The default is None.
    range2fitFOP : list, tuple or array (length=2), optional
        Initial and final pressures between which the fourth-order
        polynomial (FOP) will be fitted to the compressibility curve to
        calculate the maximum curvature point (MCP). If None, the MCP will
        be obtained using a cubic spline that passes through the data. The
        default is None.
    loglog : bool, optional
        Boolean to specify if the vertical effective stress will be
        transformed applying a logarithm twice to fit the FOT. The default
        is True.

    Returns
    -------
    fig : matplotlib figure
        Figure with the development of the method and the results.

    """

    def transform(x, reverse=False):
        if reverse:  # Remove a logaritmic scale
            return 10**10**x if loglog else 10**x
        else:  # Set a logaritmic scale
            return np.log10(np.log10(x)) if loglog else np.log10(x)

    # -- Indices to fit the Cubis Spline (CS)
    if range2fitCS is None:
        idxInitCS, idxEndCS = 1, len(self.data.cleaned["stress"]) + 1
    else:
        idxInitCS = self.data.findStressIdx(
            stress2find=range2fitCS[0], cleanedData=True
        )
        idxEndCS = self.data.findStressIdx(
            stress2find=range2fitCS[1], cleanedData=True
        )
    sigmaLog = np.log10(self.data.cleaned["stress"][idxInitCS:idxEndCS])
    cs = CubicSpline(
        x=sigmaLog, y=self.data.cleaned["e"][idxInitCS:idxEndCS]
    )
    sigmaCS = np.linspace(sigmaLog.iloc[0], sigmaLog.iloc[-1], 100)
    if range2fitFOP is None and mcp is None:  # Using a cubic spline
        x4FOP = 10**sigmaCS
        # -- Curvature function k(x) = f''(x)/(1+(f'(x))²)³/²
        curvature = abs(cs(sigmaCS, 2)) / (1 + cs(sigmaCS, 1) ** 2) ** (
            3 / 2
        )
        # maxCurvIdx = find_peaks(
        #     curvature, distance=self.data.cleaned["stress"].max()
        # )[0][0]
        try:
            maxCurvIdx = find_max_not_at_ends(curvature)
        except Exception:
            print(
                "Maximun curvature not found matematicaly.",
                "Choosing the absolute maximum value.",
                sep="\n",
            )
            maxCurvIdx = np.argmax(curvature)
        self.sigmaMC = 10 ** sigmaCS[maxCurvIdx]  # Max. Curvature point
        self.eMC = cs(sigmaCS[maxCurvIdx])  # Void ratio at MC

    elif mcp is None:  # Using a fourth order polynomial (FOP)
        # -- Indices to fit the FOP
        idxInitFOP = self.data.findStressIdx(
            stress2find=range2fitFOP[0], cleanedData=True
        )
        idxEndFOP = self.data.findStressIdx(
            stress2find=range2fitFOP[1], cleanedData=True
        )

        # -- fittig a polynomial to data without unloads
        sigmaFOP = self.data.cleaned["stress"][idxInitFOP:idxEndFOP]
        sigmaFOPlog = transform(sigmaFOP)
        eFOP = self.data.cleaned["e"][idxInitFOP:idxEndFOP]
        p0, p1, p2, p3, p4 = polyfit(sigmaFOPlog, eFOP, deg=4)
        r2FOP = r2_score(
            y_true=eFOP, y_pred=polyval(sigmaFOPlog, [p0, p1, p2, p3, p4])
        )
        x4FOP = np.linspace(sigmaFOP.iloc[0], sigmaFOP.iloc[-1], 1000)
        x4FOPlog = transform(x4FOP)
        y4FOP = polyval(x4FOPlog, [p0, p1, p2, p3, p4])

        # -- Curvature function k(x) = f''(x)/(1+(f'(X))²)³/²
        firstDer = (
            p1
            + 2 * p2 * x4FOPlog
            + 3 * p3 * x4FOPlog**2
            + 4 * p4 * x4FOPlog**3
        )
        secondDer = 2 * p2 + 6 * p3 * x4FOPlog + 12 * p4 * x4FOPlog**2
        curvature = abs(secondDer) / (1 + firstDer**2) ** 1.5
        # maxCurvIdx = find_peaks(
        #     curvature, distance=self.data.cleaned["stress"].max()
        # )[0][0]
        try:
            maxCurvIdx = find_max_not_at_ends(curvature)
        except Exception:
            print(
                "Maximun curvature not found matematicaly.",
                "Choosing the absolute maximum value.",
                sep="\n",
            )
            maxCurvIdx = np.argmax(curvature)
        self.sigmaMC = transform(x4FOPlog[maxCurvIdx], True)  # Max. Curv.
        self.eMC = y4FOP[maxCurvIdx]  # Void ratio at max. curvature

    if mcp is not None:
        self.sigmaMC = mcp  # Max. Curvature point
        self.eMC = cs(np.log10(self.sigmaMC))  # Void ratio at MC

    # -- Bisector line
    slopeMP = cs(np.log10(self.sigmaMC), nu=1)  # Slope at MC
    y1, x1 = self.eMC, np.log10(self.sigmaMC)
    x2 = np.log10(
        np.linspace(self.sigmaMC, self.data.cleaned["stress"].iloc[-1], 50)
    )
    y2 = slopeMP * (x2 - x1) + y1
    slopeBisect = np.tan(
        0.5 * np.arctan(slopeMP)
    )  # slope of bisector line
    y2bis = slopeBisect * (x2 - x1) + y1

    # -- Preconsolidation pressure
    self.sigmaP = 10 ** (
        (y1 - slopeBisect * x1 - self.data.idxCcInt)
        / (-self.data.idxCc - slopeBisect)
    )
    self.eSigmaP = slopeBisect * (np.log10(self.sigmaP) - x1) + y1
    self.ocr = self.sigmaP / self.data.sigmaV

    # -- plotting
    fig = plt.figure(figsize=figsize)
    ax1 = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    l1 = ax1.plot(
        self.data.raw["stress"][1:],
        self.data.raw["e"][1:],
        ls=(0, (1, 1)),
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Compressibility curve",
    )
    # Lines of the Casagrande's method
    ax1.plot(
        [self.sigmaMC, self.data.cleaned["stress"].iloc[-1]],
        [self.eMC, self.eMC],
        ls="--",
        lw=1.125,
        c="k",
    )  # hztl line
    ax1.plot(10**x2, y2, ls="--", lw=1.125, color="k")  # tangent line
    ax1.plot(
        10**x2, y2bis, ls="--", lw=1.125, color="k"
    )  # bisector line
    # Compression index (Cc)
    x4Cc = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
    y4Cc = -self.data.idxCc * np.log10(x4Cc) + self.data.idxCcInt
    l2 = ax1.plot(
        x4Cc,
        y4Cc,
        ls="-",
        lw=1.5,
        color=colors[1],
        label=str().join(["$C_\mathrm{c}=$", f"{self.data.idxCc:.3f}"]),
    )
    allLayers = l1 + l2
    if self.data.fitCc:
        l3 = ax1.plot(
            self.data.cleaned["stress"].iloc[self.data.maskCc],
            self.data.cleaned["e"].iloc[self.data.maskCc],
            ls="",
            marker="x",
            color=colors[1],
            label=f"Data for linear fit\n(R$^2={self.data.r2Cc:.3f}$)",
        )
        allLayers.insert(2, l3[0])
    if mcp is None:  # Curvature
        ax2 = ax1.twinx()  # second y axis for curvature function
        l6 = ax2.plot(
            x4FOP,
            curvature,
            ls="--",
            c=colors[0],
            lw=1.125,
            mfc="w",
            label="Curvature",
        )
        if range2fitCS is not None:  # Cubic spline
            l4 = ax1.plot(
                10**sigmaCS,
                cs(sigmaCS),
                ls="--",
                lw=1.125,
                color=colors[2],
                label="Cubic spline",
            )
            l5 = ax1.plot(
                self.data.cleaned["stress"][idxInitCS:idxEndCS],
                self.data.cleaned["e"][idxInitCS:idxEndCS],
                ls="",
                marker="+",
                c=colors[2],
                label="Data for cubic spline",
            )
            allLayers += l4 + l5
        # if range2fitFOP is None:  # Curvature
        if range2fitFOP is not None:  # FOP fit
            l4 = ax1.plot(
                x4FOP,
                y4FOP,
                ls="--",
                lw=1.125,
                color=colors[2],
                label="$4^\mathrm{th}$-order polynomial",
            )
            l5 = ax1.plot(
                sigmaFOP,
                eFOP,
                ls="",
                marker="+",
                c=colors[2],
                label=f"Data for linear fit\n(R$^2={r2FOP:.3f}$)",
            )
            allLayers += l4 + l5
        allLayers += l6
    # Other plots
    l7 = ax1.plot(
        self.data.sigmaV,
        self.data.eSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{v_0}=$ ",
                f"{self.data.sigmaV:.0f} kPa",
            ]
        ),
    )
    l8 = ax1.plot(
        self.sigmaMC,
        self.eMC,
        ls="",
        marker="^",
        c=colors[0],
        mfc="w",
        mew=1.5,
        ms=7,
        label="Max. curvature point",
    )
    l9 = ax1.plot(
        self.sigmaP,
        self.eSigmaP,
        ls="",
        marker="o",
        mfc="w",
        c=colors[0],
        ms=7,
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{p}=$ ",
                f"{self.sigmaP:.0f} kPa\n",
                f"OCR= {self.ocr:.1f}",
            ]
        ),
    )
    allLayers += l7 + l8 + l9
    # Legend
    labs = [layer.get_label() for layer in allLayers]
    ax1.legend(
        allLayers,
        labs,
        bbox_to_anchor=(1.125, 0.5),
        loc=6,
        title="$\\bf{Casagrande\ method}$",
    )
    # Other details
    ax1.spines["top"].set_visible(False)
    ax1.spines["right"].set_visible(False)
    ax1.set(
        xscale="log",
        ylabel="Void ratio, $e$",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax1.xaxis.set_major_formatter(mtick.ScalarFormatter())
    ax1.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax1.grid(False)
    if mcp is None:  # Curvature
        ax2.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax2.set(ylabel="Curvature, $k$")
        ax2.spines["top"].set_visible(False)
    return fig

find_max_not_at_ends

find_max_not_at_ends(vector)

Find the index of the maximum value of a vector that is not at the ends.

Parameters:
  • vector (array_like) –

    Vector to find the maximum value.

Returns:
  • max_index( int ) –

    Index of the maximum value of the vector that is not at the ends.

pysigmap/casagrande.py
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
def find_max_not_at_ends(vector):
    """
    Find the index of the maximum value of a vector that is not at the ends.

    Parameters
    ----------
    vector : array_like
        Vector to find the maximum value.

    Returns
    -------
    max_index : int
        Index of the maximum value of the vector that is not at the ends.

    """

    while True:
        # Exclude the first and last element
        vector = vector[1:-1]
        # Find the index of the maximum value
        max_index = np.argmax(vector)
        # If the maximum value is not at the ends of the new vector, break the loop
        if max_index not in [0, len(vector) - 1]:
            break
    return max_index + 1

pysigmap.energy

energy.py module.

Contains the classes and theis methods to determine the preconsolidation pressure from a compressibility curve via the strain energy methods proposed by Becker et al. (1987), Morin (1988) and Wang & Frost (2004).

BeckerEtAl

BeckerEtAl class.

When the object is instanced, the method getSigmaP() calculates the preconsolidation pressure by the method proposed by Becker et al. (1987) based on the parameters of the method. See the method documentation for more information.

Attributes:
  • data (Object instanced from the ``Data`` class.) –

    Contains the data structure from the oedometer test. See the class documentation for more information.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> method = BeckerEtAl(data)
>>> method.getSigmaP(range2fitRR=None, range2fitCR=None, zoom=4)
>>> method.sigmaP, method.ocr
(670.2104847956236, 8.936139797274983)
>>> method.getSigmaP(range2fitRR=None, range2fitCR=[700, 10000], zoom=4)
>>> method.sigmaP, method.ocr
(500.09903645877176, 6.667987152783623)
Source code in pysigmap/energy.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
class BeckerEtAl:
    """``BeckerEtAl`` class.

    When the object is instanced, the method ``getSigmaP()`` calculates the
    preconsolidation pressure by the method proposed by Becker et al. (1987)
    based on the parameters of the method. See the method documentation for
    more information.

    Attributes
    ----------
    data : Object instanced from the ``Data`` class.
        Contains the data structure from the oedometer test. See the class
        documentation for more information.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> method = BeckerEtAl(data)
    >>> method.getSigmaP(range2fitRR=None, range2fitCR=None, zoom=4)
    >>> method.sigmaP, method.ocr
    (670.2104847956236, 8.936139797274983)
    >>> method.getSigmaP(range2fitRR=None, range2fitCR=[700, 10000], zoom=4)
    >>> method.sigmaP, method.ocr
    (500.09903645877176, 6.667987152783623)
    """

    def __init__(self, data):
        """Initialize the class."""
        self.data = data
        self.morinFormulation = False
        self.calculateWork()
        return

    def calculateWork(self):
        """
        Calculate the work per unit volume.

        Returns
        -------
        None.

        """
        sigma = self.data.raw["stress"].array
        epsilon = self.data.raw["strain"].array
        deltaWork = (
            0.5 * (epsilon[1:] - epsilon[:-1]) * (sigma[1:] + sigma[:-1])
        )
        deltaWork = np.hstack((0, deltaWork))
        if self.morinFormulation:  # Work per unit volume of solids
            deltaWork /= 1 + self.data.e_0
        self.data.raw["deltaWork"] = deltaWork
        self.data.raw["work"] = np.cumsum(deltaWork)
        self.data.clean()  # Data without unloads
        return

    def getSigmaP(
        self,
        range2fitRR=None,
        range2fitCR=None,
        zoom=3,
        morinFormulation=False,
    ):
        """
        Return the value of the preconsolidation pressure.

        Parameters
        ----------
        range2fitRR : list, tuple or array (length=2), optional
            Initial and final pressures between which the first order
            polynomial will be fitted to the data on the recompression range
            (RR) (range before the preconsolidation pressure). If None, the
            first order polynomial will be fitted from the first point of the
            curve to the point before the in situ effective vertical stress.
            The default is None.
        range2fitCR : list, tuple or array (length=2), optional
            Initial and final pressures between which the first order
            polynomial will be fitted to the data on the compression range (CR)
            (range above the preconsolidation pressure). If None, the CR will
            be automatically fitted to the same points used for calculating the
            compression index with the ``Data`` class only if it was calculated
            with a linear fit, otherwise, the steepest slope of the cubic
            spline that passes through the data will be used. The default is
            None.
        zoom : int, optional
            Value to magnify the view of the first points of the curve and the
            preconsolidation pressure in an inset window. The default is 3.
        morinFormulation : bool, optional
            Boolean to specify if the work per unit volume of solids (Morin
            formulation) must be used instead of the work per unit volume
            (Becker formulation). The default is False.

        Returns
        -------
        fig : matplotlib figure
            Figure with the development of the method and the results.

        """
        # -- Calculating work
        if morinFormulation:
            self.morinFormulation = morinFormulation
            self.calculateWork()  # Calculate again with Morin Formulation

        # -- Preyield range or recompression range (RR)
        maskRR = np.full(len(self.data.cleaned), False)
        if range2fitRR is None:  # Indices for fitting the RR line
            idxInitRR = 0
            idxEndRR = self.data.findStressIdx(
                stress2find=self.data.sigmaV, cleanedData=True
            )
        else:
            idxInitRR = self.data.findStressIdx(
                stress2find=range2fitRR[0], cleanedData=True
            )
            idxEndRR = self.data.findStressIdx(
                stress2find=range2fitRR[1], cleanedData=True
            )
        maskRR[idxInitRR:idxEndRR] = True
        # Linear regresion
        sigmaRR = self.data.cleaned["stress"][maskRR]
        workRR = self.data.cleaned["work"][maskRR]
        p1_0, p1_1 = polyfit(sigmaRR, workRR, deg=1)
        r2RR = r2_score(y_true=workRR, y_pred=polyval(sigmaRR, [p1_0, p1_1]))
        xRR = np.linspace(0, self.data.cleaned["stress"].iloc[-3])
        yRR = polyval(xRR, [p1_0, p1_1])

        # -- Post yield range or compression range (CR)
        maskCR = np.full(len(self.data.cleaned), False)
        if range2fitCR is not None or self.data.fitCc:  # Using a linear fit
            if range2fitCR is not None:
                idxInitCR = self.data.findStressIdx(
                    stress2find=range2fitCR[0], cleanedData=True
                )
                idxEndCR = self.data.findStressIdx(
                    stress2find=range2fitCR[1], cleanedData=True
                )
                maskCR[idxInitCR:idxEndCR] = True
            elif self.data.fitCc:
                maskCR = self.data.maskCc
            # Linear regresion
            sigmaCR = self.data.cleaned["stress"][maskCR]
            workCR = self.data.cleaned["work"][maskCR]
            lcrInt, lcrSlope = polyfit(sigmaCR, workCR, deg=1)
            r2CR = r2_score(
                y_true=workCR, y_pred=polyval(sigmaCR, [lcrInt, lcrSlope])
            )
        else:  # Using the steepest point of a cubic spline
            sigma = self.data.cleaned["stress"]
            cs = CubicSpline(x=sigma, y=self.data.cleaned["work"])
            sigmaCS = np.linspace(sigma.iloc[0], sigma.iloc[-1], 500)
            steepestSlopeIdx = np.argmax(cs(sigmaCS, 1))
            lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
            lcrInt = (
                cs(sigmaCS[steepestSlopeIdx])
                - lcrSlope * sigmaCS[steepestSlopeIdx]
            )
        xCR = np.linspace(
            -lcrInt / lcrSlope, self.data.cleaned["stress"].iloc[-1]
        )
        yCR = polyval(xCR, [lcrInt, lcrSlope])

        # -- Preconsolitadion pressure
        workSigmaV = polyval(self.data.sigmaV, [p1_0, p1_1])
        self.sigmaP = (lcrInt - p1_0) / (p1_1 - lcrSlope)
        self.wSigmaP = polyval(self.sigmaP, [p1_0, p1_1])
        self.ocr = self.sigmaP / self.data.sigmaV

        # -- Plot compresibility curve
        fig = plt.figure(figsize=figsize)
        ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        ax.plot(
            self.data.raw["stress"],
            self.data.raw["work"],
            ls=(0, (1, 1)),
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Data",
        )  # all data
        # Recompression range
        ax.plot(
            xRR,
            yRR,
            ls="-",
            c=colors[2],
            lw=1.125,
            label="Recompression range",
        )
        ax.plot(
            sigmaRR,
            workRR,
            ls="",
            marker="+",
            c=colors[2],
            label=f"Data for linear fit\n(R$^2={r2RR:.3f}$)",
        )
        # Compression range
        ax.plot(
            xCR, yCR, ls="-", c=colors[1], lw=1.125, label="Compression range"
        )
        if range2fitCR is not None or self.data.fitCc:
            ax.plot(
                sigmaCR,
                workCR,
                ls="",
                marker="x",
                c=colors[1],
                label=f"Data for linear fit\n(R$^2={r2CR:.3f}$)",
            )
        # Other plots
        ax.plot(
            self.data.sigmaV,
            workSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{v0}=$ ",
                    f"{self.data.sigmaV:.0f} kPa",
                ]
            ),
        )
        ax.plot(
            self.sigmaP,
            self.wSigmaP,
            ls="",
            c=colors[0],
            ms=7,
            mfc="w",
            marker="o",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{p}=$ ",
                    f"{self.sigmaP:.0f} kPa\n",
                    f"OCR= {self.ocr:.1f}",
                ]
            ),
        )
        # Other details
        if morinFormulation:
            methodTitle = "$\\bf{Morin\ method}$"
        else:
            methodTitle = "$\\bf{Becker\ et\ al.\ method}$"
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.set(
            ylabel="Total work per unit volume, $W$ [kJ m$^{-3}$]",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax.xaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.grid(False)
        ax.legend(bbox_to_anchor=(1.125, 0.5), loc=6, title=methodTitle)

        # -- inset axes to zoom
        axins = zoomed_inset_axes(ax, zoom=zoom, loc=4)
        axins.plot(
            self.data.raw["stress"],
            self.data.raw["work"],
            ls=":",
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
        )  # all data
        axins.plot(xRR, yRR, ls="-", c=colors[2], lw=1.125)
        axins.plot(sigmaRR, workRR, ls="", marker="+", c=colors[2])
        axins.plot(xCR, yCR, ls="-", c=colors[1], lw=1.125)
        if range2fitCR is not None or self.data.fitCc:
            axins.plot(sigmaCR, workCR, ls="", marker="x", c=colors[1])
        axins.plot(
            self.data.sigmaV,
            workSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
        )
        axins.plot(
            self.sigmaP,
            self.wSigmaP,
            marker="o",
            c=colors[0],
            ms=7,
            mfc="w",
            mew=1.5,
        )
        # axins.spines['bottom'].set_visible(False)
        # axins.spines['right'].set_visible(False)
        axins.grid(False)
        axins.set(
            xlim=(-0.05 * self.sigmaP, 1.25 * self.sigmaP),
            ylim=(-0.05 * self.wSigmaP, 2.05 * self.wSigmaP),
            xlabel="$\sigma^\prime_\mathrm{v}$ [kPa]",
            ylabel="W [kJ m$^{-3}$]",
        )
        axins.xaxis.tick_top()
        axins.xaxis.set_label_position("top")
        axins.yaxis.tick_right()
        axins.yaxis.set_label_position("right")
        axins.xaxis.set_tick_params(labelsize="small")
        axins.yaxis.set_tick_params(labelsize="small")
        axins.xaxis.set_minor_locator(mtick.AutoMinorLocator())
        axins.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        # axins.set_yticks([])
        mark_inset(ax, axins, loc1=2, loc2=3, fc="none", ec="0.5")  # bbox
        return fig

__init__

__init__(data)

Initialize the class.

pysigmap/energy.py
64
65
66
67
68
69
def __init__(self, data):
    """Initialize the class."""
    self.data = data
    self.morinFormulation = False
    self.calculateWork()
    return

calculateWork

calculateWork()

Calculate the work per unit volume.

Returns:
  • None.
pysigmap/energy.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def calculateWork(self):
    """
    Calculate the work per unit volume.

    Returns
    -------
    None.

    """
    sigma = self.data.raw["stress"].array
    epsilon = self.data.raw["strain"].array
    deltaWork = (
        0.5 * (epsilon[1:] - epsilon[:-1]) * (sigma[1:] + sigma[:-1])
    )
    deltaWork = np.hstack((0, deltaWork))
    if self.morinFormulation:  # Work per unit volume of solids
        deltaWork /= 1 + self.data.e_0
    self.data.raw["deltaWork"] = deltaWork
    self.data.raw["work"] = np.cumsum(deltaWork)
    self.data.clean()  # Data without unloads
    return

getSigmaP

getSigmaP(range2fitRR=None, range2fitCR=None, zoom=3, morinFormulation=False)

Return the value of the preconsolidation pressure.

Parameters:
  • range2fitRR ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first order polynomial will be fitted to the data on the recompression range (RR) (range before the preconsolidation pressure). If None, the first order polynomial will be fitted from the first point of the curve to the point before the in situ effective vertical stress. The default is None.

  • range2fitCR ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first order polynomial will be fitted to the data on the compression range (CR) (range above the preconsolidation pressure). If None, the CR will be automatically fitted to the same points used for calculating the compression index with the Data class only if it was calculated with a linear fit, otherwise, the steepest slope of the cubic spline that passes through the data will be used. The default is None.

  • zoom (int, default: 3 ) –

    Value to magnify the view of the first points of the curve and the preconsolidation pressure in an inset window. The default is 3.

  • morinFormulation (bool, default: False ) –

    Boolean to specify if the work per unit volume of solids (Morin formulation) must be used instead of the work per unit volume (Becker formulation). The default is False.

Returns:
  • fig( matplotlib figure ) –

    Figure with the development of the method and the results.

pysigmap/energy.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
def getSigmaP(
    self,
    range2fitRR=None,
    range2fitCR=None,
    zoom=3,
    morinFormulation=False,
):
    """
    Return the value of the preconsolidation pressure.

    Parameters
    ----------
    range2fitRR : list, tuple or array (length=2), optional
        Initial and final pressures between which the first order
        polynomial will be fitted to the data on the recompression range
        (RR) (range before the preconsolidation pressure). If None, the
        first order polynomial will be fitted from the first point of the
        curve to the point before the in situ effective vertical stress.
        The default is None.
    range2fitCR : list, tuple or array (length=2), optional
        Initial and final pressures between which the first order
        polynomial will be fitted to the data on the compression range (CR)
        (range above the preconsolidation pressure). If None, the CR will
        be automatically fitted to the same points used for calculating the
        compression index with the ``Data`` class only if it was calculated
        with a linear fit, otherwise, the steepest slope of the cubic
        spline that passes through the data will be used. The default is
        None.
    zoom : int, optional
        Value to magnify the view of the first points of the curve and the
        preconsolidation pressure in an inset window. The default is 3.
    morinFormulation : bool, optional
        Boolean to specify if the work per unit volume of solids (Morin
        formulation) must be used instead of the work per unit volume
        (Becker formulation). The default is False.

    Returns
    -------
    fig : matplotlib figure
        Figure with the development of the method and the results.

    """
    # -- Calculating work
    if morinFormulation:
        self.morinFormulation = morinFormulation
        self.calculateWork()  # Calculate again with Morin Formulation

    # -- Preyield range or recompression range (RR)
    maskRR = np.full(len(self.data.cleaned), False)
    if range2fitRR is None:  # Indices for fitting the RR line
        idxInitRR = 0
        idxEndRR = self.data.findStressIdx(
            stress2find=self.data.sigmaV, cleanedData=True
        )
    else:
        idxInitRR = self.data.findStressIdx(
            stress2find=range2fitRR[0], cleanedData=True
        )
        idxEndRR = self.data.findStressIdx(
            stress2find=range2fitRR[1], cleanedData=True
        )
    maskRR[idxInitRR:idxEndRR] = True
    # Linear regresion
    sigmaRR = self.data.cleaned["stress"][maskRR]
    workRR = self.data.cleaned["work"][maskRR]
    p1_0, p1_1 = polyfit(sigmaRR, workRR, deg=1)
    r2RR = r2_score(y_true=workRR, y_pred=polyval(sigmaRR, [p1_0, p1_1]))
    xRR = np.linspace(0, self.data.cleaned["stress"].iloc[-3])
    yRR = polyval(xRR, [p1_0, p1_1])

    # -- Post yield range or compression range (CR)
    maskCR = np.full(len(self.data.cleaned), False)
    if range2fitCR is not None or self.data.fitCc:  # Using a linear fit
        if range2fitCR is not None:
            idxInitCR = self.data.findStressIdx(
                stress2find=range2fitCR[0], cleanedData=True
            )
            idxEndCR = self.data.findStressIdx(
                stress2find=range2fitCR[1], cleanedData=True
            )
            maskCR[idxInitCR:idxEndCR] = True
        elif self.data.fitCc:
            maskCR = self.data.maskCc
        # Linear regresion
        sigmaCR = self.data.cleaned["stress"][maskCR]
        workCR = self.data.cleaned["work"][maskCR]
        lcrInt, lcrSlope = polyfit(sigmaCR, workCR, deg=1)
        r2CR = r2_score(
            y_true=workCR, y_pred=polyval(sigmaCR, [lcrInt, lcrSlope])
        )
    else:  # Using the steepest point of a cubic spline
        sigma = self.data.cleaned["stress"]
        cs = CubicSpline(x=sigma, y=self.data.cleaned["work"])
        sigmaCS = np.linspace(sigma.iloc[0], sigma.iloc[-1], 500)
        steepestSlopeIdx = np.argmax(cs(sigmaCS, 1))
        lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
        lcrInt = (
            cs(sigmaCS[steepestSlopeIdx])
            - lcrSlope * sigmaCS[steepestSlopeIdx]
        )
    xCR = np.linspace(
        -lcrInt / lcrSlope, self.data.cleaned["stress"].iloc[-1]
    )
    yCR = polyval(xCR, [lcrInt, lcrSlope])

    # -- Preconsolitadion pressure
    workSigmaV = polyval(self.data.sigmaV, [p1_0, p1_1])
    self.sigmaP = (lcrInt - p1_0) / (p1_1 - lcrSlope)
    self.wSigmaP = polyval(self.sigmaP, [p1_0, p1_1])
    self.ocr = self.sigmaP / self.data.sigmaV

    # -- Plot compresibility curve
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    ax.plot(
        self.data.raw["stress"],
        self.data.raw["work"],
        ls=(0, (1, 1)),
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Data",
    )  # all data
    # Recompression range
    ax.plot(
        xRR,
        yRR,
        ls="-",
        c=colors[2],
        lw=1.125,
        label="Recompression range",
    )
    ax.plot(
        sigmaRR,
        workRR,
        ls="",
        marker="+",
        c=colors[2],
        label=f"Data for linear fit\n(R$^2={r2RR:.3f}$)",
    )
    # Compression range
    ax.plot(
        xCR, yCR, ls="-", c=colors[1], lw=1.125, label="Compression range"
    )
    if range2fitCR is not None or self.data.fitCc:
        ax.plot(
            sigmaCR,
            workCR,
            ls="",
            marker="x",
            c=colors[1],
            label=f"Data for linear fit\n(R$^2={r2CR:.3f}$)",
        )
    # Other plots
    ax.plot(
        self.data.sigmaV,
        workSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{v0}=$ ",
                f"{self.data.sigmaV:.0f} kPa",
            ]
        ),
    )
    ax.plot(
        self.sigmaP,
        self.wSigmaP,
        ls="",
        c=colors[0],
        ms=7,
        mfc="w",
        marker="o",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{p}=$ ",
                f"{self.sigmaP:.0f} kPa\n",
                f"OCR= {self.ocr:.1f}",
            ]
        ),
    )
    # Other details
    if morinFormulation:
        methodTitle = "$\\bf{Morin\ method}$"
    else:
        methodTitle = "$\\bf{Becker\ et\ al.\ method}$"
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set(
        ylabel="Total work per unit volume, $W$ [kJ m$^{-3}$]",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax.xaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.grid(False)
    ax.legend(bbox_to_anchor=(1.125, 0.5), loc=6, title=methodTitle)

    # -- inset axes to zoom
    axins = zoomed_inset_axes(ax, zoom=zoom, loc=4)
    axins.plot(
        self.data.raw["stress"],
        self.data.raw["work"],
        ls=":",
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
    )  # all data
    axins.plot(xRR, yRR, ls="-", c=colors[2], lw=1.125)
    axins.plot(sigmaRR, workRR, ls="", marker="+", c=colors[2])
    axins.plot(xCR, yCR, ls="-", c=colors[1], lw=1.125)
    if range2fitCR is not None or self.data.fitCc:
        axins.plot(sigmaCR, workCR, ls="", marker="x", c=colors[1])
    axins.plot(
        self.data.sigmaV,
        workSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
    )
    axins.plot(
        self.sigmaP,
        self.wSigmaP,
        marker="o",
        c=colors[0],
        ms=7,
        mfc="w",
        mew=1.5,
    )
    # axins.spines['bottom'].set_visible(False)
    # axins.spines['right'].set_visible(False)
    axins.grid(False)
    axins.set(
        xlim=(-0.05 * self.sigmaP, 1.25 * self.sigmaP),
        ylim=(-0.05 * self.wSigmaP, 2.05 * self.wSigmaP),
        xlabel="$\sigma^\prime_\mathrm{v}$ [kPa]",
        ylabel="W [kJ m$^{-3}$]",
    )
    axins.xaxis.tick_top()
    axins.xaxis.set_label_position("top")
    axins.yaxis.tick_right()
    axins.yaxis.set_label_position("right")
    axins.xaxis.set_tick_params(labelsize="small")
    axins.yaxis.set_tick_params(labelsize="small")
    axins.xaxis.set_minor_locator(mtick.AutoMinorLocator())
    axins.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    # axins.set_yticks([])
    mark_inset(ax, axins, loc1=2, loc2=3, fc="none", ec="0.5")  # bbox
    return fig

WangAndFrost

Bases: BeckerEtAl

WangAndFrost class.

When the object is instanced, the method getSigmaP() calculates the preconsolidation pressure by the method proposed by Wang & Frost (2004) based on the parameters of the method. See the method documentation for more information.

Attributes:
  • data (Object instanced from the ``Data`` class.) –

    Contains the data structure from the consolidation test. See the class documentation for more information.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> method = WangAndFrost(data)
>>> method.getSigmaP(range2fitCR=None)
>>> method.sigmaP, method.ocr
(930.59421331855, 12.407922844247334)
>>> method.getSigmaP(range2fitCR=[700, 10000])
>>> method.sigmaP, method.ocr
(718.9365936678746, 9.585821248904995)
Source code in pysigmap/energy.py
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
class WangAndFrost(BeckerEtAl):
    """``WangAndFrost`` class.

    When the object is instanced, the method ``getSigmaP()`` calculates the
    preconsolidation pressure by the method proposed by Wang & Frost (2004)
    based on the parameters of the method. See the method documentation for
    more information.

    Attributes
    ----------
    data : Object instanced from the ``Data`` class.
        Contains the data structure from the consolidation test. See the class
        documentation for more information.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> method = WangAndFrost(data)
    >>> method.getSigmaP(range2fitCR=None)
    >>> method.sigmaP, method.ocr
    (930.59421331855, 12.407922844247334)
    >>> method.getSigmaP(range2fitCR=[700, 10000])
    >>> method.sigmaP, method.ocr
    (718.9365936678746, 9.585821248904995)
    """

    def __init__(self, data):
        """Initialize the class."""
        # Heritage BeckerEtAl class
        BeckerEtAl.__init__(self, data)
        return

    def calculateDissipatedE(self, range2fitCR=None):
        """
        Calculate the accumulative dissipated strain energy corrected (ADSEC).

        Obtain the correction value based on the linear regression on the ADSE
        line or the tangent line to the steepest point of the cubic spline that
        passes through the points.

        Parameters
        ----------
        range2fitCR : list, tuple or array (length=2), optional
            Initial and final pressures between which the first order
            polynomial will be fitted to the data on the compression range (CR)
            (range above the preconsolidation pressure). If None, the CR will
            be automatically fitted to the same points used for calculating the
            compression index with the ``Data`` class only if it was calculated
            with a linear fitted, otherwise, the steepest slope of the cubic
            spline that passes through the data will be used. The default is
            None.

        Returns
        -------
        None.

        """
        # -- Incremental total strain energy (ITSE = deltaWork)
        self.data.raw["ITSE"] = self.data.raw["deltaWork"]
        # -- Accumulative total strain energy (ATSE = work)
        self.data.raw["ATSE"] = self.data.raw["work"]
        # -- Calculating the accumulative elastic strain energy (AESE)
        self.data.raw["AESE"] = (
            self.data.raw["stress"] * self.data.idxCr / (1 + self.data.e_0)
        )
        # -- Calculating the accumulative dissipated strain energy (ADSE)
        self.data.raw["ADSE"] = self.data.raw["ATSE"] - self.data.raw["AESE"]
        # -- Calculating value for correcting ADSE (OR: corrVal)
        self.data.clean()  # Updating data without unloads
        if range2fitCR is not None or self.data.fitCc:
            s2fitTE = self.data.cleaned["stress"][self.maskCR]
            w2fitTE = self.data.cleaned["ATSE"][self.maskCR]
            self.corrVal, _ = polyfit(s2fitTE, w2fitTE, deg=1)
        else:
            cs = CubicSpline(
                x=self.data.cleaned["stress"], y=self.data.cleaned["ATSE"]
            )
            sigmaCS = np.linspace(0, self.data.cleaned["stress"].iloc[-1], 500)
            steepestSlopeIdx = np.argmax(cs(sigmaCS, 1))
            lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
            self.corrVal = (
                cs(sigmaCS[steepestSlopeIdx])
                - lcrSlope * sigmaCS[steepestSlopeIdx]
            )
        # -- Calculating the accumulative total strain energy corrected (ATSEC)
        self.data.raw["ATSEC"] = self.data.raw["ATSE"] + abs(self.corrVal)
        # -- Accumulative dissipated strain energy corrected (ADSEC)
        self.data.raw["ADSEC"] = self.data.raw["ADSE"] + abs(self.corrVal)
        # -- Updating data without unloads
        self.data.clean()
        return

    def getSigmaP(self, range2fitCR=None):
        """
        Return the value of the preconsolidation pressure.

        Parameters
        ----------
        range2fitCR : list, tuple or array (length=2), optional
            Initial and final pressures between which the first order
            polynomial will be fitted to the data on the compression range (CR)
            (range above the preconsolidation pressure). If None, the CR will
            be automatically fitted to the same points used for calculating the
            compression index with the ``Data`` class only if it was calculated
            with a linear fit, otherwise, the steepest slope of the cubic
            spline that passes through the data will be used. The default is
            None.

        Returns
        -------
        fig : matplotlib figure
            Figure with the development of the method and the results.

        """
        # -- Post yield range or compression range (CR)
        self.maskCR = np.full(len(self.data.cleaned), False)
        if range2fitCR is not None or self.data.fitCc:  # Using a linear fit
            if range2fitCR is not None:
                idxInitCR = self.data.findStressIdx(
                    stress2find=range2fitCR[0], cleanedData=True
                )
                idxEndCR = self.data.findStressIdx(
                    stress2find=range2fitCR[1], cleanedData=True
                )
                self.maskCR[idxInitCR:idxEndCR] = True
            elif self.data.fitCc:
                self.maskCR = self.data.maskCc
            # -- Calculatting the dissipated strain energy corrected (ADSEC)
            self.calculateDissipatedE(range2fitCR)
            # -- Linear regresion of points on post yield line
            sigmaCR = self.data.cleaned["stress"][self.maskCR]
            disspE = self.data.cleaned["ADSEC"][self.maskCR]
            lcrInt, lcrSlope = polyfit(sigmaCR, disspE, deg=1)
            r2CR = r2_score(
                y_true=disspE, y_pred=polyval(sigmaCR, [lcrInt, lcrSlope])
            )

        else:  # Using the steepest point of a cubic spline
            # -- Calculatting the dissipated strain energy corrected (ADSEC)
            self.calculateDissipatedE(range2fitCR)
            sigma = self.data.cleaned["stress"]
            cs = CubicSpline(x=sigma, y=self.data.cleaned["ADSEC"])
            sigmaCS = np.linspace(sigma.iloc[0], sigma.iloc[-1], 500)
            steepestSlopeIdx = np.argmax(cs(sigmaCS, 1))
            lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
            lcrInt = (
                cs(sigmaCS[steepestSlopeIdx])
                - lcrSlope * sigmaCS[steepestSlopeIdx]
            )

        # -- Preconsolitadion pressure
        self.sigmaP = (abs(self.corrVal) - lcrInt) / lcrSlope
        self.sseSigmaP = polyval(self.sigmaP, [lcrInt, lcrSlope])
        self.ocr = self.sigmaP / self.data.sigmaV

        xCR = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
        yCR = polyval(xCR, [lcrInt, lcrSlope])

        # -- Plot compresibility curve
        fig = plt.figure(figsize=figsize)
        ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        ax.plot(
            self.data.raw["stress"],
            self.data.raw["ATSEC"],
            ls=":",
            marker="v",
            lw=0.5,
            c="gray",
            mfc="w",
            label="Total energy",
        )
        ax.plot(
            self.data.raw["stress"],
            self.data.raw["AESE"],
            ls=":",
            marker="s",
            lw=0.5,
            c="gray",
            mfc="w",
            label="Elastic energy",
        )
        ax.plot(
            self.data.raw["stress"],
            self.data.raw["ADSEC"],
            ls=":",
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Dissipated energy",
        )
        ax.hlines(
            y=abs(self.corrVal),
            xmin=0,
            xmax=self.sigmaP,
            lw=1.125,
            color=colors[2],
        )
        # Compression range
        ax.plot(
            xCR, yCR, ls="-", c=colors[1], lw=1.125, label="Compression range"
        )
        if range2fitCR is not None or self.data.fitCc:
            ax.plot(
                sigmaCR,
                disspE,
                ls="",
                marker="x",
                c=colors[1],
                label=f"Data for linear fit\n(R$^2={r2CR:.3f}$)",
            )
        # Other plots
        ax.plot(
            self.data.sigmaV,
            -self.corrVal,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{v0}=$ ",
                    f"{self.data.sigmaV:.0f} kPa",
                ]
            ),
        )
        ax.plot(
            self.sigmaP,
            self.sseSigmaP,
            ls="",
            marker="o",
            c=colors[0],
            ms=7,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{p}=$ ",
                    f"{self.sigmaP:.0f} kPa\n",
                    f"OCR= {self.ocr:.1f}",
                ]
            ),
        )
        # Other details
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.set(
            ylabel="Specific strain energy [kPa]",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax.xaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.grid(False)
        ax.legend(
            bbox_to_anchor=(1.125, 0.5),
            loc=6,
            title="$\\bf{Wang\ and\ Frost\ method}$",
        )
        return fig

__init__

__init__(data)

Initialize the class.

pysigmap/energy.py
387
388
389
390
391
def __init__(self, data):
    """Initialize the class."""
    # Heritage BeckerEtAl class
    BeckerEtAl.__init__(self, data)
    return

calculateDissipatedE

calculateDissipatedE(range2fitCR=None)

Calculate the accumulative dissipated strain energy corrected (ADSEC).

Obtain the correction value based on the linear regression on the ADSE line or the tangent line to the steepest point of the cubic spline that passes through the points.

Parameters:
  • range2fitCR ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first order polynomial will be fitted to the data on the compression range (CR) (range above the preconsolidation pressure). If None, the CR will be automatically fitted to the same points used for calculating the compression index with the Data class only if it was calculated with a linear fitted, otherwise, the steepest slope of the cubic spline that passes through the data will be used. The default is None.

Returns:
  • None.
pysigmap/energy.py
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
def calculateDissipatedE(self, range2fitCR=None):
    """
    Calculate the accumulative dissipated strain energy corrected (ADSEC).

    Obtain the correction value based on the linear regression on the ADSE
    line or the tangent line to the steepest point of the cubic spline that
    passes through the points.

    Parameters
    ----------
    range2fitCR : list, tuple or array (length=2), optional
        Initial and final pressures between which the first order
        polynomial will be fitted to the data on the compression range (CR)
        (range above the preconsolidation pressure). If None, the CR will
        be automatically fitted to the same points used for calculating the
        compression index with the ``Data`` class only if it was calculated
        with a linear fitted, otherwise, the steepest slope of the cubic
        spline that passes through the data will be used. The default is
        None.

    Returns
    -------
    None.

    """
    # -- Incremental total strain energy (ITSE = deltaWork)
    self.data.raw["ITSE"] = self.data.raw["deltaWork"]
    # -- Accumulative total strain energy (ATSE = work)
    self.data.raw["ATSE"] = self.data.raw["work"]
    # -- Calculating the accumulative elastic strain energy (AESE)
    self.data.raw["AESE"] = (
        self.data.raw["stress"] * self.data.idxCr / (1 + self.data.e_0)
    )
    # -- Calculating the accumulative dissipated strain energy (ADSE)
    self.data.raw["ADSE"] = self.data.raw["ATSE"] - self.data.raw["AESE"]
    # -- Calculating value for correcting ADSE (OR: corrVal)
    self.data.clean()  # Updating data without unloads
    if range2fitCR is not None or self.data.fitCc:
        s2fitTE = self.data.cleaned["stress"][self.maskCR]
        w2fitTE = self.data.cleaned["ATSE"][self.maskCR]
        self.corrVal, _ = polyfit(s2fitTE, w2fitTE, deg=1)
    else:
        cs = CubicSpline(
            x=self.data.cleaned["stress"], y=self.data.cleaned["ATSE"]
        )
        sigmaCS = np.linspace(0, self.data.cleaned["stress"].iloc[-1], 500)
        steepestSlopeIdx = np.argmax(cs(sigmaCS, 1))
        lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
        self.corrVal = (
            cs(sigmaCS[steepestSlopeIdx])
            - lcrSlope * sigmaCS[steepestSlopeIdx]
        )
    # -- Calculating the accumulative total strain energy corrected (ATSEC)
    self.data.raw["ATSEC"] = self.data.raw["ATSE"] + abs(self.corrVal)
    # -- Accumulative dissipated strain energy corrected (ADSEC)
    self.data.raw["ADSEC"] = self.data.raw["ADSE"] + abs(self.corrVal)
    # -- Updating data without unloads
    self.data.clean()
    return

getSigmaP

getSigmaP(range2fitCR=None)

Return the value of the preconsolidation pressure.

Parameters:
  • range2fitCR ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first order polynomial will be fitted to the data on the compression range (CR) (range above the preconsolidation pressure). If None, the CR will be automatically fitted to the same points used for calculating the compression index with the Data class only if it was calculated with a linear fit, otherwise, the steepest slope of the cubic spline that passes through the data will be used. The default is None.

Returns:
  • fig( matplotlib figure ) –

    Figure with the development of the method and the results.

pysigmap/energy.py
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
def getSigmaP(self, range2fitCR=None):
    """
    Return the value of the preconsolidation pressure.

    Parameters
    ----------
    range2fitCR : list, tuple or array (length=2), optional
        Initial and final pressures between which the first order
        polynomial will be fitted to the data on the compression range (CR)
        (range above the preconsolidation pressure). If None, the CR will
        be automatically fitted to the same points used for calculating the
        compression index with the ``Data`` class only if it was calculated
        with a linear fit, otherwise, the steepest slope of the cubic
        spline that passes through the data will be used. The default is
        None.

    Returns
    -------
    fig : matplotlib figure
        Figure with the development of the method and the results.

    """
    # -- Post yield range or compression range (CR)
    self.maskCR = np.full(len(self.data.cleaned), False)
    if range2fitCR is not None or self.data.fitCc:  # Using a linear fit
        if range2fitCR is not None:
            idxInitCR = self.data.findStressIdx(
                stress2find=range2fitCR[0], cleanedData=True
            )
            idxEndCR = self.data.findStressIdx(
                stress2find=range2fitCR[1], cleanedData=True
            )
            self.maskCR[idxInitCR:idxEndCR] = True
        elif self.data.fitCc:
            self.maskCR = self.data.maskCc
        # -- Calculatting the dissipated strain energy corrected (ADSEC)
        self.calculateDissipatedE(range2fitCR)
        # -- Linear regresion of points on post yield line
        sigmaCR = self.data.cleaned["stress"][self.maskCR]
        disspE = self.data.cleaned["ADSEC"][self.maskCR]
        lcrInt, lcrSlope = polyfit(sigmaCR, disspE, deg=1)
        r2CR = r2_score(
            y_true=disspE, y_pred=polyval(sigmaCR, [lcrInt, lcrSlope])
        )

    else:  # Using the steepest point of a cubic spline
        # -- Calculatting the dissipated strain energy corrected (ADSEC)
        self.calculateDissipatedE(range2fitCR)
        sigma = self.data.cleaned["stress"]
        cs = CubicSpline(x=sigma, y=self.data.cleaned["ADSEC"])
        sigmaCS = np.linspace(sigma.iloc[0], sigma.iloc[-1], 500)
        steepestSlopeIdx = np.argmax(cs(sigmaCS, 1))
        lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
        lcrInt = (
            cs(sigmaCS[steepestSlopeIdx])
            - lcrSlope * sigmaCS[steepestSlopeIdx]
        )

    # -- Preconsolitadion pressure
    self.sigmaP = (abs(self.corrVal) - lcrInt) / lcrSlope
    self.sseSigmaP = polyval(self.sigmaP, [lcrInt, lcrSlope])
    self.ocr = self.sigmaP / self.data.sigmaV

    xCR = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
    yCR = polyval(xCR, [lcrInt, lcrSlope])

    # -- Plot compresibility curve
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    ax.plot(
        self.data.raw["stress"],
        self.data.raw["ATSEC"],
        ls=":",
        marker="v",
        lw=0.5,
        c="gray",
        mfc="w",
        label="Total energy",
    )
    ax.plot(
        self.data.raw["stress"],
        self.data.raw["AESE"],
        ls=":",
        marker="s",
        lw=0.5,
        c="gray",
        mfc="w",
        label="Elastic energy",
    )
    ax.plot(
        self.data.raw["stress"],
        self.data.raw["ADSEC"],
        ls=":",
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Dissipated energy",
    )
    ax.hlines(
        y=abs(self.corrVal),
        xmin=0,
        xmax=self.sigmaP,
        lw=1.125,
        color=colors[2],
    )
    # Compression range
    ax.plot(
        xCR, yCR, ls="-", c=colors[1], lw=1.125, label="Compression range"
    )
    if range2fitCR is not None or self.data.fitCc:
        ax.plot(
            sigmaCR,
            disspE,
            ls="",
            marker="x",
            c=colors[1],
            label=f"Data for linear fit\n(R$^2={r2CR:.3f}$)",
        )
    # Other plots
    ax.plot(
        self.data.sigmaV,
        -self.corrVal,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{v0}=$ ",
                f"{self.data.sigmaV:.0f} kPa",
            ]
        ),
    )
    ax.plot(
        self.sigmaP,
        self.sseSigmaP,
        ls="",
        marker="o",
        c=colors[0],
        ms=7,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{p}=$ ",
                f"{self.sigmaP:.0f} kPa\n",
                f"OCR= {self.ocr:.1f}",
            ]
        ),
    )
    # Other details
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set(
        ylabel="Specific strain energy [kPa]",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax.xaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.grid(False)
    ax.legend(
        bbox_to_anchor=(1.125, 0.5),
        loc=6,
        title="$\\bf{Wang\ and\ Frost\ method}$",
    )
    return fig

pysigmap.bilog

bilog.py module.

Contains the class and its methods to determine the preconsolidation pressure from a compressibility curve via the Bilogarithmic methods proposed by Butterfield (1979), Oikawa (1987) and Onitsuka et al. (1995).

Bilog

Bilog class.

When the object is instanced, the method getSigmaP() calculates the preconsolidation pressure by the methods proposed by Butterfield (1979), Oikawa (1987) and Onitsuka et al. (1995) based on the parameters of the method. See the method documentation for more information.

Attributes:
  • data (Object instanced from the ``Data`` class) –

    Contains the data structure from the oedometer test. See the class documentation for more information.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> method = Bilog(data)
>>> method.getSigmaP(opt=1)  # Butterfield (1979)
>>> method.sigmaP, method.ocr
(433.29446692547555, 5.777259559006341)
>>> method.getSigmaP(range2fitCR=[1000, 9000], opt=1) # Butterfield
>>> method.sigmaP, method.ocr
(399.3361458736937, 5.324481944982582)
>>> method.getSigmaP(opt=2)  # Oikawa (1987)
>>> method.sigmaP, method.ocr
(433.2944669254731, 5.777259559006308)
>>> method.getSigmaP(range2fitCR=[1000, 9000], opt=2)  # Oikawa
>>> method.sigmaP, method.ocr
(399.3361458736935, 5.32448194498258)
>>> method.getSigmaP(opt=3)  # Onitsuka et al. (1995)
>>> method.sigmaP, method.ocr
(433.2944669254753, 5.777259559006338)
>>> method.getSigmaP(range2fitCR=[1000, 9000], opt=3)  # Onitsuka et al.
>>> method.sigmaP, method.ocr
(399.3361458736939, 5.324481944982585)
Source code in pysigmap/bilog.py
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
class Bilog:
    """``Bilog`` class.

    When the object is instanced, the method ``getSigmaP()`` calculates the
    preconsolidation pressure by the methods proposed by Butterfield (1979),
    Oikawa (1987) and Onitsuka et al. (1995) based on the parameters of the
    method. See the method documentation for more information.

    Attributes
    ----------
    data : Object instanced from the ``Data`` class
        Contains the data structure from the oedometer test. See the class
        documentation for more information.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> method = Bilog(data)
    >>> method.getSigmaP(opt=1)  # Butterfield (1979)
    >>> method.sigmaP, method.ocr
    (433.29446692547555, 5.777259559006341)
    >>> method.getSigmaP(range2fitCR=[1000, 9000], opt=1) # Butterfield
    >>> method.sigmaP, method.ocr
    (399.3361458736937, 5.324481944982582)
    >>> method.getSigmaP(opt=2)  # Oikawa (1987)
    >>> method.sigmaP, method.ocr
    (433.2944669254731, 5.777259559006308)
    >>> method.getSigmaP(range2fitCR=[1000, 9000], opt=2)  # Oikawa
    >>> method.sigmaP, method.ocr
    (399.3361458736935, 5.32448194498258)
    >>> method.getSigmaP(opt=3)  # Onitsuka et al. (1995)
    >>> method.sigmaP, method.ocr
    (433.2944669254753, 5.777259559006338)
    >>> method.getSigmaP(range2fitCR=[1000, 9000], opt=3)  # Onitsuka et al.
    >>> method.sigmaP, method.ocr
    (399.3361458736939, 5.324481944982585)
    """

    def __init__(self, data):
        """Initialize the class."""
        self.data = data
        return

    def getSigmaP(self, range2fitRR=None, range2fitCR=None, opt=1):
        """
        Return the value of the preconsolidation pressure.

        Parameters
        ----------
        range2fitRR : list, tuple or array (length=2), optional
            Initial and final pressures between which the first order
            polynomial will be fitted to the data on the recompression range
            (RR) (range before the preconsolidation pressure). If None, the
            first order polynomial will be fitted from the first point of the
            curve to the point before the in situ effective vertical stress.
            The default is None.
        range2fitCR : list, tuple or array (length=2), optional
            Initial and final pressures between which the first order
            polynomial will be fitted to the data on the compression range (CR)
            (range above the preconsolidation pressure). If None, the CR will
            be automatically fitted to the same points used for calculating the
            compression index with the ``Data`` class only if it was calculated
            with a linear fit, otherwise, the steepest slope of the cubic
            spline that passes through the data will be used. The default is
            None.
        opt : int, optional
            Integer value to indicate which bilogarithmic method will be used.
            Butterfield (opt=1), Oikawa (opt=2) and Onitsuka et al. (opt=3).
            The default is 1.

        Returns
        -------
        fig : matplotlib.figure.Figure
            Figure with the development of the method and the results.

        """

        def transformX(x, opt=1):
            return np.log(x) if opt == 1 else np.log10(x)

        def transformY(y, opt=1):
            return np.log10(y) if opt == 2 else np.log(y)

        self.data.raw["vol"] = self.data.raw["e"] + 1
        self.data.clean()  # -- Updating data without unloads

        # def ticks(x, pos): return f'$e^{np.log(x):.0f}$'

        if range2fitRR is None:  # Indices for fitting the RR line
            idxInitRR = 1
            idxEndRR = (
                self.data.findStressIdx(
                    stress2find=self.data.sigmaV, cleanedData=True
                )
                - 1
            )
        else:
            idxInitRR = self.data.findStressIdx(
                stress2find=range2fitRR[0], cleanedData=True
            )
            idxEndRR = (
                self.data.findStressIdx(
                    stress2find=range2fitRR[1], cleanedData=True
                )
                - 1
            )

        # -- Linear regresion of points on the recompression range(RR)
        sigmaRR = self.data.cleaned["stress"][idxInitRR : idxEndRR + 1]
        volRR = self.data.cleaned["vol"][idxInitRR : idxEndRR + 1]
        sigmaRRlog = transformX(sigmaRR, opt)
        volRRlog = transformY(volRR, opt)
        p1_0, p1_1 = polyfit(sigmaRRlog, volRRlog, deg=1)
        r2RR = r2_score(
            y_true=volRRlog, y_pred=polyval(sigmaRRlog, [p1_0, p1_1])
        )

        # -- Cubic spline that passes through the data
        sigmaLog = transformX(self.data.cleaned["stress"][1:], opt)
        volLog = transformY(self.data.cleaned["vol"][1:], opt)
        cs = CubicSpline(x=sigmaLog, y=volLog)
        # Specific volume at sigma V
        # volSigmaV = cs(transformX(self.data.sigmaV, opt))
        interpolator = interp1d(x=sigmaLog, y=volLog)
        volSigmaV = interpolator(transformX(self.data.sigmaV, opt))

        # -- Compression range (CR)
        self.maskCR = np.full(len(self.data.cleaned), False)
        if range2fitCR is not None or self.data.fitCc:  # Using a linear fit
            if range2fitCR is not None:
                idxInitCR = self.data.findStressIdx(
                    stress2find=range2fitCR[0], cleanedData=True
                )
                idxEndCR = self.data.findStressIdx(
                    stress2find=range2fitCR[1], cleanedData=True
                )
                self.maskCR[idxInitCR:idxEndCR] = True
            elif self.data.fitCc:
                self.maskCR = self.data.maskCc
            # -- Linear regresion of points on post yield line
            sigmaCR = self.data.cleaned["stress"][self.maskCR]
            sigmaCRlog = transformX(sigmaCR, opt)
            volCR = self.data.cleaned["vol"][self.maskCR]
            volCRlog = transformY(volCR, opt)
            lcrInt, lcrSlope = polyfit(sigmaCRlog, volCRlog, deg=1)
            r2CR = r2_score(
                y_true=volCRlog, y_pred=polyval(sigmaCRlog, [lcrInt, lcrSlope])
            )

        else:  # Using the steepest point of a cubic spline
            sigmaCS = np.linspace(sigmaLog.iloc[0], sigmaLog.iloc[-1], 500)
            steepestSlopeIdx = np.argmin(cs(sigmaCS, 1))
            lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
            lcrInt = (
                cs(sigmaCS[steepestSlopeIdx])
                - lcrSlope * sigmaCS[steepestSlopeIdx]
            )

        xScl = np.e if opt == 1 else 10  # log scale for plotting
        yLabel = "$\log_{10} (1+e)$" if opt == 2 else "$\ln (1+e)$"

        # -- Preconsolitadion pressure
        self.sigmaP = xScl ** ((lcrInt - p1_0) / (p1_1 - lcrSlope))
        self.vSigmaP = polyval(transformX(self.sigmaP, opt), [p1_0, p1_1])
        self.ocr = self.sigmaP / self.data.sigmaV

        # -- Lines of the bilogarithmic methods
        xCR = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
        yCR = polyval(transformX(xCR, opt), [lcrInt, lcrSlope])

        # Recompression range
        xRRFit = np.linspace(sigmaRR.iloc[0], self.sigmaP)
        yRRFit = polyval(transformX(xRRFit, opt), [p1_0, p1_1])

        # -- plot compresibility curve
        fig = plt.figure(figsize=figsize)
        ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        ax.semilogx(
            self.data.raw["stress"][1:],
            transformY(self.data.raw["vol"][1:], opt),
            base=xScl,
            ls=(0, (1, 1)),
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Compressibility curve",
        )
        methods = ["Butterfield", "Oikawa", "Onitsuka\ et\ al."]
        # Recompression range
        ax.plot(
            xRRFit,
            yRRFit,
            ls="-",
            c=colors[2],
            lw=1.125,
            label="Recompression range",
        )
        ax.plot(
            sigmaRR,
            volRRlog,
            ls="",
            marker="+",
            c=colors[2],
            label=f"Data for linear fit\n(R$^2={r2RR:.3f}$)",
        )
        # Compression range
        ax.plot(
            xCR, yCR, ls="-", c=colors[1], lw=1.125, label="Compression range"
        )
        if range2fitCR is not None or self.data.fitCc:
            ax.plot(
                sigmaCR,
                volCRlog,
                ls="",
                marker="x",
                c=colors[1],
                label=f"Data for linear fit\n(R$^2={r2CR:.3f}$)",
            )
        # Other plots
        ax.plot(
            self.data.sigmaV,
            volSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{v0}=$ ",
                    f"{self.data.sigmaV:.0f} kPa",
                ]
            ),
        )
        ax.plot(
            self.sigmaP,
            self.vSigmaP,
            ls="",
            marker="o",
            c=colors[0],
            ms=7,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{p}=$ ",
                    f"{self.sigmaP:.0f} kPa\n",
                    f"OCR= {self.ocr:.1f}",
                ]
            ),
        )
        # Other details
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.set(
            ylabel=f"Specific volume, {yLabel}",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
        # ax.xaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.grid(False)
        ax.legend(
            bbox_to_anchor=(1.125, 0.5),
            loc=6,
            title=str().join(["$\\bf{", f"{methods[opt-1]}", "\ method}$"]),
        )
        return fig

__init__

__init__(data)

Initialize the class.

pysigmap/bilog.py
74
75
76
77
def __init__(self, data):
    """Initialize the class."""
    self.data = data
    return

getSigmaP

getSigmaP(range2fitRR=None, range2fitCR=None, opt=1)

Return the value of the preconsolidation pressure.

Parameters:
  • range2fitRR ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first order polynomial will be fitted to the data on the recompression range (RR) (range before the preconsolidation pressure). If None, the first order polynomial will be fitted from the first point of the curve to the point before the in situ effective vertical stress. The default is None.

  • range2fitCR ((list, tuple or array(length=2)), default: None ) –

    Initial and final pressures between which the first order polynomial will be fitted to the data on the compression range (CR) (range above the preconsolidation pressure). If None, the CR will be automatically fitted to the same points used for calculating the compression index with the Data class only if it was calculated with a linear fit, otherwise, the steepest slope of the cubic spline that passes through the data will be used. The default is None.

  • opt (int, default: 1 ) –

    Integer value to indicate which bilogarithmic method will be used. Butterfield (opt=1), Oikawa (opt=2) and Onitsuka et al. (opt=3). The default is 1.

Returns:
  • fig( Figure ) –

    Figure with the development of the method and the results.

pysigmap/bilog.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
def getSigmaP(self, range2fitRR=None, range2fitCR=None, opt=1):
    """
    Return the value of the preconsolidation pressure.

    Parameters
    ----------
    range2fitRR : list, tuple or array (length=2), optional
        Initial and final pressures between which the first order
        polynomial will be fitted to the data on the recompression range
        (RR) (range before the preconsolidation pressure). If None, the
        first order polynomial will be fitted from the first point of the
        curve to the point before the in situ effective vertical stress.
        The default is None.
    range2fitCR : list, tuple or array (length=2), optional
        Initial and final pressures between which the first order
        polynomial will be fitted to the data on the compression range (CR)
        (range above the preconsolidation pressure). If None, the CR will
        be automatically fitted to the same points used for calculating the
        compression index with the ``Data`` class only if it was calculated
        with a linear fit, otherwise, the steepest slope of the cubic
        spline that passes through the data will be used. The default is
        None.
    opt : int, optional
        Integer value to indicate which bilogarithmic method will be used.
        Butterfield (opt=1), Oikawa (opt=2) and Onitsuka et al. (opt=3).
        The default is 1.

    Returns
    -------
    fig : matplotlib.figure.Figure
        Figure with the development of the method and the results.

    """

    def transformX(x, opt=1):
        return np.log(x) if opt == 1 else np.log10(x)

    def transformY(y, opt=1):
        return np.log10(y) if opt == 2 else np.log(y)

    self.data.raw["vol"] = self.data.raw["e"] + 1
    self.data.clean()  # -- Updating data without unloads

    # def ticks(x, pos): return f'$e^{np.log(x):.0f}$'

    if range2fitRR is None:  # Indices for fitting the RR line
        idxInitRR = 1
        idxEndRR = (
            self.data.findStressIdx(
                stress2find=self.data.sigmaV, cleanedData=True
            )
            - 1
        )
    else:
        idxInitRR = self.data.findStressIdx(
            stress2find=range2fitRR[0], cleanedData=True
        )
        idxEndRR = (
            self.data.findStressIdx(
                stress2find=range2fitRR[1], cleanedData=True
            )
            - 1
        )

    # -- Linear regresion of points on the recompression range(RR)
    sigmaRR = self.data.cleaned["stress"][idxInitRR : idxEndRR + 1]
    volRR = self.data.cleaned["vol"][idxInitRR : idxEndRR + 1]
    sigmaRRlog = transformX(sigmaRR, opt)
    volRRlog = transformY(volRR, opt)
    p1_0, p1_1 = polyfit(sigmaRRlog, volRRlog, deg=1)
    r2RR = r2_score(
        y_true=volRRlog, y_pred=polyval(sigmaRRlog, [p1_0, p1_1])
    )

    # -- Cubic spline that passes through the data
    sigmaLog = transformX(self.data.cleaned["stress"][1:], opt)
    volLog = transformY(self.data.cleaned["vol"][1:], opt)
    cs = CubicSpline(x=sigmaLog, y=volLog)
    # Specific volume at sigma V
    # volSigmaV = cs(transformX(self.data.sigmaV, opt))
    interpolator = interp1d(x=sigmaLog, y=volLog)
    volSigmaV = interpolator(transformX(self.data.sigmaV, opt))

    # -- Compression range (CR)
    self.maskCR = np.full(len(self.data.cleaned), False)
    if range2fitCR is not None or self.data.fitCc:  # Using a linear fit
        if range2fitCR is not None:
            idxInitCR = self.data.findStressIdx(
                stress2find=range2fitCR[0], cleanedData=True
            )
            idxEndCR = self.data.findStressIdx(
                stress2find=range2fitCR[1], cleanedData=True
            )
            self.maskCR[idxInitCR:idxEndCR] = True
        elif self.data.fitCc:
            self.maskCR = self.data.maskCc
        # -- Linear regresion of points on post yield line
        sigmaCR = self.data.cleaned["stress"][self.maskCR]
        sigmaCRlog = transformX(sigmaCR, opt)
        volCR = self.data.cleaned["vol"][self.maskCR]
        volCRlog = transformY(volCR, opt)
        lcrInt, lcrSlope = polyfit(sigmaCRlog, volCRlog, deg=1)
        r2CR = r2_score(
            y_true=volCRlog, y_pred=polyval(sigmaCRlog, [lcrInt, lcrSlope])
        )

    else:  # Using the steepest point of a cubic spline
        sigmaCS = np.linspace(sigmaLog.iloc[0], sigmaLog.iloc[-1], 500)
        steepestSlopeIdx = np.argmin(cs(sigmaCS, 1))
        lcrSlope = cs(sigmaCS, 1)[steepestSlopeIdx]
        lcrInt = (
            cs(sigmaCS[steepestSlopeIdx])
            - lcrSlope * sigmaCS[steepestSlopeIdx]
        )

    xScl = np.e if opt == 1 else 10  # log scale for plotting
    yLabel = "$\log_{10} (1+e)$" if opt == 2 else "$\ln (1+e)$"

    # -- Preconsolitadion pressure
    self.sigmaP = xScl ** ((lcrInt - p1_0) / (p1_1 - lcrSlope))
    self.vSigmaP = polyval(transformX(self.sigmaP, opt), [p1_0, p1_1])
    self.ocr = self.sigmaP / self.data.sigmaV

    # -- Lines of the bilogarithmic methods
    xCR = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
    yCR = polyval(transformX(xCR, opt), [lcrInt, lcrSlope])

    # Recompression range
    xRRFit = np.linspace(sigmaRR.iloc[0], self.sigmaP)
    yRRFit = polyval(transformX(xRRFit, opt), [p1_0, p1_1])

    # -- plot compresibility curve
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    ax.semilogx(
        self.data.raw["stress"][1:],
        transformY(self.data.raw["vol"][1:], opt),
        base=xScl,
        ls=(0, (1, 1)),
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Compressibility curve",
    )
    methods = ["Butterfield", "Oikawa", "Onitsuka\ et\ al."]
    # Recompression range
    ax.plot(
        xRRFit,
        yRRFit,
        ls="-",
        c=colors[2],
        lw=1.125,
        label="Recompression range",
    )
    ax.plot(
        sigmaRR,
        volRRlog,
        ls="",
        marker="+",
        c=colors[2],
        label=f"Data for linear fit\n(R$^2={r2RR:.3f}$)",
    )
    # Compression range
    ax.plot(
        xCR, yCR, ls="-", c=colors[1], lw=1.125, label="Compression range"
    )
    if range2fitCR is not None or self.data.fitCc:
        ax.plot(
            sigmaCR,
            volCRlog,
            ls="",
            marker="x",
            c=colors[1],
            label=f"Data for linear fit\n(R$^2={r2CR:.3f}$)",
        )
    # Other plots
    ax.plot(
        self.data.sigmaV,
        volSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{v0}=$ ",
                f"{self.data.sigmaV:.0f} kPa",
            ]
        ),
    )
    ax.plot(
        self.sigmaP,
        self.vSigmaP,
        ls="",
        marker="o",
        c=colors[0],
        ms=7,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{p}=$ ",
                f"{self.sigmaP:.0f} kPa\n",
                f"OCR= {self.ocr:.1f}",
            ]
        ),
    )
    # Other details
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set(
        ylabel=f"Specific volume, {yLabel}",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
    # ax.xaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.grid(False)
    ax.legend(
        bbox_to_anchor=(1.125, 0.5),
        loc=6,
        title=str().join(["$\\bf{", f"{methods[opt-1]}", "\ method}$"]),
    )
    return fig

pysigmap.pachecosilva

pachecosilva.py module.

Contains the class and its methods to determine the preconsolidation pressure from a compressibility curve via the method proposed by Pacheco Silva (1970).

PachecoSilva

PachecoSilva class.

When the object is instanced, the method getSigmaP() calculates the preconsolidation pressure by the method proposed by Pacheco Silva (1970) based on the compression index obtained with the Data class.

Attributes:
  • data (Object instanced from the ``Data`` class.) –

    Contains the data structure from the oedometer test. See the class documentation for more information.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> method = PachecoSilva(data)
>>> method.getSigmaP()
>>> method.sigmaP, method.ocr
(330.63741795901075, 4.408498906120143)
Source code in pysigmap/pachecosilva.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
class PachecoSilva:
    """``PachecoSilva`` class.

    When the object is instanced, the method ``getSigmaP()`` calculates the
    preconsolidation pressure by the method proposed by Pacheco Silva (1970)
    based on the compression index obtained with the ``Data`` class.

    Attributes
    ----------
    data : Object instanced from the ``Data`` class.
        Contains the data structure from the oedometer test. See the class
        documentation for more information.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> method = PachecoSilva(data)
    >>> method.getSigmaP()
    >>> method.sigmaP, method.ocr
    (330.63741795901075, 4.408498906120143)
    """

    def __init__(self, data):
        """Initialize the class."""
        self.data = data
        return

    def getSigmaP(self):
        """
        Return the value of the preconsolidation pressure.

        Returns
        -------
        fig : matplotlib figure
            Figure with the development of the method and the results.

        """
        # -- Cubic spline that passes through the data
        sigmaLog = np.log10(self.data.cleaned["stress"][1:])
        cs = CubicSpline(x=sigmaLog, y=self.data.cleaned["e"][1:])
        interpolator = interp1d(x=sigmaLog, y=self.data.cleaned["e"][1:])

        # -- Lines of the Pacheco Silva's method
        # Intersection: NCL - e_0
        xPt1 = 10 ** ((self.data.e_0 - self.data.idxCcInt) / -self.data.idxCc)
        # Intersection: first vertical - compressibility curve
        # yPt2 = cs(np.log10(xPt1))
        yPt2 = interpolator(np.log10(xPt1))
        # Intersection: first horizontal - NCL (Preconsolidation pressure)
        self.sigmaP = 10 ** ((yPt2 - self.data.idxCcInt) / -self.data.idxCc)
        self.ocr = self.sigmaP / self.data.sigmaV
        self.eSigmaP = yPt2

        # -- plotting
        fig = plt.figure(figsize=figsize)
        ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        ax.plot(
            self.data.raw["stress"][1:],
            self.data.raw["e"][1:],
            ls=(0, (1, 1)),
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Compressibility curve",
        )
        ax.hlines(
            y=self.data.e_0,
            xmin=0,
            xmax=xPt1,
            ls="--",
            lw=1.125,
            color=colors[2],
            label=f"$e_0 = {self.data.e_0:.3f}$",
        )
        # Compression index
        x4Cc = np.linspace(xPt1, self.data.cleaned["stress"].iloc[-1])
        y4Cc = -self.data.idxCc * np.log10(x4Cc) + self.data.idxCcInt
        ax.plot(
            x4Cc,
            y4Cc,
            ls="-",
            lw=1.125,
            color=colors[1],
            label=str().join(["$C_\mathrm{c}=$", f"{self.data.idxCc:.3f}"]),
        )
        if self.data.fitCc:
            ax.plot(
                self.data.cleaned["stress"].iloc[self.data.maskCc],
                self.data.cleaned["e"].iloc[self.data.maskCc],
                ls="",
                marker="x",
                lw=1.5,
                color=colors[1],
                label=f"Data for linear fit\n(R$^2={self.data.r2Cc:.3f}$)",
            )
        # Lines of the Pacheco-Silva's method
        ax.vlines(
            x=xPt1, ymin=yPt2, ymax=self.data.e_0, lw=1.125, color="k", ls="--"
        )
        ax.hlines(
            y=yPt2, xmin=xPt1, xmax=self.sigmaP, lw=1.125, color="k", ls="--"
        )
        # ax.vlines(x=self.sigmaP, ymin=self.data.raw['e'].min(), ymax=yPt2,
        #           lw=1.5, color='k', ls='-.')
        # Other plots
        ax.plot(
            self.data.sigmaV,
            self.data.eSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{v_0}=$ ",
                    f"{self.data.sigmaV:.0f} kPa",
                ]
            ),
        )
        ax.plot(
            self.sigmaP,
            self.eSigmaP,
            ls="",
            marker="o",
            c=colors[0],
            ms=7,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{p}=$ ",
                    f"{self.sigmaP:.0f} kPa\n",
                    f"OCR= {self.ocr:.1f}",
                ]
            ),
        )
        # Other details
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.set(
            xscale="log",
            ylabel="Void ratio, $e$",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
        ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.grid(False)
        ax.legend(
            bbox_to_anchor=(1.125, 0.5),
            loc=6,
            title="$\\bf{Pacheco\ Silva\ method}$",
        )
        return fig

__init__

__init__(data)

Initialize the class.

pysigmap/pachecosilva.py
54
55
56
57
def __init__(self, data):
    """Initialize the class."""
    self.data = data
    return

getSigmaP

getSigmaP()

Return the value of the preconsolidation pressure.

Returns:
  • fig( matplotlib figure ) –

    Figure with the development of the method and the results.

pysigmap/pachecosilva.py
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def getSigmaP(self):
    """
    Return the value of the preconsolidation pressure.

    Returns
    -------
    fig : matplotlib figure
        Figure with the development of the method and the results.

    """
    # -- Cubic spline that passes through the data
    sigmaLog = np.log10(self.data.cleaned["stress"][1:])
    cs = CubicSpline(x=sigmaLog, y=self.data.cleaned["e"][1:])
    interpolator = interp1d(x=sigmaLog, y=self.data.cleaned["e"][1:])

    # -- Lines of the Pacheco Silva's method
    # Intersection: NCL - e_0
    xPt1 = 10 ** ((self.data.e_0 - self.data.idxCcInt) / -self.data.idxCc)
    # Intersection: first vertical - compressibility curve
    # yPt2 = cs(np.log10(xPt1))
    yPt2 = interpolator(np.log10(xPt1))
    # Intersection: first horizontal - NCL (Preconsolidation pressure)
    self.sigmaP = 10 ** ((yPt2 - self.data.idxCcInt) / -self.data.idxCc)
    self.ocr = self.sigmaP / self.data.sigmaV
    self.eSigmaP = yPt2

    # -- plotting
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    ax.plot(
        self.data.raw["stress"][1:],
        self.data.raw["e"][1:],
        ls=(0, (1, 1)),
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Compressibility curve",
    )
    ax.hlines(
        y=self.data.e_0,
        xmin=0,
        xmax=xPt1,
        ls="--",
        lw=1.125,
        color=colors[2],
        label=f"$e_0 = {self.data.e_0:.3f}$",
    )
    # Compression index
    x4Cc = np.linspace(xPt1, self.data.cleaned["stress"].iloc[-1])
    y4Cc = -self.data.idxCc * np.log10(x4Cc) + self.data.idxCcInt
    ax.plot(
        x4Cc,
        y4Cc,
        ls="-",
        lw=1.125,
        color=colors[1],
        label=str().join(["$C_\mathrm{c}=$", f"{self.data.idxCc:.3f}"]),
    )
    if self.data.fitCc:
        ax.plot(
            self.data.cleaned["stress"].iloc[self.data.maskCc],
            self.data.cleaned["e"].iloc[self.data.maskCc],
            ls="",
            marker="x",
            lw=1.5,
            color=colors[1],
            label=f"Data for linear fit\n(R$^2={self.data.r2Cc:.3f}$)",
        )
    # Lines of the Pacheco-Silva's method
    ax.vlines(
        x=xPt1, ymin=yPt2, ymax=self.data.e_0, lw=1.125, color="k", ls="--"
    )
    ax.hlines(
        y=yPt2, xmin=xPt1, xmax=self.sigmaP, lw=1.125, color="k", ls="--"
    )
    # ax.vlines(x=self.sigmaP, ymin=self.data.raw['e'].min(), ymax=yPt2,
    #           lw=1.5, color='k', ls='-.')
    # Other plots
    ax.plot(
        self.data.sigmaV,
        self.data.eSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{v_0}=$ ",
                f"{self.data.sigmaV:.0f} kPa",
            ]
        ),
    )
    ax.plot(
        self.sigmaP,
        self.eSigmaP,
        ls="",
        marker="o",
        c=colors[0],
        ms=7,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{p}=$ ",
                f"{self.sigmaP:.0f} kPa\n",
                f"OCR= {self.ocr:.1f}",
            ]
        ),
    )
    # Other details
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set(
        xscale="log",
        ylabel="Void ratio, $e$",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
    ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.grid(False)
    ax.legend(
        bbox_to_anchor=(1.125, 0.5),
        loc=6,
        title="$\\bf{Pacheco\ Silva\ method}$",
    )
    return fig

pysigmap.boone

boone.py module.

Contains the class and its methods to determine the preconsolidation pressure from a compressibility curve via the method proposed by Boone (2010).

Boone

Boone class.

When the object is instanced, the method getSigmaP() calculates the preconsolidation pressure by the method proposed by Boone (2010) based on the compression and recompression indices obtained with the Data class.

Attributes:
  • data (Object instanced from the ``Data`` class.) –

    Contains the data structure from the oedometer test. See the class documentation for more information.

>>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
>>>                   'data4testing/main/pysigmap/testData.csv'])
>>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
>>> method = Boone(data)
>>> method.getSigmaP()
>>> method.sigmaP, method.ocr
(384.38457280143547, 5.125127637352473)
Source code in pysigmap/boone.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
class Boone:
    """``Boone`` class.

    When the object is instanced, the method ``getSigmaP()`` calculates the
    preconsolidation pressure by the method proposed by Boone (2010) based on
    the compression and recompression indices obtained with the ``Data`` class.

    Attributes
    ----------
    data : Object instanced from the ``Data`` class.
        Contains the data structure from the oedometer test. See the class
        documentation for more information.

    Examples
    --------
    >>> urlCSV = ''.join(['https://raw.githubusercontent.com/eamontoyaa/',
    >>>                   'data4testing/main/pysigmap/testData.csv'])
    >>> data = Data(pd.read_csv(urlCSV), sigmaV=75)
    >>> method = Boone(data)
    >>> method.getSigmaP()
    >>> method.sigmaP, method.ocr
    (384.38457280143547, 5.125127637352473)
    """

    def __init__(self, data):
        """Initialize the class."""
        self.data = data
        return

    def getSigmaP(self):
        """
        Return the value of the preconsolidation pressure.

        Returns
        -------
        fig : matplotlib figure
            Figure with the development of the method and the results.
        """
        # Intercept of the line parallel to Cr passing through (σ_v0, e_σ_v0)
        idxCr2Int = self.data.eSigmaV + self.data.idxCr * np.log10(
            self.data.sigmaV
        )
        # Intersection of Line parallel to Cr2 - Cc (Preconsolidation pressure)
        self.sigmaP = 10 ** (
            (self.data.idxCcInt - idxCr2Int)
            / (-self.data.idxCr + self.data.idxCc)
        )
        self.eSigmaP = polyval(
            np.log10(self.sigmaP), [self.data.idxCcInt, -self.data.idxCc]
        )
        self.ocr = self.sigmaP / self.data.sigmaV

        # -- plotting
        fig = plt.figure(figsize=figsize)
        ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
        ax.plot(
            self.data.raw["stress"][1:],
            self.data.raw["e"][1:],
            ls=(0, (1, 1)),
            marker="o",
            lw=1.5,
            c="k",
            mfc="w",
            label="Compressibility curve",
        )
        # Compression index (Cc)
        x4Cc = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
        y4Cc = -self.data.idxCc * np.log10(x4Cc) + self.data.idxCcInt
        ax.plot(
            x4Cc,
            y4Cc,
            ls="-",
            lw=1.125,
            color=colors[1],
            label=str().join(["$C_\mathrm{c}=$", f"{self.data.idxCc:.3f}"]),
        )
        if self.data.fitCc:
            ax.plot(
                self.data.cleaned["stress"].iloc[self.data.maskCc],
                self.data.cleaned["e"].iloc[self.data.maskCc],
                ls="",
                marker="x",
                color=colors[1],
                label=f"Data for linear fit\n(R$^2={self.data.r2Cc:.3f}$)",
            )
        # Recompression index (Cr)
        x4Cr = np.linspace(
            self.data.raw["stress"].iloc[self.data.maskCr].min(),
            self.data.raw["stress"].iloc[self.data.maskCr].max(),
        )
        y4Cr = -self.data.idxCr * np.log10(x4Cr) + self.data.idxCrInt
        ax.plot(
            x4Cr,
            y4Cr,
            ls="-",
            lw=1.125,
            color=colors[2],
            label=str().join(["$C_\mathrm{r}=$", f"{self.data.idxCr:.3f}"]),
        )
        ax.plot(
            self.data.raw["stress"].iloc[self.data.maskCr],
            self.data.raw["e"].iloc[self.data.maskCr],
            ls="",
            marker="+",
            color=colors[2],
            label=f"Data for linear fit\n(R$^2={self.data.r2Cr:.3f}$)",
        )
        # Line pararel to Cr
        x4Cr2 = np.linspace(self.data.cleaned["stress"].iloc[1], self.sigmaP)
        y4Cr2 = polyval(np.log10(x4Cr2), [idxCr2Int, -self.data.idxCr])
        ax.plot(
            x4Cr2,
            y4Cr2,
            ls="--",
            c=colors[2],
            lw=1.125,
            label="Parallel line to $C_\\mathrm{r}$",
        )
        # Other plots
        ax.plot(
            self.data.sigmaV,
            self.data.eSigmaV,
            ls="",
            marker="|",
            c="r",
            ms=15,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{v_0}=$ ",
                    f"{self.data.sigmaV:.0f} kPa",
                ]
            ),
        )
        ax.plot(
            self.sigmaP,
            self.eSigmaP,
            ls="",
            marker="o",
            c=colors[0],
            ms=7,
            mfc="w",
            mew=1.5,
            label=str().join(
                [
                    "$\sigma^\prime_\mathrm{p}=$ ",
                    f"{self.sigmaP:.0f} kPa\n",
                    f"OCR= {self.ocr:.1f}",
                ]
            ),
        )
        # Other details
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.set(
            xscale="log",
            ylabel="Void ratio, $e$",
            xlabel=str().join(
                [
                    "Effective vertical stress, ",
                    "$\sigma^\prime_\mathrm{v}$ [kPa]",
                ]
            ),
        )
        ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
        ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
        ax.grid(False)
        ax.legend(
            bbox_to_anchor=(1.125, 0.5), loc=6, title="$\\bf{Boone\ method}$"
        )
        return fig

__init__

__init__(data)

Initialize the class.

pysigmap/boone.py
54
55
56
57
def __init__(self, data):
    """Initialize the class."""
    self.data = data
    return

getSigmaP

getSigmaP()

Return the value of the preconsolidation pressure.

Returns:
  • fig( matplotlib figure ) –

    Figure with the development of the method and the results.

pysigmap/boone.py
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def getSigmaP(self):
    """
    Return the value of the preconsolidation pressure.

    Returns
    -------
    fig : matplotlib figure
        Figure with the development of the method and the results.
    """
    # Intercept of the line parallel to Cr passing through (σ_v0, e_σ_v0)
    idxCr2Int = self.data.eSigmaV + self.data.idxCr * np.log10(
        self.data.sigmaV
    )
    # Intersection of Line parallel to Cr2 - Cc (Preconsolidation pressure)
    self.sigmaP = 10 ** (
        (self.data.idxCcInt - idxCr2Int)
        / (-self.data.idxCr + self.data.idxCc)
    )
    self.eSigmaP = polyval(
        np.log10(self.sigmaP), [self.data.idxCcInt, -self.data.idxCc]
    )
    self.ocr = self.sigmaP / self.data.sigmaV

    # -- plotting
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0.08, 0.12, 0.55, 0.85])
    ax.plot(
        self.data.raw["stress"][1:],
        self.data.raw["e"][1:],
        ls=(0, (1, 1)),
        marker="o",
        lw=1.5,
        c="k",
        mfc="w",
        label="Compressibility curve",
    )
    # Compression index (Cc)
    x4Cc = np.linspace(self.sigmaP, self.data.cleaned["stress"].iloc[-1])
    y4Cc = -self.data.idxCc * np.log10(x4Cc) + self.data.idxCcInt
    ax.plot(
        x4Cc,
        y4Cc,
        ls="-",
        lw=1.125,
        color=colors[1],
        label=str().join(["$C_\mathrm{c}=$", f"{self.data.idxCc:.3f}"]),
    )
    if self.data.fitCc:
        ax.plot(
            self.data.cleaned["stress"].iloc[self.data.maskCc],
            self.data.cleaned["e"].iloc[self.data.maskCc],
            ls="",
            marker="x",
            color=colors[1],
            label=f"Data for linear fit\n(R$^2={self.data.r2Cc:.3f}$)",
        )
    # Recompression index (Cr)
    x4Cr = np.linspace(
        self.data.raw["stress"].iloc[self.data.maskCr].min(),
        self.data.raw["stress"].iloc[self.data.maskCr].max(),
    )
    y4Cr = -self.data.idxCr * np.log10(x4Cr) + self.data.idxCrInt
    ax.plot(
        x4Cr,
        y4Cr,
        ls="-",
        lw=1.125,
        color=colors[2],
        label=str().join(["$C_\mathrm{r}=$", f"{self.data.idxCr:.3f}"]),
    )
    ax.plot(
        self.data.raw["stress"].iloc[self.data.maskCr],
        self.data.raw["e"].iloc[self.data.maskCr],
        ls="",
        marker="+",
        color=colors[2],
        label=f"Data for linear fit\n(R$^2={self.data.r2Cr:.3f}$)",
    )
    # Line pararel to Cr
    x4Cr2 = np.linspace(self.data.cleaned["stress"].iloc[1], self.sigmaP)
    y4Cr2 = polyval(np.log10(x4Cr2), [idxCr2Int, -self.data.idxCr])
    ax.plot(
        x4Cr2,
        y4Cr2,
        ls="--",
        c=colors[2],
        lw=1.125,
        label="Parallel line to $C_\\mathrm{r}$",
    )
    # Other plots
    ax.plot(
        self.data.sigmaV,
        self.data.eSigmaV,
        ls="",
        marker="|",
        c="r",
        ms=15,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{v_0}=$ ",
                f"{self.data.sigmaV:.0f} kPa",
            ]
        ),
    )
    ax.plot(
        self.sigmaP,
        self.eSigmaP,
        ls="",
        marker="o",
        c=colors[0],
        ms=7,
        mfc="w",
        mew=1.5,
        label=str().join(
            [
                "$\sigma^\prime_\mathrm{p}=$ ",
                f"{self.sigmaP:.0f} kPa\n",
                f"OCR= {self.ocr:.1f}",
            ]
        ),
    )
    # Other details
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set(
        xscale="log",
        ylabel="Void ratio, $e$",
        xlabel=str().join(
            [
                "Effective vertical stress, ",
                "$\sigma^\prime_\mathrm{v}$ [kPa]",
            ]
        ),
    )
    ax.xaxis.set_major_formatter(mtick.ScalarFormatter())
    ax.yaxis.set_minor_locator(mtick.AutoMinorLocator())
    ax.grid(False)
    ax.legend(
        bbox_to_anchor=(1.125, 0.5), loc=6, title="$\\bf{Boone\ method}$"
    )
    return fig