pivtools 0.1.3__cp311-cp311-win_amd64.whl
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.
- pivtools-0.1.3.dist-info/METADATA +222 -0
- pivtools-0.1.3.dist-info/RECORD +127 -0
- pivtools-0.1.3.dist-info/WHEEL +5 -0
- pivtools-0.1.3.dist-info/entry_points.txt +3 -0
- pivtools-0.1.3.dist-info/top_level.txt +3 -0
- pivtools_cli/__init__.py +5 -0
- pivtools_cli/_build_marker.c +25 -0
- pivtools_cli/_build_marker.cp311-win_amd64.pyd +0 -0
- pivtools_cli/cli.py +225 -0
- pivtools_cli/example.py +139 -0
- pivtools_cli/lib/PIV_2d_cross_correlate.c +334 -0
- pivtools_cli/lib/PIV_2d_cross_correlate.h +22 -0
- pivtools_cli/lib/common.h +36 -0
- pivtools_cli/lib/interp2custom.c +146 -0
- pivtools_cli/lib/interp2custom.h +48 -0
- pivtools_cli/lib/peak_locate_gsl.c +711 -0
- pivtools_cli/lib/peak_locate_gsl.h +40 -0
- pivtools_cli/lib/peak_locate_gsl_print.c +736 -0
- pivtools_cli/lib/peak_locate_lm.c +751 -0
- pivtools_cli/lib/peak_locate_lm.h +27 -0
- pivtools_cli/lib/xcorr.c +342 -0
- pivtools_cli/lib/xcorr.h +31 -0
- pivtools_cli/lib/xcorr_cache.c +78 -0
- pivtools_cli/lib/xcorr_cache.h +26 -0
- pivtools_cli/piv/interp2custom/interp2custom.py +69 -0
- pivtools_cli/piv/piv.py +240 -0
- pivtools_cli/piv/piv_backend/base.py +825 -0
- pivtools_cli/piv/piv_backend/cpu_instantaneous.py +1005 -0
- pivtools_cli/piv/piv_backend/factory.py +28 -0
- pivtools_cli/piv/piv_backend/gpu_instantaneous.py +15 -0
- pivtools_cli/piv/piv_backend/infilling.py +445 -0
- pivtools_cli/piv/piv_backend/outlier_detection.py +306 -0
- pivtools_cli/piv/piv_backend/profile_cpu_instantaneous.py +230 -0
- pivtools_cli/piv/piv_result.py +40 -0
- pivtools_cli/piv/save_results.py +342 -0
- pivtools_cli/piv_cluster/cluster.py +108 -0
- pivtools_cli/preprocessing/filters.py +399 -0
- pivtools_cli/preprocessing/preprocess.py +79 -0
- pivtools_cli/tests/helpers.py +107 -0
- pivtools_cli/tests/instantaneous_piv/test_piv_integration.py +167 -0
- pivtools_cli/tests/instantaneous_piv/test_piv_integration_multi.py +553 -0
- pivtools_cli/tests/preprocessing/test_filters.py +41 -0
- pivtools_core/__init__.py +5 -0
- pivtools_core/config.py +703 -0
- pivtools_core/config.yaml +135 -0
- pivtools_core/image_handling/__init__.py +0 -0
- pivtools_core/image_handling/load_images.py +464 -0
- pivtools_core/image_handling/readers/__init__.py +53 -0
- pivtools_core/image_handling/readers/generic_readers.py +50 -0
- pivtools_core/image_handling/readers/lavision_reader.py +190 -0
- pivtools_core/image_handling/readers/registry.py +24 -0
- pivtools_core/paths.py +49 -0
- pivtools_core/vector_loading.py +248 -0
- pivtools_gui/__init__.py +3 -0
- pivtools_gui/app.py +687 -0
- pivtools_gui/calibration/__init__.py +0 -0
- pivtools_gui/calibration/app/__init__.py +0 -0
- pivtools_gui/calibration/app/views.py +1186 -0
- pivtools_gui/calibration/calibration_planar/planar_calibration_production.py +570 -0
- pivtools_gui/calibration/vector_calibration_production.py +544 -0
- pivtools_gui/config.py +703 -0
- pivtools_gui/image_handling/__init__.py +0 -0
- pivtools_gui/image_handling/load_images.py +464 -0
- pivtools_gui/image_handling/readers/__init__.py +53 -0
- pivtools_gui/image_handling/readers/generic_readers.py +50 -0
- pivtools_gui/image_handling/readers/lavision_reader.py +190 -0
- pivtools_gui/image_handling/readers/registry.py +24 -0
- pivtools_gui/masking/__init__.py +0 -0
- pivtools_gui/masking/app/__init__.py +0 -0
- pivtools_gui/masking/app/views.py +123 -0
- pivtools_gui/paths.py +49 -0
- pivtools_gui/piv_runner.py +261 -0
- pivtools_gui/pivtools.py +58 -0
- pivtools_gui/plotting/__init__.py +0 -0
- pivtools_gui/plotting/app/__init__.py +0 -0
- pivtools_gui/plotting/app/views.py +1671 -0
- pivtools_gui/plotting/plot_maker.py +220 -0
- pivtools_gui/post_processing/POD/__init__.py +0 -0
- pivtools_gui/post_processing/POD/app/__init__.py +0 -0
- pivtools_gui/post_processing/POD/app/views.py +647 -0
- pivtools_gui/post_processing/POD/pod_decompose.py +979 -0
- pivtools_gui/post_processing/POD/views.py +1096 -0
- pivtools_gui/post_processing/__init__.py +0 -0
- pivtools_gui/static/404.html +1 -0
- pivtools_gui/static/_next/static/chunks/117-d5793c8e79de5511.js +2 -0
- pivtools_gui/static/_next/static/chunks/484-cfa8b9348ce4f00e.js +1 -0
- pivtools_gui/static/_next/static/chunks/869-320a6b9bdafbb6d3.js +1 -0
- pivtools_gui/static/_next/static/chunks/app/_not-found/page-12f067ceb7415e55.js +1 -0
- pivtools_gui/static/_next/static/chunks/app/layout-b907d5f31ac82e9d.js +1 -0
- pivtools_gui/static/_next/static/chunks/app/page-334cc4e8444cde2f.js +1 -0
- pivtools_gui/static/_next/static/chunks/fd9d1056-ad15f396ddf9b7e5.js +1 -0
- pivtools_gui/static/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
- pivtools_gui/static/_next/static/chunks/main-a1b3ced4d5f6d998.js +1 -0
- pivtools_gui/static/_next/static/chunks/main-app-8a63c6f5e7baee11.js +1 -0
- pivtools_gui/static/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- pivtools_gui/static/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- pivtools_gui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- pivtools_gui/static/_next/static/chunks/webpack-4a8ca7c99e9bb3d8.js +1 -0
- pivtools_gui/static/_next/static/css/7d3f2337d7ea12a5.css +3 -0
- pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_buildManifest.js +1 -0
- pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_ssgManifest.js +1 -0
- pivtools_gui/static/file.svg +1 -0
- pivtools_gui/static/globe.svg +1 -0
- pivtools_gui/static/grid.svg +8 -0
- pivtools_gui/static/index.html +1 -0
- pivtools_gui/static/index.txt +8 -0
- pivtools_gui/static/next.svg +1 -0
- pivtools_gui/static/vercel.svg +1 -0
- pivtools_gui/static/window.svg +1 -0
- pivtools_gui/stereo_reconstruction/__init__.py +0 -0
- pivtools_gui/stereo_reconstruction/app/__init__.py +0 -0
- pivtools_gui/stereo_reconstruction/app/views.py +1985 -0
- pivtools_gui/stereo_reconstruction/stereo_calibration_production.py +606 -0
- pivtools_gui/stereo_reconstruction/stereo_reconstruction_production.py +544 -0
- pivtools_gui/utils.py +63 -0
- pivtools_gui/vector_loading.py +248 -0
- pivtools_gui/vector_merging/__init__.py +1 -0
- pivtools_gui/vector_merging/app/__init__.py +1 -0
- pivtools_gui/vector_merging/app/views.py +759 -0
- pivtools_gui/vector_statistics/app/__init__.py +1 -0
- pivtools_gui/vector_statistics/app/views.py +710 -0
- pivtools_gui/vector_statistics/ensemble_statistics.py +49 -0
- pivtools_gui/vector_statistics/instantaneous_statistics.py +311 -0
- pivtools_gui/video_maker/__init__.py +0 -0
- pivtools_gui/video_maker/app/__init__.py +0 -0
- pivtools_gui/video_maker/app/views.py +436 -0
- pivtools_gui/video_maker/video_maker.py +662 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import matplotlib as mpl
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
import numpy as np
|
|
7
|
+
from matplotlib.cm import ScalarMappable
|
|
8
|
+
from matplotlib.colors import Normalize, TwoSlopeNorm
|
|
9
|
+
from matplotlib.ticker import FixedFormatter, FixedLocator
|
|
10
|
+
|
|
11
|
+
from ..config import Config
|
|
12
|
+
|
|
13
|
+
mpl.use("Agg")
|
|
14
|
+
mpl.rcParams["xtick.direction"] = "in"
|
|
15
|
+
mpl.rcParams["ytick.direction"] = "in"
|
|
16
|
+
mpl.rcParams["xtick.major.size"] = 5
|
|
17
|
+
mpl.rcParams["ytick.major.size"] = 5
|
|
18
|
+
mpl.rcParams["xtick.minor.size"] = 3
|
|
19
|
+
mpl.rcParams["ytick.minor.size"] = 3
|
|
20
|
+
mpl.rcParams["xtick.minor.visible"] = True
|
|
21
|
+
mpl.rcParams["ytick.minor.visible"] = True
|
|
22
|
+
mpl.rcParams["xtick.major.pad"] = 6
|
|
23
|
+
mpl.rcParams["ytick.major.pad"] = 6
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Settings object to drive plot_scalar_field conveniently from Config
|
|
27
|
+
@dataclass
|
|
28
|
+
class Settings:
|
|
29
|
+
variableName: str
|
|
30
|
+
variableUnits: str = ""
|
|
31
|
+
length_units: str = "mm"
|
|
32
|
+
title: str = ""
|
|
33
|
+
levels: int | list = 500
|
|
34
|
+
cmap: str | None = None
|
|
35
|
+
corners: tuple | None = None
|
|
36
|
+
lower_limit: float | None = None
|
|
37
|
+
upper_limit: float | None = None
|
|
38
|
+
_xlabel: str = "x"
|
|
39
|
+
_ylabel: str = "y"
|
|
40
|
+
_fontsize: int = 12
|
|
41
|
+
_title_fontsize: int = 14
|
|
42
|
+
save_name: str | None = None
|
|
43
|
+
save_extension: str = ".png"
|
|
44
|
+
save_pickle: bool = False
|
|
45
|
+
# New: optional coordinates and symmetric scaling control
|
|
46
|
+
coords_x: np.ndarray | None = None
|
|
47
|
+
coords_y: np.ndarray | None = None
|
|
48
|
+
symmetric_around_zero: bool = True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def make_scalar_settings(
|
|
52
|
+
config: Config,
|
|
53
|
+
*,
|
|
54
|
+
variable: str,
|
|
55
|
+
run_label: int,
|
|
56
|
+
save_basepath: Path,
|
|
57
|
+
title: str | None = None,
|
|
58
|
+
variable_units: str = "",
|
|
59
|
+
length_units: str = "mm",
|
|
60
|
+
cmap: str | None = None,
|
|
61
|
+
levels: int | list = 100,
|
|
62
|
+
lower_limit: float | None = None,
|
|
63
|
+
upper_limit: float | None = None,
|
|
64
|
+
corners: tuple | None = None,
|
|
65
|
+
coords_x: np.ndarray | None = None,
|
|
66
|
+
coords_y: np.ndarray | None = None,
|
|
67
|
+
symmetric_around_zero: bool = True,
|
|
68
|
+
) -> Settings:
|
|
69
|
+
return Settings(
|
|
70
|
+
variableName=variable,
|
|
71
|
+
variableUnits=variable_units,
|
|
72
|
+
length_units=length_units,
|
|
73
|
+
title=title or f"{variable} pass {run_label}",
|
|
74
|
+
levels=levels,
|
|
75
|
+
cmap=cmap,
|
|
76
|
+
corners=corners,
|
|
77
|
+
lower_limit=lower_limit,
|
|
78
|
+
upper_limit=upper_limit,
|
|
79
|
+
_xlabel="x",
|
|
80
|
+
_ylabel="y",
|
|
81
|
+
_fontsize=config.plot_fontsize,
|
|
82
|
+
_title_fontsize=config.plot_title_fontsize,
|
|
83
|
+
save_name=str(save_basepath),
|
|
84
|
+
save_extension=config.plot_save_extension,
|
|
85
|
+
save_pickle=config.plot_save_pickle,
|
|
86
|
+
coords_x=coords_x,
|
|
87
|
+
coords_y=coords_y,
|
|
88
|
+
symmetric_around_zero=symmetric_around_zero,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Function to plot a scalar field with masking and customizable settings
|
|
92
|
+
def plot_scalar_field(variable, mask, settings): # efe
|
|
93
|
+
# Extract plot settings
|
|
94
|
+
plt.rcParams.update({"font.size": settings._fontsize})
|
|
95
|
+
plt.rcParams["axes.titlesize"] = settings._title_fontsize
|
|
96
|
+
|
|
97
|
+
cm_label = settings.variableName + " (" + settings.variableUnits + ")"
|
|
98
|
+
|
|
99
|
+
# Mask the variable array where mask is True
|
|
100
|
+
masked_var = np.ma.array(variable, mask=mask)
|
|
101
|
+
|
|
102
|
+
# Generate coordinate arrays: prefer provided coords_x/coords_y, else corners, else indices
|
|
103
|
+
X = Y = None
|
|
104
|
+
if settings.coords_x is not None and settings.coords_y is not None:
|
|
105
|
+
cx, cy = np.asarray(settings.coords_x), np.asarray(settings.coords_y)
|
|
106
|
+
# 2D grid case matching variable shape
|
|
107
|
+
if (
|
|
108
|
+
cx.ndim == 2
|
|
109
|
+
and cy.ndim == 2
|
|
110
|
+
and cx.shape == variable.shape
|
|
111
|
+
and cy.shape == variable.shape
|
|
112
|
+
):
|
|
113
|
+
X, Y = cx, cy
|
|
114
|
+
# 1D axes case
|
|
115
|
+
elif cx.ndim == 1 and cy.ndim == 1:
|
|
116
|
+
ny, nx = variable.shape
|
|
117
|
+
if cx.size == nx and cy.size == ny:
|
|
118
|
+
X, Y = np.meshgrid(cx, cy)
|
|
119
|
+
if X is None or Y is None:
|
|
120
|
+
if settings.corners is not None and all(
|
|
121
|
+
c is not None for c in settings.corners
|
|
122
|
+
):
|
|
123
|
+
x0, y0, x1, y1 = settings.corners
|
|
124
|
+
ny, nx = variable.shape
|
|
125
|
+
x = np.linspace(x0, x1, nx)
|
|
126
|
+
y = np.linspace(y0, y1, ny)
|
|
127
|
+
else:
|
|
128
|
+
ny, nx = variable.shape
|
|
129
|
+
x = np.arange(nx)
|
|
130
|
+
y = np.arange(ny-1, -1, -1)
|
|
131
|
+
X, Y = np.meshgrid(x, y)
|
|
132
|
+
|
|
133
|
+
# Create the plot (object-oriented API)
|
|
134
|
+
fig, ax = plt.subplots(figsize=(12, 7))
|
|
135
|
+
ax.set_facecolor("gray") # <-- gray shows through masked holes
|
|
136
|
+
|
|
137
|
+
# Determine limits
|
|
138
|
+
if settings.lower_limit is not None and settings.upper_limit is not None:
|
|
139
|
+
vmin, vmax = settings.lower_limit, settings.upper_limit
|
|
140
|
+
else:
|
|
141
|
+
# Use nanmin/nanmax to handle NaN values properly
|
|
142
|
+
valid_data = masked_var.compressed() # Get unmasked data
|
|
143
|
+
if len(valid_data) > 0:
|
|
144
|
+
vmin = float(np.nanmin(valid_data))
|
|
145
|
+
vmax = float(np.nanmax(valid_data))
|
|
146
|
+
# Check if min/max are still NaN (all valid data is NaN)
|
|
147
|
+
if np.isnan(vmin) or np.isnan(vmax) or np.isinf(vmin) or np.isinf(vmax):
|
|
148
|
+
# Fallback to sensible defaults
|
|
149
|
+
vmin, vmax = 0.0, 1.0
|
|
150
|
+
else:
|
|
151
|
+
# No valid data at all - use defaults
|
|
152
|
+
vmin, vmax = 0.0, 1.0
|
|
153
|
+
|
|
154
|
+
# Enforce symmetric scale around zero if data spans negative and positive
|
|
155
|
+
use_two_slope = False
|
|
156
|
+
actual_min = vmin
|
|
157
|
+
actual_max = vmax
|
|
158
|
+
if settings.symmetric_around_zero and vmin < 0 and vmax > 0:
|
|
159
|
+
vabs = max(abs(vmin), abs(vmax))
|
|
160
|
+
vmin, vmax = -vabs, vabs
|
|
161
|
+
use_two_slope = True
|
|
162
|
+
|
|
163
|
+
# Select colormap & norm
|
|
164
|
+
if settings.cmap is not None:
|
|
165
|
+
cmap = plt.get_cmap(settings.cmap)
|
|
166
|
+
norm = (
|
|
167
|
+
TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax)
|
|
168
|
+
if use_two_slope
|
|
169
|
+
else Normalize(vmin=vmin, vmax=vmax)
|
|
170
|
+
)
|
|
171
|
+
else:
|
|
172
|
+
if use_two_slope:
|
|
173
|
+
cmap = plt.get_cmap("bwr")
|
|
174
|
+
norm = TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax)
|
|
175
|
+
else:
|
|
176
|
+
bwr = plt.get_cmap("bwr")
|
|
177
|
+
if vmax <= 0:
|
|
178
|
+
colors = bwr(np.linspace(0.0, 0.5, 256))
|
|
179
|
+
cmap = mpl.colors.LinearSegmentedColormap.from_list("bwr_lower", colors)
|
|
180
|
+
norm = Normalize(vmin=vmin, vmax=vmax)
|
|
181
|
+
else:
|
|
182
|
+
colors = bwr(np.linspace(0.5, 1.0, 256))
|
|
183
|
+
cmap = mpl.colors.LinearSegmentedColormap.from_list("bwr_upper", colors)
|
|
184
|
+
norm = Normalize(vmin=vmin, vmax=vmax)
|
|
185
|
+
|
|
186
|
+
# Use ax.contourf (object-oriented)
|
|
187
|
+
im = ax.contourf(X, Y, masked_var, levels=settings.levels, cmap=cmap, norm=norm)
|
|
188
|
+
|
|
189
|
+
sm = ScalarMappable(norm=norm, cmap=cmap)
|
|
190
|
+
sm.set_array([]) # required for some Matplotlib versions
|
|
191
|
+
|
|
192
|
+
# Use object-oriented colorbar
|
|
193
|
+
cbar = fig.colorbar(sm, ax=ax, label=cm_label)
|
|
194
|
+
|
|
195
|
+
if isinstance(settings.levels, np.ndarray):
|
|
196
|
+
if isinstance(norm, TwoSlopeNorm):
|
|
197
|
+
ticks = [norm.vmin, 0.0, norm.vmax]
|
|
198
|
+
else:
|
|
199
|
+
ticks = np.linspace(norm.vmin, norm.vmax, 7)
|
|
200
|
+
else:
|
|
201
|
+
ticks = np.linspace(norm.vmin, norm.vmax, 7)
|
|
202
|
+
|
|
203
|
+
# Optional: nice fixed tick count
|
|
204
|
+
ticks = np.linspace(actual_min, actual_max, 7)
|
|
205
|
+
labels = [f"{t:.2f}" for t in ticks]
|
|
206
|
+
cbar.set_ticks(ticks)
|
|
207
|
+
cbar.set_ticklabels(labels)
|
|
208
|
+
cbar.ax.set_ylim(actual_min, actual_max)
|
|
209
|
+
cbar.ax.yaxis.set_major_locator(FixedLocator(ticks))
|
|
210
|
+
cbar.ax.yaxis.set_major_formatter(FixedFormatter(labels))
|
|
211
|
+
|
|
212
|
+
ax.set_title(f"{settings.title}")
|
|
213
|
+
if settings.length_units:
|
|
214
|
+
ax.set_xlabel(settings._xlabel + f" ({settings.length_units})")
|
|
215
|
+
ax.set_ylabel(settings._ylabel + f" ({settings.length_units})")
|
|
216
|
+
else:
|
|
217
|
+
ax.set_xlabel(settings._xlabel)
|
|
218
|
+
ax.set_ylabel(settings._ylabel)
|
|
219
|
+
|
|
220
|
+
return fig, ax, im
|
|
File without changes
|
|
File without changes
|