test: integrate pinned Re100 and jet control-volume validation
This commit is contained in:
@@ -663,6 +663,13 @@ for step in range(steps):
|
||||
streak.render("streakline.png")
|
||||
```
|
||||
|
||||
## Validation campaigns
|
||||
|
||||
- [DFG 2D-2 Re100](docs/validation/dfg_re100.md): frozen periodicity and official-reference comparisons, with a declared noncanonical outlet.
|
||||
- [Jet-circle control volume](docs/validation/jet_circle_cv.md): bounded AMR versus uniform-grid conservation diagnostic.
|
||||
|
||||
The runners are in `tests/validation`, their CPU tests in `tests/unit`, and compact reference/source guards in `tests/validation/data`. These campaigns preserve pinned production code; successful CPU checks alone do not establish GPU or physical validation. Keep generated results outside tracked files.
|
||||
|
||||
## Citation
|
||||
|
||||
```bibtex
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# DFG 2D-2 Re100 GPU campaign (validation-only)
|
||||
|
||||
## Scope and locked definitions
|
||||
|
||||
Unchanged d79e3f6 production sources. Uniform D2Q9/MRT, shifted FP32, double buffer, no LES, stationary cylinder and no-slip walls. This is not an AMR, jet, or full canonical-outlet certification.
|
||||
|
||||
Physical geometry: [0,2.2] × [0,0.41], center (0.2,0.2), radius .05, rho=1, nu=.001, inlet mean=1 and peak=1.5. D is lattice diameter; dx=.1/D, dt=U_lattice*dx, nu_lattice=U_lattice*D/100. U_lattice=.01 gives peak inlet Mach .02598. Existing local Zou–He inlet is selected in validation config only. Existing pressure-NEQ outlet is unclamped: **it does not impose the canonical vector do-nothing condition nu*d_n(u)-p*n=0**. The existing production channel-flow initial condition is used, not zero velocity. Periodic eligibility is delayed to t*=250 (25 physical seconds), with fixed end t*=300 (30 seconds).
|
||||
|
||||
Official source: https://wwwold.mathematik.tu-dortmund.de/~featflow/en/benchmarks/cfdbenchmarking/flow/dfg_benchmark2_re100.html
|
||||
|
||||
The 20141124 official newer reference archive has higher-order line-integration force data and supersedes the older example table for this comparison. The compact tests/validation/data/dfg_re100/reference_cycle.json preserves the last complete lift-minimum cycle from lv6/dt4. Reference Cd=[3.164266,3.227392], Cl=[-1.021286,.986571], pressure difference=[2.420371,2.507013], St=.301841. These are derived sampled extrema, not exact analytical values. Official mean=(min+max)/2 and amplitude=max-min; separate time averages are also recorded.
|
||||
|
||||
## Exact commands and startup ownership
|
||||
|
||||
Use the existing CUDA/PyCUDA environment; no package installation or production edits required. From the repository root, use a new task-owned output path:
|
||||
|
||||
export PYTHONPATH="$PWD/src${PYTHONPATH:+:$PYTHONPATH}"
|
||||
python -m pytest -q tests/unit/test_re100.py
|
||||
CUDA_VISIBLE_DEVICES=0 python tests/validation/run_dfg_re100.py --diameter 20 --speed .01 --mode pilot --output output/validation/pilot_d20_u001 > pilot.log 2>&1
|
||||
|
||||
Pilot is t*=5 (10,000 steps), with dense .02-t* sampling to calibrate diagnostic cost. It cannot pass periodicity or external benchmark validation. Root launch owner reviews health, memory, timing and stdout before full runs; the full pair is not launched automatically.
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 python tests/validation/run_dfg_re100.py --diameter 40 --speed .01 --mode full --output output/validation/full_d40_u001 > d40.log 2>&1
|
||||
CUDA_VISIBLE_DEVICES=1 python tests/validation/run_dfg_re100.py --diameter 80 --speed .01 --mode full --output output/validation/full_d80_u001 > d80.log 2>&1
|
||||
|
||||
These are separate invocations, not a parallel shell script. A single root launch owner serializes startup/compilation across all Horsea work. Do not launch the next process until the previous log contains an explicit `READY {...}` line. READY comes after the first physical sample, including Simulation initialize, sim.run, read_force and get_macroscopic, so lazy runtime/diagnostic paths are exercised. That sample remains in the physical trajectory/history. No hidden warmup/reset, no later new diagnostic kernel. Do not treat a process merely existing as READY. Keep separate output directories and retain exit codes/logs. GPU assignment is chosen by root after checking existing workloads; shown indices are examples of physical device selection.
|
||||
|
||||
## Measurement, frozen criteria and outcomes
|
||||
|
||||
Force uses advancing link-momentum exchange, accumulated in batches <=100 steps, combined in FP64; Cd/Cl=2F/(U_lattice² D). Force timestamp is the batch midpoint. Pressure uses fluid-only h/2h extrapolation to cylinder front/back points and physical conversion 1/(3 U_lattice²), at each sample endpoint. For phase comparisons endpoint pressure is interpolated to the same cycle phase. Both raw times are saved.
|
||||
|
||||
Before t*=240 sample width=.5; afterwards .02 (about 166 samples per lift cycle). Pilot uses dense sampling throughout. Extract complete lift-minimum-to-minimum cycles using quadratic local minimum locations, accepting periods 2–5 t*. Latest five complete cycles must be consecutive with the last ending no more than 5 t* before the final observation, and must start at or after t*=250. Eligibility requires lift amplitude >=.2, period range/mean <=.005, Cd/Cl/pressure min and max ranges <=.005 absolute, cycle-mean mass range/mean <=1e-4, and each cycle's mean mass-flux imbalance <=.005. Instantaneous flux equality is not required in this unsteady compressible LBM diagnostic.
|
||||
|
||||
Reference gates: Cd extrema relative errors <=2%, Cl extrema absolute errors <=.03, pressure extrema relative errors <=2%, St relative error <=2%. These are predeclared engineering tolerances, not official uncertainty intervals. Phase-aligned waveform RMS errors, extrema, midpoint means, time means, amplitudes, outlet residual, sampled density bounds and Mach are all reported. Waveform RMS is diagnostic (no retroactive gate). No threshold changes after GPU results. At cap, failed periodicity stays incomplete. Invalid/nonfinite state, rho outside [.5,1.5], or sampled max Mach>.2 causes a safety stop; this is a gross-health guard, not a Mach-independence claim.
|
||||
|
||||
A case_reference_pass requires both periodicity and reference gates. Every raw case keeps suite_validated=false. The two-grid comparison must report each case and errors; matched-Mach D40/D80 is evidence of resolution sensitivity only, not an asymptotic convergence order. If either fails or finer errors increase materially, report the failure instead of adjusting thresholds. No full canonical/AMR/Mach-independent validation claim is allowed.
|
||||
|
||||
## Resource and calibration plan
|
||||
|
||||
D20: 441×84=37,044 nodes, pilot 10,000 steps. D40: 881×166=146,246 nodes, 1,200,000 full steps. D80: 1761×330=581,130 nodes, 2,400,000 full steps. Main double-buffer DDF arrays alone cost 72 bytes/node: 2.7/10.5/41.8 MB decimal. Flags, masks, macro host arrays, body buffers and CUDA context/JIT memory add overhead; root verifies actual free GPU/host memory and disk before launch. Conservative per-process reservation: 1 GiB VRAM and 2 GiB host RAM, excluding shared/compiler overhead; measurements take precedence.
|
||||
|
||||
Historical Re20 D80 U=.01 step throughput was ~6082 MLUPS (45.9 seconds/480k steps). At equal throughput D80 Re100 full advancing steps would take ~229 seconds, excluding dense full-field downloads/reduction and compile overhead. D40 estimate must be calibrated, not assumed proportional. Pilot records startup_seconds, step_seconds, measurement_seconds, mlups. For each full grid, calibrate a same-grid pilot if D20 cannot predict cost adequately. Full has about 3480 records (480 coarse+3000 dense); use measured seconds/step × full steps plus seconds/record ×3480 plus startup. D20-to-D80 extrapolation is only a rough planning upper/lower guide, not an ETA commitment.
|
||||
|
||||
Only geometry.npz, one final_field.npz, history.csv, summary.json and stdout/stderr are persisted. No per-step or repeated full-field snapshots. D80 final float64 rho/ux/uy plus mask are <15 MB uncompressed; history/summary several MB, compact versioned reference/source guards <50 KiB. Budget <100 MB per case and <500 MB whole campaign including logs and optional externally retained archives, safely below 2 GiB; root still checks baseline Horsea usage. Retain sparse evidence only.
|
||||
|
||||
## CPU validation and remaining execution
|
||||
|
||||
Ten tests cover geometry/units/config, pressure scaling, periodic and nonperiodic synthetic histories, immutable production hashes, and self-reference comparison. CUDA execution is not available in the preparation environment and has not been claimed. Run pilot first on Horsea; do not confuse successful CPU tests with GPU readiness.
|
||||
|
||||
## Reference provenance and reproduction
|
||||
|
||||
Only the compact derived cycle is versioned. Its source URL, official ZIP SHA-256, column selection, and cycle-selection description are embedded in reference_cycle.json. Raw archives and GPU results are not repository inputs. To reproduce it, obtain the exact archive identified there, verify its SHA-256, and extract logs/results_cc2d_bench2_referencenew/bdforces_q2_lv6_dt4 and pointvalues_q2_lv6_dt4. Use physical time column 2 multiplied by 10, drag/lift columns 4/5, and pressure columns 7 minus 12 (one-based columns). Build rows with mass=flux_in=flux_out=1, then call periodic.extract(rows, min_time=0) and select the last complete cycle. The source guard is tests/validation/data/dfg_re100/source_manifest.json; it deliberately verifies unchanged d79 production content rather than the validation branch HEAD.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Jet-circle closed-control-volume pilot
|
||||
|
||||
Status: CPU preparation only. No GPU run or production change. Pinned unchanged production source: d79e3f6f0a424f5bcc3a1a6d610313e49daf97a2. tests/validation/data/jet_circle_cv/source_manifest.json checks all tracked source file hashes before importing GPU code.
|
||||
|
||||
## Execute only after root-owned GPU serialization
|
||||
|
||||
From the repository root, with REPO set to the repository root and an absent output directory:
|
||||
|
||||
```sh
|
||||
PYTHONPATH=src python -m pytest -q tests/unit/test_jet_circle_cv.py
|
||||
python tests/validation/run_jet_circle_cv.py --repo "$REPO" --output "$OUTPUT" --plan
|
||||
CUDA_VISIBLE_DEVICES="$VERIFIED_IDLE_GPU" python tests/validation/run_jet_circle_cv.py --repo "$REPO" --output "$OUTPUT"
|
||||
```
|
||||
|
||||
The runner does not select, reserve, or contact a GPU machine. It requires one explicit CUDA_VISIBLE_DEVICES value, at least 1 GiB free, and refuses an existing output directory. It imports existing PyCUDA; no installation. Default pilot: 800 base steps, 8 output windows, at most 1800 seconds after READY. READY is printed only after both steppers compile, execute one base/two fine warmup hops, exercise download/raw paths, and reset initial state/time. A pre-READY silence may be compilation, not stepping progress. Root owns any overall startup timeout and all launch serialization.
|
||||
|
||||
## Physical/numerical case
|
||||
|
||||
Periodic root box 192 by 128, center (96,64) in root cell-edge coordinates, D=24.084, U0=.03, nu=.0072252, Re_D=100, beta=Vj/U0=.1, rho0=1. Unforced D2Q9 SRT FP32, no DDF shifting, no LES. The slightly noninteger diameter was selected solely to avoid q clipping at all three planned scales. Circle is stationary. Two uniform radial sectors at pi/2 and 3pi/2, half-width pi/12: top blows outward +.003, bottom sucks inward -.003; remaining wall has zero velocity. This periodic transient is NOT DFG, infinite-stream drag, or statistical high-Re validation. End time is t*=t U0/D approximately .9965.
|
||||
|
||||
AMR bounds (48,32,144,96). Finest dx=dt=.5 in root lattice units, nu_fine=2 nu, finest D=48.168. Full-domain fine reference is 384 by 256 with exactly the same finest spacing, timestep, viscosity, collision and storage. Root tau=.5216756, fine tau=.5433512, Mach approximately .05196.
|
||||
|
||||
AMR uses actual public FixedPatchStepper Jet construction and raw observation. The uniform blocked reference reuses the existing test_raw_exchange_gpu.py optional-input validation adapter: production Jet-derived velocity/force-arm arrays feed the unchanged blocked curved kernel. Its normal public PeriodicBlockStepper API is passive-only. This does not claim there is no uniform Jet route anywhere: Simulation.add_jet exists, but the same-blocked-kernel adapter removes a backend/storage confound for this comparison. No new public backend is introduced. Runtime asserts matching directions, q, fallback and true hit coordinates after frame conversion; both sectors must resolve links. Unclamped q/no fallback is required.
|
||||
|
||||
## Independent measurements and signs
|
||||
|
||||
Two closed rectangles: (72,44,120,84) and (64,40,128,88), wholly within the finest patch. Every contour face has two active-fluid stencil layers. Capture physical postcollision populations at t=0 and every completed base interval. Collision conserves density/momentum; these are valid endpoint moments, NOT precollision stress estimates.
|
||||
|
||||
For each contour, M and P sum fluid-cell rho and rho*u times h^2, excluding the staircase solid. Face midpoint quadrature integrates rho*u dot n and [rho*u*u+p*I-mu*(grad u+grad u.T)] dot n. Velocity gradients use centered differences in root-length units, mu=rho*nu, p=(rho-1)/3. Constant pressure subtraction integrates to zero on a closed rectangle. No raw exchange enters these field quantities.
|
||||
|
||||
Trapezoidal time integration samples every base step over the SAME reported [step_start,step_end] as cumulative raw impulses/masses. For each window:
|
||||
|
||||
- mass into fluid through body surface = delta M + integral outer mass flux
|
||||
- raw boundary reaction target = -delta P - integral outer momentum-minus-traction flux
|
||||
|
||||
Raw rows are [Jx,Jy,angular impulse,mass into fluid]; each uniform fine hop scales by [.25,.25,.125,.25], exactly as the AMR interval ledger. Both fine hops are accumulated. AMR observation time endpoints and the public raw reduction are checked. Reports separate total, wall, aperture, blowing and suction raw rows. Coefficients divide force by .5*rho0*U0^2*D. These are momentum reactions including aperture transport; neither raw aperture nor total raw reaction is labelled solid-only hydrodynamic traction.
|
||||
|
||||
Independent aperture mass estimates integrate measured rho*u dot e_r over true arcs using 64-point Gauss quadrature and nearest active-fluid samples. No target velocity or raw row enters this estimate. Reports include maximum sampling distance, actual signed raw mass rates and nominal signed volume flux ±2*(pi/12)*R*Vj. Near-wall nearest-node sampling is only a first-order diagnostic, not certified wall extrapolation. Nominal volume flux is not asserted to equal measured mass flow at finite density variation.
|
||||
|
||||
Inventory near the circle is a staircase-cell approximation; its geometric error and face/time quadrature errors can appear in CV residuals. Two-contour spread and grid refinement are essential. No exact continuum force value, solid traction recovery, jet hardware load, or physics acceptance is claimed. Output status is deliberately PILOT_DIAGNOSTIC_ONLY even on successful execution.
|
||||
|
||||
## Scope, refinement, resources
|
||||
|
||||
Default one pilot only. Estimated device envelope 128 MiB, required free 1 GiB; bounded JSON only, conservatively under 2 MiB, no population snapshots or per-step logs. Every base step downloads both fields for independent quadrature, so PCIe/CPU diagnostic cost is intentional. Six CPU tests cover descriptor/source identity, uniform flux cancellation, affine convective divergence, quadratic viscous sign/coefficient, matched geometry/Jet selection and diffusive scaling.
|
||||
|
||||
Optional --scale 2 or 4 doubles/quadruples all lengths and divides U,V by that scale, holds lattice nu fixed, and scales steps/windows by scale^2: physical Re and t* match, Mach decreases. Source and geometry guards repeat. Device estimate scales as scale^2; memory gate requires twice the estimate or 1 GiB, whichever is greater. Scale 4 is not part of default authorization/readiness. --re 400 requires --allow-re400 and a separate reviewed root launch decision after Re100 evidence; no automatic promotion.
|
||||
|
||||
Compare AMR/reference velocity error, signed reaction coefficients, each contour's independently measured reaction and residual, contour spread, signed mass budgets, and measured aperture flux. Freeze numerical tolerances only after agreeing a scientifically appropriate convergence criterion; this preparation imposes only structural/finite-state guards. A completed pilot cannot establish convergence.
|
||||
|
||||
Follow-on design retained: porous Couette crossflow with u=U*expm1(Re_V*y/H)/expm1(Re_V), v=V and unequal exact wall shear. It requires validation-only planar geometry and is intentionally not implemented here.
|
||||
|
||||
### Amplitude, geometry, and high-Re limits
|
||||
|
||||
Both Jet amplitudes are constant throughout the run and every fine substep; geometry is fixed. There is no runtime switch, per-step forcing schedule, or moving-wall velocity in this campaign. Construction/packing happens once, with identical uploaded amplitudes in the uniform adapter. This avoids any substep timing ambiguity from amplitude changes.
|
||||
|
||||
Every case records actual tau_root and tau_fine. For this fixed s=1 geometry and speed, Re400 would give nu=.0018063, tau_root=.5054189 and tau_fine=.5108378, close to the SRT limit .5. The implemented Re choices are a bounded campaign menu, not a universal physical Reynolds-number ceiling. Higher-Re evidence may require a larger initial resolved geometry or a different justified grid/relaxation design before launch. Diffusive refinement at constant lattice nu improves spatial/Mach resolution but does not move tau farther from .5; simply selecting scale=2 does not cure a poor tau. No all-Re, LES or three-dimensional realism claim follows from this pilot.
|
||||
|
||||
The source guard checks production file content against d79, not validation-branch HEAD. This runner is validation-only; generated results and frozen execution-package archives are not committed.
|
||||
@@ -0,0 +1,139 @@
|
||||
"""CPU-only science, geometry, units, and source tests; no PyCUDA import."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
import numpy as np
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
REPO = ROOT
|
||||
sys.path.insert(0, str(ROOT / "tests/validation"))
|
||||
sys.path.insert(0, str(REPO / "src"))
|
||||
from run_jet_circle_cv import (
|
||||
CX,
|
||||
CY,
|
||||
W,
|
||||
case,
|
||||
state,
|
||||
cv,
|
||||
aperture_probe,
|
||||
measured_aperture_flux,
|
||||
verify,
|
||||
)
|
||||
|
||||
|
||||
def eq(rho, u, v):
|
||||
cu = CX[:, None, None] * u + CY[:, None, None] * v
|
||||
return W[:, None, None] * rho * (1 + 3 * cu + 4.5 * cu**2 - 1.5 * (u * u + v * v))
|
||||
|
||||
|
||||
class Host(unittest.TestCase):
|
||||
def test_descriptor_and_uniform_closed_flux(self):
|
||||
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
|
||||
|
||||
np.testing.assert_array_equal(CX, D2Q9_EX)
|
||||
np.testing.assert_array_equal(CY, D2Q9_EY)
|
||||
rho = np.full((48, 64), 1.01)
|
||||
g = eq(rho, 0.023, -0.017)
|
||||
inventory, flux = cv(g, np.ones((48, 64), bool), (0, 0), (5, 4, 25, 20), 0.0072)
|
||||
np.testing.assert_allclose(
|
||||
inventory, 320 * 1.01 * np.array([1, 0.023, -0.017]), atol=1e-12
|
||||
)
|
||||
np.testing.assert_allclose(flux, 0, atol=1e-14)
|
||||
|
||||
def test_affine_velocity_independent_divergence(self):
|
||||
y, x = np.mgrid[:48, :64]
|
||||
x = (x + 0.5) * 0.5
|
||||
y = (y + 0.5) * 0.5
|
||||
a = 0.001
|
||||
g = eq(np.ones_like(x), a * x, -a * y)
|
||||
_, flux = cv(g, np.ones(x.shape, bool), (0, 0), (5, 4, 25, 20), 0.0072)
|
||||
np.testing.assert_allclose(
|
||||
flux, [0, a * a * 320 * 15, a * a * 320 * 12], atol=1e-13
|
||||
)
|
||||
|
||||
def test_quadratic_viscous_flux_sign_and_units(self):
|
||||
y, x = np.mgrid[:48, :64]
|
||||
y = (y + 0.5) * 0.5
|
||||
a = 0.0001
|
||||
nu = 0.0072
|
||||
g = eq(np.ones_like(y), a * y * y, np.zeros_like(y))
|
||||
_, flux = cv(g, np.ones(y.shape, bool), (0, 0), (5, 4, 25, 20), nu)
|
||||
np.testing.assert_allclose(flux, [0, -2 * nu * a * 320, 0], atol=2e-14)
|
||||
|
||||
def test_geometry_adapter_match_and_apertures(self):
|
||||
from CelerisLab.config import LBMConfig
|
||||
from CelerisLab.body.geometry.circle import CircleGeometry
|
||||
from CelerisLab.body.objects import SimObject
|
||||
from CelerisLab.body.jets import Jet, Sector, Radial, jet_link_shapes
|
||||
from CelerisLab.lbm.refinement import FixedPatch2D, prepare_fine_boundary
|
||||
from CelerisLab.lbm.descriptors import SOLID
|
||||
|
||||
c = case()
|
||||
center = np.array(c["center"])
|
||||
r = c["diameter"] / 2
|
||||
cfg = LBMConfig(nx=c["nx"], ny=c["ny"], viscosity=c["nu"])
|
||||
cfg.validate()
|
||||
patch = FixedPatch2D.from_config(cfg, c["bounds"])
|
||||
obj = SimObject(0, CircleGeometry(*center, r), center=tuple(center), radius=r)
|
||||
jets = [
|
||||
Jet(
|
||||
Sector(tuple(center), a, np.pi / 12, r + 0.1),
|
||||
Radial(tuple(center), inward=False),
|
||||
"uniform",
|
||||
s * c["jet_speed"],
|
||||
)
|
||||
for a, s in [(np.pi / 2, 1), (3 * np.pi / 2, -1)]
|
||||
]
|
||||
host = prepare_fine_boundary(cfg, patch, [obj], jets=jets)
|
||||
shape, ids = jet_link_shapes(jets, host.curved_hits)
|
||||
self.assertTrue((ids == 0).any() and (ids == 1).any() and (ids < 0).any())
|
||||
self.assertGreater(host.curved_host[2].min(), 0.010001)
|
||||
self.assertFalse(np.any(host.curved_host[-1]))
|
||||
links = CircleGeometry(*(2 * center - 0.5), 2 * r).build_cut_links(
|
||||
2 * c["nx"], 2 * c["ny"]
|
||||
)
|
||||
np.testing.assert_array_equal([v.dir for v in links], host.curved_host[1])
|
||||
np.testing.assert_allclose(
|
||||
[v.q for v in links], host.curved_host[2], atol=2e-7, rtol=0
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
np.array([[v.hit_x, v.hit_y] for v in links]) * 0.5 + 0.25,
|
||||
host.curved_hits,
|
||||
atol=2e-12,
|
||||
)
|
||||
fluid = (host.flags.reshape(patch.fine_shape) & SOLID) == 0
|
||||
g = state((9, *patch.fine_shape), c["u"])
|
||||
g[:, ~fluid] = np.nan
|
||||
for contour in c["contours"]:
|
||||
cv(g, fluid, patch.fine_origin, contour, c["nu"])
|
||||
probe = aperture_probe(fluid, patch.fine_origin, center, r)
|
||||
measured = measured_aperture_flux(g, probe)
|
||||
np.testing.assert_allclose(
|
||||
measured, 0, atol=1e-14
|
||||
) # x-directed flow has zero flux on symmetric top/bottom arcs
|
||||
print(
|
||||
"GEOMETRY",
|
||||
dict(
|
||||
links=len(links),
|
||||
aperture_counts=[int((ids == i).sum()) for i in [0, 1]],
|
||||
q_min=float(host.curved_host[2].min()),
|
||||
q_max=float(host.curved_host[2].max()),
|
||||
fallbacks=int(np.count_nonzero(host.curved_host[-1])),
|
||||
sample_distances=[p[-1] for p in probe],
|
||||
),
|
||||
)
|
||||
|
||||
def test_diffusive_scaling(self):
|
||||
a, b = case(1), case(2)
|
||||
self.assertEqual(a["re_d"], b["re_d"])
|
||||
self.assertEqual(a["nu"], b["nu"])
|
||||
self.assertEqual(a["final_t_star"], b["final_t_star"])
|
||||
self.assertEqual(a["steps"] * 4, b["steps"])
|
||||
|
||||
def test_source_pin(self):
|
||||
verify(REPO)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,103 @@
|
||||
import sys, json, hashlib
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "tests/validation"))
|
||||
import re100_helpers as h
|
||||
import periodic as p
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n", [20, 40, 80])
|
||||
def test_units(n):
|
||||
x = h.parameters(n, 0.01)
|
||||
assert x["viscosity"] * x["dx"] ** 2 / x["dt"] == pytest.approx(0.001)
|
||||
assert x["mean_speed"] * x["dx"] / x["dt"] == pytest.approx(1.0)
|
||||
assert x["mean_speed"] * n / x["viscosity"] == pytest.approx(100)
|
||||
assert x["peak_mach"] == pytest.approx(0.015 * np.sqrt(3))
|
||||
assert (x["top"] - x["bottom"]) * x["dx"] == pytest.approx(0.41)
|
||||
assert (x["nx"] - 1) * x["dx"] == pytest.approx(2.2)
|
||||
assert (np.array(x["center"]) - [0, 0.5]) * x["dx"] == pytest.approx([0.2, 0.2])
|
||||
cfg, _, _ = h.make_config(n, 0.01, "MRT", "neq_extrap", "zou_he_local")
|
||||
assert cfg["method"]["inlet"]["scheme"] == "zou_he_local"
|
||||
|
||||
|
||||
def test_pressure_units():
|
||||
x = h.parameters(40, 0.01)
|
||||
ny, nx = x["ny"], x["nx"]
|
||||
xx = np.arange(nx)[None, :]
|
||||
rho = np.broadcast_to(1 + xx * 1e-6, (ny, nx))
|
||||
fluid = np.ones_like(rho, dtype=bool)
|
||||
assert h.pressure_probes(rho, fluid, x)["dp"] == pytest.approx(
|
||||
-40e-6 / 0.01**2 / 3, abs=1e-10
|
||||
)
|
||||
|
||||
|
||||
def rows(amplitude=1.0, drift=0.0):
|
||||
out = []
|
||||
for t in np.arange(240, 300.001, 0.02):
|
||||
out.append(
|
||||
dict(
|
||||
tstar=t,
|
||||
tstar_force=t,
|
||||
cl=amplitude * np.sin(2 * np.pi * t / 3.3),
|
||||
cd=3.2 + 0.03 * np.cos(4 * np.pi * t / 3.3) + drift * t,
|
||||
dp=2.46 + 0.04 * np.sin(4 * np.pi * t / 3.3),
|
||||
mass=1.0,
|
||||
flux_in=1.0,
|
||||
flux_out=1.0,
|
||||
)
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def test_periodic_synthetic():
|
||||
a = p.assess(rows())
|
||||
assert a["periodic"]
|
||||
assert len(a["cycles"]) > 5
|
||||
assert a["cycles"][-1]["st"] == pytest.approx(1 / 3.3, rel=1e-5)
|
||||
assert not p.assess(rows(amplitude=0.01))["periodic"]
|
||||
assert not p.assess(rows(drift=0.01))["periodic"]
|
||||
assert not p.assess(rows()[:100])["periodic"]
|
||||
|
||||
|
||||
def test_production_unmodified():
|
||||
m = json.loads(
|
||||
(ROOT / "tests/validation/data/dfg_re100/source_manifest.json").read_text()
|
||||
)
|
||||
assert m["commit"] == h.PIN
|
||||
for name, digest in m["production_sha256"].items():
|
||||
assert hashlib.sha256((ROOT / name).read_bytes()).hexdigest() == digest, name
|
||||
|
||||
|
||||
def test_reference():
|
||||
r = json.loads(
|
||||
(ROOT / "tests/validation/data/dfg_re100/reference_cycle.json").read_text()
|
||||
)
|
||||
assert 0.301 < r["st"] < 0.303
|
||||
assert 3.22 < r["cd_max"] < 3.23
|
||||
assert 2.5 < r["dp_max"] < 2.51
|
||||
assert p.compare(r, r)["reference_agreement"]
|
||||
|
||||
|
||||
def test_stale_cycles_rejected():
|
||||
r = rows()
|
||||
level = next(x["cl"] for x in r if x["tstar"] >= 284.5)
|
||||
for x in r:
|
||||
if x["tstar"] >= 284.5:
|
||||
x["cl"] = level
|
||||
assert not p.assess(r)["periodic"]
|
||||
|
||||
|
||||
def test_min_time_is_used(monkeypatch):
|
||||
monkeypatch.setitem(p.GATES, "min_tstar", 290.0)
|
||||
assert not p.assess(rows())["periodic"]
|
||||
|
||||
|
||||
def test_nonconsecutive_cycles_rejected(monkeypatch):
|
||||
cycles = p.extract(rows())
|
||||
bad = cycles[-6:]
|
||||
bad.pop(2)
|
||||
monkeypatch.setattr(p, "extract", lambda *a, **k: bad)
|
||||
assert not p.assess(rows())["periodic"]
|
||||
@@ -0,0 +1,818 @@
|
||||
{
|
||||
"start": 96.60907453162486,
|
||||
"end": 99.92207699254307,
|
||||
"period": 3.313002460918213,
|
||||
"st": 0.301841007302737,
|
||||
"cd_min": 3.164265915836695,
|
||||
"cd_max": 3.2273924509469625,
|
||||
"cd_midrange": 3.1958291833918286,
|
||||
"cd_amplitude": 0.06312653511026767,
|
||||
"cd_time_mean": 3.1958873825129652,
|
||||
"cd_waveform": [
|
||||
3.2192714249444543,
|
||||
3.2202803099943376,
|
||||
3.2212299416471293,
|
||||
3.222118601683268,
|
||||
3.2229440924252426,
|
||||
3.2237043728228487,
|
||||
3.224397563502949,
|
||||
3.225021951759953,
|
||||
3.2255759960759045,
|
||||
3.226058329556411,
|
||||
3.2264677639287034,
|
||||
3.226803291773589,
|
||||
3.2270640893003435,
|
||||
3.227249518358305,
|
||||
3.227359127231524,
|
||||
3.2273924509469625,
|
||||
3.2273485621938347,
|
||||
3.22722863562996,
|
||||
3.227032979281687,
|
||||
3.2267620869419815,
|
||||
3.2264166368706584,
|
||||
3.2259974896059336,
|
||||
3.225505684975984,
|
||||
3.2249424394952255,
|
||||
3.224309142817072,
|
||||
3.2236073535652885,
|
||||
3.222838795285557,
|
||||
3.222005351549716,
|
||||
3.2211090610907003,
|
||||
3.220152110736951,
|
||||
3.21913591342886,
|
||||
3.2180639425025355,
|
||||
3.2169388049876204,
|
||||
3.2157632320581224,
|
||||
3.2145400725256823,
|
||||
3.213272285099224,
|
||||
3.2119629314217737,
|
||||
3.210615168117056,
|
||||
3.209232239485073,
|
||||
3.2078174691606125,
|
||||
3.2063742523121657,
|
||||
3.204906047188526,
|
||||
3.203416367201774,
|
||||
3.201908772307293,
|
||||
3.20038671156229,
|
||||
3.1988540415270714,
|
||||
3.197314448828512,
|
||||
3.1957715956753288,
|
||||
3.194229147910405,
|
||||
3.1926907667041973,
|
||||
3.19116010031245,
|
||||
3.1896407755313154,
|
||||
3.188136389507111,
|
||||
3.1866505018003437,
|
||||
3.18518662592634,
|
||||
3.1837482219382216,
|
||||
3.1823386881615674,
|
||||
3.1809613539268415,
|
||||
3.1796199070610616,
|
||||
3.178317365693067,
|
||||
3.1770566177141566,
|
||||
3.1758406383588778,
|
||||
3.1746722981220814,
|
||||
3.1735543560753845,
|
||||
3.1724894540381894,
|
||||
3.171480110448416,
|
||||
3.1705287147383436,
|
||||
3.169637521817997,
|
||||
3.168808647409722,
|
||||
3.168044062789466,
|
||||
3.16734559035941,
|
||||
3.166714899466848,
|
||||
3.1661541138486857,
|
||||
3.165664557595598,
|
||||
3.1652468672004876,
|
||||
3.1649020564911114,
|
||||
3.164630969966524,
|
||||
3.164434280442736,
|
||||
3.1643124874918267,
|
||||
3.164265915836695,
|
||||
3.164294714155108,
|
||||
3.164398854572734,
|
||||
3.164578132207916,
|
||||
3.164832165115698,
|
||||
3.1651603948311298,
|
||||
3.165562087003554,
|
||||
3.166036763487302,
|
||||
3.166583632347454,
|
||||
3.1672006737836313,
|
||||
3.1678864585242907,
|
||||
3.1686393915089255,
|
||||
3.1694577152711365,
|
||||
3.17033951339357,
|
||||
3.171282714587482,
|
||||
3.1722850969178604,
|
||||
3.1733442928081175,
|
||||
3.17445779387909,
|
||||
3.175622956745662,
|
||||
3.176837008657004,
|
||||
3.178097054026256,
|
||||
3.1794002164290207,
|
||||
3.1807436978583974,
|
||||
3.182123709232136,
|
||||
3.1835369281843153,
|
||||
3.1849799460334114,
|
||||
3.1864492760653347,
|
||||
3.1879413618349814,
|
||||
3.1894525859099345,
|
||||
3.19097927879319,
|
||||
3.1925177278192263,
|
||||
3.1940641867753663,
|
||||
3.195614884812199,
|
||||
3.197166036575152,
|
||||
3.198713851202913,
|
||||
3.2002545337987547,
|
||||
3.2017840862384728,
|
||||
3.2032988764817527,
|
||||
3.2047951919540822,
|
||||
3.2062693641104913,
|
||||
3.207717777711432,
|
||||
3.2091368805426974,
|
||||
3.2105231925781985,
|
||||
3.2118733148571565,
|
||||
3.2131839390335397,
|
||||
3.2144518552407257,
|
||||
3.2156739612615417,
|
||||
3.216847269859638,
|
||||
3.217968917317378,
|
||||
3.219036170327386,
|
||||
3.220045562724451,
|
||||
3.220995334928343,
|
||||
3.2218832986850447,
|
||||
3.2227073170066918,
|
||||
3.2234654153623845,
|
||||
3.224155785778181,
|
||||
3.2247767917974066,
|
||||
3.2253269721252744,
|
||||
3.2258050437055585,
|
||||
3.2262099052701587,
|
||||
3.2265406390922298,
|
||||
3.226796513255351,
|
||||
3.2269769825521033,
|
||||
3.227081689768787,
|
||||
3.22710955220279,
|
||||
3.227061168669443,
|
||||
3.2269370924239436,
|
||||
3.2267377237295465,
|
||||
3.2264636466881744,
|
||||
3.2261156276975886,
|
||||
3.2256946121982577,
|
||||
3.2252017217835,
|
||||
3.224638250833764,
|
||||
3.2240056622744246,
|
||||
3.223305583413101,
|
||||
3.222539801329067,
|
||||
3.2217102575995633,
|
||||
3.2208190428742154,
|
||||
3.219867854353387,
|
||||
3.218859259026148,
|
||||
3.2177961858992568,
|
||||
3.2166812675225214,
|
||||
3.2155172535532928,
|
||||
3.2143070040629325,
|
||||
3.2130534817570826,
|
||||
3.211759744984257,
|
||||
3.210428939916766,
|
||||
3.2090642928856465,
|
||||
3.207669102594001,
|
||||
3.2062467320151002,
|
||||
3.2048006003874154,
|
||||
3.2033341751083237,
|
||||
3.2018508739555798,
|
||||
3.2003542779095873,
|
||||
3.1988481127850616,
|
||||
3.1973359573002664,
|
||||
3.1958213973977445,
|
||||
3.194308017670146,
|
||||
3.19279939346063,
|
||||
3.1912990827323733,
|
||||
3.189810618404239,
|
||||
3.188337500433207,
|
||||
3.1868831881313007,
|
||||
3.185451092560893,
|
||||
3.1840445691293837,
|
||||
3.1826669104651053,
|
||||
3.1813214859380303,
|
||||
3.1800117901097846,
|
||||
3.178740483661364,
|
||||
3.177510533708781,
|
||||
3.1763248101985377,
|
||||
3.1751860802710605,
|
||||
3.1740970016898973,
|
||||
3.173060117741568,
|
||||
3.172077851413086,
|
||||
3.171152500485684,
|
||||
3.1702862322665037,
|
||||
3.169481079259431,
|
||||
3.1687389344676062,
|
||||
3.1680615476209555,
|
||||
3.1674506253282964,
|
||||
3.1669085513260424,
|
||||
3.1664356220663086,
|
||||
3.1660329701689696,
|
||||
3.165701568133256,
|
||||
3.165442226438076,
|
||||
3.165255590934966,
|
||||
3.16514214162142,
|
||||
3.1651021912290123,
|
||||
3.1651358837399672,
|
||||
3.1652431944171915,
|
||||
3.165423928768434,
|
||||
3.165677722868927,
|
||||
3.166004043509568,
|
||||
3.1664021891744154,
|
||||
3.1668723687065503,
|
||||
3.16741250732336,
|
||||
3.168021321234566,
|
||||
3.1686974411192694,
|
||||
3.1694393380619967,
|
||||
3.170245326209458,
|
||||
3.171113566351732,
|
||||
3.172042068956519,
|
||||
3.173028698759894,
|
||||
3.1740711784468334,
|
||||
3.1751670940119223,
|
||||
3.176313898883396,
|
||||
3.177508920042188,
|
||||
3.17874936360477,
|
||||
3.1800328259079818,
|
||||
3.181355841861699,
|
||||
3.1827151303301267,
|
||||
3.1841074729782854,
|
||||
3.1855295642750754,
|
||||
3.186978019262098,
|
||||
3.1884493811374868,
|
||||
3.1899401298427543,
|
||||
3.191446689954596,
|
||||
3.192965439405816,
|
||||
3.1944927184101823,
|
||||
3.1960248382013705,
|
||||
3.1975580901006118,
|
||||
3.199088754756803,
|
||||
3.200613009960425,
|
||||
3.20212708603014,
|
||||
3.2036273295775386,
|
||||
3.205110074007536,
|
||||
3.2065716893821072,
|
||||
3.208008592293136,
|
||||
3.209417254433106,
|
||||
3.210794212466389,
|
||||
3.212136076499309,
|
||||
3.213439539354214,
|
||||
3.214701385159765,
|
||||
3.215918497504247,
|
||||
3.2170878683639637,
|
||||
3.2182066051468428,
|
||||
3.2192715160336944
|
||||
],
|
||||
"cl_min": -1.021285623433455,
|
||||
"cl_max": 0.9865706078511706,
|
||||
"cl_midrange": -0.017357507791142257,
|
||||
"cl_amplitude": 2.0078562312846255,
|
||||
"cl_time_mean": -0.018055778744564696,
|
||||
"cl_waveform": [
|
||||
-1.0212846006341447,
|
||||
-1.0209838590222233,
|
||||
-1.0200727383007768,
|
||||
-1.0185590327265965,
|
||||
-1.0164441167317841,
|
||||
-1.01372972461554,
|
||||
-1.010417946688192,
|
||||
-1.0065112246383783,
|
||||
-1.0020123475697247,
|
||||
-0.9969244470282416,
|
||||
-0.9912509924392147,
|
||||
-0.984995786253974,
|
||||
-0.9781629589771562,
|
||||
-0.9707569640900402,
|
||||
-0.9627825730042762,
|
||||
-0.9542433948273606,
|
||||
-0.9451386764809715,
|
||||
-0.9354819238811002,
|
||||
-0.925279142068636,
|
||||
-0.9145366279059466,
|
||||
-0.9032609650239106,
|
||||
-0.8914590187491076,
|
||||
-0.8791379311317419,
|
||||
-0.8663051160407256,
|
||||
-0.8529682543310683,
|
||||
-0.8391352890960927,
|
||||
-0.8248144210074706,
|
||||
-0.8100141037255332,
|
||||
-0.7947430394336983,
|
||||
-0.7790101644090639,
|
||||
-0.7628173731216689,
|
||||
-0.7461816969118068,
|
||||
-0.7291127993693902,
|
||||
-0.7116205711659391,
|
||||
-0.6937151260047051,
|
||||
-0.6754067966482753,
|
||||
-0.6567061309580705,
|
||||
-0.637623888008147,
|
||||
-0.6181710341714578,
|
||||
-0.5983587392102484,
|
||||
-0.5781983723659854,
|
||||
-0.5577014984612264,
|
||||
-0.5368798738456273,
|
||||
-0.5157454424421091,
|
||||
-0.4943063234695201,
|
||||
-0.4725784485709182,
|
||||
-0.4505750883115271,
|
||||
-0.4283089024165666,
|
||||
-0.405792716370432,
|
||||
-0.38303951669758823,
|
||||
-0.36006244613658056,
|
||||
-0.3368747985998418,
|
||||
-0.3134900140010978,
|
||||
-0.28992167285852,
|
||||
-0.2661834907138685,
|
||||
-0.24228931233131965,
|
||||
-0.21825310567974987,
|
||||
-0.19408895564887746,
|
||||
-0.16980993582635662,
|
||||
-0.14543133426087812,
|
||||
-0.12096814315114233,
|
||||
-0.09643485175457375,
|
||||
-0.0718460310133605,
|
||||
-0.04721632579948139,
|
||||
-0.022560446976959612,
|
||||
0.0021068368194982773,
|
||||
0.02677070764406316,
|
||||
0.05141630692334945,
|
||||
0.07602874438036183,
|
||||
0.10059310719281325,
|
||||
0.1250944693656024,
|
||||
0.1495179012836364,
|
||||
0.17384754771042515,
|
||||
0.19806822588832526,
|
||||
0.22216578848678734,
|
||||
0.2461253934945549,
|
||||
0.2699322466441472,
|
||||
0.2935716120906063,
|
||||
0.3170288231832102,
|
||||
0.34028929328125385,
|
||||
0.36333852677889195,
|
||||
0.3861621301373943,
|
||||
0.40874582307035934,
|
||||
0.43107544954464694,
|
||||
0.4531369891575652,
|
||||
0.47491656822324385,
|
||||
0.4963986071823978,
|
||||
0.5175678949127962,
|
||||
0.5384141735804333,
|
||||
0.5589242775445442,
|
||||
0.5790852440767277,
|
||||
0.5988843242125788,
|
||||
0.6183089934932646,
|
||||
0.6373469625245218,
|
||||
0.6559861873413382,
|
||||
0.674214879706298,
|
||||
0.6920215170336557,
|
||||
0.7093948521976959,
|
||||
0.7263239230232448,
|
||||
0.742798061573706,
|
||||
0.7588052844335239,
|
||||
0.7743307471351503,
|
||||
0.7893708273667098,
|
||||
0.803916135973999,
|
||||
0.8179576199407215,
|
||||
0.8314865695778171,
|
||||
0.8444946252982337,
|
||||
0.8569737840555796,
|
||||
0.8689164052636061,
|
||||
0.8803152163390694,
|
||||
0.8911633177499939,
|
||||
0.9014541876864335,
|
||||
0.9111816861332408,
|
||||
0.9203400586319765,
|
||||
0.9289234860093117,
|
||||
0.936918266184497,
|
||||
0.9443288660701289,
|
||||
0.9511511188545412,
|
||||
0.9573812545471582,
|
||||
0.9630159006719111,
|
||||
0.9680520825648866,
|
||||
0.972487223197885,
|
||||
0.9763191426551175,
|
||||
0.9795460569396854,
|
||||
0.9821665765670635,
|
||||
0.9841797046166384,
|
||||
0.9855848344669915,
|
||||
0.986381747045911,
|
||||
0.9865706078511706,
|
||||
0.9861430751758404,
|
||||
0.9851078843149434,
|
||||
0.9834674674373464,
|
||||
0.9812235079135473,
|
||||
0.978378053362095,
|
||||
0.974933510845897,
|
||||
0.9708926418403944,
|
||||
0.9662585570602749,
|
||||
0.9610347110820011,
|
||||
0.9552248968839854,
|
||||
0.9488332402249186,
|
||||
0.941864193965974,
|
||||
0.9343225322984421,
|
||||
0.9262133449652685,
|
||||
0.9175353367316478,
|
||||
0.9082985884427632,
|
||||
0.8985116225127334,
|
||||
0.888180745849068,
|
||||
0.8773125515396398,
|
||||
0.8659139131624679,
|
||||
0.8539919791562397,
|
||||
0.841554167318266,
|
||||
0.8286081593376501,
|
||||
0.8151618954310706,
|
||||
0.8012235691354387,
|
||||
0.7868016221216644,
|
||||
0.7719047392049175,
|
||||
0.7565418433711996,
|
||||
0.7407178806800201,
|
||||
0.7244435174512682,
|
||||
0.707731607286953,
|
||||
0.6905919887305398,
|
||||
0.6730347151255153,
|
||||
0.6550700503410322,
|
||||
0.6367084643889164,
|
||||
0.617960629252382,
|
||||
0.5988374147135249,
|
||||
0.5793498841758384,
|
||||
0.5595092905790806,
|
||||
0.5393270722633181,
|
||||
0.5188148488816408,
|
||||
0.4979844172354143,
|
||||
0.4768457513845936,
|
||||
0.45541040875158884,
|
||||
0.43369366814580723,
|
||||
0.41170800001396685,
|
||||
0.38946603027749815,
|
||||
0.3669805357724072,
|
||||
0.3442644395643103,
|
||||
0.3213308061849796,
|
||||
0.2981928366787376,
|
||||
0.2748638635506233,
|
||||
0.2513573454944852,
|
||||
0.22768686201881655,
|
||||
0.20386610779915632,
|
||||
0.17990888695762292,
|
||||
0.15582865541317795,
|
||||
0.13163881464575702,
|
||||
0.10735496845190624,
|
||||
0.08299130047733835,
|
||||
0.05856207148298293,
|
||||
0.034081612308419704,
|
||||
0.009564316642990896,
|
||||
-0.014975366427826247,
|
||||
-0.039522940080061256,
|
||||
-0.06406386762101547,
|
||||
-0.08858358056301793,
|
||||
-0.11306748690571801,
|
||||
-0.1375009796129617,
|
||||
-0.16186944527282868,
|
||||
-0.186158127286089,
|
||||
-0.21035090571304071,
|
||||
-0.23443440343427951,
|
||||
-0.2583940736125971,
|
||||
-0.28221540953832974,
|
||||
-0.30588395428926807,
|
||||
-0.3293853105136673,
|
||||
-0.35270515033803884,
|
||||
-0.37582922531739726,
|
||||
-0.39874337658360604,
|
||||
-0.4214335449640186,
|
||||
-0.44388578123531364,
|
||||
-0.4660862563718654,
|
||||
-0.4880212718764244,
|
||||
-0.509677270114379,
|
||||
-0.531036136112504,
|
||||
-0.5520885997128909,
|
||||
-0.5728219120147833,
|
||||
-0.5932231908464959,
|
||||
-0.6132797468099019,
|
||||
-0.6329790933854313,
|
||||
-0.652308956917678,
|
||||
-0.6712572864574565,
|
||||
-0.6898122635200815,
|
||||
-0.7079623116377989,
|
||||
-0.7256961057871241,
|
||||
-0.7430025816000796,
|
||||
-0.7598709443367393,
|
||||
-0.7762906777176882,
|
||||
-0.7922455571624138,
|
||||
-0.8077296357950401,
|
||||
-0.8227349965022713,
|
||||
-0.8372523342423263,
|
||||
-0.8512726627071948,
|
||||
-0.8647873213967461,
|
||||
-0.8777879823088283,
|
||||
-0.8902666562070795,
|
||||
-0.902215698680155,
|
||||
-0.9136278156586564,
|
||||
-0.9244960686140175,
|
||||
-0.9348138793173668,
|
||||
-0.9445750342635408,
|
||||
-0.9537736885083689,
|
||||
-0.9623985419578303,
|
||||
-0.9704465786953729,
|
||||
-0.977916689291491,
|
||||
-0.9848045517658248,
|
||||
-0.99110622613045,
|
||||
-0.9968181555444723,
|
||||
-1.0019371672305317,
|
||||
-1.0064604728414646,
|
||||
-1.0103856687137327,
|
||||
-1.0137107350135544,
|
||||
-1.0164340351998324,
|
||||
-1.01855431505123,
|
||||
-1.0200707006187215,
|
||||
-1.0209826964966897,
|
||||
-1.021285623433455
|
||||
],
|
||||
"dp_min": 2.4203712005726827,
|
||||
"dp_max": 2.507012902468643,
|
||||
"dp_midrange": 2.463692051520663,
|
||||
"dp_amplitude": 0.08664170189596021,
|
||||
"dp_time_mean": 2.4639514695178963,
|
||||
"dp_waveform": [
|
||||
2.485558537878651,
|
||||
2.487354668330821,
|
||||
2.489094038859844,
|
||||
2.4907730268211923,
|
||||
2.4923875927671357,
|
||||
2.493933872161179,
|
||||
2.4954081841697953,
|
||||
2.4968070398569093,
|
||||
2.498127149841111,
|
||||
2.4993654310604727,
|
||||
2.5005190130830766,
|
||||
2.5015852434313093,
|
||||
2.502561692495173,
|
||||
2.50344615769302,
|
||||
2.5042366667973424,
|
||||
2.504931223854115,
|
||||
2.5055272123906085,
|
||||
2.5060247031878675,
|
||||
2.5064226784653965,
|
||||
2.5067203585292273,
|
||||
2.506917201491131,
|
||||
2.507012902468643,
|
||||
2.507007392300157,
|
||||
2.5069008355614204,
|
||||
2.506693628251957,
|
||||
2.5063863949686396,
|
||||
2.5059799860275485,
|
||||
2.505475473290949,
|
||||
2.504874146643661,
|
||||
2.5041775076417783,
|
||||
2.503385760083982,
|
||||
2.5025023981520067,
|
||||
2.501529545753141,
|
||||
2.500469519417466,
|
||||
2.4993248229208986,
|
||||
2.49809814052029,
|
||||
2.4967923311909144,
|
||||
2.495410422113925,
|
||||
2.493955601552369,
|
||||
2.492431212574658,
|
||||
2.490840745393346,
|
||||
2.489187830493204,
|
||||
2.4874762310352,
|
||||
2.4857098352761477,
|
||||
2.483891999686845,
|
||||
2.4820274911095415,
|
||||
2.480120662304518,
|
||||
2.4781758269143728,
|
||||
2.476197383057849,
|
||||
2.474189804509867,
|
||||
2.47215763257422,
|
||||
2.47010546680718,
|
||||
2.4680379564006794,
|
||||
2.465959791179922,
|
||||
2.4638756921667406,
|
||||
2.46179040254991,
|
||||
2.4597086782017046,
|
||||
2.457635278124787,
|
||||
2.455575155334104,
|
||||
2.4535330651884157,
|
||||
2.451513647441801,
|
||||
2.4495215742730494,
|
||||
2.447561471628255,
|
||||
2.4456379085376456,
|
||||
2.443755387861672,
|
||||
2.4419183356738783,
|
||||
2.4401310914614514,
|
||||
2.438397897721916,
|
||||
2.436722890392385,
|
||||
2.4351100884293455,
|
||||
2.4335633840618365,
|
||||
2.432086533067437,
|
||||
2.4306838103185973,
|
||||
2.4293586878640068,
|
||||
2.428113869269998,
|
||||
2.4269524606913686,
|
||||
2.4258773845443202,
|
||||
2.424891371376568,
|
||||
2.423996951340259,
|
||||
2.4231964462177444,
|
||||
2.4224919620804686,
|
||||
2.4218853823843163,
|
||||
2.421378360808599,
|
||||
2.4209723155762863,
|
||||
2.420668423809868,
|
||||
2.4204676161372016,
|
||||
2.4203712005726827,
|
||||
2.420380068704988,
|
||||
2.420493289903554,
|
||||
2.420710759315692,
|
||||
2.4210321100509016,
|
||||
2.421456711924972,
|
||||
2.4219836715842935,
|
||||
2.4226118326498916,
|
||||
2.423339777335187,
|
||||
2.424165828339519,
|
||||
2.4250880518614637,
|
||||
2.4261042616582924,
|
||||
2.4272120230151923,
|
||||
2.4284086587121188,
|
||||
2.4296915368281606,
|
||||
2.4310582510675482,
|
||||
2.432504302921731,
|
||||
2.4340261048245524,
|
||||
2.435619866098322,
|
||||
2.4372816029434006,
|
||||
2.4390071492364305,
|
||||
2.440792167926676,
|
||||
2.442632162995385,
|
||||
2.444522492162555,
|
||||
2.4464583798729533,
|
||||
2.448434930859836,
|
||||
2.450447144296273,
|
||||
2.4524899276311465,
|
||||
2.4545581275894874,
|
||||
2.4566467249172272,
|
||||
2.4587500444816213,
|
||||
2.460862775988026,
|
||||
2.462979593476627,
|
||||
2.4650951703383326,
|
||||
2.4672041949249874,
|
||||
2.469301385189656,
|
||||
2.4713815036357163,
|
||||
2.4734393717724394,
|
||||
2.475469884333746,
|
||||
2.477468023387656,
|
||||
2.4794288713654966,
|
||||
2.4813476246082624,
|
||||
2.483219605457596,
|
||||
2.485039458918412,
|
||||
2.4868033804993956,
|
||||
2.4885072430697144,
|
||||
2.4901469959998734,
|
||||
2.4917187690881257,
|
||||
2.4932188815730543,
|
||||
2.4946438498431105,
|
||||
2.49599039508062,
|
||||
2.4972554500082174,
|
||||
2.498436164504503,
|
||||
2.4995299110592493,
|
||||
2.5005342891380167,
|
||||
2.501447129364351,
|
||||
2.502266495991754,
|
||||
2.5029895274516014,
|
||||
2.5036154604052916,
|
||||
2.5041435094498383,
|
||||
2.5045727041834747,
|
||||
2.504902313989728,
|
||||
2.5051318472519433,
|
||||
2.505261050325141,
|
||||
2.5052899056087403,
|
||||
2.5052186294050998,
|
||||
2.5050476691328045,
|
||||
2.5047776999609392,
|
||||
2.504409621696276,
|
||||
2.5039445544003622,
|
||||
2.503383834013878,
|
||||
2.5027281389852822,
|
||||
2.5019794867872425,
|
||||
2.501140507540692,
|
||||
2.500213360279151,
|
||||
2.4992003931281483,
|
||||
2.498104137409639,
|
||||
2.4969273011438373,
|
||||
2.495672762685901,
|
||||
2.494343563852958,
|
||||
2.492942903234649,
|
||||
2.491474128676318,
|
||||
2.4899407305330525,
|
||||
2.488346334061092,
|
||||
2.486694691617208,
|
||||
2.484989340356667,
|
||||
2.4832342121680133,
|
||||
2.4814339161814924,
|
||||
2.479592639951934,
|
||||
2.4777146583088454,
|
||||
2.4758043245876524,
|
||||
2.473866062448869,
|
||||
2.471904357327426,
|
||||
2.4699237475913485,
|
||||
2.4679288158413883,
|
||||
2.46592417988613,
|
||||
2.4639144839871556,
|
||||
2.461904389322368,
|
||||
2.459898565346157,
|
||||
2.4579017367770137,
|
||||
2.455918764942065,
|
||||
2.453954173148493,
|
||||
2.4520125671214457,
|
||||
2.4500985125535784,
|
||||
2.448216525842054,
|
||||
2.4463710641219887,
|
||||
2.444566515822548,
|
||||
2.4428071908357323,
|
||||
2.4410973107465668,
|
||||
2.439440999504861,
|
||||
2.4378422733498937,
|
||||
2.4363050317847414,
|
||||
2.434833047645694,
|
||||
2.43343006971473,
|
||||
2.432100619437783,
|
||||
2.430846995094142,
|
||||
2.429672363555455,
|
||||
2.428579719706815,
|
||||
2.427571878080977,
|
||||
2.426651464455842,
|
||||
2.4258209081907864,
|
||||
2.425082434921927,
|
||||
2.4244380590290993,
|
||||
2.423889577520045,
|
||||
2.4234385632168207,
|
||||
2.423086359460975,
|
||||
2.4228340742719734,
|
||||
2.4226825762746023,
|
||||
2.4226340587576405,
|
||||
2.4226874437485133,
|
||||
2.422842736063271,
|
||||
2.4230997975159894,
|
||||
2.4234582347462745,
|
||||
2.4239173992440146,
|
||||
2.424476386499562,
|
||||
2.4251340367353267,
|
||||
2.425888936234961,
|
||||
2.4267394193027876,
|
||||
2.427683571153293,
|
||||
2.428719231244257,
|
||||
2.429843998149402,
|
||||
2.431055234079425,
|
||||
2.432351101512926,
|
||||
2.4337276833933763,
|
||||
2.4351813534956155,
|
||||
2.43670858740822,
|
||||
2.4383056624813797,
|
||||
2.4399686671928897,
|
||||
2.4416935114716893,
|
||||
2.4434759376701103,
|
||||
2.4453115320216026,
|
||||
2.447195736733967,
|
||||
2.4491238624275873,
|
||||
2.451091101018784,
|
||||
2.4530925394872574,
|
||||
2.4551231733170757,
|
||||
2.4571781139809588,
|
||||
2.4592520095527344,
|
||||
2.4613395134290164,
|
||||
2.4634353978388575,
|
||||
2.46553441673564,
|
||||
2.467631320555607,
|
||||
2.469720870921841,
|
||||
2.4717978552593056,
|
||||
2.4738571012373916,
|
||||
2.4758934909645545,
|
||||
2.477901974977891,
|
||||
2.479877585675147,
|
||||
2.481815450926397,
|
||||
2.4837108064957207,
|
||||
2.485558618881201
|
||||
],
|
||||
"mass_min": 1.0,
|
||||
"mass_max": 1.0,
|
||||
"mass_midrange": 1.0,
|
||||
"mass_amplitude": 0.0,
|
||||
"mass_time_mean": 1.0,
|
||||
"flux_in_min": 1.0,
|
||||
"flux_in_max": 1.0,
|
||||
"flux_in_midrange": 1.0,
|
||||
"flux_in_amplitude": 0.0,
|
||||
"flux_in_time_mean": 1.0,
|
||||
"flux_out_min": 1.0,
|
||||
"flux_out_max": 1.0,
|
||||
"flux_out_midrange": 1.0,
|
||||
"flux_out_amplitude": 0.0,
|
||||
"flux_out_time_mean": 1.0,
|
||||
"flux_relative": 0.0,
|
||||
"source": "https://wwwold.mathematik.tu-dortmund.de/~featflow/media/dfg_bench2new_2d/20141124_dfg_bench2_official.zip",
|
||||
"archive_sha256": "08db59605b2f60efc512d2bbc79628ea5d1cc720c0986938a16920884a0215be",
|
||||
"selection": "lv6 dt4 last complete lift-minimum-to-minimum cycle; force columns 2,4,5; pressure columns 2,7-12; physical time multiplied by 10",
|
||||
"note": "Newer official high-order line-integration data; do not mix older website table with this reference."
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"commit": "d79e3f6f0a424f5bcc3a1a6d610313e49daf97a2",
|
||||
"production_sha256": {
|
||||
"src/CelerisLab/__init__.py": "21cc469bd6e970a1f9ff5970c8eea4602065e41fc881854759ee565ee55c10c6",
|
||||
"src/CelerisLab/body/__init__.py": "e4e0e8f0769a94428d5b02fac790443997556cfc5515a5f1ed1595d2c694d69d",
|
||||
"src/CelerisLab/body/action_smoother.py": "b1a23ee6bbd4850b172cb8738c5c3546ee465a54189e6af9d8044f023d4687dc",
|
||||
"src/CelerisLab/body/coupling/__init__.py": "c931f7eb318d48408c3e2febcc6917c88c8dcc1697cbf4329b1f340d94c35763",
|
||||
"src/CelerisLab/body/coupling/soa_packer.py": "8ec26fd55ef74801bbd5ab6bd3d0a7dabc750cbd498aaac2ca4c61a0108ad3c9",
|
||||
"src/CelerisLab/body/ddf_patch.py": "60fcfb7441e73bf290bcff41ca3a83da3e7ddad765d7247464e8f02e3043b4e2",
|
||||
"src/CelerisLab/body/geometry/__init__.py": "ada88980ab672959aca9aa3a4c4aade3219524b2ab68110cd2a2f41e3e0ca90a",
|
||||
"src/CelerisLab/body/geometry/base.py": "cceca14361ca2f34d34a3e43730e1a79b8ca81be00f0dd62e884db32b479419f",
|
||||
"src/CelerisLab/body/geometry/circle.py": "7605ca7c21103f1d801ac73ab8dd18dffbdac425b2dee7b8858b283628a9e436",
|
||||
"src/CelerisLab/body/jets.py": "4104c24af4651f4bb4f8d8fd36fb6d88f41c6e4469822f36e22e74c9ede31dac",
|
||||
"src/CelerisLab/body/manager.py": "01b653d25bd9b4333379c8bfd12383596b686d2029f2e91b1c91b618540ec6b3",
|
||||
"src/CelerisLab/body/objects.py": "8e63cee8faa9cc6243bcff428be628dfad73a02a9a3b8ece7d78c0a63406a08d",
|
||||
"src/CelerisLab/body/preprocess/__init__.py": "dfd9d58bc07f626ab23a70053e31b88a6d9165d2e4c76a4e9f48abe674d3d2ec",
|
||||
"src/CelerisLab/body/preprocess/flag_overlay.py": "831fc9e9736b758847759a085ff133eb49ec91318ace4fda76f85960138ee300",
|
||||
"src/CelerisLab/body/registry.py": "fbb7327780adbdc6da15ebdb799980daf32df22ee974af0893d7409f177bbb68",
|
||||
"src/CelerisLab/body/sync_plan.py": "3a15bfe9ebeb91482dbfc68850761ef39c872cc12d2efc795babd0c01e392323",
|
||||
"src/CelerisLab/common/__init__.py": "d4dbde78bc97fccc9eabac9abae8f0f15c112c10ff8e9b45bd9401d4723df1ef",
|
||||
"src/CelerisLab/common/_types.py": "aad2ff1a9033d4fd68783ad7123afb47201ddc58785cb6dc0246947037072a5f",
|
||||
"src/CelerisLab/common/checkpoint.py": "b2f960aecd5ac27a3a7130b809ffad4709c44d522edef0359ab952654625410b",
|
||||
"src/CelerisLab/common/pathline.py": "a1b6efcc0a076d0f1dc8d49a595779426d7d5626b00072a59bac710ff02ad046",
|
||||
"src/CelerisLab/common/preprocess.py": "7fc78a08857cf8e98519500b094a1d896af189b6b15658ff9b388ed51bed4515",
|
||||
"src/CelerisLab/common/render.py": "0e055476257eb2bcd11e733766e52195de02f8fad228f570a3511a7c831bdc29",
|
||||
"src/CelerisLab/common/streakline/__init__.py": "13ff1cfa416fdd721a08a659e76b5a5a99b8d3201add10a83b7b0bc654bc1eec",
|
||||
"src/CelerisLab/common/streakline/_config.py": "a9185ef68e9c7b5d5a1dc0b04a28341e2ad91d13c145a7c106636a878cf0a605",
|
||||
"src/CelerisLab/common/streakline/_integrate.py": "3fbd4368b2c8d0e7fda40dc65aac5df02b085008b4c68acd83cb7ae45d33a13b",
|
||||
"src/CelerisLab/common/streakline/_render.py": "236cd637414ee318c4d66f9aed32110d8dc42a100d65fde06172f7cd0ba62c65",
|
||||
"src/CelerisLab/common/streakline/_streakline.py": "1a3e08daa706b5f83cdeb337e9a945a36ddf7583c40af1d0bb9d825ac954df37",
|
||||
"src/CelerisLab/config.py": "2f1bba7515f5bc08cb3a8d140bcafe7fa0689e01b3b06494697bb2956a71530e",
|
||||
"src/CelerisLab/configs/CONFIG.md": "41cafdbe525366c08aa9d0416ea026ccf0026ba7123b8a9b65f968a80d928e3a",
|
||||
"src/CelerisLab/configs/config_body.json": "055f261f0828ca1d4f2bb694bd15b72bbcabe31d552326208f28e37f761adab1",
|
||||
"src/CelerisLab/configs/config_lbm.json": "17e4311f1f250c6ece59177a8b6ff856b69a016c7c10267e774b030a0a390865",
|
||||
"src/CelerisLab/configs/config_lbm_flume_mrt_zouhe.json": "8f593321db05a563a42931603a228d61cbcba06dfbe113262d5cd0ad93c0fe79",
|
||||
"src/CelerisLab/configs/config_lbm_flume_trt_regularized.json": "f4bba8e6a4339fc3c4eb14f6e2f57c4b22993c73703c773d853edfbe34736e56",
|
||||
"src/CelerisLab/configs/config_lbm_three_cylinder_triangle.json": "eeb8e7149dbe4aa6b3a9562802735aa950750dfbe2d972a0727e788573afa817",
|
||||
"src/CelerisLab/cuda/__init__.py": "0d01dff9f0399c8baba822aab5ca0122a135f011008ed8a499ebd9f7e5b06aea",
|
||||
"src/CelerisLab/cuda/compiler_v2.py": "c4cc3ec4445c8d75e6a253dd87963877117a722bc73b9d4811806fc6045d08c1",
|
||||
"src/CelerisLab/cuda/context.py": "013dd4ec6d01d2fbd11afdfb103eac6a273bd9f798d663d761b851eeb27885e9",
|
||||
"src/CelerisLab/cuda/obs_layout.py": "5ffb2c3aeeaaaaeb64f2cd8febfbcf4ad9919e5414020ad753cfade0683b21c2",
|
||||
"src/CelerisLab/lbm/__init__.py": "7f60fb3aaae81501b510d08c2fedff44fcb05657260eb10b6f5cc91e7204306b",
|
||||
"src/CelerisLab/lbm/block_stepper.py": "00b313daddc6e63d8ab8423c9190ca7e92ad291de20a2f6b80eb53e3f2d85ec7",
|
||||
"src/CelerisLab/lbm/blocks.py": "1e644c5719993f418185ad4a10b6950a30636fc912befd9eab41ff48b4cf85cd",
|
||||
"src/CelerisLab/lbm/curved_links.py": "b6957fa13d13f864dba4f7364e93a39905d062dbb3ee9b3f5191add81fff0732",
|
||||
"src/CelerisLab/lbm/descriptors.py": "c9f025dfc14da43b989047b84cc22bd05d5cc92c9ec695c1da68fdfb788e3f2b",
|
||||
"src/CelerisLab/lbm/equilibrium.py": "2fb2f2bb7ff326d23abce80fc2a4c0907b17740ea3e29fe6ae079c5044783826",
|
||||
"src/CelerisLab/lbm/field.py": "0e727eacc3706add4d8d15c418e9b631050871f831698d3ac689dd5f2c701b9b",
|
||||
"src/CelerisLab/lbm/hierarchy.py": "f234975074f6b6d289d573ff6ea6832feae0e27cb04f80b37fe81792b13a12e7",
|
||||
"src/CelerisLab/lbm/initializers.py": "dacf9586963e862d6d0a98ef1a066036be6f91e9d423720661d8ef2c1fb2df6a",
|
||||
"src/CelerisLab/lbm/kernels/blocks.cu": "72d7803c859c974c24ed16b36df43d9e5799828be0fe589cd5bf7a0a5e5cf485",
|
||||
"src/CelerisLab/lbm/kernels/boundary/bounce_back.cuh": "aba3748c564ed91b161d268ef8b737b7c7be96a823063d201f3da684dd845233",
|
||||
"src/CelerisLab/lbm/kernels/boundary/curved_boundary.cuh": "aeb16bfd483514fb03e15a2fed437d673c3b8b2fe68ab83edf4244ef593878ea",
|
||||
"src/CelerisLab/lbm/kernels/boundary/esopull_curved_helpers.cuh": "b9b474bc4edaff05681c5443235472d04072d0d775fc0eb9c5967faef52b0258",
|
||||
"src/CelerisLab/lbm/kernels/boundary/ibm_kernels.cuh": "e6909b547dc8d64cdd8a76a90660243a8f5fee535caa161b52b4dd8b7c29934b",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/channel_stabilized.cuh": "efe077afcbb6b731256a987c2fd79a1dbc470c902f87070d60421ea6cb13d4e2",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/common.cuh": "f3cec54c8873c65b3dca4375605d2c89a8727765884d3954c235242fdff9c6ba",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/equilibrium.cuh": "f0964c811b2c41a5aaa1c6d99c490b06e850c634eb8749f1f05ef61f93c2de1d",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/regularized.cuh": "943d4a571818bacb89c70bccfaac202691121e3e385803ce6309123a0a03ddaf",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/zou_he_local.cuh": "e73e66b853b1e4e0e30d8f4185d832aa414ce670a1813ce2b5459f2887f71f89",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet_outlet.cuh": "de8b213a6da42c2e5e9f8966199fc66ea4541b85144a90f3f5dbad3c6ab63b97",
|
||||
"src/CelerisLab/lbm/kernels/boundary/outlet/pressure_neq.cuh": "c8b2adc66d12fe602076ddc9693c5c208711fcaddb58aa9a31401a269f51c4c4",
|
||||
"src/CelerisLab/lbm/kernels/boundary/planar_velocity.cuh": "501b2759d0e2e4280cb6fdd127d66a50fa89f6de48fc2cae9a7eafb11ae90bed",
|
||||
"src/CelerisLab/lbm/kernels/config/config_grid.h": "1db3e030ebdc116cd7f02362d1fd077daac55e8f0b4329f6285948c224d3ca5d",
|
||||
"src/CelerisLab/lbm/kernels/config/config_method.h": "4374e04a130f90f97d17cc20c40d2f9ea0ed0dd8603817d07f215d02266e247c",
|
||||
"src/CelerisLab/lbm/kernels/config/config_objects.h": "db793af052c756561a190a3ff2cb93ee5fe28462ee4f97c6af80882b4dc8b072",
|
||||
"src/CelerisLab/lbm/kernels/config/config_obs.h": "c11e2249359145b5c4cbaaf0487506fa77c1f4b82c7c169ddc574aa0babcd727",
|
||||
"src/CelerisLab/lbm/kernels/config/config_physics.h": "8ac0990b168e1058588676cc05c80df8634cfd671dbc92a89246c3c372d83a4e",
|
||||
"src/CelerisLab/lbm/kernels/config.h": "7d1124125e0b354558297934460e64c760fb68cdd7bf6d01a1238bb2eaab8c37",
|
||||
"src/CelerisLab/lbm/kernels/core/descriptors.cuh": "8440bc14857da9007982650f828a23608eec9f7cc4bf772e8c8dc1239cb45356",
|
||||
"src/CelerisLab/lbm/kernels/core/flags.cuh": "0025705f24710330d9d9195f713879bfb650a85a3449baef0e196b831af104ab",
|
||||
"src/CelerisLab/lbm/kernels/core/layout.cuh": "1fbd820021c89ac3efcde24145996cb0147769e607e498e61f308ed3e0b13706",
|
||||
"src/CelerisLab/lbm/kernels/core/obs.cuh": "b742b77c9f61e1f020fe9922c3838ae9d855e17b381154958b58f10a22938922",
|
||||
"src/CelerisLab/lbm/kernels/core/params.cuh": "6e18bf730d936d940e3d85452f0714a2eaae31bf41002f9fd520ec5b372878dd",
|
||||
"src/CelerisLab/lbm/kernels/core/precision.cuh": "849df14b4cce50af52399e4b8f3e997196f3ae5f1163f68e28df3e6b2507a8c4",
|
||||
"src/CelerisLab/lbm/kernels/kernel_v2.cu": "c7bd3fb705b77261ba2441c52a24a7e6a7865c165cba088a1b75201c4d29c408",
|
||||
"src/CelerisLab/lbm/kernels/operators/collision_mrt.cuh": "aee444eb60c47a3fd518dca975d1af05425a0818a0ec6c3fb3f6150f60c2cb44",
|
||||
"src/CelerisLab/lbm/kernels/operators/collision_srt.cuh": "6b7ff842f392c8f853d2d1a97cc69bfe50f21283f73e659404da07cf6c95f5e1",
|
||||
"src/CelerisLab/lbm/kernels/operators/collision_trt.cuh": "9725b9840b9ad8b4ec7be61ff5d06d3151e86d1a87585c13f2fbf934d0d2c5f6",
|
||||
"src/CelerisLab/lbm/kernels/operators/forcing_guo.cuh": "969a430c1af03f32e3bd0df95d805e64f56334717c44dd4ba208ba503062a24b",
|
||||
"src/CelerisLab/lbm/kernels/operators/helpers.cuh": "0b3a4d95d7d19f5fec09e5423b8f4cc99f8a226b541967425967330da5a051ec",
|
||||
"src/CelerisLab/lbm/kernels/operators/macro.cuh": "2be20eb2b7d7f5522daaad7f4b57b1ff8cdf883efe4a67846a96783b2fe846a3",
|
||||
"src/CelerisLab/lbm/kernels/operators/turbulence_smag.cuh": "ff7f000297ee0817e255ad9fcb6a13b647e3ddd5b206518d35f38d2895a9f501",
|
||||
"src/CelerisLab/lbm/kernels/refinement.cu": "129f3d91da93b4324f88a0da73a4da1e5390f72c14cce574ee63306712da9895",
|
||||
"src/CelerisLab/lbm/kernels/step/aux_kernels.cu": "578a5b20a157241ee678547059abb1372dd5a589ad06bb2c15b987eb01ca1013",
|
||||
"src/CelerisLab/lbm/kernels/step/curved_double.cu": "bfa7a1df1e939020314d49755045f7a85fe4a19cc1c68dbe62653c66c5e7c2b3",
|
||||
"src/CelerisLab/lbm/kernels/step/esopull_macro.cu": "c73116020ca217ede1e4de27c8269c31f9d91e33433ac8252a21e9b14187c50f",
|
||||
"src/CelerisLab/lbm/kernels/step/esopull_sync.cu": "c0ff3e72900c3ab89a05dd03684e96d3b740cf1672d565727fc2af165933d087",
|
||||
"src/CelerisLab/lbm/kernels/step/init_flow.cu": "305be3bb12fc0d039acec1701baabcf0e8a35e683c49c34f2af8ca9867d2b29c",
|
||||
"src/CelerisLab/lbm/kernels/step/one_step_double.cu": "f9f8242b38e8e0cf8425b75e2b0215bc15363c62eaa0fb43c761b8e0931bfdc9",
|
||||
"src/CelerisLab/lbm/kernels/step/one_step_esopull.cu": "aa223b2744ae6c9125673df311a96ce588032c1ba58bebb11fd33ecb990f8ed4",
|
||||
"src/CelerisLab/lbm/kernels/streaming/esopull_semantic_helpers.cuh": "dd5a9091628b079efd97e53fd49e52d09dcee88529235db77a5648e02937af6e",
|
||||
"src/CelerisLab/lbm/kernels/streaming/esopull_single_buffer.cuh": "77ed4ab9a0ebc5ff62a91a7c39066ef395391ae36e9040a69fb2bd8b1626dc79",
|
||||
"src/CelerisLab/lbm/kernels/streaming/pull_double_buffer.cuh": "b65d741ed78b7b62f99531ba0c0ce1936663d8dc9640e7d7d16c707f1baf6549",
|
||||
"src/CelerisLab/lbm/population_storage.py": "a6a86c29c52c892edfe4802fb773c387212e3f4cc86708d80e154986ddf88044",
|
||||
"src/CelerisLab/lbm/raw_exchange.py": "8694202466eba848563e298656353b0f5db94e21a01fa05f51aad6dc2b9fd311",
|
||||
"src/CelerisLab/lbm/refinement.py": "08c2a0557276a98cbcaa459d9bb7a9735d8bbbf64a6114c57af2fda61481a687",
|
||||
"src/CelerisLab/lbm/refinement_stepper.py": "16f778522c8e4dbb444b84d6381e94c047e0e05299541e589f0f365fb9a89c4e",
|
||||
"src/CelerisLab/lbm/refinement_tables.py": "fdd344024b89df3a83174dc810ca5e32db5e19eb8b1609f8f73b0821f56546b6",
|
||||
"src/CelerisLab/lbm/stepper.py": "0dcb7cd7ae87e33e3dc67a38b6aa43042fbe673e37d9ad165342ee1d695933ce",
|
||||
"src/CelerisLab/lbm/synchronized_remap.py": "7400fc4beaffd4649d6a9c524d38ce5fd6fa7eeb32eb5332e4030a4b6648f197",
|
||||
"src/CelerisLab/simulation.py": "0a03904dc6de88667d7af699005a289adf8b09f0976cb0d099b041f4830a8f4c"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"revision": "d79e3f6f0a424f5bcc3a1a6d610313e49daf97a2",
|
||||
"files": {
|
||||
"src/CelerisLab/__init__.py": "21cc469bd6e970a1f9ff5970c8eea4602065e41fc881854759ee565ee55c10c6",
|
||||
"src/CelerisLab/body/__init__.py": "e4e0e8f0769a94428d5b02fac790443997556cfc5515a5f1ed1595d2c694d69d",
|
||||
"src/CelerisLab/body/action_smoother.py": "b1a23ee6bbd4850b172cb8738c5c3546ee465a54189e6af9d8044f023d4687dc",
|
||||
"src/CelerisLab/body/coupling/__init__.py": "c931f7eb318d48408c3e2febcc6917c88c8dcc1697cbf4329b1f340d94c35763",
|
||||
"src/CelerisLab/body/coupling/soa_packer.py": "8ec26fd55ef74801bbd5ab6bd3d0a7dabc750cbd498aaac2ca4c61a0108ad3c9",
|
||||
"src/CelerisLab/body/ddf_patch.py": "60fcfb7441e73bf290bcff41ca3a83da3e7ddad765d7247464e8f02e3043b4e2",
|
||||
"src/CelerisLab/body/geometry/__init__.py": "ada88980ab672959aca9aa3a4c4aade3219524b2ab68110cd2a2f41e3e0ca90a",
|
||||
"src/CelerisLab/body/geometry/base.py": "cceca14361ca2f34d34a3e43730e1a79b8ca81be00f0dd62e884db32b479419f",
|
||||
"src/CelerisLab/body/geometry/circle.py": "7605ca7c21103f1d801ac73ab8dd18dffbdac425b2dee7b8858b283628a9e436",
|
||||
"src/CelerisLab/body/jets.py": "4104c24af4651f4bb4f8d8fd36fb6d88f41c6e4469822f36e22e74c9ede31dac",
|
||||
"src/CelerisLab/body/manager.py": "01b653d25bd9b4333379c8bfd12383596b686d2029f2e91b1c91b618540ec6b3",
|
||||
"src/CelerisLab/body/objects.py": "8e63cee8faa9cc6243bcff428be628dfad73a02a9a3b8ece7d78c0a63406a08d",
|
||||
"src/CelerisLab/body/preprocess/__init__.py": "dfd9d58bc07f626ab23a70053e31b88a6d9165d2e4c76a4e9f48abe674d3d2ec",
|
||||
"src/CelerisLab/body/preprocess/flag_overlay.py": "831fc9e9736b758847759a085ff133eb49ec91318ace4fda76f85960138ee300",
|
||||
"src/CelerisLab/body/registry.py": "fbb7327780adbdc6da15ebdb799980daf32df22ee974af0893d7409f177bbb68",
|
||||
"src/CelerisLab/body/sync_plan.py": "3a15bfe9ebeb91482dbfc68850761ef39c872cc12d2efc795babd0c01e392323",
|
||||
"src/CelerisLab/common/__init__.py": "d4dbde78bc97fccc9eabac9abae8f0f15c112c10ff8e9b45bd9401d4723df1ef",
|
||||
"src/CelerisLab/common/_types.py": "aad2ff1a9033d4fd68783ad7123afb47201ddc58785cb6dc0246947037072a5f",
|
||||
"src/CelerisLab/common/checkpoint.py": "b2f960aecd5ac27a3a7130b809ffad4709c44d522edef0359ab952654625410b",
|
||||
"src/CelerisLab/common/pathline.py": "a1b6efcc0a076d0f1dc8d49a595779426d7d5626b00072a59bac710ff02ad046",
|
||||
"src/CelerisLab/common/preprocess.py": "7fc78a08857cf8e98519500b094a1d896af189b6b15658ff9b388ed51bed4515",
|
||||
"src/CelerisLab/common/render.py": "0e055476257eb2bcd11e733766e52195de02f8fad228f570a3511a7c831bdc29",
|
||||
"src/CelerisLab/common/streakline/__init__.py": "13ff1cfa416fdd721a08a659e76b5a5a99b8d3201add10a83b7b0bc654bc1eec",
|
||||
"src/CelerisLab/common/streakline/_config.py": "a9185ef68e9c7b5d5a1dc0b04a28341e2ad91d13c145a7c106636a878cf0a605",
|
||||
"src/CelerisLab/common/streakline/_integrate.py": "3fbd4368b2c8d0e7fda40dc65aac5df02b085008b4c68acd83cb7ae45d33a13b",
|
||||
"src/CelerisLab/common/streakline/_render.py": "236cd637414ee318c4d66f9aed32110d8dc42a100d65fde06172f7cd0ba62c65",
|
||||
"src/CelerisLab/common/streakline/_streakline.py": "1a3e08daa706b5f83cdeb337e9a945a36ddf7583c40af1d0bb9d825ac954df37",
|
||||
"src/CelerisLab/config.py": "2f1bba7515f5bc08cb3a8d140bcafe7fa0689e01b3b06494697bb2956a71530e",
|
||||
"src/CelerisLab/configs/CONFIG.md": "41cafdbe525366c08aa9d0416ea026ccf0026ba7123b8a9b65f968a80d928e3a",
|
||||
"src/CelerisLab/configs/config_body.json": "055f261f0828ca1d4f2bb694bd15b72bbcabe31d552326208f28e37f761adab1",
|
||||
"src/CelerisLab/configs/config_lbm.json": "17e4311f1f250c6ece59177a8b6ff856b69a016c7c10267e774b030a0a390865",
|
||||
"src/CelerisLab/configs/config_lbm_flume_mrt_zouhe.json": "8f593321db05a563a42931603a228d61cbcba06dfbe113262d5cd0ad93c0fe79",
|
||||
"src/CelerisLab/configs/config_lbm_flume_trt_regularized.json": "f4bba8e6a4339fc3c4eb14f6e2f57c4b22993c73703c773d853edfbe34736e56",
|
||||
"src/CelerisLab/configs/config_lbm_three_cylinder_triangle.json": "eeb8e7149dbe4aa6b3a9562802735aa950750dfbe2d972a0727e788573afa817",
|
||||
"src/CelerisLab/cuda/__init__.py": "0d01dff9f0399c8baba822aab5ca0122a135f011008ed8a499ebd9f7e5b06aea",
|
||||
"src/CelerisLab/cuda/compiler_v2.py": "c4cc3ec4445c8d75e6a253dd87963877117a722bc73b9d4811806fc6045d08c1",
|
||||
"src/CelerisLab/cuda/context.py": "013dd4ec6d01d2fbd11afdfb103eac6a273bd9f798d663d761b851eeb27885e9",
|
||||
"src/CelerisLab/cuda/obs_layout.py": "5ffb2c3aeeaaaaeb64f2cd8febfbcf4ad9919e5414020ad753cfade0683b21c2",
|
||||
"src/CelerisLab/lbm/__init__.py": "7f60fb3aaae81501b510d08c2fedff44fcb05657260eb10b6f5cc91e7204306b",
|
||||
"src/CelerisLab/lbm/block_stepper.py": "00b313daddc6e63d8ab8423c9190ca7e92ad291de20a2f6b80eb53e3f2d85ec7",
|
||||
"src/CelerisLab/lbm/blocks.py": "1e644c5719993f418185ad4a10b6950a30636fc912befd9eab41ff48b4cf85cd",
|
||||
"src/CelerisLab/lbm/curved_links.py": "b6957fa13d13f864dba4f7364e93a39905d062dbb3ee9b3f5191add81fff0732",
|
||||
"src/CelerisLab/lbm/descriptors.py": "c9f025dfc14da43b989047b84cc22bd05d5cc92c9ec695c1da68fdfb788e3f2b",
|
||||
"src/CelerisLab/lbm/equilibrium.py": "2fb2f2bb7ff326d23abce80fc2a4c0907b17740ea3e29fe6ae079c5044783826",
|
||||
"src/CelerisLab/lbm/field.py": "0e727eacc3706add4d8d15c418e9b631050871f831698d3ac689dd5f2c701b9b",
|
||||
"src/CelerisLab/lbm/hierarchy.py": "f234975074f6b6d289d573ff6ea6832feae0e27cb04f80b37fe81792b13a12e7",
|
||||
"src/CelerisLab/lbm/initializers.py": "dacf9586963e862d6d0a98ef1a066036be6f91e9d423720661d8ef2c1fb2df6a",
|
||||
"src/CelerisLab/lbm/kernels/blocks.cu": "72d7803c859c974c24ed16b36df43d9e5799828be0fe589cd5bf7a0a5e5cf485",
|
||||
"src/CelerisLab/lbm/kernels/boundary/bounce_back.cuh": "aba3748c564ed91b161d268ef8b737b7c7be96a823063d201f3da684dd845233",
|
||||
"src/CelerisLab/lbm/kernels/boundary/curved_boundary.cuh": "aeb16bfd483514fb03e15a2fed437d673c3b8b2fe68ab83edf4244ef593878ea",
|
||||
"src/CelerisLab/lbm/kernels/boundary/esopull_curved_helpers.cuh": "b9b474bc4edaff05681c5443235472d04072d0d775fc0eb9c5967faef52b0258",
|
||||
"src/CelerisLab/lbm/kernels/boundary/ibm_kernels.cuh": "e6909b547dc8d64cdd8a76a90660243a8f5fee535caa161b52b4dd8b7c29934b",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/channel_stabilized.cuh": "efe077afcbb6b731256a987c2fd79a1dbc470c902f87070d60421ea6cb13d4e2",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/common.cuh": "f3cec54c8873c65b3dca4375605d2c89a8727765884d3954c235242fdff9c6ba",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/equilibrium.cuh": "f0964c811b2c41a5aaa1c6d99c490b06e850c634eb8749f1f05ef61f93c2de1d",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/regularized.cuh": "943d4a571818bacb89c70bccfaac202691121e3e385803ce6309123a0a03ddaf",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet/zou_he_local.cuh": "e73e66b853b1e4e0e30d8f4185d832aa414ce670a1813ce2b5459f2887f71f89",
|
||||
"src/CelerisLab/lbm/kernels/boundary/inlet_outlet.cuh": "de8b213a6da42c2e5e9f8966199fc66ea4541b85144a90f3f5dbad3c6ab63b97",
|
||||
"src/CelerisLab/lbm/kernels/boundary/outlet/pressure_neq.cuh": "c8b2adc66d12fe602076ddc9693c5c208711fcaddb58aa9a31401a269f51c4c4",
|
||||
"src/CelerisLab/lbm/kernels/boundary/planar_velocity.cuh": "501b2759d0e2e4280cb6fdd127d66a50fa89f6de48fc2cae9a7eafb11ae90bed",
|
||||
"src/CelerisLab/lbm/kernels/config.h": "7d1124125e0b354558297934460e64c760fb68cdd7bf6d01a1238bb2eaab8c37",
|
||||
"src/CelerisLab/lbm/kernels/config/config_grid.h": "1db3e030ebdc116cd7f02362d1fd077daac55e8f0b4329f6285948c224d3ca5d",
|
||||
"src/CelerisLab/lbm/kernels/config/config_method.h": "4374e04a130f90f97d17cc20c40d2f9ea0ed0dd8603817d07f215d02266e247c",
|
||||
"src/CelerisLab/lbm/kernels/config/config_objects.h": "db793af052c756561a190a3ff2cb93ee5fe28462ee4f97c6af80882b4dc8b072",
|
||||
"src/CelerisLab/lbm/kernels/config/config_obs.h": "c11e2249359145b5c4cbaaf0487506fa77c1f4b82c7c169ddc574aa0babcd727",
|
||||
"src/CelerisLab/lbm/kernels/config/config_physics.h": "8ac0990b168e1058588676cc05c80df8634cfd671dbc92a89246c3c372d83a4e",
|
||||
"src/CelerisLab/lbm/kernels/core/descriptors.cuh": "8440bc14857da9007982650f828a23608eec9f7cc4bf772e8c8dc1239cb45356",
|
||||
"src/CelerisLab/lbm/kernels/core/flags.cuh": "0025705f24710330d9d9195f713879bfb650a85a3449baef0e196b831af104ab",
|
||||
"src/CelerisLab/lbm/kernels/core/layout.cuh": "1fbd820021c89ac3efcde24145996cb0147769e607e498e61f308ed3e0b13706",
|
||||
"src/CelerisLab/lbm/kernels/core/obs.cuh": "b742b77c9f61e1f020fe9922c3838ae9d855e17b381154958b58f10a22938922",
|
||||
"src/CelerisLab/lbm/kernels/core/params.cuh": "6e18bf730d936d940e3d85452f0714a2eaae31bf41002f9fd520ec5b372878dd",
|
||||
"src/CelerisLab/lbm/kernels/core/precision.cuh": "849df14b4cce50af52399e4b8f3e997196f3ae5f1163f68e28df3e6b2507a8c4",
|
||||
"src/CelerisLab/lbm/kernels/kernel_v2.cu": "c7bd3fb705b77261ba2441c52a24a7e6a7865c165cba088a1b75201c4d29c408",
|
||||
"src/CelerisLab/lbm/kernels/operators/collision_mrt.cuh": "aee444eb60c47a3fd518dca975d1af05425a0818a0ec6c3fb3f6150f60c2cb44",
|
||||
"src/CelerisLab/lbm/kernels/operators/collision_srt.cuh": "6b7ff842f392c8f853d2d1a97cc69bfe50f21283f73e659404da07cf6c95f5e1",
|
||||
"src/CelerisLab/lbm/kernels/operators/collision_trt.cuh": "9725b9840b9ad8b4ec7be61ff5d06d3151e86d1a87585c13f2fbf934d0d2c5f6",
|
||||
"src/CelerisLab/lbm/kernels/operators/forcing_guo.cuh": "969a430c1af03f32e3bd0df95d805e64f56334717c44dd4ba208ba503062a24b",
|
||||
"src/CelerisLab/lbm/kernels/operators/helpers.cuh": "0b3a4d95d7d19f5fec09e5423b8f4cc99f8a226b541967425967330da5a051ec",
|
||||
"src/CelerisLab/lbm/kernels/operators/macro.cuh": "2be20eb2b7d7f5522daaad7f4b57b1ff8cdf883efe4a67846a96783b2fe846a3",
|
||||
"src/CelerisLab/lbm/kernels/operators/turbulence_smag.cuh": "ff7f000297ee0817e255ad9fcb6a13b647e3ddd5b206518d35f38d2895a9f501",
|
||||
"src/CelerisLab/lbm/kernels/refinement.cu": "129f3d91da93b4324f88a0da73a4da1e5390f72c14cce574ee63306712da9895",
|
||||
"src/CelerisLab/lbm/kernels/step/aux_kernels.cu": "578a5b20a157241ee678547059abb1372dd5a589ad06bb2c15b987eb01ca1013",
|
||||
"src/CelerisLab/lbm/kernels/step/curved_double.cu": "bfa7a1df1e939020314d49755045f7a85fe4a19cc1c68dbe62653c66c5e7c2b3",
|
||||
"src/CelerisLab/lbm/kernels/step/esopull_macro.cu": "c73116020ca217ede1e4de27c8269c31f9d91e33433ac8252a21e9b14187c50f",
|
||||
"src/CelerisLab/lbm/kernels/step/esopull_sync.cu": "c0ff3e72900c3ab89a05dd03684e96d3b740cf1672d565727fc2af165933d087",
|
||||
"src/CelerisLab/lbm/kernels/step/init_flow.cu": "305be3bb12fc0d039acec1701baabcf0e8a35e683c49c34f2af8ca9867d2b29c",
|
||||
"src/CelerisLab/lbm/kernels/step/one_step_double.cu": "f9f8242b38e8e0cf8425b75e2b0215bc15363c62eaa0fb43c761b8e0931bfdc9",
|
||||
"src/CelerisLab/lbm/kernels/step/one_step_esopull.cu": "aa223b2744ae6c9125673df311a96ce588032c1ba58bebb11fd33ecb990f8ed4",
|
||||
"src/CelerisLab/lbm/kernels/streaming/esopull_semantic_helpers.cuh": "dd5a9091628b079efd97e53fd49e52d09dcee88529235db77a5648e02937af6e",
|
||||
"src/CelerisLab/lbm/kernels/streaming/esopull_single_buffer.cuh": "77ed4ab9a0ebc5ff62a91a7c39066ef395391ae36e9040a69fb2bd8b1626dc79",
|
||||
"src/CelerisLab/lbm/kernels/streaming/pull_double_buffer.cuh": "b65d741ed78b7b62f99531ba0c0ce1936663d8dc9640e7d7d16c707f1baf6549",
|
||||
"src/CelerisLab/lbm/population_storage.py": "a6a86c29c52c892edfe4802fb773c387212e3f4cc86708d80e154986ddf88044",
|
||||
"src/CelerisLab/lbm/raw_exchange.py": "8694202466eba848563e298656353b0f5db94e21a01fa05f51aad6dc2b9fd311",
|
||||
"src/CelerisLab/lbm/refinement.py": "08c2a0557276a98cbcaa459d9bb7a9735d8bbbf64a6114c57af2fda61481a687",
|
||||
"src/CelerisLab/lbm/refinement_stepper.py": "16f778522c8e4dbb444b84d6381e94c047e0e05299541e589f0f365fb9a89c4e",
|
||||
"src/CelerisLab/lbm/refinement_tables.py": "fdd344024b89df3a83174dc810ca5e32db5e19eb8b1609f8f73b0821f56546b6",
|
||||
"src/CelerisLab/lbm/stepper.py": "0dcb7cd7ae87e33e3dc67a38b6aa43042fbe673e37d9ad165342ee1d695933ce",
|
||||
"src/CelerisLab/lbm/synchronized_remap.py": "7400fc4beaffd4649d6a9c524d38ce5fd6fa7eeb32eb5332e4030a4b6648f197",
|
||||
"src/CelerisLab/simulation.py": "0a03904dc6de88667d7af699005a289adf8b09f0976cb0d099b041f4830a8f4c"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
"""CPU-only cycle extraction. All criteria frozen before any Re100 GPU result."""
|
||||
|
||||
import numpy as np
|
||||
|
||||
GATES = dict(
|
||||
min_tstar=250.0,
|
||||
cycles=5,
|
||||
min_period=2.0,
|
||||
max_period=5.0,
|
||||
min_lift_amplitude=0.2,
|
||||
period_relative_range=0.005,
|
||||
extrema_absolute_range=0.005,
|
||||
mass_relative_range=1e-4,
|
||||
cycle_mean_flux_relative=0.005,
|
||||
)
|
||||
REFERENCE_GATES = dict(
|
||||
cd_relative=0.02, cl_absolute=0.03, dp_relative=0.02, st_relative=0.02
|
||||
)
|
||||
|
||||
|
||||
def minima(t, y):
|
||||
out = []
|
||||
for i in range(1, len(y) - 1):
|
||||
if y[i] < y[i - 1] and y[i] <= y[i + 1]:
|
||||
# Quadratic sub-sample location; only separated physical cycles accepted.
|
||||
coef = np.polyfit(t[i - 1 : i + 2] - t[i], y[i - 1 : i + 2], 2)
|
||||
v = t[i] - coef[1] / (2 * coef[0]) if coef[0] > 0 else t[i]
|
||||
if t[i - 1] <= v <= t[i + 1] and (not out or v - out[-1] > 1.0):
|
||||
out.append(float(v))
|
||||
return out
|
||||
|
||||
|
||||
def extract(rows, min_time=250.0):
|
||||
if len(rows) < 3:
|
||||
return []
|
||||
t = np.array([r["tstar_force"] for r in rows])
|
||||
cl = np.array([r["cl"] for r in rows])
|
||||
ends = minima(t, cl)
|
||||
cycles = []
|
||||
for a, b in zip(ends, ends[1:]):
|
||||
if a < min_time or not GATES["min_period"] < b - a < GATES["max_period"]:
|
||||
continue
|
||||
grid = np.linspace(a, b, 257)
|
||||
c = dict(start=a, end=b, period=b - a, st=1 / (b - a))
|
||||
for key in ("cd", "cl", "dp", "mass", "flux_in", "flux_out"):
|
||||
times = np.array(
|
||||
[r["tstar" if key not in ("cd", "cl") else "tstar_force"] for r in rows]
|
||||
)
|
||||
# No endpoint extrapolation: pressure/mass must cover the whole cycle.
|
||||
if a < times[0] or b > times[-1]:
|
||||
break
|
||||
v = np.interp(grid, times, [r[key] for r in rows])
|
||||
c[key + "_min"] = float(v.min())
|
||||
c[key + "_max"] = float(v.max())
|
||||
c[key + "_midrange"] = float((v.min() + v.max()) / 2)
|
||||
c[key + "_amplitude"] = float(v.max() - v.min())
|
||||
c[key + "_time_mean"] = float(
|
||||
np.sum((v[1:] + v[:-1]) * np.diff(grid) / 2) / (b - a)
|
||||
)
|
||||
if key in ("cd", "cl", "dp"):
|
||||
c[key + "_waveform"] = v.tolist()
|
||||
else:
|
||||
c["flux_relative"] = abs(
|
||||
c["flux_in_time_mean"] - c["flux_out_time_mean"]
|
||||
) / max(abs(c["flux_in_time_mean"]), 1e-30)
|
||||
cycles.append(c)
|
||||
return cycles
|
||||
|
||||
|
||||
def assess(rows):
|
||||
cycles = extract(rows, min_time=GATES["min_tstar"])
|
||||
tail = cycles[-GATES["cycles"] :]
|
||||
checks = {}
|
||||
checks["enough_cycles"] = len(tail) == GATES["cycles"]
|
||||
if checks["enough_cycles"]:
|
||||
checks["consecutive"] = all(
|
||||
abs(a["end"] - b["start"]) < 1e-8 for a, b in zip(tail, tail[1:])
|
||||
)
|
||||
checks["recent"] = (
|
||||
0 <= rows[-1]["tstar_force"] - tail[-1]["end"] <= GATES["max_period"]
|
||||
)
|
||||
checks["shedding"] = (
|
||||
min(c["cl_amplitude"] for c in tail) >= GATES["min_lift_amplitude"]
|
||||
)
|
||||
periods = [c["period"] for c in tail]
|
||||
checks["period"] = (
|
||||
np.ptp(periods) / np.mean(periods) <= GATES["period_relative_range"]
|
||||
)
|
||||
checks["extrema"] = all(
|
||||
np.ptp([c[k + "_" + e] for c in tail]) <= GATES["extrema_absolute_range"]
|
||||
for k in ("cd", "cl", "dp")
|
||||
for e in ("min", "max")
|
||||
)
|
||||
mass = [c["mass_time_mean"] for c in tail]
|
||||
checks["mass"] = (
|
||||
np.ptp(mass) / abs(np.mean(mass)) <= GATES["mass_relative_range"]
|
||||
)
|
||||
checks["flux"] = (
|
||||
max(c["flux_relative"] for c in tail) <= GATES["cycle_mean_flux_relative"]
|
||||
)
|
||||
return dict(
|
||||
periodic=bool(checks["enough_cycles"] and all(checks.values())),
|
||||
checks={k: bool(v) for k, v in checks.items()},
|
||||
cycles=cycles,
|
||||
)
|
||||
|
||||
|
||||
def compare(cycle, ref):
|
||||
errors = {}
|
||||
passed = True
|
||||
for key in ("cd", "cl", "dp"):
|
||||
for ext in ("min", "max"):
|
||||
k = key + "_" + ext
|
||||
e = abs(cycle[k] - ref[k])
|
||||
e = e if key == "cl" else e / abs(ref[k])
|
||||
errors[k] = e
|
||||
passed &= (
|
||||
e
|
||||
<= REFERENCE_GATES[key + ("_absolute" if key == "cl" else "_relative")]
|
||||
)
|
||||
errors[key + "_waveform_rms"] = float(
|
||||
np.sqrt(
|
||||
np.mean(
|
||||
(np.array(cycle[key + "_waveform"]) - ref[key + "_waveform"]) ** 2
|
||||
)
|
||||
)
|
||||
)
|
||||
errors["st_relative"] = abs(cycle["st"] - ref["st"]) / ref["st"]
|
||||
passed &= errors["st_relative"] <= REFERENCE_GATES["st_relative"]
|
||||
return dict(reference_agreement=bool(passed), errors=errors)
|
||||
@@ -0,0 +1,127 @@
|
||||
"""DFG Re100 periodic reference comparison with declared pressure-NEQ outlet, not exact do-nothing.
|
||||
|
||||
CPU helpers import without CUDA. GPU execution uses unchanged d79e3f6 production
|
||||
sources. Default pilot is deliberately too short to certify periodic convergence.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import time
|
||||
from tempfile import TemporaryDirectory
|
||||
import numpy as np
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
PIN = "d79e3f6f0a424f5bcc3a1a6d610313e49daf97a2"
|
||||
|
||||
|
||||
def geometry(n):
|
||||
if isinstance(n, bool) or n < 10 or n % 10:
|
||||
raise ValueError("Diameter must be a positive multiple of 10, at least 10")
|
||||
return dict(
|
||||
diameter=n,
|
||||
nx=22 * n + 1,
|
||||
ny=41 * n // 10 + 2,
|
||||
center=[2 * n, 2 * n + 0.5],
|
||||
radius=n / 2,
|
||||
dx=0.1 / n,
|
||||
bottom=0.5,
|
||||
top=41 * n / 10 + 0.5,
|
||||
inlet_source_x=0.0,
|
||||
outlet_source_x=22.0 * n,
|
||||
)
|
||||
|
||||
|
||||
def parameters(n, speed):
|
||||
g = geometry(n)
|
||||
if not 0 < speed <= 0.04:
|
||||
raise ValueError("Require 0 < lattice mean speed <= .04")
|
||||
return dict(
|
||||
**g,
|
||||
mean_speed=speed,
|
||||
peak_speed=1.5 * speed,
|
||||
viscosity=speed * n / 100,
|
||||
dt=speed * g["dx"],
|
||||
peak_mach=1.5 * speed * math.sqrt(3),
|
||||
re=100.0,
|
||||
)
|
||||
|
||||
|
||||
def make_config(n, speed, collision, outlet, inlet="regularized"):
|
||||
p = parameters(n, speed)
|
||||
cfg = json.loads((ROOT / "src/CelerisLab/configs/config_lbm.json").read_text())
|
||||
cfg["grid"].update(nx=p["nx"], ny=p["ny"], nz=1, lattice_model="D2Q9")
|
||||
cfg["physics"].update(velocity=speed, viscosity=p["viscosity"], rho=1.0)
|
||||
cfg["method"].update(
|
||||
collision=collision,
|
||||
streaming="double_buffer",
|
||||
store_precision="FP32",
|
||||
ddf_shifting=True,
|
||||
y_wall_bc="bounce_back",
|
||||
)
|
||||
cfg["method"]["les"]["enabled"] = False
|
||||
cfg["method"]["inlet"].update(profile="parabolic", scheme=inlet)
|
||||
cfg["method"]["outlet"].update(mode=outlet, backflow_clamp=False, blend_alpha=0.7)
|
||||
body = {
|
||||
"objects": [
|
||||
dict(type="cylinder", center=p["center"], radius=p["radius"], omega=0.0)
|
||||
]
|
||||
}
|
||||
return cfg, body, p
|
||||
|
||||
|
||||
def bilinear_fluid(a, fluid, x, y):
|
||||
ix, iy = math.floor(x), math.floor(y)
|
||||
tx, ty = x - ix, y - iy
|
||||
result = 0.0
|
||||
for xx, yy, w in (
|
||||
(ix, iy, (1 - tx) * (1 - ty)),
|
||||
(ix + 1, iy, tx * (1 - ty)),
|
||||
(ix, iy + 1, (1 - tx) * ty),
|
||||
(ix + 1, iy + 1, tx * ty),
|
||||
):
|
||||
if w == 0:
|
||||
continue
|
||||
if not (0 <= yy < a.shape[0] and 0 <= xx < a.shape[1]) or not fluid[yy, xx]:
|
||||
raise ValueError("Pressure interpolation touched a non-fluid/outside node")
|
||||
result += w * float(a[yy, xx])
|
||||
return result
|
||||
|
||||
|
||||
def pressure_probes(rho, fluid, p):
|
||||
# Linear extrapolation from distances h and 2h outside the cylinder to
|
||||
# each exact wall probe, after bilinear interpolation using only fluid.
|
||||
cx, cy = p["center"]
|
||||
r = p["radius"]
|
||||
values = []
|
||||
for sign in (-1, 1):
|
||||
wall = cx + sign * r
|
||||
near = bilinear_fluid(rho, fluid, wall + sign, cy)
|
||||
far = bilinear_fluid(rho, fluid, wall + 2 * sign, cy)
|
||||
values.append(2 * near - far)
|
||||
scale = (1.0 / p["mean_speed"]) ** 2 / 3
|
||||
return dict(
|
||||
rho_front=values[0], rho_back=values[1], dp=(values[0] - values[1]) * scale
|
||||
)
|
||||
|
||||
|
||||
def outlet_diagnostic(rho, ux, uy, p):
|
||||
# Extrapolate fluid values to the declared x=NX-1 source plane. Diagnostic
|
||||
# excludes two rows beside each wall; not an exact discrete BC identity.
|
||||
rows = slice(3, -3)
|
||||
i = -2
|
||||
dudx = (5 * ux[rows, i] - 8 * ux[rows, i - 1] + 3 * ux[rows, i - 2]) / 2
|
||||
dvdx = (5 * uy[rows, i] - 8 * uy[rows, i - 1] + 3 * uy[rows, i - 2]) / 2
|
||||
rho_plane = 2 * rho[rows, i] - rho[rows, i - 1]
|
||||
pressure = (rho_plane - 1) / 3
|
||||
scale = p["mean_speed"] ** 2
|
||||
residual = np.stack((p["viscosity"] * dudx - pressure, p["viscosity"] * dvdx))
|
||||
return dict(
|
||||
rms_over_rho_U2=float(np.sqrt(np.mean(residual**2)) / scale),
|
||||
max_over_rho_U2=float(np.max(abs(residual)) / scale),
|
||||
definition="fluid-side one-sided estimate of nu*d_x(u)-p*n at declared outlet",
|
||||
)
|
||||
@@ -0,0 +1,218 @@
|
||||
"""Uniform DFG 2D-2 Re100, existing Zou-He inlet / noncanonical pressure-NEQ outlet."""
|
||||
|
||||
import argparse, csv, hashlib, json, math, os, time
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
import numpy as np
|
||||
from re100_helpers import ROOT, PIN, make_config, pressure_probes, outlet_diagnostic
|
||||
from periodic import assess, compare, GATES, REFERENCE_GATES
|
||||
|
||||
|
||||
def run(a):
|
||||
import CelerisLab
|
||||
from CelerisLab.simulation import Simulation
|
||||
from CelerisLab.lbm.descriptors import FLUID
|
||||
|
||||
if Path(CelerisLab.__file__).resolve() != ROOT / "src/CelerisLab/__init__.py":
|
||||
raise RuntimeError("Wrong source checkout")
|
||||
manifest = json.loads(
|
||||
(ROOT / "tests/validation/data/dfg_re100/source_manifest.json").read_text()
|
||||
)
|
||||
for rel, digest in manifest["production_sha256"].items():
|
||||
if hashlib.sha256((ROOT / rel).read_bytes()).hexdigest() != digest:
|
||||
raise RuntimeError("Production mismatch: " + rel)
|
||||
if a.output.exists() and any(a.output.iterdir()):
|
||||
raise ValueError("Output must be empty/new")
|
||||
a.output.mkdir(parents=True, exist_ok=True)
|
||||
cfg, body, p = make_config(a.diameter, a.speed, "MRT", "neq_extrap", "zou_he_local")
|
||||
cap = 5.0 if a.mode == "pilot" else 300.0
|
||||
max_steps = math.ceil(cap * p["diameter"] / p["mean_speed"])
|
||||
result = dict(
|
||||
case="DFG 2D-2 Re100, pressure-NEQ outlet deviation",
|
||||
pin=PIN,
|
||||
production_verified=True,
|
||||
parameters=p,
|
||||
mode=a.mode,
|
||||
cap_tstar=cap,
|
||||
inlet=cfg["method"]["inlet"],
|
||||
outlet=cfg["method"]["outlet"],
|
||||
canonical_outlet="nu*d_n(u)-p*n=0; NOT imposed exactly",
|
||||
initialization="unchanged production channel-flow initialization, not zero initial velocity",
|
||||
force_phase="batch-averaged link momentum exchange, timestamp at batch midpoint",
|
||||
pressure_phase="postcollision end-sample; interpolated onto force-cycle times for comparison",
|
||||
gates=GATES,
|
||||
reference_gates=REFERENCE_GATES,
|
||||
status="running",
|
||||
history=[],
|
||||
suite_validated=False,
|
||||
)
|
||||
reference = json.loads(
|
||||
(ROOT / "tests/validation/data/dfg_re100/reference_cycle.json").read_text()
|
||||
)
|
||||
started = time.monotonic()
|
||||
step_seconds = 0.0
|
||||
healthy = True
|
||||
with TemporaryDirectory() as tmp:
|
||||
cp, bp = Path(tmp) / "lbm.json", Path(tmp) / "body.json"
|
||||
cp.write_text(json.dumps(cfg))
|
||||
bp.write_text(json.dumps(body))
|
||||
with Simulation(str(cp), str(bp), device_id=0) as sim:
|
||||
sim.initialize()
|
||||
result["startup_seconds"] = time.monotonic() - started
|
||||
fluid = (sim.field.flag.reshape(p["ny"], p["nx"]) & FLUID) != 0
|
||||
np.savez_compressed(
|
||||
a.output / "geometry.npz",
|
||||
flag=sim.field.flag.reshape(p["ny"], p["nx"]),
|
||||
x=np.arange(p["nx"]) * p["dx"],
|
||||
y=(np.arange(p["ny"]) - 0.5) * p["dx"],
|
||||
)
|
||||
with (a.output / "history.csv").open("w") as f:
|
||||
writer = None
|
||||
while sim.stepper.step_count < max_steps:
|
||||
begin = sim.stepper.step_count
|
||||
tstar = begin * p["mean_speed"] / p["diameter"]
|
||||
# Pilot exercises the expensive dense measurement path too.
|
||||
width = 0.02 if a.mode == "pilot" or tstar >= 240 else 0.5
|
||||
n = min(
|
||||
max(1, round(width * p["diameter"] / p["mean_speed"])),
|
||||
max_steps - begin,
|
||||
)
|
||||
impulse = np.zeros(2)
|
||||
timer = time.monotonic()
|
||||
done = 0
|
||||
while done < n:
|
||||
batch = min(100, n - done)
|
||||
sim.run(batch)
|
||||
impulse += sim.read_force(0).astype(np.float64) * batch
|
||||
done += batch
|
||||
step_seconds += time.monotonic() - timer
|
||||
macro = sim.get_macroscopic()
|
||||
rho, ux, uy = [
|
||||
np.asarray(macro[k], np.float64).reshape(p["ny"], p["nx"])
|
||||
for k in ("rho", "ux", "uy")
|
||||
]
|
||||
healthy = bool(
|
||||
all(np.isfinite(v[fluid]).all() for v in (rho, ux, uy))
|
||||
and np.isfinite(impulse).all()
|
||||
and (rho[fluid] > 0).all()
|
||||
)
|
||||
if not healthy:
|
||||
break
|
||||
den = p["mean_speed"] ** 2 * p["diameter"] / 2
|
||||
row = dict(
|
||||
step=sim.stepper.step_count,
|
||||
tstar=sim.stepper.step_count * p["mean_speed"] / p["diameter"],
|
||||
tstar_force=(begin + n / 2) * p["mean_speed"] / p["diameter"],
|
||||
cd=float(impulse[0] / n / den),
|
||||
cl=float(impulse[1] / n / den),
|
||||
mass=float(rho[fluid].sum()),
|
||||
rho_min=float(rho[fluid].min()),
|
||||
rho_max=float(rho[fluid].max()),
|
||||
max_mach=float(
|
||||
np.sqrt(3 * (ux[fluid] ** 2 + uy[fluid] ** 2)).max()
|
||||
),
|
||||
flux_in=float(np.sum(rho[1:-1, 2] * ux[1:-1, 2])),
|
||||
flux_out=float(np.sum(rho[1:-1, -3] * ux[1:-1, -3])),
|
||||
**pressure_probes(rho, fluid, p),
|
||||
)
|
||||
result["history"].append(row)
|
||||
if (
|
||||
row["max_mach"] > 0.2
|
||||
or row["rho_min"] < 0.5
|
||||
or row["rho_max"] > 1.5
|
||||
):
|
||||
healthy = False
|
||||
result["safety_stop"] = "Mach > .2 or density outside [.5,1.5]"
|
||||
if writer is None:
|
||||
writer = csv.DictWriter(f, fieldnames=row)
|
||||
writer.writeheader()
|
||||
writer.writerow(row)
|
||||
if not healthy:
|
||||
f.flush()
|
||||
break
|
||||
if len(result["history"]) == 1:
|
||||
print(
|
||||
"READY "
|
||||
+ json.dumps(
|
||||
dict(
|
||||
pid=os.getpid(),
|
||||
step=row["step"],
|
||||
tstar=row["tstar"],
|
||||
all_runtime_and_macro_paths_exercised=True,
|
||||
)
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
if len(result["history"]) % 100 == 0:
|
||||
f.flush()
|
||||
print(json.dumps(row), flush=True)
|
||||
result.update(
|
||||
steps=sim.stepper.step_count,
|
||||
step_seconds=step_seconds,
|
||||
wall_seconds=time.monotonic() - started,
|
||||
)
|
||||
result["measurement_seconds"] = max(
|
||||
0.0,
|
||||
result["wall_seconds"] - result["startup_seconds"] - step_seconds,
|
||||
)
|
||||
result["mlups"] = (
|
||||
p["nx"] * p["ny"] * result["steps"] / max(step_seconds, 1e-30) / 1e6
|
||||
)
|
||||
result.update(assess(result["history"]))
|
||||
if not healthy:
|
||||
result["periodic"] = False
|
||||
result["status"] = (
|
||||
"invalid_state"
|
||||
if not healthy
|
||||
else (
|
||||
"pilot_complete"
|
||||
if a.mode == "pilot"
|
||||
else (
|
||||
"periodic"
|
||||
if result["periodic"]
|
||||
else "incomplete_periodicity"
|
||||
)
|
||||
)
|
||||
)
|
||||
if healthy:
|
||||
np.savez_compressed(
|
||||
a.output / "final_field.npz", rho=rho, ux=ux, uy=uy, fluid=fluid
|
||||
)
|
||||
result["outlet_residual"] = outlet_diagnostic(rho, ux, uy, p)
|
||||
if result["cycles"]:
|
||||
result["reference_comparison"] = compare(
|
||||
result["cycles"][-1], reference
|
||||
)
|
||||
result["case_reference_pass"] = bool(
|
||||
healthy
|
||||
and result["periodic"]
|
||||
and result.get("reference_comparison", {}).get(
|
||||
"reference_agreement", False
|
||||
)
|
||||
)
|
||||
(a.output / "summary.json").write_text(
|
||||
json.dumps(result, indent=2, allow_nan=False)
|
||||
)
|
||||
print(
|
||||
json.dumps({k: v for k, v in result.items() if k not in ("history", "cycles")}),
|
||||
flush=True,
|
||||
)
|
||||
return (
|
||||
1 if not healthy else (2 if a.mode == "full" and not result["periodic"] else 0)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--diameter", type=int, choices=[20, 40, 80], default=20)
|
||||
parser.add_argument("--speed", type=float, choices=[0.01, 0.02], default=0.01)
|
||||
parser.add_argument("--mode", choices=["pilot", "full"], default="pilot")
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
a = parser.parse_args()
|
||||
if not os.environ.get("CUDA_VISIBLE_DEVICES"):
|
||||
raise SystemExit("Select physical GPU with CUDA_VISIBLE_DEVICES")
|
||||
return run(a)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,558 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bounded jet-on periodic cylinder: actual AMR Jet API vs blocked validation adapter."""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
PIN = "d79e3f6f0a424f5bcc3a1a6d610313e49daf97a2"
|
||||
HERE = Path(__file__).resolve().parent
|
||||
CX = np.array([0, 1, -1, 0, 0, 1, -1, 1, -1])
|
||||
CY = np.array([0, 0, 0, 1, -1, 1, -1, -1, 1])
|
||||
W = np.array([4 / 9] + [1 / 9] * 4 + [1 / 36] * 4)
|
||||
|
||||
|
||||
def case(scale=1, re=100, steps=800):
|
||||
u = 0.03 / scale
|
||||
d = 24.084 * scale
|
||||
return dict(
|
||||
scale=scale,
|
||||
re_d=re,
|
||||
beta=0.1,
|
||||
nx=192 * scale,
|
||||
ny=128 * scale,
|
||||
diameter=d,
|
||||
center=[96 * scale, 64 * scale],
|
||||
u=u,
|
||||
jet_speed=0.1 * u,
|
||||
nu=u * d / re,
|
||||
tau_root=0.5 + 3 * u * d / re,
|
||||
tau_fine=0.5 + 6 * u * d / re,
|
||||
bounds=[48 * scale, 32 * scale, 144 * scale, 96 * scale],
|
||||
contours=[
|
||||
[72 * scale, 44 * scale, 120 * scale, 84 * scale],
|
||||
[64 * scale, 40 * scale, 128 * scale, 88 * scale],
|
||||
],
|
||||
steps=steps * scale**2,
|
||||
window=100 * scale**2,
|
||||
dx_root=1 / scale,
|
||||
dt_root=1 / scale**2,
|
||||
fine_dx_root_units=0.5,
|
||||
fine_dt_root_units=0.5,
|
||||
mach=np.sqrt(3) * u,
|
||||
final_t_star=steps * scale**2 * u / d,
|
||||
)
|
||||
|
||||
|
||||
def state(shape, u):
|
||||
cu = CX[:, None, None] * u
|
||||
return (
|
||||
np.broadcast_to(
|
||||
W[:, None, None] * (1 + 3 * cu + 4.5 * cu**2 - 1.5 * u * u), shape
|
||||
)
|
||||
.astype(np.float32)
|
||||
.copy()
|
||||
)
|
||||
|
||||
|
||||
def moments(g):
|
||||
f = np.asarray(g, np.float64)
|
||||
rho = f.sum(axis=0)
|
||||
momentum = np.stack([np.einsum("q,qyx->yx", v, f) for v in (CX, CY)])
|
||||
return rho, momentum, momentum / rho
|
||||
|
||||
|
||||
def cv(g, fluid, origin, rectangle, nu, h=0.5):
|
||||
"""Cell-centred inventory and midpoint face quadrature, physical root units.
|
||||
|
||||
Cauchy stress from centred velocity gradients, NOT postcollision non-equilibrium.
|
||||
Rectangle faces lie on cell edges; fluid mask excludes the staircase cylinder.
|
||||
"""
|
||||
rho, mom, vel = moments(g)
|
||||
x0, y0, x1, y1 = np.rint((np.array(rectangle) - np.tile(origin, 2)) / h).astype(int)
|
||||
np.testing.assert_allclose(
|
||||
np.array([x0, y0, x1, y1]) * h + np.tile(origin, 2), rectangle, atol=1e-12
|
||||
)
|
||||
ny, nx = fluid.shape
|
||||
if not (2 <= x0 < x1 <= nx - 2 and 2 <= y0 < y1 <= ny - 2):
|
||||
raise ValueError("Contour needs two fluid-node stencil layers")
|
||||
# Every face and its velocity-gradient stencil must be active fluid.
|
||||
for ys, xs in [
|
||||
(slice(y0 - 2, y1 + 2), slice(x0 - 2, x0 + 2)),
|
||||
(slice(y0 - 2, y1 + 2), slice(x1 - 2, x1 + 2)),
|
||||
(slice(y0 - 2, y0 + 2), slice(x0 - 2, x1 + 2)),
|
||||
(slice(y1 - 2, y1 + 2), slice(x0 - 2, x1 + 2)),
|
||||
]:
|
||||
if not fluid[ys, xs].all():
|
||||
raise ValueError("Contour stencil intersects solid/inactive region")
|
||||
use = fluid[y0:y1, x0:x1]
|
||||
inventory = (
|
||||
np.array(
|
||||
[
|
||||
rho[y0:y1, x0:x1][use].sum(),
|
||||
mom[0, y0:y1, x0:x1][use].sum(),
|
||||
mom[1, y0:y1, x0:x1][use].sum(),
|
||||
]
|
||||
)
|
||||
* h
|
||||
* h
|
||||
)
|
||||
duy, dux = np.gradient(vel[0], h)
|
||||
dvy, dvx = np.gradient(vel[1], h)
|
||||
mu = rho * nu
|
||||
# Subtract constant reference pressure, whose closed-contour integral is zero.
|
||||
p = (rho - 1) / 3
|
||||
tensor = np.array(
|
||||
[
|
||||
[
|
||||
rho * vel[0] ** 2 + p - 2 * mu * dux,
|
||||
rho * vel[0] * vel[1] - mu * (duy + dvx),
|
||||
],
|
||||
[
|
||||
rho * vel[0] * vel[1] - mu * (duy + dvx),
|
||||
rho * vel[1] ** 2 + p - 2 * mu * dvy,
|
||||
],
|
||||
]
|
||||
)
|
||||
transport = np.concatenate(
|
||||
[mom[None], tensor], axis=0
|
||||
) # [mass,Px,Py] x normal axis
|
||||
flux = np.zeros(3)
|
||||
for i, sign in [(x0, -1), (x1, 1)]:
|
||||
flux += (
|
||||
sign
|
||||
* 0.5
|
||||
* (transport[:, 0, y0:y1, i - 1] + transport[:, 0, y0:y1, i]).sum(axis=1)
|
||||
* h
|
||||
)
|
||||
for j, sign in [(y0, -1), (y1, 1)]:
|
||||
flux += (
|
||||
sign
|
||||
* 0.5
|
||||
* (transport[:, 1, j - 1, x0:x1] + transport[:, 1, j, x0:x1]).sum(axis=1)
|
||||
* h
|
||||
)
|
||||
if not np.isfinite(np.r_[inventory, flux]).all():
|
||||
raise RuntimeError("Nonfinite CV quantities")
|
||||
return inventory, flux
|
||||
|
||||
|
||||
def aperture_probe(fluid, origin, center, radius, h=0.5):
|
||||
"""Independent true-arc quadrature with nearest active-fluid sampling.
|
||||
|
||||
This is a first-order near-wall diagnostic, not certified wall reconstruction.
|
||||
No target velocity or raw-population ledger is used to measure the flux.
|
||||
"""
|
||||
z, w = np.polynomial.legendre.leggauss(64)
|
||||
result = []
|
||||
for angle in (np.pi / 2, 3 * np.pi / 2):
|
||||
theta = angle + np.pi / 12 * z
|
||||
er = np.column_stack((np.cos(theta), np.sin(theta)))
|
||||
points = np.asarray(center) + radius * er
|
||||
selected = []
|
||||
distance = []
|
||||
for point in points:
|
||||
ix, iy = np.rint((point - np.asarray(origin)) / h - 0.5).astype(int)
|
||||
candidates = [
|
||||
(y, x)
|
||||
for y in range(iy - 3, iy + 4)
|
||||
for x in range(ix - 3, ix + 4)
|
||||
if 0 <= y < fluid.shape[0] and 0 <= x < fluid.shape[1] and fluid[y, x]
|
||||
]
|
||||
if not candidates:
|
||||
raise ValueError("No active near-wall sampling node")
|
||||
yy, xx = np.array(candidates).T
|
||||
dist = np.linalg.norm(
|
||||
np.column_stack((xx, yy)) * h + np.asarray(origin) + h / 2 - point,
|
||||
axis=1,
|
||||
)
|
||||
k = np.argmin(dist)
|
||||
selected.append(candidates[k])
|
||||
distance.append(dist[k])
|
||||
yy, xx = np.array(selected).T
|
||||
result.append((yy, xx, er, w * radius * np.pi / 12, float(max(distance))))
|
||||
return result
|
||||
|
||||
|
||||
def measured_aperture_flux(g, probe):
|
||||
_, momentum, _ = moments(g)
|
||||
return np.array(
|
||||
[
|
||||
np.sum(np.einsum("in,ni->n", momentum[:, y, x], normal) * ds)
|
||||
for y, x, normal, ds, _ in probe
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def verify(repo):
|
||||
manifest = json.loads(
|
||||
(HERE / "data/jet_circle_cv/source_manifest.json").read_text()
|
||||
)
|
||||
if manifest["revision"] != PIN:
|
||||
raise RuntimeError("Wrong pinned revision")
|
||||
for path, sha in manifest["files"].items():
|
||||
if hashlib.sha256((repo / path).read_bytes()).hexdigest() != sha:
|
||||
raise RuntimeError("Source hash mismatch: " + path)
|
||||
return manifest
|
||||
|
||||
|
||||
def run_case(c, ctx, output):
|
||||
from CelerisLab.body.geometry.circle import CircleGeometry
|
||||
from CelerisLab.body.objects import SimObject
|
||||
from CelerisLab.body.jets import Jet, Sector, Radial
|
||||
from CelerisLab.config import LBMConfig
|
||||
from CelerisLab.lbm.blocks import BlockLayout2D
|
||||
from CelerisLab.lbm.block_stepper import PeriodicBlockStepper
|
||||
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
|
||||
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
|
||||
import pycuda.driver as cuda
|
||||
|
||||
np.testing.assert_array_equal(CX, D2Q9_EX)
|
||||
np.testing.assert_array_equal(CY, D2Q9_EY)
|
||||
nx, ny, u, nu = c["nx"], c["ny"], c["u"], c["nu"]
|
||||
center = np.array(c["center"], float)
|
||||
radius = c["diameter"] / 2
|
||||
cfg = LBMConfig(
|
||||
nx=nx,
|
||||
ny=ny,
|
||||
viscosity=nu,
|
||||
velocity=0.0,
|
||||
collision="SRT",
|
||||
streaming="double_buffer",
|
||||
store_precision="FP32",
|
||||
ddf_shifting=False,
|
||||
les_enabled=False,
|
||||
)
|
||||
fine_cfg = LBMConfig(
|
||||
nx=2 * nx,
|
||||
ny=2 * ny,
|
||||
viscosity=2 * nu,
|
||||
velocity=0.0,
|
||||
collision="SRT",
|
||||
streaming="double_buffer",
|
||||
store_precision="FP32",
|
||||
ddf_shifting=False,
|
||||
les_enabled=False,
|
||||
)
|
||||
obj = SimObject(
|
||||
0, CircleGeometry(*center, radius), center=tuple(center), radius=radius
|
||||
)
|
||||
fc = 2 * center - 0.5
|
||||
fobj = SimObject(
|
||||
0, CircleGeometry(*fc, 2 * radius), center=tuple(fc), radius=2 * radius
|
||||
)
|
||||
jets = [
|
||||
Jet(
|
||||
Sector(tuple(center), angle, np.pi / 12, radius + 0.1),
|
||||
Radial(tuple(center), inward=False),
|
||||
"uniform",
|
||||
sign * c["jet_speed"],
|
||||
)
|
||||
for angle, sign in [(np.pi / 2, 1), (3 * np.pi / 2, -1)]
|
||||
]
|
||||
layout = BlockLayout2D.build(2 * ny, 2 * nx, 8, 8)
|
||||
with (
|
||||
PeriodicBlockStepper(fine_cfg, layout, context=ctx, objects=[fobj]) as uniform,
|
||||
FixedPatchStepper(
|
||||
cfg,
|
||||
c["bounds"],
|
||||
context=ctx,
|
||||
base_block=(8, 8),
|
||||
fine_block=(8, 8),
|
||||
fine_objects=[obj],
|
||||
jets=jets,
|
||||
observe_raw_exchange=True,
|
||||
) as amr,
|
||||
):
|
||||
b = amr._level_boundaries[1]
|
||||
count = len(b.tables["cell"])
|
||||
if np.any(b.tables["q"] <= 0.010001) or np.any(b.tables["fallback"]):
|
||||
raise RuntimeError("Pilot requires unclamped q and no fallback")
|
||||
links = fobj.geometry.build_cut_links(2 * nx, 2 * ny)
|
||||
np.testing.assert_array_equal([v.dir for v in links], b.tables["dir"])
|
||||
np.testing.assert_allclose(
|
||||
[v.q for v in links], b.tables["q"], atol=2e-7, rtol=0
|
||||
)
|
||||
np.testing.assert_array_equal([v.fallback for v in links], b.tables["fallback"])
|
||||
np.testing.assert_allclose(
|
||||
np.array([[v.hit_x, v.hit_y] for v in links]) * 0.5 + 0.25,
|
||||
b.host.curved_hits,
|
||||
atol=2e-12,
|
||||
rtol=0,
|
||||
)
|
||||
# Same pre-existing optional-input adapter as test_raw_exchange_gpu.py.
|
||||
velocity = uniform._upload(b.tables["surface_velocity"])
|
||||
rx = uniform._upload(b.tables["rx"])
|
||||
ry = uniform._upload(b.tables["ry"])
|
||||
raw = uniform._allocate(count * 16)
|
||||
original = uniform._curved
|
||||
samples = []
|
||||
|
||||
def boundary(*args, **kw):
|
||||
original(*args[:7], velocity, rx, ry, raw, **kw)
|
||||
uniform._stream.synchronize()
|
||||
a = np.empty((count, 4), np.float32)
|
||||
cuda.memcpy_dtoh(a, raw)
|
||||
samples.append(a)
|
||||
|
||||
uniform._curved = boundary
|
||||
|
||||
def initialize():
|
||||
uniform.initialize(state((9, 2 * ny, 2 * nx), u))
|
||||
amr.initialize(*(state(l.shape, u) for l in amr._levels))
|
||||
|
||||
initialize()
|
||||
# Compile/launch all stepping and diagnostic paths, then reset physical time.
|
||||
amr.step()
|
||||
uniform.step(2)
|
||||
uniform.download()
|
||||
amr.download()
|
||||
amr.read_raw_exchange(0)
|
||||
initialize()
|
||||
samples.clear()
|
||||
jet_id = b.jet_cache[1]
|
||||
masks = {
|
||||
"wall": jet_id < 0,
|
||||
"aperture": jet_id >= 0,
|
||||
"total": np.ones(count, bool),
|
||||
"blowing": jet_id == 0,
|
||||
"suction": jet_id == 1,
|
||||
}
|
||||
if not masks["blowing"].any() or not masks["suction"].any():
|
||||
raise RuntimeError("Both apertures must resolve cut links")
|
||||
fluid_u = ~uniform.boundary["solid"]
|
||||
fluid_a = amr._leaf[1]
|
||||
origin_a = np.array(amr.patch.fine_origin)
|
||||
probes = {
|
||||
"uniform_adapter": aperture_probe(fluid_u, (0.0, 0.0), center, radius),
|
||||
"amr_api": aperture_probe(fluid_a, origin_a, center, radius),
|
||||
}
|
||||
|
||||
def capture():
|
||||
ug = uniform.download()
|
||||
ag = amr.download()[1]
|
||||
data = {}
|
||||
aperture = {}
|
||||
for name, g, fluid, origin in [
|
||||
("uniform_adapter", ug, fluid_u, (0.0, 0.0)),
|
||||
("amr_api", ag, fluid_a, origin_a),
|
||||
]:
|
||||
data[name] = [cv(g, fluid, origin, r, nu) for r in c["contours"]]
|
||||
aperture[name] = measured_aperture_flux(g, probes[name])
|
||||
rho, _, _ = moments(g)
|
||||
if not np.isfinite(rho[fluid]).all() or rho[fluid].min() <= 0:
|
||||
raise RuntimeError("Nonfinite/nonpositive fluid density")
|
||||
ox, oy = np.rint(2 * origin_a).astype(int)
|
||||
ay, ax = fluid_a.shape
|
||||
_, _, uv = moments(ug[:, oy : oy + ay, ox : ox + ax])
|
||||
_, _, av = moments(ag)
|
||||
error = float(
|
||||
np.linalg.norm((av - uv)[:, fluid_a])
|
||||
/ max(np.linalg.norm(uv[:, fluid_a]), 1e-30)
|
||||
)
|
||||
return data, error, aperture
|
||||
|
||||
previous, _, previous_aperture = capture()
|
||||
initial = previous
|
||||
integrated = {name: np.zeros((2, 3)) for name in previous}
|
||||
integrated_aperture = {name: np.zeros(2) for name in previous}
|
||||
accumulated = {name: {k: np.zeros(4) for k in masks} for name in previous}
|
||||
rows = []
|
||||
start = time.monotonic()
|
||||
print(
|
||||
json.dumps(
|
||||
dict(
|
||||
status="READY",
|
||||
case=c,
|
||||
links=count,
|
||||
actual_amr_jet_api=True,
|
||||
uniform_reference="private optional-input validation adapter",
|
||||
q_range=[float(b.tables["q"].min()), float(b.tables["q"].max())],
|
||||
fallback_count=int(np.count_nonzero(b.tables["fallback"])),
|
||||
)
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
for step in range(1, c["steps"] + 1):
|
||||
if time.monotonic() - start > 1800:
|
||||
raise RuntimeError(
|
||||
"1800-second post-READY pilot budget exceeded; partial report retained"
|
||||
)
|
||||
samples.clear()
|
||||
amr.step()
|
||||
uniform.step(2)
|
||||
if len(samples) != 2:
|
||||
raise RuntimeError("Uniform interval must contain two fine hops")
|
||||
ur = sum(a.astype(float) * [0.25, 0.25, 0.125, 0.25] for a in samples)
|
||||
ar = np.empty((count, 4), np.float64)
|
||||
with ctx.activate():
|
||||
cuda.memcpy_dtoh(ar, b.interval_integral)
|
||||
obs = amr.read_raw_exchange(0)
|
||||
if obs["time_start"] != step - 1 or obs["time_end"] != step:
|
||||
raise RuntimeError("Raw interval time mismatch")
|
||||
np.testing.assert_allclose(
|
||||
ar.sum(axis=0)[:2], obs["total"]["impulse"], atol=1e-13, rtol=1e-13
|
||||
)
|
||||
current, error, current_aperture = capture()
|
||||
for name, rr in [("uniform_adapter", ur), ("amr_api", ar)]:
|
||||
for k, mask in masks.items():
|
||||
accumulated[name][k] += rr[mask].sum(axis=0)
|
||||
integrated_aperture[name] += 0.5 * (
|
||||
previous_aperture[name] + current_aperture[name]
|
||||
)
|
||||
integrated[name] += 0.5 * (
|
||||
np.array([x[1] for x in previous[name]])
|
||||
+ np.array([x[1] for x in current[name]])
|
||||
)
|
||||
previous = current
|
||||
previous_aperture = current_aperture
|
||||
if step % c["window"] == 0 or step == c["steps"]:
|
||||
end0 = rows[-1]["step_end"] if rows else 0
|
||||
duration = step - end0
|
||||
row = dict(
|
||||
step_start=end0,
|
||||
step_end=step,
|
||||
t_star=step * u / c["diameter"],
|
||||
fine_velocity_relative_l2=error,
|
||||
elapsed_seconds=time.monotonic() - start,
|
||||
measurements={},
|
||||
)
|
||||
for name in current:
|
||||
delta = np.array([x[0] for x in current[name]]) - np.array(
|
||||
[x[0] for x in initial[name]]
|
||||
)
|
||||
mass_cv = delta[:, 0] + integrated[name][:, 0]
|
||||
reaction_cv = -delta[:, 1:] - integrated[name][:, 1:]
|
||||
ledger = accumulated[name]
|
||||
total = ledger["total"]
|
||||
normal = 0.5 * u * u * c["diameter"]
|
||||
row["measurements"][name] = dict(
|
||||
raw_interval={k: v.tolist() for k, v in ledger.items()},
|
||||
cv_mass_into_fluid=mass_cv.tolist(),
|
||||
cv_reaction_impulse=reaction_cv.tolist(),
|
||||
mass_balance_residual=(mass_cv - total[3]).tolist(),
|
||||
reaction_balance_residual=(reaction_cv - total[:2]).tolist(),
|
||||
reaction_residual_coefficient=(
|
||||
(reaction_cv - total[:2]) / duration / normal
|
||||
).tolist(),
|
||||
raw_reaction_coefficient=(
|
||||
total[:2] / duration / normal
|
||||
).tolist(),
|
||||
cv_reaction_coefficient=(
|
||||
reaction_cv / duration / normal
|
||||
).tolist(),
|
||||
contour_reaction_spread_coefficient=(
|
||||
(reaction_cv[1] - reaction_cv[0]) / duration / normal
|
||||
).tolist(),
|
||||
measured_arc_mass_rates=(
|
||||
integrated_aperture[name] / duration
|
||||
).tolist(),
|
||||
arc_max_sample_distance=[v[-1] for v in probes[name]],
|
||||
nominal_volume_fluxes=[
|
||||
2 * np.pi / 12 * radius * c["jet_speed"],
|
||||
-2 * np.pi / 12 * radius * c["jet_speed"],
|
||||
],
|
||||
aperture_mass_rates={
|
||||
k: float(ledger[k][3] / duration)
|
||||
for k in ("blowing", "suction", "wall", "total")
|
||||
},
|
||||
)
|
||||
integrated[name].fill(0)
|
||||
integrated_aperture[name].fill(0)
|
||||
for v in accumulated[name].values():
|
||||
v.fill(0)
|
||||
rows.append(row)
|
||||
initial = current
|
||||
payload = dict(
|
||||
case=c,
|
||||
source_revision=PIN,
|
||||
status="PILOT_DIAGNOSTIC_ONLY",
|
||||
windows=rows,
|
||||
physics_validation_pass=False,
|
||||
precision="FP32 evolution, FP64 host diagnostics",
|
||||
)
|
||||
(output / f"re{c['re_d']}_s{c['scale']}.json").write_text(
|
||||
json.dumps(payload, indent=2) + "\n"
|
||||
)
|
||||
print(
|
||||
json.dumps(
|
||||
dict(
|
||||
status="PROGRESS",
|
||||
step=step,
|
||||
steps=c["steps"],
|
||||
elapsed_seconds=row["elapsed_seconds"],
|
||||
fine_velocity_relative_l2=error,
|
||||
)
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument("--repo", type=Path, required=True)
|
||||
p.add_argument("--output", type=Path, required=True)
|
||||
p.add_argument("--scale", type=int, choices=[1, 2, 4], default=1)
|
||||
p.add_argument("--re", type=int, choices=[100, 400], default=100)
|
||||
p.add_argument(
|
||||
"--steps", type=int, default=800, help="Base s=1 steps; scales as s squared"
|
||||
)
|
||||
p.add_argument("--allow-re400", action="store_true")
|
||||
p.add_argument("--plan", action="store_true")
|
||||
a = p.parse_args()
|
||||
if a.steps <= 0 or a.steps > 3200:
|
||||
p.error("steps must be 1..3200")
|
||||
if a.re == 400 and not a.allow_re400:
|
||||
p.error("Re400 requires explicit conditional-campaign opt-in")
|
||||
c = case(a.scale, a.re, a.steps)
|
||||
plan = dict(
|
||||
case=c,
|
||||
required_revision=PIN,
|
||||
required_free_device_mib=1024,
|
||||
estimated_peak_device_mib=128 * a.scale**2,
|
||||
estimated_output_mib=2,
|
||||
max_steps=c["steps"],
|
||||
gpu_launch_owner="root; this runner does not select or reserve a GPU",
|
||||
)
|
||||
if a.plan:
|
||||
print(json.dumps(plan, indent=2))
|
||||
return
|
||||
if (
|
||||
not os.environ.get("CUDA_VISIBLE_DEVICES")
|
||||
or "," in os.environ["CUDA_VISIBLE_DEVICES"]
|
||||
):
|
||||
p.error("Root must select one verified idle GPU via CUDA_VISIBLE_DEVICES")
|
||||
repo = a.repo.resolve()
|
||||
verify(repo)
|
||||
sys.path.insert(0, str(repo / "src"))
|
||||
import CelerisLab
|
||||
|
||||
if Path(CelerisLab.__file__).resolve() != repo / "src/CelerisLab/__init__.py":
|
||||
raise RuntimeError("Wrong source import")
|
||||
from CelerisLab.cuda.context import CudaContext
|
||||
import pycuda.driver as cuda
|
||||
|
||||
a.output.mkdir(parents=True, exist_ok=False)
|
||||
(a.output / "plan.json").write_text(json.dumps(plan, indent=2) + "\n")
|
||||
with CudaContext(0) as ctx:
|
||||
with ctx.activate():
|
||||
free, total = cuda.mem_get_info()
|
||||
if free < max(1024, 2 * plan["estimated_peak_device_mib"]) * 1024**2:
|
||||
raise RuntimeError("Insufficient free device memory")
|
||||
run_case(c, ctx, a.output)
|
||||
print(
|
||||
json.dumps(
|
||||
dict(status="COMPLETE_DIAGNOSTIC_ONLY", physics_validation_pass=False)
|
||||
),
|
||||
flush=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user