Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caeb6a3051 | ||
|
|
7886906517 | ||
|
|
48ee0982c9 | ||
|
|
307468b671 |
+2
-2
@@ -6,8 +6,8 @@ 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 blocked slot/reservoir root reads its slot from the `d_block_wall` constant (the former dense route used `WALL_*` macros; both were bit-identical in the parity gates). | 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_finest`), 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`). | Input (index, level, amplitude) is validated before any device write; invalid input changes nothing and the instance stays usable. The host copy commits only after the upload returned. Any error after validation leaves the device column undefined and fails the instance: `FixedPatchStepper` status `failed`, `Simulation._failed`; every later device-touching public method (`Simulation`: `stream`, `set_jet_amplitude`, `initialize`, `sync_bodies`, `run`/`step`, `read_surface_exchange`, `read_body(s)`, `get_macroscopic`, `get_ddf`, `update_runtime_params`, `snapshot`, `restore`, save/load checkpoint) raises; cached host readers and host-side staging (e.g. `get_flags`, `add_body`/`remove_body` before a sync) do not touch the device and stay callable. `close()` still releases resources and stays retryable. |
|
||||
| 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, except translating circles on the uniform grid: their swept-band flags and cut links are rewritten on the device every advancing step (`MovingCircleCells`/`MovingCircleLinks`, docs/moving_bodies.md); the host flag mirror is then stale for those cells. Uniform grid otherwise: rebuilt by body sync. `FixedPatchStepper`: a geometry change is a new instance (`remap_finest`), 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`). | Input (index, level, amplitude) is validated before any device write; invalid input changes nothing and the instance stays usable. The host copy commits only after the upload returned. Any error after validation leaves the device column undefined and fails the instance: `FixedPatchStepper` status `failed`, `Simulation._failed`; every later device-touching public method (`Simulation`: `stream`, `set_jet_amplitude`, `initialize`, `sync_bodies`, `run`/`step`, `read_surface_exchange`, `read_body(s)`, `get_macroscopic`, `get_ddf`, `update_runtime_params`, `snapshot`, `restore`, save/load checkpoint) raises; cached host readers and host-side staging (e.g. `get_flags` without translating circles, `add_body`/`remove_body` before a sync) do not touch the device and stay callable; with translating circles `get_flags` reads the device flags back and refuses after a failure. Translating-circle targets follow the same rule: `set_body` validates before mutation, the applied offset commits only after a successful synchronized step, and a failed moving step fails the instance (docs/moving_bodies.md). `close()` still releases resources and stays retryable. |
|
||||
| 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. |
|
||||
|
||||
Storage routes are separate concerns:
|
||||
|
||||
+56
-12
@@ -1,17 +1,61 @@
|
||||
# Moving circles on the uniform dense grid — S0 DRAFT contract
|
||||
# Moving circles on the uniform dense grid
|
||||
|
||||
Status: **DRAFT, not frozen** for the force RHS, kernels and physical
|
||||
acceptance. Host-only S1 (state, validation, guards; no kernels) is on branch
|
||||
`claude/moving-bodies-s1`; positive-step runs with a moving body are rejected
|
||||
until S2. When S2 lifts that guard, `run(n > 1)` with a pending target must
|
||||
raise `ValueError` before mutation (4a), and `run(0)` must not upload a motion
|
||||
record or consume the pending target. Base: main
|
||||
`b2034f87e03656c5f67ab88562a70d6c338b9918`. Scope approved by Frank (via TRON,
|
||||
thread C0C78UFKGRM/1793335557.460509): uniform-grid prescribed translation and
|
||||
single-cylinder free transverse VIV. Out of scope: refinement/blocked path,
|
||||
pinball assembly, EsoPull, shifted storage, D3Q19, DynamisLab, manuscript.
|
||||
## Status and user guide (current)
|
||||
|
||||
Items marked **OPEN** are undecided; items marked **CANDIDATE** are proposals.
|
||||
Development-stage feature, integrated from the reviewed slices S1–S3a
|
||||
(production code identical to `653fd997`). Physical evidence and its limits:
|
||||
`docs/validation/moving_bodies_stage1.md`. The force used as a structural RHS
|
||||
is a declared stage choice, not an established result.
|
||||
|
||||
*What it does.* Translating circles (with optional spin) on the dense
|
||||
double-buffer D2Q9 path: per-step cut links from an FP64 geometry record
|
||||
(M2 `MovingCircleLinks`), reclassification and refill of covered/uncovered
|
||||
cells (M1 `MovingCircleCells`), moving-wall Bouzidi with wall velocity
|
||||
`u + omega x r`, and separate per-body diagnostics.
|
||||
|
||||
*API.*
|
||||
- `Simulation.add_body("circle", center=(x, y), radius=r,
|
||||
translation=Translation(offset_box, max_speed))` before `initialize()`;
|
||||
`offset_box = (x_min, x_max, y_min, y_max)` in lattice cells around the
|
||||
base centre (must contain 0), `0 < max_speed ≤ .5`.
|
||||
- `set_body(id, offset=(dx, dy), vx=..., vy=..., omega=...)` sets a pending
|
||||
target (validated against the applied offset: box, Euclidean step ≤ .5
|
||||
cell, speed, no sliding hold); `vx`/`vy` are the interval wall velocity.
|
||||
- `run(1)` advances a pending target and commits it only after the step
|
||||
synchronizes with `no_donor == 0`; `run(n > 1)` with a pending target
|
||||
raises; without a target `run(n)` holds the geometry (spin allowed).
|
||||
Direct `LBMStepper.step` refuses while a target is pending.
|
||||
- `moving_diagnostics()` returns per body the columns of
|
||||
`MovingTables.DIAG` (CME, Wen term, link mass, cover/uncover momentum and
|
||||
mass, eq_only, no_donor, crossings) accumulated since obs were zeroed;
|
||||
`obs`/`read_force` keep their contract (CME on the body).
|
||||
- `get_flags()` reads device flags back when moving circles exist.
|
||||
|
||||
*Supported:* dense double buffer, D2Q9, FP32 unshifted, SRT/MRT, circles,
|
||||
spin. *Rejected before mutation:* EsoPull, ddf shifting, D3Q19, refinement /
|
||||
blocked steppers, Jets or surface actuation on a moving circle,
|
||||
`sync_bodies` and checkpoints while a moving circle exists, swept bands that
|
||||
touch the domain edge, other bodies, sensors or force regions.
|
||||
|
||||
*Failure lifecycle.* Device-side failures (upload, step, sync, no_donor)
|
||||
mark the Simulation failed; later run/step/sync/checkpoint calls and direct
|
||||
stepper calls refuse until `close()`.
|
||||
|
||||
*Known limitations.* No mass correction at curved walls (static or moving);
|
||||
cover/uncover and Wen terms are diagnostics, not a selected force law; the
|
||||
host loop costs a fixed per-step synchronization (≈ 1 ms/step measured on a
|
||||
7.2 M-cell grid, run(1) class); outer boundaries are the single dense
|
||||
channel package (see `configs/CONFIG.md`).
|
||||
|
||||
## Design record (staged contract, kept as written)
|
||||
|
||||
The sections below are the contract the slices were reviewed against
|
||||
(S0 → S3a). They are kept unchanged for provenance; where they say "draft",
|
||||
"proposed" or "until S2/S3", the status above supersedes them.
|
||||
|
||||
Original scope (Frank via TRON, thread C0C78UFKGRM/1793335557.460509):
|
||||
uniform-grid prescribed translation and single-cylinder free transverse VIV;
|
||||
base main `b2034f87e03656c5f67ab88562a70d6c338b9918`.
|
||||
|
||||
## 1. Working standards
|
||||
|
||||
|
||||
@@ -82,6 +82,13 @@ Tests: `tests/unit/test_jets.py`, `test_jets_review.py`, `test_shifted_jets.py`,
|
||||
- DFG 2D-1 Re 20 (V4, SRT, t* 60, pressure-NEQ outlet and local Zou-He inlet): refined and uniform
|
||||
runs were NOT_STEADY under the frozen steady rule, so no interval verdict was given.
|
||||
|
||||
## Moving circles (dense `Simulation`, uniform grid only)
|
||||
|
||||
Translating circles with optional spin are supported on the dense double
|
||||
buffer D2Q9 FP32 path only (not on `FixedPatchStepper` or refined levels).
|
||||
API, supported/rejected modes and limits: `docs/moving_bodies.md`; stage-1
|
||||
evidence: `docs/validation/moving_bodies_stage1.md`.
|
||||
|
||||
## Not supported in this release
|
||||
|
||||
3D, dynamic/automatic AMR (tagging, time-varying hierarchies beyond `remap_finest`),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Moving circles — stage-1 evidence (development level)
|
||||
|
||||
Owner criterion (Yanqi via TRON): prescribed motion and one free VIV case both
|
||||
"reasonably close" to a predeclared reference = development-stage success.
|
||||
This is practical closeness, not a formal validation claim. Every number
|
||||
below is from Astra's receipts as posted in Slack C0C78UFKGRM thread
|
||||
1793335557.460509; raw outputs stay on Horsea under `/tmp/celeris-results-*`
|
||||
and the Mac evidence directory (not in git).
|
||||
|
||||
## Code
|
||||
|
||||
- Production: `src/` identical to `653fd997` (slices S1 `983020b6`,
|
||||
S2a `b920a303`, S3a `653fd997`). CPU suites and GPU hold/crossing gates
|
||||
passed on those slices (G0a/G0b population-bitwise; X1 crossing ledger).
|
||||
- Research drivers: `research/duetsch_re100_kc5/` (`dd37883d`, capture
|
||||
`a0aea3b9`) and `research/viv_re150/` (`fb507aa1`), run as committed.
|
||||
|
||||
## Prescribed in-line oscillation (Dütsch et al. 1998, Re 100, KC 5)
|
||||
|
||||
- Protocol: `docs/validation/duetsch_re100_kc5_protocol.md`.
|
||||
- Frozen settling gate (.002 F_ref waveform): **FAILED** and kept on record:
|
||||
D40 domain stage (20/40/60 D, 11.25 T) and the 60 D duration diagnostic
|
||||
(31.25 T, 0/28 pairs; last waveform drift .00613 F_ref).
|
||||
- Unscored late-cycle CME fits c_d ≈ 2.02–2.05, c_i ≈ 1.46–1.48 vs the
|
||||
paper's computed (2.09, 1.45); fields clean and symmetric (owner view).
|
||||
The owner accepts ~0.61 % cycle drift at D40 for this stage; the gate is
|
||||
not re-scored.
|
||||
- Fig. 8 LDA profiles: qualitative only (no digitized ordinates).
|
||||
|
||||
## Free transverse VIV (Kolahdouz §4.1.1 setup, Qin §4.5 target)
|
||||
|
||||
- Predeclaration: `docs/validation/viv_re150_predeclare.md` (agreed at
|
||||
`1abfe954` under its former name `leontini_viv_predeclare.md`); run at `fb507aa1`, single case, D = 40, 75 D × 60 D.
|
||||
- Result: status `scored`, 336,384 steps (t U/D = 336.4); A_max = .5464 D vs
|
||||
.520 D (+5.1 %, inside the declared ±10 % band); mean half peak-to-peak
|
||||
.5448 D; f/f_n = .955; block drifts .78/.29/.18 %, last-five spreads
|
||||
.15/.11/.10 %; finite, no_donor = 0, no seed; 337.7 s, ~1.0 ms/step.
|
||||
|
||||
## Limitations (binding)
|
||||
|
||||
- One resolution (D40) per case; no grid or time convergence.
|
||||
- Structural RHS = CME + Wen is a theory-motivated stage choice; CME-only
|
||||
and node-impulse variants were not run as RHS; no force law is validated.
|
||||
- Boundary mismatches: NEQ outlet with backflow clamp vs traction outlet;
|
||||
finite box; the strict Dütsch settling failure is unexplained.
|
||||
- No mass correction at curved walls; ~1 ms/step host-coupled overhead.
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
# Single-cylinder free transverse VIV — PREDECLARATION (agreed at 1abfe954)
|
||||
# Single-cylinder free transverse VIV, Re 150 (Kolahdouz/Qin) — PREDECLARATION (agreed at 1abfe954)
|
||||
|
||||
(File renamed from `leontini_viv_predeclare.md`; the Leontini Re 200 case is only the queued second case below.)
|
||||
|
||||
Stage goal (Yanqi via TRON): prescribed Dütsch case + one VIV case both
|
||||
"reasonably close" to a predeclared reference = development-stage success, then
|
||||
@@ -0,0 +1,23 @@
|
||||
# Research VIV driver — coupling contract (not production)
|
||||
|
||||
`run.py` couples a 1-DOF undamped spring–mass (`viv.py`) to the production
|
||||
moving-circle API. Everything specific to fluid–structure coupling lives here,
|
||||
not in `src/`.
|
||||
|
||||
- Per step: known (y_n, v_n) and the last completed force F_last → exact
|
||||
harmonic candidate (y_{n+1}, v_{n+1}) → `set_body(offset=(0, y_{n+1}),
|
||||
vy=y_{n+1} − y_n)` (interval wall velocity, lattice dt = 1) → `run(1)` →
|
||||
commit y, v and F_last only after the step succeeded. One step of explicit
|
||||
force lag (weak coupling); no sub-iteration.
|
||||
- Structural RHS = CME + Wen (y), read from `moving_diagnostics()` by the
|
||||
names in `MovingTables.DIAG`; raw CME, the `obs` force and node terms are
|
||||
logged as diagnostics. This is a stage choice, not a validated force law.
|
||||
- Production guarantees used: fail-closed target commit (synchronized step,
|
||||
`no_donor == 0`), input rejection before mutation, a failed device step
|
||||
marks the Simulation failed.
|
||||
- Not provided, by design: device-resident structure state or batched
|
||||
(multi-step) coupling, strong/sub-iterated coupling (would need state
|
||||
rollback), more than one DOF. These are future work and would be a separate
|
||||
execution path, not an abstraction over this driver.
|
||||
- Recorded result and limits: `docs/validation/moving_bodies_stage1.md`;
|
||||
predeclaration: `docs/validation/viv_re150_predeclare.md`.
|
||||
@@ -1,4 +1,4 @@
|
||||
"""One-shot predeclared VIV run (docs/validation/leontini_viv_predeclare.md).
|
||||
"""One-shot predeclared VIV run (docs/validation/viv_re150_predeclare.md).
|
||||
|
||||
Re 150, U* = 5, m/(rho D^2) = 2, zeta = 0, D = 40 on a 75 D x 60 D native
|
||||
channel (uniform inlet, NEQ outlet, free-slip y walls), cylinder at
|
||||
@@ -65,7 +65,8 @@ def main():
|
||||
step_cap = args.steps
|
||||
centre = (30.0 * Dn, 0.5 * (ny - 1))
|
||||
|
||||
from CelerisLab.body.motion import Translation
|
||||
from CelerisLab.body.motion import MovingTables, Translation
|
||||
col = {k: MovingTables.DIAG.index(k) for k in ("cme_y", "wen_y", "cover_jy", "uncover_jy", "no_donor")}
|
||||
from CelerisLab.simulation import Simulation
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as fh:
|
||||
json.dump(cfg, fh)
|
||||
@@ -95,9 +96,9 @@ def main():
|
||||
sim.run(1) # device failures raise and mark the Simulation failed
|
||||
d = sim.moving_diagnostics()[0]
|
||||
f_obs = sim.read_force(body)[1]
|
||||
f_rhs = float(d[1] + d[3]) # CME + Wen (y)
|
||||
row = (n, y1, v1, f_rhs, float(d[1]), float(d[3]), float(f_obs),
|
||||
float(d[7] - d[10]), float(d[13]))
|
||||
f_rhs = float(d[col["cme_y"]] + d[col["wen_y"]]) # CME + Wen (y)
|
||||
row = (n, y1, v1, f_rhs, float(d[col["cme_y"]]), float(d[col["wen_y"]]), float(f_obs),
|
||||
float(d[col["cover_jy"]] - d[col["uncover_jy"]]), float(d[col["no_donor"]]))
|
||||
if not viv.finite(row): # every recorded output, not only the RHS
|
||||
status = "fail_nonfinite_output"
|
||||
break
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Pure logic of the predeclared Re 150 free transverse VIV case (stdlib only).
|
||||
|
||||
Contract: docs/validation/leontini_viv_predeclare.md. Oscillator update with a
|
||||
Contract: docs/validation/viv_re150_predeclare.md. Oscillator update with a
|
||||
constant force over one lattice step, response-cycle tracking with fixed y = 0
|
||||
crossings, eligibility, 10-cycle blocks, settling/scoring with deterministic
|
||||
restart, and the conditional seed.
|
||||
|
||||
@@ -86,6 +86,20 @@ step/one_step_*.cu → kernel 编排
|
||||
- 转速 `omega` 属于运行时状态,可在步进期间更新;该更新不改变 `N_OBJS` 或 `OBS_*` 内存布局,因此**不需要**因 `omega` 变化触发重新编译。
|
||||
- 力矩观测从 `obs` 的 torque segment 读取;force / torque / sensor 段统一由 `config_obs.h` 宏描述。
|
||||
|
||||
### 外边界能力(Outer boundaries, dense `Simulation`)
|
||||
|
||||
The dense path supports one channel package, not four independently
|
||||
selectable sides:
|
||||
- left x = 0: velocity inlet (`method.inlet.scheme`, `profile`, `physics.velocity`);
|
||||
- right x = nx−1: outlet (`method.outlet.mode`; `neq_extrap` holds rho = 1 with
|
||||
a `max(u, 0)` backflow clamp when `backflow_clamp` is set);
|
||||
- bottom and top: one shared `y_wall_bc` (`bounce_back` halfway or
|
||||
`free_slip`); corners are walls.
|
||||
`y_wall_bc = "on_site_velocity"` switches to x-periodic with on-site y planes
|
||||
and requires an object-free domain. There is no x/y-periodic or symmetry
|
||||
option for domains with bodies; adding outer BC types is a separate design
|
||||
question, not covered by these keys.
|
||||
|
||||
### 稳定性默认窗口(Stability defaults)
|
||||
|
||||
- 默认 `method.omega_guard = {min: 0.01, max: 1.99}`,用于约束碰撞频率与 LES 有效粘度路径。
|
||||
|
||||
@@ -28,7 +28,7 @@ from .cuda.context import CudaContext, context_bound
|
||||
from .cuda import compiler
|
||||
from .lbm.descriptors import FLUID
|
||||
|
||||
# Translating circles step only through their moving tables (fixed centre in S2a).
|
||||
# Translating circles step only through their moving tables (attached at initialize).
|
||||
PENDING_BLOCKED = "a pending translation target is advanced only by Simulation.run(1)"
|
||||
MOVING_BLOCKED = ("translating circles need the moving kernels and their tables (attached "
|
||||
"at initialize); "
|
||||
@@ -122,8 +122,9 @@ class Simulation:
|
||||
D2Q9/FP32/unshifted/DB only; set before initialization or stage
|
||||
a new body. No runtime motion or amplitude setter.
|
||||
translation: ``body.motion.Translation`` declaring a translating
|
||||
circle (before initialize only; docs/moving_bodies.md). Until
|
||||
the moving kernels exist, positive-step runs are rejected.
|
||||
circle (before initialize only; docs/moving_bodies.md).
|
||||
Positive-step runs are rejected until ``initialize()`` has
|
||||
attached its moving tables.
|
||||
**kwargs: passed to the geometry constructor.
|
||||
|
||||
Returns:
|
||||
@@ -360,8 +361,11 @@ class Simulation:
|
||||
Args:
|
||||
id: body_id from add_body().
|
||||
omega: rotation speed (lattice units). ``None`` = no change.
|
||||
vx: translation velocity x (future). ``None`` = no change.
|
||||
vy: translation velocity y (future). ``None`` = no change.
|
||||
vx: translation velocity x. For a translating circle: the
|
||||
interval wall velocity of the target, only together with
|
||||
``offset``; otherwise stored in the body state (not applied
|
||||
to stationary walls). ``None`` = no change.
|
||||
vy: translation velocity y; same rules as ``vx``.
|
||||
offset: target centre offset of a translating circle; with vx/vy
|
||||
(default 0) it replaces the pending target after validation
|
||||
against the applied offset (docs/moving_bodies.md, 4a). Only
|
||||
|
||||
@@ -11,6 +11,20 @@ _spec = importlib.util.spec_from_file_location(
|
||||
p = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(p)
|
||||
|
||||
def _load(rel, name):
|
||||
"""Load a research driver module under a unique name (no bare ``import run``)."""
|
||||
import sys
|
||||
path = Path(__file__).resolve().parents[2] / rel
|
||||
sys.path.insert(0, str(path.parent)) # for the driver's own sibling imports
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
finally:
|
||||
sys.path.remove(str(path.parent))
|
||||
return mod
|
||||
|
||||
|
||||
T = 2520
|
||||
|
||||
|
||||
@@ -93,12 +107,7 @@ def test_exact_point_inside_body_is_missing_even_with_fluid_corners():
|
||||
|
||||
def test_generated_config_passes_the_real_loader(tmp_path):
|
||||
import json
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "research/duetsch_re100_kc5"))
|
||||
try:
|
||||
import run
|
||||
finally:
|
||||
sys.path.pop(0)
|
||||
run = _load("research/duetsch_re100_kc5/run.py", "duetsch_run")
|
||||
from CelerisLab.config import load_lbm_config
|
||||
cfg_dict, u_max, n = run.config(20, 10.0, 2520)
|
||||
path = tmp_path / "config_lbm.json"
|
||||
@@ -111,13 +120,7 @@ def test_generated_config_passes_the_real_loader(tmp_path):
|
||||
|
||||
|
||||
def _analyze():
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "research/duetsch_re100_kc5"))
|
||||
try:
|
||||
import analyze
|
||||
finally:
|
||||
sys.path.pop(0)
|
||||
return analyze
|
||||
return _load("research/duetsch_re100_kc5/analyze.py", "duetsch_analyze")
|
||||
|
||||
|
||||
def test_hf_energy_is_parseval_weighted():
|
||||
|
||||
@@ -8,6 +8,20 @@ _spec = importlib.util.spec_from_file_location(
|
||||
viv = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(viv)
|
||||
|
||||
def _load(rel, name):
|
||||
"""Load a research driver module under a unique name (no bare ``import run``)."""
|
||||
import sys
|
||||
path = Path(__file__).resolve().parents[2] / rel
|
||||
sys.path.insert(0, str(path.parent)) # for the driver's own sibling imports
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
finally:
|
||||
sys.path.remove(str(path.parent))
|
||||
return mod
|
||||
|
||||
|
||||
D, U = 40, 0.04
|
||||
|
||||
|
||||
@@ -72,12 +86,7 @@ def test_seed_reset_discards_incomplete_cycle_and_proof():
|
||||
|
||||
def test_viv_config_passes_the_real_loader(tmp_path):
|
||||
import json
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "research/viv_re150"))
|
||||
try:
|
||||
import run
|
||||
finally:
|
||||
sys.path.pop(0)
|
||||
run = _load("research/viv_re150/run.py", "viv_run")
|
||||
from CelerisLab.config import load_lbm_config
|
||||
cfg_dict, nx, ny = run.config()
|
||||
path = tmp_path / "config_lbm.json"
|
||||
|
||||
Reference in New Issue
Block a user