Benchmarks¶
The voids.benchmarks sub-package provides utilities for cross-checking voids
results against reference implementations such as OpenPNM and XLB. In the
broader project documentation, these utilities belong to the verification
side of the Verification & Validation split: they benchmark voids against
software references or alternative numerical workflows, not directly against
experimental measurements.
The two high-level segmented-volume benchmark wrappers now share the same physical pressure convention:
- the preferred public input is the physical pressure drop
delta_p, typically in Pa - optional
pinandpoutvalues can also be supplied when the user wants to preserve a particular absolute pressure reference level - for the current incompressible permeability benchmark, only the pressure drop
Δp = pin - poutaffects the reported permeability - the applied
p_inlet_physical,p_outlet_physical, anddp_physicalvalues are recorded in the benchmark result tables
So delta_p=1.0, pin=1.0/pout=0.0, and delta_p=1.0 with
pin=101326.0/pout=101325.0 all represent the same current benchmark
driving condition.
The XLB benchmark API now has two distinct layers:
solve_binary_volume_with_xlbis the low-level direct-image solver. It works in lattice units and accepts lattice pressure boundary conditions throughpressure_inlet_lattice,pressure_outlet_lattice, orpressure_drop_lattice.benchmark_segmented_volume_with_xlbis the high-level verification wrapper. It resolves a physical pressure drop fromdelta_pand optionalpin/pout, then maps that same physicalΔpinto lattice units before calling XLB on the original binary image.
For the high-level XLB benchmark, fluid.density must be provided because the
shared physical pressure drop must be converted into lattice pressure units.
Cross-Check¶
voids.benchmarks.crosscheck
¶
SinglePhaseCrosscheckSummary
dataclass
¶
Summary of a solver-to-reference comparison.
Attributes:
| Name | Type | Description |
|---|---|---|
reference |
str
|
Name of the reference implementation or workflow. |
axis |
str
|
Flow axis used in the comparison. |
permeability_abs_diff, permeability_rel_diff |
Absolute and relative differences between apparent permeabilities. |
|
total_flow_abs_diff, total_flow_rel_diff |
Absolute and relative differences between total flow rates. |
|
details |
dict[str, Any]
|
Auxiliary metadata useful for debugging and reporting. |
Source code in src/voids/benchmarks/crosscheck.py
crosscheck_singlephase_roundtrip_openpnm_dict
¶
Cross-check voids after a dict roundtrip through OpenPNM-style keys.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net
|
Network
|
Network to solve and round-trip. |
required |
fluid
|
FluidSinglePhase
|
Fluid properties. |
required |
bc
|
PressureBC
|
Pressure boundary conditions. |
required |
axis
|
str
|
Flow axis used in the permeability calculation. |
required |
options
|
SinglePhaseOptions | None
|
Optional solver configuration. |
None
|
Returns:
| Type | Description |
|---|---|
SinglePhaseCrosscheckSummary
|
Comparison between the original |
Notes
This path does not require OpenPNM itself. It checks whether exporting to the
flat OpenPNM/PoreSpy naming convention and importing back into voids changes
any transport-relevant fields.
Source code in src/voids/benchmarks/crosscheck.py
crosscheck_singlephase_with_openpnm
¶
Cross-check voids against OpenPNM StokesFlow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net
|
Network
|
Network to simulate. |
required |
fluid
|
FluidSinglePhase
|
Fluid properties. |
required |
bc
|
PressureBC
|
Pressure boundary conditions. |
required |
axis
|
str
|
Flow axis used for apparent permeability. |
required |
options
|
SinglePhaseOptions | None
|
Optional solver configuration. |
None
|
Returns:
| Type | Description |
|---|---|
SinglePhaseCrosscheckSummary
|
Comparison between |
Raises:
| Type | Description |
|---|---|
ImportError
|
If OpenPNM is not installed. |
RuntimeError
|
If the installed OpenPNM API is incompatible with the adapter. |
ValueError
|
If the imposed pressure drop is zero. |
Notes
The comparison injects the voids-computed throat.hydraulic_conductance
into OpenPNM. That means the crosscheck isolates differences in system assembly,
boundary-condition handling, sign conventions, and linear-solver behavior,
rather than differences in geometric conductance modeling.
Source code in src/voids/benchmarks/crosscheck.py
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | |
Segmented Volume Benchmarks¶
voids.benchmarks.segmented_volume
¶
SegmentedVolumeCrosscheckResult
dataclass
¶
Store extraction, porosity, and solver cross-check outputs.
Attributes:
| Name | Type | Description |
|---|---|---|
extract |
NetworkExtractionResult
|
Result of importing the segmented volume into a |
fluid |
FluidSinglePhase
|
Fluid properties used in the permeability solve. |
bc |
PressureBC
|
Pressure boundary conditions imposed on the extracted network. |
options |
SinglePhaseOptions
|
Solver and conductance options used for the comparison. |
image_porosity |
float
|
Void fraction of the segmented binary image. |
absolute_porosity, effective_porosity |
Porosity diagnostics computed from the pruned extracted network. |
|
summary |
SinglePhaseCrosscheckSummary
|
Comparison summary between |
Notes
This high-level benchmark follows the same public pressure-BC convention as
benchmark_segmented_volume_with_xlb: the preferred user input is
delta_p, while optional pin / pout values can still be used to
preserve an absolute pressure gauge. The applied physical pressures and
pressure drop are recorded explicitly in
:meth:SegmentedVolumeCrosscheckResult.to_record.
Source code in src/voids/benchmarks/segmented_volume.py
to_record
¶
Return scalar diagnostics suitable for tabulation.
Source code in src/voids/benchmarks/segmented_volume.py
benchmark_segmented_volume_with_openpnm
¶
benchmark_segmented_volume_with_openpnm(
phases,
*,
voxel_size,
flow_axis=None,
fluid=None,
delta_p=None,
pin=None,
pout=None,
options=None,
length_unit="m",
pressure_unit="Pa",
extraction_kwargs=None,
provenance_notes=None,
strict=True,
)
Benchmark an extracted segmented volume against OpenPNM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
phases
|
ndarray
|
Binary segmented image encoded as |
required |
voxel_size
|
float
|
Edge length of one voxel in the declared length unit. |
required |
flow_axis
|
str | None
|
Requested transport axis. When omitted, the longest image axis is used. |
None
|
fluid
|
FluidSinglePhase | None
|
Fluid properties. Defaults to water-like viscosity |
None
|
delta_p
|
float | None
|
Preferred physical pressure drop for the benchmark, typically in Pa.
When provided alone, the wrapper uses |
None
|
pin
|
float | None
|
Optional absolute physical inlet and outlet pressures. They are kept for
backward compatibility and for cases where the user wants to preserve a
particular pressure reference level. For the current incompressible
benchmark, only the pressure drop |
None
|
pout
|
float | None
|
Optional absolute physical inlet and outlet pressures. They are kept for
backward compatibility and for cases where the user wants to preserve a
particular pressure reference level. For the current incompressible
benchmark, only the pressure drop |
None
|
options
|
SinglePhaseOptions | None
|
Solver controls. Defaults to the image-workflow baseline
|
None
|
length_unit
|
str
|
Units attached to the extracted sample geometry. |
'm'
|
pressure_unit
|
str
|
Units attached to the extracted sample geometry. |
'm'
|
extraction_kwargs
|
dict[str, object] | None
|
Extra keyword arguments forwarded to |
None
|
provenance_notes
|
dict[str, object] | None
|
Optional metadata attached to the extracted network provenance. |
None
|
strict
|
bool
|
Forwarded to :func: |
True
|
Returns:
| Type | Description |
|---|---|
SegmentedVolumeCrosscheckResult
|
Extraction metadata, porosity diagnostics, and the OpenPNM comparison. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the image is invalid, the pressure specification is inconsistent, or the implied pressure drop is not positive. |
Notes
This helper uses :func:voids.benchmarks.crosscheck_singlephase_with_openpnm,
which injects the voids throat hydraulic conductances into OpenPNM. The
resulting comparison isolates extraction consistency, boundary-condition
handling, and linear-solver agreement; it does not benchmark independent
conductance models between packages.
Unlike the XLB high-level benchmark, no fluid-density-based unit conversion is needed here because both sides solve the same extracted pore network directly under the same physical pressure BC.
The absolute pressure offset is numerically immaterial for this current
incompressible benchmark. For example, delta_p=1, pin=1/pout=0,
and delta_p=1 with pin=101326/pout=101325 all impose the same
permeability-driving pressure drop.
Source code in src/voids/benchmarks/segmented_volume.py
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
voids.benchmarks.xlb
¶
Direct-image permeability benchmarks against XLB.
This module exposes two public layers:
solve_binary_volume_with_xlbfor a direct-image XLB solve configured in lattice units, with pressure BCs specified through lattice pressure values or a lattice pressure drop.benchmark_segmented_volume_with_xlbfor the scientifically relevantvoidsversus XLB comparison on the same segmented image. This high-level wrapper resolves a physical pressure drop fromdelta_pand optionalpin/poutinputs, then maps that same physical pressure drop into lattice pressure units for XLB.
XLBOptions
dataclass
¶
Numerical controls for the direct-image XLB benchmark.
Attributes:
| Name | Type | Description |
|---|---|---|
formulation |
str
|
Either |
backend |
str
|
XLB compute backend. The current |
precision_policy |
str
|
XLB precision policy name, for example |
collision_model |
str
|
XLB collision operator label passed to the stepper. |
streaming_scheme |
str
|
XLB streaming scheme label passed to the stepper. |
lattice_viscosity |
float
|
Kinematic viscosity in lattice units. |
pressure_inlet_lattice, pressure_outlet_lattice |
Optional inlet and outlet lattice pressures. If both are provided they define the pressure BC directly. |
|
pressure_drop_lattice |
float | None
|
Optional lattice pressure drop. When set without explicit inlet/outlet
pressures, it is applied relative to |
reference_density_lattice |
float
|
Reference lattice density used to construct a baseline outlet pressure
when only |
rho_inlet, rho_outlet |
Legacy density-based BC inputs retained for backward compatibility.
They are converted internally to lattice pressure using
|
|
inlet_outlet_buffer_cells |
int
|
Number of fluid buffer layers inserted ahead of and behind the sample. |
max_steps, min_steps, check_interval, steady_rtol |
Iteration and convergence controls for the steady-state solve. |
Notes
The current voids adapter uses XLB's JAX backend only. This keeps the
dependency path compatible with CPU-only macOS and Linux environments, which
is the relevant portability target for the benchmark notebook.
The currently exposed XLB operator is the incompressible Navier-Stokes
lattice-Boltzmann stepper. Setting formulation="steady_stokes_limit"
does not switch to a different PDE solver; it selects conservative forcing
and convergence defaults so the converged solution can be interpreted in the
steady creeping-flow limit.
In this isothermal LBM setting, lattice pressure satisfies
p_lu = c_s^2 rho. The preferred public inputs are therefore the lattice
pressure fields pressure_inlet_lattice / pressure_outlet_lattice or
the pressure drop pressure_drop_lattice. The legacy fields rho_inlet
and rho_outlet remain supported for backward compatibility and are
converted internally to pressure.
Source code in src/voids/benchmarks/xlb.py
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | |
steady_stokes_defaults
classmethod
¶
Return a conservative preset for the steady creeping-flow limit.
Notes
XLB does not currently expose a separate Stokes-only stepper in the
installed package used by voids. This preset therefore still uses the
incompressible Navier-Stokes LBM operator, but with a smaller lattice
pressure drop and tighter steady-state controls so the converged solution is
interpreted in the low-Reynolds, low-Mach limit.
Source code in src/voids/benchmarks/xlb.py
XLBDirectSimulationResult
dataclass
¶
Store direct-image LBM outputs from an XLB run.
Attributes:
| Name | Type | Description |
|---|---|---|
lattice_pressure_inlet, lattice_pressure_outlet, lattice_pressure_drop |
Resolved inlet, outlet, and differential pressure in lattice units. |
|
lattice_density_inlet, lattice_density_outlet |
Equivalent lattice densities associated with the pressure BCs through
|
|
permeability |
float
|
Apparent permeability mapped back to physical units. |
max_mach_lattice, reynolds_voxel_max |
Low-inertia diagnostics useful when interpreting a run as a creeping-flow reference. |
Source code in src/voids/benchmarks/xlb.py
SegmentedVolumeXLBResult
dataclass
¶
Store extraction, porosity, and direct-image XLB benchmark outputs.
Attributes:
| Name | Type | Description |
|---|---|---|
bc |
PressureBC
|
Physical pressure BC used on the extracted-network |
xlb_options |
XLBOptions
|
XLB options actually used for the direct-image solve. For the high-level
benchmark wrapper these are pressure-coupled so they match the resolved
physical pressure drop used on the |
xlb_result |
XLBDirectSimulationResult
|
Direct-image XLB result, including resolved lattice pressure diagnostics. |
Source code in src/voids/benchmarks/xlb.py
to_record
¶
Return scalar diagnostics suitable for tabulation.
Source code in src/voids/benchmarks/xlb.py
solve_binary_volume_with_xlb
¶
Solve a binary segmented volume directly with XLB and estimate permeability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
phases
|
ndarray
|
Binary segmented volume with |
required |
voxel_size
|
float
|
Physical voxel size used when mapping lattice permeability back to physical units. |
required |
flow_axis
|
str | None
|
Requested flow axis. When omitted, the canonical sample axis inferred by
:func: |
None
|
options
|
XLBOptions | None
|
XLB numerical controls. This low-level interface expects lattice-unit
pressure inputs through |
None
|
Returns:
| Type | Description |
|---|---|
XLBDirectSimulationResult
|
Direct-image XLB solution summary and permeability diagnostics. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the input volume, flow axis, or XLB numerical controls are invalid. |
RuntimeError
|
If the run completes with a non-physical permeability estimate. |
Notes
The current adapter uses XLB's incompressible Navier-Stokes lattice
Boltzmann stepper with BGK-style collision and pressure / bounce-back
boundary conditions. If options.formulation == "steady_stokes_limit",
the same stepper is still used; the solution is simply driven more gently
and interpreted in the low-Reynolds, low-Mach steady limit.
The inlet and outlet conditions are pressure boundary conditions. The public
API accepts lattice pressure values or a lattice pressure drop, which are
converted internally to the density values expected by the isothermal LBM
boundary operator through p_lu = c_s^2 rho.
The permeability conversion is based on lattice units:
K_phys = nu_lu * U_lu * L_lu * dx_phys**2 / dp_lu
where U_lu is the superficial sample velocity, nu_lu is the lattice
kinematic viscosity, L_lu is the voxel count along the flow axis, and
dp_lu = p_in,lu - p_out,lu.
This keeps the benchmark focused on permeability, which is the transport quantity comparable to the PNM solve without requiring a full physical pressure-unit calibration of the lattice simulation.
Source code in src/voids/benchmarks/xlb.py
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | |
benchmark_segmented_volume_with_xlb
¶
benchmark_segmented_volume_with_xlb(
phases,
*,
voxel_size,
flow_axis=None,
fluid=None,
delta_p=None,
pin=None,
pout=None,
options=None,
xlb_options=None,
length_unit="m",
pressure_unit="Pa",
extraction_kwargs=None,
provenance_notes=None,
strict=True,
)
Benchmark a segmented volume against a direct-image XLB solve.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
phases
|
ndarray
|
Binary segmented volume with |
required |
voxel_size
|
float
|
Physical voxel size of the segmented image. |
required |
flow_axis
|
str | None
|
Requested macroscopic flow axis. When omitted, the extracted sample axis is inferred from the image shape. |
None
|
fluid
|
FluidSinglePhase | None
|
Single-phase fluid properties. A positive density is required here because the shared physical pressure drop must be mapped into lattice pressure units for XLB. |
None
|
delta_p
|
float | None
|
Preferred physical pressure drop for the benchmark, typically in Pa.
When provided alone, the wrapper uses |
None
|
pin
|
float | None
|
Optional absolute physical inlet and outlet pressures. They are kept for
backward compatibility and for cases where a particular physical
reference level should be recorded. For the current incompressible
permeability benchmark, only the pressure drop |
None
|
pout
|
float | None
|
Optional absolute physical inlet and outlet pressures. They are kept for
backward compatibility and for cases where a particular physical
reference level should be recorded. For the current incompressible
permeability benchmark, only the pressure drop |
None
|
options
|
SinglePhaseOptions | None
|
|
None
|
xlb_options
|
XLBOptions | None
|
XLB options template. For this high-level wrapper the lattice pressure fields are overwritten so the direct-image XLB run matches the resolved physical pressure drop. |
None
|
length_unit
|
str
|
Metadata passed through the network-extraction provenance. |
'm'
|
pressure_unit
|
str
|
Metadata passed through the network-extraction provenance. |
'm'
|
extraction_kwargs
|
dict[str, object] | None
|
Optional keyword arguments forwarded to
:func: |
None
|
provenance_notes
|
dict[str, object] | None
|
Optional provenance metadata stored on the extracted-network result. |
None
|
strict
|
bool
|
Extraction strictness flag forwarded to the extraction layer. |
True
|
Returns:
| Type | Description |
|---|---|
SegmentedVolumeXLBResult
|
Joint record containing extraction results, the |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the image is invalid, the extracted spanning network lacks usable inlet/outlet pores, the pressure specification is inconsistent, the implied pressure drop is not positive, or the fluid density is missing for the physical-to-lattice pressure mapping. |
Notes
The voids side solves on the extracted pore network. The XLB side solves
directly on the binary segmented image. This is the scientifically relevant
comparison if the goal is to assess extraction loss and PNM model discrepancy
against a higher-fidelity voxel-scale reference.
The crucial consistency condition enforced here is that the same physical
pressure drop is used for both the voids and XLB sides of the benchmark.
The absolute pressure offset is numerically immaterial for the current
incompressible permeability benchmark. For example, delta_p=1 with the
default gauge and delta_p=1 with pin=101326 / pout=101325 drive
the same benchmark permeability comparison.
Source code in src/voids/benchmarks/xlb.py
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 | |