Skip to main content

Conceptualization: Relations, derivedFrom, and the Genealogy Spine

📍 Where we are: Part III · Conceptualization — the lifecycle phase where the classes from the last chapter are wired together with relations. Methodology: NeOn's conceptualization activity, run on SAMOD's test-first loop (two ontology-engineering methodologies introduced in the specification chapter) — each relation earns its place by answering a competency question: a plain-English question the finished ontology must be able to answer, used here as a pass/fail acceptance test (the 23 are numbered CQ-01…CQ-23 in the specification). The worked example is a real CHO (Chinese Hamster Ovary, the industry-standard antibody-producing cell line) monoclonal-antibody (mAb) campaign.

A taxonomy of classes is a set of labeled boxes. What makes it a graph — something you can walk, query, and reason over — is the relations between the boxes. This chapter conceptualizes the load-bearing ones, and it spends most of its weight on a single relation, bp:derivedFrom, because that one edge is the spine the entire digital thread — the connected record that links every step of a product's making — of a monoclonal-antibody lot hangs from: every vial a patient receives traces, hop by hop, back to one frozen vial of working cell bank (a characterized, frozen stock of the production cells, explained in full below). The wet-lab terms below — fed-batch, titer, glycosylation, transfection, cell bank — are walked in plain language in Book 1's bioprocessing overview; this chapter models them rather than re-teaches them. The other three relations it builds — affectsQuality, occursIn, contains — each answer a different manufacturing question, and one of them, contains, exists precisely to not be confused with lineage when a recall has to be scoped.

The simple version

A family tree has two kinds of line in it. One says "descended from" — your grandparent is your ancestor, forever, no matter what. The other says "lives in the same house right now" — which changes when someone moves out. Confuse the two and you get nonsense ("my roommate is my ancestor"). A bioprocess graph has the same two lines: derivedFrom is the permanent "made from" ancestry that roots every vial of antibody in one frozen cell bank, and contains is the mutable "packed inside right now" that changes every time a shipping case is opened. This chapter draws both, keeps them apart on purpose, and adds the Quality-by-Design edge that says which process knob affects which quality of the drug.

Start from the questions

Four competency questions from the specification shape this chapter, and they split cleanly across the four relations. CQ-06 ("which process parameters affect monomer purity?") is answered directly by affectsQuality — and monomer purity is the headline release attribute of an antibody, the fraction that elutes as intact monomer by size-exclusion chromatography (SEC), as opposed to high-molecular-weight aggregate or low-molecular-weight fragments. CQ-21 ("which vessel did a cell-culture run occur in?") is answered by occursIn, the edge that lets us keep the batch material and its bioreactor as separate things. CQ-13 ("what serialized vials are contained, transitively, within a given pallet?") is the contains walk, and CQ-14 ("is containment kept distinct from genealogy?") is the guard that proves contains and derivedFrom never collapse into each other — the difference between scoping a recall by lineage and accidentally recalling a shipping carton. Holding those four in view, every relation below is judged by whether it makes its manufacturing question answerable. The chapter carries its weight on derivedFrom and contains specifically because they are the chapter's only two transitive relations — the pair whose long-range walks would silently merge if authored carelessly — so working the authoring procedure on them, rather than on affectsQuality or occursIn, is what shows both how a relation is built and how the one dangerous confusion in a recall is designed out.

The fork that decides everything: object vs datatype property

OWL (the Web Ontology Language, the W3C standard the model is written in) draws one distinction worth fixing before any relation is authored: an object property links a thing to another thing, while a datatype property links a thing to a literal value [1]. bp:derivedFrom relates a lot to a parent lot — an edge you can walk back to the cell bank. bp:monomerPct relates a drug-substance lot to the number 98.611 — its SEC %monomer release reading (the percentage of the product that elutes as intact monomer), a value you can read. That is the difference between a relationship and a measurement, and it decides which of the two you author. The genealogy spine is object properties all the way down; the release numbers an antibody lot must hit — monomer purity, high-molecular-weight aggregate, charge-variant distribution, protein concentration — are datatype properties hung on the lot. The four object properties this chapter builds line up like this — domain and range pinning each edge, which two close to long-range walks, the competency question each answers, and the external term each aligns up to (the OBO Relation Ontology's RO_0001000 derives from [5] and BFO's BFO_0000066 occurs in [6]):

RelationDomain → RangeTransitive?AnswersAligns up to
derivedFromMaterial → MaterialyesCQ-01/02/04 lineage and impactRO derives from (RO_0001000)
affectsQualityProcessParameter → QualityAttributenoCQ-06 the QbD linklocal (no 1:1 standard)
occursInProcess → EquipmentnoCQ-21 run-to-vesselBFO occurs in (BFO_0000066)
containsPackage → MaterialyesCQ-13 packing walk; CQ-14 the guardlocal (kept off lineage)

The two transitive rows are the ones the chapter works hardest to keep apart: both close to long-range walks, so collapsing them would let a shipping carton masquerade as an ancestor. Here is the seam, both kinds declared verbatim from the vocabulary. (In Turtle — the text syntax for RDF used throughout this book — a means "is a", a semicolon adds another fact about the same subject, and the colon-prefixes like bp:, owl:, and rdfs: simply namespace the terms; the prose around each block explains what it says.)

# bioproc.ttl — object property (an edge to walk) vs datatype property (a value to read).
bp:derivedFrom a owl:ObjectProperty , owl:TransitiveProperty ;
rdfs:label "derived from" ;
rdfs:domain bp:Material ; rdfs:range bp:Material ;
skos:definition "Relates a material to the parent material it originated from; transitive, so lineage is inferable to any depth." .

bp:monomerPct a owl:DatatypeProperty ; rdfs:label "SEC %monomer" .
bp:passageNumber a owl:DatatypeProperty ; rdfs:label "passage number" ; rdfs:range xsd:integer .

The rdfs:domain and rdfs:range on derivedFrom are not documentation — they are rules. Because both ends are pinned to bp:Material, asserting X derivedFrom Y types both X and Y as materials, and if Y were declared something disjoint from bp:Material — a person who signed the batch record, a piece of equipment, a process — the graph is flagged inconsistent. A loose edge becomes a typed one. This matters because lineage edges get loaded from many source systems — the electronic lab notebook (ELN), the manufacturing execution system (MES), and the laboratory information management system (LIMS) — and a careless load that points a lot's parent at the operator who ran it, or at the bioreactor it grew in, is exactly the kind of error that quietly poisons a genealogy walk; domain and range turn that error into a flagged contradiction instead of a silent lie.

How a relation was authored. Every edge in the table above was written by the same short procedure — the relation-level analogue of the competency-question recipe from the specification — and it is worth doing in the open on derivedFrom, the spine the rest of the chapter rests on:

  1. Read the verb out of the competency question. CQ-01 asks which materials does a given material derive from? The relation's name is the question's verb, lifted whole: derive frombp:derivedFrom. (The same step on CQ-13's what is contained within a pallet? yields bp:contains.)
  2. Fix the domain and the range from the question's two nouns. A material derives from a material, so both ends are bp:Material. Pinning them is what lets a wrong load — a parent that is an operator or a vessel — flag as a contradiction rather than corrupt the walk.
  3. Declare the BFO/RO superproperty and the property characteristics the question forces. CQ-01 says to any depth, which is a request for owl:TransitiveProperty; and because the field already has a faithful term for "one material comes from another," derivedFrom is declared rdfs:subPropertyOf the Relation Ontology's derives from (RO_0001000) in align.ttl [5] — reusing an existing standard term when one fits faithfully, and minting (coining) a new local term only when no faithful parent exists (which is exactly the case for affectsQuality).
  4. Name the competency question the edge now answers, so it is testable. derivedFrom is bound to CQ-01/02/04; if a later edit breaks the lineage walk, those tests go red. The relation, written this way, is already a specification of the query it must satisfy.

Run on contains, the same four steps land on a deliberately different answer at step 3 — transitive like derivedFrom, but not declared a sub-property of it — and that single divergence is the whole reason the chapter keeps containment (what a unit is packed inside, now) and genealogy (what a material was made from, permanently) apart, the distinction CQ-14 exists to guard.

The one transitive spine: derivedFrom

Transitivity is the single most valuable axiom in this book, and it lives on one property [1]. Declaring bp:derivedFrom a owl:TransitiveProperty tells a reasoner that if SEED-001 derivedFrom SEEDFLASK-001 and SEEDFLASK-001 derivedFrom WCB-CHO-001, then SEED-001 derivedFrom WCB-CHO-001 — and, hop after hop, that the drug substance DS-001 derives all the way up to the research cell bank, without anyone asserting a single long-range link. Only the immediate parent edges are stated. In the running example DS-001 has eleven ancestors, so its lineage chain is twelve materials deep counting the lot itself, and the seed train is where its first concrete edges of a specific campaign are laid down: a vial of WCB-CHO-001 is thawed, expanded through a shake flask, then a seed bioreactor, each scale-up a process that consumes one material and produces the next.

# instances.ttl — the seed train lays the first derivedFrom edges of the campaign, passage climbing.
bp:SEEDFLASK-001 a bp:ShakeFlaskCulture ; rdfs:label "shake-flask seed culture" ;
bp:derivedFrom bp:WCB-CHO-001 ; # rooted in the cell bank
bp:participatesIn bp:EXP-001 ;
bp:passageNumber 12 .
bp:SEED-001 a bp:SeedBioreactorCulture ; rdfs:label "SEED-001 (seed bioreactor culture)" ;
bp:derivedFrom bp:SEEDFLASK-001 ; # ...one hop back to the shake flask
bp:participatesIn bp:EXP-002 ;
bp:passageNumber 16 . # the count carried forward, checkable at release
bp:BATCH-2026-001 a bp:Batch ; rdfs:label "BATCH-2026-001" ;
bp:derivedFrom bp:SEED-001 . # ...and so, transitively, back to WCB-CHO-001

Why the root needs genealogy: the cell-bank tiers

The node every one of these edges climbs toward is not arbitrary. Mammalian-cell antibody manufacturing builds its cell substrate in tiers — a research cell bank (RCB), a master cell bank (MCB) drawn from it, and a working cell bank (WCB) drawn from the master — and each routine campaign is inoculated from a WCB vial so the rare, irreplaceable master is spent slowly. These are not optional housekeeping; the tier structure is a regulatory expectation for cell substrates [4], because the whole identity, sterility, and viral safety of every future lot rests on a characterized, frozen origin. The model makes the tiers a small taxonomy under bp:CellBank, and — crucially — declares them mutually disjoint, so a single bank can never be typed as two tiers at once:

# bioproc.ttl — the cell-bank tiers, and the axiom that keeps a bank in exactly one tier.
bp:CellBank a owl:Class ; rdfs:subClassOf bp:Material ; rdfs:label "Cell bank" .
bp:ResearchCellBank a owl:Class ; rdfs:subClassOf bp:CellBank ; rdfs:label "Research cell bank (RCB)" .
bp:MasterCellBank a owl:Class ; rdfs:subClassOf bp:CellBank ; rdfs:label "Master cell bank (MCB)" .
bp:WorkingCellBank a owl:Class ; rdfs:subClassOf bp:CellBank ; rdfs:label "Working cell bank (WCB)" .
[] a owl:AllDisjointClasses ; owl:members ( bp:ResearchCellBank bp:MasterCellBank bp:WorkingCellBank ) .

In the dataset those tiers are themselves a derivedFrom chain — WCB-CHO-001 derivedFrom MCB-CHO-001 derivedFrom RCB-CHO-001 — so the spine does not start at the working bank; it climbs through master and research to the very first frozen ampoule. The working bank also reaches sideways off the spine to the facts that make it usable: it expresses bp:mAb-A (the antibody it produces), and — because bp:hasClone is asserted on the master bank it derives from — points transitively back to the single-cell clone bp:CLONE-7 the line was selected from. The CHO host that does the expressing is not a string — bp:WCB-CHO-001 bp:hasHostOrganism bp:CHO-host, the same bp:CHO-host instance the engineered cell line carries — so "which organism makes this product?" resolves to one shared individual aligned to NCBI Taxonomy (an external, authoritative registry of organisms) — one governed identifier every mention of the host resolves to, rather than the word "CHO" free-typed in a dozen places where the spellings can drift.

The cell line the banks are aliquoted from is itself the output of an occurrent — bp:CELLLINE-001 createdBy bp:TF-001, a transfection that takes the genetic construct as input and yields the engineered line as output (createdBy is domain-restricted to bp:CellLine, so it attaches to the line, never to a bank); the banks are then frozen aliquots of that line. Modeling that step as a process (not a footnote) is what lets "where did this line come from?" be a one-hop answer rather than a paragraph in a development report.

The passage count rides the same chain

The passage count — how many times the culture has been split and regrown — rides the genealogy. The working cell bank carries passageNumber 8, the shake-flask culture 12, the seed-bioreactor culture 16; each expansion adds generations [1]. Passage count is not bookkeeping: it bounds how long living cells may be grown before productivity drifts and product quality shifts, so a validated passage limit (here bp:PassageLimit-mAb-A bp:validatedPassageLimit 40) is a governance constraint a batch must respect. Because the count is a datatype property on each material, and lineage is the transitive object property between them, the graph answers the GMP question was this batch inoculated from cells within the validated passage limit? as a join rather than a manual reconstruction from lab notebooks — a fact established at the root meeting a count accumulated along the train.

derivedFrom carries two rdfs:subPropertyOf refinements — bp:fromBatch and bp:includedFraction — so a more specific downstream edge (a clarified fraction included in a Protein A capture pool, a material from a batch) still entails the generic lineage edge a reasoner walks. Those refinements earn their keep at the one place lineage genuinely forks backward — the capture pool. Three column cycles each produce an eluate (bp:ELU-001a, bp:ELU-001b, bp:ELU-001c, each derivedFrom the clarified harvest), but the pooling decision includes only two: bp:PApool-001 includedFraction bp:ELU-001a , bp:ELU-001b and bp:PApool-001 fromBatch bp:BATCH-2026-001. Because both includedFraction and fromBatch are sub-properties of derivedFrom, a reasoner still walks the generic spine from the pool up to the harvest and the batch — but the record now says which cycles pooled, rather than collapsing the fork into one anonymous batch edge. The excluded cycle bp:ELU-001c stays reachable as a sibling eluate yet is correctly not an includedFraction of the pool: lineage that remembers what was left out is lineage an investigation can actually use (the concrete individuals are laid down in the instances chapter).

A genealogy chain reading left to right: the working cell bank WCB-CHO-001 at passage 8 expands via the process EXP-001 in shake flask SF-01 into the shake-flask culture SEEDFLASK-001 at passage 12, which expands via EXP-002 in seed bioreactor SBR-01 into the seed-bioreactor culture SEED-001 at passage 16, which is the input to the cell-culture run CCP-001 that produces the production batch BATCH-2026-001 in BR-101; a dashed transitive derivedFrom arc runs from the batch back to the cell bank, and a note records the validated passage limit of 40 that passages 8, 12, and 16 are all within. The transitive spine in action: each expansion is a process turning one cell material into the next, only the immediate derivedFrom edges are asserted, and the dashed arc is what a reasoner infers — the batch traces back to the working cell bank through hops no one stated. Original diagram by the authors, created with AI assistance.

Why the bank must be characterized — and why a reasoner alone will not enforce it

The reason genealogy matters at the root is that a misidentified cell bank is the worst possible error in this entire industry. Cross-contaminated and misidentified cell lines have quietly corrupted decades of life-science work, and for a manufacturing root node the failure is uniquely dangerous: a wrong identity at the cell bank propagates transitively, with full confidence, to every batch, every drug-substance lot, and every vial that derives from it — and no downstream integrity check can catch it, because every downstream record is internally consistent. So a working cell bank is required to carry characterization evidence — identity (is it the cell line we think it is? — isoenzyme or STR profiling, or sequence-level confirmation), sterility/mycoplasma (free of bacterial and fungal contamination, plus a dedicated mycoplasma test the broad sterility assay will not catch), adventitious-agent / viral safety (a panel that includes the endogenous retrovirus-like particles CHO lines are known to carry), and genetic stability (the construct and copy number hold out to the end of the validated passage limit, not merely at thaw) — each a bp:CharacterizationResult. The passage cap of 40 and this characterization gate are two halves of one argument: the gate proves the bank is what it claims to be and stays that way across the generations the cap allows. The model encodes the requirement as a qualified cardinality restriction on the WCB class:

# bioproc.ttl — a working cell bank must carry at least one characterization result.
bp:WorkingCellBank rdfs:subClassOf [ a owl:Restriction ;
owl:onProperty bp:hasCharacterization ;
owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger ; owl:onClass bp:CharacterizationResult ] .

In the running example WCB-CHO-001 carries all four: bp:hasCharacterization bp:CR-identity , bp:CR-sterility , bp:CR-viral , bp:CR-genetic, each a result that isAbout the bank with a verdict "PASS". But OWL is open-world — an OWL reasoner treats anything you have not stated as merely not yet known, never as false, so a missing characterization reads as unknown, not absent — and the restriction therefore states the necessary condition without catching a gap. The runnable catch is a closed-world SHACL (Shapes Constraint Language, a shape-validation language) gate — closed-world meaning what is not in the data is treated as false — that demands the bank actually carry the evidence (CQ-17), and exactly one passage count, before it conforms:

# shapes.ttl — the cell-bank gate: a WCB must carry characterization and exactly one passage count.
bp:CellBankShape a sh:NodeShape ;
sh:targetClass bp:WorkingCellBank ;
sh:property [
sh:path bp:hasCharacterization ;
sh:minCount 1 ;
sh:message "A working cell bank must carry at least one characterization result." ] ;
sh:property [
sh:path bp:passageNumber ;
sh:minCount 1 ; sh:maxCount 1 ; sh:datatype xsd:integer ;
sh:message "A working cell bank must record exactly one passage count." ] .

The functional properties bp:hasHostOrganism, bp:hasClone, and bp:createdBy are declared owl:FunctionalProperty for the same reason: a bank descends from exactly one clone, has exactly one host, was created by one transfection — so two source systems each asserting the host collapse to one fact rather than duplicating provenance. This is where the instances chapter leaves its mark on the relations: the graph can pin a stable IRI to a bank, but it cannot make a living culture stop mutating. A culture at passage 60 is not, biologically, the same population it was at passage 5, and owl:sameAs cannot adjudicate whether it is still "the same line." The IRI is stable; the cells drift. That tension is the honest limit the passage limit and the characterization gate exist to manage — and the reason the genealogy spine is worth getting exactly right.

affectsQuality: the QbD knowledge as one edge

The preface promised that Quality by Design is secretly an ontology, and this is where that becomes literal. The link a development team works hardest to establish — this critical process parameter (CPP) affects that critical quality attribute (CQA) — is one object property waiting to be declared [2]. (A CPP is a knob the process can turn — feed rate, temperature; a CQA is a property of the drug the lot must hit — monomer purity.) For an antibody, "feed rate affects monomer purity" is not a slogan; it is the kind of finding that decides whether a lot aggregates in the bioreactor or comes out as intact monomer. Declared as bp:affectsQuality from a bp:ProcessParameter to a bp:QualityAttribute, the fact stops living in a development report and becomes a queryable edge:

# bioproc.ttl — the QbD link, declared with its domain and range.
bp:affectsQuality a owl:ObjectProperty ;
rdfs:label "affects quality" ;
rdfs:domain bp:ProcessParameter ; rdfs:range bp:QualityAttribute ;
skos:definition "The Quality-by-Design link: a critical process parameter affects a critical quality attribute." .

But an edge alone is a claim without backing. What makes process knowledge trustworthy is the evidence hung on the parameter — the criticality assessment, the normal operating range (NOR), the wider proven acceptable range (PAR), and the design-of-experiments study that established the link [2]. In the dataset both feed rate and culture temperature carry real affectsQuality edges to the monomer CQA, and feed rate carries its full evidence trail:

# instances.ttl — affectsQuality with its NOR/PAR evidence and the study that proved it.
bp:FeedRate a bp:ProcessParameter ; rdfs:label "feed rate (CPP)" ;
bp:affectsQuality bp:MonomerPct-CQA ;
bp:hasCriticality bp:CRIT-FeedRate ;
bp:hasNormalOperatingRange bp:NOR-FeedRate ; # NOR 0.35-0.45
bp:hasProvenAcceptableRange bp:PAR-FeedRate ; # PAR 0.30-0.50 (wider)
bp:establishedBy bp:DOE-07 .
bp:Temperature a bp:ProcessParameter ; rdfs:label "culture temperature (CPP)" ;
bp:affectsQuality bp:MonomerPct-CQA ;
bp:hasCriticality bp:CRIT-Temp . # temperature carries its own HIGH criticality too
bp:NOR-FeedRate a bp:NormalOperatingRange ; rdfs:label "feed-rate NOR 0.35-0.45" ;
bp:norLow 0.35 ; bp:norHigh 0.45 .
bp:PAR-FeedRate a bp:ProvenAcceptableRange ; rdfs:label "feed-rate PAR 0.30-0.50" ;
bp:parLow 0.30 ; bp:parHigh 0.50 .

The NOR is the tighter routine window the parameter is held to; the PAR is the wider region proven to still yield acceptable antibody. Note the unit, which the prose must not drop: the feed rate here is a relative, vessel-volume-normalized daily feed — 0.40 vessel-volumes/day at setpoint (i.e. 40 % of the vessel's working volume added per day; /d is the per-day unit code from UCUM, the standard catalogue of units of measure), held to a NOR of 0.35–0.45 inside a PAR of 0.30–0.50 /d — the same unit IRI the realized setting bp:RPS-feedrate-CCP001-qv carries and the same one the OPC UA EngineeringUnits field and the historian's unit column emit (the wire-to-graph boundary is where those PAT tags become QuantityValues). A range without its unit is exactly the kind of bare number that means three different operations to three different readers; the graph keeps the unit attached so a feed setpoint can never be silently misread. Culture temperature carries its own HIGH criticality (bp:CRIT-Temp, established by the same risk assessment) too; only feed rate's full NOR/PAR/DoE trail is shown here for brevity.

Modeling both ranges as typed ranges — not as a sentence in a report — is what turns "feed rate matters" into "feed rate affects monomer purity per study DOE-07, controlled to its NOR 0.35–0.45 inside the PAR 0.30–0.50," a fact an investigator deviation-hunting on a low-monomer batch, or an auditor, can stand on.

Why temperature belongs on that edge at all is itself process knowledge worth making explicit. A fed-batch CHO culture is run in two modeled phases — a growth phase to build viable-cell mass (bp:CCP-001-growth), then a production phase held at its own controlled temperature setpoint (here 36.5 °C, NOR 36.0–37.0; bp:CCP-001-production), the lever a real process shifts further — commonly down to roughly 30–33 °C, walked in Book 1's production-bioreactor chapter — to steer titer, glycosylation, and aggregation. That biphasic recipe is in the graph already, which is why feed rate and temperature jointly land on monomer: they are the two levers the production phase is tuned on. The response surface that says exactly how much monomer a given feed rate buys is not flattened into triples — that would balloon the graph into millions of meaningless rows and still lose the surface's shape; it is referenced by IRI through bp:DESIGNSPACE-mAb-A bp:referencesModel, keeping the graph the navigable index of the design space rather than its warehouse (the fitted model the IRI points at is built in the machine-learning book's process-development chapter). The graph says that feed rate and temperature jointly affect monomer; the fitted model says how much — and an affectsQuality edge cannot say two parameters matter only in combination, which is exactly the unsolved part this chapter returns to.

One honesty note the alignment file forces on us: unlike derivedFrom, affectsQuality has no faithful external counterpart, and that is a deliberate choice, not an omission. The Relation Ontology's regulation and causal relations are scoped to occurrents and molecular functions — a process realizing a function, a molecular entity interacting with another. Our edge runs between two types in information artifacts: a process-parameter type (feed rate) and a quality-attribute type (monomer purity). Subsuming it under an RO causal relation would be an unfaithful category jump, so align.ttl records affectsQuality as a local QbD relation with no rdfs:subPropertyOf to RO. An OOPS! scan will flag it as an isolated relation; the defense is that a faithful local term beats a wrong alignment, and the ICH provenance lives in the citation [2], not in a forced equivalence.

The same edges are the ground truth a model learns over

The referencesModel link points the other way too: where the graph ends, the machine-learning book begins, and the relations this chapter authored are exactly what keep that learning honest. Three handoffs are worth making explicit, because they are where the ontology stops being documentation and becomes the model's substrate.

First, the graph is the leak-proof label store. A supervised model that predicts monomer purity from process settings needs (feed rate, temperature) → monomer rows, and the affectsQuality edges plus the xsd-typed datatype properties are precisely those rows, already unit-checked and lineage-anchored. The danger is leakage — a feature that secretly encodes the answer — and the genealogy spine is where it hides: two drug-substance lots that share a BATCH-2026-001 ancestor are not independent rows, so a model scored by an ordinary random train/test split will look brilliant and generalise to nothing, because near-duplicate siblings have leaked across the split. The fix is to split by the spine: a grouped, leave-one-batch-out cross-validation that holds out an entire derivedFrom-connected lineage at a time, so the held-out batch is genuinely unseen. The same (bp:derivedFrom)+ walk that scopes a recall is the grouping key that makes a validation score admissible — exactly the batch-grouped split Book 5's data chapter insists on, here handed to the model for free because the relations already record which rows descend from one campaign.

Second, SHACL gates the training set, not only the release. The cell-bank and release shapes (shapes.ttl, CQ-17) were built to refuse a non-conformant lot; pointed at a candidate training subgraph they refuse a non-conformant dataset — every lot must carry its bp:derivedFrom parent, every CQA its unit-bearing value, every characterization its verdict — before a single row is handed to a learner. This is the closed-world catch the open-world reasoner cannot make: a missing feature reads as unknown to OWL but as a gap to SHACL, and a model trained on a graph with silent gaps will cheerfully invent what the data never said. Conformance-before-training is how the validation paradox — a fluent model that narrates a false lineage as smoothly as a true one — is held at bay: the reasoned, shape-validated graph is the ground truth the model is checked against, not the other way round.

Third, the typed edges are themselves features. Lineage depth, the number of includedFraction cycles that built a pool, whether a run occursIn a vessel with a prior contamination, the resin's cycleCount against its limit — these graph-derived features carry process structure a flat feature table loses, and they are computed by the very property paths the competency questions already run. A graph good enough to answer CQ-04 is a graph good enough to engineer features for a release predictor; the relations earn a second life as model inputs. None of this relaxes the chapter's honesty note — affectsQuality still cannot encode the form of a feed-rate-by-temperature interaction, which is exactly why the effect is referencesModel-deferred to a fitted response surface rather than faked in triples. The division of labour is clean: the graph supplies identified, validated, lineage-grouped substance; the model supplies the function over it.

occursIn: the run to its vessel

The next relation exists to prevent a category error the taxonomy chapter warned about — the "the batch is the bioreactor" conflation a naive multi-source load produces when the MES has one row for "the run in BR-101" and someone types it as both the material and the vessel. The batch is a bp:Material; the production bioreactor is bp:Equipment; the two are declared disjoint. So the link between them cannot be a second rdf:type on one node. It is an explicit object property from the process to the equipment:

# bioproc.ttl — occursIn ties a run to the persisting vessel it happened in, not by re-typing the batch.
bp:occursIn a owl:ObjectProperty ; rdfs:label "occurs in" ;
rdfs:domain bp:Process ; rdfs:range bp:Equipment ;
skos:definition "Relates a process to the persisting equipment in which it occurs (the run to its vessel) — in place of typing the batch material as equipment." .

The seed-train expansions already use it — bp:EXP-001 ... bp:occursIn bp:SF-01 (a shake flask), bp:EXP-002 ... bp:occursIn bp:SBR-01 (a seed bioreactor) — and the production culture run does too: bp:CCP-001 ... bp:occursIn bp:BR-101. This is what makes CQ-21 a one-hop query instead of an ambiguous second type on the batch node: ask which bp:CellCultureProcess occurred in which vessel, and the run-to-vessel fact is right there on its own edge, the vessel resolving to BR-101, a ProductionBioreactor. The distinction earns its keep when a vessel is implicated in a contamination: the bioreactor persists across campaigns while each batch material is consumed, so "which other runs occurred in this same vessel?" is only answerable if the vessel is its own individual, not a label fused onto one batch. A refinement, bp:performedOn (a sub-property of occursIn), pins a unit operation to the specific chromatography column or resin lot it ran on, so "which batches shared this resin?" — a real carryover-investigation question, since a Protein A resin is reused for many cycles — is answerable too. The resin is a persisting consumable with a usage history the graph carries: bp:RESIN-PrA-07 cycleCount 38 against a cycleLifetimeLimit 200, so "has this resin exceeded its validated cycle life?" is a comparison on two literals, not a hunt through column logbooks.

A subtler domain decision sits one layer down and is worth surfacing, because it is exactly the over-commit this chapter warns against. The vessel BR-101 does not only host a run; it bears a role — bp:BR-101 hasRole bp:BR-101-role, a ProductionReactorRole. So hasRole, hasDisposition, and hasFunction cannot take rdfs:domain bp:Material: a resin, a filter, and a vessel are all bp:Equipment, and pinning the domain to bp:Material would infer every role-bearing vessel to be a material — re-firing the very Material/Equipment disjointness guard occursIn exists to honour. The model therefore declares their domain as obo:BFO_0000040 (BFO material entity), the shared parent of both bp:Material and bp:Equipment, so a vessel can bear a role without being mistyped as product (the formalization chapter is where that domain choice is written down). Domain that is one class too tight is as much a bug as domain that is one class too loose.

contains: the hierarchy that is NOT lineage

The fourth relation is the one most easily confused with the first, and the model spends a deliberate effort keeping them apart. Once the antibody is filled into vials, the lot stops being a bulk material measured by concentration and becomes a population of counted, individually identified units — the bulk-to-discrete shift the instances chapter makes concrete. Aggregation — vials packed into a carton, cartons into a case, cases onto a pallet — is a parent-child containment structure mandated by track-and-trace regulation [3]. A carton does not derive from its vials; it contains them. So bp:contains is its own transitive object property, and the comment on its declaration is explicit that it is not a sub-property of derivedFrom:

# bioproc.ttl — contains is transitive like derivedFrom, but deliberately NOT a sub-property of it.
bp:contains a owl:ObjectProperty , owl:TransitiveProperty ; rdfs:label "contains" ;
rdfs:domain bp:Package ; rdfs:range bp:Material ;
skos:definition "The packing containment hierarchy (carton contains vials, case contains cartons, pallet contains cases). DELIBERATELY NOT a sub-property of derivedFrom: containment is mutable and is not lineage." .

The instances are a clean three-level chain over the one serialized vial in the dataset — a vial that itself derivedFrom its drug-product lot, so its release quality is inherited through the lot rather than restated on every vial:

# instances.ttl — vial -> carton -> case -> pallet (containment, NOT genealogy).
bp:CARTON-001 a bp:Carton ; rdfs:label "carton C-001" ; bp:contains bp:VIAL-DP-001-000042 .
bp:CASE-001 a bp:Case ; rdfs:label "case CS-001" ; bp:contains bp:CARTON-001 .
bp:PALLET-001 a bp:Pallet ; rdfs:label "pallet P-001" ; bp:contains bp:CASE-001 .

Both contains and derivedFrom are transitive, which is exactly why they must stay separate: a bp:contains+ walk answers "what is this packed inside, right now?" (mutable — it changes when a case is opened and repacked at a distributor), while a bp:derivedFrom+ walk answers "what was this made from?" (permanent — the vial's path back to the cell bank never changes). Collapse them and a recall query would treat a shipping carton as an ancestor of its contents — the family-tree-versus-roommate confusion, with regulatory consequences: a recall scoped by a muddled relation either misses contaminated units or quarantines the loading dock. (The serialized vial's own GS1 item key versus its ontology IRI — two legitimate identity systems for one object — is the lot-versus-item individuation tension the serialization model takes up, and the reason contains is designed now to keep that future cleanly off the lineage spine.)

Evaluation: the relations answer their questions

validate.py parses bioproc.ttl + align.ttl + instances.ttl, applies the OWL-RL closure, and runs the competency questions — 2120 triples close to 7137 after reasoning (a triple is a single subject-predicate-object fact; the count grows because the reasoner makes the implied transitive edges explicit, so the larger number is the expected result, not an error), and the transitive spine earns its long-range edges with no hand assertion. CQ-06 reads the affectsQuality edges directly:

# queries/CQ-06.rq — CQ-06: which process parameters affect monomer purity?
PREFIX bp: <https://example.org/bioproc#>
SELECT ?parameter ?attribute WHERE {
?parameter bp:affectsQuality ?attribute .
?attribute a bp:QualityAttribute .
}

It returns the two CPPs the dataset asserts — feed rate and culture temperature, both affecting MonomerPct-CQA — turning the most prized knowledge in development into a one-line query. CQ-13 walks the containment hierarchy with bp:contains+, returning the carton, case, and pallet the vial is packed inside, and CQ-14 proves the two transitive hierarchies never touch, as an ASK that is correctly False:

# queries/CQ-14.rq — CQ-14: containment is NOT genealogy. Correct when this ASK is FALSE.
PREFIX bp: <https://example.org/bioproc#>
ASK {
?container bp:contains+ bp:VIAL-DP-001-000042 .
bp:VIAL-DP-001-000042 bp:derivedFrom+ ?container .
}

The model is correct precisely because the answer is False: nothing a vial is packed inside is also something the vial was made from. CQ-21 confirms the run-to-vessel occursIn edge resolves to BR-101 for the production run, CQ-17 confirms WCB-CHO-001 carries all four characterizations so it conforms to the cell-bank gate, and CQ-18 confirms passage 8 is within the validated limit of 40. And the payoff of the spine is CQ-04, the impact walk: when the out-of-spec lot DP-004 fails — its high-molecular-weight aggregate at 2.41 %, above the 2.0 % release limit, even though its monomer is in spec — a lot must meet every release limit, so failing on any single attribute (here aggregate) dispositions the whole lot as out-of-spec regardless of the ones that pass — the query walks up its lineage to the shared ancestor and back down to find every sibling that shares its fate:

# queries/CQ-04.rq — when DP-004 fails, which drug products share its lineage?
PREFIX bp: <https://example.org/bioproc#>
SELECT DISTINCT ?affected WHERE {
bp:DP-004 (bp:derivedFrom)+ ?shared . # an ancestor of the failed lot
?affected (bp:derivedFrom)+ ?shared . # anything else derived from it
?affected a bp:DrugProduct .
FILTER(?affected != bp:DP-004)
} ORDER BY ?affected

DP-004 and the golden lots DP-001/DP-002 all trace back to WCB-CHO-001, so the walk surfaces DP-001 and DP-002 as sharing the failed lot's cell-bank ancestry — a recall scoped by query instead of by quarantining the whole campaign. The shared ancestor the walk lands on is not DS-001: DP-004 derives from a different substance lot, DS-004, off its own out-of-spec batch BATCH-2026-004. The lots reconverge only higher up, at the cell-bank tier (WCB-CHO-001, and through it the master and research banks), which is exactly why CQ-04's (derivedFrom)+ ?shared ranges over all common ancestors and lets the join surface DP-001/DP-002 despite their disjoint downstream chains. The same forward fork that makes DS-001 fillsInto DP-001 , DP-002 (one drug substance splitting into sibling drug-product lots) is what makes shared-fate analysis a traversal rather than a spreadsheet archaeology dig.

The aggregate failure also has a downstream story the genealogy makes investigable. Aggregate is not removed at capture — Protein A binds the antibody regardless of its aggregation state — but at polishing: the in-process trail shows HMW at 4.1 % after the capture pool, dropping to 1.4 % after polishing, and finishing at 1.287 % in the golden drug substance. When DP-004 lands at 2.41 % HMW, the lineage walk does more than scope a recall; it hands the investigator a root-cause hypothesis — the polishing step under-cleared aggregate on the BATCH-2026-004 train — because every in-process result is hung on a material node the derivedFrom walk passes through. A traceable spine turns "the lot failed on aggregate" into "and here is the unit operation that should have removed it." Five competency questions, all green — and the headline lineage walk from DS-001 returns its 11 ancestors entirely by transitivity.

The unsolved part: a relation is consistent, not necessarily correct

A reasoner can prove these relations are consistent — domain and range type their ends, transitivity closes the lineage, the disjointness guards keep contains from masquerading as derivedFrom and the cell-bank tiers from collapsing into one. It cannot prove they are correct. A derivedFrom edge with its direction reversed reasons perfectly and lies about ancestry — and at the cell-bank root, that lie propagates to every downstream lot with full confidence. Worse, the spine cannot even forbid a cycle. One might reach for OWL's owl:IrreflexiveProperty or owl:AsymmetricProperty to declare "nothing derives from itself" — but OWL 2 DL forbids it: a transitive property is non-simple, and the profile bars non-simple properties from carrying those axioms. So lineage acyclicity — the very thing that makes "walk up to the ancestor" terminate — is a SHACL-and-convention guarantee, not something the transitive axiom enforces; transitivity buys the long-range edge for free, but not a guarantee that the edges form a tree rather than a loop (the formalization chapter takes up this profile constraint directly). The model also records lineage, not conservation: derivedFrom says a capture pool came from a batch, but it does not enforce mass balance, so a traversable genealogy can be quantitatively impossible (more antibody out than went in) unless the quantities are modeled too. Where a continuous, living seed-train expansion gets cut into discrete material nodes is a modeling judgment, not a fact the biology hands over — model too coarsely and you cannot trace a contamination to a transfer; too finely and the graph drowns in nodes nobody queries. And affectsQuality asserts that a parameter matters but cannot represent the form of the effect (that lives in the referenced response surface), so parameters that matter only in combination — feed rate and temperature jointly — are easy to model one edge at a time, quietly implying a process is simpler than it is [2]. The relations make the knowledge legible and queryable, a real advance over a buried report; legible is not the same as complete or correct.

Why it matters

The quality of a graph is decided by its relations more than its classes. Transitivity on the single derivedFrom spine is what makes lineage walkable to any depth from one frozen vial of cell bank to the patient's vial — and what makes an out-of-spec lot's siblings findable by query when a recall is on the line. The object/datatype fork is what keeps an edge you walk (back to the cell bank) distinct from a value you read (the lot's monomer purity). occursIn is what lets the batch and its bioreactor be separate things, so a vessel implicated in contamination can be cross-checked across campaigns. And keeping contains deliberately off the lineage spine is what keeps "what is this antibody made from?" and "what carton is this vial packed in, now?" two answerable questions instead of one muddled one. Get these four right and every lineage, impact, QbD, and recall query the digital thread runs is trustworthy by construction.

In the real world

None of these relations is this book's coinage. derivedFrom aligns up to the Relation Ontology's derives from, the standard way biomedical and manufacturing graphs say one thing comes from another; the cell-bank tiers and their characterization are not optional good practice but regulatory expectations for cell substrates [4], which is why every commercial mammalian-cell process maintains an RCB/MCB/WCB lineage with documented passage limits — CHO being the dominant host for commercial antibodies precisely because its behavior is so well characterized. affectsQuality, CPPs, CQAs, NOR and PAR are the vocabulary of the ICH quality guidelines that regulators and industry already share, which is why modeling them as a graph is natural rather than forced [2]; and aggregation under GS1, distinct from manufacturing lineage, is mandated by track-and-trace regulation and is live daily operation for every commercial drug product [3]. The same evidence-bearing pattern recurs on the viral-safety side: a low-pH-hold step and a viral-filtration step each carry a validated log-reduction value (LRV — the log10 reduction in virus the step achieves; 4.5 and 4.2 LRV, summing to 8.7 across two independent, orthogonal removal mechanisms — one inactivating, one filtering — so a virus surviving one is unlikely to survive the other, CQ-12; the physical steps are walked in Book 1's viral inactivation chapter), establishedBy a clearance study — hasClearanceCapability is to viral safety what affectsQuality is to a CQA. The relationships a development, manufacturing, and supply-chain team establish already are graphs in their reports and barcodes; the move this chapter argues for is to write them down as edges a machine can traverse — and to keep the two transitive hierarchies, lineage and containment, from ever being confused.

Key terms

  • Object property / datatype property — a relation to another thing (an edge to walk, like derivedFrom back to the cell bank) versus a relation to a literal value (a measurement to read, like monomerPct or passageNumber).
  • derivedFrom — the one transitive object property that is the genealogy spine; only immediate parent edges are asserted, and a reasoner infers lineage to any depth, rooting every campaign material in the working cell bank and, through it, the master and research banks.
  • Cell-bank tiers (RCB/MCB/WCB) — the disjoint, regulatorily expected hierarchy at the root of the spine; the working bank is characterized for identity, sterility, viral safety, and genetic stability, because a misidentified root propagates transitively to every lot.
  • affectsQuality — the core QbD object property from a process parameter to a quality attribute (feed rate → monomer purity), carrying its NOR, PAR, criticality, and DoE study as attached evidence.
  • NOR / PAR — the tighter normal operating range and the wider proven acceptable range bounding a parameter, modeled as typed ranges rather than prose.
  • occursIn — the object property tying a process to the persisting equipment it ran in (the culture run to BR-101), used instead of re-typing the batch material as a vessel (the material/equipment guard).
  • contains — the transitive packing hierarchy (carton → case → pallet), deliberately not a sub-property of derivedFrom because containment is mutable and is not lineage.
  • Grouped / leave-one-batch-out cross-validation — splitting a model's training and test rows by the derivedFrom lineage so that near-duplicate sibling lots never straddle the split; the spine that scopes a recall is also the grouping key that makes an ML validation score honest.
  • Ground truth (for a model) — the reasoned, SHACL-validated graph a learning model is checked against; the ontology supplies identified, unit-bearing, lineage-grouped facts, and the model supplies the function over them, never the reverse.

Where this leads

The relations are wired and the spine is transitive. But a relation that is merely declared can still be misused — a functional property like hasHostOrganism left unstated, an existential restriction not enforced, a disjointness forgotten so the "batch is the run" error slips through, or a cell-bank characterization gate that an open-world reasoner will not catch. The next chapter, Formalization: Axioms, Restrictions, and What a Reasoner Can and Cannot Catch, adds the OWL 2 axioms that give these classes and relations teeth — functional properties, existential and cardinality restrictions, disjointness — and faces honestly the line between what an OWL-RL closure enforces and what only a full DL reasoner or a SHACL gate will catch.