') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); BNGResult loader crashes (np.loadtxt) on ragged .cdat files that BNG2.pl emits for SSA · Issue #103 · RuleWorld/PyBioNetGen · GitHub
Skip to content

BNGResult loader crashes (np.loadtxt) on ragged .cdat files that BNG2.pl emits for SSA #103

Description

@wshlavacek

Summary

bionetgen.run() raises ValueError while building its BNGResult whenever BNG2.pl writes a ragged .cdat. run_network does this for SSA on networks where some species are unpopulated at t=0 and become populated during the run: the per-row species-column count grows over the trajectory while the header line stays fixed at the initial count. result.py:_load_dat builds a fixed NumPy dtype from the header and calls np.loadtxt(path, dtype={...}), which requires every row to have the same width and aborts on the first wider row.

The simulation itself succeeds and the output files are written correctly — only the post-run load-back into BNGResult fails, which makes the whole run() call fatal even though a perfectly good .gdat is on disk.

Error

File ".../bionetgen/core/tools/result.py", line 176, in _load_dat
return np.rec.array(np.loadtxt(path, dtype={"names": names, "formats": formats}))
...
ValueError: the dtype passed requires 26 columns but 27 were found at row 2; use `usecols` to select a subset and avoid this error

Reproduction

Any SSA run on a network with species unpopulated at t=0 triggers it. For example, agentCellSystem with a capped network:

generate_network({overwrite=>1,max_iter=>3,max_agg=>4})
simulate({method=>"ssa",seed=>1,t_start=>0,t_end=>1,n_steps=>20,print_CDAT=>1})
importbionetgenbionetgen.run("model.bngl", out="out")

The produced .cdat has a fixed-width header but data rows whose width grows as species first appear, e.g.:

header (incl. # and time): 27 tokens
data row widths: 26, 27, 28, 34, 36, 38, 40, 42

The .gdat (a fixed observable set) loads fine. The same model under method=>"ode" writes a dense .cdat that loads fine. The ragged .cdat is itself reproducible byte-for-byte across runs (fixed seed) — it is well-defined output, just not rectangular.

Root cause

result.py:_load_dat assumes a rectangular table (a fixed dtype derived from the header width). run_network's SSA .cdat output is not guaranteed rectangular.

Possible directions

  • Tolerate ragged rows (read max width / pad missing trailing species with 0 / usecols), since species absent from a row have population 0.
  • Or make a failed .cdat load non-fatal so a valid .gdat still yields a usable BNGResult.

Environment

BNG2.pl 2.9.3; Python 3.12; current numpy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions