Skip to content

Add Flox development environment for CUDA builds#64

Draft
charlesbmi wants to merge 2 commits into
mainfrom
feature/flox-cuda-env
Draft

Add Flox development environment for CUDA builds#64
charlesbmi wants to merge 2 commits into
mainfrom
feature/flox-cuda-env

Conversation

@charlesbmi

@charlesbmi charlesbmi commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Introduction

Adds a Flox environment for compiling mach from source without installing the CUDA toolkit system-wide. Complements the existing Docker workflow with a lighter-weight, native Linux option.

CUDA 12.2 is pinned to match common driver support (e.g. NVIDIA 535.x). Python dependencies remain in the project .venv via uv sync — Flox supplies the build toolchain only.

Changes

  • .flox/env/manifest.toml: Linux-only environment with:

    • flox-cuda/cudaPackages_12_2cuda_nvcc, cuda_cudart (incl. static libs), cudatoolkit
    • gcc12 — host compiler compatible with CUDA 12.2 nvcc
    • uv@0.9.28 and gnumake — satisfy make compile prerequisites
    • CC, CXX, CUDA_HOME, LIBRARY_PATH vars for CMake/nvcc
  • .flox/env/manifest.lock: Locked package resolution for x86_64-linux and aarch64-linux

  • README.md: One-line Flox alternative under build prerequisites

Behavior

New capability: contributors with Flox installed can compile with:

flox activate
make compile

Tested locally: make check-system-dep and make compile succeed inside flox activate on Linux Mint 22.3 (driver 535, GTX 1060).

Note: prebuilt wheels target sm_75/89/90; older GPUs (e.g. GTX 1060, sm_61) can compile but cannot run the extension without adjusting CMAKE_CUDA_ARCHITECTURES.

Review checklist

  • All existing tests and checks pass
  • Unit tests covering the new feature or bugfix have been added (N/A — dev environment)
  • The documentation has been updated if necessary (✓ README updated)

Made with Cursor

@qodo-code-review

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test-gpu (3.11, linux-x64-nvidia-gpu-t4)

Failed stage: Run CUDA unit tests [❌]

Failed test name: tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[0]

Failure summary:

The action failed during make test because multiple pytest tests errored at setup while trying to
download an external dataset file.
- Tests attempt to download
PICMUS_experiment_resolution_distortion.uff from
http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff, which redirects to
https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff.
- The download
request returned 404 Client Error: Not Found, causing requests.raise_for_status() to raise an
HTTPError in src/mach/io/utils.py:223.
- That HTTPError is wrapped and re-raised as RuntimeError:
Failed to download ... in src/mach/io/utils.py:234, which aborts test setup and produces 8 test
errors (not assertion failures).
Affected tests include
tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[...],
tests/compare/test_vbeam.py::test_mach_matches_vbeam, and
tests/io/test_uff.py::test_picmus_phantom_resolution*.

Relevant error logs:
1:  Runner name: 'linux-x64-nvidia-gpu-t4-1000017624'
2:  Runner group name: 'gpu'
...

915:  ##[group]Run make compile
916:  �[36;1mmake compile�[0m
917:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
918:  env:
919:  CUDA_PATH: /usr/local/cuda-12.6
920:  CUDA_PATH_V12_6: /usr/local/cuda-12.6
921:  CUDA_PATH_VX_Y: CUDA_PATH_V12_6
922:  LD_LIBRARY_PATH: /usr/local/cuda-12.6/lib64:
923:  UV_PYTHON: 3.11
924:  VIRTUAL_ENV: /home/runner/work/mach/mach/.venv
925:  UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
926:  CC: gcc
927:  CXX: g++
928:  ##[endgroup]
929:  Compiling CUDA extension with nanobind...
930:  If you get an scikit-build-core error, you may need to 'uv cache clean' and 'trash build/'
931:  uv pip install scikit-build-core nanobind ninja cmake
...

1220:  DEBUG -- Found Python: /home/runner/work/mach/mach/.venv/bin/python3 (found suitable version "3.11.12", minimum required is "3.10") found components: Interpreter Development.Module
1221:  DEBUG -- Parsed NVCC version: major=12, minor=6
1222:  DEBUG -- Configuring done (2.2s)
1223:  DEBUG -- Generating done (0.0s)
1224:  DEBUG -- Build files have been written to: /home/runner/work/mach/mach/build/cp311-cp311-linux_x86_64
1225:  DEBUG *** Building project with Ninja...
1226:  DEBUG [1/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_internals.cpp.o
1227:  DEBUG [2/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_func.cpp.o
1228:  DEBUG [3/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_ft.cpp.o
1229:  DEBUG [4/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_ndarray.cpp.o
1230:  DEBUG [5/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_type.cpp.o
1231:  DEBUG [6/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_static_property.cpp.o
1232:  DEBUG [7/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/trampoline.cpp.o
1233:  DEBUG [8/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/nb_enum.cpp.o
1234:  DEBUG [9/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/implicit.cpp.o
1235:  DEBUG [10/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/error.cpp.o
1236:  DEBUG [11/14] Building CXX object CMakeFiles/nanobind-static.dir/.venv/lib/python3.11/site-packages/nanobind/src/common.cpp.o
...

1427:  CuPy Version                 : 13.4.1
1428:  CuPy Platform                : NVIDIA CUDA
1429:  NumPy Version                : 2.2.3
1430:  SciPy Version                : 1.15.1
1431:  Cython Build Version         : 3.0.12
1432:  Cython Runtime Version       : None
1433:  CUDA Root                    : /usr/local/cuda-12.6
1434:  nvcc PATH                    : /usr/local/cuda-12.6/bin/nvcc
1435:  CUDA Build Version           : 12080
1436:  CUDA Driver Version          : 13010
1437:  CUDA Runtime Version         : 12080 (linked to CuPy) / 12060 (locally installed)
1438:  CUDA Extra Include Dirs      : []
1439:  cuBLAS Version               : (available)
1440:  cuFFT Version                : None
1441:  cuRAND Version               : 10307
1442:  cuSOLVER Version             : ImportError('libcusolver.so.11: cannot open shared object file: No such file or directory')
1443:  cuSPARSE Version             : (available)
...

1491:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[1/16x_frames (2 frames)] PASSED
1492:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[1/8x_frames (4 frames)] PASSED
1493:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[1/4x_frames (8 frames)] PASSED
1494:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[1/2x_frames (16 frames)] PASSED
1495:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[1x_frames (32 frames)] PASSED
1496:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[2x_frames (64 frames)] PASSED
1497:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[4x_frames (128 frames)] PASSED
1498:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[8x_frames (256 frames)] PASSED
1499:  tests/compare/test_performance_scaling.py::test_scaling_ensemble_size[16x_frames (512 frames)] PASSED
1500:  tests/compare/test_pymust.py::test_pymust_benchmark[cpu] PASSED
1501:  tests/compare/test_pymust.py::test_pymust_benchmark[gpu] SKIPPED (ja...)
1502:  tests/compare/test_pymust.py::test_mach PASSED
1503:  tests/compare/test_pymust.py::test_mach_from_cpu PASSED
1504:  tests/compare/test_vbeam.py::test_vbeam_benchmark[gpu] Saved debug figures to /home/runner/work/mach/mach/tests/output/vbeam_results
1505:  PASSED
1506:  tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[0] ERROR
1507:  tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[1] ERROR
1508:  tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[10] ERROR
1509:  tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[37] ERROR
1510:  tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[74] ERROR
1511:  tests/compare/test_vbeam.py::test_mach_matches_vbeam ERROR
1512:  tests/io/test_must.py::test_download_pymust_doppler_data PASSED
1513:  tests/io/test_must.py::test_extract_pymust_params PASSED
1514:  tests/io/test_must.py::test_linear_probe_positions PASSED
1515:  tests/io/test_must.py::test_scan_grid PASSED
1516:  tests/io/test_uff.py::test_picmus_phantom_resolution ERROR
1517:  tests/io/test_uff.py::test_picmus_phantom_resolution_single_transmit ERROR
1518:  tests/private_api/test_array_api.py::test_array_protocol[numpy] PASSED
...

1586:  tests/test_nanobind.py::test_beamform_parameters[cupy-InterpolationType.Quadratic-0.0-3.0] PASSED
1587:  tests/test_nanobind.py::test_beamform_parameters[cupy-InterpolationType.Quadratic-1.0-1.0] PASSED
1588:  tests/test_nanobind.py::test_beamform_parameters[cupy-InterpolationType.Quadratic-1.0-3.0] PASSED
1589:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Linear-0.0-1.0] PASSED
1590:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Linear-0.0-3.0] PASSED
1591:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Linear-1.0-1.0] PASSED
1592:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Linear-1.0-3.0] PASSED
1593:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.NearestNeighbor-0.0-1.0] PASSED
1594:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.NearestNeighbor-0.0-3.0] PASSED
1595:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.NearestNeighbor-1.0-1.0] PASSED
1596:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.NearestNeighbor-1.0-3.0] PASSED
1597:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Quadratic-0.0-1.0] PASSED
1598:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Quadratic-0.0-3.0] PASSED
1599:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Quadratic-1.0-1.0] PASSED
1600:  tests/test_nanobind.py::test_beamform_parameters[jax-InterpolationType.Quadratic-1.0-3.0] PASSED
1601:  tests/test_nanobind.py::test_data_shape_errors[numpy] PASSED
1602:  tests/test_nanobind.py::test_data_shape_errors[cupy] PASSED
1603:  tests/test_nanobind.py::test_data_shape_errors[jax] PASSED
1604:  tests/test_nanobind.py::test_nvcc_version PASSED
1605:  tests/test_wavefront.py::TestPlaneWave::test_single_point_along_direction PASSED
1606:  tests/test_wavefront.py::TestPlaneWave::test_single_point_45_degrees PASSED
1607:  tests/test_wavefront.py::TestPlaneWave::test_single_point_perpendicular PASSED
1608:  tests/test_wavefront.py::TestPlaneWave::test_batch_points_1x3_shape PASSED
1609:  tests/test_wavefront.py::TestPlaneWave::test_batch_points_nx3_shape PASSED
1610:  tests/test_wavefront.py::TestPlaneWave::test_non_aligned_direction PASSED
1611:  tests/test_wavefront.py::TestPlaneWave::test_non_unit_direction_raises_error PASSED
1612:  tests/test_wavefront.py::TestPlaneWave::test_different_origins PASSED
1613:  tests/test_wavefront.py::TestPlaneWave::test_3d_direction_vector PASSED
1614:  tests/test_wavefront.py::TestPlaneWave::test_zero_distance_points PASSED
1615:  tests/test_wavefront.py::TestSphericalWave::test_focus_at_origin_point_on_axis PASSED
1616:  tests/test_wavefront.py::TestSphericalWave::test_diverging_wave_focus_behind_origin PASSED
1617:  tests/test_wavefront.py::TestSphericalWave::test_point_at_focus PASSED
1618:  tests/test_wavefront.py::TestSphericalWave::test_point_at_origin PASSED
1619:  tests/test_wavefront.py::TestSphericalWave::test_batch_points_nx3_shape PASSED
1620:  tests/test_wavefront.py::TestSphericalWave::test_off_axis_geometry PASSED
1621:  tests/test_wavefront.py::TestSphericalWave::test_3d_displacement PASSED
1622:  tests/test_wavefront.py::TestSphericalWave::test_symmetric_geometry PASSED
1623:  tests/test_wavefront.py::TestSphericalWave::test_batch_points_1x3_shape PASSED
1624:  ==================================== ERRORS ====================================
1625:  _________ ERROR at setup of test_mach_matches_vbeam_single_transmit[0] _________
1626:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

1640:  ) -> Path:
1641:  """Download a file from a URL with optional progress bar and integrity verification.
1642:  Args:
1643:  url: URL to download
1644:  output_path: Path where the file will be saved
1645:  timeout: Connection timeout in seconds
1646:  chunk_size: Size of chunks to download
1647:  overwrite: Whether to overwrite existing files
1648:  expected_hash: Expected hash value for integrity verification
1649:  digest: Hash algorithm to use (default: "sha1")
1650:  expected_size: Expected file size in bytes
1651:  show_progress: Whether to show progress bar (requires tqdm)
1652:  Returns:
1653:  Path to the downloaded file
1654:  Raises:
1655:  RuntimeError: If download fails or integrity check fails
1656:  ImportError: If show_progress=True but tqdm is not installed
1657:  """
1658:  if show_progress and (tqdm is None):
1659:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
1660:  output_path = Path(output_path)
1661:  output_path.parent.mkdir(parents=True, exist_ok=True)
1662:  # Check existing file
1663:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
1664:  return output_path
1665:  # Download the file
1666:  try:
1667:  response = requests.get(url, stream=True, timeout=timeout)
1668:  >           response.raise_for_status()
1669:  src/mach/io/utils.py:223: 
1670:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
1671:  self = <Response [404]>
1672:  def raise_for_status(self):
1673:  """Raises :class:`HTTPError`, if one occurred."""
1674:  http_error_msg = ""
1675:  if isinstance(self.reason, bytes):
1676:  # We attempt to decode utf-8 first because some servers
1677:  # choose to localize their reason strings. If the string
1678:  # isn't utf-8, we fall back to iso-8859-1 for all other
1679:  # encodings. (See PR #3538)
1680:  try:
1681:  reason = self.reason.decode("utf-8")
1682:  except UnicodeDecodeError:
1683:  reason = self.reason.decode("iso-8859-1")
1684:  else:
1685:  reason = self.reason
1686:  if 400 <= self.status_code < 500:
1687:  http_error_msg = (
1688:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
1689:  )
1690:  elif 500 <= self.status_code < 600:
1691:  http_error_msg = (
1692:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
1693:  )
1694:  if http_error_msg:
1695:  >           raise HTTPError(http_error_msg, response=self)
1696:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
1697:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
1698:  The above exception was the direct cause of the following exception:
...

1728:  ) -> Path:
1729:  """Download a file from a URL with optional progress bar and integrity verification.
1730:  Args:
1731:  url: URL to download
1732:  output_path: Path where the file will be saved
1733:  timeout: Connection timeout in seconds
1734:  chunk_size: Size of chunks to download
1735:  overwrite: Whether to overwrite existing files
1736:  expected_hash: Expected hash value for integrity verification
1737:  digest: Hash algorithm to use (default: "sha1")
1738:  expected_size: Expected file size in bytes
1739:  show_progress: Whether to show progress bar (requires tqdm)
1740:  Returns:
1741:  Path to the downloaded file
1742:  Raises:
1743:  RuntimeError: If download fails or integrity check fails
1744:  ImportError: If show_progress=True but tqdm is not installed
1745:  """
1746:  if show_progress and (tqdm is None):
1747:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
1748:  output_path = Path(output_path)
1749:  output_path.parent.mkdir(parents=True, exist_ok=True)
1750:  # Check existing file
1751:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
1752:  return output_path
1753:  # Download the file
1754:  try:
1755:  response = requests.get(url, stream=True, timeout=timeout)
1756:  response.raise_for_status()
1757:  total_size = int(response.headers.get("content-length", 0))
1758:  if expected_size is not None and total_size != expected_size:
1759:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
1760:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
1761:  except (OSError, requests.RequestException) as err:
1762:  output_path.unlink(missing_ok=True)
1763:  msg = f"Failed to download {url}: {err!s}"
1764:  >           raise RuntimeError(msg) from err
1765:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
1766:  src/mach/io/utils.py:234: RuntimeError
1767:  _________ ERROR at setup of test_mach_matches_vbeam_single_transmit[1] _________
1768:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

1782:  ) -> Path:
1783:  """Download a file from a URL with optional progress bar and integrity verification.
1784:  Args:
1785:  url: URL to download
1786:  output_path: Path where the file will be saved
1787:  timeout: Connection timeout in seconds
1788:  chunk_size: Size of chunks to download
1789:  overwrite: Whether to overwrite existing files
1790:  expected_hash: Expected hash value for integrity verification
1791:  digest: Hash algorithm to use (default: "sha1")
1792:  expected_size: Expected file size in bytes
1793:  show_progress: Whether to show progress bar (requires tqdm)
1794:  Returns:
1795:  Path to the downloaded file
1796:  Raises:
1797:  RuntimeError: If download fails or integrity check fails
1798:  ImportError: If show_progress=True but tqdm is not installed
1799:  """
1800:  if show_progress and (tqdm is None):
1801:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
1802:  output_path = Path(output_path)
1803:  output_path.parent.mkdir(parents=True, exist_ok=True)
1804:  # Check existing file
1805:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
1806:  return output_path
1807:  # Download the file
1808:  try:
1809:  response = requests.get(url, stream=True, timeout=timeout)
1810:  >           response.raise_for_status()
1811:  src/mach/io/utils.py:223: 
1812:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
1813:  self = <Response [404]>
1814:  def raise_for_status(self):
1815:  """Raises :class:`HTTPError`, if one occurred."""
1816:  http_error_msg = ""
1817:  if isinstance(self.reason, bytes):
1818:  # We attempt to decode utf-8 first because some servers
1819:  # choose to localize their reason strings. If the string
1820:  # isn't utf-8, we fall back to iso-8859-1 for all other
1821:  # encodings. (See PR #3538)
1822:  try:
1823:  reason = self.reason.decode("utf-8")
1824:  except UnicodeDecodeError:
1825:  reason = self.reason.decode("iso-8859-1")
1826:  else:
1827:  reason = self.reason
1828:  if 400 <= self.status_code < 500:
1829:  http_error_msg = (
1830:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
1831:  )
1832:  elif 500 <= self.status_code < 600:
1833:  http_error_msg = (
1834:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
1835:  )
1836:  if http_error_msg:
1837:  >           raise HTTPError(http_error_msg, response=self)
1838:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
1839:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
1840:  The above exception was the direct cause of the following exception:
...

1870:  ) -> Path:
1871:  """Download a file from a URL with optional progress bar and integrity verification.
1872:  Args:
1873:  url: URL to download
1874:  output_path: Path where the file will be saved
1875:  timeout: Connection timeout in seconds
1876:  chunk_size: Size of chunks to download
1877:  overwrite: Whether to overwrite existing files
1878:  expected_hash: Expected hash value for integrity verification
1879:  digest: Hash algorithm to use (default: "sha1")
1880:  expected_size: Expected file size in bytes
1881:  show_progress: Whether to show progress bar (requires tqdm)
1882:  Returns:
1883:  Path to the downloaded file
1884:  Raises:
1885:  RuntimeError: If download fails or integrity check fails
1886:  ImportError: If show_progress=True but tqdm is not installed
1887:  """
1888:  if show_progress and (tqdm is None):
1889:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
1890:  output_path = Path(output_path)
1891:  output_path.parent.mkdir(parents=True, exist_ok=True)
1892:  # Check existing file
1893:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
1894:  return output_path
1895:  # Download the file
1896:  try:
1897:  response = requests.get(url, stream=True, timeout=timeout)
1898:  response.raise_for_status()
1899:  total_size = int(response.headers.get("content-length", 0))
1900:  if expected_size is not None and total_size != expected_size:
1901:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
1902:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
1903:  except (OSError, requests.RequestException) as err:
1904:  output_path.unlink(missing_ok=True)
1905:  msg = f"Failed to download {url}: {err!s}"
1906:  >           raise RuntimeError(msg) from err
1907:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
1908:  src/mach/io/utils.py:234: RuntimeError
1909:  ________ ERROR at setup of test_mach_matches_vbeam_single_transmit[10] _________
1910:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

1924:  ) -> Path:
1925:  """Download a file from a URL with optional progress bar and integrity verification.
1926:  Args:
1927:  url: URL to download
1928:  output_path: Path where the file will be saved
1929:  timeout: Connection timeout in seconds
1930:  chunk_size: Size of chunks to download
1931:  overwrite: Whether to overwrite existing files
1932:  expected_hash: Expected hash value for integrity verification
1933:  digest: Hash algorithm to use (default: "sha1")
1934:  expected_size: Expected file size in bytes
1935:  show_progress: Whether to show progress bar (requires tqdm)
1936:  Returns:
1937:  Path to the downloaded file
1938:  Raises:
1939:  RuntimeError: If download fails or integrity check fails
1940:  ImportError: If show_progress=True but tqdm is not installed
1941:  """
1942:  if show_progress and (tqdm is None):
1943:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
1944:  output_path = Path(output_path)
1945:  output_path.parent.mkdir(parents=True, exist_ok=True)
1946:  # Check existing file
1947:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
1948:  return output_path
1949:  # Download the file
1950:  try:
1951:  response = requests.get(url, stream=True, timeout=timeout)
1952:  >           response.raise_for_status()
1953:  src/mach/io/utils.py:223: 
1954:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
1955:  self = <Response [404]>
1956:  def raise_for_status(self):
1957:  """Raises :class:`HTTPError`, if one occurred."""
1958:  http_error_msg = ""
1959:  if isinstance(self.reason, bytes):
1960:  # We attempt to decode utf-8 first because some servers
1961:  # choose to localize their reason strings. If the string
1962:  # isn't utf-8, we fall back to iso-8859-1 for all other
1963:  # encodings. (See PR #3538)
1964:  try:
1965:  reason = self.reason.decode("utf-8")
1966:  except UnicodeDecodeError:
1967:  reason = self.reason.decode("iso-8859-1")
1968:  else:
1969:  reason = self.reason
1970:  if 400 <= self.status_code < 500:
1971:  http_error_msg = (
1972:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
1973:  )
1974:  elif 500 <= self.status_code < 600:
1975:  http_error_msg = (
1976:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
1977:  )
1978:  if http_error_msg:
1979:  >           raise HTTPError(http_error_msg, response=self)
1980:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
1981:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
1982:  The above exception was the direct cause of the following exception:
...

2012:  ) -> Path:
2013:  """Download a file from a URL with optional progress bar and integrity verification.
2014:  Args:
2015:  url: URL to download
2016:  output_path: Path where the file will be saved
2017:  timeout: Connection timeout in seconds
2018:  chunk_size: Size of chunks to download
2019:  overwrite: Whether to overwrite existing files
2020:  expected_hash: Expected hash value for integrity verification
2021:  digest: Hash algorithm to use (default: "sha1")
2022:  expected_size: Expected file size in bytes
2023:  show_progress: Whether to show progress bar (requires tqdm)
2024:  Returns:
2025:  Path to the downloaded file
2026:  Raises:
2027:  RuntimeError: If download fails or integrity check fails
2028:  ImportError: If show_progress=True but tqdm is not installed
2029:  """
2030:  if show_progress and (tqdm is None):
2031:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2032:  output_path = Path(output_path)
2033:  output_path.parent.mkdir(parents=True, exist_ok=True)
2034:  # Check existing file
2035:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2036:  return output_path
2037:  # Download the file
2038:  try:
2039:  response = requests.get(url, stream=True, timeout=timeout)
2040:  response.raise_for_status()
2041:  total_size = int(response.headers.get("content-length", 0))
2042:  if expected_size is not None and total_size != expected_size:
2043:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
2044:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
2045:  except (OSError, requests.RequestException) as err:
2046:  output_path.unlink(missing_ok=True)
2047:  msg = f"Failed to download {url}: {err!s}"
2048:  >           raise RuntimeError(msg) from err
2049:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2050:  src/mach/io/utils.py:234: RuntimeError
2051:  ________ ERROR at setup of test_mach_matches_vbeam_single_transmit[37] _________
2052:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

2066:  ) -> Path:
2067:  """Download a file from a URL with optional progress bar and integrity verification.
2068:  Args:
2069:  url: URL to download
2070:  output_path: Path where the file will be saved
2071:  timeout: Connection timeout in seconds
2072:  chunk_size: Size of chunks to download
2073:  overwrite: Whether to overwrite existing files
2074:  expected_hash: Expected hash value for integrity verification
2075:  digest: Hash algorithm to use (default: "sha1")
2076:  expected_size: Expected file size in bytes
2077:  show_progress: Whether to show progress bar (requires tqdm)
2078:  Returns:
2079:  Path to the downloaded file
2080:  Raises:
2081:  RuntimeError: If download fails or integrity check fails
2082:  ImportError: If show_progress=True but tqdm is not installed
2083:  """
2084:  if show_progress and (tqdm is None):
2085:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2086:  output_path = Path(output_path)
2087:  output_path.parent.mkdir(parents=True, exist_ok=True)
2088:  # Check existing file
2089:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2090:  return output_path
2091:  # Download the file
2092:  try:
2093:  response = requests.get(url, stream=True, timeout=timeout)
2094:  >           response.raise_for_status()
2095:  src/mach/io/utils.py:223: 
2096:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2097:  self = <Response [404]>
2098:  def raise_for_status(self):
2099:  """Raises :class:`HTTPError`, if one occurred."""
2100:  http_error_msg = ""
2101:  if isinstance(self.reason, bytes):
2102:  # We attempt to decode utf-8 first because some servers
2103:  # choose to localize their reason strings. If the string
2104:  # isn't utf-8, we fall back to iso-8859-1 for all other
2105:  # encodings. (See PR #3538)
2106:  try:
2107:  reason = self.reason.decode("utf-8")
2108:  except UnicodeDecodeError:
2109:  reason = self.reason.decode("iso-8859-1")
2110:  else:
2111:  reason = self.reason
2112:  if 400 <= self.status_code < 500:
2113:  http_error_msg = (
2114:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
2115:  )
2116:  elif 500 <= self.status_code < 600:
2117:  http_error_msg = (
2118:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
2119:  )
2120:  if http_error_msg:
2121:  >           raise HTTPError(http_error_msg, response=self)
2122:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2123:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
2124:  The above exception was the direct cause of the following exception:
...

2154:  ) -> Path:
2155:  """Download a file from a URL with optional progress bar and integrity verification.
2156:  Args:
2157:  url: URL to download
2158:  output_path: Path where the file will be saved
2159:  timeout: Connection timeout in seconds
2160:  chunk_size: Size of chunks to download
2161:  overwrite: Whether to overwrite existing files
2162:  expected_hash: Expected hash value for integrity verification
2163:  digest: Hash algorithm to use (default: "sha1")
2164:  expected_size: Expected file size in bytes
2165:  show_progress: Whether to show progress bar (requires tqdm)
2166:  Returns:
2167:  Path to the downloaded file
2168:  Raises:
2169:  RuntimeError: If download fails or integrity check fails
2170:  ImportError: If show_progress=True but tqdm is not installed
2171:  """
2172:  if show_progress and (tqdm is None):
2173:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2174:  output_path = Path(output_path)
2175:  output_path.parent.mkdir(parents=True, exist_ok=True)
2176:  # Check existing file
2177:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2178:  return output_path
2179:  # Download the file
2180:  try:
2181:  response = requests.get(url, stream=True, timeout=timeout)
2182:  response.raise_for_status()
2183:  total_size = int(response.headers.get("content-length", 0))
2184:  if expected_size is not None and total_size != expected_size:
2185:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
2186:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
2187:  except (OSError, requests.RequestException) as err:
2188:  output_path.unlink(missing_ok=True)
2189:  msg = f"Failed to download {url}: {err!s}"
2190:  >           raise RuntimeError(msg) from err
2191:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2192:  src/mach/io/utils.py:234: RuntimeError
2193:  ________ ERROR at setup of test_mach_matches_vbeam_single_transmit[74] _________
2194:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

2208:  ) -> Path:
2209:  """Download a file from a URL with optional progress bar and integrity verification.
2210:  Args:
2211:  url: URL to download
2212:  output_path: Path where the file will be saved
2213:  timeout: Connection timeout in seconds
2214:  chunk_size: Size of chunks to download
2215:  overwrite: Whether to overwrite existing files
2216:  expected_hash: Expected hash value for integrity verification
2217:  digest: Hash algorithm to use (default: "sha1")
2218:  expected_size: Expected file size in bytes
2219:  show_progress: Whether to show progress bar (requires tqdm)
2220:  Returns:
2221:  Path to the downloaded file
2222:  Raises:
2223:  RuntimeError: If download fails or integrity check fails
2224:  ImportError: If show_progress=True but tqdm is not installed
2225:  """
2226:  if show_progress and (tqdm is None):
2227:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2228:  output_path = Path(output_path)
2229:  output_path.parent.mkdir(parents=True, exist_ok=True)
2230:  # Check existing file
2231:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2232:  return output_path
2233:  # Download the file
2234:  try:
2235:  response = requests.get(url, stream=True, timeout=timeout)
2236:  >           response.raise_for_status()
2237:  src/mach/io/utils.py:223: 
2238:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2239:  self = <Response [404]>
2240:  def raise_for_status(self):
2241:  """Raises :class:`HTTPError`, if one occurred."""
2242:  http_error_msg = ""
2243:  if isinstance(self.reason, bytes):
2244:  # We attempt to decode utf-8 first because some servers
2245:  # choose to localize their reason strings. If the string
2246:  # isn't utf-8, we fall back to iso-8859-1 for all other
2247:  # encodings. (See PR #3538)
2248:  try:
2249:  reason = self.reason.decode("utf-8")
2250:  except UnicodeDecodeError:
2251:  reason = self.reason.decode("iso-8859-1")
2252:  else:
2253:  reason = self.reason
2254:  if 400 <= self.status_code < 500:
2255:  http_error_msg = (
2256:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
2257:  )
2258:  elif 500 <= self.status_code < 600:
2259:  http_error_msg = (
2260:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
2261:  )
2262:  if http_error_msg:
2263:  >           raise HTTPError(http_error_msg, response=self)
2264:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2265:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
2266:  The above exception was the direct cause of the following exception:
...

2296:  ) -> Path:
2297:  """Download a file from a URL with optional progress bar and integrity verification.
2298:  Args:
2299:  url: URL to download
2300:  output_path: Path where the file will be saved
2301:  timeout: Connection timeout in seconds
2302:  chunk_size: Size of chunks to download
2303:  overwrite: Whether to overwrite existing files
2304:  expected_hash: Expected hash value for integrity verification
2305:  digest: Hash algorithm to use (default: "sha1")
2306:  expected_size: Expected file size in bytes
2307:  show_progress: Whether to show progress bar (requires tqdm)
2308:  Returns:
2309:  Path to the downloaded file
2310:  Raises:
2311:  RuntimeError: If download fails or integrity check fails
2312:  ImportError: If show_progress=True but tqdm is not installed
2313:  """
2314:  if show_progress and (tqdm is None):
2315:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2316:  output_path = Path(output_path)
2317:  output_path.parent.mkdir(parents=True, exist_ok=True)
2318:  # Check existing file
2319:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2320:  return output_path
2321:  # Download the file
2322:  try:
2323:  response = requests.get(url, stream=True, timeout=timeout)
2324:  response.raise_for_status()
2325:  total_size = int(response.headers.get("content-length", 0))
2326:  if expected_size is not None and total_size != expected_size:
2327:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
2328:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
2329:  except (OSError, requests.RequestException) as err:
2330:  output_path.unlink(missing_ok=True)
2331:  msg = f"Failed to download {url}: {err!s}"
2332:  >           raise RuntimeError(msg) from err
2333:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2334:  src/mach/io/utils.py:234: RuntimeError
2335:  __________________ ERROR at setup of test_mach_matches_vbeam ___________________
2336:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

2350:  ) -> Path:
2351:  """Download a file from a URL with optional progress bar and integrity verification.
2352:  Args:
2353:  url: URL to download
2354:  output_path: Path where the file will be saved
2355:  timeout: Connection timeout in seconds
2356:  chunk_size: Size of chunks to download
2357:  overwrite: Whether to overwrite existing files
2358:  expected_hash: Expected hash value for integrity verification
2359:  digest: Hash algorithm to use (default: "sha1")
2360:  expected_size: Expected file size in bytes
2361:  show_progress: Whether to show progress bar (requires tqdm)
2362:  Returns:
2363:  Path to the downloaded file
2364:  Raises:
2365:  RuntimeError: If download fails or integrity check fails
2366:  ImportError: If show_progress=True but tqdm is not installed
2367:  """
2368:  if show_progress and (tqdm is None):
2369:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2370:  output_path = Path(output_path)
2371:  output_path.parent.mkdir(parents=True, exist_ok=True)
2372:  # Check existing file
2373:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2374:  return output_path
2375:  # Download the file
2376:  try:
2377:  response = requests.get(url, stream=True, timeout=timeout)
2378:  >           response.raise_for_status()
2379:  src/mach/io/utils.py:223: 
2380:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2381:  self = <Response [404]>
2382:  def raise_for_status(self):
2383:  """Raises :class:`HTTPError`, if one occurred."""
2384:  http_error_msg = ""
2385:  if isinstance(self.reason, bytes):
2386:  # We attempt to decode utf-8 first because some servers
2387:  # choose to localize their reason strings. If the string
2388:  # isn't utf-8, we fall back to iso-8859-1 for all other
2389:  # encodings. (See PR #3538)
2390:  try:
2391:  reason = self.reason.decode("utf-8")
2392:  except UnicodeDecodeError:
2393:  reason = self.reason.decode("iso-8859-1")
2394:  else:
2395:  reason = self.reason
2396:  if 400 <= self.status_code < 500:
2397:  http_error_msg = (
2398:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
2399:  )
2400:  elif 500 <= self.status_code < 600:
2401:  http_error_msg = (
2402:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
2403:  )
2404:  if http_error_msg:
2405:  >           raise HTTPError(http_error_msg, response=self)
2406:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2407:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
2408:  The above exception was the direct cause of the following exception:
...

2438:  ) -> Path:
2439:  """Download a file from a URL with optional progress bar and integrity verification.
2440:  Args:
2441:  url: URL to download
2442:  output_path: Path where the file will be saved
2443:  timeout: Connection timeout in seconds
2444:  chunk_size: Size of chunks to download
2445:  overwrite: Whether to overwrite existing files
2446:  expected_hash: Expected hash value for integrity verification
2447:  digest: Hash algorithm to use (default: "sha1")
2448:  expected_size: Expected file size in bytes
2449:  show_progress: Whether to show progress bar (requires tqdm)
2450:  Returns:
2451:  Path to the downloaded file
2452:  Raises:
2453:  RuntimeError: If download fails or integrity check fails
2454:  ImportError: If show_progress=True but tqdm is not installed
2455:  """
2456:  if show_progress and (tqdm is None):
2457:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2458:  output_path = Path(output_path)
2459:  output_path.parent.mkdir(parents=True, exist_ok=True)
2460:  # Check existing file
2461:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2462:  return output_path
2463:  # Download the file
2464:  try:
2465:  response = requests.get(url, stream=True, timeout=timeout)
2466:  response.raise_for_status()
2467:  total_size = int(response.headers.get("content-length", 0))
2468:  if expected_size is not None and total_size != expected_size:
2469:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
2470:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
2471:  except (OSError, requests.RequestException) as err:
2472:  output_path.unlink(missing_ok=True)
2473:  msg = f"Failed to download {url}: {err!s}"
2474:  >           raise RuntimeError(msg) from err
2475:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2476:  src/mach/io/utils.py:234: RuntimeError
2477:  _______________ ERROR at setup of test_picmus_phantom_resolution _______________
2478:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

2492:  ) -> Path:
2493:  """Download a file from a URL with optional progress bar and integrity verification.
2494:  Args:
2495:  url: URL to download
2496:  output_path: Path where the file will be saved
2497:  timeout: Connection timeout in seconds
2498:  chunk_size: Size of chunks to download
2499:  overwrite: Whether to overwrite existing files
2500:  expected_hash: Expected hash value for integrity verification
2501:  digest: Hash algorithm to use (default: "sha1")
2502:  expected_size: Expected file size in bytes
2503:  show_progress: Whether to show progress bar (requires tqdm)
2504:  Returns:
2505:  Path to the downloaded file
2506:  Raises:
2507:  RuntimeError: If download fails or integrity check fails
2508:  ImportError: If show_progress=True but tqdm is not installed
2509:  """
2510:  if show_progress and (tqdm is None):
2511:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2512:  output_path = Path(output_path)
2513:  output_path.parent.mkdir(parents=True, exist_ok=True)
2514:  # Check existing file
2515:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2516:  return output_path
2517:  # Download the file
2518:  try:
2519:  response = requests.get(url, stream=True, timeout=timeout)
2520:  >           response.raise_for_status()
2521:  src/mach/io/utils.py:223: 
2522:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2523:  self = <Response [404]>
2524:  def raise_for_status(self):
2525:  """Raises :class:`HTTPError`, if one occurred."""
2526:  http_error_msg = ""
2527:  if isinstance(self.reason, bytes):
2528:  # We attempt to decode utf-8 first because some servers
2529:  # choose to localize their reason strings. If the string
2530:  # isn't utf-8, we fall back to iso-8859-1 for all other
2531:  # encodings. (See PR #3538)
2532:  try:
2533:  reason = self.reason.decode("utf-8")
2534:  except UnicodeDecodeError:
2535:  reason = self.reason.decode("iso-8859-1")
2536:  else:
2537:  reason = self.reason
2538:  if 400 <= self.status_code < 500:
2539:  http_error_msg = (
2540:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
2541:  )
2542:  elif 500 <= self.status_code < 600:
2543:  http_error_msg = (
2544:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
2545:  )
2546:  if http_error_msg:
2547:  >           raise HTTPError(http_error_msg, response=self)
2548:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2549:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
2550:  The above exception was the direct cause of the following exception:
...

2580:  ) -> Path:
2581:  """Download a file from a URL with optional progress bar and integrity verification.
2582:  Args:
2583:  url: URL to download
2584:  output_path: Path where the file will be saved
2585:  timeout: Connection timeout in seconds
2586:  chunk_size: Size of chunks to download
2587:  overwrite: Whether to overwrite existing files
2588:  expected_hash: Expected hash value for integrity verification
2589:  digest: Hash algorithm to use (default: "sha1")
2590:  expected_size: Expected file size in bytes
2591:  show_progress: Whether to show progress bar (requires tqdm)
2592:  Returns:
2593:  Path to the downloaded file
2594:  Raises:
2595:  RuntimeError: If download fails or integrity check fails
2596:  ImportError: If show_progress=True but tqdm is not installed
2597:  """
2598:  if show_progress and (tqdm is None):
2599:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2600:  output_path = Path(output_path)
2601:  output_path.parent.mkdir(parents=True, exist_ok=True)
2602:  # Check existing file
2603:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2604:  return output_path
2605:  # Download the file
2606:  try:
2607:  response = requests.get(url, stream=True, timeout=timeout)
2608:  response.raise_for_status()
2609:  total_size = int(response.headers.get("content-length", 0))
2610:  if expected_size is not None and total_size != expected_size:
2611:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
2612:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
2613:  except (OSError, requests.RequestException) as err:
2614:  output_path.unlink(missing_ok=True)
2615:  msg = f"Failed to download {url}: {err!s}"
2616:  >           raise RuntimeError(msg) from err
2617:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2618:  src/mach/io/utils.py:234: RuntimeError
2619:  _______ ERROR at setup of test_picmus_phantom_resolution_single_transmit _______
2620:  url = 'http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff'
...

2634:  ) -> Path:
2635:  """Download a file from a URL with optional progress bar and integrity verification.
2636:  Args:
2637:  url: URL to download
2638:  output_path: Path where the file will be saved
2639:  timeout: Connection timeout in seconds
2640:  chunk_size: Size of chunks to download
2641:  overwrite: Whether to overwrite existing files
2642:  expected_hash: Expected hash value for integrity verification
2643:  digest: Hash algorithm to use (default: "sha1")
2644:  expected_size: Expected file size in bytes
2645:  show_progress: Whether to show progress bar (requires tqdm)
2646:  Returns:
2647:  Path to the downloaded file
2648:  Raises:
2649:  RuntimeError: If download fails or integrity check fails
2650:  ImportError: If show_progress=True but tqdm is not installed
2651:  """
2652:  if show_progress and (tqdm is None):
2653:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2654:  output_path = Path(output_path)
2655:  output_path.parent.mkdir(parents=True, exist_ok=True)
2656:  # Check existing file
2657:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2658:  return output_path
2659:  # Download the file
2660:  try:
2661:  response = requests.get(url, stream=True, timeout=timeout)
2662:  >           response.raise_for_status()
2663:  src/mach/io/utils.py:223: 
2664:  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2665:  self = <Response [404]>
2666:  def raise_for_status(self):
2667:  """Raises :class:`HTTPError`, if one occurred."""
2668:  http_error_msg = ""
2669:  if isinstance(self.reason, bytes):
2670:  # We attempt to decode utf-8 first because some servers
2671:  # choose to localize their reason strings. If the string
2672:  # isn't utf-8, we fall back to iso-8859-1 for all other
2673:  # encodings. (See PR #3538)
2674:  try:
2675:  reason = self.reason.decode("utf-8")
2676:  except UnicodeDecodeError:
2677:  reason = self.reason.decode("iso-8859-1")
2678:  else:
2679:  reason = self.reason
2680:  if 400 <= self.status_code < 500:
2681:  http_error_msg = (
2682:  f"{self.status_code} Client Error: {reason} for url: {self.url}"
2683:  )
2684:  elif 500 <= self.status_code < 600:
2685:  http_error_msg = (
2686:  f"{self.status_code} Server Error: {reason} for url: {self.url}"
2687:  )
2688:  if http_error_msg:
2689:  >           raise HTTPError(http_error_msg, response=self)
2690:  E           requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2691:  .venv/lib/python3.11/site-packages/requests/models.py:1024: HTTPError
2692:  The above exception was the direct cause of the following exception:
...

2722:  ) -> Path:
2723:  """Download a file from a URL with optional progress bar and integrity verification.
2724:  Args:
2725:  url: URL to download
2726:  output_path: Path where the file will be saved
2727:  timeout: Connection timeout in seconds
2728:  chunk_size: Size of chunks to download
2729:  overwrite: Whether to overwrite existing files
2730:  expected_hash: Expected hash value for integrity verification
2731:  digest: Hash algorithm to use (default: "sha1")
2732:  expected_size: Expected file size in bytes
2733:  show_progress: Whether to show progress bar (requires tqdm)
2734:  Returns:
2735:  Path to the downloaded file
2736:  Raises:
2737:  RuntimeError: If download fails or integrity check fails
2738:  ImportError: If show_progress=True but tqdm is not installed
2739:  """
2740:  if show_progress and (tqdm is None):
2741:  raise ImportError("tqdm is required for progress bars. Install with: pip install tqdm")
2742:  output_path = Path(output_path)
2743:  output_path.parent.mkdir(parents=True, exist_ok=True)
2744:  # Check existing file
2745:  if not overwrite and _verify_file(output_path, expected_size, expected_hash, digest):
2746:  return output_path
2747:  # Download the file
2748:  try:
2749:  response = requests.get(url, stream=True, timeout=timeout)
2750:  response.raise_for_status()
2751:  total_size = int(response.headers.get("content-length", 0))
2752:  if expected_size is not None and total_size != expected_size:
2753:  warnings.warn(f"Server reports size {total_size}, expected {expected_size}", stacklevel=2)
2754:  _download_with_progress(response, output_path, chunk_size, total_size, show_progress)
2755:  except (OSError, requests.RequestException) as err:
2756:  output_path.unlink(missing_ok=True)
2757:  msg = f"Failed to download {url}: {err!s}"
2758:  >           raise RuntimeError(msg) from err
2759:  E           RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2760:  src/mach/io/utils.py:234: RuntimeError
2761:  =========================== short test summary info ============================
2762:  ERROR tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[0] - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2763:  ERROR tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[1] - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2764:  ERROR tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[10] - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2765:  ERROR tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[37] - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2766:  ERROR tests/compare/test_vbeam.py::test_mach_matches_vbeam_single_transmit[74] - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2767:  ERROR tests/compare/test_vbeam.py::test_mach_matches_vbeam - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2768:  ERROR tests/io/test_uff.py::test_picmus_phantom_resolution - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2769:  ERROR tests/io/test_uff.py::test_picmus_phantom_resolution_single_transmit - RuntimeError: Failed to download http://www.ustb.no/datasets/PICMUS_experiment_resolution_distortion.uff: 404 Client Error: Not Found for url: https://unioslo.github.io/USTB/datasets/PICMUS_experiment_resolution_distortion.uff
2770:  ================== 134 passed, 2 skipped, 8 errors in 15.81s ===================
2771:  make: *** [Makefile:71: test] Error 1
2772:  ##[error]Process completed with exit code 2.
2773:  Post job cleanup.

charlesbmi and others added 2 commits June 13, 2026 21:57
Provide a reproducible Linux toolchain (CUDA 12.2, gcc 12, uv, make) so
contributors can compile without a system-wide CUDA toolkit install.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace Jimver/cuda-toolkit with a new setup-flox composite action that
installs Flox, caches the Nix store and .flox/cache, and runs flox activate.

Update setup-cuda-python-env, check, test_cpu, test_gpu, docs, and wheels
workflows to use setup-flox. Subsequent steps run commands via flox activate -c
since each GitHub Actions step runs in an isolated shell environment.

Co-authored-by: charles <charlesbmi@users.noreply.github.com>
@cursor cursor Bot force-pushed the feature/flox-cuda-env branch from 1df1e67 to 872740b Compare June 13, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants