From d318911221f17d766920d58d8b3a0d06a28cd466 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 19:15:50 +0000 Subject: [PATCH] cuda: compiler_v2 module becomes compiler The package already exposed it as CelerisLab.cuda.compiler; the module now has that name and every tip caller, doc and current-tree test imports it directly (no alias). Historical-revision tooling is not edited: the Taylor-Green harness (run_case.py, hash-pinned check_projected_pairs.py, assess receipts) only runs against a135252, whose package has cuda/compiler_v2.py, and the J campaign runners and dated docs describe past trees. Its two current-tree tests now import that package from git (historical_package) instead of the tip, and the kernel-root test first checks the accepted case. Co-Authored-By: Claude Opus 5.5 --- docs/lifecycle.md | 2 +- src/CelerisLab/body/manager.py | 4 +- src/CelerisLab/config.py | 2 +- src/CelerisLab/configs/CONFIG.md | 4 +- src/CelerisLab/cuda/__init__.py | 2 +- .../cuda/{compiler_v2.py => compiler.py} | 2 +- src/CelerisLab/cuda/obs_layout.py | 2 +- src/CelerisLab/lbm/block_stepper.py | 2 +- src/CelerisLab/lbm/refinement_stepper.py | 2 +- src/CelerisLab/simulation.py | 2 +- .../integration/test_runtime_contracts_gpu.py | 4 +- tests/unit/test_planar_velocity.py | 2 +- .../multigrid/test_block_stepper.py | 2 +- .../multigrid/test_channel_root_gpu.py | 2 +- .../multigrid/test_fine_surface_gpu.py | 2 +- .../multigrid/test_refinement_blocked_base.py | 2 +- .../multigrid/test_refinement_blocked_fine.py | 2 +- .../multigrid/test_refinement_gpu.py | 2 +- .../multigrid/test_refinement_jets.py | 2 +- .../multigrid/test_refinement_three_level.py | 2 +- .../taylor_green/test_projected_pairs.py | 47 +++++++++++++++++-- 21 files changed, 65 insertions(+), 28 deletions(-) rename src/CelerisLab/cuda/{compiler_v2.py => compiler.py} (99%) diff --git a/docs/lifecycle.md b/docs/lifecycle.md index c3e5c79..efdce16 100644 --- a/docs/lifecycle.md +++ b/docs/lifecycle.md @@ -5,7 +5,7 @@ it is; it adds no framework. Line references are to candidate 0f8fdbc3. | Class | Contents | How it changes | Owner rule | |---|---|---|---| -| 1. Module and its globals | Compile-time (visible to the compiler, fixed per module): `config.h` macros incl. `NX`/`NY`, `COLLISION_MODEL`, `BLOCK_ROOT_BC`, `WALL_*`. Runtime per-module globals (not visible to the compiler; written by host code): `__constant__` `d_params`, `d_w`, `d_cx`, `d_cy`, `d_block_channel`, `d_block_wall`; plus bound kernel handles | Globals are written once by the owner right after `load_module`. Uniform grid: `Simulation._recompile` (construction, body-topology sync) loads a new module and re-uploads; `update_runtime_params` rewrites `d_params` (omega, force, n_objects) between runs. Steppers: never rewritten after construction, so they are immutable for the instance's lifetime although they are runtime values. The same quantity can be a macro in one route and a global in another (dense slot root: `WALL_SLOT_WIDTH`/`WALL_NORMAL_VELOCITY` macros; blocked slot root: `d_block_wall`), which can change generated arithmetic. | Every owner loads its own module (`compiler_v2.load_module` = `module_from_file`, no cache). A loaded module is never shared between live owners: globals are per module, so sharing would share mutable `d_params`. | +| 1. Module and its globals | Compile-time (visible to the compiler, fixed per module): `config.h` macros incl. `NX`/`NY`, `COLLISION_MODEL`, `BLOCK_ROOT_BC`, `WALL_*`. Runtime per-module globals (not visible to the compiler; written by host code): `__constant__` `d_params`, `d_w`, `d_cx`, `d_cy`, `d_block_channel`, `d_block_wall`; plus bound kernel handles | Globals are written once by the owner right after `load_module`. Uniform grid: `Simulation._recompile` (construction, body-topology sync) loads a new module and re-uploads; `update_runtime_params` rewrites `d_params` (omega, force, n_objects) between runs. Steppers: never rewritten after construction, so they are immutable for the instance's lifetime although they are runtime values. The same quantity can be a macro in one route and a global in another (dense slot root: `WALL_SLOT_WIDTH`/`WALL_NORMAL_VELOCITY` macros; blocked slot root: `d_block_wall`), which can change generated arithmetic. | Every owner loads its own module (`compiler.load_module` = `module_from_file`, no cache). A loaded module is never shared between live owners: globals are per module, so sharing would share mutable `d_params`. | | 2. Geometry, layout, tables | flags, curved-link tables, block layouts and halo pairs, transfer (explode/coalesce) tables, block kinds | Immutable for the life of an instance. Uniform grid: rebuilt by body sync. `FixedPatchStepper`: a geometry change is a new instance (`remap_child`), committed only after a successful transfer. | No in-place table edits. | | 3. Controls and dependent boundary columns | Jet amplitude -> per-link `surface_velocity`; body actions (uniform grid) | Between steps, without a table or module rebuild (`set_jet_amplitude`). | Host copy commits only after the device upload succeeded; a device error fails the instance. | | 4. Per-step state | population double buffers and their parity, raw-exchange ledgers, observations | Device-only during `step(n)` / `run(n)`; buffer swaps are host-handle swaps. | Valid on the host only after the single wait at the end of the call. | diff --git a/src/CelerisLab/body/manager.py b/src/CelerisLab/body/manager.py index 819c42b..e1ab25a 100644 --- a/src/CelerisLab/body/manager.py +++ b/src/CelerisLab/body/manager.py @@ -11,7 +11,7 @@ Responsibilities: Design:: Packed ``obs`` layout matches ``generate_config(..., n_objects=count)`` and - ``config_obs.h`` macros. Host sizing uses :func:`~CelerisLab.cuda.compiler_v2.obs_layout`. + ``config_obs.h`` macros. Host sizing uses :func:`~CelerisLab.cuda.compiler.obs_layout`. Pending edits are lightweight: only the "intent" (add / remove) is stored. All derived quantities (flags, compact lists, GPU buffers) are built at @@ -28,7 +28,7 @@ import pycuda.driver as cuda from .objects import SimObject from .registry import BodyRegistry from .preprocess.flag_overlay import merge_flag_masks, validate_cut_link_ownership -from ..cuda.compiler_v2 import obs_layout +from ..cuda.compiler import obs_layout from ..cuda.obs_layout import ObsLayout from ..lbm.descriptors import ( FLUID, SOLID, diff --git a/src/CelerisLab/config.py b/src/CelerisLab/config.py index bff1c45..d5ed576 100644 --- a/src/CelerisLab/config.py +++ b/src/CelerisLab/config.py @@ -7,7 +7,7 @@ Two JSON files: config_body.json – object list LBMConfig / BodyConfig are plain dataclasses; they translate to -macro dicts consumed by `cuda/compiler_v2.py` for header generation. +macro dicts consumed by `cuda/compiler.py` for header generation. """ import json diff --git a/src/CelerisLab/configs/CONFIG.md b/src/CelerisLab/configs/CONFIG.md index 3724e18..57d38d6 100644 --- a/src/CelerisLab/configs/CONFIG.md +++ b/src/CelerisLab/configs/CONFIG.md @@ -60,14 +60,14 @@ Python `config.py` 只负责读取和校验,不是配置位置。显式配置 **`lbm/kernels/config/*.h` 由编译器根据 `LBMConfig` 自动生成,请勿手改。** -仓库中检入的 `config/*.h` 可能是历史版本。`compiler_v2.generate_config` 会复制 kernel 源码到独立临时构建目录,并在副本中生成配置;不会改写包内头文件或 PTX。返回目录须显式传给 `compile_kernel(build_dir=...)`,编译结果与配置摘要均保存在该目录。`Simulation` 负责目录清理;直接使用编译器的调用者须调用 `cleanup_build_dir()`。跳过生成配置直接编译仓库中的 `uniform.cu` 属不受支持用法。 +仓库中检入的 `config/*.h` 可能是历史版本。`compiler.generate_config` 会复制 kernel 源码到独立临时构建目录,并在副本中生成配置;不会改写包内头文件或 PTX。返回目录须显式传给 `compile_kernel(build_dir=...)`,编译结果与配置摘要均保存在该目录。`Simulation` 负责目录清理;直接使用编译器的调用者须调用 `cleanup_build_dir()`。跳过生成配置直接编译仓库中的 `uniform.cu` 属不受支持用法。 ``` config_lbm.json ↓ load_lbm_config() config.py:LBMConfig (读取 + 校验) ↓ to_macros() -cuda/compiler_v2.py (独立构建目录中生成 config/*.h) +cuda/compiler.py (独立构建目录中生成 config/*.h) ↓ config_grid.h → NX, NY, NZ, NT, LATTICE_MODEL; DIM / NQ 由 LATTICE_MODEL 推导写入(与 grid.lattice_model 一致,非 JSON 顶层字段) diff --git a/src/CelerisLab/cuda/__init__.py b/src/CelerisLab/cuda/__init__.py index e494b9f..c399b34 100644 --- a/src/CelerisLab/cuda/__init__.py +++ b/src/CelerisLab/cuda/__init__.py @@ -1,4 +1,4 @@ """CUDA build helpers (compilation itself does not need a live context).""" -from . import compiler_v2 as compiler +from . import compiler __all__ = ["compiler"] diff --git a/src/CelerisLab/cuda/compiler_v2.py b/src/CelerisLab/cuda/compiler.py similarity index 99% rename from src/CelerisLab/cuda/compiler_v2.py rename to src/CelerisLab/cuda/compiler.py index 6916d1d..dcc46ee 100644 --- a/src/CelerisLab/cuda/compiler_v2.py +++ b/src/CelerisLab/cuda/compiler.py @@ -1,4 +1,4 @@ -# CelerisLab/cuda/compiler_v2.py +# CelerisLab/cuda/compiler.py """ Kernel configuration, compilation, and module loading. diff --git a/src/CelerisLab/cuda/obs_layout.py b/src/CelerisLab/cuda/obs_layout.py index 44d2c68..5c798f3 100644 --- a/src/CelerisLab/cuda/obs_layout.py +++ b/src/CelerisLab/cuda/obs_layout.py @@ -2,7 +2,7 @@ """Packed ``obs`` buffer layout for force / torque / sensor telemetry. Usage:: - Host code (e.g. ``ObjectManager``) and ``compiler_v2.generate_config`` must use + Host code (e.g. ``ObjectManager``) and ``compiler.generate_config`` must use the same ``dim`` and ``n_objects`` so allocation sizes match device macros. Design:: diff --git a/src/CelerisLab/lbm/block_stepper.py b/src/CelerisLab/lbm/block_stepper.py index 257256e..d687426 100644 --- a/src/CelerisLab/lbm/block_stepper.py +++ b/src/CelerisLab/lbm/block_stepper.py @@ -17,7 +17,7 @@ from threading import RLock import numpy as np import pycuda.driver as cuda -from ..cuda import compiler_v2 as compiler +from ..cuda import compiler from .blocks import (NQ, BlockLayout2D, blocked_link_tables, launch_curved_boundary, launch_halo_copy, level_config) from .descriptors import D2Q9_EX, D2Q9_EY, FLUID, SOLID diff --git a/src/CelerisLab/lbm/refinement_stepper.py b/src/CelerisLab/lbm/refinement_stepper.py index 8be028a..3c23880 100644 --- a/src/CelerisLab/lbm/refinement_stepper.py +++ b/src/CelerisLab/lbm/refinement_stepper.py @@ -14,7 +14,7 @@ from threading import RLock import numpy as np import pycuda.driver as cuda -from ..cuda import compiler_v2 as compiler +from ..cuda import compiler from .blocks import BlockLayout2D, launch_curved_boundary, launch_halo_copy, level_config from .channel import channel_masks, validate_channel_config, validate_channel_transfers from ..body.jets import jet_link_velocity diff --git a/src/CelerisLab/simulation.py b/src/CelerisLab/simulation.py index 4b47062..6710e86 100644 --- a/src/CelerisLab/simulation.py +++ b/src/CelerisLab/simulation.py @@ -25,7 +25,7 @@ import pycuda.driver as cuda from .config import LBMConfig, BodyConfig, load_lbm_config, load_body_config from .cuda.context import CudaContext, context_bound -from .cuda import compiler_v2 as compiler +from .cuda import compiler from .lbm.field import LBMField from .lbm.stepper import LBMStepper from .body.objects import SimObject diff --git a/tests/integration/test_runtime_contracts_gpu.py b/tests/integration/test_runtime_contracts_gpu.py index 5692c7f..585accc 100644 --- a/tests/integration/test_runtime_contracts_gpu.py +++ b/tests/integration/test_runtime_contracts_gpu.py @@ -6,14 +6,14 @@ import pycuda.driver as cuda import pytest import CelerisLab -from CelerisLab.cuda import compiler_v2 +from CelerisLab.cuda import compiler from ._cases import make_sim def test_source_provenance(): root = Path(__file__).resolve().parents[2] / "src/CelerisLab" assert Path(CelerisLab.__file__).resolve().parent == root - assert Path(compiler_v2.kernel_path("uniform.cu")).resolve() == root / "lbm/kernels/uniform.cu" + assert Path(compiler.kernel_path("uniform.cu")).resolve() == root / "lbm/kernels/uniform.cu" def test_two_contexts_interleaved_and_non_lifo_close(): diff --git a/tests/unit/test_planar_velocity.py b/tests/unit/test_planar_velocity.py index a91cb59..f59303a 100644 --- a/tests/unit/test_planar_velocity.py +++ b/tests/unit/test_planar_velocity.py @@ -9,7 +9,7 @@ import pytest import CelerisLab from CelerisLab.config import LBMConfig, load_lbm_config -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.common.checkpoint import save_checkpoint, load_checkpoint from .test_numerical_operators import compile_run from .test_state_regressions import device, make_field, manager diff --git a/tests/validation/multigrid/test_block_stepper.py b/tests/validation/multigrid/test_block_stepper.py index c03ccad..12939d2 100644 --- a/tests/validation/multigrid/test_block_stepper.py +++ b/tests/validation/multigrid/test_block_stepper.py @@ -21,7 +21,7 @@ import pycuda.driver as cuda from CelerisLab.body.geometry.circle import CircleGeometry from CelerisLab.body.objects import SimObject from CelerisLab.config import LBMConfig -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.lbm import block_stepper as implementation from CelerisLab.lbm.blocks import NO_DONOR, BlockLayout2D from CelerisLab.lbm.block_stepper import PeriodicBlockStepper diff --git a/tests/validation/multigrid/test_channel_root_gpu.py b/tests/validation/multigrid/test_channel_root_gpu.py index fccdbcb..8b0aeb3 100644 --- a/tests/validation/multigrid/test_channel_root_gpu.py +++ b/tests/validation/multigrid/test_channel_root_gpu.py @@ -11,7 +11,7 @@ import numpy as np import pycuda.driver as cuda from CelerisLab.config import LBMConfig -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.cuda.context import CudaContext from CelerisLab.lbm.blocks import BlockLayout2D from CelerisLab.lbm.descriptors import FLUID, SOLID, BC_WALL, BC_INLET, BC_OUTLET diff --git a/tests/validation/multigrid/test_fine_surface_gpu.py b/tests/validation/multigrid/test_fine_surface_gpu.py index 4b961cc..e9f806f 100644 --- a/tests/validation/multigrid/test_fine_surface_gpu.py +++ b/tests/validation/multigrid/test_fine_surface_gpu.py @@ -18,7 +18,7 @@ import CelerisLab from CelerisLab.body.geometry.circle import CircleGeometry from CelerisLab.body.objects import SimObject from CelerisLab.config import LBMConfig -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.cuda.context import CudaContext from CelerisLab.lbm import refinement_stepper as implementation from CelerisLab.lbm.descriptors import FLUID, SOLID diff --git a/tests/validation/multigrid/test_refinement_blocked_base.py b/tests/validation/multigrid/test_refinement_blocked_base.py index 5af68d5..6591c62 100644 --- a/tests/validation/multigrid/test_refinement_blocked_base.py +++ b/tests/validation/multigrid/test_refinement_blocked_base.py @@ -13,7 +13,7 @@ from unittest.mock import Mock, patch import numpy as np import pycuda.driver as cuda -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.lbm import refinement_stepper from CelerisLab.lbm.blocks import BlockLayout2D from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY diff --git a/tests/validation/multigrid/test_refinement_blocked_fine.py b/tests/validation/multigrid/test_refinement_blocked_fine.py index 6a0351f..f7f4e08 100644 --- a/tests/validation/multigrid/test_refinement_blocked_fine.py +++ b/tests/validation/multigrid/test_refinement_blocked_fine.py @@ -15,7 +15,7 @@ import pycuda.driver as cuda from CelerisLab.body.geometry.circle import CircleGeometry from CelerisLab.body.objects import SimObject, SurfaceActuation from CelerisLab.config import LBMConfig -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.lbm import curved_links from CelerisLab.lbm.blocks import NO_DONOR, BlockLayout2D, blocked_link_tables from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY diff --git a/tests/validation/multigrid/test_refinement_gpu.py b/tests/validation/multigrid/test_refinement_gpu.py index 94bd042..bafa6d3 100644 --- a/tests/validation/multigrid/test_refinement_gpu.py +++ b/tests/validation/multigrid/test_refinement_gpu.py @@ -20,7 +20,7 @@ import pycuda.driver as cuda import CelerisLab from CelerisLab.config import LBMConfig -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.cuda.context import CudaContext from CelerisLab.lbm.refinement_stepper import FixedPatchStepper, PeriodicPatchStepper diff --git a/tests/validation/multigrid/test_refinement_jets.py b/tests/validation/multigrid/test_refinement_jets.py index 7ed275b..802bfec 100644 --- a/tests/validation/multigrid/test_refinement_jets.py +++ b/tests/validation/multigrid/test_refinement_jets.py @@ -16,7 +16,7 @@ from unittest.mock import Mock, patch import numpy as np import pycuda.driver as cuda -from CelerisLab.cuda import compiler_v2 as compiler +from CelerisLab.cuda import compiler from CelerisLab.lbm.descriptors import SOLID from CelerisLab.lbm.refinement import ACTIVE, FixedPatch2D, prepare_fine_boundary from CelerisLab.lbm.refinement_stepper import FixedPatchStepper diff --git a/tests/validation/multigrid/test_refinement_three_level.py b/tests/validation/multigrid/test_refinement_three_level.py index c846f61..b612b23 100644 --- a/tests/validation/multigrid/test_refinement_three_level.py +++ b/tests/validation/multigrid/test_refinement_three_level.py @@ -52,7 +52,7 @@ def child_jet(speed=.002): def mocked_child(): - from CelerisLab.cuda import compiler_v2 as compiler + from CelerisLab.cuda import compiler context = Mock(activate=nullcontext, sm_arch="sm_70") module = Mock(get_function=Mock(side_effect=lambda name: Mock(name=name))) module.get_global.return_value = (Mock(), 20) diff --git a/tests/validation/taylor_green/test_projected_pairs.py b/tests/validation/taylor_green/test_projected_pairs.py index 52af16d..64a701d 100644 --- a/tests/validation/taylor_green/test_projected_pairs.py +++ b/tests/validation/taylor_green/test_projected_pairs.py @@ -1,6 +1,6 @@ """Host geometry, receipt-corruption tests and mocked scheduling; no CUDA/flow.""" -from contextlib import nullcontext, redirect_stdout +from contextlib import contextmanager, nullcontext, redirect_stdout from copy import deepcopy from dataclasses import asdict, replace import hashlib @@ -24,6 +24,37 @@ from oracle import LIMITS, TIMES, U, rectangles REPO = Path(__file__).resolve().parents[3] OLD_REVISION = "519473a61c7b319183aa54e55b9f097499dde0bf" MOCK_REVISION = "f" * 40 + + +@contextmanager +def historical_package(): + """Import CelerisLab from NUMERICAL_REVISION, the only tree run_case accepts. + + The harness and its pinned build-capture helper are tooling for that revision + (cuda/compiler_v2.py); the current package is restored afterwards. + """ + import tarfile + archive = subprocess.run(["git", "-C", str(REPO), "archive", "--format=tar", + NUMERICAL_REVISION, "src/CelerisLab"], + capture_output=True, check=True).stdout + before = set(sys.modules) + saved = {k: v for k, v in sys.modules.items() if k == "CelerisLab" or k.startswith("CelerisLab.")} + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp).resolve() + with tarfile.open(fileobj=io.BytesIO(archive)) as tar: + tar.extractall(root) + for name in saved: + del sys.modules[name] + sys.path.insert(0, str(root / "src")) + try: + yield root + finally: + sys.path.remove(str(root / "src")) + for name in set(sys.modules) - before | set(saved): # nothing keeps the old tree + sys.modules.pop(name, None) + sys.modules.update(saved) + + RUNTIME = dict(device=dict(name="host mock", pci_bus_id="mock", capability=[7, 0], driver=12040, runtime=[12, 4, 0]), nvcc_path="/mock/nvcc", nvcc_version="mock nvcc", python="mock Python", numpy=np.__version__, pycuda="mock") @@ -125,6 +156,10 @@ class ProjectedPairsTests(unittest.TestCase): frozen[name]) def test_actual_build_capture_rejects_tampered_ptx_and_sidecar(self): + with historical_package(): + self._check_build_capture() + + def _check_build_capture(self): from CelerisLab.cuda import compiler_v2 from tests.validation.multigrid.check_projected_pairs import capture_builds @@ -346,10 +381,12 @@ class ProjectedPairsTests(unittest.TestCase): self.assertFalse(output.exists()) def test_runtime_rejects_installed_kernel_tree(self): - from CelerisLab.cuda import compiler_v2 - with patch.object(compiler_v2, "_KERNEL_DIR", "/other/CelerisLab/lbm/kernels"): - with self.assertRaisesRegex(RuntimeError, "CUDA source/include root"): - run_case.runtime_source(REPO) + with historical_package() as repo: + from CelerisLab.cuda import compiler_v2 + self.assertEqual(run_case.runtime_source(repo)["checkout"], str(repo)) + with patch.object(compiler_v2, "_KERNEL_DIR", "/other/CelerisLab/lbm/kernels"): + with self.assertRaisesRegex(RuntimeError, "CUDA source/include root"): + run_case.runtime_source(repo) def test_existing_output_is_never_overwritten(self): with tempfile.TemporaryDirectory() as tmp: