nanodent 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. nanodent-0.0.3/.github/dependabot.yml +6 -0
  2. nanodent-0.0.3/.github/workflows/ci.yml +69 -0
  3. nanodent-0.0.3/.github/workflows/release.yml +159 -0
  4. nanodent-0.0.3/.gitignore +145 -0
  5. nanodent-0.0.3/.pre-commit-config.yaml +38 -0
  6. nanodent-0.0.3/.readthedocs.yml +18 -0
  7. nanodent-0.0.3/CITATION.cff +14 -0
  8. nanodent-0.0.3/COPYING.md +6 -0
  9. nanodent-0.0.3/LICENSE.md +8 -0
  10. nanodent-0.0.3/PKG-INFO +153 -0
  11. nanodent-0.0.3/README.md +120 -0
  12. nanodent-0.0.3/codecov.yml +10 -0
  13. nanodent-0.0.3/doc/Makefile +20 -0
  14. nanodent-0.0.3/doc/api.rst +9 -0
  15. nanodent-0.0.3/doc/conf.py +43 -0
  16. nanodent-0.0.3/doc/index.rst +16 -0
  17. nanodent-0.0.3/doc/intro.rst +2 -0
  18. nanodent-0.0.3/doc/make.bat +35 -0
  19. nanodent-0.0.3/notebooks/demo.ipynb +448 -0
  20. nanodent-0.0.3/pyproject.toml +82 -0
  21. nanodent-0.0.3/src/nanodent/__init__.py +137 -0
  22. nanodent-0.0.3/src/nanodent/analysis/__init__.py +46 -0
  23. nanodent-0.0.3/src/nanodent/analysis/filters.py +64 -0
  24. nanodent-0.0.3/src/nanodent/analysis/force_peaks.py +159 -0
  25. nanodent-0.0.3/src/nanodent/analysis/oliver_pharr.py +970 -0
  26. nanodent-0.0.3/src/nanodent/analysis/onset.py +271 -0
  27. nanodent-0.0.3/src/nanodent/analysis/quality.py +476 -0
  28. nanodent-0.0.3/src/nanodent/analysis/unloading.py +105 -0
  29. nanodent-0.0.3/src/nanodent/io.py +546 -0
  30. nanodent-0.0.3/src/nanodent/models.py +544 -0
  31. nanodent-0.0.3/src/nanodent/plotting.py +876 -0
  32. nanodent-0.0.3/src/nanodent/study.py +1300 -0
  33. nanodent-0.0.3/tests/__init__.py +0 -0
  34. nanodent-0.0.3/tests/conftest.py +17 -0
  35. nanodent-0.0.3/tests/data/experiment_a.hld +2813 -0
  36. nanodent-0.0.3/tests/data/experiment_b.hld +2816 -0
  37. nanodent-0.0.3/tests/data/experiment_c.hld +2617 -0
  38. nanodent-0.0.3/tests/data/experiment_d.hld +2811 -0
  39. nanodent-0.0.3/tests/test_analysis.py +1004 -0
  40. nanodent-0.0.3/tests/test_io.py +360 -0
  41. nanodent-0.0.3/tests/test_plotting.py +480 -0
  42. nanodent-0.0.3/tests/test_study.py +1472 -0
  43. nanodent-0.0.3/uv.toml +5 -0
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "monthly"
@@ -0,0 +1,69 @@
1
+ name: CI
2
+
3
+
4
+ on:
5
+ # Run on pushes to the main branch
6
+ push:
7
+ branches:
8
+ - main
9
+ # Run on all pull requests to the main branch
10
+ pull_request:
11
+ branches:
12
+ - main
13
+ # Run upon manual triggers through the 'Actions' tab of the Github UI
14
+ workflow_dispatch:
15
+
16
+ jobs:
17
+ build-and-test:
18
+ name: Testing on ${{matrix.os}} / Python ${{matrix.python-version}}
19
+ runs-on: ${{matrix.os}}
20
+ strategy:
21
+ matrix:
22
+ os: [ubuntu-latest, macos-latest, windows-latest]
23
+ python-version: ["3.10", "3.14"]
24
+
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v6
28
+
29
+ - name: Set up Python ${{ matrix.python-version }}
30
+ uses: actions/setup-python@v6
31
+ with:
32
+ python-version: ${{ matrix.python-version }}
33
+
34
+ - name: Install Python package
35
+ run: |
36
+ python -m pip install .[tests]
37
+
38
+ - name: Run Python tests
39
+ run: |
40
+ python -m pytest
41
+
42
+ coverage:
43
+ name: Coverage Testing
44
+ runs-on: ubuntu-latest
45
+
46
+ steps:
47
+ - name: Checkout repository
48
+ uses: actions/checkout@v6
49
+
50
+ - uses: actions/setup-python@v6
51
+ with:
52
+ python-version: "3.14"
53
+
54
+ - name: Install Python package
55
+ run: |
56
+ python -m pip install .[tests]
57
+
58
+ - name: Run Python tests
59
+ run: |
60
+ python -m pytest --cov --cov-branch --cov-report=xml
61
+
62
+ - name: Upload coverage to Codecov
63
+ uses: codecov/codecov-action@v6
64
+ with:
65
+ fail_ci_if_error: true
66
+ files: ./tests/coverage.xml
67
+ flags: python
68
+ token: ${{ secrets.CODECOV_TOKEN }}
69
+ verbose: true
@@ -0,0 +1,159 @@
1
+ name: Publish on PyPI and TestPyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ publish_target:
7
+ description: "Choose whether to build only, publish to TestPyPI, or publish to PyPI"
8
+ required: true
9
+ default: dry-run
10
+ type: choice
11
+ options:
12
+ - dry-run
13
+ - testpypi
14
+ push:
15
+ tags:
16
+ - "v*"
17
+
18
+ jobs:
19
+ build:
20
+ name: Build distribution artifacts
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: read
24
+
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v6
28
+
29
+ - name: Set up Python 3.14
30
+ uses: actions/setup-python@v6
31
+ with:
32
+ python-version: "3.14"
33
+
34
+ - name: Verify release tag matches package version
35
+ if: ${{ github.event_name == 'push' }}
36
+ run: |
37
+ python - <<'PY'
38
+ import os
39
+ import pathlib
40
+ import tomllib
41
+
42
+ pyproject = pathlib.Path("pyproject.toml")
43
+ version = tomllib.loads(pyproject.read_text())["project"]["version"]
44
+ ref = os.environ["GITHUB_REF"]
45
+ expected_ref = f"refs/tags/v{version}"
46
+
47
+ if ref != expected_ref:
48
+ raise SystemExit(
49
+ "Publishing requires running this workflow from the "
50
+ f"tag {expected_ref}, but GitHub provided {ref!r}."
51
+ )
52
+
53
+ print(f"Publishing from {ref}, matching project version {version}.")
54
+ PY
55
+
56
+ - name: Install uv
57
+ uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57
58
+
59
+ - name: Build source and wheel distributions
60
+ run: |
61
+ uv build
62
+
63
+ - name: Upload built distributions
64
+ uses: actions/upload-artifact@v7
65
+ with:
66
+ name: python-package-distributions
67
+ path: dist/*
68
+ if-no-files-found: error
69
+
70
+ publish-testpypi:
71
+ name: Publish to TestPyPI
72
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_target == 'testpypi' }}
73
+ needs: build
74
+ runs-on: ubuntu-latest
75
+ permissions:
76
+ contents: read
77
+ id-token: write
78
+ environment:
79
+ name: testpypi
80
+
81
+ steps:
82
+ - name: Checkout repository
83
+ uses: actions/checkout@v6
84
+
85
+ - name: Download built distributions
86
+ uses: actions/download-artifact@v8
87
+ with:
88
+ name: python-package-distributions
89
+ path: dist/
90
+
91
+ - name: Install uv
92
+ uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57
93
+
94
+ - name: Show downloaded files
95
+ run: |
96
+ pwd
97
+ ls -lah
98
+ ls -lah dist
99
+
100
+ - name: Publish distributions to TestPyPI
101
+ run: |
102
+ uv publish --index testpypi \
103
+ --config-file uv.toml \
104
+ dist/*
105
+
106
+ publish-pypi:
107
+ name: Publish to PyPI
108
+ if: ${{ github.event_name == 'push' }}
109
+ needs: build
110
+ runs-on: ubuntu-latest
111
+ permissions:
112
+ contents: read
113
+ id-token: write
114
+ environment:
115
+ name: pypi
116
+
117
+ steps:
118
+ - name: Checkout repository
119
+ uses: actions/checkout@v6
120
+
121
+ - name: Download built distributions
122
+ uses: actions/download-artifact@v8
123
+ with:
124
+ name: python-package-distributions
125
+ path: dist/
126
+
127
+ - name: Install uv
128
+ uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57
129
+
130
+ - name: Show downloaded files
131
+ run: |
132
+ pwd
133
+ ls -lah
134
+ ls -lah dist
135
+
136
+ - name: Publish distributions to PyPI
137
+ run: |
138
+ uv publish dist/*
139
+
140
+ github-release:
141
+ name: Create GitHub Release
142
+ if: ${{ github.event_name == 'push' }}
143
+ needs: [build, publish-pypi]
144
+ runs-on: ubuntu-latest
145
+ permissions:
146
+ contents: write
147
+
148
+ steps:
149
+ - name: Download built distributions
150
+ uses: actions/download-artifact@v8
151
+ with:
152
+ name: python-package-distributions
153
+ path: dist/
154
+
155
+ - name: Create GitHub Release
156
+ uses: softprops/action-gh-release@v2
157
+ with:
158
+ generate_release_notes: true
159
+ files: dist/*
@@ -0,0 +1,145 @@
1
+
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
99
+ __pypackages__/
100
+
101
+ # Celery stuff
102
+ celerybeat-schedule
103
+ celerybeat.pid
104
+
105
+ # SageMath parsed files
106
+ *.sage.py
107
+
108
+ # Environments
109
+ .env
110
+ .venv
111
+ env/
112
+ venv/
113
+ ENV/
114
+ env.bak/
115
+ venv.bak/
116
+
117
+ # Spyder project settings
118
+ .spyderproject
119
+ .spyproject
120
+
121
+ # Rope project settings
122
+ .ropeproject
123
+
124
+ # mkdocs documentation
125
+ /site
126
+
127
+ # mypy
128
+ .mypy_cache/
129
+ .dmypy.json
130
+ dmypy.json
131
+
132
+ # Pyre type checker
133
+ .pyre/
134
+
135
+ # pytype static type analyzer
136
+ .pytype/
137
+
138
+ # Cython debug symbols
139
+ cython_debug/
140
+
141
+ # Image files
142
+ *.png
143
+
144
+ # Model files
145
+ *.pkl
@@ -0,0 +1,38 @@
1
+ repos:
2
+ - repo: https://github.com/kynan/nbstripout
3
+ rev: 0.9.1
4
+ hooks:
5
+ - id: nbstripout
6
+ files: ".ipynb"
7
+
8
+ - repo: https://github.com/astral-sh/ruff-pre-commit
9
+ rev: v0.15.7
10
+ hooks:
11
+ # Run the linter
12
+ - id: ruff-check
13
+ args: [ --fix, --show-fixes ]
14
+ types_or: [ python, pyi, pyproject ]
15
+ # Run the formatter
16
+ - id: ruff-format
17
+ types_or: [ python, pyi ]
18
+
19
+ # Add some general purpose useful hooks
20
+ - repo: https://github.com/pre-commit/pre-commit-hooks
21
+ rev: v6.0.0
22
+ hooks:
23
+ # Make sure that contained YAML files are well-formed
24
+ - id: check-yaml
25
+ # Trim trailing whitespace of all sorts
26
+ - id: trailing-whitespace
27
+ # Apply a file size limit of 500kB
28
+ - id: check-added-large-files
29
+ # Simple parser validation of e.g. pyproject.toml
30
+ - id: check-toml
31
+ # Unify file endings
32
+ - id: end-of-file-fixer
33
+
34
+ # GitHub Actions Workflow linter
35
+ - repo: https://github.com/rhysd/actionlint
36
+ rev: v1.7.11
37
+ hooks:
38
+ - id: actionlint
@@ -0,0 +1,18 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3.13"
7
+
8
+ sphinx:
9
+ configuration: doc/conf.py
10
+
11
+ formats: all
12
+
13
+ python:
14
+ install:
15
+ - method: pip
16
+ path: .
17
+ extra_requirements:
18
+ - docs
@@ -0,0 +1,14 @@
1
+ cff-version: 1.2.0
2
+ message: If you want to cite this software, you can do so using the metadata from this file.
3
+ title: nanodent
4
+ abstract: Analyze data from nanoindentation experiments.
5
+ authors:
6
+ - family-names: Isensee
7
+ given-names: Thomas
8
+ orcid: "https://orcid.org/0000-0002-7242-4529"
9
+ version: 0.0.1
10
+ keywords:
11
+ - nanoindentation
12
+ date-released: "2026-03-21"
13
+ license: MIT
14
+ repository-code: https://github.com/thomasisensee/nanodent
@@ -0,0 +1,6 @@
1
+ This is the list of copyright holders of nanodent.
2
+
3
+ For information on the license, see LICENSE.md.
4
+
5
+
6
+ * Thomas Isensee, 2026
@@ -0,0 +1,8 @@
1
+ Copyright 2026, The copyright holders according to COPYING.md
2
+
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: nanodent
3
+ Version: 0.0.3
4
+ Summary: Analysis package for nanoindentation experimental data.
5
+ Project-URL: repository, https://github.com/thomasisensee/nanodent
6
+ Author-email: Thomas Isensee <thomas.isensee@iwr.uni-heidelberg.de>
7
+ License-Expression: MIT
8
+ License-File: LICENSE.md
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: matplotlib
14
+ Requires-Dist: numpy
15
+ Requires-Dist: scipy
16
+ Provides-Extra: dev
17
+ Requires-Dist: mypy; extra == 'dev'
18
+ Requires-Dist: pytest; extra == 'dev'
19
+ Requires-Dist: pytest-cov; extra == 'dev'
20
+ Requires-Dist: ruff; extra == 'dev'
21
+ Provides-Extra: docs
22
+ Requires-Dist: myst-parser; extra == 'docs'
23
+ Requires-Dist: sphinx; extra == 'docs'
24
+ Requires-Dist: sphinx-rtd-theme; extra == 'docs'
25
+ Provides-Extra: lint
26
+ Requires-Dist: mypy; extra == 'lint'
27
+ Requires-Dist: ruff; extra == 'lint'
28
+ Provides-Extra: tests
29
+ Requires-Dist: nbval; extra == 'tests'
30
+ Requires-Dist: pytest; extra == 'tests'
31
+ Requires-Dist: pytest-cov; extra == 'tests'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # nanodent
35
+
36
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
37
+ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/thomasisensee/nanodent/ci.yml?branch=main)](https://github.com/thomasisensee/nanodent/actions/workflows/ci.yml)
38
+ [![codecov](https://codecov.io/github/thomasisensee/nanodent/graph/badge.svg?token=DRJB38CIZI)](https://codecov.io/github/thomasisensee/nanodent)
39
+ ![Python](https://img.shields.io/badge/python-3.10%20|%203.11%20|%203.12%20|%203.13%20|%203.14-blue)
40
+
41
+ ## Installation
42
+
43
+ The Python package `nanodent` can be installed from PyPI:
44
+
45
+ ```
46
+ python -m pip install nanodent
47
+ ```
48
+
49
+ ## Development installation
50
+
51
+ If you want to contribute to the development of `nanodent`, we recommend the
52
+ following editable installation from this repository:
53
+
54
+ ```
55
+ git clone https://github.com/thomasisensee/nanodent
56
+ cd nanodent
57
+ python -m pip install --editable .[dev,docs,lint,tests]
58
+ ```
59
+
60
+ Having done so, the test suite can be run using `pytest`:
61
+
62
+ ```
63
+ python -m pytest
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ `nanodent` loads `.hld` as the canonical source.
69
+
70
+ ```python
71
+ from datetime import datetime, timedelta
72
+
73
+ import matplotlib.pyplot as plt
74
+
75
+ from nanodent import (
76
+ load_folder,
77
+ plot_experiments,
78
+ plot_group_timeline,
79
+ save_experiment_plots,
80
+ )
81
+
82
+ study = load_folder("path/to/experiment-folder")
83
+ filtered_study = study.classify_quality()
84
+ filtered_study = filtered_study.analyze_oliver_pharr()
85
+ hardness_rows = filtered_study.scalar_series("hardness")
86
+ pop_in_rows = filtered_study.detect_force_peaks().scalar_series("pop_in_load")
87
+ manual_groups = filtered_study.group_by_datetime_ranges(
88
+ [
89
+ (
90
+ datetime(2026, 3, 4, 13, 0, 0),
91
+ datetime(2026, 3, 4, 15, 0, 0),
92
+ ),
93
+ ]
94
+ )
95
+
96
+ timeline_fig, timeline_ax = plot_group_timeline(
97
+ filtered_study,
98
+ max_gap=timedelta(minutes=30),
99
+ )
100
+
101
+ fig, ax = plt.subplots()
102
+ plot_experiments(
103
+ ax,
104
+ filtered_study,
105
+ fit_kwargs={"color": "gray", "linestyle": "solid", "linewidth": 2},
106
+ zero_onset=False,
107
+ cmap="rainbow",
108
+ )
109
+
110
+ ax.set_xlabel("Displacement h / nm")
111
+ ax.set_ylabel("Force P / μN")
112
+
113
+ saved = save_experiment_plots(
114
+ filtered_study, "plots/", zero_onset=False
115
+ )
116
+
117
+ filtered_study.save_session("analysis-session.pkl")
118
+ resumed_study = load_folder("path/to/experiment-folder").load_session(
119
+ "analysis-session.pkl"
120
+ )
121
+ ```
122
+
123
+ `Study.classify_quality()` keeps all experiments loaded but marks
124
+ heuristically bad runs as `enabled=False` with a short `disabled_reason`,
125
+ currently including `gradual_onset`, `flat_force`, and local-jump outliers
126
+ such as `outlier_disp` or `outlier_force`.
127
+ Grouping and plotting ignore disabled experiments by default; group summaries
128
+ include them by default so quality decisions stay visible. Pass
129
+ `include_disabled=True` when you want disabled runs included in plots or
130
+ grouping output. `plot_experiments(...)` and `save_experiment_plots(...)`
131
+ always visualize the test force-displacement curve.
132
+
133
+ The public API also exposes:
134
+
135
+ - `load_experiment(path) -> Experiment`
136
+ - `load_folder(path) -> Study`
137
+ - `Study.analyze_oliver_pharr(...) -> Study`
138
+ - `Study.scalar_series(...) -> list[dict[str, Any]]`
139
+ - `Study.save_session(path) -> Path`
140
+ - `Study.load_session(path) -> Study`
141
+ - `Study.group_by_datetime_ranges(...) -> list[ExperimentGroup]`
142
+ - `Study.group_by_time_gap(...) -> list[ExperimentGroup]`
143
+ - `plot_group_timeline(...) -> tuple[Figure, Axes]`
144
+ - `plot_experiments(...) -> Axes`
145
+ - `save_experiment_plots(...) -> list[Path]`
146
+
147
+
148
+ ## Demo notebook using methods provided by py4dgeo
149
+ Use the [demo notebook](https://github.com/thomasisensee/nanodent/blob/main/notebooks/demo.ipynb) to test the functionality of `nanodent` and see how it can be used to analyze and visualize nanoindentation experiments.
150
+
151
+ ## Acknowledgments
152
+
153
+ This repository was set up using the [SSC Cookiecutter for Python Packages](https://github.com/ssciwr/cookiecutter-python-package).