NSFopen 2.3.0__tar.gz → 2.4.1__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 (138) hide show
  1. {nsfopen-2.3.0 → nsfopen-2.4.1}/CHANGELOG.md +149 -103
  2. {nsfopen-2.3.0 → nsfopen-2.4.1}/MANIFEST.in +1 -1
  3. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/__init__.py +29 -29
  4. nsfopen-2.4.1/NSFopen/_version.py +1 -0
  5. nsfopen-2.4.1/NSFopen/example/Force_Spectroscopy/Contact_Models.py +48 -0
  6. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/__init__.py +9 -9
  7. nsfopen-2.4.1/NSFopen/process/colormaps.py +244 -0
  8. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/combine.py +112 -112
  9. nsfopen-2.4.1/NSFopen/process/contact.py +387 -0
  10. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/fitting.py +211 -211
  11. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/grains.py +316 -316
  12. nsfopen-2.4.1/NSFopen/process/iv.py +175 -0
  13. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/measure.py +862 -862
  14. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/ops.py +1278 -1278
  15. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/process.py +1205 -1205
  16. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/profile_iso.py +232 -232
  17. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/psdfit.py +215 -215
  18. nsfopen-2.4.1/NSFopen/process/spectroscopy.py +1005 -0
  19. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/process/surface3d.py +495 -502
  20. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/read/__init__.py +10 -10
  21. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/read/nhf.py +687 -687
  22. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/read/nhf_spec.py +27 -27
  23. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/read/read.py +654 -654
  24. {nsfopen-2.3.0 → nsfopen-2.4.1/NSFopen.egg-info}/PKG-INFO +18 -5
  25. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen.egg-info/SOURCES.txt +4 -59
  26. {nsfopen-2.3.0/NSFopen.egg-info → nsfopen-2.4.1}/PKG-INFO +18 -5
  27. {nsfopen-2.3.0 → nsfopen-2.4.1}/README.md +178 -165
  28. {nsfopen-2.3.0 → nsfopen-2.4.1}/setup.py +6 -1
  29. {nsfopen-2.3.0 → nsfopen-2.4.1}/tests/test_examples.py +26 -26
  30. {nsfopen-2.3.0 → nsfopen-2.4.1}/tests/test_nsfopen.py +711 -711
  31. nsfopen-2.3.0/NSFopen/_version.py +0 -1
  32. nsfopen-2.3.0/NSFopen/process/colormaps.py +0 -76
  33. nsfopen-2.3.0/NSFopen/process/gradients/BW1 +0 -11
  34. nsfopen-2.3.0/NSFopen/process/gradients/BW2 +0 -21
  35. nsfopen-2.3.0/NSFopen/process/gradients/Blend1 +0 -8
  36. nsfopen-2.3.0/NSFopen/process/gradients/Blend2 +0 -13
  37. nsfopen-2.3.0/NSFopen/process/gradients/Blue +0 -4
  38. nsfopen-2.3.0/NSFopen/process/gradients/Blue-Cyan +0 -5
  39. nsfopen-2.3.0/NSFopen/process/gradients/Blue-Violet +0 -5
  40. nsfopen-2.3.0/NSFopen/process/gradients/Blue-Yellow +0 -6
  41. nsfopen-2.3.0/NSFopen/process/gradients/Body +0 -7
  42. nsfopen-2.3.0/NSFopen/process/gradients/Caribbean +0 -7
  43. nsfopen-2.3.0/NSFopen/process/gradients/Clusters +0 -13
  44. nsfopen-2.3.0/NSFopen/process/gradients/Code-V +0 -14
  45. nsfopen-2.3.0/NSFopen/process/gradients/Cold +0 -7
  46. nsfopen-2.3.0/NSFopen/process/gradients/DFit +0 -15
  47. nsfopen-2.3.0/NSFopen/process/gradients/Digitalis +0 -8
  48. nsfopen-2.3.0/NSFopen/process/gradients/Gold +0 -5
  49. nsfopen-2.3.0/NSFopen/process/gradients/Gray-inverted +0 -3
  50. nsfopen-2.3.0/NSFopen/process/gradients/Green +0 -4
  51. nsfopen-2.3.0/NSFopen/process/gradients/Green-Cyan +0 -5
  52. nsfopen-2.3.0/NSFopen/process/gradients/Green-Stripes-4 +0 -17
  53. nsfopen-2.3.0/NSFopen/process/gradients/Green-Violet +0 -6
  54. nsfopen-2.3.0/NSFopen/process/gradients/Green-Yellow +0 -5
  55. nsfopen-2.3.0/NSFopen/process/gradients/Gwyddion.net +0 -5
  56. nsfopen-2.3.0/NSFopen/process/gradients/Halcyon +0 -8
  57. nsfopen-2.3.0/NSFopen/process/gradients/Lines +0 -23
  58. nsfopen-2.3.0/NSFopen/process/gradients/Maple +0 -8
  59. nsfopen-2.3.0/NSFopen/process/gradients/MetroPro +0 -32
  60. nsfopen-2.3.0/NSFopen/process/gradients/NT-MDT +0 -13
  61. nsfopen-2.3.0/NSFopen/process/gradients/Neon +0 -6
  62. nsfopen-2.3.0/NSFopen/process/gradients/Olive +0 -4
  63. nsfopen-2.3.0/NSFopen/process/gradients/Painbow +0 -62
  64. nsfopen-2.3.0/NSFopen/process/gradients/Pink +0 -4
  65. nsfopen-2.3.0/NSFopen/process/gradients/Plum +0 -6
  66. nsfopen-2.3.0/NSFopen/process/gradients/Pm3d +0 -6
  67. nsfopen-2.3.0/NSFopen/process/gradients/RGB-Blue +0 -3
  68. nsfopen-2.3.0/NSFopen/process/gradients/RGB-Green +0 -3
  69. nsfopen-2.3.0/NSFopen/process/gradients/RGB-Red +0 -3
  70. nsfopen-2.3.0/NSFopen/process/gradients/Rainbow1 +0 -10
  71. nsfopen-2.3.0/NSFopen/process/gradients/Rainbow2 +0 -6
  72. nsfopen-2.3.0/NSFopen/process/gradients/Red +0 -4
  73. nsfopen-2.3.0/NSFopen/process/gradients/Red-Cyan +0 -6
  74. nsfopen-2.3.0/NSFopen/process/gradients/Red-Stripes-5 +0 -21
  75. nsfopen-2.3.0/NSFopen/process/gradients/Red-Violet +0 -5
  76. nsfopen-2.3.0/NSFopen/process/gradients/Red-Yellow +0 -5
  77. nsfopen-2.3.0/NSFopen/process/gradients/Rust +0 -8
  78. nsfopen-2.3.0/NSFopen/process/gradients/Saw1 +0 -7
  79. nsfopen-2.3.0/NSFopen/process/gradients/Shame +0 -6
  80. nsfopen-2.3.0/NSFopen/process/gradients/Sky +0 -7
  81. nsfopen-2.3.0/NSFopen/process/gradients/Sm2 +0 -7
  82. nsfopen-2.3.0/NSFopen/process/gradients/Spectral +0 -13
  83. nsfopen-2.3.0/NSFopen/process/gradients/Spectral-white +0 -13
  84. nsfopen-2.3.0/NSFopen/process/gradients/Spring +0 -6
  85. nsfopen-2.3.0/NSFopen/process/gradients/Viridis +0 -14
  86. nsfopen-2.3.0/NSFopen/process/gradients/Warm +0 -8
  87. nsfopen-2.3.0/NSFopen/process/gradients/Warpp-mono +0 -13
  88. nsfopen-2.3.0/NSFopen/process/gradients/Warpp-spectral +0 -13
  89. nsfopen-2.3.0/NSFopen/process/gradients/Wyko +0 -13
  90. nsfopen-2.3.0/NSFopen/process/gradients/Yellow +0 -4
  91. nsfopen-2.3.0/NSFopen/process/gradients/Zones +0 -17
  92. {nsfopen-2.3.0 → nsfopen-2.4.1}/LICENSE +0 -0
  93. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Calculate_Roughness/Calculate_Roughness.html +0 -0
  94. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Calculate_Roughness/Calculate_Roughness.ipynb +0 -0
  95. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Calculate_Roughness/Calculate_Roughness.py +0 -0
  96. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Calculate_Roughness/__pycache__/Calculate_Roughness.cpython-310.pyc +0 -0
  97. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Calculate_Roughness/sapphire.nid +0 -0
  98. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Force_Spectroscopy/Force_Spectroscopy.html +0 -0
  99. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Force_Spectroscopy/Force_Spectroscopy.ipynb +0 -0
  100. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Force_Spectroscopy/Force_Spectroscopy.py +0 -0
  101. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Force_Spectroscopy/spectroscopy.nid +0 -0
  102. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Grain_Analysis/Grain_Analysis.html +0 -0
  103. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Grain_Analysis/Grain_Analysis.ipynb +0 -0
  104. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Grain_Analysis/Grain_Analysis.py +0 -0
  105. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Lateral_Force/Lateral_Force_Processing.html +0 -0
  106. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Lateral_Force/Lateral_Force_Processing.ipynb +0 -0
  107. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Lateral_Force/Lateral_Force_Processing.py +0 -0
  108. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Lateral_Force/SBS-PMMA.nid +0 -0
  109. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/Plane_Removal.html +0 -0
  110. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/Plane_Removal.ipynb +0 -0
  111. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/Plane_Removal.py +0 -0
  112. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/circles.nid +0 -0
  113. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/circles_holes.nid +0 -0
  114. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/grid.nid +0 -0
  115. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plane_Removal/squares_holes.nid +0 -0
  116. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plotting_Data/MoS2.nid +0 -0
  117. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plotting_Data/Plotting_Data.html +0 -0
  118. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plotting_Data/Plotting_Data.ipynb +0 -0
  119. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Plotting_Data/Plotting_Data.py +0 -0
  120. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Sharpen_Image/Sharpen_Image.html +0 -0
  121. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Sharpen_Image/Sharpen_Image.ipynb +0 -0
  122. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Sharpen_Image/Sharpen_Image.py +0 -0
  123. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Sharpen_Image/grid.nid +0 -0
  124. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/Spring_Constant.html +0 -0
  125. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/Spring_Constant.ipynb +0 -0
  126. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/Spring_Constant.py +0 -0
  127. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/__pycache__/Spring_Constant.cpython-310.pyc +0 -0
  128. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/__pycache__/__init__.cpython-310.pyc +0 -0
  129. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/sader.py +0 -0
  130. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/thermal.nid +0 -0
  131. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen/example/Spring_Constant/thermal.png +0 -0
  132. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen.egg-info/dependency_links.txt +0 -0
  133. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen.egg-info/entry_points.txt +0 -0
  134. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen.egg-info/not-zip-safe +0 -0
  135. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen.egg-info/requires.txt +0 -0
  136. {nsfopen-2.3.0 → nsfopen-2.4.1}/NSFopen.egg-info/top_level.txt +0 -0
  137. {nsfopen-2.3.0 → nsfopen-2.4.1}/pyproject.toml +0 -0
  138. {nsfopen-2.3.0 → nsfopen-2.4.1}/setup.cfg +0 -0
@@ -1,103 +1,149 @@
1
- # Change log
2
-
3
- ## 2.3.0 (not yet released)
4
-
5
- Compared to **2.2.4**, the latest version on PyPI (August 2024).
6
-
7
- ### Highlights
8
- - `pip install NSFopen` now works by itself: the `nanosurf` and `scikit-learn` packages are no longer needed.
9
- - NID files are read about 5 times faster, and line flattening is about twice as fast.
10
- - NHF spectroscopy files of any size open immediately, and their curves are read when asked for.
11
- - WaveMode NMA files are read, with a curve for every pixel and maps of any quantity.
12
- - `process` has many new image analysis functions, brought over from the Nanosurf Surface Analyzer.
13
- - Figures take their units and labels from the file, can be placed in subplots, and there is a new 3D plot.
14
-
15
- ### Reading files
16
- Added:
17
- - New NHF reader that needs only h5py, for file versions 1.1 and 2.0 to 2.6. Images are identical to those of Nanosurf's
18
- reader on versions 1.1, 2.0, 2.3, 2.4 and 2.6.
19
- - Spectroscopy (`afm.spec`): segments, grid position, other units through the file's calibration, force curves.
20
- - WaveMode NMA (`afm.nma`): `curve()` gives Z, interaction and force of a pixel; `map()` builds per-pixel maps.
21
- - `afm.units` holds the unit of every channel. `afm.close()` releases an NHF file; `read` also works in a `with` block.
22
- - `read(..., measurement=)` picks a measurement of an NHF file.
23
-
24
- Fixed:
25
- - 2.2.4 needed `nanosurf` to be imported but did not declare it, and failed on NHF files with `nanosurf` 2.0.7.
26
- - NID headers were read as Latin-1, so units and sizes came out as `°` and `3µm`.
27
- - Duplicate `Wait` segments of older NHF files overwrote each other: the second is now `Wait 2`.
28
- - A third NID channel of the same name overwrote the second: they are now `Amplitude`, `Amplitude2`, `Amplitude3`.
29
- - A truncated NID file now says so.
30
-
31
- ### Processing images
32
- Added:
33
- - Leveling: `level_flatten`, `level_poly2d`, `level_lines`, `level_align_rows`, `level_facet`, `level_three_point`,
34
- `level_median`, `level_rolling_ball`, `level_shift`.
35
- - Filters: `filter_basic`, `filter_median`, `filter_gaussian`, `filter_fft`, `filter_notch`.
36
- - Glitches: `glitch_spikes`, `glitch_lines`, `glitch_auto_lines`, `glitch_scars`, `glitch_step_lines`, `glitch_fill_mask`.
37
- - Masks, kept per channel: `set_mask` and `mask_threshold`, `mask_otsu`, `mask_watershed`, `mask_outliers`, `mask_scars`,
38
- `mask_shape`, `mask_floodfill`, `mask_grow`, `mask_shrink`, `mask_invert`, `mask_clear`, `mask_remove_small`,
39
- `mask_fill_holes`. Leveling, filters and statistics can use the mask.
40
- - Geometry and scale: `geometry_crop`, `geometry_flip`, `geometry_rotate`, `geometry_resample`, `geometry_square_pixels`,
41
- `rescale_scale`, `rescale_z_to_reference`, `rescale_xy`.
42
- - Statistics after ISO 25178: `statistics`, `bearing_curve`, `histogram_fit`, `slope_distributions`.
43
- - Lines: `line_profile`, `step_heights`, `critical_dimension`, `masked_step_height`, and `profile_roughness` after
44
- ISO 21920.
45
- - Spectra and texture: `psd`, `psd_fit`, `spectrum`, `acf`, `line_functions`, `rms_by_scale`, `lattice`.
46
- - Grain analysis: `grains`.
47
- - Two channels: `combine`, `find_shift`.
48
- - Access: `channels`, `array`, `set_channel`, `size`, `unit`, `origin`, and `apply` and `operations`.
49
-
50
- Fixed:
51
- - `NSFopen.process` could not be imported, because `colormaps.py` imported itself.
52
- - `flatten(inplace=False)` and similar calls changed the data as well as returning the result.
53
- - `plane_level` was wrong on non-square images.
54
- - The physical sizes of non-square NHF images were swapped.
55
-
56
- ### Figures
57
- Added:
58
- - `plot3d`, a 3D view after the Surface Analyzer's, with materials, lighting and overlays of another channel. It needs no
59
- graphics card.
60
- - `plot_profile`, `plot_psd`, `plot_histogram_fit`, `plot_bearing`, `plot_acf`.
61
- - `ax=` to draw into subplots, and `figsize`, `dpi`, `theme` and `color_range` on the plots.
62
- - `image(profile=)` shows where a line cut lies.
63
- - Color maps by name, including the Gwyddion gradients now included with the package: `cmap='Gold'`.
64
-
65
- Fixed:
66
- - The default scale bar had zero length on scans of about 2.5 µm or less, and non-integer lengths were labelled wrongly.
67
- - `hist(color=)` raised an error.
68
-
69
- ### Changes that can affect existing scripts
70
- | What | 2.2.4 | 2.3.0 |
71
- |---|---|---|
72
- | NHF force curves `[row, col]` | Position in the file | True grid position, row 0 at the bottom |
73
- | NHF force curves | Arrays | Read on access; `np.asarray(curves)` gives the array |
74
- | `image` and `hist`, `scale` | `1e9` for every channel | A readable unit from the channel; `scale=1e9` still forces it |
75
- | `hist`, `xlabel` | `Z-Axis [nm]` for every channel | The channel and its unit |
76
- | `image`, color bar | Only with `cbar=` | Always; `colorbar=False` turns it off |
77
- | `image`, pixels | matplotlib's default | `interpolation='nearest'`; can be overridden |
78
- | `remove_outliers` | Nearest neighbors with scikit-learn | Interpolation from the pixels around; the numbers differ, and `neighbors` is gone |
79
- | Unknown channel | pandas `KeyError` | `ValueError` that lists the channels |
80
- | Default channel | `Z-Axis` only | Falls back to `Topography` in files that have no `Z-Axis` |
81
- | `NSFopen.read.checkFileType` | Present | Removed |
82
-
83
- `stats()` keeps its results, including `Sku` as excess kurtosis (0 for a Gaussian surface). The new `statistics()` gives
84
- the ISO value of `Sku` (3 for a Gaussian surface).
85
-
86
- ### Examples
87
- - All are rewritten around `process`, and there is a new `Grain_Analysis`.
88
- - **`Spring_Constant`: the result changes from 14.5 to 24.0 N/m.** The old fit used a power formula on amplitude data and
89
- found Q = 253; the file records Nanosurf's own Q of 442, and the corrected fit finds 417.
90
- - `Plotting_Data`: the image was drawn upside down.
91
- - `Force_Spectroscopy`: the map axes were labelled µm but showed pixels.
92
- - `Sharpen_Image`: it no longer passes the data through an 8-bit JPEG, and needs neither OpenCV nor PIL.
93
- - `Plane_Removal`: it crashed on non-square images, and its clipping changed the data.
94
- - `Lateral_Force`: the offset between the scans is measured instead of set by hand.
95
- - Renamed without spaces: `Plotting_Data.py`, `Lateral_Force_Processing.py`, `Sharpen_Image.py`.
96
-
97
- ### Requirements
98
- - matplotlib 3.6 or newer; the other requirements are unchanged (numpy, scipy, pandas, h5py, Python 3.9 or newer).
99
- - Removed: `nanosurf` and `scikit-learn`, which 2.2.4 used without declaring them.
100
-
101
- ## Earlier versions
102
- The earlier releases have no change notes. Their list and dates are on
103
- [PyPI](https://pypi.org/project/NSFopen/#history).
1
+ # Change log
2
+
3
+ ## 2.4.1 (November 4, 2026)
4
+
5
+ ### Fixed
6
+ - Reading a NID file gave a pandas warning (`Series.dropna(inplace=True)` is deprecated from pandas 3.1). The data is
7
+ the same.
8
+
9
+ ## 2.4.0 (November 4, 2026)
10
+
11
+ Compared to **2.3.0**.
12
+
13
+ ### Force spectroscopy analysis
14
+ Added, in `NSFopen.process` (the calculations of the Spectroscopy page of NSSA, the NSF Surface and Spectroscopy
15
+ Analyzer, so a script and the program give the same numbers):
16
+ - `contact`: elastic contact of a tip pressed into a flat sample. Models Hertz, Sneddon (exact), DMT, JKR and
17
+ Oliver-Pharr; tip shapes sphere, cone, four-sided pyramid, flat cylinder punch and hyperboloid. `fit_elastic`,
18
+ `fit_jkr`, `fit_oliver_pharr` give the Young's modulus, reduced modulus, contact point, adhesion and the fit quality.
19
+ - `spectroscopy`: `SpecFile` opens the force curves and force maps of NID and NHF files. `ForceSettings` and
20
+ `ForceAnalysis` process them: smoothing, baseline (offset, tilt, or an interference sine with its period found),
21
+ contact point, indentation, adhesion, pull-off, adhesion and dissipated energy, and the model fit. `Results` has
22
+ statistics, maps, masks, fits of different models on different regions, a quality filter (leave out fits below an R²)
23
+ and CSV export. Maps can be analyzed partly (every n-th line and column, or a region).
24
+ - `iv`: I-V curves with dI/dV, normalized conductance, zero-bias conductance and resistance, gap and rectification.
25
+
26
+ ### Color maps
27
+ - 12 maps are defined in `colormaps.py` (`NANOSURF`): Sky (the default for heights), Warm, Flare, Caribbean, Strata,
28
+ Gold, Digitalis, Neon, Plum, Cold, Spectrum and Maple, plus BlueBlackRed. `register()` adds them and their reversed
29
+ versions (`Sky_r`) to matplotlib; `colormap(name)` returns any of them or a matplotlib map.
30
+ - The 59 Gwyddion gradient files that 2.3.0 included are removed: they are GPL, NSFopen is MIT. An installed Gwyddion's
31
+ gradients are found on Windows, macOS and Linux (`gwyddion_dirs`, `gwyddion_gradients`, `register(gwyddion=True)`), and
32
+ `colors(name)` uses them when the name is not one of the 12.
33
+
34
+ ### Changes that can affect existing scripts
35
+ | What | 2.3.0 | 2.4.0 |
36
+ |---|---|---|
37
+ | `colors(name)` with a Gwyddion name | 59 gradient files in the package | 12 maps defined in the code; `Pm3d`, `Blend2`, `Code-V` open as `Flare`, `Strata`, `Spectrum`; the others only with an installed Gwyddion or `gwy_dir=` |
38
+ | `colormaps.GRADIENTS` | The gradient folder | Removed |
39
+
40
+ ### Examples
41
+ - New `Force_Spectroscopy/Contact_Models.py`: a force map fitted with `process.spectroscopy`, with statistics, the
42
+ quality filter, maps of the modulus and adhesion, and the stiffest curve with its fit.
43
+
44
+ ### Packaging
45
+ - Credentials (`tokens.json`, `.env`, `*.token`) in the example folders are never packaged.
46
+
47
+ ## 2.3.0 (November 2, 2026, yanked on PyPI)
48
+
49
+ Yanked because the package included the GPL-licensed Gwyddion gradient files; use 2.4.0 or newer.
50
+
51
+ Compared to **2.2.4**, the latest version on PyPI (August 2024).
52
+
53
+ ### Highlights
54
+ - `pip install NSFopen` now works by itself: the `nanosurf` and `scikit-learn` packages are no longer needed.
55
+ - NID files are read about 5 times faster, and line flattening is about twice as fast.
56
+ - NHF spectroscopy files of any size open immediately, and their curves are read when asked for.
57
+ - WaveMode NMA files are read, with a curve for every pixel and maps of any quantity.
58
+ - `process` has many new image analysis functions, brought over from the Nanosurf Surface Analyzer (now NSSA).
59
+ - Figures take their units and labels from the file, can be placed in subplots, and there is a new 3D plot.
60
+
61
+ ### Reading files
62
+ Added:
63
+ - New NHF reader that needs only h5py, for file versions 1.1 and 2.0 to 2.6. Images are identical to those of Nanosurf's
64
+ reader on versions 1.1, 2.0, 2.3, 2.4 and 2.6.
65
+ - Spectroscopy (`afm.spec`): segments, grid position, other units through the file's calibration, force curves.
66
+ - WaveMode NMA (`afm.nma`): `curve()` gives Z, interaction and force of a pixel; `map()` builds per-pixel maps.
67
+ - `afm.units` holds the unit of every channel. `afm.close()` releases an NHF file; `read` also works in a `with` block.
68
+ - `read(..., measurement=)` picks a measurement of an NHF file.
69
+
70
+ Fixed:
71
+ - 2.2.4 needed `nanosurf` to be imported but did not declare it, and failed on NHF files with `nanosurf` 2.0.7.
72
+ - NID headers were read as Latin-1, so units and sizes came out as `°` and `3µm`.
73
+ - Duplicate `Wait` segments of older NHF files overwrote each other: the second is now `Wait 2`.
74
+ - A third NID channel of the same name overwrote the second: they are now `Amplitude`, `Amplitude2`, `Amplitude3`.
75
+ - A truncated NID file now says so.
76
+
77
+ ### Processing images
78
+ Added:
79
+ - Leveling: `level_flatten`, `level_poly2d`, `level_lines`, `level_align_rows`, `level_facet`, `level_three_point`,
80
+ `level_median`, `level_rolling_ball`, `level_shift`.
81
+ - Filters: `filter_basic`, `filter_median`, `filter_gaussian`, `filter_fft`, `filter_notch`.
82
+ - Glitches: `glitch_spikes`, `glitch_lines`, `glitch_auto_lines`, `glitch_scars`, `glitch_step_lines`, `glitch_fill_mask`.
83
+ - Masks, kept per channel: `set_mask` and `mask_threshold`, `mask_otsu`, `mask_watershed`, `mask_outliers`, `mask_scars`,
84
+ `mask_shape`, `mask_floodfill`, `mask_grow`, `mask_shrink`, `mask_invert`, `mask_clear`, `mask_remove_small`,
85
+ `mask_fill_holes`. Leveling, filters and statistics can use the mask.
86
+ - Geometry and scale: `geometry_crop`, `geometry_flip`, `geometry_rotate`, `geometry_resample`, `geometry_square_pixels`,
87
+ `rescale_scale`, `rescale_z_to_reference`, `rescale_xy`.
88
+ - Statistics after ISO 25178: `statistics`, `bearing_curve`, `histogram_fit`, `slope_distributions`.
89
+ - Lines: `line_profile`, `step_heights`, `critical_dimension`, `masked_step_height`, and `profile_roughness` after
90
+ ISO 21920.
91
+ - Spectra and texture: `psd`, `psd_fit`, `spectrum`, `acf`, `line_functions`, `rms_by_scale`, `lattice`.
92
+ - Grain analysis: `grains`.
93
+ - Two channels: `combine`, `find_shift`.
94
+ - Access: `channels`, `array`, `set_channel`, `size`, `unit`, `origin`, and `apply` and `operations`.
95
+
96
+ Fixed:
97
+ - `NSFopen.process` could not be imported, because `colormaps.py` imported itself.
98
+ - `flatten(inplace=False)` and similar calls changed the data as well as returning the result.
99
+ - `plane_level` was wrong on non-square images.
100
+ - The physical sizes of non-square NHF images were swapped.
101
+
102
+ ### Figures
103
+ Added:
104
+ - `plot3d`, a 3D view after the Surface Analyzer's, with materials, lighting and overlays of another channel. It needs no
105
+ graphics card.
106
+ - `plot_profile`, `plot_psd`, `plot_histogram_fit`, `plot_bearing`, `plot_acf`.
107
+ - `ax=` to draw into subplots, and `figsize`, `dpi`, `theme` and `color_range` on the plots.
108
+ - `image(profile=)` shows where a line cut lies.
109
+ - Color maps by name, including the Gwyddion gradients now included with the package: `cmap='Gold'`.
110
+
111
+ Fixed:
112
+ - The default scale bar had zero length on scans of about 2.5 µm or less, and non-integer lengths were labelled wrongly.
113
+ - `hist(color=)` raised an error.
114
+
115
+ ### Changes that can affect existing scripts
116
+ | What | 2.2.4 | 2.3.0 |
117
+ |---|---|---|
118
+ | NHF force curves `[row, col]` | Position in the file | True grid position, row 0 at the bottom |
119
+ | NHF force curves | Arrays | Read on access; `np.asarray(curves)` gives the array |
120
+ | `image` and `hist`, `scale` | `1e9` for every channel | A readable unit from the channel; `scale=1e9` still forces it |
121
+ | `hist`, `xlabel` | `Z-Axis [nm]` for every channel | The channel and its unit |
122
+ | `image`, color bar | Only with `cbar=` | Always; `colorbar=False` turns it off |
123
+ | `image`, pixels | matplotlib's default | `interpolation='nearest'`; can be overridden |
124
+ | `remove_outliers` | Nearest neighbors with scikit-learn | Interpolation from the pixels around; the numbers differ, and `neighbors` is gone |
125
+ | Unknown channel | pandas `KeyError` | `ValueError` that lists the channels |
126
+ | Default channel | `Z-Axis` only | Falls back to `Topography` in files that have no `Z-Axis` |
127
+ | `NSFopen.read.checkFileType` | Present | Removed |
128
+
129
+ `stats()` keeps its results, including `Sku` as excess kurtosis (0 for a Gaussian surface). The new `statistics()` gives
130
+ the ISO value of `Sku` (3 for a Gaussian surface).
131
+
132
+ ### Examples
133
+ - All are rewritten around `process`, and there is a new `Grain_Analysis`.
134
+ - **`Spring_Constant`: the result changes from 14.5 to 24.0 N/m.** The old fit used a power formula on amplitude data and
135
+ found Q = 253; the file records Nanosurf's own Q of 442, and the corrected fit finds 417.
136
+ - `Plotting_Data`: the image was drawn upside down.
137
+ - `Force_Spectroscopy`: the map axes were labelled µm but showed pixels.
138
+ - `Sharpen_Image`: it no longer passes the data through an 8-bit JPEG, and needs neither OpenCV nor PIL.
139
+ - `Plane_Removal`: it crashed on non-square images, and its clipping changed the data.
140
+ - `Lateral_Force`: the offset between the scans is measured instead of set by hand.
141
+ - Renamed without spaces: `Plotting_Data.py`, `Lateral_Force_Processing.py`, `Sharpen_Image.py`.
142
+
143
+ ### Requirements
144
+ - matplotlib 3.6 or newer; the other requirements are unchanged (numpy, scipy, pandas, h5py, Python 3.9 or newer).
145
+ - Removed: `nanosurf` and `scikit-learn`, which 2.2.4 used without declaring them.
146
+
147
+ ## Earlier versions
148
+ The earlier releases have no change notes. Their list and dates are on
149
+ [PyPI](https://pypi.org/project/NSFopen/#history).
@@ -1 +1 @@
1
- include CHANGELOG.md
1
+ include CHANGELOG.md
@@ -1,29 +1,29 @@
1
- """Package for scripting the Nanosurf control software.
2
- Copyright (C) Nanosurf AG - All Rights Reserved (2021)
3
- License - MIT"""
4
-
5
- import os
6
- import pathlib
7
- from ._version import __version__
8
-
9
- from .read import read
10
-
11
- # from . process import process
12
-
13
-
14
- def package_path() -> pathlib.Path:
15
- return pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
16
-
17
-
18
- def example_path() -> pathlib.Path:
19
- return package_path() / "example"
20
-
21
-
22
- def help():
23
- print("Nanosurf NID and NHF file reader and image analysis:")
24
- print("-------------------------------")
25
- print(f"Installed version: {__version__}")
26
- print("\nExamples are stored here:")
27
- print(example_path())
28
- for example in os.listdir(example_path()):
29
- print(f" {example}")
1
+ """Package for scripting the Nanosurf control software.
2
+ Copyright (C) Nanosurf AG - All Rights Reserved (2021)
3
+ License - MIT"""
4
+
5
+ import os
6
+ import pathlib
7
+ from ._version import __version__
8
+
9
+ from .read import read
10
+
11
+ # from . process import process
12
+
13
+
14
+ def package_path() -> pathlib.Path:
15
+ return pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
16
+
17
+
18
+ def example_path() -> pathlib.Path:
19
+ return package_path() / "example"
20
+
21
+
22
+ def help():
23
+ print("Nanosurf NID and NHF file reader and image analysis:")
24
+ print("-------------------------------")
25
+ print(f"Installed version: {__version__}")
26
+ print("\nExamples are stored here:")
27
+ print(example_path())
28
+ for example in os.listdir(example_path()):
29
+ print(f" {example}")
@@ -0,0 +1 @@
1
+ __version__ = "2.4.1"
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python
2
+ # coding: utf-8
3
+ """ Fitting a force map with the contact models of NSFopen.process.spectroscopy and contact (NSFopen 2.4).
4
+ The same calculations as the Spectroscopy page of the Nanosurf Surface Analyzer, so a script and the app give the same
5
+ numbers. Run it from this folder, next to spectroscopy.nid. The tip radius and Poisson ratio are assumptions: the
6
+ modulus scales with them, so set them for your own sample. """
7
+
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+
11
+ from NSFopen.process import contact, spectroscopy
12
+
13
+ spec = spectroscopy.SpecFile("spectroscopy.nid")
14
+ print(f"{spec.n_curves} curves, grid {spec.grid}, k = {spec.spring_constant:.4g} N/m")
15
+ print("Models:", ", ".join(label for key, label in contact.MODELS))
16
+
17
+ # DMT on the retract curves, spherical tip, offset and tilt removed from the first 40 % of the approach
18
+ settings = spectroscopy.ForceSettings(model="dmt", shape="sphere", radius=10e-9, poisson=0.35,
19
+ baseline="linear", baseline_from=0, baseline_to=40)
20
+ results = spectroscopy.ForceAnalysis(spec, settings).run()
21
+ results.min_r2 = 0.95 # leave out fits that do not describe their curve
22
+
23
+ for name, unit, n, mean, std, median, low, high in results.statistics():
24
+ print(f"{name:28s} N={n:4d} mean {spectroscopy.format_value(mean, unit):>12s} "
25
+ f"std {spectroscopy.format_value(std, unit):>12s}")
26
+ results.write_csv("spectroscopy_results.csv")
27
+
28
+ # Maps of the modulus and the adhesion (NaN: left out or not fitted)
29
+ fig, axes = plt.subplots(1, 2, figsize=(10, 4))
30
+ for ax, name in zip(axes, ("E", "adhesion force")):
31
+ values = results.map(name)
32
+ image = ax.imshow(values, origin="lower", cmap="viridis")
33
+ ax.set_title(f"{name} [{results.units[name]}]")
34
+ fig.colorbar(image, ax=ax)
35
+ plt.tight_layout()
36
+ plt.show()
37
+
38
+ # The stiffest curve with its fit: force against the indentation
39
+ curve = spectroscopy.ForceAnalysis(spec, settings).curve(int(np.nanargmax(results.column("E"))))
40
+ segment = curve.retract
41
+ plt.figure()
42
+ plt.plot((segment.delta - curve.contact_delta) * 1e9, segment.force * 1e9, label="retract")
43
+ plt.plot((curve.fit.fit_delta - curve.contact_delta) * 1e9, curve.fit.fit_force * 1e9, "--", label="DMT fit")
44
+ plt.xlabel("indentation [nm]")
45
+ plt.ylabel("force [nN]")
46
+ plt.title(f"curve {curve.index + 1}: E = {spectroscopy.format_value(curve.fit.modulus, 'Pa')}")
47
+ plt.legend()
48
+ plt.show()
@@ -1,9 +1,9 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Created on Sat Oct 12 12:43:59 2019
4
-
5
- @author: Edward
6
- """
7
-
8
- from . process import process, n_bins, OpError
9
- from . colormaps import colors
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sat Oct 12 12:43:59 2019
4
+
5
+ @author: Edward
6
+ """
7
+
8
+ from . process import process, n_bins, OpError
9
+ from . colormaps import colors