diff --git a/cockpit/public/fma.soa b/cockpit/public/fma.soa index 45b0be0ee..6269c5b27 100644 Binary files a/cockpit/public/fma.soa and b/cockpit/public/fma.soa differ diff --git a/cockpit/src/FmaGraph.tsx b/cockpit/src/FmaGraph.tsx index bf29f1f31..22a4ce03a 100644 --- a/cockpit/src/FmaGraph.tsx +++ b/cockpit/src/FmaGraph.tsx @@ -3,7 +3,8 @@ // same vis-network renderer, and on click shows a node resolving to BOTH: // · its part-of position (basin-local: organ → chamber → wall → structure) // · its leaf-limited global TYPE (the 0xFFFF ceiling pole — cross-cutting, -// the same "Cardiac muscle tissue" shared by every chamber). +// e.g. one "Subdivision of cavity of cardiac chamber" or "Endothelium of +// endocardium" shared by structures in different parts of the heart). // // This is the `Cascade` (ontology / part-of) reading of OGAR PR #116's HhtlMode // FMA tier model: each node is a stack of 8:8 [container:identity] tiers — diff --git a/crates/osint-bake/data/fma-heart.fixture.ttl b/crates/osint-bake/data/fma-heart.fixture.ttl deleted file mode 100644 index f2814b3fc..000000000 --- a/crates/osint-bake/data/fma-heart.fixture.ttl +++ /dev/null @@ -1,53 +0,0 @@ -# fma-heart.fixture.ttl — FIXTURE, NOT the real FMA. -# -# A faithful but hand-authored heart subtree in the canonical FMA predicate -# set. The real Foundational Model of Anatomy (266 MB fma.owl, ~1.5M triples, -# OGIT contextId 13, dcterms:source AdaWorldAPI/MedCare-rs bioportal-ontologies) -# hydrates through lance-graph-rdf / lance_graph_ontology::hydrate_fma at the -# spine; this light q2 bake mirrors that shape on a subtree so /fma renders -# without the lance/datafusion closure that bake deliberately excludes. -# -# Line-oriented Turtle subset: one `subject predicate object .` per line. -# Predicates mirror the canonical hydrator set (pr-d-1-fma-owl-hydrator): -# bfo:part_of → partonomy (drives the HHTL cascade tiers) -# rdfs:subClassOf → cross-cutting tissue type (the is-a ceiling) - -@prefix fma: . -@prefix bfo: . -@prefix rdfs: . - -# ── chambers regional-part-of the heart ── -fma:Left_atrium bfo:part_of fma:Heart . -fma:Right_atrium bfo:part_of fma:Heart . -fma:Left_ventricle bfo:part_of fma:Heart . -fma:Right_ventricle bfo:part_of fma:Heart . - -# ── each chamber's wall layers, each a subClassOf its tissue type ── -fma:Myocardium_of_left_atrium bfo:part_of fma:Left_atrium . -fma:Myocardium_of_left_atrium rdfs:subClassOf fma:Cardiac_muscle_tissue . -fma:Endocardium_of_left_atrium bfo:part_of fma:Left_atrium . -fma:Endocardium_of_left_atrium rdfs:subClassOf fma:Endothelium . -fma:Epicardium_of_left_atrium bfo:part_of fma:Left_atrium . -fma:Epicardium_of_left_atrium rdfs:subClassOf fma:Mesothelium . - -fma:Myocardium_of_right_atrium bfo:part_of fma:Right_atrium . -fma:Myocardium_of_right_atrium rdfs:subClassOf fma:Cardiac_muscle_tissue . -fma:Endocardium_of_right_atrium bfo:part_of fma:Right_atrium . -fma:Endocardium_of_right_atrium rdfs:subClassOf fma:Endothelium . -fma:Epicardium_of_right_atrium bfo:part_of fma:Right_atrium . -fma:Epicardium_of_right_atrium rdfs:subClassOf fma:Mesothelium . - -fma:Myocardium_of_left_ventricle bfo:part_of fma:Left_ventricle . -fma:Myocardium_of_left_ventricle rdfs:subClassOf fma:Cardiac_muscle_tissue . -fma:Endocardium_of_left_ventricle bfo:part_of fma:Left_ventricle . -fma:Endocardium_of_left_ventricle rdfs:subClassOf fma:Endothelium . -fma:Epicardium_of_left_ventricle bfo:part_of fma:Left_ventricle . -fma:Epicardium_of_left_ventricle rdfs:subClassOf fma:Mesothelium . - -fma:Myocardium_of_right_ventricle bfo:part_of fma:Right_ventricle . -fma:Myocardium_of_right_ventricle rdfs:subClassOf fma:Cardiac_muscle_tissue . -fma:Endocardium_of_right_ventricle bfo:part_of fma:Right_ventricle . -fma:Endocardium_of_right_ventricle rdfs:subClassOf fma:Endothelium . -fma:Epicardium_of_right_ventricle bfo:part_of fma:Right_ventricle . -fma:Epicardium_of_right_ventricle rdfs:subClassOf fma:Mesothelium . - diff --git a/crates/osint-bake/data/fma-heart.ttl b/crates/osint-bake/data/fma-heart.ttl new file mode 100644 index 000000000..21493a115 --- /dev/null +++ b/crates/osint-bake/data/fma-heart.ttl @@ -0,0 +1,166 @@ +# fma-heart.ttl — REAL FMA heart subtree, extracted from fma.owl (FMA v5.0.0). +# Source: BioPortal #29 / UW Structural Informatics Group, CC BY 3.0, +# SHA-256 59465eb503c418dbf9216d362b94c7f42bc3b96b297e553ec17c7fee77979e44, +# mirror github.com/AdaWorldAPI/q2/releases/download/fma-ontology-5.0.0/fma.owl +# Generated by tools/extract_fma_heart.py (streaming ET; regional_part + +# constitutional_part partonomy, BFS from FMA:Heart fma7088, depth<=4, <=80 nodes). +# The 266 MB OWL is NOT committed; the production hydrator is the spine's +# lance_graph_ontology::hydrate_fma. This is the real-data heart slice the +# q2 /fma render surface hydrates. + +fma:Cavity_of_right_atrium bfo:part_of fma:Heart . +fma:Cardiac_vein bfo:part_of fma:Heart . +fma:Neural_network_of_heart bfo:part_of fma:Heart . +fma:Coronary_sinus bfo:part_of fma:Heart . +fma:Right_coronary_artery bfo:part_of fma:Heart . +fma:Left_coronary_artery bfo:part_of fma:Heart . +fma:Interatrial_septum bfo:part_of fma:Heart . +fma:Interventricular_septum bfo:part_of fma:Heart . +fma:Atrioventricular_septum bfo:part_of fma:Heart . +fma:Right_side_of_heart bfo:part_of fma:Heart . +fma:Left_side_of_heart bfo:part_of fma:Heart . +fma:Tricuspid_valve bfo:part_of fma:Heart . +fma:Mitral_valve bfo:part_of fma:Heart . +fma:Aortic_valve bfo:part_of fma:Heart . +fma:Pulmonary_valve bfo:part_of fma:Heart . +fma:Wall_of_heart bfo:part_of fma:Heart . +fma:Systemic_capillary_bed_of_heart bfo:part_of fma:Heart . +fma:Lymphatic_capillary_bed_of_heart bfo:part_of fma:Heart . +fma:Cavity_of_right_ventricle bfo:part_of fma:Heart . +fma:Cavity_of_left_atrium bfo:part_of fma:Heart . +fma:Cavity_of_left_ventricle bfo:part_of fma:Heart . +fma:Fibrous_skeleton_of_heart bfo:part_of fma:Heart . +fma:Cavity_of_inflow_part_of_right_atrium bfo:part_of fma:Cavity_of_right_atrium . +fma:Ostium_of_superior_vena_cava bfo:part_of fma:Cavity_of_right_atrium . +fma:Ostium_of_inferior_vena_cava bfo:part_of fma:Cavity_of_right_atrium . +fma:Variant_branch_of_right_coronary_arterial_tree bfo:part_of fma:Right_coronary_artery . +fma:Recurrent_atrioventricular_branch_of_right_coronary_artery bfo:part_of fma:Right_coronary_artery . +fma:Lateral_atrial_branch_of_right_coronary_artery bfo:part_of fma:Right_coronary_artery . +fma:Wall_of_left_coronary_artery bfo:part_of fma:Left_coronary_artery . +fma:Lumen_of_left_coronary_artery bfo:part_of fma:Left_coronary_artery . +fma:Trunk_of_left_coronary_artery bfo:part_of fma:Left_coronary_artery . +fma:Membranous_part_of_interatrial_septum bfo:part_of fma:Interatrial_septum . +fma:Fibrocollagenous_connective_tissue_of_interatrial_septum bfo:part_of fma:Interatrial_septum . +fma:Muscular_part_of_interatrial_septum bfo:part_of fma:Interatrial_septum . +fma:Fibrocollagenous_connective_tissue_of_interventricular_septum bfo:part_of fma:Interventricular_septum . +fma:Muscular_part_of_interventricular_septum bfo:part_of fma:Interventricular_septum . +fma:Membranous_part_of_interventricular_septum bfo:part_of fma:Interventricular_septum . +fma:Endothelium_of_atrioventricular_septum bfo:part_of fma:Atrioventricular_septum . +fma:Fibroelastic_connective_tissue_of_endocardium bfo:part_of fma:Atrioventricular_septum . +fma:Neural_network_of_right_side_of_heart bfo:part_of fma:Right_side_of_heart . +fma:Small_cardiac_vein bfo:part_of fma:Right_side_of_heart . +fma:Right_marginal_vein bfo:part_of fma:Right_side_of_heart . +fma:Neural_network_of_left_side_of_heart bfo:part_of fma:Left_side_of_heart . +fma:Great_cardiac_vein bfo:part_of fma:Left_side_of_heart . +fma:Left_atrium bfo:part_of fma:Left_side_of_heart . +fma:Endothelium_of_tricuspid_valve bfo:part_of fma:Tricuspid_valve . +fma:Anterior_leaflet_of_tricuspid_valve bfo:part_of fma:Tricuspid_valve . +fma:Posterior_leaflet_of_tricuspid_valve bfo:part_of fma:Tricuspid_valve . +fma:Endothelium_of_mitral_valve bfo:part_of fma:Mitral_valve . +fma:Anterior_leaflet_of_mitral_valve bfo:part_of fma:Mitral_valve . +fma:Posterior_leaflet_of_mitral_valve bfo:part_of fma:Mitral_valve . +fma:Endothelium_of_aortic_valve bfo:part_of fma:Aortic_valve . +fma:Right_posterior_cusp_of_aortic_valve bfo:part_of fma:Aortic_valve . +fma:Anterior_cusp_of_aortic_valve bfo:part_of fma:Aortic_valve . +fma:Left_anterior_cusp_of_pulmonary_valve bfo:part_of fma:Pulmonary_valve . +fma:Right_anterior_cusp_of_pulmonary_valve bfo:part_of fma:Pulmonary_valve . +fma:Posterior_cusp_of_pulmonary_valve bfo:part_of fma:Pulmonary_valve . +fma:Endocardium bfo:part_of fma:Wall_of_heart . +fma:Epicardium bfo:part_of fma:Wall_of_heart . +fma:Myocardium bfo:part_of fma:Wall_of_heart . +fma:Ostium_of_least_cardiac_vein_of_right_ventricle bfo:part_of fma:Cavity_of_right_ventricle . +fma:Cavity_of_inflow_part_of_right_ventricle bfo:part_of fma:Cavity_of_right_ventricle . +fma:Ostium_of_pulmonary_trunk bfo:part_of fma:Cavity_of_right_ventricle . +fma:Cavity_of_inflow_part_of_left_atrium bfo:part_of fma:Cavity_of_left_atrium . +fma:Cavity_proper_of_left_atrium bfo:part_of fma:Cavity_of_left_atrium . +fma:Ostium_of_right_superior_pulmonary_vein bfo:part_of fma:Cavity_of_left_atrium . +fma:Cavity_of_inflow_part_of_left_ventricle bfo:part_of fma:Cavity_of_left_ventricle . +fma:Ostium_of_aorta bfo:part_of fma:Cavity_of_left_ventricle . +fma:Cavity_of_outflow_part_of_left_ventricle bfo:part_of fma:Cavity_of_left_ventricle . +fma:Connective_tissue_of_fibrous_skeleton_of_heart bfo:part_of fma:Fibrous_skeleton_of_heart . +fma:Fibrocollagenous_connective_tissue_of_fibrous_skeleton_of_heart bfo:part_of fma:Fibrous_skeleton_of_heart . +fma:Central_fibrous_body bfo:part_of fma:Fibrous_skeleton_of_heart . +fma:Cavity_proper_of_inflow_part_of_right_atrium bfo:part_of fma:Cavity_of_inflow_part_of_right_atrium . +fma:Ostium_of_coronary_sinus bfo:part_of fma:Cavity_of_inflow_part_of_right_atrium . +fma:Ostium_of_least_cardiac_vein_of_right_atrium bfo:part_of fma:Cavity_of_inflow_part_of_right_atrium . +fma:Lumen_of_trunk_of_left_coronary_artery bfo:part_of fma:Trunk_of_left_coronary_artery . +fma:Wall_of_trunk_of_left_coronary_artery bfo:part_of fma:Trunk_of_left_coronary_artery . +fma:Endocardium_of_interatrial_septum bfo:part_of fma:Membranous_part_of_interatrial_septum . +fma:Fibrocollagenous_connective_tissue_of_muscular_part_of_interatrial_septum bfo:part_of fma:Muscular_part_of_interatrial_septum . +fma:Heart rdfs:subClassOf fma:Organ_with_cavitated_organ_parts . +fma:Cavity_of_right_atrium rdfs:subClassOf fma:Cavity_of_atrium . +fma:Cardiac_vein rdfs:subClassOf fma:Systemic_vein . +fma:Neural_network_of_heart rdfs:subClassOf fma:Neural_network_of_organ . +fma:Coronary_sinus rdfs:subClassOf fma:Trunk_of_systemic_vein . +fma:Right_coronary_artery rdfs:subClassOf fma:Coronary_artery . +fma:Left_coronary_artery rdfs:subClassOf fma:Coronary_artery . +fma:Interatrial_septum rdfs:subClassOf fma:Cardiac_septum . +fma:Interventricular_septum rdfs:subClassOf fma:Cardiac_septum . +fma:Atrioventricular_septum rdfs:subClassOf fma:Region_of_membranous_part_of_interventricular_septum . +fma:Right_side_of_heart rdfs:subClassOf fma:Zone_of_heart . +fma:Left_side_of_heart rdfs:subClassOf fma:Zone_of_heart . +fma:Tricuspid_valve rdfs:subClassOf fma:Atrioventricular_valve . +fma:Mitral_valve rdfs:subClassOf fma:Atrioventricular_valve . +fma:Aortic_valve rdfs:subClassOf fma:Cardiac_valve . +fma:Pulmonary_valve rdfs:subClassOf fma:Cardiac_valve . +fma:Wall_of_heart rdfs:subClassOf fma:Wall_of_organ . +fma:Systemic_capillary_bed_of_heart rdfs:subClassOf fma:Systemic_capillary_bed . +fma:Lymphatic_capillary_bed_of_heart rdfs:subClassOf fma:Lymphatic_capillary_bed . +fma:Cavity_of_right_ventricle rdfs:subClassOf fma:Cavity_of_ventricle . +fma:Cavity_of_left_atrium rdfs:subClassOf fma:Cavity_of_atrium . +fma:Cavity_of_left_ventricle rdfs:subClassOf fma:Cavity_of_ventricle . +fma:Fibrous_skeleton_of_heart rdfs:subClassOf fma:Fibrous_connective_tissue . +fma:Cavity_of_inflow_part_of_right_atrium rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Ostium_of_superior_vena_cava rdfs:subClassOf fma:Orifice_of_vein . +fma:Ostium_of_inferior_vena_cava rdfs:subClassOf fma:Orifice_of_vein . +fma:Variant_branch_of_right_coronary_arterial_tree rdfs:subClassOf fma:Branch_of_right_coronary_artery . +fma:Recurrent_atrioventricular_branch_of_right_coronary_artery rdfs:subClassOf fma:Branch_of_right_coronary_artery . +fma:Lateral_atrial_branch_of_right_coronary_artery rdfs:subClassOf fma:Atrial_branch_of_right_coronary_artery . +fma:Wall_of_left_coronary_artery rdfs:subClassOf fma:Wall_of_coronary_artery . +fma:Lumen_of_left_coronary_artery rdfs:subClassOf fma:Lumen_of_coronary_artery . +fma:Trunk_of_left_coronary_artery rdfs:subClassOf fma:Trunk_of_coronary_artery . +fma:Membranous_part_of_interatrial_septum rdfs:subClassOf fma:Membranous_part_of_cardiac_septum . +fma:Fibrocollagenous_connective_tissue_of_interatrial_septum rdfs:subClassOf fma:Fibrocollagenous_sheath_of_cardiac_muscle_tissue . +fma:Muscular_part_of_interatrial_septum rdfs:subClassOf fma:Muscular_part_of_cardiac_septum . +fma:Fibrocollagenous_connective_tissue_of_interventricular_septum rdfs:subClassOf fma:Fibrocollagenous_sheath_of_cardiac_muscle_tissue . +fma:Muscular_part_of_interventricular_septum rdfs:subClassOf fma:Muscular_part_of_cardiac_septum . +fma:Membranous_part_of_interventricular_septum rdfs:subClassOf fma:Membranous_part_of_cardiac_septum . +fma:Endothelium_of_atrioventricular_septum rdfs:subClassOf fma:Endothelium_of_endocardium . +fma:Fibroelastic_connective_tissue_of_endocardium rdfs:subClassOf fma:Fibroelastic_connective_tissue . +fma:Neural_network_of_right_side_of_heart rdfs:subClassOf fma:Neural_network_of_organ_part . +fma:Neural_network_of_left_side_of_heart rdfs:subClassOf fma:Neural_network_of_organ_part . +fma:Left_atrium rdfs:subClassOf fma:Cardiac_atrium . +fma:Endothelium_of_tricuspid_valve rdfs:subClassOf fma:Endothelium_of_endocardium . +fma:Anterior_leaflet_of_tricuspid_valve rdfs:subClassOf fma:Leaflet_of_tricuspid_valve . +fma:Posterior_leaflet_of_tricuspid_valve rdfs:subClassOf fma:Leaflet_of_tricuspid_valve . +fma:Endothelium_of_mitral_valve rdfs:subClassOf fma:Endothelium_of_endocardium . +fma:Anterior_leaflet_of_mitral_valve rdfs:subClassOf fma:Leaflet_of_mitral_valve . +fma:Posterior_leaflet_of_mitral_valve rdfs:subClassOf fma:Leaflet_of_mitral_valve . +fma:Endothelium_of_aortic_valve rdfs:subClassOf fma:Endothelium_of_endocardium . +fma:Right_posterior_cusp_of_aortic_valve rdfs:subClassOf fma:Cusp_of_aortic_valve . +fma:Anterior_cusp_of_aortic_valve rdfs:subClassOf fma:Cusp_of_aortic_valve . +fma:Left_anterior_cusp_of_pulmonary_valve rdfs:subClassOf fma:Cusp_of_pulmonary_valve . +fma:Right_anterior_cusp_of_pulmonary_valve rdfs:subClassOf fma:Cusp_of_pulmonary_valve . +fma:Posterior_cusp_of_pulmonary_valve rdfs:subClassOf fma:Cusp_of_pulmonary_valve . +fma:Endocardium rdfs:subClassOf fma:Tunica_intima . +fma:Epicardium rdfs:subClassOf fma:Region_of_visceral_serous_pericardium . +fma:Myocardium rdfs:subClassOf fma:Muscle_body . +fma:Ostium_of_least_cardiac_vein_of_right_ventricle rdfs:subClassOf fma:Ostium_of_least_cardiac_vein . +fma:Cavity_of_inflow_part_of_right_ventricle rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Ostium_of_pulmonary_trunk rdfs:subClassOf fma:Orifice_of_artery . +fma:Cavity_of_inflow_part_of_left_atrium rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Cavity_proper_of_left_atrium rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Ostium_of_right_superior_pulmonary_vein rdfs:subClassOf fma:Ostium_of_superior_pulmonary_vein . +fma:Cavity_of_inflow_part_of_left_ventricle rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Ostium_of_aorta rdfs:subClassOf fma:Orifice_of_artery . +fma:Cavity_of_outflow_part_of_left_ventricle rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Connective_tissue_of_fibrous_skeleton_of_heart rdfs:subClassOf fma:Fibroelastic_connective_tissue . +fma:Fibrocollagenous_connective_tissue_of_fibrous_skeleton_of_heart rdfs:subClassOf fma:Fibrocollagenous_connective_tissue . +fma:Central_fibrous_body rdfs:subClassOf fma:Subdivision_of_fibrous_skeleton_of_heart . +fma:Cavity_proper_of_inflow_part_of_right_atrium rdfs:subClassOf fma:Subdivision_of_cavity_of_cardiac_chamber . +fma:Ostium_of_coronary_sinus rdfs:subClassOf fma:Orifice_of_vein . +fma:Ostium_of_least_cardiac_vein_of_right_atrium rdfs:subClassOf fma:Ostium_of_least_cardiac_vein . +fma:Lumen_of_trunk_of_left_coronary_artery rdfs:subClassOf fma:Lumen_of_trunk_of_coronary_artery . +fma:Wall_of_trunk_of_left_coronary_artery rdfs:subClassOf fma:Wall_of_trunk_of_coronary_artery . +fma:Endocardium_of_interatrial_septum rdfs:subClassOf fma:Endocardium_of_cardiac_septum . +fma:Fibrocollagenous_connective_tissue_of_muscular_part_of_interatrial_septum rdfs:subClassOf fma:Fibrocollagenous_sheath_of_cardiac_muscle_tissue . diff --git a/crates/osint-bake/src/bin/fma.rs b/crates/osint-bake/src/bin/fma.rs index f6bc36718..948e0f328 100644 --- a/crates/osint-bake/src/bin/fma.rs +++ b/crates/osint-bake/src/bin/fma.rs @@ -1,23 +1,32 @@ //! FMA anatomy slice — the "real test" of the dual-membership lattice. //! -//! **Hydrated from an FMA `.ttl` fixture** (`data/fma-heart.fixture.ttl`) via +//! **Hydrated from a real FMA heart subtree** (`data/fma-heart.ttl`, extracted +//! from the 266 MB `fma.owl` v5.0.0 by `tools/extract_fma_heart.py`) via //! [`hydrate_fma`] — no longer hand-built. Stands up a Foundational-Model-of- -//! Anatomy-shaped slice of the **heart** (organ → chambers → wall layers) and -//! proves that one node resolves to BOTH addresses at once: +//! Anatomy slice of the **heart** (organ → its 22 major subdivisions: the four +//! chamber cavities, the four valves, the three cardiac septa, both coronary +//! arteries, the wall's endo/epi/myocardium, the fibrous skeleton, … → their +//! sub-parts) and proves that one node resolves to BOTH addresses at once: //! -//! * **part-of position** (basin-local): HEEL=[Organ:Heart], HIP=[Chamber:id], -//! TWIG=[Wall:id] — where the node *is* in the body, read straight off the key -//! (the partonomy walk fills the cascade; deeper tiers stay 0 until the real -//! 75K FMA hydrates tissues/cells through the same walk). +//! * **part-of position** (basin-local): HEEL=[Organ:Heart], HIP=[part:id], +//! TWIG=[sub-part:id], LEAF=[deeper:id] — where the node *is* in the body, read +//! straight off the key (the partonomy walk fills the cascade; deeper tiers +//! stay 0 until the full 75K-term FMA hydrates tissues/cells through the same +//! walk). //! * **leaf-limited global type** (the CEILING pole, HEEL=HIP=TWIG=0xFFFF, -//! LEAF=type): "cardiac muscle tissue", "endothelium" — cross-cutting types -//! that appear in *every* chamber. The deepest sentinel run (through TWIG) -//! makes the LEAF the sole discriminator: "limited to the leaf". +//! LEAF=type): "Atrioventricular valve", "Endothelium of endocardium", +//! "Cavity of ventricle" — cross-cutting FMA `subClassOf` types carried by +//! structures in *different* parts of the heart. The deepest sentinel run +//! (through TWIG) makes the LEAF the sole discriminator: "limited to the leaf". //! -//! The same `Cardiac muscle tissue` ceiling node is the `is-a` target for the -//! myocardium tissue in all four chambers — visibly cross-cutting. Emits -//! `cockpit/public/fma.soa` (OSO1, the cockpit's `/fma` view reads it) and -//! prints the dual-membership proof. +//! The proof picks the strongest real cross-cutter in the slice — the ceiling +//! type that is the `is-a` target of the most structures (e.g. `Subdivision of +//! cavity of cardiac chamber`, shared by the inflow/outflow cavities of all four +//! chambers; or `Endothelium of endocardium`, shared by the valve and septum +//! endothelia) — then a member sitting deepest in the body, and shows it +//! resolving to both its part-of cascade and that shared global type. Emits +//! `cockpit/public/fma.soa` (OSO1, the cockpit's `/fma` view reads it) and prints +//! the dual-membership proof. //! //! ## Relation to OGAR PR #116 (the FMA canon) //! @@ -171,12 +180,14 @@ impl Builder { } } -/// Embedded FMA heart fixture — real class names + the canonical FMA predicate -/// set. The production path hydrates the 266 MB `fma.owl` through -/// `lance-graph-rdf` at the spine; this light bake hydrates the fixture so -/// `/fma` renders without the lance/datafusion closure. See -/// `data/fma-heart.fixture.ttl`. -const FMA_TTL: &str = include_str!("../../data/fma-heart.fixture.ttl"); +/// Embedded real FMA heart subtree — extracted from `fma.owl` (FMA v5.0.0, +/// CC BY 3.0) by `tools/extract_fma_heart.py`: a balanced BFS of the +/// `regional_part`/`constitutional_part` partonomy from `FMA:Heart` (depth ≤ 4, +/// per-parent fan-out capped so no single subtree dominates). The production +/// path hydrates the full 266 MB `fma.owl` through `lance-graph-rdf` at the +/// spine; this light bake hydrates the committed subtree so `/fma` renders +/// without the lance/datafusion closure. See `data/fma-heart.ttl`. +const FMA_TTL: &str = include_str!("../../data/fma-heart.ttl"); /// Hydrate an FMA `.ttl` fragment into the bake's [`Builder`] — the light-bake /// twin of `lance_graph_ontology::hydrate_fma`. Walk the `bfo:part_of` partonomy @@ -321,17 +332,51 @@ fn main() { let b = hydrate_fma(FMA_TTL); let bytes = emit_oso1(&b); - // dual-membership proof: a hydrated wall layer carries BOTH addresses. - let tissue = b - .nodes + // Dual-membership proof: pick the strongest real cross-cutter in the slice — + // the ceiling TYPE that is the `is-a` target of the most structures — then the + // member sitting deepest in the body. Whatever the hydrated heart slice + // contains, this surfaces a node that genuinely resolves to BOTH a basin-local + // part-of cascade and a cross-cutting global type. `tier_depth` counts the + // non-zero HHTL tiers: how deep into the body the part-of address runs. + let tier_depth = |n: usize| -> usize { + let k = &b.nodes[n].key; + [k.heel(), k.hip(), k.twig(), k.leaf(), k.family_v2()] + .iter() + .filter(|&&t| t != 0) + .count() + }; + // group is-a edges by ceiling-type target → the members that share it. + let mut by_type: std::collections::BTreeMap> = + std::collections::BTreeMap::new(); + for &(s, t, rel) in &b.edges { + if rel == REL_IS_A { + by_type.entry(t).or_default().push(s); + } + } + // strongest cross-cutter: most members, ties broken by label (deterministic). + let (>ype, sharers) = by_type + .iter() + .max_by(|x, y| { + x.1.len() + .cmp(&y.1.len()) + .then_with(|| b.nodes[*y.0].label.cmp(&b.nodes[*x.0].label)) + }) + .expect("the slice has is-a edges"); + // anchor: the member sitting deepest in the body (ties broken by label). + let &anchor = sharers .iter() - .position(|x| x.label == "Myocardium of left ventricle") - .expect("LV myocardium hydrated from the fixture"); - let key = &b.nodes[tissue].key; + .max_by(|&&x, &&y| { + tier_depth(x) + .cmp(&tier_depth(y)) + .then_with(|| b.nodes[y].label.cmp(&b.nodes[x].label)) + }) + .expect("a cross-cutting type has members"); + + let key = &b.nodes[anchor].key; + let show = |t: u16| format!("[{:02x}:{:02x}]", t >> 8, t & 0xFF); println!("── FMA dual-membership proof ──"); - println!("node: {}", b.nodes[tissue].label); + println!("node: {}", b.nodes[anchor].label); // the partonomy IS the key: each HHTL tier is an 8:8 [mixin:identity] pair. - let show = |t: u16| format!("[{:02x}:{:02x}]", t >> 8, t & 0xFF); println!( " part-of address — HHTL 8:8 [mixin:identity]: HEEL {} HIP {} TWIG {} LEAF {} family {}", show(key.heel()), @@ -340,13 +385,7 @@ fn main() { show(key.leaf()), show(key.family_v2()), ); - // its is-a edge → the leaf-limited global type (ceiling pole) - let gtype = b - .edges - .iter() - .find(|&&(s, _, rel)| s == tissue && rel == REL_IS_A) - .map(|&(_, t, _)| t) - .expect("tissue is-a a global type"); + // its is-a edge → the leaf-limited global type (ceiling pole). let gk = &b.nodes[gtype].key; println!( " is-a global type (ceiling): {} HEEL={:#06x} HIP={:#06x} TWIG={:#06x} LEAF={}", @@ -356,16 +395,15 @@ fn main() { gk.twig(), gk.leaf() ); - // cross-cutting: how many chambers' tissues share this one global type? - let members = b - .edges - .iter() - .filter(|&&(_, t, rel)| t == gtype && rel == REL_IS_A) - .count(); + // cross-cutting: how many structures across the heart share this one type? println!( - " '{}' is the is-a target of {members} tissues across the chambers (cross-cutting)", - b.nodes[gtype].label + " '{}' is the is-a target of {} structures in different parts of the heart (cross-cutting)", + b.nodes[gtype].label, + sharers.len() ); + let mut shared_by: Vec<&str> = sharers.iter().map(|&m| b.nodes[m].label.as_str()).collect(); + shared_by.sort_unstable(); + println!(" shared by: {}", shared_by.join(", ")); let n_types = b.nodes.iter().filter(|x| x.class == C_TYPE).count(); println!( diff --git a/crates/osint-bake/tools/extract_fma_heart.py b/crates/osint-bake/tools/extract_fma_heart.py new file mode 100644 index 000000000..5644b5763 --- /dev/null +++ b/crates/osint-bake/tools/extract_fma_heart.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""Extract the FMA *heart* part-of subtree from the real fma.owl into the +line-oriented Turtle subset the q2 light bake hydrates (osint-bake/src/fma_ttl.rs). + +Source (provenance): fma.owl, FMA v5.0.0, BioPortal #29 / UW Structural +Informatics Group, CC BY 3.0, SHA-256 59465eb503c418dbf9216d362b94c7f42bc3b96b297e553ec17c7fee77979e44, +mirrored at github.com/AdaWorldAPI/q2/releases/download/fma-ontology-5.0.0/fma.owl + +This is a one-off data-prep tool. The PRODUCTION hydrator is +lance_graph_ontology::hydrate_fma (rio_xml) at the spine; this script exists so +the light bake (no lance/datafusion closure) can carry a *real* heart subtree +instead of a hand-authored fixture. Streaming ET.iterparse keeps memory bounded +(only label / part / superclass maps are retained, not the 1.5M-triple graph). + +Usage: python3 extract_fma_heart.py +""" +import sys +import xml.etree.ElementTree as ET + +RDF = "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}" +RDFS = "{http://www.w3.org/2000/01/rdf-schema#}" +OWL = "{http://www.w3.org/2002/07/owl#}" +BASE = "http://purl.org/sig/ont/fma/" + +# forward "has-part" properties (whole → part); the part is a CHILD of the whole. +PART_PROPS = {"regional_part", "constitutional_part", "systemic_part"} +HEART = "fma7088" # FMA:Heart +MAX_DEPTH = 4 # organ → chamber → wall → tissue +MAX_NODES = 80 # keep the cockpit slice legible +MAX_CHILDREN = 3 # per NON-root parent — a balanced cross-section, not a + # coronary-artery dump (FMA's coronary tree has ~40 named + # branches; uncapped BFS lets it starve every other part). + # The root (Heart) is exempt: all its major subdivisions + # — 4 chamber cavities, 4 valves, 3 septa, both coronary + # arteries, wall, fibrous skeleton, … — are the point. + +CLEAR_TAGS = {OWL + t for t in ("Class", "Axiom", "ObjectProperty", + "AnnotationProperty", "DatatypeProperty", + "NamedIndividual")} | {RDF + "Description"} + + +def local(iri): + return iri.rsplit("/", 1)[-1] if iri else None + + +def main(src, out): + label, children, supers = {}, {}, {} + for _ev, elem in ET.iterparse(src, events=("end",)): + if elem.tag == OWL + "Class": + about = elem.get(RDF + "about") + if about and about.startswith(BASE): + cid = local(about) + lab = elem.find(RDFS + "label") + if lab is not None and lab.text: + label[cid] = lab.text.strip() + for sc in elem.findall(RDFS + "subClassOf"): + res = sc.get(RDF + "resource") + if res and res.startswith(BASE): # named superclass → is-a + supers.setdefault(cid, []).append(local(res)) + continue + restr = sc.find(OWL + "Restriction") + if restr is None: + continue + op = restr.find(OWL + "onProperty") + sv = restr.find(OWL + "someValuesFrom") + if op is None or sv is None: + continue + prop = local(op.get(RDF + "resource") or "") + tgt = sv.get(RDF + "resource") + if prop in PART_PROPS and tgt and tgt.startswith(BASE): + children.setdefault(cid, []).append(local(tgt)) # tgt is part of cid + if elem.tag in CLEAR_TAGS: + elem.clear() + + # BFS the heart subtree; first whole to reach a part becomes its tree-parent + # (FMA part-of is a DAG; we take a spanning tree rooted at the heart). Each + # non-root whole contributes at most MAX_CHILDREN parts, so breadth stays + # balanced across the heart's subdivisions instead of draining into one + # deep subtree. Because every level-1 node is popped before any level-2 node + # (plain BFS), this caps fan-out per parent fairly across the whole level. + parent, depth, order = {}, {HEART: 0}, [HEART] + queue = [HEART] + while queue and len(order) < MAX_NODES: + n = queue.pop(0) + if depth[n] >= MAX_DEPTH: + continue + cap = MAX_NODES if n == HEART else MAX_CHILDREN # root keeps every major part + taken = 0 + for c in sorted(set(children.get(n, []))): + if taken >= cap: + break + if c in depth or c not in label: # unseen + actually a labelled class + continue + parent[c], depth[c] = n, depth[n] + 1 + order.append(c) + queue.append(c) + taken += 1 + if len(order) >= MAX_NODES: + break + + def tok(cid): + # real FMA label → a prefix:token the line-Turtle reader splits on + # (label_of() turns '_' back into spaces). alnum kept, else '_'. + lab = label.get(cid, cid) + t = "".join(ch if ch.isalnum() else "_" for ch in lab).strip("_") + while "__" in t: + t = t.replace("__", "_") + return "fma:" + t + + lines = [ + "# fma-heart.ttl — REAL FMA heart subtree, extracted from fma.owl (FMA v5.0.0).", + "# Source: BioPortal #29 / UW Structural Informatics Group, CC BY 3.0,", + "# SHA-256 59465eb503c418dbf9216d362b94c7f42bc3b96b297e553ec17c7fee77979e44,", + "# mirror github.com/AdaWorldAPI/q2/releases/download/fma-ontology-5.0.0/fma.owl", + "# Generated by tools/extract_fma_heart.py (streaming ET; regional_part +", + "# constitutional_part partonomy, BFS from FMA:Heart fma7088, depth<=%d, <=%d nodes)." + % (MAX_DEPTH, MAX_NODES), + "# The 266 MB OWL is NOT committed; the production hydrator is the spine's", + "# lance_graph_ontology::hydrate_fma. This is the real-data heart slice the", + "# q2 /fma render surface hydrates.", + "", + ] + seen_types = set() + for c in order: + if c in parent: + lines.append(f"{tok(c)} bfo:part_of {tok(parent[c])} .") + for c in order: # cross-cutting type: first named superclass + for s in supers.get(c, []): + if s in label and s not in depth: # a real category outside the part-of tree + lines.append(f"{tok(c)} rdfs:subClassOf {tok(s)} .") + seen_types.add(s) + break + open(out, "w").write("\n".join(lines) + "\n") + + # inspection summary to stderr + print(f"heart subtree: {len(order)} nodes (<= depth {MAX_DEPTH}), " + f"{sum(1 for c in order if c in parent)} part-of edges, " + f"{len(seen_types)} cross-cutting types", file=sys.stderr) + for c in order[:14]: + print(f" depth {depth[c]} {label.get(c,c)}" + + (f" ⊂ {label.get(parent[c])}" if c in parent else " (root)"), file=sys.stderr) + + +if __name__ == "__main__": + main(sys.argv[1], sys.argv[2])