Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update heart terms, improve epicardium, add docs #195

Merged
merged 6 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/_images/scaffoldmaker_human_heart.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# -- Project information

project = 'Scaffold Maker'
project = 'scaffoldmaker'
copyright = '2022, University of Auckland'
author = 'University of Auckland'

Expand Down Expand Up @@ -33,3 +33,6 @@

# -- Options for EPUB output
epub_show_urls = 'footnote'

# If true, enable figure numbering
numfig = True
13 changes: 6 additions & 7 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
Scaffold Maker
==============
*scaffoldmaker library*
=======================

The **Scaffold Maker** is part of the software that is used in the collection of tools used for mapping data to scaffolds.
The *scaffoldmaker library* contains scripts for programmatically generating anatomical scaffolds for a range of organs and other structures, and includes utility code for building common geometric shapes, annotation, and refinement. The scripts generate the scaffold model in the data structures of the underlying *OpenCMISS-Zinc library*, and through the Zinc API clients can interrogate the model or export it for subsequent use.

.. note::

This project is under active development.
Most users will make scaffolds using the ABI Mapping Tools' **Scaffold Creator** user interface for this library, and its documentation gives a good introduction to anatomical scaffolds and some common features of them.

This documentation is intended to be a resource describing details about using individual scaffolds, as well as developing new ones.

.. toctree::

install

scaffolds/heart
5 changes: 2 additions & 3 deletions docs/install.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

Installing
==========
Installation
============

Install with::

Expand Down
83 changes: 83 additions & 0 deletions docs/scaffolds/heart.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
Heart Scaffold
==============

The current recommended heart scaffold is ``3D Heart 1`` built from ``class MeshType_3d_heart1``; the human variant is shown in :numref:`fig-scaffoldmaker-human-heart`.

.. _fig-scaffoldmaker-human-heart:

.. figure:: ../_images/scaffoldmaker_human_heart.jpg
:align: center

Human heart scaffold.

The heart scaffold is a 3-D volumetric model of the heart representing solid walls of all four chambers. It has pulmonary vein and vena cava inlets, simple representations of auricles (atrial appendages), but does not have representations of the heart valves (only their orifices, or boundaries with the ventricles). Only the myocardium layer is fully represented (but see below).

.. note::

Separate scaffolds are provided for the heart ventricles (below base/valve plane), ventricles with base, and atria. Beware that the heart atria scaffold is not compatible with the full heart scaffold because it uses different numbering for elements, faces and nodes; if working on a subset of the heart it is recommended to create the whole heart scaffold and delete the unwanted elements, which allows the resulting subset to be merged with the whole heart. Further to this, all scaffolds used must be built with the same parameters for numbers of elements, inlets, optional layers etc. to be able to merge together.

Variants
--------

The heart scaffold is provided with parameter sets for the following four species, which differ in shape, and in particular have different numbers of pulmonary veins:

* Human (2 left, 2 right pulmonary veins)
* Pig (1 left, 1 right pulmonary veins)
* Rat (3 pulmonary veins: left, middle, right)
* Mouse (3 pulmonary veins: left, middle, right)

These variants' geometry and annotations are best viewed in the **Scaffold Creator** tool in the ABI Mapping Tools. On the web, the latest published generic heart scaffold variants can be viewed on the `SPARC Portal <https://sparc.science/>`_ by searching for ``heart``, filtering for models, selecting a variant and viewing the scaffold in its Gallery tab.

The heart scaffold script generates the scaffold mesh and geometry from ellipsoid and cubic functions with many parameters controlling the shape. The parameters were carefully tuned for each species, and it is not recommended that these be edited.

An advanced optional feature is to check *Define epicardium layer* (set parameter to ``true``) which adds a layer of 3-D elements outside the myocardium to represent the thick epicardium layer consisting of epicardial fat and other tissue. This is currently only implemented over the atria, excluding the auricles.

Coordinates
-----------

The heart scaffold only defines geometric coordinates (field ``coordinates``) which give geometry at approximately unit scale. Note that the scaffold has a *Unit scale* parameter (default value ``1.0``) which scales the entire scaffold efficiently.

A material coordinates field is not provided, so to perform embedding at this time, it is necessary to use the generic ``coordinates`` field as material coordinates, built with the standard, *unmodified* parameter set (incl. *Unit scale* ``1.0``) for the species.

The heart scaffold supports limited refinement/resampling by checking *Refine* (set parameter to ``true``) with chosen *Refine number of elements~* parameters. Be aware that only the ``coordinates`` field is currently defined on the refined mesh (but annotations are transferred), and the refined whole heart is only conformant if all *Refine number of elements~* parameters have the same value.

Annotations
-----------

Important anatomical regions of the heart are defined by groups of elements (or faces, edges and nodes/points) and annotated with standard term names and identifiers from a controlled vocabulary.

Annotated 3-dimensional volume regions are defined by groups of 3-D elements including (using only one of the items separated by slash /):

* left/right atrium/ventricle myocardium
* left/middle/right pulmonary vein
* inferior/superior vena cava
* left/right auricle

**Terms for volume regions such as the above are not to be used for digitized contours!** They are used for applying different material properties in models and the strain/curvature penalty (stiffness) parameters in fitting.

Annotated 2-dimensional surface regions are defined for matching annotated contours digitized from medical images including (where ``luminal`` means bordering the lumen or cavity of a tubular structure, ``outer`` is the outside boundary of a layer on the wall relative to the cavity):

* luminal surface of left/right atrium/ventricle
* luminal surface of left/middle/right pulmonary vein
* luminal surface of inferior/superior vena cava
* outer surface of myocardium
* outer surface of myocardium of left/right atrium/ventricle (if need to distinguish)
* outer surface of epicardium (only if *Define epicardium layer* is checked)

The following are proposed for future heart scaffolds including great vessels (the root variants being the subset of the luminal surface within the arterial valve up to the sinotubular junction) so should be used for annotating their contours:

* luminal surface of [root of] aorta
* luminal surface of [root of] pulmonary trunk

Note that luminal surfaces of atria structures are defined with an overlap in the scaffold as it is difficult to determine the exact boundary between pulmonary veins/vena cavae with the myocardium for the left/right atrium, which gives leeway for variation between digitized datasets.

Note that at organ scale additional terms such as ``endocardium of left ventricle`` which represent microscopically thin layers are defined identically to ``luminal surface of left ventricle`` etc. surface groups. However, this is not the case on the epicardium which can be a layer of finite thickness at whole organ scale.

The heart scaffold currently has no annotated 1-dimensional line regions.

Several fiducial marker points are defined on the heart scaffold, of which the following two are potentially usable when digitizing:

* apex of heart
* crux cordis

At present these are both defined on the outer surface of myocardium, but when a volumetric epicardium layer is defined over the whole heart these will either be defined on the outer surface of epicardium, or separate points defined to distinguish distinct points on the two surfaces.
139 changes: 86 additions & 53 deletions src/scaffoldmaker/annotation/heart_terms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,93 @@

# convention: preferred name, preferred id, followed by any other ids and alternative names
heart_terms = [
( "heart", "UBERON:0000948", "FMA:7088" ),
# ventricles
( "left ventricle myocardium", "FMA:9558" ),
( "right ventricle myocardium", "FMA:9535" ),
( "interventricular septum", "FMA:7133" ),
( "endocardium of left ventricle", "FMA:9559" ),
( "endocardium of right ventricle", "FMA:9536" ),
( "epicardial fat", "UBERON:0015129"),
( "epicardium", "FMA:9461", "UBERON:0002348"),
#( "epicardium of ventricle", "FMA:12150", "UBERON:0001082" ),
# ventricles with base
( "conus arteriosus", "UBERON:0003983" ),
( "left fibrous ring", "FMA:77124" ),
( "right fibrous ring", "FMA:77125" ),
# atria
( "left atrium myocardium", "FMA:7285" ),
( "right atrium myocardium", "FMA:7282" ),
( "endocardium of left atrium", "FMA:7286", "UBERON:0034903" ),
( "endocardium of right atrium", "FMA:7281", "UBERON:0009129" ),
( "interatrial septum", "FMA:7108" ),
( "fossa ovalis", "FMA:9246" ),
( "left auricle", "FMA:7219", "UBERON:0006630" ), # uncertain if just the tissue like myocardium
( "right auricle", "FMA:7218", "UBERON:0006631" ), # uncertain if just the tissue like myocardium
( "endocardium of left auricle", "FMA:13236", "UBERON:0011006" ),
( "endocardium of right auricle", "FMA:13235", "UBERON:0011007" ),
( "epicardium of left auricle", "FMA:13233" ),
( "epicardium of right auricle", "FMA:13232" ),
( "pulmonary vein", "FMA:66643", "UBERON:0002016" ),
( "left pulmonary vein", "UBERON:0009030" ),
( "left inferior pulmonary vein", "FMA:49913" ),
( "left superior pulmonary vein", "FMA:49916" ),
( "middle pulmonary vein", "ILX:0739222" ), # in mouse, rat, rabbit
( "right pulmonary vein", "UBERON:0009032" ),
( "right inferior pulmonary vein", "FMA:49911" ),
( "right superior pulmonary vein", "FMA:49914" ),
( "inferior vena cava", "FMA:10951", "UBERON:0001072", "posterior vena cava" ),
( "inferior vena cava inlet", "ILX:0738358" ),
( "superior vena cava", "FMA:4720", "UBERON:0001585", "anterior vena cava" ),
( "superior vena cava inlet", "ILX:0738367" ),
# arterial root
( "root of aorta", "FMA:3740" ),
( "posterior cusp of aortic valve", "FMA:7253" ),
( "right cusp of aortic valve", "FMA:7252" ),
( "left cusp of aortic valve", "FMA:7251" ),
( "root of pulmonary trunk", "FMA:8612" ),
( "right cusp of pulmonary valve", "FMA:7250" ),
( "anterior cusp of pulmonary valve", "FMA:7249" ),
( "left cusp of pulmonary valve", "FMA:7247" ),
# heart - volume terms
("heart", "UBERON:0000948", "FMA:7088"), # group of the entire heart
("epicardial fat", "UBERON:0015129"), # not used
("epicardium", "UBERON:0002348", "FMA:9461"), # volumetric layer outside myocardium to pericardial cavity
# heart - surface terms
("outer surface of epicardium", "ILX:0793548"),
("outer surface of myocardium", "ILX:0793530"),

# ventricles - volume terms
("conus arteriosus", "UBERON:0003983"),
("endocardium of left ventricle", "UBERON:0009713", "FMA:9559"),
("endocardium of right ventricle", "UBERON:0009712", "FMA:9536"),
("heart left ventricle", "UBERON:0002084"), # the whole left ventricle
("heart right ventricle", "UBERON:0002080"), # the whole right ventricle
("interventricular septum", "UBERON:0002094", "FMA:7133"),
("left fibrous ring", "FMA:77124"),
("left ventricle myocardium", "UBERON:0006566", "FMA:9558"),
("right fibrous ring", "FMA:77125"),
("right ventricle myocardium", "UBERON:0006567", "FMA:9535"),
# ventricles - surface terms
("luminal surface of left ventricle", "ILX:0793537"),
("luminal surface of right ventricle", "ILX:0793538"),
("outer surface of myocardium of left ventricle", "ILX:0793533"),
("outer surface of myocardium of right ventricle", "ILX:0793534"),

# atria - volume terms
("left atrium endocardium", "UBERON:0034903", "FMA:7286"),
("endocardium of left auricle", "UBERON:0011006", "FMA:13236"),
("right atrium endocardium", "UBERON:0009129", "FMA:7281"),
("endocardium of right auricle", "UBERON:0011007", "FMA:13235"),
("epicardium of left auricle", "FMA:13233"),
("epicardium of right auricle", "FMA:13232"),
("fossa ovalis", "UBERON:0003369", "FMA:9246"),
("inferior vena cava", "UBERON:0001072", "FMA:10951", "posterior vena cava"),
("inferior vena cava inlet", "ILX:0738358"),
("interatrial septum", "UBERON:0002085", "FMA:7108"),
("left atrium myocardium", "FMA:7285"),
("left auricle", "UBERON:0006630", "FMA:7219"),
("left cardiac atrium", "UBERON:0002079"), # the whole left atrium
("left inferior pulmonary vein", "FMA:49913"),
("left pulmonary vein", "UBERON:0009030"),
("left superior pulmonary vein", "FMA:49916"),
("middle pulmonary vein", "ILX:0739222"), # in mouse, rat, rabbit
("pulmonary vein", "UBERON:0002016", "FMA:66643"),
("right atrium myocardium", "FMA:7282"),
("right auricle", "UBERON:0006631", "FMA:7218"),
("right cardiac atrium", "UBERON:0002078"), # the whole right atrium
("right inferior pulmonary vein", "FMA:49911"),
("right pulmonary vein", "UBERON:0009032"),
("right superior pulmonary vein", "FMA:49914"),
("superior vena cava", "UBERON:0001585", "FMA:4720", "anterior vena cava"),
("superior vena cava inlet", "ILX:0738367"),
# atria - surface terms
("luminal surface of left atrium", "ILX:0793535"),
("luminal surface of right atrium", "ILX:0793536"),
("luminal surface of inferior vena cava", "ILX:0793542"),
("luminal surface of left pulmonary vein", "ILX:0793539"),
("luminal surface of middle pulmonary vein", "ILX:0793540"),
("luminal surface of right pulmonary vein", "ILX:0793541"),
("luminal surface of superior vena cava", "ILX:0793543"),
("outer surface of myocardium of left atrium", "ILX:0793531"),
("outer surface of myocardium of right atrium", "ILX:0793532"),

# arterial valves and great vessels - volume terms
("root of aorta", "FMA:3740"),
("posterior cusp of aortic valve", "FMA:7253"),
("right cusp of aortic valve", "FMA:7252"),
("left cusp of aortic valve", "FMA:7251"),
("root of pulmonary trunk", "FMA:8612"),
("right cusp of pulmonary valve", "FMA:7250"),
("anterior cusp of pulmonary valve", "FMA:7249"),
("left cusp of pulmonary valve", "FMA:7247"),
# arterial valves and great vessels - surface terms
("luminal surface of aorta", "ILX:0793544"),
("luminal surface of pulmonary trunk", "ILX:0793546"),
("luminal surface of root of aorta", "ILX:0793545"),
("luminal surface of root of pulmonary trunk", "ILX:0793547"),

# fiducial markers
( "apex of heart", "UBERON:0002098", "FMA:7164"),
( "crux of heart", "ILX:0777104", "FMA:7220" ), # point on posterior surface where the four chambers meet on interventricular, atrio-ventricular and interatrial sulci
( "left atrium epicardium venous midpoint", "ILX:0778116"), # point at centre of pulmonary vein ostia on left atrium epicardium, on anterior/ventral side for rodents
( "right atrium epicardium venous midpoint", "ILX:0778117") # point at centre of inferior & superior vena cavae on right atrium epicardium
]
("apex of heart", "UBERON:0002098", "FMA:7164"),
# point on posterior surface where the four chambers meet on interventricular, A-V and interatrial sulci
("crux cordis", "ILX:0777104", "FMA:7220"),
# point at centre of pulmonary vein ostia on left atrium epicardium, on anterior/ventral side for rodents
("left atrium epicardium venous midpoint", "ILX:0778116"),
# point at centre of inferior & superior vena cavae on right atrium epicardium
("right atrium epicardium venous midpoint", "ILX:0778117")
]

def get_heart_term(name : str):
"""
Expand Down
38 changes: 10 additions & 28 deletions src/scaffoldmaker/meshtypes/meshtype_3d_heart1.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ def getParameterSetNames():
'Human 1',
'Mouse 1',
'Pig 1',
'Rat 1',
'Unit Human 1',
'Unit Mouse 1',
'Unit Pig 1',
'Unit Rat 1']
'Rat 1']

@staticmethod
def getDefaultOptions(parameterSetName='Default'):
Expand Down Expand Up @@ -83,7 +79,7 @@ def getOrderedOptionNames():
'Refine number of elements surface',
'Refine number of elements through LV wall',
'Refine number of elements through wall',
'Refine number of elements through epicardial fat layer']:
'Refine number of elements through epicardium layer']:
optionNames.remove(optionName)
optionNames.append(optionName)
return optionNames
Expand Down Expand Up @@ -133,32 +129,20 @@ def generateBaseMesh(cls, region, options):
coordinates = findOrCreateFieldCoordinates(fm)
cache = fm.createFieldcache()

mesh = fm.findMeshByDimension(3)

# generate heartventriclesbase1 model and put atria1 on it
ventriclesAnnotationGroups = MeshType_3d_heartventriclesbase1.generateBaseMesh(region, options)
atriaAnnotationGroups = MeshType_3d_heartatria1.generateBaseMesh(region, options)
annotationGroups = mergeAnnotationGroups(ventriclesAnnotationGroups, atriaAnnotationGroups)
heartGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_heart_term("heart"))
cruxGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_heart_term("crux of heart"))
cruxGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_heart_term("crux cordis"))
lFibrousRingGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_heart_term("left fibrous ring"))
rFibrousRingGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_heart_term("right fibrous ring"))

# annotation fiducial points
markerGroup = findOrCreateFieldGroup(fm, "marker")
markerName = findOrCreateFieldStoredString(fm, name="marker_name")
markerLocation = findOrCreateFieldStoredMeshLocation(fm, mesh, name="marker_location")

nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
markerPoints = findOrCreateFieldNodeGroup(markerGroup, nodes).getNodesetGroup()
markerTemplateInternal = nodes.createNodetemplate()
markerTemplateInternal.defineField(markerName)
markerTemplateInternal.defineField(markerLocation)

##############
# Create nodes
##############

nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1)

# discover left and right fibrous ring nodes from ventricles and atria
Expand Down Expand Up @@ -219,6 +203,7 @@ def generateBaseMesh(cls, region, options):
# Create elements
#################

mesh = fm.findMeshByDimension(3)
heartMeshGroup = heartGroup.getMeshGroup(mesh)
lFibrousRingMeshGroup = lFibrousRingGroup.getMeshGroup(mesh)
rFibrousRingMeshGroup = rFibrousRingGroup.getMeshGroup(mesh)
Expand Down Expand Up @@ -397,16 +382,13 @@ def generateBaseMesh(cls, region, options):
for meshGroup in meshGroups:
meshGroup.addElement(element)

# annotation fiducial points
# crux cordis annotation point
cruxElement = mesh.findElementByIdentifier(cruxElementId)
cruxXi = [ 0.5, 0.5, 1.0 ]
markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal)
nodeIdentifier += 1
cache.setNode(markerPoint)
markerName.assignString(cache, cruxGroup.getName())
markerLocation.assignMeshLocation(cache, cruxElement, cruxXi)
for group in [ heartGroup, cruxGroup ]:
group.getNodesetGroup(nodes).addNode(markerPoint)
markerNode = cruxGroup.createMarkerNode(nodeIdentifier, element=cruxElement, xi=cruxXi)
nodeIdentifier = markerNode.getIdentifier() + 1
for group in [ heartGroup ]:
group.getNodesetGroup(nodes).addNode(markerNode)

return annotationGroups

Expand Down
Loading