Skip to content

Shear

__init__(self, folder, id_row=None, stress_col=None, strain_col=None, header=0, strain1=0.05, strain2=0.15, expect_yield=True) special

Tensile test class.

Parameters:

Name Type Description Default
folder Union[pathlib.Path, str]

String or Path-like folder containing test data.

required
id_row Optional[int]

Row number of the specimen ID. Most test machines export a headed csv file with some metadata like date, test method name etc. Specimen ID should be contained in this section. If not passed, pymechtest will use the filename as the specimen ID.

None
stress_col Optional[str]

Name of the column containing stress data. If not passed, pymechtest will try to autodetect it from your data.

None
strain_col Optional[str]

Name of the column containing strain data. If not passed, pymechtest will try to autodetect it from your data.

None
header int

0-indexed row number of the table header (i.e. the row containing things like "Stress", "Strain", "Load" etc.). Defaults to 0.

0
strain1 float

Lower strain bound for modulus calculation. Must be in %. Defaults to 0.05.

0.05
strain2 float

Upper strain bound for modulus calculation. Must be in %. Defaults to 0.15.

0.15
expect_yield bool

Whether the specimens are expected to be elastic to failure (False) or they are expected to have a yield strength (True). Defaults to True.

True
Source code in pymechtest/shear.py
def __init__(
    self,
    folder: Union[Path, str],
    id_row: Optional[int] = None,
    stress_col: Optional[str] = None,
    strain_col: Optional[str] = None,
    header: int = 0,
    strain1: float = 0.05,
    strain2: float = 0.15,
    expect_yield: bool = True,
) -> None:
    """
    Tensile test class.

    Args:
        folder (Union[Path, str]): String or Path-like folder containing
            test data.

        id_row (int, optional): Row number of the specimen ID.
            Most test machines export a headed csv file with some
            metadata like date, test method name etc. Specimen ID
            should be contained in this section. If not passed, pymechtest
            will use the filename as the specimen ID.

        stress_col (str, optional): Name of the column containing stress data.
            If not passed, pymechtest will try to autodetect it from your data.

        strain_col (str, optional): Name of the column containing strain data.
            If not passed, pymechtest will try to autodetect it from your data.

        header (int, optional): 0-indexed row number of the table header
            (i.e. the row containing things like "Stress", "Strain", "Load" etc.).
            Defaults to 0.

        strain1 (float, optional): Lower strain bound for modulus calculation.
            Must be in %. Defaults to 0.05.

        strain2 (float, optional): Upper strain bound for modulus calculation.
            Must be in %. Defaults to 0.15.

        expect_yield (bool, optional): Whether the specimens are expected to be
            elastic to failure (False) or they are expected to have a
            yield strength (True). Defaults to True.
    """
    super().__init__(
        folder=folder,
        id_row=id_row,
        stress_col=stress_col,
        strain_col=strain_col,
        header=header,
        strain1=strain1,
        strain2=strain2,
        expect_yield=expect_yield,
    )

load_all(self) inherited

Loads all the found files in 'folder' into a dataframe.

Recursively searches 'folder' for all csv files, grabs the specimen identifier specified during 'id_row' and includes this in the dataframe.

Returns:

Type Description
DataFrame

pd.DataFrame: All found test data with specimen identifier.

Source code in pymechtest/shear.py
def load_all(self) -> pd.DataFrame:
    """
    Loads all the found files in 'folder' into a dataframe.

    Recursively searches 'folder' for all csv files, grabs the specimen identifier
    specified during 'id_row' and includes this in the dataframe.

    Returns:
        pd.DataFrame: All found test data with specimen identifier.
    """

    # Cast to Path so can glob even if user passed str
    fp = Path(self.folder).resolve()

    df = (
        (pd.concat([self._load(f) for f in sorted(fp.rglob("*.csv"))]))
        .assign(spec_id=lambda x: pd.Categorical(x["Specimen ID"]))
        .drop(columns=["Specimen ID"])
        .rename(columns={"spec_id": "Specimen ID"})
    )

    # Reorder the columns for OCD reasons
    col = df.pop("Specimen ID")
    df.insert(0, "Specimen ID", col)

    return df

plot_curves(self, title=None, save_path=None, save_method='selenium', x_label=None, y_label=None, height=500, width=750) inherited

Creates a nice looking stress strain plot of all the specimens using altair.

Will use the class name to fill in axis labels if not passed, e.g 'Tensile' Strain

Parameters:

Name Type Description Default
title Optional[str]

Title for the plot. Defaults to "{class_name} Stress-Strain Curves".

None
save_path Union[str, pathlib.Path]

str or Pathlike path to save a png of the plot. Requires chrome and selenium. If not passed, plot is simply returned and not saved.

None
save_method Optional[str]

One of 2 altair save methods: 'selenium' or 'node'. if 'selenium' requires a configured geckodriver or chromedriver on PATH. if 'node' requires nodejs installation. Defaults to 'selenium'

'selenium'
x_label Optional[str]

Label for x-axis. Defaults to "{class name}Strain (%)".

None
y_label Optional[str]

Label for y-axis. Defaults to "{class name}Stress (MPa)".

None
height int

Height of the plot. Defaults to 500.

500
width int

Width of the plot. Defaults to 750.

750

Returns:

Type Description
Chart

alt.Chart: Stress strain plot.

Source code in pymechtest/shear.py
def plot_curves(
    self,
    title: Optional[str] = None,
    save_path: Optional[Union[str, Path]] = None,
    save_method: Optional[str] = "selenium",
    x_label: Optional[str] = None,
    y_label: Optional[str] = None,
    height: int = 500,
    width: int = 750,
) -> alt.Chart:
    """
    Creates a nice looking stress strain plot of all the specimens using altair.

    Will use the class name to fill in axis labels if not passed, e.g
    'Tensile' Strain

    Args:
        title (str, optional): Title for the plot.
            Defaults to "{class_name} Stress-Strain Curves".

        save_path (Union[str, Path], optional): str or Pathlike path to save
            a png of the plot. Requires chrome and selenium. If not passed, plot
            is simply returned and not saved.

        save_method (str, optional): One of 2 altair save methods:
            'selenium' or 'node'.
            if 'selenium' requires a configured geckodriver or chromedriver on PATH.
            if 'node' requires nodejs installation. Defaults to 'selenium'

        x_label (str, optional): Label for x-axis.
            Defaults to "{class name}Strain (%)".

        y_label (str, optional): Label for y-axis.
            Defaults to "{class name}Stress (MPa)".

        height (int, optional): Height of the plot.
            Defaults to 500.

        width (int, optional): Width of the plot.
            Defaults to 750.

    Returns:
        alt.Chart: Stress strain plot.
    """

    # Altair will warn if over 5,000 rows in a notebook. This is cleanest solution.
    alt.data_transformers.enable("data_server")

    if not x_label:
        x_label = f"{self.__class__.__qualname__} Strain (%)"

    if not y_label:
        y_label = f"{self.__class__.__qualname__} Stress (MPa)"

    if not title:
        title = f"{self.__class__.__qualname__} Stress Strain Curves"

    df = self.load_all()

    chart = (
        alt.Chart(data=df)
        .mark_line(size=1)
        .encode(
            x=alt.X(f"{self.strain_col}:Q", title=x_label),
            y=alt.Y(f"{self.stress_col}:Q", title=y_label),
            color=alt.Color("Specimen ID:N", title="Specimen ID"),
        )
        .properties(title=title, height=height, width=width)
    )

    if save_method not in set(["selenium", "node"]):
        raise ValueError(
            f"Save method must be one of 'selenium' or 'node'. Got: {save_method}"
        )

    if save_path:
        fp = Path(save_path).resolve()
        save(
            chart,
            fp=str(fp),
            scale_factor=6.0,
            method=save_method,
        )

    return chart

stats(self) inherited

Returns a table of summary statistics e.g. mean, std, cov etc. for the data in folder.

Uses pandas df.describe() to do the bulk of the work, just adds in cov for good measure.

Returns:

Type Description
DataFrame

pd.DataFrame: Summary statistics.

Source code in pymechtest/shear.py
def stats(self) -> pd.DataFrame:
    """
    Returns a table of summary statistics e.g. mean, std, cov etc.
    for the data in folder.

    Uses pandas df.describe() to do the bulk of the work, just adds in
    cov for good measure.

    Returns:
        pd.DataFrame: Summary statistics.
    """

    df = self.summarise().describe()

    df.loc["cov%"] = df.loc["std"] / df.loc["mean"] * 100

    # Reorganise so cov is close to std
    new_index = ["count", "mean", "std", "cov%", "min", "25%", "50%", "75%", "max"]
    df = df.reindex(new_index)

    return df

summarise(self) inherited

High level summary method, generates a dataframe containing key test values such as UTS, Modulus etc. for all the data in the target folder.

Returns:

Type Description
DataFrame

pd.DataFrame: Dataframe containing test summary values for each specimen.

Source code in pymechtest/shear.py
def summarise(self) -> pd.DataFrame:
    """
    High level summary method, generates a dataframe containing key
    test values such as UTS, Modulus etc. for all the data in the
    target folder.

    Returns:
        pd.DataFrame: Dataframe containing test summary values for each
            specimen.
    """

    fp = Path(self.folder).resolve()

    rows = [
        self._extract_values(df)
        for df in [self._load(f) for f in sorted(fp.rglob("*.csv"))]
    ]

    # .T transposes to that it's the expected dataframe format
    return (pd.concat(rows, axis=1, ignore_index=True).T).convert_dtypes()