nanite-community 0.0.0__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.
- nanite_community-0.0.0/LICENSE +21 -0
- nanite_community-0.0.0/PKG-INFO +37 -0
- nanite_community-0.0.0/README.md +20 -0
- nanite_community-0.0.0/nanite_community/__init__.py +0 -0
- nanite_community-0.0.0/nanite_community/models/__init__.py +0 -0
- nanite_community-0.0.0/nanite_community/models/model_hertz_corrected_viscoelasticity_KVM.py +429 -0
- nanite_community-0.0.0/nanite_community.egg-info/PKG-INFO +37 -0
- nanite_community-0.0.0/nanite_community.egg-info/SOURCES.txt +11 -0
- nanite_community-0.0.0/nanite_community.egg-info/dependency_links.txt +1 -0
- nanite_community-0.0.0/nanite_community.egg-info/requires.txt +2 -0
- nanite_community-0.0.0/nanite_community.egg-info/top_level.txt +1 -0
- nanite_community-0.0.0/pyproject.toml +33 -0
- nanite_community-0.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Refractive Index Imaging
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nanite-community
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Repository for storing nanite models
|
|
5
|
+
Author-email: Eoghan O'Connell <eoclives@hotmail.com>, Paul Müller <dev@craban.de>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: AFM,Atomic Force Microscopy,Hertz KVM,nanite,pyjibe
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: lmfit>=1.3.2
|
|
15
|
+
Requires-Dist: numpy==1.26.*
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# Nanite Community Models
|
|
19
|
+
|
|
20
|
+
This is a repository for storing
|
|
21
|
+
[nanite](https://github.com/AFM-Analysis/nanite) models. These can be models
|
|
22
|
+
that exist as part of a publication, or not. Storing them here will make them
|
|
23
|
+
easy to use in nanite and PyJibe.
|
|
24
|
+
|
|
25
|
+
## Future Plans
|
|
26
|
+
|
|
27
|
+
We soon plan to incorporate this package into the
|
|
28
|
+
[nanite](https://github.com/AFM-Analysis/nanite) package so that all
|
|
29
|
+
models are available as an optional dependency.
|
|
30
|
+
|
|
31
|
+
## Contributing
|
|
32
|
+
|
|
33
|
+
If you wish to add a model to this repository and are not familiar with git
|
|
34
|
+
and github, please make a new Issue.
|
|
35
|
+
Then, place the model code in a single python file as described in the
|
|
36
|
+
[nanite docs here](https://nanite.readthedocs.io/en/stable/sec_develop.html#writing-model-functions).
|
|
37
|
+
Then, fork this repository, make a Pull Request and choose a reviewer.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Nanite Community Models
|
|
2
|
+
|
|
3
|
+
This is a repository for storing
|
|
4
|
+
[nanite](https://github.com/AFM-Analysis/nanite) models. These can be models
|
|
5
|
+
that exist as part of a publication, or not. Storing them here will make them
|
|
6
|
+
easy to use in nanite and PyJibe.
|
|
7
|
+
|
|
8
|
+
## Future Plans
|
|
9
|
+
|
|
10
|
+
We soon plan to incorporate this package into the
|
|
11
|
+
[nanite](https://github.com/AFM-Analysis/nanite) package so that all
|
|
12
|
+
models are available as an optional dependency.
|
|
13
|
+
|
|
14
|
+
## Contributing
|
|
15
|
+
|
|
16
|
+
If you wish to add a model to this repository and are not familiar with git
|
|
17
|
+
and github, please make a new Issue.
|
|
18
|
+
Then, place the model code in a single python file as described in the
|
|
19
|
+
[nanite docs here](https://nanite.readthedocs.io/en/stable/sec_develop.html#writing-model-functions).
|
|
20
|
+
Then, fork this repository, make a Pull Request and choose a reviewer.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import lmfit
|
|
2
|
+
import numpy as np
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_parameter_defaults():
|
|
7
|
+
# The order of the parameters must match the order
|
|
8
|
+
# of ´parameter_names´ and ´parameter_keys´.
|
|
9
|
+
params = lmfit.Parameters()
|
|
10
|
+
params.add("_mod_constraint", value=1e2, min=0, max=20e3, vary=True)
|
|
11
|
+
params.add("Eu", value=1e3, min=0, max=20e3, vary=True)
|
|
12
|
+
params.add("Eapp", expr='Eu-_mod_constraint')
|
|
13
|
+
params.add("time_ind", value=1, min=1e-10, max=10, vary=False)
|
|
14
|
+
params.add("R", value=2.5e-6, vary=False)
|
|
15
|
+
params.add("eta", value=.5, min=0, vary=False)
|
|
16
|
+
params.add("nu", value=.5, vary=False)
|
|
17
|
+
params.add('_constraint', value=0.5, min=1 / 5, max=10, vary=True)
|
|
18
|
+
params.add("lmd", expr='_constraint*time_ind')
|
|
19
|
+
params.add("velocity", value=5e-6, vary=False)
|
|
20
|
+
params.add("contact_point", value=0, vary=True)
|
|
21
|
+
params.add("baseline", value=0, vary=False)
|
|
22
|
+
return params
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def hertz_corrected_viscelasticity_KVM(delta, _mod_constraint, Eu, Eapp,
|
|
26
|
+
time_ind, R, eta, nu,
|
|
27
|
+
_constraint, lmd, velocity,
|
|
28
|
+
contact_point,
|
|
29
|
+
baseline):
|
|
30
|
+
r"""Hertz model for a spherical indenter including correction for
|
|
31
|
+
viscoelasticity
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
_mod_constraint: float
|
|
36
|
+
Constraint for Eu > Eapp
|
|
37
|
+
Eu: float
|
|
38
|
+
Unrelaxed Young's modulus = E0 + E1[N/m²]
|
|
39
|
+
Eapp: float
|
|
40
|
+
Apparent Young's modulus = E0 + E1*exp(
|
|
41
|
+
-0.365*time_indentation/lambda) [N/m²]
|
|
42
|
+
delta: 1d ndarray
|
|
43
|
+
Indentation [m]
|
|
44
|
+
time_ind: float
|
|
45
|
+
Indentation time [s]
|
|
46
|
+
R: float
|
|
47
|
+
Tip radius [m]
|
|
48
|
+
eta: float
|
|
49
|
+
Dashpot value of Kelvin Voigt model [Pa.s]
|
|
50
|
+
nu: float
|
|
51
|
+
Poisson's ratio
|
|
52
|
+
_constraint: float
|
|
53
|
+
constraint for Maxwell relaxation (lmd)
|
|
54
|
+
lmd: float
|
|
55
|
+
relaxation time of Maxwell elements [s]
|
|
56
|
+
velocity: float
|
|
57
|
+
Velocity of indentation [m/s]
|
|
58
|
+
contact_point: float
|
|
59
|
+
Indentation offset [m]
|
|
60
|
+
baseline: float
|
|
61
|
+
Force offset [N]
|
|
62
|
+
negindent: bool
|
|
63
|
+
If `True`, will assume that the indentation value(s) given by
|
|
64
|
+
`delta` are negative and must be multiplied by -1.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
F: float
|
|
69
|
+
Force [N]
|
|
70
|
+
|
|
71
|
+
Notes
|
|
72
|
+
-----
|
|
73
|
+
These approximations are made by the Hertz model:
|
|
74
|
+
|
|
75
|
+
- The sample is isotropic.
|
|
76
|
+
- The sample is behave as Kelvin-Voigt-Maxwell material.
|
|
77
|
+
- The sample is extended infinitely in one half space.
|
|
78
|
+
- The indenter is not deformable.
|
|
79
|
+
- There are no additional interactions between sample and indenter.
|
|
80
|
+
|
|
81
|
+
Additional assumptions:
|
|
82
|
+
|
|
83
|
+
- no surface forces
|
|
84
|
+
|
|
85
|
+
.. math::
|
|
86
|
+
|
|
87
|
+
F = \\(frac{4}{3}
|
|
88
|
+
\\frac{E}{1-\\nu^2}
|
|
89
|
+
\\sqrt{R}
|
|
90
|
+
\\delta^{3/2})
|
|
91
|
+
\\(1-0.15frac{delta}{R})
|
|
92
|
+
\\+(frac{4}{3}
|
|
93
|
+
\\frac{E1}{1-\\nu^2}
|
|
94
|
+
\\sqrt{R}
|
|
95
|
+
\\delta^{3/2})
|
|
96
|
+
\\(1-0.15frac{delta}{R})(\\exp^(frac{-0.365delta}{\\lambdaV}))
|
|
97
|
+
\\+7.25delta^{1/2}R^{1/2}\\muV
|
|
98
|
+
|
|
99
|
+
References
|
|
100
|
+
----------
|
|
101
|
+
.. [1] Sneddon (1965) :cite:`Sneddon1965`
|
|
102
|
+
.. [2] Dobler (personal communication, 2018) :cite:`Dobler`
|
|
103
|
+
.. [3] Ding et. al (2017) :cite:'Ding'
|
|
104
|
+
.. [4] Abuhattum et al. (2022) :cite:'Abuhattum'
|
|
105
|
+
|
|
106
|
+
Original model python file:
|
|
107
|
+
.. [5] Abuhattum, Shada; Mokbel, Dominic; Müller, Paul; Soteriou, Despina;
|
|
108
|
+
Guck, Jochen; Aland, Sebastian (2022), “An explicit model to extract
|
|
109
|
+
viscoelastic properties of cells from AFM force-indentation curves”,
|
|
110
|
+
Mendeley Data, V2, doi: 10.17632/c2gccnfkgd.2
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
root = (contact_point - delta)
|
|
114
|
+
pos = root > 0
|
|
115
|
+
D = (1 - nu ** 2)
|
|
116
|
+
aa0 = 4 / 3 * np.sqrt(R) * (Eu - (Eu - Eapp) /
|
|
117
|
+
(1 - np.exp(-0.365 * time_ind / lmd))) / D
|
|
118
|
+
|
|
119
|
+
aa1 = 4 / 3 * np.sqrt(R) * ((Eu - Eapp) /
|
|
120
|
+
(1 - np.exp(-0.365 * time_ind / lmd))) / D
|
|
121
|
+
|
|
122
|
+
bb = np.zeros_like(delta)
|
|
123
|
+
bb[pos] = (root[pos]) ** (3 / 2)
|
|
124
|
+
cc = np.zeros_like(delta)
|
|
125
|
+
|
|
126
|
+
cc[pos] = 1 - 0.15 * root[pos] / R
|
|
127
|
+
|
|
128
|
+
dd = np.zeros_like(delta)
|
|
129
|
+
dd[pos] = (np.exp(-0.365 * root[pos] / (velocity * lmd)))
|
|
130
|
+
|
|
131
|
+
ee = np.zeros_like(delta)
|
|
132
|
+
ee[pos] = 7.25 * (root[pos]) ** (1 / 2) * R ** (1 / 2) * eta * velocity
|
|
133
|
+
return bb * cc * (aa0 + aa1 * dd) + ee + baseline
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def model(params, x):
|
|
137
|
+
if x[0] < x[-1]:
|
|
138
|
+
revert = True
|
|
139
|
+
else:
|
|
140
|
+
revert = False
|
|
141
|
+
if revert:
|
|
142
|
+
x = x[::-1]
|
|
143
|
+
|
|
144
|
+
mf = hertz_corrected_viscelasticity_KVM(delta=x,
|
|
145
|
+
_mod_constraint=params[
|
|
146
|
+
"_mod_constraint"].value,
|
|
147
|
+
Eu=params["Eu"].value,
|
|
148
|
+
Eapp=params["Eapp"].value,
|
|
149
|
+
time_ind=params["time_ind"].value,
|
|
150
|
+
R=params["R"].value,
|
|
151
|
+
eta=params["eta"].value,
|
|
152
|
+
nu=params["nu"].value,
|
|
153
|
+
_constraint=params["_constraint"]
|
|
154
|
+
.value,
|
|
155
|
+
lmd=params["lmd"].value,
|
|
156
|
+
velocity=params["velocity"].value,
|
|
157
|
+
contact_point=params
|
|
158
|
+
["contact_point"].value,
|
|
159
|
+
baseline=params["baseline"].value)
|
|
160
|
+
|
|
161
|
+
if revert:
|
|
162
|
+
return mf[::-1]
|
|
163
|
+
return mf
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def compute_ancillaries(idnt):
|
|
167
|
+
# This function takes a nanite.indent.Indentation instance
|
|
168
|
+
# (https://nanite.readthedocs.io/en/stable/sec_code_reference.html
|
|
169
|
+
# nanite.indent.Indentation)
|
|
170
|
+
# as an argument and returns additional parameters as a
|
|
171
|
+
# dictionary.
|
|
172
|
+
"""first, find the contact point that is computed from simple Hertz
|
|
173
|
+
model"""
|
|
174
|
+
parms = idnt.get_initial_fit_parameters(model_key=model_key,
|
|
175
|
+
model_ancillaries=False)
|
|
176
|
+
R = parms["R"].value
|
|
177
|
+
velocity = parms["velocity"].value
|
|
178
|
+
indentation = copy.deepcopy(idnt)
|
|
179
|
+
indentation.fit_properties["model_key"] = "hertz_para"
|
|
180
|
+
params_ind = indentation.get_initial_fit_parameters()
|
|
181
|
+
params_ind["R"].value = R
|
|
182
|
+
|
|
183
|
+
indentation.fit_model(model_key="hertz_para",
|
|
184
|
+
params_initial=params_ind,
|
|
185
|
+
range_x=(-2e-6, 1e-6), range_type='absolute')
|
|
186
|
+
|
|
187
|
+
if "params_fitted" in indentation.fit_properties:
|
|
188
|
+
params_ind_updated = indentation.fit_properties["params_fitted"]
|
|
189
|
+
contact_point_simple = params_ind_updated["contact_point"].value
|
|
190
|
+
else:
|
|
191
|
+
contact_point_simple = 0
|
|
192
|
+
|
|
193
|
+
force = indentation.data["force"]
|
|
194
|
+
segment = indentation.data["segment"]
|
|
195
|
+
tip_position = indentation.data["tip position"]
|
|
196
|
+
force = force[(segment == 1)]
|
|
197
|
+
tip_position = tip_position[(segment == 1)]
|
|
198
|
+
|
|
199
|
+
force_jump_max_indent, fitted_max_indent = fit_viscosity_jump_model(
|
|
200
|
+
tip_position=tip_position,
|
|
201
|
+
force=force,
|
|
202
|
+
R=R,
|
|
203
|
+
velocity=velocity)
|
|
204
|
+
|
|
205
|
+
fitted_max_indent = fitted_max_indent - contact_point_simple
|
|
206
|
+
force_jump_max_indent = force_jump_max_indent
|
|
207
|
+
|
|
208
|
+
eta_constants = 2 * 7.25 * velocity * R ** (1 / 2) * (
|
|
209
|
+
abs(fitted_max_indent)) ** (1 / 2)
|
|
210
|
+
time_ind = -(fitted_max_indent) / velocity
|
|
211
|
+
eta = force_jump_max_indent / eta_constants
|
|
212
|
+
|
|
213
|
+
anc_dict = {"force_jump_max_indent": force_jump_max_indent,
|
|
214
|
+
"eta": eta,
|
|
215
|
+
"time_ind": time_ind}
|
|
216
|
+
return anc_dict
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def helper_retract_fraction(tip_position, force, fraction_force):
|
|
220
|
+
"""
|
|
221
|
+
The function specifies the segment of the retract curve that will be
|
|
222
|
+
used for fitting the viscosity model.
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
tip_position: 1D numpy array
|
|
227
|
+
the tip position values of the cantilever's retract motion [m]
|
|
228
|
+
force: 1D numpy array
|
|
229
|
+
the force values of the cantilever's retract motion [N]
|
|
230
|
+
fraction_force: float
|
|
231
|
+
fraction of the data points that should be taken into account for
|
|
232
|
+
fitting, the decision is made according to the maximal value of
|
|
233
|
+
the force
|
|
234
|
+
|
|
235
|
+
Returns
|
|
236
|
+
-------
|
|
237
|
+
position_seg: 1D numpy array
|
|
238
|
+
the segment of tip position array that will be used for the fitting [m]
|
|
239
|
+
force_seg: 1D numpy array
|
|
240
|
+
the segment of force array that will be used for the fitting [N]
|
|
241
|
+
max_position: float
|
|
242
|
+
the maximal absolute value of the tip position [m]
|
|
243
|
+
max_force: float
|
|
244
|
+
the maximal value of the force [N]
|
|
245
|
+
|
|
246
|
+
Notes
|
|
247
|
+
-----
|
|
248
|
+
max_position is assumed to be the first point of the retract
|
|
249
|
+
curve even though it is not always the largest indentation value. This
|
|
250
|
+
is because the movement of the Piezo changes the direction at the
|
|
251
|
+
beginning of the retract signal and larger indentation values after the
|
|
252
|
+
first point are assumed to be as a result of the measurement noise.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
fraction = int(0.8 * len(force))
|
|
256
|
+
max_force = np.max(force[:fraction])
|
|
257
|
+
max_position = -1 * tip_position[0]
|
|
258
|
+
fit_stop = int(np.argwhere(force < (max_force * fraction_force))[0])
|
|
259
|
+
position_seg = tip_position[:fit_stop].copy()
|
|
260
|
+
force_seg = force[:fit_stop].copy()
|
|
261
|
+
|
|
262
|
+
return position_seg, force_seg, max_position, max_force
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def helper_line_polynomial_fit(position_seg, force_seg):
|
|
266
|
+
"""
|
|
267
|
+
piecewise fitting of two functions, the first part of the signal with a
|
|
268
|
+
vertical line followed by a third order polynomial.
|
|
269
|
+
..math:: y = \\y0
|
|
270
|
+
when x<x0 \\y0 + a*(x-x0) + b*(x-x0)^2 + c*(x-x0)^3 when x>=x0
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
position_seg: 1D numpy array [m]
|
|
275
|
+
the tip position values used for the fitting
|
|
276
|
+
force_seg: 1D numpy array [N]
|
|
277
|
+
the force values used for the fitting
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
fit: 1D numpy array
|
|
282
|
+
fit results
|
|
283
|
+
res.params: lmfit.parameter
|
|
284
|
+
fitting parameters
|
|
285
|
+
"""
|
|
286
|
+
params = lmfit.Parameters()
|
|
287
|
+
params.add("x0",
|
|
288
|
+
value=force_seg[0] * 0.98,
|
|
289
|
+
max=force_seg[0],
|
|
290
|
+
min=force_seg[0] * 0.6,
|
|
291
|
+
vary=True)
|
|
292
|
+
|
|
293
|
+
params.add("y0",
|
|
294
|
+
value=position_seg[0],
|
|
295
|
+
max=position_seg[0] * 0.98,
|
|
296
|
+
min=position_seg[0] * 1.02,
|
|
297
|
+
vary=False)
|
|
298
|
+
|
|
299
|
+
params.add('a',
|
|
300
|
+
value=-1.0e1,
|
|
301
|
+
vary=True)
|
|
302
|
+
params.add("b",
|
|
303
|
+
value=-1.0e2,
|
|
304
|
+
vary=True)
|
|
305
|
+
params.add("c",
|
|
306
|
+
value=1.0e2,
|
|
307
|
+
vary=True)
|
|
308
|
+
params.add('d',
|
|
309
|
+
expr="y0")
|
|
310
|
+
|
|
311
|
+
def fcn(params, x, y):
|
|
312
|
+
parvals = params.valuesdict()
|
|
313
|
+
split = x < parvals["x0"]
|
|
314
|
+
mod1 = np.zeros_like(y)
|
|
315
|
+
mod1 += parvals["d"] + parvals["a"] * (x - parvals["x0"]) + parvals[
|
|
316
|
+
"b"] * (x - parvals["x0"]) ** 2 + parvals[
|
|
317
|
+
"c"] * (x - parvals["x0"]) ** 3
|
|
318
|
+
mod1[~split] = 0
|
|
319
|
+
|
|
320
|
+
mod2 = np.zeros_like(y)
|
|
321
|
+
mod2 += parvals["y0"]
|
|
322
|
+
mod2[split] = 0
|
|
323
|
+
|
|
324
|
+
return y - mod1 - mod2
|
|
325
|
+
|
|
326
|
+
res = lmfit.minimize(fcn=fcn,
|
|
327
|
+
params=params,
|
|
328
|
+
method='nelder',
|
|
329
|
+
args=(force_seg, position_seg))
|
|
330
|
+
fit = position_seg - res.residual
|
|
331
|
+
|
|
332
|
+
return fit, res.params
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def force_jump(params, max_force):
|
|
336
|
+
"""
|
|
337
|
+
calculating the value of the force jump from the difference between
|
|
338
|
+
the maximal force and the force value at the end of the fitted
|
|
339
|
+
vertical line in the piecewise function fitting.
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
----------
|
|
343
|
+
params: lmfit.parameter
|
|
344
|
+
piecewise fitting parameters
|
|
345
|
+
max_force: float [N]
|
|
346
|
+
the maximal value of the force
|
|
347
|
+
|
|
348
|
+
Returns
|
|
349
|
+
-------
|
|
350
|
+
force_jump_max_indent: float
|
|
351
|
+
force jump value [N]
|
|
352
|
+
jump_end: float
|
|
353
|
+
the force value at the end (smallest force) of the fitted
|
|
354
|
+
vertical line [N]
|
|
355
|
+
fitted_max_indent: float
|
|
356
|
+
the fitted maximal indentation [m]
|
|
357
|
+
"""
|
|
358
|
+
fitting_parvals = params.valuesdict()
|
|
359
|
+
jump_end = fitting_parvals['x0']
|
|
360
|
+
fitted_max_indent = fitting_parvals['y0']
|
|
361
|
+
force_jump_max_indent = max_force - jump_end
|
|
362
|
+
return force_jump_max_indent, jump_end, fitted_max_indent
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def fit_viscosity_jump_model(tip_position: object, force: object, R: object,
|
|
366
|
+
velocity: object, fraction_force: object =
|
|
367
|
+
0.85) -> object:
|
|
368
|
+
""" finding the viscosity value from the force jump in the retract curve
|
|
369
|
+
.. math::
|
|
370
|
+
mu =
|
|
371
|
+
\frac{F_jump}{2*7.25*delta_max^{1/2}*R^{1/2}*velocity}
|
|
372
|
+
|
|
373
|
+
Parameters
|
|
374
|
+
----------
|
|
375
|
+
tip_position: 1D numpy array
|
|
376
|
+
the tip position values of the cantilever's retract motion [m]
|
|
377
|
+
force: 1D numpy array
|
|
378
|
+
the force values of the cantilever's retract motion [N]
|
|
379
|
+
R: float [m]
|
|
380
|
+
the radius of the spherical tip used for indentation
|
|
381
|
+
velocity: float [m/s]
|
|
382
|
+
the absolute value of the Piezo velocity set by the user
|
|
383
|
+
fraction_force: float
|
|
384
|
+
fraction of the data points that should be taken into account for
|
|
385
|
+
fitting, the decision is made according to the maximal value of the
|
|
386
|
+
force
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
force_jump_max_indent: float
|
|
391
|
+
value of the the force jump at the maximal indentation [N]
|
|
392
|
+
fitted_max_indent: float
|
|
393
|
+
value of the fitted maximal indentation [m]
|
|
394
|
+
"""
|
|
395
|
+
position_seg, force_seg, max_position, max_force = helper_retract_fraction(
|
|
396
|
+
tip_position, force, fraction_force)
|
|
397
|
+
position_fit, params = helper_line_polynomial_fit(position_seg, force_seg)
|
|
398
|
+
force_jump_max_indent, jump_end, fitted_max_indent = force_jump(params,
|
|
399
|
+
max_force)
|
|
400
|
+
|
|
401
|
+
return force_jump_max_indent, fitted_max_indent
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
model_doc = hertz_corrected_viscelasticity_KVM.__doc__
|
|
405
|
+
model_func = hertz_corrected_viscelasticity_KVM
|
|
406
|
+
model_key = "hertz_corr_visco_KVM"
|
|
407
|
+
model_name = "Hertz model corrected for viscoelasticity using KVM model"
|
|
408
|
+
parameter_keys = ["_mod_constraint", "Eu", "Eapp", "time_ind", "R", "eta",
|
|
409
|
+
"nu",
|
|
410
|
+
"_constraint", "lmd", "velocity", "contact_point",
|
|
411
|
+
"baseline"]
|
|
412
|
+
parameter_names = ["Constraint Modulus", "Young's Modulus unrelaxed",
|
|
413
|
+
"Young's Modulus apparent",
|
|
414
|
+
"Time to indent",
|
|
415
|
+
"Tip Radius", "viscosity", "Poisson's Ratio",
|
|
416
|
+
"Constraint",
|
|
417
|
+
"Maxwell element relaxation",
|
|
418
|
+
"Velocity of indenter",
|
|
419
|
+
"Contact Point", "Force Baseline"]
|
|
420
|
+
parameter_units = ["Pa", "Pa", "Pa", "s", "m", "Pa·s", " ", " ", "s", "m/s",
|
|
421
|
+
"m", "N"]
|
|
422
|
+
parameter_anc_keys = ["force_jump_max_indent", "eta",
|
|
423
|
+
"time_ind"]
|
|
424
|
+
parameter_anc_names = ["Force jump at max indent", "viscosity",
|
|
425
|
+
"Time to indent"]
|
|
426
|
+
parameter_anc_units = ["N", "Pa·s", "s"]
|
|
427
|
+
|
|
428
|
+
valid_axes_x = ["tip position"]
|
|
429
|
+
valid_axes_y = ["force"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nanite-community
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Repository for storing nanite models
|
|
5
|
+
Author-email: Eoghan O'Connell <eoclives@hotmail.com>, Paul Müller <dev@craban.de>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: AFM,Atomic Force Microscopy,Hertz KVM,nanite,pyjibe
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: lmfit>=1.3.2
|
|
15
|
+
Requires-Dist: numpy==1.26.*
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# Nanite Community Models
|
|
19
|
+
|
|
20
|
+
This is a repository for storing
|
|
21
|
+
[nanite](https://github.com/AFM-Analysis/nanite) models. These can be models
|
|
22
|
+
that exist as part of a publication, or not. Storing them here will make them
|
|
23
|
+
easy to use in nanite and PyJibe.
|
|
24
|
+
|
|
25
|
+
## Future Plans
|
|
26
|
+
|
|
27
|
+
We soon plan to incorporate this package into the
|
|
28
|
+
[nanite](https://github.com/AFM-Analysis/nanite) package so that all
|
|
29
|
+
models are available as an optional dependency.
|
|
30
|
+
|
|
31
|
+
## Contributing
|
|
32
|
+
|
|
33
|
+
If you wish to add a model to this repository and are not familiar with git
|
|
34
|
+
and github, please make a new Issue.
|
|
35
|
+
Then, place the model code in a single python file as described in the
|
|
36
|
+
[nanite docs here](https://nanite.readthedocs.io/en/stable/sec_develop.html#writing-model-functions).
|
|
37
|
+
Then, fork this repository, make a Pull Request and choose a reviewer.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
nanite_community/__init__.py
|
|
5
|
+
nanite_community.egg-info/PKG-INFO
|
|
6
|
+
nanite_community.egg-info/SOURCES.txt
|
|
7
|
+
nanite_community.egg-info/dependency_links.txt
|
|
8
|
+
nanite_community.egg-info/requires.txt
|
|
9
|
+
nanite_community.egg-info/top_level.txt
|
|
10
|
+
nanite_community/models/__init__.py
|
|
11
|
+
nanite_community/models/model_hertz_corrected_viscoelasticity_KVM.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nanite_community
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "nanite-community"
|
|
3
|
+
description = "Repository for storing nanite models"
|
|
4
|
+
requires-python = ">=3.10"
|
|
5
|
+
authors = [
|
|
6
|
+
{name="Eoghan O'Connell", email="eoclives@hotmail.com"},
|
|
7
|
+
{name="Paul Müller", email="dev@craban.de"},
|
|
8
|
+
]
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
dependencies = [
|
|
12
|
+
"lmfit>=1.3.2",
|
|
13
|
+
"numpy==1.26.*",
|
|
14
|
+
]
|
|
15
|
+
dynamic = ["version"]
|
|
16
|
+
keywords=[
|
|
17
|
+
"AFM",
|
|
18
|
+
"Atomic Force Microscopy",
|
|
19
|
+
"Hertz KVM",
|
|
20
|
+
"nanite",
|
|
21
|
+
"pyjibe",
|
|
22
|
+
]
|
|
23
|
+
classifiers=[
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Intended Audience :: Science/Research"
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
lint = ["flake8"]
|
|
31
|
+
dev = [
|
|
32
|
+
{include-group = "lint"}
|
|
33
|
+
]
|