Basic postprocessing with IndenToolBox
In this example, we’ll look at tests carried out on fused quartz samples and measure their modulus.
Important: run the pre_processing.ipynb file before.
%matplotlib widget
import indentoolbox as itb
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
pd.options.display.float_format = "{:.5e}".format
Setup
root_path = "./FusedQuartz_batch"
batch = itb.core.Batch.load(root_path)
batch
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[2], line 2
1 root_path = "./FusedQuartz_batch"
----> 2 batch = itb.core.Batch.load(root_path)
3 batch
File ~/checkouts/readthedocs.org/user_builds/indentoolbox/envs/latest/lib/python3.11/site-packages/indentoolbox/core.py:765, in Batch.load(cls, root_path)
752 @classmethod
753 def load(cls, root_path):
754 """
755 Load a `Batch` object from the specified root path.
756
(...)
763 :return: A `Batch` object loaded from the files.
764 """
--> 765 batch_data = pd.read_csv(root_path + ".csv")
766 dic = toml.load(open(root_path + ".toml"))
767 batch_data = pd.read_csv(root_path + ".csv")
File ~/checkouts/readthedocs.org/user_builds/indentoolbox/envs/latest/lib/python3.11/site-packages/pandas/io/parsers/readers.py:912, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)
899 kwds_defaults = _refine_defaults_read(
900 dialect,
901 delimiter,
(...)
908 dtype_backend=dtype_backend,
909 )
910 kwds.update(kwds_defaults)
--> 912 return _read(filepath_or_buffer, kwds)
File ~/checkouts/readthedocs.org/user_builds/indentoolbox/envs/latest/lib/python3.11/site-packages/pandas/io/parsers/readers.py:577, in _read(filepath_or_buffer, kwds)
574 _validate_names(kwds.get("names", None))
576 # Create the parser.
--> 577 parser = TextFileReader(filepath_or_buffer, **kwds)
579 if chunksize or iterator:
580 return parser
File ~/checkouts/readthedocs.org/user_builds/indentoolbox/envs/latest/lib/python3.11/site-packages/pandas/io/parsers/readers.py:1407, in TextFileReader.__init__(self, f, engine, **kwds)
1404 self.options["has_index_names"] = kwds["has_index_names"]
1406 self.handles: IOHandles | None = None
-> 1407 self._engine = self._make_engine(f, self.engine)
File ~/checkouts/readthedocs.org/user_builds/indentoolbox/envs/latest/lib/python3.11/site-packages/pandas/io/parsers/readers.py:1661, in TextFileReader._make_engine(self, f, engine)
1659 if "b" not in mode:
1660 mode += "b"
-> 1661 self.handles = get_handle(
1662 f,
1663 mode,
1664 encoding=self.options.get("encoding", None),
1665 compression=self.options.get("compression", None),
1666 memory_map=self.options.get("memory_map", False),
1667 is_text=is_text,
1668 errors=self.options.get("encoding_errors", "strict"),
1669 storage_options=self.options.get("storage_options", None),
1670 )
1671 assert self.handles is not None
1672 f = self.handles.handle
File ~/checkouts/readthedocs.org/user_builds/indentoolbox/envs/latest/lib/python3.11/site-packages/pandas/io/common.py:859, in get_handle(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)
854 elif isinstance(handle, str):
855 # Check whether the filename is to be opened in binary mode.
856 # Binary mode does not support 'encoding' and 'newline'.
857 if ioargs.encoding and "b" not in ioargs.mode:
858 # Encoding
--> 859 handle = open(
860 handle,
861 ioargs.mode,
862 encoding=ioargs.encoding,
863 errors=errors,
864 newline="",
865 )
866 else:
867 # Binary mode
868 handle = open(handle, ioargs.mode)
FileNotFoundError: [Errno 2] No such file or directory: './FusedQuartz_batch.csv'
First look at the data
# THE TIP
batch.tip
# TEST DATA
print(batch.tests[0].data)
batch.collect_steps(0).data
Basic Plots
plot_reject = False
colors = "rgb"
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
tests = batch.tests
Ntests = len(tests)
for Nt in range(Ntests):
test = tests[Nt]
if plot_reject == False and test.reject == False:
test_data = test.data
test_structure = [step.kind() for step in test.steps]
for step_id, step_data in test_data.groupby("step"):
ax.plot(
step_data.disp * 1.0e9,
step_data.force * 1.0e6,
"-",
lw=1.0,
color=colors[step_id%len(colors)],
)
ax.grid()
ax.set_xlabel("Displacement, $h$ [nm]")
ax.set_ylabel("Force, $P$ [µN]")
#plt.savefig("experimental_batch.png")
plt.show()
print(test_structure)
Post-processing
In this section, we post-process our tests to extract basic data from the various steps. Indentoolbox can be used to collectively process one step of data from all trials and export the result as a dataframe. This is a powerful feature for batch processing, as is often the case with indentation.
First, we focus on the parabolic loading step. Please note that:
The method
collect_stepsallows us to collect every occurrence of the same step in every test.The method
parabolic_fitperforms a parabolic fit on every step.
loading_data = batch.collect_steps(0).parabolic_fit(displim = 50.e-9, htrunc = batch.tip.htrunc)
loading_data.index = loading_data.index.droplevel(1) #
print(loading_data)
We then turn our attention to the unloading step:
unloading_data = batch.collect_steps(2).unloading_fit()
unloading_data.index = unloading_data.index.droplevel(1)
print(unloading_data)
And we apply the method of Oliver and Pharr to deduce the indentation modulus and then the Young’s modulus.
OP = itb.processing.OliverPharr(data = unloading_data, tip = batch.tip)
print(OP)
nu_samp = 0.17
OP["Esamp"] = OP.Eeqsamp * (1. - nu_samp**2)
print(OP)
Reverse analysis
In this section, we use several inverse analysis algorithms to retrieve the mechanical properties of the material under study. As this material is fused quartz, these methods are unsuitable for crystalline metals. See it as a demonstration, but disregard the results.
test_id = 0
C = loading_data.loc[test_id, "C"]
S = unloading_data.loc[test_id, "S"]
hm = unloading_data.loc[test_id, "hm"]
Pm = loading_data.loc[test_id, "Pm"]
hf = unloading_data.loc[test_id, "hf"]
E_ind = batch.tip.young_modulus
nu_ind = batch.tip.poisson_coefficient
Wrev = unloading_data.loc[test_id, "W"]
Wtot = loading_data.loc[test_id, "W"]
Wirr = Wtot + Wrev
Wfrac = Wirr/Wtot
#Wfrac = 0.9
gian99 = pd.DataFrame(itb.processing.GIAN99(hm = hm, hf = hf, S = S, C = C, nu = nu_samp, E_ind = E_ind, nu_ind = nu_ind))
print(gian99)
dao01 = pd.DataFrame(itb.processing.DAO01(S=S,C=C,Pm=Pm,Wfrac=Wfrac,hm=hm,hf=hf))
print(dao01)
casals05 = pd.DataFrame(itb.processing.CASA05(hm=hm, hf=hf, Pm=Pm, S=S, C=C))
print(casals05)