test: close concave union crossing and transit ownership
This commit is contained in:
1 parent
90694e69af
commit
f287b460fd
2 files changed
+150
-27
No files matched your search
@@ -48,6 +48,20 @@ def _first_entry(covered, origin, child, c):
|
||||
return None
|
||||
|
||||
|
||||
def _crossing_time(covered, origin, position, c, axes):
|
||||
"""Fig.11 convex T uses the last plane; Fig.13 concave T uses the first.
|
||||
|
||||
Both use the same KKT formulation on different boundary geometry.
|
||||
DOI/primary PDF: https://www.osti.gov/servlets/purl/1226215, §3.5.1.
|
||||
"""
|
||||
times=[(origin[j]+int(c[j]>0)-position[j])/c[j] for j in axes]
|
||||
if len(axes)==1: return times[0]
|
||||
sides=[bool(covered[tuple(origin+np.eye(2,dtype=int)[j]*c[j])]) for j in axes]
|
||||
if sides[0]!=sides[1]:
|
||||
raise UnresolvedTopology('mixed_corner_support',source_yx=origin.tolist())
|
||||
return min(times) if all(sides) else max(times)
|
||||
|
||||
|
||||
class UnionPlan:
|
||||
"""Two-level, level-wide square geometry; all coordinates are level-local.
|
||||
|
||||
@@ -72,9 +86,10 @@ class UnionPlan:
|
||||
if not np.array_equal(active, pair.fine_kind == 0):
|
||||
raise UnresolvedTopology('non_pair_active_coverage')
|
||||
self.maps = SimpleNamespace(coarse_active=~covered, coarse_covered=covered, fine_active=active)
|
||||
self.outgoing = [[], []]
|
||||
# Check transit FIRST: a fine exit may not be silently routed into a
|
||||
# covered coarse cell. Returning fine packets need a separately derived rule.
|
||||
self.outgoing, transit = [[], []], []
|
||||
# Guzik et al. §3.5.4, Fig.19 (https://www.osti.gov/servlets/purl/1226215):
|
||||
# retain substep-0 outgoing G at source, then fill the substep-1 ghost.
|
||||
# Rows are (q, fine source, next ghost); no covered coarse receiver credit.
|
||||
for q in range(1, 9):
|
||||
c = np.array((ref.cpu.CY[q], ref.cpu.CX[q]))
|
||||
boundary = active & ~np.roll(active, tuple(-c), axis=(0, 1))
|
||||
@@ -83,10 +98,14 @@ class UnionPlan:
|
||||
for m in (0, 1):
|
||||
target = (p+(2-m)*c)//2
|
||||
if covered[tuple(target)]:
|
||||
raise UnresolvedTopology('fine_exit_reentry', q=q, substep=m,
|
||||
fine_path_yx=[(p+j*c).tolist() for j in range(3-m)], coarse_target_yx=target.tolist())
|
||||
self.outgoing[m].append((q, int(y*self.nf+x), int(target[0]*nx+target[1])))
|
||||
if m != 0 or not active[tuple(p+2*c)]:
|
||||
raise UnresolvedTopology('unsupported_transit', q=q, substep=m, source_yx=p.tolist())
|
||||
ghost=p+c
|
||||
transit.append((q,int(y*self.nf+x),int(ghost[0]*self.nf+ghost[1])))
|
||||
else:
|
||||
self.outgoing[m].append((q, int(y*self.nf+x), int(target[0]*nx+target[1])))
|
||||
self.outgoing = [np.array(x, dtype=np.int64).reshape(-1, 3) for x in self.outgoing]
|
||||
self.transit = np.array(transit,dtype=np.int64).reshape(-1,3)
|
||||
self.groups = []
|
||||
for iy, ix in zip(*np.nonzero(~covered)):
|
||||
origin = np.array((iy, ix))
|
||||
@@ -112,9 +131,7 @@ class UnionPlan:
|
||||
points = []
|
||||
for p, entry, event in zip(starts, entries, events):
|
||||
position = (p+.5)/2
|
||||
planes = origin+(c > 0)
|
||||
times = [(planes[j]-position[j])/c[j] for j in axes]
|
||||
time = max(times)
|
||||
time = _crossing_time(covered,origin,position,c,axes)
|
||||
if entry is not None and (4*time != entry[0] or event is None):
|
||||
raise UnresolvedTopology('nonconvex_entry_support', source_yx=origin.tolist(), q=q,
|
||||
quarter_time=entry[0], support_quarter_time=float(4*time))
|
||||
@@ -140,6 +157,14 @@ def validate_ownership(plan):
|
||||
"""Independent mask-based event coverage and four-quarter receiver accounting."""
|
||||
n, nf, active = plan.n, plan.nf, plan.maps.fine_active
|
||||
actual = [(m,g.q,slot) for g in plan.groups for e in g.events if e is not None for m,slot in [e]]
|
||||
expected_transit={(q,int(y*nf+x),int((y+ref.cpu.CY[q])*nf+x+ref.cpu.CX[q]))
|
||||
for q in range(1,9) for y,x in zip(*np.nonzero(active))
|
||||
if not active[y+ref.cpu.CY[q],x+ref.cpu.CX[q]]
|
||||
and active[y+2*ref.cpu.CY[q],x+2*ref.cpu.CX[q]]}
|
||||
routes=[tuple(map(int,row)) for row in plan.transit]
|
||||
if len(routes)!=len(set(routes)) or set(routes)!=expected_transit:
|
||||
raise ValueError('transit provenance/uniqueness/coverage failed')
|
||||
actual += [(1,q,ghost) for q,source,ghost in routes]
|
||||
expected = {(m,q,int((y-ref.cpu.CY[q])*nf+x-ref.cpu.CX[q]))
|
||||
for m in (0,1) for q in range(1,9) for y,x in zip(*np.nonzero(active))
|
||||
if not active[y-ref.cpu.CY[q],x-ref.cpu.CX[q]]}
|
||||
@@ -178,7 +203,7 @@ def validate_ownership(plan):
|
||||
if not np.array_equal(g.samples,indices): raise ValueError('sample anchor provenance failed')
|
||||
for k,(dy,dx) in enumerate(((0,0),(0,1),(1,0),(1,1))):
|
||||
position=origin+(np.array((dy,dx))+.5)/2
|
||||
time=max((origin[j]+int(c[j]>0)-position[j])/c[j] for j in axes)
|
||||
time=_crossing_time(plan.maps.coarse_covered,origin,position,c,axes)
|
||||
offset=position+c*time-(origin+.5+c*.5)
|
||||
point=(float(offset[1]),float(offset[0]),float(time-.5)) if g.kind=='corner' else (float(offset[1-axes[0]]),float(time-.5))
|
||||
if g.points[k]!=point: raise ValueError('evaluation point provenance failed')
|
||||
@@ -193,7 +218,8 @@ def validate_ownership(plan):
|
||||
for m,rows in enumerate(plan.outgoing):
|
||||
expected_out = {(q,int(y*nf+x),int(((y+(2-m)*ref.cpu.CY[q])//2)*n+(x+(2-m)*ref.cpu.CX[q])//2))
|
||||
for q in range(1,9) for y,x in zip(*np.nonzero(active))
|
||||
if not active[y+ref.cpu.CY[q],x+ref.cpu.CX[q]]}
|
||||
if not active[y+ref.cpu.CY[q],x+ref.cpu.CX[q]]
|
||||
and plan.maps.coarse_active[(y+(2-m)*ref.cpu.CY[q])//2,(x+(2-m)*ref.cpu.CX[q])//2]}
|
||||
actual_out = [tuple(map(int,row)) for row in rows]
|
||||
if len(actual_out)!=len(set(actual_out)) or set(actual_out)!=expected_out:
|
||||
raise ValueError('outgoing uniqueness/coverage failed')
|
||||
@@ -207,6 +233,7 @@ def validate_ownership(plan):
|
||||
if any(len(row)!=4 for row in packets.values()) or len(tokens)!=len(set(tokens)):
|
||||
raise ValueError('receiver needs four distinct quarter-packets')
|
||||
return dict(topology='supported',incoming_events=len(actual),outgoing_events=sum(map(len,plan.outgoing)),
|
||||
transit_events=len(routes),
|
||||
groups=len(plan.groups),receivers=len(packets),receiver_quarter_packets=len(tokens),
|
||||
interpolation_accuracy=plan.interpolation_accuracy,runtime_supported=False,
|
||||
scope='pair-local ownership/coefficients; complete parent domain assumed, nested coverage unvalidated')
|
||||
Reference in new issue
Block a user