@pikaa-ai/pikaa 0.3.23 → 0.3.25

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 (191) hide show
  1. package/assets/brand/orbit-logo-option4-whale.jpg +0 -0
  2. package/assets/brand/orbit-logo.jpg +0 -0
  3. package/assets/brand/orbit-logo.png +0 -0
  4. package/assets/brand/orbit-logo.svg +3 -0
  5. package/dist/cli.js +407 -219
  6. package/dist/index.js +7 -2
  7. package/package.json +1 -2
  8. package/skills/adaptyv/SKILL.md +0 -240
  9. package/skills/aeon/SKILL.md +0 -402
  10. package/skills/analytical-method-validation/SKILL.md +0 -299
  11. package/skills/anndata/SKILL.md +0 -431
  12. package/skills/arbor/SKILL.md +0 -152
  13. package/skills/arboreto/SKILL.md +0 -267
  14. package/skills/astropy/SKILL.md +0 -353
  15. package/skills/autoskill/SKILL.md +0 -233
  16. package/skills/benchling-integration/SKILL.md +0 -229
  17. package/skills/bgpt-paper-search/SKILL.md +0 -75
  18. package/skills/bids/SKILL.md +0 -237
  19. package/skills/biopython/SKILL.md +0 -472
  20. package/skills/bioservices/SKILL.md +0 -399
  21. package/skills/bulk-rnaseq/SKILL.md +0 -198
  22. package/skills/cellxgene-census/SKILL.md +0 -283
  23. package/skills/cirq/SKILL.md +0 -370
  24. package/skills/citation-management/SKILL.md +0 -329
  25. package/skills/clinical-decision-support/SKILL.md +0 -238
  26. package/skills/clinical-decision-support/references/README.md +0 -62
  27. package/skills/clinical-reports/SKILL.md +0 -248
  28. package/skills/clinical-reports/references/README.md +0 -34
  29. package/skills/cobrapy/SKILL.md +0 -496
  30. package/skills/consciousness-council/SKILL.md +0 -151
  31. package/skills/dask/SKILL.md +0 -482
  32. package/skills/database-lookup/SKILL.md +0 -386
  33. package/skills/datamol/SKILL.md +0 -200
  34. package/skills/deepchem/SKILL.md +0 -244
  35. package/skills/deepspot-m/SKILL.md +0 -175
  36. package/skills/deeptools/SKILL.md +0 -412
  37. package/skills/depmap/SKILL.md +0 -301
  38. package/skills/dhdna-profiler/SKILL.md +0 -184
  39. package/skills/diffdock/SKILL.md +0 -488
  40. package/skills/dnanexus-integration/SKILL.md +0 -325
  41. package/skills/docx/SKILL.md +0 -99
  42. package/skills/esm/SKILL.md +0 -334
  43. package/skills/etetoolkit/SKILL.md +0 -327
  44. package/skills/exa-search/SKILL.md +0 -102
  45. package/skills/executing-plans/SKILL.md +0 -14
  46. package/skills/experimental-design/SKILL.md +0 -234
  47. package/skills/exploratory-data-analysis/SKILL.md +0 -280
  48. package/skills/flowio/SKILL.md +0 -310
  49. package/skills/fluidsim/SKILL.md +0 -279
  50. package/skills/frontend-design/SKILL.md +0 -100
  51. package/skills/generate-image/SKILL.md +0 -304
  52. package/skills/geniml/SKILL.md +0 -310
  53. package/skills/genomic-coordinates/SKILL.md +0 -189
  54. package/skills/genomic-intelligence/SKILL.md +0 -243
  55. package/skills/geomaster/README.md +0 -105
  56. package/skills/geomaster/SKILL.md +0 -366
  57. package/skills/geopandas/SKILL.md +0 -250
  58. package/skills/get-available-resources/SKILL.md +0 -260
  59. package/skills/gget/SKILL.md +0 -153
  60. package/skills/ginkgo-cloud-lab/SKILL.md +0 -106
  61. package/skills/glycoengineering/SKILL.md +0 -339
  62. package/skills/gtars/SKILL.md +0 -282
  63. package/skills/guardian-rails/SKILL.md +0 -54
  64. package/skills/histolab/SKILL.md +0 -243
  65. package/skills/hugging-science/SKILL.md +0 -132
  66. package/skills/hypogenic/SKILL.md +0 -290
  67. package/skills/hypothesis-generation/SKILL.md +0 -264
  68. package/skills/imaging-data-commons/SKILL.md +0 -496
  69. package/skills/infographics/SKILL.md +0 -315
  70. package/skills/iso-standards-readiness/SKILL.md +0 -352
  71. package/skills/lab-hardware-cad/SKILL.md +0 -372
  72. package/skills/labarchive-integration/SKILL.md +0 -216
  73. package/skills/lamindb/SKILL.md +0 -408
  74. package/skills/latchbio-integration/SKILL.md +0 -227
  75. package/skills/latex-posters/SKILL.md +0 -369
  76. package/skills/latex-posters/references/README.md +0 -439
  77. package/skills/liteparse/SKILL.md +0 -295
  78. package/skills/literature-review/SKILL.md +0 -263
  79. package/skills/markdown-mermaid-writing/SKILL.md +0 -322
  80. package/skills/market-research-reports/SKILL.md +0 -337
  81. package/skills/markitdown/SKILL.md +0 -264
  82. package/skills/matchms/SKILL.md +0 -276
  83. package/skills/matlab/SKILL.md +0 -274
  84. package/skills/matplotlib/SKILL.md +0 -378
  85. package/skills/medchem/SKILL.md +0 -321
  86. package/skills/modal/SKILL.md +0 -468
  87. package/skills/molecular-dynamics/SKILL.md +0 -458
  88. package/skills/molfeat/SKILL.md +0 -348
  89. package/skills/ncats-arax/SKILL.md +0 -178
  90. package/skills/networkx/SKILL.md +0 -440
  91. package/skills/neurokit2/SKILL.md +0 -323
  92. package/skills/neuropixels-analysis/SKILL.md +0 -412
  93. package/skills/nextflow/SKILL.md +0 -195
  94. package/skills/omero-integration/SKILL.md +0 -222
  95. package/skills/onekgpd/SKILL.md +0 -371
  96. package/skills/ontology-term-resolution/SKILL.md +0 -147
  97. package/skills/open-notebook/SKILL.md +0 -297
  98. package/skills/openpiv/SKILL.md +0 -469
  99. package/skills/opentrons-integration/SKILL.md +0 -322
  100. package/skills/optimize-for-gpu/SKILL.md +0 -176
  101. package/skills/owasp-top10/SKILL.md +0 -48
  102. package/skills/pacsomatic/LICENSE +0 -21
  103. package/skills/pacsomatic/SKILL.md +0 -150
  104. package/skills/paper-lookup/SKILL.md +0 -263
  105. package/skills/paperclip/SKILL.md +0 -413
  106. package/skills/paperzilla/SKILL.md +0 -159
  107. package/skills/parallel-web/SKILL.md +0 -128
  108. package/skills/pathml/SKILL.md +0 -222
  109. package/skills/pathogen-variant-surveillance/SKILL.md +0 -208
  110. package/skills/pathway-enrichment/SKILL.md +0 -194
  111. package/skills/pdf/SKILL.md +0 -322
  112. package/skills/peer-review/SKILL.md +0 -288
  113. package/skills/penetration-testing/SKILL.md +0 -31
  114. package/skills/pennylane/SKILL.md +0 -240
  115. package/skills/phylogenetics/SKILL.md +0 -409
  116. package/skills/pi-agent/SKILL.md +0 -83
  117. package/skills/pkpd-modeling/SKILL.md +0 -381
  118. package/skills/polars/SKILL.md +0 -393
  119. package/skills/polars-bio/SKILL.md +0 -379
  120. package/skills/ponytail/SKILL.md +0 -31
  121. package/skills/ponytail-audit/SKILL.md +0 -18
  122. package/skills/pptx/SKILL.md +0 -246
  123. package/skills/pptx-posters/SKILL.md +0 -258
  124. package/skills/primekg/SKILL.md +0 -99
  125. package/skills/protocolsio-integration/SKILL.md +0 -236
  126. package/skills/pufferlib/SKILL.md +0 -328
  127. package/skills/pydeseq2/SKILL.md +0 -369
  128. package/skills/pydicom/SKILL.md +0 -381
  129. package/skills/pyhealth/SKILL.md +0 -124
  130. package/skills/pylabrobot/SKILL.md +0 -216
  131. package/skills/pymatgen/SKILL.md +0 -404
  132. package/skills/pymc/SKILL.md +0 -310
  133. package/skills/pymoo/SKILL.md +0 -276
  134. package/skills/pyopenms/SKILL.md +0 -179
  135. package/skills/pysam/SKILL.md +0 -330
  136. package/skills/pytdc/SKILL.md +0 -297
  137. package/skills/pytorch-lightning/SKILL.md +0 -191
  138. package/skills/pyzotero/SKILL.md +0 -137
  139. package/skills/qiskit/SKILL.md +0 -259
  140. package/skills/qutip/SKILL.md +0 -317
  141. package/skills/rdkit/SKILL.md +0 -94
  142. package/skills/relsa-severity-assessment/SKILL.md +0 -354
  143. package/skills/research-grants/SKILL.md +0 -296
  144. package/skills/research-grants/references/README.md +0 -287
  145. package/skills/research-lookup/README.md +0 -106
  146. package/skills/research-lookup/SKILL.md +0 -338
  147. package/skills/rowan/SKILL.md +0 -398
  148. package/skills/scanpy/SKILL.md +0 -303
  149. package/skills/scholar-evaluation/SKILL.md +0 -296
  150. package/skills/scientific-brainstorming/SKILL.md +0 -282
  151. package/skills/scientific-critical-thinking/SKILL.md +0 -180
  152. package/skills/scientific-schematics/SKILL.md +0 -370
  153. package/skills/scientific-slides/SKILL.md +0 -379
  154. package/skills/scientific-visualization/SKILL.md +0 -285
  155. package/skills/scientific-writing/SKILL.md +0 -356
  156. package/skills/scikit-bio/SKILL.md +0 -470
  157. package/skills/scikit-learn/SKILL.md +0 -324
  158. package/skills/scikit-survival/SKILL.md +0 -313
  159. package/skills/scvelo/SKILL.md +0 -328
  160. package/skills/scvi-tools/SKILL.md +0 -201
  161. package/skills/seaborn/SKILL.md +0 -254
  162. package/skills/security-auditor/SKILL.md +0 -37
  163. package/skills/shap/SKILL.md +0 -282
  164. package/skills/simpy/SKILL.md +0 -283
  165. package/skills/stable-baselines3/SKILL.md +0 -325
  166. package/skills/statistical-analysis/SKILL.md +0 -446
  167. package/skills/statistical-power/SKILL.md +0 -200
  168. package/skills/statsmodels/SKILL.md +0 -238
  169. package/skills/sympy/SKILL.md +0 -354
  170. package/skills/systematic-debugging/SKILL.md +0 -35
  171. package/skills/tamarind/SKILL.md +0 -285
  172. package/skills/tdd/SKILL.md +0 -26
  173. package/skills/tiledbvcf/SKILL.md +0 -456
  174. package/skills/timesfm-forecasting/SKILL.md +0 -408
  175. package/skills/timesfm-forecasting/examples/global-temperature/README.md +0 -178
  176. package/skills/torch-geometric/SKILL.md +0 -458
  177. package/skills/torchdrug/SKILL.md +0 -241
  178. package/skills/transformers/SKILL.md +0 -195
  179. package/skills/treatment-plans/SKILL.md +0 -174
  180. package/skills/treatment-plans/references/README.md +0 -19
  181. package/skills/umap-learn/SKILL.md +0 -488
  182. package/skills/uncertainty-and-units/SKILL.md +0 -384
  183. package/skills/usfiscaldata/SKILL.md +0 -171
  184. package/skills/vaex/SKILL.md +0 -204
  185. package/skills/venue-templates/SKILL.md +0 -269
  186. package/skills/verification-before-completion/SKILL.md +0 -22
  187. package/skills/waypoint-bio/SKILL.md +0 -273
  188. package/skills/what-if-oracle/SKILL.md +0 -184
  189. package/skills/writing-plans/SKILL.md +0 -15
  190. package/skills/xlsx/SKILL.md +0 -110
  191. package/skills/zarr-python/SKILL.md +0 -241
@@ -1,469 +0,0 @@
1
- ---
2
- name: openpiv
3
- description: Particle Image Velocimetry (PIV) analysis with OpenPIV. Use when extracting velocity fields from PIV image pairs, analyzing fluid dynamics or flow visualization experiments, cross-correlating interrogation windows, validating and replacing spurious PIV vectors, or computing vorticity, strain rate, and turbulence statistics from measured velocity fields.
4
- license: BSD-3-Clause
5
- compatibility: Requires Python 3.10+ with openpiv installed (uv pip install openpiv). numpy, scipy, scikit-image, and matplotlib arrive as dependencies. No network access needed after install.
6
- allowed-tools: Read Write Edit Bash
7
- metadata:
8
- version: "1.1"
9
- skill-author: OpenPIV Team
10
- tested-against: "openpiv 0.25.4"
11
- ---
12
-
13
- # OpenPIV
14
-
15
- ## Overview
16
-
17
- OpenPIV (Open Particle Image Velocimetry) analyzes fluid flow from PIV image pairs. It covers
18
- preprocessing, cross-correlation, vector validation, outlier replacement, smoothing, and scaling to
19
- physical units.
20
-
21
- Everything below is verified against **openpiv 0.25.4**. The API moves between releases — check
22
- `inspect.signature()` before trusting a snippet against a different version.
23
-
24
- ## When to use
25
-
26
- Use this skill when working with experimental PIV or flow-visualization image pairs: measuring 2D
27
- velocity fields, tuning interrogation-window parameters, validating vectors, or deriving vorticity,
28
- strain rate, and turbulence statistics. For *simulating* flow rather than measuring it, use a CFD
29
- skill instead.
30
-
31
- ## Quick Start
32
-
33
- Install OpenPIV:
34
-
35
- ```bash
36
- uv pip install openpiv
37
-
38
- # Pin it when the analysis needs to be reproducible -- this is the version every
39
- # snippet below was checked against.
40
- uv pip install "openpiv==0.25.4"
41
- ```
42
-
43
- Run PIV analysis on an image pair:
44
-
45
- ```python
46
- import numpy as np
47
- from openpiv import tools, pyprocess, validation, filters, scaling
48
-
49
- frame_a = tools.imread("image_a.bmp")
50
- frame_b = tools.imread("image_b.bmp")
51
-
52
- # Cross-correlate. Returns (u, v, s2n) whenever sig2noise_method is not None.
53
- u, v, s2n = pyprocess.extended_search_area_piv(
54
- frame_a.astype(np.int32),
55
- frame_b.astype(np.int32),
56
- window_size=32,
57
- overlap=12,
58
- dt=0.02,
59
- search_area_size=38,
60
- correlation_method="linear", # required for search_area_size > window_size
61
- sig2noise_method="peak2peak",
62
- )
63
-
64
- x, y = pyprocess.get_coordinates(
65
- image_size=frame_a.shape,
66
- search_area_size=38,
67
- overlap=12,
68
- )
69
-
70
- # flags is a boolean array: True marks a spurious vector.
71
- flags = validation.sig2noise_val(s2n, threshold=1.05)
72
- u, v = filters.replace_outliers(u, v, flags, method="localmean", max_iter=3, kernel_size=2)
73
-
74
- # Scale to physical units, then flip to image coordinates for plotting.
75
- x, y, u, v = scaling.uniform(x, y, u, v, scaling_factor=96.52)
76
- x, y, u, v = tools.transform_coordinates(x, y, u, v)
77
-
78
- tools.save("vectors.txt", x, y, u, v, flags)
79
- ```
80
-
81
- Or use the bundled CLI, which wraps exactly that pipeline:
82
-
83
- ```bash
84
- python skills/openpiv/scripts/runner.py \
85
- --image frame_a.bmp --image frame_b.bmp --output_dir results --verbose
86
- ```
87
-
88
- ## Core Concepts
89
-
90
- ### PIV Fundamentals
91
-
92
- Particle Image Velocimetry is an optical method for measuring fluid velocity by tracking illuminated
93
- tracer particles between two images.
94
-
95
- **Process flow:**
96
-
97
- 1. Capture an image pair (`frame_a`, `frame_b`) separated by a known time `dt`.
98
- 2. Divide the images into interrogation windows.
99
- 3. Cross-correlate matching windows to find peak displacement.
100
- 4. Validate vectors (signal-to-noise, global range, local median).
101
- 5. Replace spurious vectors with interpolated values.
102
- 6. Scale pixel displacements to physical units.
103
-
104
- ### Interrogation Window Parameters
105
-
106
- **`window_size`** — correlation window in pixels (typically 16–128). Larger windows give better
107
- correlation but coarser spatial resolution.
108
-
109
- **`overlap`** — pixels shared between adjacent windows (typically 50–75% of `window_size`). Higher
110
- overlap raises vector density and cost, but adjacent vectors become correlated rather than
111
- independent.
112
-
113
- **`search_area_size`** — the window searched in the second frame. Must be ≥ `window_size`; a few
114
- pixels larger accommodates larger displacements. Pair an extended search area with
115
- `correlation_method="linear"` — the default `"circular"` relies on FFT wrap-around and aliases large
116
- displacements into small ones. See `references/advanced_algorithms.md`.
117
-
118
- Rules of thumb: keep the largest displacement under about a quarter of `window_size`, and aim for
119
- 5–10 particles per window.
120
-
121
- ### Signal-to-Noise Ratio
122
-
123
- `s2n` measures how distinct the correlation peak is. `sig2noise_method` controls how it is computed —
124
- `"peak2mean"` (the function default) or `"peak2peak"`. **The two are on different scales**, so a
125
- threshold tuned for one is meaningless for the other. Typical `peak2peak` thresholds are 1.05–1.3.
126
-
127
- ```python
128
- flags = validation.sig2noise_val(s2n, threshold=1.05)
129
- # flags is bool: True == spurious. `~flags` selects the good vectors.
130
- ```
131
-
132
- ## Common Operations
133
-
134
- ### Dynamic Masking
135
-
136
- Masking lives in `openpiv.preprocess`, **not** in an `openpiv.masking` module. It returns an
137
- `(image, mask)` tuple and expects a float image.
138
-
139
- ```python
140
- from openpiv import preprocess
141
-
142
- # method="edges" for dark, sharp-edged objects; "intensity" for high-contrast objects.
143
- frame_a_masked, mask_a = preprocess.dynamic_masking(
144
- frame_a.astype(np.float64), method="intensity", filter_size=7, threshold=0.005
145
- )
146
- frame_b_masked, mask_b = preprocess.dynamic_masking(
147
- frame_b.astype(np.float64), method="intensity", filter_size=7, threshold=0.005
148
- )
149
- ```
150
-
151
- Feed the **returned image** into the correlation step — it already has the masked region zeroed. Do
152
- not multiply the original frame by `mask`: masking is already applied, and for `method="edges"` the
153
- mask comes back as `uint8` 0/255 rather than boolean, so multiplying rescales the image by 255.
154
-
155
- ### Multi-Pass Processing
156
-
157
- Multi-pass (window deformation) lives in `openpiv.windef`, driven by a `PIVSettings` dataclass.
158
- `pyprocess` has no multi-pass entry point.
159
-
160
- ```python
161
- import numpy as np
162
- from openpiv import scaling, windef
163
-
164
- settings = windef.PIVSettings()
165
- settings.windowsizes = (64, 32, 16) # one entry per pass, decreasing (this is also the default)
166
- settings.overlap = (32, 16, 8) # same length as windowsizes
167
- settings.num_iterations = 3 # number of passes to actually run
168
- settings.sig2noise_threshold = 1.05
169
-
170
- x, y, u, v, flags = windef.simple_multipass(
171
- frame_a.astype(np.int32), frame_b.astype(np.int32), settings
172
- )
173
-
174
- # Output is in PIXELS PER FRAME -- convert yourself. scaling.uniform only divides
175
- # by scaling_factor, so apply dt separately.
176
- dt = 0.02
177
- x, y, u, v = scaling.uniform(x, y, u, v, scaling_factor=96.52)
178
- u, v = u / dt, v / dt
179
- ```
180
-
181
- `simple_multipass` already validates, replaces outliers, fills remaining NaNs with zeros, and calls
182
- `transform_coordinates` — do not repeat those steps.
183
-
184
- **Units trap:** `PIVSettings` has `dt` and `scaling_factor` fields, but `windef` never uses either —
185
- `first_pass` calls `extended_search_area_piv` without `dt`, so the whole multi-pass chain works in
186
- pixels per frame. Setting `settings.dt = 0.02` changes nothing about the returned values. Convert
187
- after the fact, as above.
188
-
189
- For control over individual passes, `windef.first_pass` and `windef.multipass_img_deform` are the
190
- lower-level building blocks.
191
-
192
- ## Validation and Post-Processing
193
-
194
- ### Validation Methods
195
-
196
- Every validator returns a boolean array where **True marks a spurious vector**.
197
-
198
- ```python
199
- # Signal-to-noise
200
- flags = validation.sig2noise_val(s2n, threshold=1.05)
201
-
202
- # Global range -- takes (min, max) TUPLES, positionally or as u_thresholds/v_thresholds.
203
- flags = validation.global_val(u, v, (-300, 300), (-300, 300))
204
-
205
- # Local median -- u_threshold and v_threshold are REQUIRED; size is the neighbourhood half-width.
206
- flags = validation.local_median_val(u, v, u_threshold=30.0, v_threshold=30.0, size=1)
207
-
208
- # Combine with boolean OR (not np.maximum -- these are bool arrays).
209
- flags = (
210
- validation.sig2noise_val(s2n, threshold=1.05)
211
- | validation.global_val(u, v, (-300, 300), (-300, 300))
212
- | validation.local_median_val(u, v, u_threshold=30.0, v_threshold=30.0)
213
- )
214
- ```
215
-
216
- **Set these thresholds in the units of `u` and `v`, not in pixels per frame.**
217
- `extended_search_area_piv` divides by `dt`, so with `dt=0.02` a 3 px/frame displacement arrives as
218
- 150 px/s. The thresholds above suit that case; the `(-30, 30)` figure that PIV literature and
219
- `PIVSettings.min_max_u_disp` use is a px/frame limit, and applying it to px/s output rejects the
220
- entire field. Either validate before scaling, or scale the thresholds by `1/dt` too.
221
-
222
- ### Outlier Replacement
223
-
224
- ```python
225
- u, v = filters.replace_outliers(
226
- u, v, flags, method="localmean", max_iter=3, tol=1e-3, kernel_size=2
227
- )
228
- ```
229
-
230
- `method` accepts `"localmean"`, `"disk"`, or `"distance"` — and only those three. An unrecognized
231
- name is not rejected; it falls through to an all-zero kernel and silently returns a useless field.
232
- Note that replacement *fills* the flagged
233
- positions with interpolated values — if you then overwrite them with NaN, the replacement was
234
- wasted. Choose one or the other:
235
-
236
- ```python
237
- # Keep flagged vectors out of the analysis entirely, instead of interpolating them.
238
- u = np.where(flags, np.nan, u)
239
- v = np.where(flags, np.nan, v)
240
- ```
241
-
242
- ### Smoothing
243
-
244
- Smoothing is `openpiv.smoothn.smoothn`; there is no `openpiv.smooth` module. It returns a tuple
245
- whose first element is the smoothed field, and it does not accept NaN input.
246
-
247
- ```python
248
- from openpiv.smoothn import smoothn
249
-
250
- u_smooth, *_ = smoothn(np.nan_to_num(u), s=0.5) # s: larger == smoother
251
- v_smooth, *_ = smoothn(np.nan_to_num(v), s=0.5)
252
- u_smooth = np.asarray(u_smooth)
253
- ```
254
-
255
- ## Visualization
256
-
257
- ### Vector Field Plotting
258
-
259
- `display_vector_field` reads a saved vectors file and calls `plt.show()` internally, so select a
260
- non-interactive backend for batch runs.
261
-
262
- ```python
263
- import matplotlib
264
- matplotlib.use("Agg")
265
- import matplotlib.pyplot as plt
266
- from openpiv import tools
267
-
268
- fig, ax = plt.subplots(figsize=(8, 8))
269
- tools.display_vector_field(
270
- "vectors.txt",
271
- ax=ax,
272
- scaling_factor=96.52, # same factor used in scaling.uniform, to map back onto the image
273
- scale=50,
274
- width=0.0035,
275
- on_img=True,
276
- image_name="frame_a.bmp",
277
- )
278
- fig.savefig("vector_field.png", dpi=150, bbox_inches="tight")
279
- plt.close(fig)
280
- ```
281
-
282
- ### Custom Visualization
283
-
284
- ```python
285
- import numpy as np
286
- import matplotlib.pyplot as plt
287
-
288
- fig, axes = plt.subplots(1, 3, figsize=(15, 5))
289
-
290
- mag = np.sqrt(u**2 + v**2)
291
- for ax, field, title, cmap in [
292
- (axes[0], mag, "Velocity Magnitude", "viridis"),
293
- (axes[1], u, "U Velocity", "RdBu_r"),
294
- (axes[2], v, "V Velocity", "RdBu_r"),
295
- ]:
296
- im = ax.imshow(field, cmap=cmap)
297
- ax.set_title(title)
298
- plt.colorbar(im, ax=ax)
299
-
300
- fig.tight_layout()
301
- fig.savefig("velocity_components.png")
302
- plt.close(fig)
303
- ```
304
-
305
- ## Analysis Functions
306
-
307
- `scripts/analyze.py` bundles these against a `params.npz` written by `runner.py`. It infers the
308
- physical grid spacing from the saved coordinates, so the derivatives come out per unit length:
309
-
310
- ```python
311
- import sys
312
- sys.path.insert(0, "skills/openpiv/scripts")
313
- from analyze import PIVAnalyzer
314
-
315
- piv = PIVAnalyzer("results/params.npz")
316
- vorticity = piv.compute_vorticity() # dv/dx - du/dy
317
- exx, eyy, exy = piv.compute_strain()
318
- stats = piv.compute_statistics() # u_mean, v_mean, rms_u, rms_v, tke
319
- piv.plot_vector_field(save_path="quiver.png")
320
- ```
321
-
322
- The standalone forms, if you would rather compute them inline:
323
-
324
- ### Vorticity
325
-
326
- ```python
327
- def compute_vorticity(u, v, dx=1.0, dy=None):
328
- """Out-of-plane vorticity dv/dx - du/dy. Pass the physical grid spacing, not 1.0."""
329
- dy = dx if dy is None else dy
330
- return np.gradient(v, dx, axis=1) - np.gradient(u, dy, axis=0)
331
- ```
332
-
333
- The grid spacing is `(window_size - overlap) / scaling_factor` in physical units, so leaving `dx=1.0`
334
- yields vorticity per grid cell, not per unit length.
335
-
336
- **Sign convention:** `runner.py` ends with `transform_coordinates`, which relabels the grid into a
337
- right-handed y-up frame but leaves the rows in image order, so the saved `y` *decreases* as the row
338
- index grows. The standalone forms above assume the opposite, so on a `params.npz` field they return
339
- `-du/dy` and flip the sign of the vorticity and the shear strain — negate the `axis=0` derivatives, or
340
- use `PIVAnalyzer`, which reads the orientation off the saved coordinates.
341
-
342
- ### Strain Rate
343
-
344
- ```python
345
- def compute_strain(u, v, dx=1.0, dy=None):
346
- """Return (exx, eyy, exy) of the 2D strain-rate tensor."""
347
- dy = dx if dy is None else dy
348
- du_dx = np.gradient(u, dx, axis=1)
349
- du_dy = np.gradient(u, dy, axis=0)
350
- dv_dx = np.gradient(v, dx, axis=1)
351
- dv_dy = np.gradient(v, dy, axis=0)
352
- return du_dx, dv_dy, 0.5 * (du_dy + dv_dx)
353
- ```
354
-
355
- ### Turbulence Statistics
356
-
357
- ```python
358
- def compute_statistics(u, v):
359
- """Single-frame spatial statistics. NOT Reynolds decomposition."""
360
- u_prime = u - np.nanmean(u)
361
- v_prime = v - np.nanmean(v)
362
- rms_u, rms_v = np.nanstd(u_prime), np.nanstd(v_prime)
363
- return {
364
- "u_mean": np.nanmean(u),
365
- "v_mean": np.nanmean(v),
366
- "rms_u": rms_u,
367
- "rms_v": rms_v,
368
- "tke": 0.5 * (rms_u**2 + rms_v**2),
369
- }
370
- ```
371
-
372
- **Caveat:** subtracting the *spatial* mean of one frame measures spatial variance, which equals
373
- turbulent intensity only for a homogeneous field. Genuine Reynolds decomposition needs an ensemble of
374
- image pairs: average over the time axis, then subtract that mean field from each realization.
375
-
376
- ## CLI Usage
377
-
378
- ```bash
379
- # Basic run
380
- python skills/openpiv/scripts/runner.py \
381
- --image img1.bmp --image img2.bmp --output_dir results --verbose
382
-
383
- # Tuned parameters with dynamic masking
384
- python skills/openpiv/scripts/runner.py \
385
- --image frame_a.bmp \
386
- --image frame_b.bmp \
387
- --output_dir results \
388
- --window_size 32 \
389
- --overlap 12 \
390
- --search_area 38 \
391
- --dt 0.02 \
392
- --scaling 96.52 \
393
- --threshold 1.05 \
394
- --mask dynamic \
395
- --mask_method intensity \
396
- --verbose
397
- ```
398
-
399
- ### CLI Options
400
-
401
- | Option | Default | Description |
402
- |--------|---------|-------------|
403
- | `--image` | required | Image file; specify exactly twice for the pair |
404
- | `--output_dir` | `results` | Output directory (created if absent) |
405
- | `--window_size` | 32 | Interrogation window size (px) |
406
- | `--overlap` | 12 | Window overlap (px) |
407
- | `--search_area` | 38 | Search area size (px), must be ≥ `--window_size` |
408
- | `--dt` | 0.02 | Time between frames (s) |
409
- | `--scaling` | 96.52 | Scaling factor, pixels per physical unit (e.g. px/mm) |
410
- | `--threshold` | 1.05 | `peak2peak` signal-to-noise threshold |
411
- | `--mask` | `none` | `none` or `dynamic` (`openpiv.preprocess.dynamic_masking`) |
412
- | `--mask_method` | `intensity` | `edges` or `intensity`, used only with `--mask dynamic` |
413
- | `--drop_invalid` | off | NaN out flagged vectors instead of keeping interpolated values |
414
- | `--verbose` | off | Print progress messages |
415
-
416
- Verify an install end to end against OpenPIV's own bundled image pair:
417
-
418
- ```bash
419
- python skills/openpiv/scripts/run_example.py --output_dir /tmp/openpiv-demo
420
- ```
421
-
422
- ## Output Files
423
-
424
- - **vectors.txt** — tab-delimited, `%.4e` formatted, with a `# x y u v flags mask` comment header
425
- - **params.npz** — NumPy archive with `x`, `y`, `u`, `v`, `flags` arrays
426
- - **vector_field.png** — vector field drawn over the first frame
427
-
428
- ```text
429
- # x y u v flags mask
430
- 2.1757e-01 3.5226e+00 -6.2220e-02 -2.7081e+00 0.0000e+00 0.0000e+00
431
- 4.8695e-01 3.5226e+00 -3.1587e-01 -2.9800e+00 0.0000e+00 0.0000e+00
432
- ```
433
-
434
- `flags` is written as a float, `0` for a valid vector and `1` for a flagged one.
435
-
436
- ## Best Practices
437
-
438
- ### Parameter Selection
439
-
440
- 1. **Window size** — 32×32 suits most cases. 64/128 for better correlation at coarser resolution;
441
- 16/24 for finer resolution at the cost of noise.
442
- 2. **Overlap** — 50–75% of window size.
443
- 3. **Threshold** — raise it to reject more vectors; always re-tune after switching
444
- `sig2noise_method`.
445
- 4. **Scaling factor** — calibrate against a known reference such as a calibration grid, and keep the
446
- units straight (`96.52` in OpenPIV's `test1` tutorial data is px/mm).
447
-
448
- ### Image Quality
449
-
450
- - Particles visible and evenly distributed, 5–10 per interrogation window
451
- - No saturated or overexposed regions
452
- - Minimal background noise; consider background subtraction across a run
453
-
454
- ### Processing Tips
455
-
456
- 1. Start from the defaults, then tune against the vector field you get.
457
- 2. Inspect the `s2n` distribution — a low median means poor correlation, not a bad threshold.
458
- 3. Visualize early; obvious problems (uniform vectors, edge artifacts) show up immediately.
459
- 4. Use multi-pass (`windef`) for flows with large velocity gradients or displacements.
460
- 5. Mask reflections and solid boundaries rather than letting them generate vectors.
461
-
462
- ## Resources
463
-
464
- ### references/
465
-
466
- - `advanced_algorithms.md` — correlation and subpixel methods, multi-pass window deformation,
467
- `PIVSettings` fields, 3D and phase-separation modules
468
-
469
- Load the reference when detailed algorithm or settings information is needed.