def getDefaultOptions(parameterSetName='Default'):
     options = {}
     options['Bifurcation tree'] = ScaffoldPackage(
         MeshType_1d_bifurcationtree1)
     options['Number of elements around root'] = 6
     options['Maximum element length'] = 0.5
     return options
Exemplo n.º 2
0
def Scaffolds_decodeJSON(dct):
    '''
    Function for passing as object_hook argument to json.loads.
    Constructs scaffold objects from their JSON object encoding.
    '''
    if ('_ScaffoldPackage' in dct):
        scaffoldType = Scaffolds().findScaffoldTypeByName(dct['scaffoldTypeName'])
        #print('Scaffolds_decodeJSON scaffoldType',scaffoldType.getName(), dct)
        return ScaffoldPackage(scaffoldType, dct)
    return dct
 def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None):
     '''
     :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
     :return: ScaffoldPackage.
     '''
     if parameterSetName:
         assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
             'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
             ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
     if optionName == 'Central path':
         if not parameterSetName:
             parameterSetName = list(cls.centralPathDefaultScaffoldPackages.keys())[0]
         return copy.deepcopy(cls.centralPathDefaultScaffoldPackages[parameterSetName])
     if optionName == 'Segment profile':
         if not parameterSetName:
             parameterSetName = scaffoldType.getParameterSetNames()[0]
         return ScaffoldPackage(scaffoldType, defaultParameterSetName = parameterSetName)
     assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'
 def getOptionScaffoldPackage(cls,
                              optionName,
                              scaffoldType,
                              parameterSetName=None):
     '''
     :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
     :return: ScaffoldPackage.
     '''
     if parameterSetName:
         assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
             'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
     if optionName == 'Bifurcation tree':
         if not parameterSetName:
             parameterSetName = MeshType_1d_bifurcationtree1.getParameterSetNames(
             )[0]
         return ScaffoldPackage(MeshType_1d_bifurcationtree1,
                                defaultParameterSetName=parameterSetName)
     assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'
Exemplo n.º 5
0
    def __init__(self, context, region, generator_model, parameters, material_module, scaffold_package,
                 scaffold_package_class):

        self._context = context
        self._region = region
        # self._region = self._context.createRegion()
        # self._region.setName('custom_scaffold')
        # self._region.readFile('D:\\sparc\\tmp\\pig_scaffold.exf')
        self._generator_model = generator_model
        self._material_module = material_module
        self._parameters = parameters.keys()
        self._coordinate_field = None
        _scaffold_package = scaffold_package
        _scaffold_package_class = scaffold_package_class

        scaffolds = Scaffolds()
        self._all_scaffold_types = scaffolds.getScaffoldTypes()

        scaffold_type = None
        for scaffold in self._all_scaffold_types:
            if scaffold == _scaffold_package[-1].getScaffoldType():
                scaffold_type = scaffold
        if scaffold_type is None:
            raise TypeError('Scaffold Type was not found.')

        scaffold_package = ScaffoldPackage(scaffold_type)
        self._parameterSetName = scaffold_type.getParameterSetNames()[0]
        self._scaffold_package = scaffold_package

        self._scaffold = None
        self._scaffold_options = None
        self._temp_region = None
        self._annotation_groups = None
        self._scene = None
        self._scaffold_is_time_aware = None
        self._scaffold_fit_parameters = None
        self._initialise_surface_material()
Exemplo n.º 6
0
class MeshType_3d_wholebody1(Scaffold_base):
    """
Generates body coordinates using a solid cylinder of all cube elements,
 with variable numbers of elements in major, minor, shell and for each section of abdomen, thorax, neck and head.
    """
    cylinder1Settings = {
                'Coordinate dimensions': 3,
                'D2 derivatives': True,
                'D3 derivatives': True,
                'Length': 3.5,
                'Number of elements': 1
            }

    axis1 = [0, 0, 1]
    axis2 = [1, 0, 0]
    axis3 = [0, 1, 0]
    centralPathDefaultScaffoldPackages = {
        'Default': ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings': cylinder1Settings,
            'meshEdits': exnodeStringFromNodeValues(
                [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                 Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3], [
                    [[0.0, 0.0, 0.0], setMagnitude(axis1, cylinder1Settings['Length']), setMagnitude(axis2, 0.5), [0.0, 0.0, 0.0], setMagnitude(axis3, 0.5), [0.0, 0.0, 0.0]],
                    [setMagnitude(axis1, cylinder1Settings['Length']), setMagnitude(axis1, cylinder1Settings['Length']), setMagnitude(axis2, 0.5), [0.0, 0.0, 0.0], setMagnitude(axis3, 0.5), [0.0, 0.0, 0.0]]
                ])
        })
    }

    @staticmethod
    def getName():
        return '3D Whole Body 1'

    @staticmethod
    def getParameterSetNames():
        return [
            'Default',
            'Human Coarse',
            'Human Fine',
            'Rat Coarse',
            'Rat Fine'
        ]

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        if parameterSetName == 'Default':
            parameterSetName = 'Human Coarse'
        centralPathOption = cls.centralPathDefaultScaffoldPackages['Default']
        options = {}
        options['Base parameter set'] = parameterSetName
        options['Central path'] = copy.deepcopy(centralPathOption)
        options['Number of elements across major'] = 6
        options['Number of elements across minor'] = 6
        options['Number of elements across shell'] = 1
        options['Number of elements across transition'] = 1
        options['Number of elements in abdomen'] = 5
        options['Number of elements in thorax'] = 3
        options['Number of elements in neck'] = 1
        options['Number of elements in head'] = 2
        options['Shell thickness proportion'] = 0.2
        options['Discontinuity on the core boundary'] = True
        options['Lower half'] = False
        options['Use cross derivatives'] = False
        options['Refine'] = False
        options['Refine number of elements across major'] = 1
        options['Refine number of elements along'] = 1

        if 'Coarse' in parameterSetName:
            pass
        if 'Fine' in parameterSetName:
            options['Number of elements across major'] = 10
            options['Number of elements across minor'] = 10
            options['Number of elements across shell'] = 1
            options['Number of elements across transition'] = 1
            options['Number of elements in abdomen'] = 10
            options['Number of elements in thorax'] = 6
            options['Number of elements in neck'] = 2
            options['Number of elements in head'] = 4
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path',
            'Number of elements across major',
            'Number of elements across minor',
            'Number of elements across shell',
            'Number of elements across transition',
            'Number of elements in abdomen',
            'Number of elements in thorax',
            'Number of elements in neck',
            'Number of elements in head',
            'Shell thickness proportion',
            'Discontinuity on the core boundary',
            'Refine',
            'Refine number of elements across major',
            'Refine number of elements along'
        ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [MeshType_1d_path1]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \
            cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
                ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(cls.centralPathDefaultScaffoldPackages[parameterSetName])
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType() in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage('Central path', MeshType_1d_path1)
        dependentChanges = False

        if options['Number of elements across major'] < 4:
            options['Number of elements across major'] = 4
        if options['Number of elements across major'] % 2:
            options['Number of elements across major'] += 1

        if options['Number of elements across minor'] != options['Number of elements across major']:
            options['Number of elements across minor'] = options['Number of elements across major']
            dependentChanges = True

        if options['Number of elements across transition'] < 1:
            options['Number of elements across transition'] = 1

        Rcrit = min(options['Number of elements across major']-4, options['Number of elements across minor']-4)//2
        if options['Number of elements across shell'] + options['Number of elements across transition'] - 1 > Rcrit:
            dependentChanges = True
            options['Number of elements across shell'] = Rcrit
            options['Number of elements across transition'] = 1

        if options['Shell thickness proportion'] < 0.07 or options['Shell thickness proportion'] > 0.7:
            options['Shell thickness proportion'] = 2*options['Number of elements across shell']/options['Number of elements across major']

        if options['Number of elements in abdomen'] < 1:
            options['Number of elements in abdomen'] = 1
        if options['Number of elements in head'] < 1:
            options['Number of elements in head'] = 1
        if options['Number of elements in neck'] < 1:
            options['Number of elements in neck'] = 1
        if options['Number of elements in thorax'] < 1:
            options['Number of elements in thorax'] = 1

        return dependentChanges

    @staticmethod
    def generateBaseMesh(region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: List of AnnotationGroup
        """

        baseParameterSetName = options['Base parameter set']
        isHuman = 'Human' in baseParameterSetName
        isRat = 'Rat' in baseParameterSetName

        centralPath = options['Central path']
        full = not options['Lower half']
        elementsCountAcrossMajor = options['Number of elements across major']
        if not full:
            elementsCountAcrossMajor //= 2
        elementsCountAcrossMinor = options['Number of elements across minor']
        elementsCountAcrossShell = options['Number of elements across shell']
        elementsCountAcrossTransition = options['Number of elements across transition']
        elementsCountAlongAbdomen = options['Number of elements in abdomen']
        elementsCountAlongHead = options['Number of elements in head']
        elementsCountAlongNeck = options['Number of elements in neck']
        elementsCountAlongThorax = options['Number of elements in thorax']
        shellRadiusProportion = options['Shell thickness proportion']
        shellProportion = 1/(1/shellRadiusProportion-1)*(elementsCountAcrossMajor/2/elementsCountAcrossShell - 1)
        discontinuity = options['Discontinuity on the core boundary']
        useCrossDerivatives = options['Use cross derivatives']

        elementsCountAlong = elementsCountAlongAbdomen + elementsCountAlongThorax + elementsCountAlongNeck + elementsCountAlongHead

        fieldmodule = region.getFieldmodule()
        coordinates = findOrCreateFieldCoordinates(fieldmodule)
        mesh = fieldmodule.findMeshByDimension(3)

        bodyGroup = AnnotationGroup(region, get_body_term("body"))
        coreGroup = AnnotationGroup(region, get_body_term("core"))
        non_coreGroup = AnnotationGroup(region, get_body_term("non core"))
        abdomenGroup = AnnotationGroup(region, get_body_term("abdomen"))
        thoraxGroup = AnnotationGroup(region, get_body_term("thorax"))
        neckGroup = AnnotationGroup(region, get_body_term("neck core"))
        headGroup = AnnotationGroup(region, get_body_term("head core"))
        annotationGroups = [bodyGroup, coreGroup, non_coreGroup, abdomenGroup, thoraxGroup, neckGroup, headGroup]

        cylinderCentralPath = CylinderCentralPath(region, centralPath, elementsCountAlong)

        cylinderShape = CylinderShape.CYLINDER_SHAPE_FULL

        base = CylinderEnds(elementsCountAcrossMajor, elementsCountAcrossMinor, elementsCountAcrossShell,
                            elementsCountAcrossTransition, shellProportion,
                            [0.0, 0.0, 0.0], cylinderCentralPath.alongAxis[0], cylinderCentralPath.majorAxis[0],
                            cylinderCentralPath.minorRadii[0])
        cylinder1 = CylinderMesh(fieldmodule, coordinates, elementsCountAlong, base,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath, useCrossDerivatives=False)

        # body coordinates
        bodyCoordinates = findOrCreateFieldCoordinates(fieldmodule, name="body coordinates")
        tmp_region = region.createRegion()
        tmp_fieldmodule = tmp_region.getFieldmodule()
        tmp_body_coordinates = findOrCreateFieldCoordinates(tmp_fieldmodule, name="body coordinates")
        tmp_cylinder = CylinderMesh(tmp_fieldmodule, tmp_body_coordinates, elementsCountAlong, base,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath, useCrossDerivatives=False)
        sir = tmp_region.createStreaminformationRegion()
        srm = sir.createStreamresourceMemory()
        tmp_region.write(sir)
        result, buffer = srm.getBuffer()
        sir = region.createStreaminformationRegion()
        srm = sir.createStreamresourceMemoryBuffer(buffer)
        region.read(sir)

        del srm
        del sir
        del tmp_body_coordinates
        del tmp_fieldmodule
        del tmp_region

        # Groups of different parts of the body
        is_body = fieldmodule.createFieldConstant(1)
        bodyMeshGroup = bodyGroup.getMeshGroup(mesh)
        bodyMeshGroup.addElementsConditional(is_body)

        coreMeshGroup = coreGroup.getMeshGroup(mesh)

        # core group
        e1a = elementsCountAcrossShell
        e1z = elementsCountAcrossMinor - elementsCountAcrossShell - 1
        e2a = elementsCountAcrossShell
        e2b = e2a + elementsCountAcrossTransition
        e2z = elementsCountAcrossMajor - elementsCountAcrossShell - 1
        e2y = e2z - elementsCountAcrossTransition
        e1oc = elementsCountAcrossMinor - 2*elementsCountAcrossShell - 2*elementsCountAcrossTransition
        e2oc = elementsCountAcrossMajor - 2*elementsCountAcrossShell - 2*elementsCountAcrossTransition
        e2oCore = e2oc * e1oc + 2 * elementsCountAcrossTransition * (e2oc + e1oc)
        elementsCountAround = cylinder1.getElementsCountAround()
        e2oShell = elementsCountAround * elementsCountAcrossShell
        e2o = e2oCore + e2oShell
        elementId = cylinder1.getElementIdentifiers()
        for e3 in range(elementsCountAlong):
            for e2 in range(elementsCountAcrossMajor):
                for e1 in range(elementsCountAcrossMinor):
                    coreElement = ((e2 >= e2a) and (e2 <= e2z)) and ((e1 >= e1a) and (e1 <= e1z))
                    if coreElement:
                        elementIdentifier = elementId[e3][e2][e1]
                        if elementIdentifier:
                            element = mesh.findElementByIdentifier(elementIdentifier)
                            coreMeshGroup.addElement(element)

        is_non_core = fieldmodule.createFieldNot(coreGroup.getGroup())
        non_coreMeshGroup = non_coreGroup.getMeshGroup(mesh)
        non_coreMeshGroup.addElementsConditional(is_non_core)

        abdomenMeshGroup = abdomenGroup.getMeshGroup(mesh)
        thoraxMeshGroup = thoraxGroup.getMeshGroup(mesh)
        neckMeshGroup = neckGroup.getMeshGroup(mesh)
        headMeshGroup = headGroup.getMeshGroup(mesh)
        meshGroups = [abdomenMeshGroup, thoraxMeshGroup, neckMeshGroup, headMeshGroup]

        abdomenRange = [1, elementsCountAlongAbdomen*e2o]
        thoraxRange = [abdomenRange[1]+1, abdomenRange[1]+elementsCountAlongThorax*e2o]
        neckRange = [thoraxRange[1]+1, thoraxRange[1] + elementsCountAlongNeck*e2o]
        headRange = [neckRange[1]+1, elementsCountAlong*e2o]
        groupsRanges = [abdomenRange, thoraxRange, neckRange, headRange]

        totalElements = e2o*elementsCountAlong
        for elementIdentifier in range(1, totalElements+1):
            element = mesh.findElementByIdentifier(elementIdentifier)
            if coreMeshGroup.containsElement(element):
                ri = 0
                for groupRange in groupsRanges:
                    if (elementIdentifier >= groupRange[0]) and (elementIdentifier <= groupRange[1]):
                        meshGroups[ri].addElement(element)
                        break
                    ri += 1

        if discontinuity:
            # create discontinuity in d3 on the core boundary
            nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
            elementtemplate = mesh.createElementtemplate()
            undefineNodetemplate = nodes.createNodetemplate()
            undefineNodetemplate.undefineField(coordinates)
            nodetemplate = nodes.createNodetemplate()
            fieldcache = fieldmodule.createFieldcache()
            with ChangeManager(fieldmodule):
                localNodeIndexes = [1, 2, 3, 4]
                valueLabel = Node.VALUE_LABEL_D_DS3
                for e3 in range(elementsCountAlong):
                    for e2 in range(elementsCountAcrossMajor):
                        for e1 in range(elementsCountAcrossMinor):
                            regularRowElement = (((e2 >= e2b) and (e2 <= e2y)) and ((e1 == e1a - 1) or (e1 == e1z + 1)))
                            non_coreFirstLayerElement = (e2 == e2a - 1) or regularRowElement or (e2 == e2z + 1)
                            elementIdentifier = elementId[e3][e2][e1]
                            if elementIdentifier and non_coreFirstLayerElement:
                                element = mesh.findElementByIdentifier(elementIdentifier)
                                eft = element.getElementfieldtemplate(coordinates, -1)
                                nodeIds = get_element_node_identifiers(element, eft)
                                for localNodeIndex in localNodeIndexes:
                                    node = element.getNode(eft, localNodeIndex)
                                    nodetemplate.defineFieldFromNode(coordinates, node)
                                    versionsCount = nodetemplate.getValueNumberOfVersions(coordinates, -1, valueLabel)
                                    if versionsCount == 1:
                                        fieldcache.setNode(node)
                                        result0, x = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, 3)
                                        result0, d1 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, 3)
                                        result0, d2 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, 3)
                                        result0, d3 = coordinates.getNodeParameters(fieldcache, -1, valueLabel, 1, 3)
                                        result1 = node.merge(undefineNodetemplate)
                                        result2 = nodetemplate.setValueNumberOfVersions(coordinates, -1, valueLabel, 2)
                                        result3 = node.merge(nodetemplate)
                                        result4 = coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, x)
                                        result4 = coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, d1)
                                        result4 = coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, d2)
                                        result4 = coordinates.setNodeParameters(fieldcache, -1, valueLabel, 1, d3)
                                        result5 = coordinates.setNodeParameters(fieldcache, -1, valueLabel, 2, d3)
                                remapEftNodeValueLabelsVersion(eft, localNodeIndexes, [valueLabel], 2)
                                result1 = elementtemplate.defineField(coordinates, -1, eft)
                                result2 = element.merge(elementtemplate)
                                result3 = element.setNodesByIdentifier(eft, nodeIds)
        else:
            fieldcache = fieldmodule.createFieldcache()

        # Annotation fiducial point
        markerGroup = findOrCreateFieldGroup(fieldmodule, "marker")
        markerName = findOrCreateFieldStoredString(fieldmodule, name="marker_name")
        markerLocation = findOrCreateFieldStoredMeshLocation(fieldmodule, mesh, name="marker_location")
        markerBodyCoordinates = findOrCreateFieldCoordinates(fieldmodule, name="marker_body_coordinates")
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        markerPoints = findOrCreateFieldNodeGroup(markerGroup, nodes).getNodesetGroup()
        markerTemplateInternal = nodes.createNodetemplate()
        markerTemplateInternal.defineField(markerName)
        markerTemplateInternal.defineField(markerLocation)
        markerTemplateInternal.defineField(markerBodyCoordinates)
        #
        middleLeft = elementsCountAcrossMinor//2
        topElem = elementsCountAcrossMajor - 1
        middleRight = middleLeft - 1
        neckFirstElem = elementsCountAlongAbdomen+elementsCountAlongThorax
        thoraxFirstElem = elementsCountAlongAbdomen
        middleDown = elementsCountAcrossMajor//2 - 1

        # organ landmarks groups
        apexOfHeart = heart_terms.get_heart_term('apex of heart')
        leftAtriumEpicardiumVenousMidpoint = heart_terms.get_heart_term('left atrium epicardium venous midpoint')
        rightAtriumEpicardiumVenousMidpoint = heart_terms.get_heart_term('right atrium epicardium venous midpoint')
        apexOfUrinaryBladder = bladder_terms.get_bladder_term('apex of urinary bladder')
        leftUreterJunctionWithBladder = bladder_terms.get_bladder_term('left ureter junction with bladder')
        rightUreterJunctionWithBladder = bladder_terms.get_bladder_term('right ureter junction with bladder')
        urethraJunctionWithBladderDorsal = bladder_terms.get_bladder_term('urethra junction of dorsal bladder neck')
        urethraJunctionWithBladderVentral = bladder_terms.get_bladder_term('urethra junction of ventral bladder neck')
        gastroesophagalJunctionOnLesserCurvature = stomach_terms.get_stomach_term('esophagogastric junction along the lesser curvature on serosa')
        limitingRidgeOnGreaterCurvature = stomach_terms.get_stomach_term('limiting ridge at the greater curvature on serosa')
        pylorusOnGreaterCurvature = stomach_terms.get_stomach_term('gastroduodenal junction along the greater curvature on serosa')
        junctionBetweenFundusAndBodyOnGreaterCurvature = stomach_terms.get_stomach_term("fundus-body junction along the greater curvature on serosa")
        apexOfLeftLung = lung_terms.get_lung_term('apex of left lung')
        ventralBaseOfLeftLung = lung_terms.get_lung_term('ventral base of left lung')
        dorsalBaseOfLeftLung = lung_terms.get_lung_term('dorsal base of left lung')
        apexOfRightLung = lung_terms.get_lung_term('apex of right lung')
        ventralBaseOfRightLung = lung_terms.get_lung_term('ventral base of right lung')
        dorsalBaseOfRightLung = lung_terms.get_lung_term('dorsal base of right lung')
        laterodorsalTipOfMiddleLobeOfRightLung = lung_terms.get_lung_term('laterodorsal tip of middle lobe of right lung')
        apexOfRightLungAccessoryLobe = lung_terms.get_lung_term('apex of right lung accessory lobe')
        ventralBaseOfRightLungAccessoryLobe = lung_terms.get_lung_term('ventral base of right lung accessory lobe')
        dorsalBaseOfRightLungAccessoryLobe = lung_terms.get_lung_term('dorsal base of right lung accessory lobe')
        medialBaseOfLeftLung = lung_terms.get_lung_term("medial base of left lung")
        medialBaseOfRightLung = lung_terms.get_lung_term("medial base of right lung")
        brainstemDorsalMidlineCaudalPoint = brainstem_terms.get_brainstem_term('brainstem dorsal midline caudal point')
        brainstemDorsalMidlineCranialPoint = brainstem_terms.get_brainstem_term('brainstem dorsal midline cranial point')
        brainstemVentralMidlineCaudalPoint = brainstem_terms.get_brainstem_term('brainstem ventral midline caudal point')
        brainstemVentralMidlineCranialPoint = brainstem_terms.get_brainstem_term('brainstem ventral midline cranial point')

        # marker coordinates. In future we want to have only one table for all species.
        if isRat:
            bodyMarkerPoints = [
                {"group": ("left hip joint", ''), "x": [0.367, 0.266, 0.477]},
                {"group": ("right hip joint", ''), "x": [-0.367, 0.266, 0.477]},
                {"group": ("left shoulder joint", ''), "x": [0.456, -0.071, 2.705]},
                {"group": ("right shoulder joint", ''), "x": [-0.456, -0.071, 2.705]},
                {"group": ("along left femur", ''), "x": [0.456, 0.07, 0.633]},
                {"group": ("along right femur", ''), "x": [-0.456, 0.07, 0.633]},
                {"group": ("along left humerus", ''), "x": [0.423, -0.173, 2.545]},
                {"group": ("along right humerus", ''), "x": [-0.423, -0.173, 2.545]},
                {"group": apexOfUrinaryBladder, "x": [-0.124, -0.383, 0.434]},
                {"group": leftUreterJunctionWithBladder, "x": [-0.111, -0.172, 0.354]},
                {"group": rightUreterJunctionWithBladder, "x": [-0.03, -0.196, 0.363]},
                {"group": urethraJunctionWithBladderDorsal, "x": [-0.03, -0.26, 0.209]},
                {"group": urethraJunctionWithBladderVentral, "x": [-0.037, -0.304, 0.203]},
                {"group": brainstemDorsalMidlineCaudalPoint, "x": [-0.032, 0.418, 2.713]},
                {"group": brainstemDorsalMidlineCranialPoint, "x": [-0.017, 0.203, 2.941]},
                {"group": brainstemVentralMidlineCaudalPoint, "x": [-0.028, 0.388, 2.72]},
                {"group": brainstemVentralMidlineCranialPoint, "x": [-0.019, 0.167, 2.95]},
                {"group": apexOfHeart, "x": [0.096, -0.128, 1.601]},
                {"group": leftAtriumEpicardiumVenousMidpoint, "x": [0.127, -0.083, 2.079]},
                {"group": rightAtriumEpicardiumVenousMidpoint, "x": [0.039, -0.082, 2.075]},
                {"group": apexOfLeftLung, "x": [0.172, -0.175, 2.337]},
                {"group": ventralBaseOfLeftLung, "x": [0.274, -0.285, 1.602]},
                {"group": dorsalBaseOfLeftLung, "x": [0.037, 0.31, 1.649]},
                {"group": apexOfRightLung, "x": [-0.086, -0.096, 2.311]},
                {"group": ventralBaseOfRightLung, "x": [0.14, -0.357, 1.662]},
                {"group": dorsalBaseOfRightLung, "x": [-0.054, 0.304, 1.667]},
                {"group": laterodorsalTipOfMiddleLobeOfRightLung, "x": [-0.258, -0.173, 2.013]},
                {"group": apexOfRightLungAccessoryLobe, "x": [0.041, -0.063, 1.965]},
                {"group": ventralBaseOfRightLungAccessoryLobe, "x": [0.143, -0.356, 1.66]},
                {"group": dorsalBaseOfRightLungAccessoryLobe, "x": [0.121, -0.067, 1.627]},
                {"group": gastroesophagalJunctionOnLesserCurvature, "x": [0.12, 0.009, 1.446]},
                {"group": limitingRidgeOnGreaterCurvature, "x": [0.318, 0.097, 1.406]},
                {"group": pylorusOnGreaterCurvature, "x": [0.08, -0.111, 1.443]},
            ]
        elif isHuman:
            bodyMarkerPoints = [
                {"group": urethraJunctionWithBladderDorsal, "x": [-0.0071, -0.2439, 0.1798]},
                {"group": urethraJunctionWithBladderVentral, "x": [-0.007, -0.2528, 0.1732]},
                {"group": leftUreterJunctionWithBladder, "x": [0.1074, 0.045, 0.1728]},
                {"group": rightUreterJunctionWithBladder, "x": [-0.1058, 0.0533, 0.1701]},
                {"group": apexOfUrinaryBladder, "x": [0.005, 0.1286, 0.1264]},
                {"group": brainstemDorsalMidlineCaudalPoint, "x": [0.0068, 0.427, 2.7389]},
                {"group": brainstemDorsalMidlineCranialPoint, "x": [0.008, -0.0231, 3.0778]},
                {"group": brainstemVentralMidlineCaudalPoint, "x": [0.0054, 0.3041, 2.7374]},
                {"group": brainstemVentralMidlineCranialPoint, "x": [0.0025, -0.2308, 3.091]},
                {"group": apexOfHeart, "x": [0.1373, -0.1855, 1.421]},
                {"group": leftAtriumEpicardiumVenousMidpoint, "x": [0.0024, 0.1452, 1.8022]},
                {"group": rightAtriumEpicardiumVenousMidpoint, "x": [-0.0464, 0.0373, 1.7491]},
                {"group": apexOfLeftLung, "x": [0.0655, -0.0873, 2.3564]},
                {"group": apexOfRightLung, "x": [-0.088, -0.0363, 2.3518]},
                {"group": laterodorsalTipOfMiddleLobeOfRightLung, "x": [-0.2838, -0.0933, 1.9962]},
                {"group": ventralBaseOfLeftLung, "x": [0.219, -0.2866, 1.4602]},
                {"group": medialBaseOfLeftLung, "x": [0.0426, -0.0201, 1.4109]},
                {"group": ventralBaseOfRightLung, "x": [-0.2302, -0.2356, 1.3926]},
                {"group": medialBaseOfRightLung, "x": [-0.0363, 0.0589, 1.3984]},
                {"group": dorsalBaseOfLeftLung, "x": [0.1544, 0.2603, 1.3691]},
                {"group": dorsalBaseOfRightLung, "x": [0.0369, -0.2524, 0.912]},
                {"group": gastroesophagalJunctionOnLesserCurvature, "x": [-0.0062, -0.3259, 0.8586]},
                {"group": pylorusOnGreaterCurvature, "x": [-0.0761, -0.3189, 0.8663]},
                {"group": junctionBetweenFundusAndBodyOnGreaterCurvature, "x": [0.1884, -0.1839, 0.9639]},
            ]

        nodeIdentifier = cylinder1._endNodeIdentifier
        findMarkerLocation = fieldmodule.createFieldFindMeshLocation(markerBodyCoordinates, bodyCoordinates, mesh)
        findMarkerLocation.setSearchMode(FieldFindMeshLocation.SEARCH_MODE_EXACT)
        for bodyMarkerPoint in bodyMarkerPoints:
            markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal)
            fieldcache.setNode(markerPoint)
            markerBodyCoordinates.assignReal(fieldcache, bodyMarkerPoint["x"])
            markerName.assignString(fieldcache, bodyMarkerPoint["group"][0])

            element, xi = findMarkerLocation.evaluateMeshLocation(fieldcache, 3)
            markerLocation.assignMeshLocation(fieldcache, element, xi)
            nodeIdentifier += 1

        return annotationGroups

    @classmethod
    def refineMesh(cls, meshRefinement, options):
        """
        Refine source mesh into separate region, with change of basis.
        :param meshRefinement: MeshRefinement, which knows source and target region.
        :param options: Dict containing options. See getDefaultOptions().
        """
        assert isinstance(meshRefinement, MeshRefinement)
        refineElementsCountAcrossMajor = options['Refine number of elements across major']
        refineElementsCountAlong = options['Refine number of elements along']
        meshRefinement.refineAllElementsCubeStandard3d(refineElementsCountAcrossMajor, refineElementsCountAlong, refineElementsCountAcrossMajor)

    @classmethod
    def defineFaceAnnotations(cls, region, options, annotationGroups):
        """
        Add face annotation groups from the highest dimension mesh.
        Must have defined faces and added subelements for highest dimension groups.
        :param region: Zinc region containing model.
        :param options: Dict containing options. See getDefaultOptions().
        :param annotationGroups: List of annotation groups for top-level elements.
        New face annotation groups are appended to this list.
        """

        # create 2d surface mesh groups
        fm = region.getFieldmodule()
        mesh2d = fm.findMeshByDimension(2)

        bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_body_term("body"))
        coreGroup = getAnnotationGroupForTerm(annotationGroups, get_body_term("core"))
        non_coreGroup = getAnnotationGroupForTerm(annotationGroups, get_body_term("non core"))
        abdomenGroup = getAnnotationGroupForTerm(annotationGroups, get_body_term("abdomen"))
        thoraxGroup = getAnnotationGroupForTerm(annotationGroups, get_body_term("thorax"))
        neckGroup = getAnnotationGroupForTerm(annotationGroups, get_body_term("neck core"))

        skinGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_body_term("skin epidermis"))
        coreBoundaryGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_body_term("core boundary"))
        diaphragmGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_body_term("diaphragm"))
        spinalCordGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_body_term("spinal cord"))

        is_exterior = fm.createFieldIsExterior()
        is_on_face_xi3_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)
        is_skin = fm.createFieldAnd(is_exterior, is_on_face_xi3_1)

        skinMeshGroup = skinGroup.getMeshGroup(mesh2d)
        skinMeshGroup.addElementsConditional(is_skin)

        is_core_boundary = fm.createFieldAnd(coreGroup.getGroup(), non_coreGroup.getGroup())
        coreBoundaryMeshGroup = coreBoundaryGroup.getMeshGroup(mesh2d)
        coreBoundaryMeshGroup.addElementsConditional(is_core_boundary)

        is_diaphragm = fm.createFieldAnd(abdomenGroup.getGroup(), thoraxGroup.getGroup())
        diaphragmMeshGroup = diaphragmGroup.getMeshGroup(mesh2d)
        diaphragmMeshGroup.addElementsConditional(is_diaphragm)

        # spinal cord
        coordinates = fm.findFieldByName('coordinates').castFiniteElement()
        zero = fm.createFieldConstant(0)
        zero_m = fm.createFieldConstant(-0.01)
        zero_p = fm.createFieldConstant(0.01)
        comp2 = cls.axis2.index(max(cls.axis2)) + 1
        ax2_comp = fm.createFieldComponent(coordinates, comp2)
        ax2_gt_zero_m = fm.createFieldGreaterThan(ax2_comp, zero_m)
        ax2_lt_zero_p = fm.createFieldLessThan(ax2_comp, zero_p)
        ax2_gt_zero_xi10 = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), ax2_gt_zero_m)
        ax2_lt_zero_xi10 = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), ax2_lt_zero_p)
        is_ax2_zero = fm.createFieldAnd(ax2_lt_zero_xi10, ax2_gt_zero_xi10)
        comp3 = cls.axis3.index(max(cls.axis3)) + 1
        ax3_comp = fm.createFieldComponent(coordinates, comp3)
        ax3_positive = fm.createFieldGreaterThan(ax3_comp, zero)
        is_ax2_zero_ax3_positive = fm.createFieldAnd(is_ax2_zero, ax3_positive)
        is_abdomen_thorax = fm.createFieldAdd(abdomenGroup.getGroup(), thoraxGroup.getGroup())
        is_abdomen_thorax_neck = fm.createFieldAdd(is_abdomen_thorax, neckGroup.getGroup())
        is_abdomen_thorax_neck_boundary = fm.createFieldAnd(is_core_boundary, is_abdomen_thorax_neck)
        is_spinal_cord = fm.createFieldAnd(is_ax2_zero_ax3_positive, is_abdomen_thorax_neck_boundary)

        mesh1d = fm.findMeshByDimension(1)
        spinalCordMeshGroup = spinalCordGroup.getMeshGroup(mesh1d)
        spinalCordMeshGroup.addElementsConditional(is_spinal_cord)
class MeshType_3d_colon1(Scaffold_base):
    '''
    Generates a 3-D colon mesh with variable numbers
    of elements around, along the central line, and through wall.
    The colon is created by a function that generates a colon
    segment and uses tubemesh to map the segment along a central
    line profile.
    '''

    centralPathDefaultScaffoldPackages = {
        'Human 1' : ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings' : {
                'Coordinate dimensions' : 3,
                'Length' : 1.0,
                'Number of elements' : 8
                },
            'meshEdits' : exnodeStringFromNodeValues(
                [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2  ], [
                [ [   0.0,   0.0, 0.0 ], [ -50.7,  178.2, 0.0 ], [ -24.0,  -6.0, -12.0 ], [ -14.0,  -1.0, -12.0 ] ],
                [ [ -47.4, 188.6, 0.0 ], [ -19.3,  177.1, 0.0 ], [ -22.0,  -4.0,  -8.0 ], [  -4.0,  19.0,  22.0 ] ],
                [ [  -4.4, 396.5, 0.0 ], [ 206.0,   40.1, 0.0 ], [ -10.0,  20.0,   8.0 ], [  -6.0,   0.0,  51.0 ] ],
                [ [ 130.0, 384.1, 0.0 ], [ 130.8,  -40.5, 0.0 ], [  -5.0,   4.0,  29.0 ], [   0.0,   1.0,  24.0 ] ],
                [ [ 279.4, 383.0, 0.0 ], [ 118.0,   48.7, 0.0 ], [  -2.0,  10.0,  22.0 ], [   5.0,  25.0, -20.0 ] ],
                [ [ 443.9, 390.8, 0.0 ], [ 111.3,  -97.0, 0.0 ], [  10.0,  17.0,   6.0 ], [   1.0,  -6.0, -35.0 ] ],
                [ [ 475.2, 168.0, 0.0 ], [  -0.8, -112.4, 0.0 ], [  20.0,   0.0, -20.0 ], [  15.0,  -1.0, -10.0 ] ],
                [ [ 432.6, -32.3, 0.0 ], [ -90.5,  -59.0, 0.0 ], [   6.0,  -9.0, -14.0 ], [   8.0, -11.0, -13.0 ] ],
                [ [ 272.4,   7.5, 0.0 ], [ -79.0,   47.4, 0.0 ], [   1.0, -11.0, -18.0 ], [   4.0, -12.0, -12.0 ] ] ] )
            } ),
        'Human 2' : ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings' : {
                'Coordinate dimensions' : 3,
                'Length' : 1.0,
                'Number of elements' : 8
                },
            'meshEdits' : exnodeStringFromNodeValues(
                [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2 ], [
                [ [   0.0,   0.0,    0.0 ], [ -34.7,  104.1, -18.1 ], [ -24.0,  -6.0, -12.0 ], [ -14.0,  -1.0, -12.0 ] ],
                [ [ -34.5,  114.0, -18.1 ], [   1.2,   86.6,  -3.4 ], [ -22.0,  -4.0,  -8.0 ], [  -4.0,  19.0,  22.0 ] ],
                [ [ -19.1,  218.5,   5.5 ], [  78.7,   -7.1,  94.5 ], [ -10.0,  20.0,   8.0 ], [  -6.0,   0.0,  51.0 ] ],
                [ [  82.5,  189.1,  94.2 ], [  84.5,    7.1,  71.6 ], [  -5.0,   4.0,  29.0 ], [   0.0,   1.0,  24.0 ] ],
                [ [ 226.6,  218.7,  85.7 ], [  95.0,   91.3, -58.5 ], [  -2.0,  10.0,  22.0 ], [   5.0,  25.0, -20.0 ] ],
                [ [ 325.5,  381.7, -57.9 ], [ 229.2,  -66.7, -20.4 ], [  10.0,  17.0,   6.0 ], [   1.0,  -6.0, -35.0 ] ],
                [ [ 354.0,  105.3, -24.4 ], [  -6.3, -143.7,  20.3 ], [  20.0,   0.0, -20.0 ], [  15.0,  -1.0, -10.0 ] ],
                [ [ 296.5, -121.2,  -0.6 ], [ -90.5,  -59.0,   0.0 ], [   6.0,  -9.0, -14.0 ], [   8.0, -11.0, -13.0 ] ],
                [ [ 169.8,  -73.4, -33.5 ], [ -72.2,   43.4, -27.4 ], [   1.0, -11.0, -18.0 ], [   4.0, -12.0, -12.0 ] ] ] )
            } ),
        'Mouse 1' : ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings' : {
                'Coordinate dimensions' : 3,
                'Length' : 1.0,
                'Number of elements' : 7
                },
            'meshEdits' : exnodeStringFromNodeValues(
                [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2 ], [
                [ [   0.0,   0.0,  0.0 ], [  6.0, 12.0,  -2.0 ], [ 2.0,  1.0,  2.0 ], [ 6.0, 0.0, 3.0 ] ],
                [ [  -2.0,  11.0, -3.0 ], [ -8.0,  4.0,   9.0 ], [ 2.0,  2.0,  1.0 ], [ 0.0, 1.0, 2.0 ] ],
                [ [  -3.0,   2.0,  3.0 ], [ -4.0, -8.0,   0.0 ], [ 2.0, -1.0,  2.0 ], [ 1.0, 0.0, 2.0 ] ],
                [ [ -11.0,  -3.0, -4.0 ], [ -8.0, -3.0,  -7.0 ], [ 1.0, -2.0,  1.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -16.0,  -4.0,  0.0 ], [  4.0, -3.0,  14.0 ], [ 1.0, -3.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -7.0,  -8.0,  0.0 ], [  5.0, -1.0, -14.0 ], [ 0.0, -3.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -1.0,  -6.0, -1.0 ], [  2.0, -2.0,   9.0 ], [ 1.0, -3.0, -1.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -2.0, -14.0,  5.0 ], [ -2.0, -4.0,   2.0 ], [ 1.0, -2.0, -2.0 ], [ 0.0, 0.0, 0.5 ] ] ] )
            } ),
        'Mouse 2' : ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings' : {
                'Coordinate dimensions' : 3,
                'Length' : 1.0,
                'Number of elements' : 4
                },
            'meshEdits' : exnodeStringFromNodeValues(
                [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2 ], [
                [ [   0.0,  0.0,   0.0 ], [  0.0,  0.0,  13.0 ], [  0.0, -10.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   0.0,  0.0,  13.0 ], [  0.0,  2.0,  28.0 ], [  0.0, -10.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -14.0, -2.0,  13.0 ], [  0.0, -3.0, -19.0 ], [  0.0, -10.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -14.0, -1.0, -10.0 ], [  1.0,  1.0, -17.0 ], [  0.0, -10.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -14.0,  0.0, -28.0 ], [  0.0,  0.0, -11.0 ], [  0.0, -10.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ] ] )
            } ),
        'Pig 1' : ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings' : {
                'Coordinate dimensions' : 3,
                'Length' : 1.0,
                'Number of elements' : 36
                },
            'meshEdits' : exnodeStringFromNodeValues(
                [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2 ], [
                [ [   -7.2,   83.3,  -20.7 ], [  -65.2,   -8.1,   7.6 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -68.5,   52.8,   -9.6 ], [  -40.1,  -36.1,  10.7 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -97.4,  -26.3,    5.7 ], [   18.0,  -93.2,  13.7 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -56.8,  -90.5,   14.1 ], [   65.5,  -41.4,   7.3 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   48.9, -100.8,   24.0 ], [  112.2,   40.1,  19.0 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  114.8,  -12.6,   38.7 ], [    8.2,   96.1,  14.2 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   60.3,   83.5,   43.7 ], [ -108.7,   54.1,  22.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -41.2,   90.7,   56.3 ], [  -89.0,  -32.4,  14.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -107.9,   -9.7,   76.6 ], [   11.1,  -94.4,  11.3 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -57.3,  -91.9,   81.3 ], [   71.2,  -31.2,   5.7 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   51.2,  -89.4,   97.2 ], [   99.1,   55.4,  12.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   91.6,    9.3,  103.6 ], [    4.7,   51.2,   3.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   61.6,  111.8,  109.6 ], [  -85.2,   46.1,   2.6 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -54.6,   91.9,  129.4 ], [  -92.7,  -55.0,  14.5 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -109.0,    5.6,  156.9 ], [   23.6, -108.2,  27.7 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -59.1,  -62.5,  170.8 ], [   74.0,  -20.1,  14.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   23.5,  -53.2,  179.7 ], [   84.6,   47.0,   6.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   62.3,   30.1,  187.5 ], [  -12.8,   58.0,   0.8 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   22.4,   45.2,  181.1 ], [  -23.6,  -34.5,  -7.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   -1.9,    4.9,  180.5 ], [  -41.3,  -30.9,   7.5 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -45.1,  -12.6,  194.4 ], [  -40.5,   -4.6,   6.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -71.7,   -2.2,  197.2 ], [  -25.2,   35.8,  -6.8 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -65.8,   42.1,  182.3 ], [   26.6,   37.6, -15.6 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -14.1,   81.2,  163.5 ], [   41.0,   10.3,  -9.5 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   61.7,   86.1,  156.4 ], [   77.9,  -40.7,   8.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   92.9,   20.5,  150.3 ], [    0.0,  -73.3,  -5.2 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   48.9,  -65.0,  142.8 ], [  -82.8,  -80.0,  -1.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -54.3,  -90.8,  134.0 ], [  -60.1,   26.4,  -8.2 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -89.9,   11.2,  115.0 ], [   34.9,  125.1, -27.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -17.4,   74.2,   91.1 ], [   78.8,   19.1, -15.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   43.4,   50.2,   73.3 ], [   30.2,  -36.0,  -9.9 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   62.4,   -5.1,   63.5 ], [   10.9,  -54.2,  -2.7 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   32.7,  -51.7,   56.1 ], [  -38.6,  -29.8,  -8.1 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -38.1,  -28.6,   46.8 ], [  -66.0,   83.3, -12.1 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [   -5.4,   32.0,   26.0 ], [   48.1,   17.6, -21.4 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  146.6,  101.2,  -41.2 ], [   63.3,   35.3, -31.2 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  312.3,  199.5, -123.4 ], [   39.7,   24.3, -20.0 ], [ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 0.5 ] ] ] )
            } ),
        'Pig 2': ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings': {
                'Coordinate dimensions': 3,
                'Length': 90.0,
                'Number of elements': 3
            },
            'meshEdits': exnodeStringFromNodeValues(
                [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], [
                [ [  0.0, 0.0, 0.0 ], [ 30.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 0.0, 0.0 ] ],
                [ [ 30.0, 0.0, 0.0 ], [ 30.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 0.0, 0.0 ] ],
                [ [ 60.0, 0.0, 0.0 ], [ 30.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 0.0, 0.0 ] ],
                [ [ 90.0, 0.0, 0.0 ], [ 30.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 0.0, 0.0 ] ] ] )
            } ),
        }

    @staticmethod
    def getName():
        return '3D Colon 1'

    @staticmethod
    def getParameterSetNames():
        return [
            'Default',
            'Human 1',
            'Human 2',
            'Mouse 1',
            'Mouse 2',
            'Pig 1',
            'Pig 2']

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        if 'Human 2' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Human 2']
        elif 'Mouse 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Mouse 1']
        elif 'Mouse 2' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Mouse 2']
        elif 'Pig 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 1']
        elif 'Pig 2' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 2']
        else:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Human 1']
        if 'Mouse' in parameterSetName:
            segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName = 'Mouse 1')
        elif 'Pig' in parameterSetName:
            segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName = 'Pig 1')
        else:
            segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName = 'Human 1')
        options = {
            'Central path' : copy.deepcopy(centralPathOption),
            'Segment profile' : segmentProfileOption,
            'Number of segments': 30,
            'Start phase': 0.0,
            'Proximal length': 420.0,
            'Transverse length': 460.0,
            'Distal length': 620.0,
            'Proximal inner radius': 43.5,
            'Proximal tenia coli width': 10.0,
            'Proximal-transverse inner radius': 33.0,
            'Proximal-transverse tenia coli width': 10.0,
            'Transverse-distal inner radius': 29.0,
            'Transverse-distal tenia coli width': 10.0,
            'Distal inner radius': 31.5,
            'Distal tenia coli width': 10.0,
            'Refine' : False,
            'Refine number of elements around' : 1,
            'Refine number of elements along' : 1,
            'Refine number of elements through wall' : 1
            }
        if 'Human 2' in parameterSetName:
            options['Proximal length'] = 180.0
            options['Transverse length'] = 620.0
            options['Distal length'] = 700.0
        elif 'Mouse' in parameterSetName:
            options['Number of segments'] = 10
            options['Proximal length'] = 30.0
            options['Transverse length'] = 20.0
            options['Distal length'] = 25.0
            options['Proximal inner radius'] = 1.0
            options['Proximal tenia coli width'] = 0.5
            options['Proximal-transverse inner radius'] = 0.9
            options['Proximal-transverse tenia coli width'] = 0.7
            options['Transverse-distal inner radius'] = 0.7
            options['Transverse-distal tenia coli width'] = 1.0
            options['Distal inner radius'] = 0.7
            options['Distal tenia coli width'] = 1.0
        elif 'Pig 1' in parameterSetName:
            options['Number of segments'] = 120
            options['Proximal length'] = 3000.0
            options['Transverse length'] = 200.0
            options['Distal length'] = 200.0
            options['Proximal inner radius'] = 38.0
            options['Proximal tenia coli width'] = 5.0
            options['Proximal-transverse inner radius'] = 14.0
            options['Proximal-transverse tenia coli width'] = 4.0
            options['Transverse-distal inner radius'] = 10.5
            options['Transverse-distal tenia coli width'] = 3.0
            options['Distal inner radius'] = 8.0
            options['Distal tenia coli width'] = 1.5
        elif 'Pig 2' in parameterSetName:
            options['Number of segments'] = 3
            options['Proximal length'] = 30.0
            options['Transverse length'] = 30.0
            options['Distal length'] = 30.0
            options['Proximal inner radius'] = 16.0
            options['Proximal tenia coli width'] = 5.0
            options['Proximal-transverse inner radius'] = 16.0
            options['Proximal-transverse tenia coli width'] = 5.0
            options['Transverse-distal inner radius'] = 16.0
            options['Transverse-distal tenia coli width'] = 5.0
            options['Distal inner radius'] = 16.0
            options['Distal tenia coli width'] = 5.0
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path',
            'Segment profile',
            'Number of segments',
            'Start phase',
            'Proximal length',
            'Transverse length',
            'Distal length',
            'Proximal inner radius',
            'Proximal tenia coli width',
            'Proximal-transverse inner radius',
            'Proximal-transverse tenia coli width',
            'Transverse-distal inner radius',
            'Transverse-distal tenia coli width',
            'Distal inner radius',
            'Distal tenia coli width',
            'Refine',
            'Refine number of elements around',
            'Refine number of elements along',
            'Refine number of elements through wall' ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [ MeshType_1d_path1 ]
        if optionName == 'Segment profile':
            return [ MeshType_3d_colonsegment1 ]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \
            cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
                ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(cls.centralPathDefaultScaffoldPackages[parameterSetName])
        if optionName == 'Segment profile':
            if not parameterSetName:
                parameterSetName = scaffoldType.getParameterSetNames()[0]
            return ScaffoldPackage(scaffoldType, defaultParameterSetName = parameterSetName)
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType() in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage('Central path', MeshType_1d_path1)
        if not options['Segment profile'].getScaffoldType() in cls.getOptionValidScaffoldTypes('Segment profile'):
            options['Segment profile'] = cls.getOptionScaffoldPackage('Segment profile', MeshType_3d_colonsegment1)
        for key in [
            'Number of segments',
            'Refine number of elements around',
            'Refine number of elements along',
            'Refine number of elements through wall']:
            if options[key] < 1:
                options[key] = 1
        for key in [
            'Proximal length',
            'Transverse length',
            'Distal length',
            'Proximal inner radius',
            'Proximal tenia coli width',
            'Proximal-transverse inner radius',
            'Proximal-transverse tenia coli width',
            'Transverse-distal inner radius',
            'Transverse-distal tenia coli width',
            'Distal inner radius',
            'Distal tenia coli width']:
            if options[key] < 0.0:
                options[key] = 0.0

    @classmethod
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: annotationGroups
        """
        centralPath = options['Central path']
        segmentProfile = options['Segment profile']
        segmentCount = options['Number of segments']
        startPhase = options['Start phase'] % 360.0
        proximalLength = options['Proximal length']
        transverseLength = options['Transverse length']
        proximalInnerRadius = options['Proximal inner radius']
        proximalTCWidth = options['Proximal tenia coli width']
        proximalTransverseInnerRadius = options['Proximal-transverse inner radius']
        proximalTransverseTCWidth = options['Proximal-transverse tenia coli width']
        transverseDistalInnerRadius = options['Transverse-distal inner radius']
        transverseDistalTCWidth = options['Transverse-distal tenia coli width']
        distalInnerRadius = options['Distal inner radius']
        distalTCWidth = options['Distal tenia coli width']
        segmentSettings = segmentProfile.getScaffoldSettings()

        elementsCountAroundTC = segmentSettings['Number of elements around tenia coli']
        elementsCountAroundHaustrum = segmentSettings['Number of elements around haustrum']
        cornerInnerRadiusFactor = segmentSettings['Corner inner radius factor']
        haustrumInnerRadiusFactor = segmentSettings['Haustrum inner radius factor']
        segmentLengthEndDerivativeFactor = segmentSettings['Segment length end derivative factor']
        segmentLengthMidDerivativeFactor = segmentSettings['Segment length mid derivative factor']
        tcCount = segmentSettings['Number of tenia coli']
        tcThickness = segmentSettings['Tenia coli thickness']
        elementsCountAround = (elementsCountAroundTC + elementsCountAroundHaustrum)*tcCount

        elementsCountAlongSegment = segmentSettings['Number of elements along segment']
        elementsCountThroughWall = segmentSettings['Number of elements through wall']
        wallThickness = segmentSettings['Wall thickness']
        useCrossDerivatives = segmentSettings['Use cross derivatives']
        useCubicHermiteThroughWall = not(segmentSettings['Use linear through wall'])
        elementsCountAlong = int(elementsCountAlongSegment*segmentCount)

        firstNodeIdentifier = 1
        firstElementIdentifier = 1

        # Central path
        tmpRegion = region.createRegion()
        centralPath.generate(tmpRegion)
        cx, cd1, cd2, cd12 = extractPathParametersFromRegion(tmpRegion)
        # for i in range(len(cx)):
        #     print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], '],')
        del tmpRegion

        # find arclength of colon
        length = 0.0
        elementsCountIn = len(cx) - 1
        sd1 = interp.smoothCubicHermiteDerivativesLine(cx, cd1, fixAllDirections = True,
            magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN)
        for e in range(elementsCountIn):
            arcLength = interp.getCubicHermiteArcLength(cx[e], sd1[e], cx[e + 1], sd1[e + 1])
            # print(e+1, arcLength)
            length += arcLength
        segmentLength = length / segmentCount
        # print('Length = ', length)
        elementAlongLength = length / elementsCountAlong

        # Sample central path
        sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(cx, cd1, elementsCountAlong)
        sd2, sd12 = interp.interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf)

        # Generate variation of radius & tc width along length
        lengthList = [0.0, proximalLength, proximalLength + transverseLength, length]
        innerRadiusList = [proximalInnerRadius, proximalTransverseInnerRadius,
                           transverseDistalInnerRadius, distalInnerRadius]
        innerRadiusAlongElementList, dInnerRadiusAlongElementList = interp.sampleParameterAlongLine(lengthList,
                                                                                                    innerRadiusList,
                                                                                                    elementsCountAlong)

        tcWidthList = [proximalTCWidth, proximalTransverseTCWidth, transverseDistalTCWidth, distalTCWidth]
        tcWidthAlongElementList, dTCWidthAlongElementList = interp.sampleParameterAlongLine(lengthList,
                                                                                            tcWidthList,
                                                                                            elementsCountAlong)

        # Account for reduced haustrum appearance in transverse and distal pig colon
        if tcCount == 2:
            haustrumInnerRadiusFactorList = [haustrumInnerRadiusFactor, haustrumInnerRadiusFactor*0.75,
                                             haustrumInnerRadiusFactor*0.5, haustrumInnerRadiusFactor*0.2]
            haustrumInnerRadiusFactorAlongElementList = \
                interp.sampleParameterAlongLine(lengthList, haustrumInnerRadiusFactorList, elementsCountAlong)[0]
        else:
            haustrumInnerRadiusFactorAlongElementList = [haustrumInnerRadiusFactor]*(elementsCountAlong+1)

        # Create annotation groups for colon sections
        elementsAlongInProximal = round(proximalLength/elementAlongLength)
        elementsAlongInTransverse = round(transverseLength/elementAlongLength)
        elementsAlongInDistal = elementsCountAlong - elementsAlongInProximal - elementsAlongInTransverse
        elementsCountAlongGroups = [elementsAlongInProximal, elementsAlongInTransverse, elementsAlongInDistal]

        colonGroup = AnnotationGroup(region, get_colon_term("colon"))

        if tcCount == 1:
            proximalGroup = AnnotationGroup(region, get_colon_term("proximal colon"))
            transverseGroup = AnnotationGroup(region, get_colon_term("transverse colon"))
            distalGroup = AnnotationGroup(region, get_colon_term("distal colon"))
            annotationGroupAlong = [[colonGroup, proximalGroup],
                                    [colonGroup, transverseGroup],
                                    [colonGroup, distalGroup]]

        elif tcCount == 2:
            spiralGroup = AnnotationGroup(region, get_colon_term("spiral colon"))
            transverseGroup = AnnotationGroup(region, get_colon_term("transverse colon"))
            distalGroup = AnnotationGroup(region, get_colon_term("distal colon"))
            annotationGroupAlong = [[colonGroup, spiralGroup],
                                    [colonGroup, transverseGroup],
                                    [colonGroup, distalGroup]]

        elif tcCount == 3:
            ascendingGroup = AnnotationGroup(region, get_colon_term("ascending colon"))
            transverseGroup = AnnotationGroup(region, get_colon_term("transverse colon"))
            descendingGroup = AnnotationGroup(region, get_colon_term("descending colon"))
            annotationGroupAlong = [[colonGroup, ascendingGroup],
                                    [colonGroup, transverseGroup],
                                    [colonGroup, descendingGroup]]

        annotationGroupsAlong = []
        for i in range(len(elementsCountAlongGroups)):
            elementsCount = elementsCountAlongGroups[i]
            for n in range(elementsCount):
                annotationGroupsAlong.append(annotationGroupAlong[i])

        annotationGroupsThroughWall = []
        for i in range(elementsCountThroughWall):
            annotationGroupsThroughWall.append([ ])

        xExtrude = []
        d1Extrude = []
        d2Extrude = []
        d3UnitExtrude = []
        sxRefExtrudeList = []

        # Create object
        colonSegmentTubeMeshInnerPoints = ColonSegmentTubeMeshInnerPoints(
            region, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlongSegment,
            tcCount, segmentLengthEndDerivativeFactor, segmentLengthMidDerivativeFactor,
            segmentLength, wallThickness, cornerInnerRadiusFactor, haustrumInnerRadiusFactorAlongElementList,
            innerRadiusAlongElementList, dInnerRadiusAlongElementList, tcWidthAlongElementList,
            startPhase)

        for nSegment in range(segmentCount):
            # Create inner points
            xInner, d1Inner, d2Inner, transitElementList, segmentAxis, annotationGroupsAround \
                = colonSegmentTubeMeshInnerPoints.getColonSegmentTubeMeshInnerPoints(nSegment)

            # Project reference point for warping onto central path
            start = nSegment * elementsCountAlongSegment
            end = (nSegment + 1) * elementsCountAlongSegment + 1
            sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \
                tubemesh.getPlaneProjectionOnCentralPath(xInner, elementsCountAround, elementsCountAlongSegment,
                                                         segmentLength, sx[start:end], sd1[start:end], sd2[start:end],
                                                         sd12[start:end])

            # Warp segment points
            xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = tubemesh.warpSegmentPoints(
                xInner, d1Inner, d2Inner, segmentAxis, sxRefList, sd1RefList, sd2ProjectedListRef,
                elementsCountAround, elementsCountAlongSegment, zRefList, innerRadiusAlongElementList[start:end],
                closedProximalEnd=False)

            # Store points along length
            xExtrude +=  xWarpedList if nSegment == 0 else xWarpedList[elementsCountAround:]
            d1Extrude += d1WarpedList if nSegment == 0 else d1WarpedList[elementsCountAround:]
            d2Extrude += d2WarpedList if nSegment == 0 else d2WarpedList[elementsCountAround:]
            d3UnitExtrude += d3WarpedUnitList if nSegment == 0 else d3WarpedUnitList[elementsCountAround:]
            sxRefExtrudeList += sxRefList if nSegment == 0 else sxRefList[elementsCountAround:]

        contractedWallThicknessList = colonSegmentTubeMeshInnerPoints.getContractedWallThicknessList()

        # Create coordinates and derivatives
        xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(xExtrude, d1Extrude,
            d2Extrude, d3UnitExtrude, contractedWallThicknessList,
            elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList)

        relaxedLengthList, xiList = colonSegmentTubeMeshInnerPoints.getRelaxedLengthAndXiList()

        closedProximalEnd = False

        if tcThickness > 0:
            tubeTCWidthList = colonSegmentTubeMeshInnerPoints.getTubeTCWidthList()
            xList, d1List, d2List, d3List, annotationArrayAround = getTeniaColi(
                region, xList, d1List, d2List, d3List, curvatureList, tcCount, elementsCountAroundTC,
                elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall,
                tubeTCWidthList, tcThickness, sxRefExtrudeList, annotationGroupsAround,
                closedProximalEnd)

            # Create flat and texture coordinates
            xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = createFlatAndTextureCoordinatesTeniaColi(
                xiList, relaxedLengthList, length, wallThickness, tcCount, tcThickness,
                elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong,
                elementsCountThroughWall, transitElementList, closedProximalEnd)

            # Create nodes and elements
            nextNodeIdentifier, nextElementIdentifier, annotationGroups = createNodesAndElementsTeniaColi(
                region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture,
                elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall,
                tcCount, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall,
                firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives,
                closedProximalEnd)

        else:
            # Create flat and texture coordinates
            xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = tubemesh.createFlatAndTextureCoordinates(
                xiList, relaxedLengthList, length, wallThickness, elementsCountAround,
                elementsCountAlong, elementsCountThroughWall, transitElementList)

            # Create nodes and elements
            nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements(
                region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture,
                elementsCountAround, elementsCountAlong, elementsCountThroughWall,
                annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall,
                firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives,
                closedProximalEnd)

        return annotationGroups

    @classmethod
    def refineMesh(cls, meshrefinement, options):
        """
        Refine source mesh into separate region, with change of basis.
        :param meshrefinement: MeshRefinement, which knows source and target region.
        :param options: Dict containing options. See getDefaultOptions().
        """
        refineElementsCountAround = options['Refine number of elements around']
        refineElementsCountAlong = options['Refine number of elements along']
        refineElementsCountThroughWall = options['Refine number of elements through wall']

        meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong,
                                                       refineElementsCountThroughWall)
        return
 def getDefaultOptions(cls, parameterSetName='Default'):
     if 'Human 2' in parameterSetName:
         centralPathOption = cls.centralPathDefaultScaffoldPackages['Human 2']
     elif 'Mouse 1' in parameterSetName:
         centralPathOption = cls.centralPathDefaultScaffoldPackages['Mouse 1']
     elif 'Mouse 2' in parameterSetName:
         centralPathOption = cls.centralPathDefaultScaffoldPackages['Mouse 2']
     elif 'Pig 1' in parameterSetName:
         centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 1']
     elif 'Pig 2' in parameterSetName:
         centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 2']
     else:
         centralPathOption = cls.centralPathDefaultScaffoldPackages['Human 1']
     if 'Mouse' in parameterSetName:
         segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName = 'Mouse 1')
     elif 'Pig' in parameterSetName:
         segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName = 'Pig 1')
     else:
         segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName = 'Human 1')
     options = {
         'Central path' : copy.deepcopy(centralPathOption),
         'Segment profile' : segmentProfileOption,
         'Number of segments': 30,
         'Start phase': 0.0,
         'Proximal length': 420.0,
         'Transverse length': 460.0,
         'Distal length': 620.0,
         'Proximal inner radius': 43.5,
         'Proximal tenia coli width': 10.0,
         'Proximal-transverse inner radius': 33.0,
         'Proximal-transverse tenia coli width': 10.0,
         'Transverse-distal inner radius': 29.0,
         'Transverse-distal tenia coli width': 10.0,
         'Distal inner radius': 31.5,
         'Distal tenia coli width': 10.0,
         'Refine' : False,
         'Refine number of elements around' : 1,
         'Refine number of elements along' : 1,
         'Refine number of elements through wall' : 1
         }
     if 'Human 2' in parameterSetName:
         options['Proximal length'] = 180.0
         options['Transverse length'] = 620.0
         options['Distal length'] = 700.0
     elif 'Mouse' in parameterSetName:
         options['Number of segments'] = 10
         options['Proximal length'] = 30.0
         options['Transverse length'] = 20.0
         options['Distal length'] = 25.0
         options['Proximal inner radius'] = 1.0
         options['Proximal tenia coli width'] = 0.5
         options['Proximal-transverse inner radius'] = 0.9
         options['Proximal-transverse tenia coli width'] = 0.7
         options['Transverse-distal inner radius'] = 0.7
         options['Transverse-distal tenia coli width'] = 1.0
         options['Distal inner radius'] = 0.7
         options['Distal tenia coli width'] = 1.0
     elif 'Pig 1' in parameterSetName:
         options['Number of segments'] = 120
         options['Proximal length'] = 3000.0
         options['Transverse length'] = 200.0
         options['Distal length'] = 200.0
         options['Proximal inner radius'] = 38.0
         options['Proximal tenia coli width'] = 5.0
         options['Proximal-transverse inner radius'] = 14.0
         options['Proximal-transverse tenia coli width'] = 4.0
         options['Transverse-distal inner radius'] = 10.5
         options['Transverse-distal tenia coli width'] = 3.0
         options['Distal inner radius'] = 8.0
         options['Distal tenia coli width'] = 1.5
     elif 'Pig 2' in parameterSetName:
         options['Number of segments'] = 3
         options['Proximal length'] = 30.0
         options['Transverse length'] = 30.0
         options['Distal length'] = 30.0
         options['Proximal inner radius'] = 16.0
         options['Proximal tenia coli width'] = 5.0
         options['Proximal-transverse inner radius'] = 16.0
         options['Proximal-transverse tenia coli width'] = 5.0
         options['Transverse-distal inner radius'] = 16.0
         options['Transverse-distal tenia coli width'] = 5.0
         options['Distal inner radius'] = 16.0
         options['Distal tenia coli width'] = 5.0
     return options
class MeshType_3d_esophagus1(Scaffold_base):
    """
    Generates a 3-D esophagus mesh with variable numbers of elements around, along the central line, and through wall.
    The esophagus is created by a function that generates an elliptical tube segment and uses tubemesh to map the
    segment along a central path profile.
    """

    centralPathDefaultScaffoldPackages = {
        'Human 1':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 1.0,
                    'Number of elements': 4
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                    Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                ], [[[7.5, 289.8, 0.0], [2.8, -33.6, 0.0], [9.9, 0.8, 0.0],
                     [0.1, -1.6, 0.0], [0.0, 0.0, 10.0], [0.0, 0.0, 0.0]],
                    [[8.9, 242.5, 0.0], [-2.6, -87.5, 0.0], [10.0, -0.3, 0.0],
                     [0.1, -0.6, 0.0], [0.0, 0.0, 10.0], [0.0, 0.0, 0.0]],
                    [[-2.5, 115.4, 0.0], [3.9, -102.0, 0.0], [10.0, 0.4, 0.0],
                     [-0.3, 1.9, 0.0], [0.0, 0.0, 10.0], [0.0, 0.0, 0.0]],
                    [[10.1, 40.3, 0.0], [17.8, -57.9, 0.0], [9.6, 2.9, 0.0],
                     [-1.3, 3.2, 0.0], [0.0, 0.0, 10.0], [0.0, 0.0, 0.0]],
                    [[28.6, -0.1, 0.0], [18.5, -22.1, 0.0], [7.7, 6.5, 0.0],
                     [-2.4, 3.9, 0.0], [0.0, 0.0, 10.0], [0.0, 0.0, 0.0]]]),
                'userAnnotationGroups': [{
                    '_AnnotationGroup':
                    True,
                    'dimension':
                    1,
                    'identifierRanges':
                    '1',
                    'name':
                    get_esophagus_term('cervical part of esophagus')[0],
                    'ontId':
                    get_esophagus_term('cervical part of esophagus')[1]
                }, {
                    '_AnnotationGroup':
                    True,
                    'dimension':
                    1,
                    'identifierRanges':
                    '2-3',
                    'name':
                    get_esophagus_term('thoracic part of esophagus')[0],
                    'ontId':
                    get_esophagus_term('thoracic part of esophagus')[1]
                }, {
                    '_AnnotationGroup':
                    True,
                    'dimension':
                    1,
                    'identifierRanges':
                    '4',
                    'name':
                    get_esophagus_term('abdominal part of esophagus')[0],
                    'ontId':
                    get_esophagus_term('abdominal part of esophagus')[1]
                }]
            })
    }

    @staticmethod
    def getName():
        return '3D Esophagus 1'

    @staticmethod
    def getParameterSetNames():
        return ['Default', 'Human 1']

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        centralPathOption = cls.centralPathDefaultScaffoldPackages['Human 1']
        options = {
            'Central path': copy.deepcopy(centralPathOption),
            'Number of elements around': 8,
            'Number of elements along': 12,
            'Number of elements through wall': 4,
            'Wall thickness': 5.0,  # 3.0mm when relaxed
            'Mucosa relative thickness': 0.35,
            'Submucosa relative thickness': 0.15,
            'Circular muscle layer relative thickness': 0.25,
            'Longitudinal muscle layer relative thickness': 0.25,
            'Use cross derivatives': False,
            'Use linear through wall': True,
            'Refine': False,
            'Refine number of elements around': 1,
            'Refine number of elements along': 1,
            'Refine number of elements through wall': 1
        }
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path', 'Number of elements around',
            'Number of elements along', 'Number of elements through wall',
            'Wall thickness', 'Mucosa relative thickness',
            'Submucosa relative thickness',
            'Circular muscle layer relative thickness',
            'Longitudinal muscle layer relative thickness',
            'Use cross derivatives', 'Use linear through wall', 'Refine',
            'Refine number of elements around',
            'Refine number of elements along',
            'Refine number of elements through wall'
        ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [MeshType_1d_path1]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \
            cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls,
                                 optionName,
                                 scaffoldType,
                                 parameterSetName=None):
        """
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        """
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
                ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(
                    cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(
                cls.centralPathDefaultScaffoldPackages[parameterSetName])
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType(
        ) in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage(
                'Central path', MeshType_1d_path1)
        if options['Number of elements through wall'] != (1 or 4):
            options['Number of elements through wall'] = 4
        for key in [
                'Number of elements around', 'Number of elements along',
                'Number of elements through wall',
                'Refine number of elements around',
                'Refine number of elements along',
                'Refine number of elements through wall'
        ]:
            if options[key] < 1:
                options[key] = 1
        if options['Wall thickness'] < 0.0:
            options['Wall thickness'] = 0.0

    @classmethod
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: annotationGroups
        """
        centralPath = options['Central path']
        elementsCountAround = options['Number of elements around']
        elementsCountAlong = options['Number of elements along']
        elementsCountThroughWall = options['Number of elements through wall']
        wallThickness = options['Wall thickness']
        mucosaRelThickness = options['Mucosa relative thickness']
        submucosaRelThickness = options['Submucosa relative thickness']
        circularRelThickness = options[
            'Circular muscle layer relative thickness']
        longitudinalRelThickness = options[
            'Longitudinal muscle layer relative thickness']
        useCrossDerivatives = options['Use cross derivatives']
        useCubicHermiteThroughWall = not (options['Use linear through wall'])

        firstNodeIdentifier = 1
        firstElementIdentifier = 1

        # Central path
        esophagusTermsAlong = [
            None, 'cervical part of esophagus', 'thoracic part of esophagus',
            'abdominal part of esophagus'
        ]
        arcLengthOfGroupsAlong = []
        for i in range(len(esophagusTermsAlong)):
            tmpRegion = region.createRegion()
            centralPath.generate(tmpRegion)
            cxGroup, cd1Group, cd2Group, cd3Group, cd12Group, cd13Group = \
                extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                                                            Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3,
                                                            Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3],
                                                groupName=esophagusTermsAlong[i])
            arcLength = 0.0
            for e in range(len(cxGroup) - 1):
                arcLength += interp.getCubicHermiteArcLength(
                    cxGroup[e], cd1Group[e], cxGroup[e + 1], cd1Group[e + 1])
            arcLengthOfGroupsAlong.append(arcLength)

            if i == 0:
                cx = cxGroup
                cd1 = cd1Group
                cd2 = cd2Group
                cd3 = cd3Group
                cd12 = cd12Group
                cd13 = cd13Group

            del tmpRegion

        # Sample central path
        sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(
            cx, cd1, elementsCountAlong)
        sd2, sd12 = interp.interpolateSampleCubicHermite(
            cd2, cd12, se, sxi, ssf)
        sd3, sd13 = interp.interpolateSampleCubicHermite(
            cd3, cd13, se, sxi, ssf)

        centralPathLength = arcLengthOfGroupsAlong[0]
        elementAlongLength = centralPathLength / elementsCountAlong

        elementsCountAlongGroups = []
        groupLength = 0.0
        e = 0
        elementsCount = 1
        length = elementAlongLength
        for i in range(1, len(esophagusTermsAlong)):
            groupLength += arcLengthOfGroupsAlong[i]
            if e == elementsCountAlong - 2:
                elementsCount += 1
                elementsCountAlongGroups.append(elementsCount)
            else:
                while length < groupLength:
                    elementsCount += 1
                    e += 1
                    length += elementAlongLength

                # check which end is grouplength closer to
                distToUpperEnd = abs(length - groupLength)
                distToLowerEnd = abs(groupLength -
                                     (length - elementsCountAlong))
                if distToLowerEnd < distToUpperEnd:
                    elementsCount -= 1
                    elementsCountAlongGroups.append(elementsCount)
                    e -= 1
                    length -= elementAlongLength
                else:
                    elementsCountAlongGroups.append(elementsCount)
            elementsCount = 0

        majorRadiusElementList = sd2
        minorRadiusElementList = sd3

        # Create annotation groups along esophagus
        esophagusGroup = AnnotationGroup(region,
                                         get_esophagus_term("esophagus"))
        cervicalGroup = AnnotationGroup(
            region, get_esophagus_term("cervical part of esophagus"))
        thoracicGroup = AnnotationGroup(
            region, get_esophagus_term("thoracic part of esophagus"))
        abdominalGroup = AnnotationGroup(
            region, get_esophagus_term("abdominal part of esophagus"))

        annotationGroupAlong = [[esophagusGroup, cervicalGroup],
                                [esophagusGroup, thoracicGroup],
                                [esophagusGroup, abdominalGroup]]

        annotationGroupsAlong = []
        for i in range(len(elementsCountAlongGroups)):
            elementsCount = elementsCountAlongGroups[i]
            for n in range(elementsCount):
                annotationGroupsAlong.append(annotationGroupAlong[i])

        annotationGroupsAround = []
        for i in range(elementsCountAround):
            annotationGroupsAround.append([])

        # Groups through wall
        longitudinalMuscleGroup = AnnotationGroup(
            region,
            get_esophagus_term("esophagus smooth muscle longitudinal layer"))
        circularMuscleGroup = AnnotationGroup(
            region,
            get_esophagus_term("esophagus smooth muscle circular layer"))
        submucosaGroup = AnnotationGroup(
            region, get_esophagus_term("submucosa of esophagus"))
        mucosaGroup = AnnotationGroup(region,
                                      get_esophagus_term("esophagus mucosa"))

        if elementsCountThroughWall == 1:
            relativeThicknessList = [1.0]
            annotationGroupsThroughWall = [[]]
        else:
            relativeThicknessList = [
                mucosaRelThickness, submucosaRelThickness,
                circularRelThickness, longitudinalRelThickness
            ]
            annotationGroupsThroughWall = [[mucosaGroup], [submucosaGroup],
                                           [circularMuscleGroup],
                                           [longitudinalMuscleGroup]]

        xToSample = []
        d1ToSample = []
        for n2 in range(elementsCountAlong + 1):
            # Create inner points
            cx = [0.0, 0.0, elementAlongLength * n2]
            axis1 = [vector.magnitude(majorRadiusElementList[n2]), 0.0, 0.0]
            axis2 = [0.0, vector.magnitude(minorRadiusElementList[n2]), 0.0]
            xInner, d1Inner = geometry.createEllipsePoints(cx,
                                                           2 * math.pi,
                                                           axis1,
                                                           axis2,
                                                           elementsCountAround,
                                                           startRadians=0.0)
            xToSample += xInner
            d1ToSample += d1Inner

        d2ToSample = [[0.0, 0.0, elementAlongLength]
                      ] * (elementsCountAround * (elementsCountAlong + 1))

        # Sample along length
        xInnerRaw = []
        d2InnerRaw = []
        xToWarp = []
        d1ToWarp = []
        d2ToWarp = []
        flatWidthList = []
        xiList = []

        for n1 in range(elementsCountAround):
            xForSamplingAlong = []
            d2ForSamplingAlong = []
            for n2 in range(elementsCountAlong + 1):
                idx = n2 * elementsCountAround + n1
                xForSamplingAlong.append(xToSample[idx])
                d2ForSamplingAlong.append(d2ToSample[idx])
            xSampled, d2Sampled = interp.sampleCubicHermiteCurves(
                xForSamplingAlong,
                d2ForSamplingAlong,
                elementsCountAlong,
                arcLengthDerivatives=True)[0:2]
            xInnerRaw.append(xSampled)
            d2InnerRaw.append(d2Sampled)

        # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2
        for n2 in range(elementsCountAlong + 1):
            xAround = []
            d2Around = []

            for n1 in range(elementsCountAround):
                x = xInnerRaw[n1][n2]
                d2 = d2InnerRaw[n1][n2]
                xAround.append(x)
                d2Around.append(d2)

            d1Around = []
            for n1 in range(elementsCountAround):
                v1 = xAround[n1]
                v2 = xAround[(n1 + 1) % elementsCountAround]
                d1 = d2 = [v2[c] - v1[c] for c in range(3)]
                arcLengthAround = interp.computeCubicHermiteArcLength(
                    v1, d1, v2, d2, True)
                dx_ds1 = [c * arcLengthAround for c in vector.normalise(d1)]
                d1Around.append(dx_ds1)
            d1Smoothed = interp.smoothCubicHermiteDerivativesLoop(
                xAround, d1Around)

            xToWarp += xAround
            d1ToWarp += d1Smoothed
            d2ToWarp += d2Around

            # Flat width and xi
            flatWidth = 0.0
            xiFace = []
            for n1 in range(elementsCountAround):
                v1 = xAround[n1]
                d1 = d1Smoothed[n1]
                v2 = xAround[(n1 + 1) % elementsCountAround]
                d2 = d1Smoothed[(n1 + 1) % elementsCountAround]
                flatWidth += interp.getCubicHermiteArcLength(v1, d1, v2, d2)
            flatWidthList.append(flatWidth)

            for n1 in range(elementsCountAround + 1):
                xi = 1.0 / elementsCountAround * n1
                xiFace.append(xi)
            xiList.append(xiFace)

        # Project reference point for warping onto central path
        sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \
            tubemesh.getPlaneProjectionOnCentralPath(xToWarp, elementsCountAround, elementsCountAlong,
                                                     centralPathLength, sx, sd1, sd2, sd12)

        # Warp points
        segmentAxis = [0.0, 0.0, 1.0]
        closedProximalEnd = False

        innerRadiusAlong = []
        for n2 in range(elementsCountAlong + 1):
            firstNodeAlong = xToWarp[n2 * elementsCountAround]
            midptSegmentAxis = [0.0, 0.0, elementAlongLength * n2]
            radius = vector.magnitude(firstNodeAlong[c] - midptSegmentAxis[c]
                                      for c in range(3))
            innerRadiusAlong.append(radius)

        xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = \
            tubemesh.warpSegmentPoints(xToWarp, d1ToWarp, d2ToWarp, segmentAxis, sxRefList, sd1RefList,
                                       sd2ProjectedListRef, elementsCountAround, elementsCountAlong,
                                       zRefList, innerRadiusAlong, closedProximalEnd)

        # Create coordinates and derivatives
        transitElementList = [0] * elementsCountAround
        xList, d1List, d2List, d3List, curvatureList = \
            tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList,
                                             [wallThickness]*(elementsCountAlong+1), relativeThicknessList,
                                             elementsCountAround, elementsCountAlong, elementsCountThroughWall,
                                             transitElementList)

        # Create flat coordinates
        xFlat, d1Flat, d2Flat = tubemesh.createFlatCoordinates(
            xiList, flatWidthList, length, wallThickness,
            relativeThicknessList, elementsCountAround, elementsCountAlong,
            elementsCountThroughWall, transitElementList)

        # Create nodes and elements
        xOrgan = []
        d1Organ = []
        d2Organ = []
        nodeIdentifier, elementIdentifier, annotationGroups = \
            tubemesh.createNodesAndElements(region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat,
                                            xOrgan, d1Organ, d2Organ, None, elementsCountAround, elementsCountAlong,
                                            elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong,
                                            annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier,
                                            useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd)

        # annotation fiducial points
        fm = region.getFieldmodule()
        fm.beginChange()
        mesh = fm.findMeshByDimension(3)
        cache = fm.createFieldcache()

        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)

        markerNames = [
            "proximodorsal midpoint on serosa of upper esophageal sphincter",
            "proximoventral midpoint on serosa of upper esophageal sphincter",
            "distal point of lower esophageal sphincter serosa on the greater curvature of stomach",
            "distal point of lower esophageal sphincter serosa on the lesser curvature of stomach"
        ]

        totalElements = elementIdentifier
        radPerElementAround = math.pi * 2.0 / elementsCountAround
        elementAroundHalfPi = int(0.25 * elementsCountAround)
        xi1HalfPi = (math.pi * 0.5 - radPerElementAround *
                     elementAroundHalfPi) / radPerElementAround
        elementAroundPi = int(0.5 * elementsCountAround)
        xi1Pi = (math.pi -
                 radPerElementAround * elementAroundPi) / radPerElementAround

        markerElementIdentifiers = [
            elementsCountAround * elementsCountThroughWall -
            elementAroundHalfPi, elementAroundHalfPi + 1 +
            elementsCountAround * (elementsCountThroughWall - 1),
            totalElements - elementsCountAround,
            totalElements - elementsCountAround + elementAroundPi
        ]

        markerXis = [[1.0 - xi1HalfPi, 0.0, 1.0], [xi1HalfPi, 0.0, 1.0],
                     [0.0, 1.0, 1.0], [xi1Pi, 1.0, 1.0]]

        for n in range(len(markerNames)):
            markerGroup = findOrCreateAnnotationGroupForTerm(
                annotationGroups, region, get_esophagus_term(markerNames[n]))
            markerElement = mesh.findElementByIdentifier(
                markerElementIdentifiers[n])
            markerXi = markerXis[n]
            cache.setMeshLocation(markerElement, markerXi)
            markerPoint = markerPoints.createNode(nodeIdentifier,
                                                  markerTemplateInternal)
            nodeIdentifier += 1
            cache.setNode(markerPoint)
            markerName.assignString(cache, markerGroup.getName())
            markerLocation.assignMeshLocation(cache, markerElement, markerXi)
            for group in [esophagusGroup, markerGroup]:
                group.getNodesetGroup(nodes).addNode(markerPoint)

        fm.endChange()

        return annotationGroups

    @classmethod
    def refineMesh(cls, meshrefinement, options):
        """
        Refine source mesh into separate region, with change of basis.
        :param meshrefinement: MeshRefinement, which knows source and target region.
        :param options: Dict containing options. See getDefaultOptions().
        """
        refineElementsCountAround = options['Refine number of elements around']
        refineElementsCountAlong = options['Refine number of elements along']
        refineElementsCountThroughWall = options[
            'Refine number of elements through wall']

        meshrefinement.refineAllElementsCubeStandard3d(
            refineElementsCountAround, refineElementsCountAlong,
            refineElementsCountThroughWall)
        return

    @classmethod
    def defineFaceAnnotations(cls, region, options, annotationGroups):
        '''
        Add face annotation groups from the highest dimension mesh.
        Must have defined faces and added subelements for highest dimension groups.
        :param region: Zinc region containing model.
        :param options: Dict containing options. See getDefaultOptions().
        :param annotationGroups: List of annotation groups for top-level elements.
        New face annotation groups are appended to this list.
        '''
        # Create 2d surface mesh groups
        fm = region.getFieldmodule()
        mesh2d = fm.findMeshByDimension(2)

        esophagusGroup = getAnnotationGroupForTerm(
            annotationGroups, get_esophagus_term("esophagus"))
        is_exterior = fm.createFieldIsExterior()
        is_exterior_face_xi3_1 = fm.createFieldAnd(
            is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1))
        is_esophagus = esophagusGroup.getFieldElementGroup(mesh2d)
        is_serosa = fm.createFieldAnd(is_esophagus, is_exterior_face_xi3_1)
        serosa = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_esophagus_term("serosa of esophagus"))
        serosa.getMeshGroup(mesh2d).addElementsConditional(is_serosa)
Exemplo n.º 10
0
    def test_colon1(self):
        """
        Test creation of colon scaffold.
        """
        parameterSetNames = MeshType_3d_colon1.getParameterSetNames()
        self.assertEqual(parameterSetNames, ["Default", "Human 1", "Human 2", "Mouse 1", "Mouse 2", "Pig 1", "Pig 2"])
        centralPathDefaultScaffoldPackages = {
            'Test line': ScaffoldPackage(MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 1
                },
                'meshEdits': exnodeStringFromNodeValues(
                    [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2,
                     Node.VALUE_LABEL_D2_DS1DS2], [
                        [[163.7, -25.2, 12.2], [-21.7, 50.1, -18.1], [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                        [[117.2, 32.8, -2.6], [-64.3, 34.4, -3.9], [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]]])
            })
        }
        centralPathOption = centralPathDefaultScaffoldPackages['Test line']
        segmentProfileOption = ScaffoldPackage(MeshType_3d_colonsegment1, defaultParameterSetName='Human 1')
        options = {
            'Central path': copy.deepcopy(centralPathOption),
            'Segment profile': segmentProfileOption,
            'Number of segments': 3,
            'Start phase': 0.0,
            'Proximal length': 25.0,
            'Transverse length': 25.0,
            'Distal length': 25.0,
            'Proximal inner radius': 20.0,
            'Proximal tenia coli width': 8.0,
            'Proximal-transverse inner radius': 18.0,
            'Proximal-transverse tenia coli width': 6.0,
            'Transverse-distal inner radius': 16.0,
            'Transverse-distal tenia coli width': 5.0,
            'Distal inner radius': 15.0,
            'Distal tenia coli width': 5.0,
            'Refine': False,
            'Refine number of elements around': 1,
            'Refine number of elements along': 1,
            'Refine number of elements through wall': 1
        }
        self.assertEqual(19, len(options))
        centralPath = options['Central path']
        segmentProfile = options.get("Segment profile")
        segmentSettings = segmentProfile.getScaffoldSettings()
        self.assertEqual(8, segmentSettings.get("Number of elements around haustrum"))
        self.assertEqual(0.5, segmentSettings.get("Corner inner radius factor"))
        self.assertEqual(0.5, segmentSettings.get("Haustrum inner radius factor"))
        self.assertEqual(0.5, segmentSettings.get("Segment length end derivative factor"))
        self.assertEqual(3, segmentSettings.get("Number of tenia coli"))
        self.assertEqual(1.6, segmentSettings.get("Tenia coli thickness"))
        self.assertEqual(3, options.get("Number of segments"))
        self.assertEqual(0.0, options.get("Start phase"))
        self.assertEqual(25.0, options.get("Transverse length"))
        self.assertEqual(20.0, options.get("Proximal inner radius"))
        self.assertEqual(6.0, options.get("Proximal-transverse tenia coli width"))
        self.assertEqual(16.0, options.get("Transverse-distal inner radius"))
        self.assertEqual(5.0, options.get("Distal tenia coli width"))

        context = Context("Test")
        region = context.getDefaultRegion()
        self.assertTrue(region.isValid())

        tmpRegion = region.createRegion()
        centralPath.generate(tmpRegion)
        cx = extractPathParametersFromRegion(tmpRegion)[0]
        self.assertEqual(2, len(cx))
        assertAlmostEqualList(self, cx[0], [ 163.7, -25.2, 12.2 ], 1.0E-6)
        assertAlmostEqualList(self, cx[1], [ 117.2, 32.8, -2.6 ], 1.0E-6)
        del tmpRegion

        annotationGroups = MeshType_3d_colon1.generateBaseMesh(region, options)
        self.assertEqual(7, len(annotationGroups))

        fieldmodule = region.getFieldmodule()
        self.assertEqual(RESULT_OK, fieldmodule.defineAllFaces())
        if annotationGroups is not None:
            for annotationGroup in annotationGroups:
                annotationGroup.addSubelements()
        mesh3d = fieldmodule.findMeshByDimension(3)
        self.assertEqual(432, mesh3d.getSize())
        mesh2d = fieldmodule.findMeshByDimension(2)
        self.assertEqual(1656, mesh2d.getSize())
        mesh1d = fieldmodule.findMeshByDimension(1)
        self.assertEqual(2043, mesh1d.getSize())
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        self.assertEqual(819, nodes.getSize())
        datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS)
        self.assertEqual(0, datapoints.getSize())

        coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement()
        self.assertTrue(coordinates.isValid())
        minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes)
        assertAlmostEqualList(self, minimums, [ 108.0250647983898, -36.876103983560014,  -25.89741158325083 ], 1.0E-6)
        assertAlmostEqualList(self, maximums, [ 185.46457506220003, 48.101157490744, 34.995316052158934 ], 1.0E-6)

        flatCoordinates = fieldmodule.findFieldByName("flat coordinates").castFiniteElement()
        self.assertTrue(flatCoordinates.isValid())
        minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes)
        assertAlmostEqualList(self, minimums, [ 0.0, 0.0, 0.0 ], 1.0E-6)
        assertAlmostEqualList(self, maximums, [ 186.72988844629867, 77.4178187926561, 3.2000000000000006 ], 1.0E-6)

        textureCoordinates = fieldmodule.findFieldByName("texture coordinates").castFiniteElement()
        minimums, maximums = evaluateFieldNodesetRange(textureCoordinates, nodes)
        assertAlmostEqualList(self, minimums, [ 0.0, 0.0, 0.0 ], 1.0E-6)
        assertAlmostEqualList(self, maximums, [ 0.9812471574796385, 1.0, 2.0 ], 1.0E-6)

        with ChangeManager(fieldmodule):
            one = fieldmodule.createFieldConstant(1.0)
            faceMeshGroup = createFaceMeshGroupExteriorOnFace(fieldmodule, Element.FACE_TYPE_XI3_1)
            surfaceAreaField = fieldmodule.createFieldMeshIntegral(one, coordinates, faceMeshGroup)
            surfaceAreaField.setNumbersOfPoints(4)
            volumeField = fieldmodule.createFieldMeshIntegral(one, coordinates, mesh3d)
            volumeField.setNumbersOfPoints(3)
        fieldcache = fieldmodule.createFieldcache()
        result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1)
        self.assertEqual(result, RESULT_OK)
        self.assertAlmostEqual(surfaceArea, 14612.416788520026, delta=1.0E-6)
        result, volume = volumeField.evaluateReal(fieldcache, 1)
        self.assertEqual(result, RESULT_OK)
        self.assertAlmostEqual(volume, 26826.069540921028, delta=1.0E-6)
Exemplo n.º 11
0
    def test_transformation(self):
        """
        Test transformation of a box scaffold with scaffold package.
        """
        scaffoldPackage = ScaffoldPackage(MeshType_3d_box1)

        tmpScale = scaffoldPackage.getScale()
        TOL = 1.0E-7
        FINETOL = 1.0E-12
        assertAlmostEqualList(self, tmpScale, [ 1.0, 1.0, 1.0 ], delta=FINETOL)
        newScale = [ 2.0, 1.5, 0.5 ]
        scaffoldPackage.setScale(newScale)
        tmpScale = scaffoldPackage.getScale()
        assertAlmostEqualList(self, tmpScale, newScale, delta=FINETOL)

        tmpRotation = scaffoldPackage.getRotation()
        assertAlmostEqualList(self, tmpRotation, [ 0.0, 0.0, 0.0 ], delta=FINETOL)
        newRotation = [ 30.0, -10.0, 90.0 ]
        scaffoldPackage.setRotation(newRotation)
        tmpRotation = scaffoldPackage.getRotation()
        assertAlmostEqualList(self, tmpRotation, newRotation, delta=FINETOL)

        tmpTranslation = scaffoldPackage.getTranslation()
        assertAlmostEqualList(self, tmpTranslation, [ 0.0, 0.0, 0.0 ], delta=FINETOL)
        newTranslation = [ 0.5, 1.2, -0.1 ]
        scaffoldPackage.setTranslation(newTranslation)
        tmpTranslation = scaffoldPackage.getTranslation()
        assertAlmostEqualList(self, tmpTranslation, newTranslation, delta=FINETOL)

        context = Context("Test")
        region = context.getDefaultRegion()
        self.assertTrue(region.isValid())

        scaffoldPackage.generate(region)

        fieldmodule = region.getFieldmodule()
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        self.assertEqual(8, nodes.getSize())

        coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement()
        self.assertTrue(coordinates.isValid())
        minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes)
        assertAlmostEqualList(self, minimums, [  2.744244002293470e-01,  6.367511648575830e-01, -1.000000000000000e-01 ], delta=TOL)
        assertAlmostEqualList(self, maximums, [  2.455737063904887e+00,  2.184807753012208e+00,  1.724507984852172e+00 ], delta=TOL)

        node = nodes.findNodeByIdentifier(8)
        self.assertTrue(node.isValid())
        fieldcache = fieldmodule.createFieldcache()
        fieldcache.setNode(node)
        result, x = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, 3)
        self.assertEqual(RESULT_OK, result)
        assertAlmostEqualList(self, x , [  2.230161464134234e+00,  1.621558917869791e+00,  1.724507984852172e+00 ], delta=TOL)
        # derivative magnitudes must also equal scale
        result, d1 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, 3)
        self.assertEqual(RESULT_OK, result)
        assertAlmostEqualList(self, d1, [  1.705737064039425e+00,  9.848077530127952e-01,  3.472963553408093e-01 ], delta=TOL)
        self.assertAlmostEqual(newScale[0], magnitude(d1), delta=TOL)
        result, d2 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, 3)
        self.assertEqual(RESULT_OK, result)
        assertAlmostEqualList(self, d2, [ -2.255755995328457e-01, -1.302361332111701e-01,  1.477211629352659e+00 ], delta=TOL)
        self.assertAlmostEqual(newScale[1], magnitude(d2), delta=TOL)
        result, d3 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, 3)
        self.assertEqual(RESULT_OK, result)
        assertAlmostEqualList(self, d3, [  2.499999998128999e-01, -4.330127019169794e-01,  0.000000000000000e+00 ], delta=TOL)
        self.assertAlmostEqual(newScale[2], magnitude(d3), delta=TOL)
Exemplo n.º 12
0
    def test_bladder1(self):
        """
        Test creation of bladder scaffold.
        """
        parameterSetNames = MeshType_3d_bladder1.getParameterSetNames()
        self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Rat 1"])
        ostiumDefaultScaffoldPackages = {
            'Test ostium': ScaffoldPackage(MeshType_3d_ostium1, {
                'scaffoldSettings': {
                    'Number of vessels': 1,
                    'Number of elements across common': 2,
                    'Number of elements around ostium': 8,
                    'Number of elements along': 2,
                    'Number of elements through wall': 1,  # not implemented for > 1
                    'Unit scale': 1.0,
                    'Outlet': False,
                    'Ostium diameter': 0.3,
                    'Ostium length': 0.2,
                    'Ostium wall thickness': 0.05,
                    'Ostium inter-vessel distance': 0.8,
                    'Ostium inter-vessel height': 0.0,
                    'Use linear through ostium wall': True,
                    'Vessel end length factor': 1.0,
                    'Vessel inner diameter': 0.15,
                    'Vessel wall thickness': 0.04,
                    'Vessel angle 1 degrees': 0.0,
                    'Vessel angle 1 spread degrees': 0.0,
                    'Vessel angle 2 degrees': 0.0,
                    'Use linear through vessel wall': True,
                    # 'Use cross derivatives' : False,
                    'Refine': False,
                    'Refine number of elements around': 4,
                    'Refine number of elements along': 4,
                    'Refine number of elements through wall': 1
                },
            })
        }
        ostiumOption = ostiumDefaultScaffoldPackages['Test ostium']
        options = {
            'Number of elements up neck': 8,
            'Number of elements up body': 16,
            'Number of elements around': 8,  # should be even
            'Number of elements through wall': 1,
            'Number of elements around ostium': 8,  # implemented for 8
            'Number of elements radially on annulus': 1,
            'Height': 5.0,
            'Major diameter': 6.0,
            'Minor diameter': 6.0,
            'Bladder wall thickness': 0.05,
            'Urethra diameter': 1.0,
            'Ureter': copy.deepcopy(ostiumOption),
            'Ostium position around': 0.15,
            'Ostium position up': 0.25,
            'Use cross derivatives': False,
            'Refine': False,
            'Refine number of elements around': 4,
            'Refine number of elements up': 4,
            'Refine number of elements through wall': 1
        }
        self.assertEqual(19, len(options))
        ostiumSettings = ostiumOption.getScaffoldSettings()
        self.assertEqual(1, ostiumSettings.get("Number of vessels"))
        self.assertEqual(8, ostiumSettings.get("Number of elements around ostium"))
        self.assertEqual(1, ostiumSettings.get("Number of elements through wall"))
        self.assertEqual(0.3, ostiumSettings.get("Ostium diameter"))
        self.assertEqual(8, options.get("Number of elements up neck"))
        self.assertEqual(16, options.get("Number of elements up body"))
        self.assertEqual(8, options.get("Number of elements around"))
        self.assertEqual(1.0, options.get("Urethra diameter"))

        context = Context("Test")
        region = context.getDefaultRegion()
        self.assertTrue(region.isValid())

        annotationGroups = MeshType_3d_bladder1.generateBaseMesh(region, options)
        self.assertEqual(3, len(annotationGroups))
        fieldmodule = region.getFieldmodule()
        self.assertEqual(RESULT_OK, fieldmodule.defineAllFaces())
        if annotationGroups is not None:
            for annotationGroup in annotationGroups:
                annotationGroup.addSubelements()

        mesh3d = fieldmodule.findMeshByDimension(3)
        self.assertEqual(232, mesh3d.getSize())
        mesh2d = fieldmodule.findMeshByDimension(2)
        self.assertEqual(936, mesh2d.getSize())
        mesh1d = fieldmodule.findMeshByDimension(1)
        self.assertEqual(1183, mesh1d.getSize())
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        self.assertEqual(478, nodes.getSize())
        datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS)
        self.assertEqual(0, datapoints.getSize())

        coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement()
        self.assertTrue(coordinates.isValid())
        minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes)
        assertAlmostEqualList(self, minimums, [-2.996386368615517, -2.996386368615517, -6.464466094067262], 1.0E-6)
        assertAlmostEqualList(self, maximums, [2.996386368615517, 2.996386368615517, 5.0], 1.0E-6)
Exemplo n.º 13
0
    def test_smallintestine1(self):
        """
        Test creation of small intestine scaffold.
        """
        parameterSetNames = MeshType_3d_smallintestine1.getParameterSetNames()
        self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Mouse 1"])
        centralPathDefaultScaffoldPackages = {
            'Test line': ScaffoldPackage(MeshType_1d_path1, {
                'scaffoldSettings': {
                    'D2 derivatives': True,
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 3
                },
                'meshEdits': exnodeStringFromNodeValues(
                    [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2,
                     Node.VALUE_LABEL_D2_DS1DS2], [
                        [[-2.3, 18.5, -4.4], [-4.2, -0.8, 3.7], [0.0, 5.0, 0.0], [0.0, 0.0, 0.5]],
                        [[-8.6, 16.3, -0.4], [-7.1, -2.7, 1.6], [0.0, 5.0, 0.0], [0.0, 0.0, 0.5]],
                        [[-18.3, 12.6, -1.5], [-6.4, -1.7, -3.8], [0.0, 5.0, 0.0], [0.0, 0.0, 0.5]],
                        [[-15.6, 13.7, -6.1], [7.0, 2.1, -1.8], [0.0, 5.0, 0.0], [0.0, 0.0, 0.5]]])
            })
        }
        centralPathOption = centralPathDefaultScaffoldPackages['Test line']
        options = MeshType_3d_smallintestine1.getDefaultOptions("Mouse 1")
        options['Central path'] = copy.deepcopy(centralPathOption)
        options['Number of segments'] = 4
        options['Duodenum length'] = 5.0
        options['Jejunum length'] = 15.0
        options['Ileum length'] = 5.0
        self.assertEqual(19, len(options))
        centralPath = options['Central path']
        self.assertEqual(4, options.get("Number of segments"))
        self.assertEqual(8, options.get("Number of elements around"))
        self.assertEqual(4, options.get("Number of elements along segment"))
        self.assertEqual(1, options.get("Number of elements through wall"))
        self.assertEqual(5.0, options.get("Duodenum length"))
        self.assertEqual(5.0, options.get("Ileum length"))
        self.assertEqual(0.6, options.get("Duodenum inner radius"))
        self.assertEqual(1.0, options.get("Jejunum-ileum inner radius"))
        self.assertEqual(0.1, options.get("Wall thickness"))

        context = Context("Test")
        region = context.getDefaultRegion()
        self.assertTrue(region.isValid())

        tmpRegion = region.createRegion()
        centralPath.generate(tmpRegion)
        cx = extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE])[0]
        self.assertEqual(4, len(cx))
        assertAlmostEqualList(self, cx[0], [-2.3, 18.5, -4.4], 1.0E-6)
        assertAlmostEqualList(self, cx[2], [-18.3, 12.6, -1.5], 1.0E-6)
        del tmpRegion

        annotationGroups = MeshType_3d_smallintestine1.generateBaseMesh(region, options)
        self.assertEqual(4, len(annotationGroups))

        fieldmodule = region.getFieldmodule()
        self.assertEqual(RESULT_OK, fieldmodule.defineAllFaces())
        mesh3d = fieldmodule.findMeshByDimension(3)
        self.assertEqual(128, mesh3d.getSize())
        mesh2d = fieldmodule.findMeshByDimension(2)
        self.assertEqual(520, mesh2d.getSize())
        mesh1d = fieldmodule.findMeshByDimension(1)
        self.assertEqual(664, mesh1d.getSize())
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        self.assertEqual(272, nodes.getSize())
        datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS)
        self.assertEqual(0, datapoints.getSize())

        coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement()
        self.assertTrue(coordinates.isValid())
        minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes)
        assertAlmostEqualList(self, minimums, [-20.06978981419564, 11.406595205949705, -7.1653294859433965], 1.0E-6)
        assertAlmostEqualList(self, maximums, [-1.8300388314851923, 19.193885338090105, 0.9772071374844936], 1.0E-6)

        flatCoordinates = fieldmodule.findFieldByName("flat coordinates").castFiniteElement()
        self.assertTrue(flatCoordinates.isValid())
        minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes)
        assertAlmostEqualList(self, minimums, [-1.39038154442654, 0.0, 0.0], 1.0E-6)
        assertAlmostEqualList(self, maximums, [4.891237158967401, 25.293706698841913, 0.1], 1.0E-6)

        with ChangeManager(fieldmodule):
            one = fieldmodule.createFieldConstant(1.0)
            faceMeshGroup = createFaceMeshGroupExteriorOnFace(fieldmodule, Element.FACE_TYPE_XI3_1)
            surfaceAreaField = fieldmodule.createFieldMeshIntegral(one, coordinates, faceMeshGroup)
            surfaceAreaField.setNumbersOfPoints(4)
            volumeField = fieldmodule.createFieldMeshIntegral(one, coordinates, mesh3d)
            volumeField.setNumbersOfPoints(3)
            flatSurfaceAreaField = fieldmodule.createFieldMeshIntegral(one, flatCoordinates, faceMeshGroup)
            flatSurfaceAreaField.setNumbersOfPoints(4)

        fieldcache = fieldmodule.createFieldcache()
        result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1)
        self.assertEqual(result, RESULT_OK)
        self.assertAlmostEqual(surfaceArea, 171.27464080337143, delta=1.0E-6)
        result, volume = volumeField.evaluateReal(fieldcache, 1)
        self.assertEqual(result, RESULT_OK)
        self.assertAlmostEqual(volume, 16.35219225882822, delta=1.0E-6)
        result, flatSurfaceArea = flatSurfaceAreaField.evaluateReal(fieldcache, 1)
        self.assertEqual(result, RESULT_OK)
        self.assertAlmostEqual(flatSurfaceArea, 171.37026123844635, delta=1.0E-3)
class MeshType_3d_solidcylinder1(Scaffold_base):
    """
Generates a solid cylinder using a ShieldMesh of all cube elements,
with variable numbers of elements in major, minor, shell and axial directions.
    """
    centralPathDefaultScaffoldPackages = {
        'Cylinder 1': ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings': {
                'Coordinate dimensions': 3,
                'D2 derivatives': True,
                'D3 derivatives': True,
                'Length': 3.0,
                'Number of elements': 3
            },
            'meshEdits': exnodeStringFromNodeValues(
                [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                 Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3], [
                    [[0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                    [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                    [[0.0, 0.0, 2.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                    [[0.0, 0.0, 3.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]]
                ])
        })
    }

    @staticmethod
    def getName():
        return '3D Solid Cylinder 1'

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        centralPathOption = cls.centralPathDefaultScaffoldPackages['Cylinder 1']
        options = {
            'Central path': copy.deepcopy(centralPathOption),
            'Number of elements across major': 4,
            'Number of elements across minor': 4,
            'Number of elements across shell': 0,
            'Number of elements across transition': 1,
            'Number of elements along': 1,
            'Shell element thickness proportion': 1.0,
            'Lower half': False,
            'Use cross derivatives': False,
            'Refine': False,
            'Refine number of elements across major': 1,
            'Refine number of elements along': 1
        }
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path',
            'Number of elements across major',
            'Number of elements across minor',
            'Number of elements across shell',
            'Number of elements across transition',
            'Number of elements along',
            'Shell element thickness proportion',
            'Lower half',
            'Refine',
            'Refine number of elements across major',
            'Refine number of elements along'
        ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [MeshType_1d_path1]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \
            cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
                ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(cls.centralPathDefaultScaffoldPackages[parameterSetName])
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType() in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage('Central path', MeshType_1d_path1)
        dependentChanges = False

        if options['Number of elements across major'] < 4:
            options['Number of elements across major'] = 4
        if options['Number of elements across major'] % 2:
            options['Number of elements across major'] += 1

        if options['Number of elements across minor'] < 4:
            options['Number of elements across minor'] = 4
        if options['Number of elements across minor'] % 2:
            options['Number of elements across minor'] += 1
        if options['Number of elements along'] < 1:
            options['Number of elements along'] = 1
        if options['Number of elements across transition'] < 1:
            options['Number of elements across transition'] = 1
        Rcrit = min(options['Number of elements across major']-4, options['Number of elements across minor']-4)//2
        if options['Number of elements across shell'] + options['Number of elements across transition'] - 1 > Rcrit:
            dependentChanges = True
            options['Number of elements across shell'] = Rcrit
            options['Number of elements across transition'] = 1

        if options['Shell element thickness proportion'] < 0.15:
            options['Shell element thickness proportion'] = 1.0

        return dependentChanges

    @staticmethod
    def generateBaseMesh(region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """

        centralPath = options['Central path']
        full = not options['Lower half']
        elementsCountAcrossMajor = options['Number of elements across major']
        if not full:
            elementsCountAcrossMajor //= 2
        elementsCountAcrossMinor = options['Number of elements across minor']
        elementsCountAcrossShell = options['Number of elements across shell']
        elementsCountAcrossTransition = options['Number of elements across transition']
        elementsCountAlong = options['Number of elements along']
        shellProportion = options['Shell element thickness proportion']
        useCrossDerivatives = options['Use cross derivatives']

        fm = region.getFieldmodule()
        coordinates = findOrCreateFieldCoordinates(fm)

        cylinderCentralPath = CylinderCentralPath(region, centralPath, elementsCountAlong)

        cylinderShape = CylinderShape.CYLINDER_SHAPE_FULL if full else CylinderShape.CYLINDER_SHAPE_LOWER_HALF

        base = CylinderEnds(elementsCountAcrossMajor, elementsCountAcrossMinor, elementsCountAcrossShell,
                            elementsCountAcrossTransition,
                            shellProportion,
                            [0.0, 0.0, 0.0], cylinderCentralPath.alongAxis[0], cylinderCentralPath.majorAxis[0],
                            cylinderCentralPath.minorRadii[0])
        cylinder1 = CylinderMesh(fm, coordinates, elementsCountAlong, base,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath, useCrossDerivatives=False)

        annotationGroup = []
        return annotationGroup

    @classmethod
    def refineMesh(cls, meshRefinement, options):
        """
        Refine source mesh into separate region, with change of basis.
        :param meshRefinement: MeshRefinement, which knows source and target region.
        :param options: Dict containing options. See getDefaultOptions().
        """
        assert isinstance(meshRefinement, MeshRefinement)
        refineElementsCountAcrossMajor = options['Refine number of elements across major']
        refineElementsCountAlong = options['Refine number of elements along']
        meshRefinement.refineAllElementsCubeStandard3d(refineElementsCountAcrossMajor, refineElementsCountAlong, refineElementsCountAcrossMajor)
class MeshType_3d_smallintestine1(Scaffold_base):
    '''
    Generates a 3-D small intestine mesh with variable numbers
    of elements around, along the central line, and through wall.
    The small intestine is created by a function that generates
    a small intestine segment and uses tubemesh to map the segment
    along a central line profile.
    '''

    centralPathDefaultScaffoldPackages = {
        'Mouse 1' : ScaffoldPackage(MeshType_1d_path1, {
            'scaffoldSettings' : {
                'Coordinate dimensions' : 3,
                'Length' : 1.0,
                'Number of elements' : 45
                },
            'meshEdits' : exnodeStringFromNodeValues(
                [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2  ], [
                [ [  -2.3, 18.5,  -4.4 ], [ -4.2, -0.8,   3.7 ], [  0.0,  5.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -8.6, 16.3,  -0.4 ], [ -7.1, -2.7,   1.6 ], [  0.0,  5.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -18.3, 12.6,  -1.5 ], [ -6.4, -1.7,  -3.8 ], [  0.0,  5.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -15.6, 13.7,  -6.1 ], [  7.0,  2.1,  -1.8 ], [  0.0,  5.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -9.3, 14.8,  -4.9 ], [  4.7,  0.7,   1.8 ], [  0.0,  5.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -3.9, 15.7,  -3.0 ], [  4.3,  0.7,   2.0 ], [  0.0,  5.0,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -3.4, 13.4,  -2.8 ], [ -4.1, -0.7,  -1.7 ], [  0.6, -2.0,  0.3 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -7.6, 12.4,  -4.6 ], [ -3.7, -0.8,  -0.9 ], [  0.0, -2.1,  0.1 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -11.6, 11.6,  -5.7 ], [ -4.2, -0.7,  -0.2 ], [  0.0, -1.9,  0.1 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -16.5, 11.7,  -3.9 ], [ -1.0,  0.2,   5.8 ], [  0.3, -1.4, -0.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -12.5, 11.7,  -1.4 ], [  3.6,  0.1,   0.6 ], [ -0.1, -1.4, -0.7 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -6.8, 11.8,  -0.6 ], [  2.9,  0.0,   0.7 ], [ -0.7, -1.2, -0.9 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -6.4,  9.8,  -1.6 ], [ -2.9, -0.3,  -1.4 ], [ -0.9,  1.6,  0.4 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -9.5,  9.5,  -2.9 ], [ -4.6,  0.0,  -1.8 ], [ -0.5,  1.7,  0.7 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -14.3,  9.4,  -4.6 ], [ -3.4,  0.1,  -1.6 ], [ -0.1,  1.6,  0.5 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -19.0,  9.4,  -2.9 ], [  0.3,  0.2,   6.7 ], [  0.0,  1.8,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -14.5,  9.7,   0.2 ], [  3.6, -1.2,   1.0 ], [  0.7,  2.2,  0.9 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -12.6,  7.7,   0.7 ], [  0.6, -2.7,   0.2 ], [  1.7,  0.8,  1.6 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -13.1,  3.8,   0.3 ], [ -4.0, -3.6,  -1.5 ], [  1.0, -1.1,  1.5 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -15.2,  5.1,  -0.8 ], [  6.0,  6.9,   1.8 ], [ -0.9, -0.4,  0.3 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -17.3,  6.9,  -1.0 ], [ -2.5,  0.0,  -0.4 ], [ -1.6,  0.0,  1.7 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -19.9,  6.8,  -2.5 ], [ -1.5, -1.1,  -3.4 ], [ -1.8, -0.7,  0.4 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -17.2,  6.3,  -5.1 ], [  4.0,  0.8,  -1.3 ], [ -0.4, -1.3, -1.5 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -12.2,  7.8,  -6.8 ], [  4.8,  1.7,  -0.3 ], [  0.1, -0.6, -1.6 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -7.9,  9.6,  -6.5 ], [  3.7,  1.7,   0.7 ], [  0.5, -0.6, -1.6 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -3.8, 10.3,  -5.5 ], [  3.8, -2.7,  -0.1 ], [ -1.3, -0.3, -3.1 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -5.3,  7.6,  -6.4 ], [ -3.5, -1.0,  -1.3 ], [ -0.4,  1.1, -1.5 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -9.0,  6.4,  -7.3 ], [ -3.2, -1.3,   1.9 ], [ -0.9,  0.5, -1.1 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -11.6,  4.0,  -2.0 ], [  5.6, -0.2,   4.3 ], [ -1.8, -0.3,  1.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -5.9,  5.0,  -3.1 ], [  4.1,  1.2,  -1.6 ], [  1.0, -0.3,  1.9 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -2.5,  6.0,  -3.8 ], [  3.6,  0.7,   3.2 ], [ -1.7, -1.2,  1.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -4.1,  3.2,  -0.4 ], [ -3.5, -1.7,   2.6 ], [ -1.0,  0.4, -1.1 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -9.7,  1.7,   2.3 ], [ -7.9, -1.0,   1.0 ], [ -0.6,  1.0, -1.6 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -19.0,  0.6,  -0.4 ], [  0.2,  3.7,  -6.8 ], [  2.6,  0.8,  0.0 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -13.9,  2.3,  -5.8 ], [  4.4,  0.6,  -1.1 ], [  0.5, -0.1,  2.4 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -7.7,  1.2,  -4.6 ], [  3.9, -3.4,   1.5 ], [ -0.5,  0.0,  2.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -4.8, -4.0,  -1.3 ], [ -4.2, -3.3,   3.1 ], [ -1.5,  2.5,  2.3 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -10.9, -6.1,  -0.6 ], [ -5.3, -1.2,  -0.9 ], [ -0.5,  1.9,  1.3 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -19.9, -6.4,  -5.5 ], [ -0.3,  1.7, -10.5 ], [ -0.9,  2.0,  0.7 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -10.7, -3.2,  -8.8 ], [  7.8,  0.4,   0.1 ], [ -0.2,  2.1,  0.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -1.2, -1.9,  -7.3 ], [  0.8,  8.1,   2.5 ], [ -3.8,  0.1, -0.6 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -6.3,  0.5,  -8.1 ], [ -9.8, -1.2,   0.5 ], [  0.2, -1.9, -0.5 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -16.0, -0.7,  -7.4 ], [ -7.6,  1.2,   1.5 ], [ -0.1, -2.0, -0.9 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -20.5,  2.3,  -6.1 ], [  3.5,  7.2,  -2.9 ], [ -2.3,  0.2, -0.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [ -11.4,  2.6, -10.1 ], [ 10.4,  1.5,  -0.2 ], [ -0.2,  1.3, -2.2 ], [ 0.0, 0.0, 0.5 ] ],
                [ [  -3.8,  4.2,  -7.3 ], [  3.5,  0.9,   2.7 ], [  0.2,  1.0, -2.6 ], [ 0.0, 0.0, 0.5 ] ] ] )
            } )
        }

    @staticmethod
    def getName():
        return '3D Small Intestine 1'

    @staticmethod
    def getParameterSetNames():
        return [
            'Default',
            'Mouse 1']

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        centralPathOption = cls.centralPathDefaultScaffoldPackages['Mouse 1']
        options = {
            'Central path': copy.deepcopy(centralPathOption),
            'Number of segments': 100,
            'Number of elements around': 8,
            'Number of elements along segment': 4,
            'Number of elements through wall': 1,
            'Duodenum length': 25.0,
            'Jejunum length': 240.0,
            'Ileum length': 10.0,
            'Duodenum inner radius': 0.6,
            'Duodenum-jejunum inner radius': 1.0,
            'Jejunum-ileum inner radius': 1.0,
            'Ileum inner radius': 1.0,
            'Wall thickness': 0.1,
            'Use cross derivatives': False,
            'Use linear through wall': True,
            'Refine': False,
            'Refine number of elements around': 1,
            'Refine number of elements along': 1,
            'Refine number of elements through wall': 1
            }
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path',
            'Number of segments',
            'Number of elements around',
            'Number of elements along segment',
            'Number of elements through wall',
            'Duodenum length',
            'Jejunum length',
            'Ileum length',
            'Duodenum inner radius',
            'Duodenum-jejunum inner radius',
            'Jejunum-ileum inner radius',
            'Ileum inner radius',
            'Wall thickness',
            'Use cross derivatives',
            'Use linear through wall',
            'Refine',
            'Refine number of elements around',
            'Refine number of elements along',
            'Refine number of elements through wall' ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [ MeshType_1d_path1 ]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \
            cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
                ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(cls.centralPathDefaultScaffoldPackages[parameterSetName])
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType() in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage('Central path', MeshType_1d_path1)
        for key in [
            'Number of segments',
            'Number of elements around',
            'Number of elements along segment',
            'Number of elements through wall',
            'Refine number of elements around',
            'Refine number of elements along',
            'Refine number of elements through wall']:
            if options[key] < 1:
                options[key] = 1
        for key in [
            'Duodenum length',
            'Jejunum length',
            'Ileum length',
            'Duodenum inner radius',
            'Duodenum-jejunum inner radius',
            'Jejunum-ileum inner radius',
            'Ileum inner radius',
            'Wall thickness']:
            if options[key] < 0.0:
                options[key] = 0.0

    @classmethod
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: annotationGroups
        """
        centralPath = options['Central path']
        segmentCount = options['Number of segments']
        elementsCountAround = options['Number of elements around']
        elementsCountAlongSegment = options['Number of elements along segment']
        elementsCountThroughWall = options['Number of elements through wall']
        duodenumLength = options['Duodenum length']
        jejunumLength = options['Jejunum length']
        duodenumInnerRadius = options['Duodenum inner radius']
        duodenumJejunumInnerRadius = options['Duodenum-jejunum inner radius']
        jejunumIleumInnerRadius = options['Jejunum-ileum inner radius']
        ileumInnerRadius = options['Ileum inner radius']
        wallThickness = options['Wall thickness']
        useCrossDerivatives = options['Use cross derivatives']
        useCubicHermiteThroughWall = not(options['Use linear through wall'])
        elementsCountAlong = int(elementsCountAlongSegment*segmentCount)
        startPhase = 0.0

        firstNodeIdentifier = 1
        firstElementIdentifier = 1

        # Central path
        tmpRegion = region.createRegion()
        centralPath.generate(tmpRegion)
        cx, cd1, cd2, cd12 = extractPathParametersFromRegion(tmpRegion)
        # for i in range(len(cx)):
        #     print(i, '[', cx[i], ',', cd1[i], ',', cd2[i],',', cd12[i], '],')
        del tmpRegion

        # find arclength of colon
        length = 0.0
        elementsCountIn = len(cx) - 1
        sd1 = interp.smoothCubicHermiteDerivativesLine(cx, cd1, fixAllDirections = True,
            magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN)
        for e in range(elementsCountIn):
            arcLength = interp.getCubicHermiteArcLength(cx[e], sd1[e], cx[e + 1], sd1[e + 1])
            # print(e+1, arcLength)
            length += arcLength
        segmentLength = length / segmentCount
        elementAlongLength = length / elementsCountAlong
        # print('Length = ', length)

        # Sample central path
        sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(cx, cd1, elementsCountAlongSegment*segmentCount)
        sd2, sd12 = interp.interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf)

        # Generate variation of radius & tc width along length
        lengthList = [0.0, duodenumLength, duodenumLength + jejunumLength, length]
        innerRadiusList = [duodenumInnerRadius, duodenumJejunumInnerRadius, jejunumIleumInnerRadius, ileumInnerRadius]
        innerRadiusSegmentList, dInnerRadiusSegmentList = interp.sampleParameterAlongLine(lengthList, innerRadiusList,
                                                                                          segmentCount)

        # Create annotation groups for small intestine sections
        elementsAlongDuodenum = round(duodenumLength / elementAlongLength)
        elementsAlongJejunum = round(jejunumLength / elementAlongLength)
        elementsAlongIleum = elementsCountAlong - elementsAlongDuodenum - elementsAlongJejunum
        elementsCountAlongGroups = [elementsAlongDuodenum, elementsAlongJejunum, elementsAlongIleum]

        smallintestineGroup = AnnotationGroup(region, get_smallintestine_term("small intestine"))
        duodenumGroup = AnnotationGroup(region, get_smallintestine_term("duodenum"))
        jejunumGroup = AnnotationGroup(region, get_smallintestine_term("jejunum"))
        ileumGroup = AnnotationGroup(region, get_smallintestine_term("ileum"))

        annotationGroupAlong = [[smallintestineGroup, duodenumGroup],
                                [smallintestineGroup, jejunumGroup],
                                [smallintestineGroup, ileumGroup]]

        annotationGroupsAlong = []
        for i in range(len(elementsCountAlongGroups)):
            elementsCount = elementsCountAlongGroups[i]
            for n in range(elementsCount):
                annotationGroupsAlong.append(annotationGroupAlong[i])

        annotationGroupsAround = []
        for i in range(elementsCountAround):
            annotationGroupsAround.append([ ])

        annotationGroupsThroughWall = []
        for i in range(elementsCountThroughWall):
            annotationGroupsThroughWall.append([ ])

        xExtrude = []
        d1Extrude = []
        d2Extrude = []
        d3UnitExtrude = []

        # Create object
        smallIntestineSegmentTubeMeshInnerPoints = CylindricalSegmentTubeMeshInnerPoints(
            elementsCountAround, elementsCountAlongSegment, segmentLength,
            wallThickness, innerRadiusSegmentList, dInnerRadiusSegmentList, startPhase)

        for nSegment in range(segmentCount):
            # Create inner points
            xInner, d1Inner, d2Inner, transitElementList, segmentAxis, radiusAlongSegmentList = \
               smallIntestineSegmentTubeMeshInnerPoints.getCylindricalSegmentTubeMeshInnerPoints(nSegment)

            # Project reference point for warping onto central path
            start = nSegment*elementsCountAlongSegment
            end = (nSegment + 1)*elementsCountAlongSegment + 1
            sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \
                tubemesh.getPlaneProjectionOnCentralPath(xInner, elementsCountAround, elementsCountAlongSegment,
                                                         segmentLength, sx[start:end], sd1[start:end], sd2[start:end],
                                                         sd12[start:end])

            # Warp segment points
            xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = tubemesh.warpSegmentPoints(
                xInner, d1Inner, d2Inner, segmentAxis, sxRefList, sd1RefList, sd2ProjectedListRef,
                elementsCountAround, elementsCountAlongSegment, zRefList, radiusAlongSegmentList,
                closedProximalEnd=False)

            # Store points along length
            xExtrude = xExtrude + (xWarpedList if nSegment == 0 else xWarpedList[elementsCountAround:])
            d1Extrude = d1Extrude + (d1WarpedList if nSegment == 0 else d1WarpedList[elementsCountAround:])

            # Smooth d2 for nodes between segments and recalculate d3
            if nSegment == 0:
                d2Extrude = d2Extrude + (d2WarpedList[:-elementsCountAround])
                d3UnitExtrude = d3UnitExtrude + (d3WarpedUnitList[:-elementsCountAround])
            else:
                xSecondFace = xWarpedList[elementsCountAround:elementsCountAround*2]
                d2SecondFace = d2WarpedList[elementsCountAround:elementsCountAround*2]
                for n1 in range(elementsCountAround):
                    nx = [xLastTwoFaces[n1], xLastTwoFaces[n1 + elementsCountAround], xSecondFace[n1]]
                    nd2 = [d2LastTwoFaces[n1], d2LastTwoFaces[n1 + elementsCountAround], d2SecondFace[n1]]
                    d2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative = True,
                                                                  fixEndDerivative = True)[1]
                    d2Extrude.append(d2)
                    d3Unit = vector.normalise(vector.crossproduct3(vector.normalise(d1LastTwoFaces[n1 + elementsCountAround]),
                                                                   vector.normalise(d2)))
                    d3UnitExtrude.append(d3Unit)
                d2Extrude = d2Extrude + \
                            (d2WarpedList[elementsCountAround:-elementsCountAround] if nSegment < segmentCount - 1 else
                             d2WarpedList[elementsCountAround:])
                d3UnitExtrude = d3UnitExtrude + \
                                (d3WarpedUnitList[elementsCountAround:-elementsCountAround] if nSegment < segmentCount - 1 else
                                 d3WarpedUnitList[elementsCountAround:])
            xLastTwoFaces = xWarpedList[-elementsCountAround*2:]
            d1LastTwoFaces = d1WarpedList[-elementsCountAround*2:]
            d2LastTwoFaces = d2WarpedList[-elementsCountAround*2:]

        # Create coordinates and derivatives
        xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(xExtrude, d1Extrude,
            d2Extrude, d3UnitExtrude, [wallThickness]*(elementsCountAlong+1),
            elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList)

        flatWidthList, xiList = smallIntestineSegmentTubeMeshInnerPoints.getFlatWidthAndXiList()

        # Create flat and texture coordinates
        xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = tubemesh.createFlatAndTextureCoordinates(
            xiList, flatWidthList, length, wallThickness, elementsCountAround,
            elementsCountAlong, elementsCountThroughWall, transitElementList)

        # Create nodes and elements
        nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements(
            region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture,
            elementsCountAround, elementsCountAlong, elementsCountThroughWall,
            annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall,
            firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives,
            closedProximalEnd=False)

        return annotationGroups

    @classmethod
    def refineMesh(cls, meshrefinement, options):
        """
        Refine source mesh into separate region, with change of basis.
        :param meshrefinement: MeshRefinement, which knows source and target region.
        :param options: Dict containing options. See getDefaultOptions().
        """
        refineElementsCountAround = options['Refine number of elements around']
        refineElementsCountAlong = options['Refine number of elements along']
        refineElementsCountThroughWall = options['Refine number of elements through wall']

        meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong,
                                                       refineElementsCountThroughWall)
        return
Exemplo n.º 16
0
class MeshType_3d_colon1(Scaffold_base):
    '''
    Generates a 3-D colon mesh with variable numbers
    of elements around, along the central line, and through wall.
    The colon is created by a function that generates a colon
    segment and uses tubemesh to map the segment along a central
    line profile.
    '''

    centralPathDefaultScaffoldPackages = {
        'Human 1':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 8
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2
                ], [[[0.0, 0.0, 0.0], [-50.7, 178.2, 0.0],
                     [-24.0, -6.0, -12.0], [-14.0, -1.0, -12.0]],
                    [[-47.4, 188.6, 0.0], [-19.3, 177.1, 0.0],
                     [-22.0, -4.0, -8.0], [-4.0, 19.0, 22.0]],
                    [[-4.4, 396.5, 0.0], [206.0, 40.1, 0.0],
                     [-10.0, 20.0, 8.0], [-6.0, 0.0, 51.0]],
                    [[130.0, 384.1, 0.0], [130.8, -40.5, 0.0],
                     [-5.0, 4.0, 29.0], [0.0, 1.0, 24.0]],
                    [[279.4, 383.0, 0.0], [118.0, 48.7, 0.0],
                     [-2.0, 10.0, 22.0], [5.0, 25.0, -20.0]],
                    [[443.9, 390.8, 0.0], [111.3, -97.0, 0.0],
                     [10.0, 17.0, 6.0], [1.0, -6.0, -35.0]],
                    [[475.2, 168.0, 0.0], [-0.8, -112.4, 0.0],
                     [20.0, 0.0, -20.0], [15.0, -1.0, -10.0]],
                    [[432.6, -32.3, 0.0], [-90.5, -59.0, 0.0],
                     [6.0, -9.0, -14.0], [8.0, -11.0, -13.0]],
                    [[272.4, 7.5, 0.0], [-79.0, 47.4, 0.0],
                     [1.0, -11.0, -18.0], [4.0, -12.0, -12.0]]])
            }),
        'Human 2':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 8
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2
                ], [[[0.0, 0.0, 0.0], [-34.7, 104.1, -18.1],
                     [-24.0, -6.0, -12.0], [-14.0, -1.0, -12.0]],
                    [[-34.5, 114.0, -18.1], [1.2, 86.6, -3.4],
                     [-22.0, -4.0, -8.0], [-4.0, 19.0, 22.0]],
                    [[-19.1, 218.5, 5.5], [78.7, -7.1, 94.5],
                     [-10.0, 20.0, 8.0], [-6.0, 0.0, 51.0]],
                    [[82.5, 189.1, 94.2], [84.5, 7.1, 71.6], [-5.0, 4.0, 29.0],
                     [0.0, 1.0, 24.0]],
                    [[226.6, 218.7, 85.7], [95.0, 91.3, -58.5],
                     [-2.0, 10.0, 22.0], [5.0, 25.0, -20.0]],
                    [[325.5, 381.7, -57.9], [229.2, -66.7, -20.4],
                     [10.0, 17.0, 6.0], [1.0, -6.0, -35.0]],
                    [[354.0, 105.3, -24.4], [-6.3, -143.7, 20.3],
                     [20.0, 0.0, -20.0], [15.0, -1.0, -10.0]],
                    [[296.5, -121.2, -0.6], [-90.5, -59.0, 0.0],
                     [6.0, -9.0, -14.0], [8.0, -11.0, -13.0]],
                    [[169.8, -73.4, -33.5], [-72.2, 43.4, -27.4],
                     [1.0, -11.0, -18.0], [4.0, -12.0, -12.0]]])
            }),
        'Mouse 1':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 7
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2
                ], [[[0.0, 0.0, 0.0], [6.0, 12.0, -2.0], [2.0, 1.0, 2.0],
                     [6.0, 0.0, 3.0]],
                    [[-2.0, 11.0, -3.0], [-8.0, 4.0, 9.0], [2.0, 2.0, 1.0],
                     [0.0, 1.0, 2.0]],
                    [[-3.0, 2.0, 3.0], [-4.0, -8.0, 0.0], [2.0, -1.0, 2.0],
                     [1.0, 0.0, 2.0]],
                    [[-11.0, -3.0, -4.0], [-8.0, -3.0, -7.0], [1.0, -2.0, 1.0],
                     [0.0, 0.0, 0.5]],
                    [[-16.0, -4.0, 0.0], [4.0, -3.0, 14.0], [1.0, -3.0, 0.0],
                     [0.0, 0.0, 0.5]],
                    [[-7.0, -8.0, 0.0], [5.0, -1.0, -14.0], [0.0, -3.0, 0.0],
                     [0.0, 0.0, 0.5]],
                    [[-1.0, -6.0, -1.0], [2.0, -2.0, 9.0], [1.0, -3.0, -1.0],
                     [0.0, 0.0, 0.5]],
                    [[-2.0, -14.0, 5.0], [-2.0, -4.0, 2.0], [1.0, -2.0, -2.0],
                     [0.0, 0.0, 0.5]]])
            }),
        'Mouse 2':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 4
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2
                ], [[[0.0, 0.0, 0.0], [0.0, 0.0, 13.0], [0.0, -10.0, 0.0],
                     [0.0, 0.0, 0.5]],
                    [[0.0, 0.0, 13.0], [0.0, 2.0, 28.0], [0.0, -10.0, 0.0],
                     [0.0, 0.0, 0.5]],
                    [[-14.0, -2.0, 13.0], [0.0, -3.0, -19.0],
                     [0.0, -10.0, 0.0], [0.0, 0.0, 0.5]],
                    [[-14.0, -1.0, -10.0], [1.0, 1.0, -17.0],
                     [0.0, -10.0, 0.0], [0.0, 0.0, 0.5]],
                    [[-14.0, 0.0, -28.0], [0.0, 0.0, -11.0], [0.0, -10.0, 0.0],
                     [0.0, 0.0, 0.5]]])
            }),
        'Pig 1':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 1.0,
                    'Number of elements': 36
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2
                ], [[[163.7, -25.2, 12.2], [-21.7, 50.1, -18.1],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[117.2, 32.8, -2.6], [-64.3, 34.4, -3.9], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[38.8, 68.0, 2.6], [-83.9, 24.9, 14.7], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-42.8, 65.5, 15.5], [-69.9, -46.7, 10.3],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-93.1, -11.4, 30.6], [5.7, -104.4, 9.7], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-51.1, -83.7, 32.0], [69.7, -44.1, 1.0], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[43.0, -88.5, 28.5], [71.0, 41.0, -1.0], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[94.9, -5.8, 33.7], [-14.3, 60.1, 0.7], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[56.6, 72.9, 45.0], [-108.7, 54.1, 22.4], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-37.0, 64.6, 59.3], [-72.5, -33.9, 6.2], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-94.1, -15.6, 78.2], [8.5, -82.1, 13.7], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-46.1, -90.1, 82.9], [76.6, -39.9, 11.9],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[45.8, -87.7, 92.3], [99.1, 55.4, 12.9], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[110.7, 2.2, 102.2], [0.4, 61.7, 12.7], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[73.9, 58.3, 110.7], [-86.3, 19.7, 9.2], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-21.8, 25.4, 118.0], [-78.1, -83.9, 23.6],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-41.7, -49.8, 134.9], [10.8, -41.9, 18.8],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[4.0, -46.5, 133.5], [8.3, 43.8, -7.7], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[12.0, 2.6, 130.0], [27.8, 34.6, 1.5], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[67.4, 25.7, 125.9], [66.6, -38.7, -10.1],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[75.1, -21.4, 122.5], [-17.5, -31.0, 4.2],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[42.7, -66.4, 124.2], [-47.7, -41.1, -2.1],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-2.1, -88.6, 122.8], [-29.6, -5.3, -1.2],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-52.1, -78.6, 117.3], [-42.2, 33.8, -6.8],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-72.2, -6.5, 104.4], [16.8, 80.4, -10.7],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-28.6, 45.7, 90.9], [47.4, 37.5, -9.5], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[67.1, 63.6, 78.6], [77.1, -44.5, -5.2], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[89.1, -11.8, 67.7], [0.0, -73.3, -5.2], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[40.5, -79.2, 62.3], [-63.5, -36.7, -5.2],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-36.4, -80.2, 59.6], [-60.1, 26.4, -8.2],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-69.2, -19.3, 47.0], [0.0, 73.3, -5.2], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[-22.4, 39.1, 34.8], [63.5, 36.7, -5.2], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[40.6, 46.0, 32.9], [25.4, -23.1, -7.5], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[66.2, 11.0, 24.6], [13.7, -51.4, -1.8], [0.0, 0.0, 5.0],
                     [0.0, 0.0, 0.5]],
                    [[32.0, -49.3, 24.4], [-51.1, -50.1, -1.2],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-51.1, -45.3, 15.7], [-38.0, 52.2, -16.2],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]],
                    [[-122.9, 124.2, -36.0], [-21.3, 64.5, -18.3],
                     [0.0, 0.0, 5.0], [0.0, 0.0, 0.5]]])
            }),
        'Pig 2':
        ScaffoldPackage(
            MeshType_1d_path1, {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'Length': 90.0,
                    'Number of elements': 3
                },
                'meshEdits':
                exnodeStringFromNodeValues([
                    Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                    Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2
                ], [[[0.0, 0.0, 0.0], [30.0, 0.0, 0.0], [0.0, 1.0, 0.0],
                     [0.0, 0.0, 0.0]],
                    [[30.0, 0.0, 0.0], [30.0, 0.0, 0.0], [0.0, 1.0, 0.0],
                     [0.0, 0.0, 0.0]],
                    [[60.0, 0.0, 0.0], [30.0, 0.0, 0.0], [0.0, 1.0, 0.0],
                     [0.0, 0.0, 0.0]],
                    [[90.0, 0.0, 0.0], [30.0, 0.0, 0.0], [0.0, 1.0, 0.0],
                     [0.0, 0.0, 0.0]]])
            }),
    }

    @staticmethod
    def getName():
        return '3D Colon 1'

    @staticmethod
    def getParameterSetNames():
        return [
            'Default', 'Human 1', 'Human 2', 'Mouse 1', 'Mouse 2', 'Pig 1',
            'Pig 2'
        ]

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        if 'Human 2' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Human 2']
        elif 'Mouse 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Mouse 1']
        elif 'Mouse 2' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Mouse 2']
        elif 'Pig 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 1']
        elif 'Pig 2' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 2']
        else:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Human 1']
        if 'Mouse' in parameterSetName:
            segmentProfileOption = ScaffoldPackage(
                MeshType_3d_colonsegment1, defaultParameterSetName='Mouse 1')
        elif 'Pig' in parameterSetName:
            segmentProfileOption = ScaffoldPackage(
                MeshType_3d_colonsegment1, defaultParameterSetName='Pig 1')
        else:
            segmentProfileOption = ScaffoldPackage(
                MeshType_3d_colonsegment1, defaultParameterSetName='Human 1')
        options = {
            'Central path': copy.deepcopy(centralPathOption),
            'Segment profile': segmentProfileOption,
            'Number of segments': 30,
            'Start phase': 0.0,
            'Proximal length': 420.0,
            'Transverse length': 460.0,
            'Distal length': 620.0,
            'Proximal inner radius': 43.5,
            'Proximal tenia coli width': 10.0,
            'Proximal-transverse inner radius': 33.0,
            'Proximal-transverse tenia coli width': 10.0,
            'Transverse-distal inner radius': 29.0,
            'Transverse-distal tenia coli width': 10.0,
            'Distal inner radius': 31.5,
            'Distal tenia coli width': 10.0,
            'Refine': False,
            'Refine number of elements around': 1,
            'Refine number of elements along': 1,
            'Refine number of elements through wall': 1
        }
        if 'Human 2' in parameterSetName:
            options['Proximal length'] = 180.0
            options['Transverse length'] = 620.0
            options['Distal length'] = 700.0
        elif 'Mouse' in parameterSetName:
            options['Number of segments'] = 10
            options['Proximal length'] = 30.0
            options['Transverse length'] = 20.0
            options['Distal length'] = 25.0
            options['Proximal inner radius'] = 1.0
            options['Proximal tenia coli width'] = 0.5
            options['Proximal-transverse inner radius'] = 0.9
            options['Proximal-transverse tenia coli width'] = 0.7
            options['Transverse-distal inner radius'] = 0.7
            options['Transverse-distal tenia coli width'] = 1.0
            options['Distal inner radius'] = 0.7
            options['Distal tenia coli width'] = 1.0
        elif 'Pig 1' in parameterSetName:
            options['Number of segments'] = 120
            options['Proximal length'] = 2610.0
            options['Transverse length'] = 200.0
            options['Distal length'] = 200.0
            options['Proximal inner radius'] = 20.0
            options['Proximal tenia coli width'] = 5.0
            options['Proximal-transverse inner radius'] = 10.5
            options['Proximal-transverse tenia coli width'] = 5.0
            options['Transverse-distal inner radius'] = 8.0
            options['Transverse-distal tenia coli width'] = 5.0
            options['Distal inner radius'] = 8.0
            options['Distal tenia coli width'] = 5.0
        elif 'Pig 2' in parameterSetName:
            options['Number of segments'] = 3
            options['Proximal length'] = 30.0
            options['Transverse length'] = 30.0
            options['Distal length'] = 30.0
            options['Proximal inner radius'] = 16.0
            options['Proximal tenia coli width'] = 5.0
            options['Proximal-transverse inner radius'] = 16.0
            options['Proximal-transverse tenia coli width'] = 5.0
            options['Transverse-distal inner radius'] = 16.0
            options['Transverse-distal tenia coli width'] = 5.0
            options['Distal inner radius'] = 16.0
            options['Distal tenia coli width'] = 5.0
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path', 'Segment profile', 'Number of segments',
            'Start phase', 'Proximal length', 'Transverse length',
            'Distal length', 'Proximal inner radius',
            'Proximal tenia coli width', 'Proximal-transverse inner radius',
            'Proximal-transverse tenia coli width',
            'Transverse-distal inner radius',
            'Transverse-distal tenia coli width', 'Distal inner radius',
            'Distal tenia coli width', 'Refine',
            'Refine number of elements around',
            'Refine number of elements along',
            'Refine number of elements through wall'
        ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [MeshType_1d_path1]
        if optionName == 'Segment profile':
            return [MeshType_3d_colonsegment1]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls,
                                 optionName,
                                 scaffoldType,
                                 parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(
                    cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(
                cls.centralPathDefaultScaffoldPackages[parameterSetName])
        if optionName == 'Segment profile':
            if not parameterSetName:
                parameterSetName = scaffoldType.getParameterSetNames()[0]
            return ScaffoldPackage(scaffoldType,
                                   defaultParameterSetName=parameterSetName)
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType(
        ) in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage(
                'Central path', MeshType_1d_path1)
        if not options['Segment profile'].getScaffoldType(
        ) in cls.getOptionValidScaffoldTypes('Segment profile'):
            options['Segment profile'] = cls.getOptionScaffoldPackage(
                'Segment profile', MeshType_3d_colonsegmentteniacoli1)
        for key in [
                'Number of segments', 'Refine number of elements around',
                'Refine number of elements along',
                'Refine number of elements through wall'
        ]:
            if options[key] < 1:
                options[key] = 1
        for key in [
                'Proximal length', 'Transverse length', 'Distal length',
                'Proximal inner radius', 'Proximal tenia coli width',
                'Proximal-transverse inner radius',
                'Proximal-transverse tenia coli width',
                'Transverse-distal inner radius',
                'Transverse-distal tenia coli width', 'Distal inner radius',
                'Distal tenia coli width'
        ]:
            if options[key] < 0.0:
                options[key] = 0.0

    @staticmethod
    def generateBaseMesh(region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: annotationGroups
        """
        centralPath = options['Central path']
        segmentProfile = options['Segment profile']
        segmentCount = options['Number of segments']
        startPhase = options['Start phase'] % 360.0
        proximalLength = options['Proximal length']
        transverseLength = options['Transverse length']
        distalLength = options['Distal length']
        proximalInnerRadius = options['Proximal inner radius']
        proximalTCWidth = options['Proximal tenia coli width']
        proximalTransverseInnerRadius = options[
            'Proximal-transverse inner radius']
        proximalTransverseTCWidth = options[
            'Proximal-transverse tenia coli width']
        transverseDistalInnerRadius = options['Transverse-distal inner radius']
        transverseDistalTCWidth = options['Transverse-distal tenia coli width']
        distalInnerRadius = options['Distal inner radius']
        distalTCWidth = options['Distal tenia coli width']
        segmentSettings = segmentProfile.getScaffoldSettings()

        elementsCountAroundTC = segmentSettings[
            'Number of elements around tenia coli']
        elementsCountAroundHaustrum = segmentSettings[
            'Number of elements around haustrum']
        cornerInnerRadiusFactor = segmentSettings['Corner inner radius factor']
        haustrumInnerRadiusFactor = segmentSettings[
            'Haustrum inner radius factor']
        segmentLengthEndDerivativeFactor = segmentSettings[
            'Segment length end derivative factor']
        segmentLengthMidDerivativeFactor = segmentSettings[
            'Segment length mid derivative factor']
        tcCount = segmentSettings['Number of tenia coli']
        tcThickness = segmentSettings['Tenia coli thickness']
        elementsCountAround = (elementsCountAroundTC +
                               elementsCountAroundHaustrum) * tcCount

        elementsCountAlongSegment = segmentSettings[
            'Number of elements along segment']
        elementsCountThroughWall = segmentSettings[
            'Number of elements through wall']
        wallThickness = segmentSettings['Wall thickness']
        useCrossDerivatives = segmentSettings['Use cross derivatives']
        useCubicHermiteThroughWall = not (
            segmentSettings['Use linear through wall'])
        elementsCountAlong = int(elementsCountAlongSegment * segmentCount)

        firstNodeIdentifier = 1
        firstElementIdentifier = 1

        # Central path
        tmpRegion = region.createRegion()
        centralPath.generate(tmpRegion)
        cx, cd1, cd2, cd12 = extractPathParametersFromRegion(tmpRegion)
        # for i in range(len(cx)):
        # print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], '],')
        del tmpRegion

        # find arclength of colon
        length = 0.0
        elementsCountIn = len(cx) - 1
        sd1 = interp.smoothCubicHermiteDerivativesLine(
            cx,
            cd1,
            fixAllDirections=True,
            magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN)
        for e in range(elementsCountIn):
            arcLength = interp.getCubicHermiteArcLength(
                cx[e], sd1[e], cx[e + 1], sd1[e + 1])
            # print(e+1, arcLength)
            length += arcLength
        segmentLength = length / segmentCount
        # print('Length = ', length)

        # Sample central path
        sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(
            cx, cd1, elementsCountAlongSegment * segmentCount)
        sd2 = interp.interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf)[0]

        # Generate variation of radius & tc width along length
        lengthList = [
            0.0, proximalLength, proximalLength + transverseLength, length
        ]
        innerRadiusList = [
            proximalInnerRadius, proximalTransverseInnerRadius,
            transverseDistalInnerRadius, distalInnerRadius
        ]
        innerRadiusAlongElementList, dInnerRadiusAlongElementList = interp.sampleParameterAlongLine(
            lengthList, innerRadiusList, elementsCountAlong)

        tcWidthList = [
            proximalTCWidth, proximalTransverseTCWidth,
            transverseDistalTCWidth, distalTCWidth
        ]
        tcWidthAlongElementList, dTCWidthAlongElementList = interp.sampleParameterAlongLine(
            lengthList, tcWidthList, elementsCountAlong)

        xExtrude = []
        d1Extrude = []
        d2Extrude = []
        d3UnitExtrude = []

        # Create object
        colonSegmentTubeMeshInnerPoints = ColonSegmentTubeMeshInnerPoints(
            region, elementsCountAroundTC, elementsCountAroundHaustrum,
            elementsCountAlongSegment, tcCount,
            segmentLengthEndDerivativeFactor, segmentLengthMidDerivativeFactor,
            segmentLength, wallThickness, cornerInnerRadiusFactor,
            haustrumInnerRadiusFactor, innerRadiusAlongElementList,
            dInnerRadiusAlongElementList, tcWidthAlongElementList, startPhase)

        for nSegment in range(segmentCount):
            # Create inner points
            xInner, d1Inner, d2Inner, transitElementList, segmentAxis, annotationGroups, annotationArray, \
                faceMidPointsZ = colonSegmentTubeMeshInnerPoints.getColonSegmentTubeMeshInnerPoints(nSegment)

            # Warp segment points
            xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = tubemesh.warpSegmentPoints(
                xInner, d1Inner, d2Inner, segmentAxis, segmentLength, sx, sd1,
                sd2, elementsCountAround, elementsCountAlongSegment, nSegment,
                faceMidPointsZ)

            # Store points along length
            xExtrude = xExtrude + (xWarpedList if nSegment == 0 else
                                   xWarpedList[elementsCountAround:])
            d1Extrude = d1Extrude + (d1WarpedList if nSegment == 0 else
                                     d1WarpedList[elementsCountAround:])
            d2Extrude = d2Extrude + (d2WarpedList if nSegment == 0 else
                                     d2WarpedList[elementsCountAround:])
            d3UnitExtrude = d3UnitExtrude + (
                d3WarpedUnitList
                if nSegment == 0 else d3WarpedUnitList[elementsCountAround:])

        contractedWallThicknessList = colonSegmentTubeMeshInnerPoints.getContractedWallThicknessList(
        )

        # Create coordinates and derivatives
        xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(
            xExtrude, d1Extrude, d2Extrude, d3UnitExtrude,
            contractedWallThicknessList, elementsCountAround,
            elementsCountAlong, elementsCountThroughWall, transitElementList)

        relaxedLengthList, xiList = colonSegmentTubeMeshInnerPoints.getRelaxedLengthAndXiList(
        )

        if tcThickness > 0:
            tubeTCWidthList = colonSegmentTubeMeshInnerPoints.getTubeTCWidthList(
            )
            xList, d1List, d2List, d3List, annotationGroups, annotationArray = getTeniaColi(
                region, xList, d1List, d2List, d3List, curvatureList, tcCount,
                elementsCountAroundTC, elementsCountAroundHaustrum,
                elementsCountAlong, elementsCountThroughWall, tubeTCWidthList,
                tcThickness, sx, annotationGroups, annotationArray)

            # Create flat and texture coordinates
            xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = createFlatAndTextureCoordinatesTeniaColi(
                xiList, relaxedLengthList, length, wallThickness, tcCount,
                tcThickness, elementsCountAroundTC,
                elementsCountAroundHaustrum, elementsCountAlong,
                elementsCountThroughWall, transitElementList)

            # Create nodes and elements
            nextNodeIdentifier, nextElementIdentifier, annotationGroups = createNodesAndElementsTeniaColi(
                region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat,
                xTexture, d1Texture, d2Texture, elementsCountAroundTC,
                elementsCountAroundHaustrum, elementsCountAlong,
                elementsCountThroughWall, tcCount, annotationGroups,
                annotationArray, firstNodeIdentifier, firstElementIdentifier,
                useCubicHermiteThroughWall, useCrossDerivatives)

        else:
            # Create flat and texture coordinates
            xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = tubemesh.createFlatAndTextureCoordinates(
                xiList, relaxedLengthList, length, wallThickness,
                elementsCountAround, elementsCountAlong,
                elementsCountThroughWall, transitElementList)

            # Create nodes and elements
            nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements(
                region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat,
                xTexture, d1Texture, d2Texture, elementsCountAround,
                elementsCountAlong, elementsCountThroughWall, annotationGroups,
                annotationArray, firstNodeIdentifier, firstElementIdentifier,
                useCubicHermiteThroughWall, useCrossDerivatives)

        return annotationGroups

    @classmethod
    def generateMesh(cls, region, options):
        """
        Generate base or refined mesh.
        :param region: Zinc region to create mesh in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: list of AnnotationGroup for mesh.
        """
        if not options['Refine']:
            return cls.generateBaseMesh(region, options)

        refineElementsCountAround = options['Refine number of elements around']
        refineElementsCountAlong = options['Refine number of elements along']
        refineElementsCountThroughWall = options[
            'Refine number of elements through wall']

        baseRegion = region.createRegion()
        baseAnnotationGroups = cls.generateBaseMesh(baseRegion, options)

        meshrefinement = MeshRefinement(baseRegion, region,
                                        baseAnnotationGroups)
        meshrefinement.refineAllElementsCubeStandard3d(
            refineElementsCountAround, refineElementsCountAlong,
            refineElementsCountThroughWall)
        return meshrefinement.getAnnotationGroups()
Exemplo n.º 17
0
class MeshType_3d_brainstem1(Scaffold_base):
    """
    Generates a tapered cylinder for the brainstem based on
    solid cylinder mesh, with variable numbers of elements
    in major, minor and length directions. Regions of the
    brainstem are annotated.
    """

    centralPathDefaultScaffoldPackages = {
        'Brainstem 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 3.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ], [[[0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 2.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 3.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 4.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 5.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 6.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 7.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]],
                        [[0.0, 0.0, 8.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0],
                         [0.0, 0.0, 0.0], [-0.0, 1.0, 0.0], [0.0, 0.0, 0.0]]]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '2',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            }),
        'Cat 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 3.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ],
                    [
                        # [ [0.0,1.0,-5.0], [0.0,0.0,4.5], [5.0,0.0,0.0], [1.0,0.0,0.0], [0.0,2.4,0.0], [0.0,2.2,0.0] ],
                        # [ [0.0,1.0,-0.5], [0.0,0.0,4.5], [6.0,0.0,0.0], [1.0,0.0,0.0], [0.0,4.0,0.0], [0.0,1.1,0.0] ],
                        # [ [0.0,1.0, 4.0], [0.0,0.0,4.5], [7.0,0.0,0.0], [1.0,0.0,0.0], [0.0,4.5,0.0], [0.0,0.8,0.0] ],
                        # [ [0.0,1.0, 8.5], [0.0,0.0,4.5], [8.0,0.0,0.0], [1.0,0.0,0.0], [0.0,5.5,0.0], [0.0,0.8,0.0] ],
                        # [ [0.0,1.0,13.0], [0.0,0.0,4.5], [9.0,0.0,0.0], [1.0,0.0,0.0], [0.0,6.0,0.0], [0.0,0.2,0.0] ]
                        [[0.0, 10.7, -12.3], [0.0, -3.8, 3.1], [4.8, 0.0, 0.0],
                         [-0.7, 0.0, 0.0], [-0.0, 2.2, 2.6], [0.0, 0.5, 0.1]],
                        [[0.0, 6.8, -8.8], [0.0, -4.0, 3.9], [4.5, 0.0, 0.0],
                         [0.1, 0.0, 0.0], [-0.0, 2.7, 2.7], [0.0, 0.5, 0.1]],
                        [[0.0, 2.8, -4.5], [0.0, -3.5, 4.2], [5.0, 0.0, 0.0],
                         [1.2, 0.0, 0.0], [-0.0, 3.2, 2.8], [0.0, 0.5, 0.0]],
                        [[0.0, -0.2, -0.5], [0.0, -2.8, 3.8], [6.8, 0.0, 0.0],
                         [0.8, 0.0, 0.0], [-0.0, 3.8, 2.8], [0.0, 0.6, -0.4]],
                        [[0.0, -2.8, 3.0], [0.0, -1.9, 4.1], [6.8, 0.0, 0.0],
                         [-0.6, 0.0, 0.0], [-0.0, 4.5, 2.0], [0.0, 0.3, -0.1]],
                        [[0.0, -3.8, 7.5], [0.0, -2.7, 4.6], [5.5, 0.0, 0.0],
                         [-0.2, 0.0, 0.0], [-0.0, 4.3, 2.5], [0.0, -1.0, 1.0]],
                        [[0.0, -8.2, 11.5], [0.0, -5.9, 3.2], [6.8, 0.0, 0.0],
                         [2.8, 0.0, 0.0], [-0.0, 2.3, 4.2], [0.0, -3.1, 2.4]]
                    ]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '2',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            }),
        'Human 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 1.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ],
                    [[[0.0, 18.9, -50.7], [0.0, -3.2, 15.4], [7.8, 0.0, 0.0],
                      [0.5, 0.0, 0.0], [-0.0, 6.1, 1.3], [0.0, 0.8, 1.7]],
                     [[0.0, 14.6, -36.3], [0.0, -5.4, 13.3], [8.5, 0.0, 0.0],
                      [0.9, 0.0, 0.0], [-0.0, 7.2, 2.9], [0.0, 1.4, 1.6]],
                     [[0.0, 8.3, -24.1], [0.0, -6.4, 12.5], [9.6, 0.0, 0.0],
                      [4.4, 0.0, 0.0], [-0.0, 8.9, 4.6], [0.0, 1.3, 0.5]],
                     [[0.0, 1.8, -11.4], [0.0, -5.3, 13.3], [17.4, 0.0, 0.0],
                      [5.3, 0.0, 0.0], [-0.0, 9.8, 3.9], [0.0, 2.6, -0.2]],
                     [[0.0, -2.3, 2.4], [0.0, -3.9, 13.2], [20.2, 0.0, 0.0],
                      [-0.4, 0.0, 0.0], [-0.0, 14.1, 4.2], [0.0, 2.2, -0.5]],
                     [[0.0, -6.0, 15.0], [0.0, -2.6, 12.9], [16.8, 0.0, 0.0],
                      [-2.4, 0.0, 0.0], [-0.0, 14.4, 2.9], [0.0, 0.2, -1.9]],
                     [[0.0, -7.5, 28.0], [0.0, -0.4, 13.1], [15.3, 0.0, 0.0],
                      [-0.6, 0.0, 0.0], [-0.0, 14.5, 0.4], [0.0, 0.0, -3.1]]]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1-2',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3-4',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '5-6',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            }),
        'Mouse 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 1.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ],
                    [([[0.0, 1.5, -3.4], [0.0, -0.1, 1.1], [1.4, 0.0, 0.0],
                       [-0.1, 0.0, 0.0], [0.0, 1.0, 0.1], [0.0, 0.1, 0.9]]),
                     ([[0.0, 1.0, -2.3], [0.0, -0.8, 1.0], [1.3, 0.0, 0.0],
                       [0.0, 0.0, 0.0], [0.0, 0.8, 0.7], [0.0, 0.0, 0.6]]),
                     ([[0.0, -0.1, -1.6], [0.0, -1.2, 0.8], [1.5, 0.0, 0.0],
                       [0.4, 0.0, 0.0], [0.0, 0.7, 1.0], [0.0, 0.1, 0.1]]),
                     ([[0.0, -1.2, -0.7], [0.0, -0.7, 1.2], [2.0, 0.0, 0.0],
                       [0.2, 0.0, 0.0], [0.0, 1.2, 0.7], [0.0, 0.1, -0.1]]),
                     ([[0.0, -1.5, 0.5], [0.0, 0.1, 1.4], [2.0, 0.0, 0.0],
                       [-0.1, 0.0, 0.0], [0.0, 1.4, -0.1], [0.0, 0.1, -0.1]]),
                     ([[0.0, -1.1, 1.9], [0.0, -0.3, 1.4], [1.6, 0.0, 0.0],
                       [-0.1, 0.0, 0.0], [0.0, 1.5, 0.3], [0.0, -0.3, 0.3]]),
                     ([[0.0, -2.1, 3.1], [0.0, -1.3, 0.8], [2.0, 0.0, 0.0],
                       [0.8, 0.0, 0.0], [0.0, 0.7, 1.2], [0.0, -0.8, 0.7]])]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1-2',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3-4',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '5-6',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            }),
        'Rat 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 1.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ],
                    [[[0.0, 2.9, -5.4], [0.0, 0.5, 1.7], [1.9, 0.0, 0.0],
                      [-0.2, 0.0, 0.0], [0.0, 1.3, -0.4], [-0.0, 0.2, 1.3]],
                     [[0.0, 2.7, -3.5], [0.0, -0.9, 2.0], [1.8, 0.0, 0.0],
                      [0.0, 0.0, 0.0], [-0.0, 1.4, 0.6], [-0.0, 0.0, 0.8]],
                     [[0.0, 1.1, -1.8], [0.0, -1.4, 1.7], [2.0, 0.0, 0.0],
                      [0.5, 0.0, 0.0], [-0.0, 1.3, 1.1], [0.0, 0.1, 0.2]],
                     [[0.0, -0.1, -0.2], [0.0, -1.1, 1.5], [2.7, 0.0, 0.0],
                      [0.3, 0.0, 0.0], [-0.0, 1.5, 1.1], [0.0, 0.2, -0.2]],
                     [[0.0, -1.1, 1.2], [0.0, -0.7, 1.6], [2.7, 0.0, 0.0],
                      [-0.2, 0.0, 0.0], [-0.0, 1.8, 0.8], [0.0, 0.1, -0.1]],
                     [[0.0, -1.5, 3.0], [0.0, -1.1, 1.9], [2.2, 0.0, 0.0],
                      [-0.1, 0.0, 0.0], [-0.0, 1.7, 1.0], [0.0, -0.4, 0.4]],
                     [[0.0, -3.3, 4.6], [0.0, -2.4, 1.3], [2.7, 0.0, 0.0],
                      [1.1, 0.0, 0.0], [-0.0, 0.9, 1.7], [0.0, -1.2, 1.0]]]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1-2',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3-4',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '5-6',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            }),
        'Pig 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 1.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ],
                    [([[0.0, 25.6, -29.3], [0.0, -5.4, 3.5], [4.8, 0.0, 0.0],
                       [-0.7, 0.0, 0.0], [0.0, 1.8, 2.9], [0.0, 1.1, 1.4]]),
                     ([[0.0, 19.5, -25.3], [0.0, -6.7, 4.8], [5.7, 0.0, 0.0],
                       [2.5, 0.0, 0.0], [0.0, 2.8, 4.0], [0.0, 0.8, 0.7]]),
                     ([[0.0, 12.2, -19.9], [0.0, -6.7, 5.4], [10.1, 0.0, 0.0],
                       [3.0, 0.0, 0.0], [0.0, 3.3, 4.2], [0.0, 1.5, 0.5]]),
                     ([[0.0, 6.0, -14.5], [0.0, -5.5, 6.3], [12.9, -0.2, -0.2],
                       [0.7, 0.0, 0.0], [0.1, 5.7, 4.9], [0.0, 2.0, -0.6]]),
                     ([[0.0, 1.5, -7.4], [0.0, -2.8, 7.0], [11.4, 0.0, 0.0],
                       [-0.3, 0.2, 0.0], [0.0, 7.4, 3.0], [-0.2, 2.4, -1.2]]),
                     ([[0.0, 0.1, -1.0], [0.0, -1.5, 6.9], [11.2, 0.3, 0.1],
                       [-0.5, 0.1, 0.1], [-0.3, 11.3, 2.5], [-0.1, 1.5, -0.2]
                       ]),
                     ([[0.0, -1.5, 6.6], [0.0, -2.0, 7.4], [10.3, 0.2, 0.0],
                       [-1.3, -0.2, -0.0], [-0.2, 9.4, 2.5], [0.2, -2.3, 0.9]])
                     ]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1-2',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3-4',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '5-6',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            }),
        'Sheep 1':
        ScaffoldPackage(
            MeshType_1d_path1,
            {
                'scaffoldSettings': {
                    'Coordinate dimensions': 3,
                    'D2 derivatives': True,
                    'D3 derivatives': True,
                    'Length': 1.0,
                    'Number of elements': 6
                },
                'meshEdits':
                exnodeStringFromNodeValues(  # dimensional.
                    [
                        Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                        Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2,
                        Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3
                    ],
                    [([[0.0, 21.3, -33.2], [0.0, -7.5, 6.0], [4.8, 0.0, 0.0],
                       [4.8, -0.0, -0.0], [0.0, 2.8, 3.5], [0.0, 3.2, 1.3]]),
                     ([[0.0, 14.5, -26.7], [0.0, -6.1, 7.2], [8.5, -0.0, -0.0],
                       [2.6, 0.0, 0.0], [0.0, 4.9, 4.1], [-0.0, 1.1, 0.1]]),
                     ([[0.0, 9.2, -19.0], [0.0, -5.2, 7.2], [10.1, 0.0, 0.0],
                       [1.1, -0.1, -0.0], [0.0, 5.0, 3.6], [0.1, 0.4, -0.6]]),
                     ([[0.0, 4.0, -12.2], [0.0, -3.5, 7.0], [10.8, -0.2, -0.1],
                       [-0.4, 0.2, 0.0], [0.1, 5.7, 2.9], [-0.1, 0.8, -1.4]]),
                     ([[0.0, 2.1, -5.5], [0.0, -1.2, 7.6], [9.4, 0.3, 0.1],
                       [0.1, 0.2, 0.1], [-0.2, 6.6, 1.0], [-0.2, 2.2, -0.3]]),
                     ([[0.0, 1.9, 2.9], [0.0, -2.1, 8.8], [11.2, 0.2, 0.1],
                       [3.3, -0.1, -0.0], [-0.2, 10.4, 2.5], [0.1, 0.3, 2.0]]),
                     ([[0.0, -2.3, 11.4], [0.0, -6.2, 8.1], [16.2, 0.1, 0.0],
                       [6.7, -0.2, -0.0], [-0.0, 6.7, 5.1], [0.3, -7.7, 3.2]])
                     ]),
                'userAnnotationGroups': [
                    {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '1-2',
                        'name': get_brainstem_term('medulla oblongata')[0],
                        'ontId': get_brainstem_term('medulla oblongata')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '3-4',
                        'name': get_brainstem_term('pons')[0],
                        'ontId': get_brainstem_term('pons')[1]
                    }, {
                        '_AnnotationGroup': True,
                        'dimension': 1,
                        'identifierRanges': '5-6',
                        'name': get_brainstem_term('midbrain')[0],
                        'ontId': get_brainstem_term('midbrain')[1]
                    }
                ]
            })
    }

    @staticmethod
    def getName():
        return '3D Brainstem 1'

    @staticmethod
    def getParameterSetNames():
        return [
            'Default', 'Cat 1', 'Human 1', 'Mouse 1', 'Rat 1', 'Pig 1',
            'Sheep 1'
        ]

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        if parameterSetName == 'Default':
            parameterSetName = 'Human 1'

        if 'Cat 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Cat 1']

        if 'Human 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Human 1']

        if 'Mouse 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Mouse 1']

        if 'Rat 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Rat 1']

        if 'Pig 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages['Pig 1']

        if 'Sheep 1' in parameterSetName:
            centralPathOption = cls.centralPathDefaultScaffoldPackages[
                'Sheep 1']

        options = {
            'Base parameter set': parameterSetName,
            'Central path': copy.deepcopy(centralPathOption),
            'Number of elements across major': 6,
            'Number of elements across minor': 6,
            'Number of elements along': 8,
            'Use cross derivatives': False,
            'Refine': False,
            'Refine number of elements across major and minor': 1,
            'Refine number of elements along': 1
        }

        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Central path', 'Number of elements across major',
            'Number of elements across minor', 'Number of elements along',
            'Refine', 'Refine number of elements across major and minor',
            'Refine number of elements along'
        ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Central path':
            return [MeshType_1d_path1]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Central path':
            return list(cls.centralPathDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \
            cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls,
                                 optionName,
                                 scaffoldType,
                                 parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \
                ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Central path':
            if not parameterSetName:
                parameterSetName = list(
                    cls.centralPathDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(
                cls.centralPathDefaultScaffoldPackages[parameterSetName])
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Central path'].getScaffoldType(
        ) in cls.getOptionValidScaffoldTypes('Central path'):
            options['Central path'] = cls.getOptionScaffoldPackage(
                'Central path', MeshType_1d_path1)
        dependentChanges = False
        if options['Number of elements across major'] < 4:
            options['Number of elements across major'] = 4
        if options['Number of elements across major'] % 2:
            options['Number of elements across major'] += 1
        if options['Number of elements across minor'] < 4:
            options['Number of elements across minor'] = 4
        if options['Number of elements across minor'] % 2:
            options['Number of elements across minor'] += 1
        if options['Number of elements along'] < 2:
            options['Number of elements along'] = 2
        return dependentChanges

    @classmethod
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        parameterSetName = options['Base parameter set']
        isCat = 'Cat 1' in parameterSetName
        isHuman = 'Human 1' in parameterSetName
        isMouse = 'Mouse 1' in parameterSetName
        isRat = 'Rat 1' in parameterSetName
        isPig = 'Pig 1' in parameterSetName
        isSheep = 'Sheep 1' in parameterSetName

        centralPath = options['Central path']
        brainstemPath = cls.centralPathDefaultScaffoldPackages['Brainstem 1']
        elementsCountAcrossMajor = options['Number of elements across major']
        elementsCountAcrossMinor = options['Number of elements across minor']
        elementsCountAlong = options['Number of elements along']

        # Cross section at Z axis
        halfBrainStem = False
        if halfBrainStem:
            elementsCountAcrossMajor //= 2
        elementsPerLayer = (
            (elementsCountAcrossMajor - 2) *
            elementsCountAcrossMinor) + (2 * (elementsCountAcrossMinor - 2))

        fm = region.getFieldmodule()
        cache = fm.createFieldcache()
        coordinates = findOrCreateFieldCoordinates(fm)
        mesh = fm.findMeshByDimension(3)

        # Annotation groups
        brainstemGroup = AnnotationGroup(region,
                                         get_brainstem_term('brainstem'))
        brainstemMeshGroup = brainstemGroup.getMeshGroup(mesh)
        midbrainGroup = AnnotationGroup(region, get_brainstem_term('midbrain'))
        midbrainMeshGroup = midbrainGroup.getMeshGroup(mesh)
        ponsGroup = AnnotationGroup(region, get_brainstem_term('pons'))
        ponsMeshGroup = ponsGroup.getMeshGroup(mesh)
        medullaGroup = AnnotationGroup(region,
                                       get_brainstem_term('medulla oblongata'))
        medullaMeshGroup = medullaGroup.getMeshGroup(mesh)
        annotationGroups = [
            brainstemGroup, midbrainGroup, ponsGroup, medullaGroup
        ]

        annotationGroupAlong = [[brainstemGroup, midbrainGroup],
                                [brainstemGroup, ponsGroup],
                                [brainstemGroup, medullaGroup]]

        # point markers
        # centralCanal = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('central canal of spinal cord'))
        # cerebralAqueduct = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('cerebral aqueduct'))
        # foramenCaecum = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('foramen caecum of medulla oblongata'))
        dorsalMidCaudalGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem dorsal midline caudal point'))
        ventralMidCaudalGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem ventral midline caudal point'))
        dorsalMidCranGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem dorsal midline cranial point'))
        ventralMidCranGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem ventral midline cranial point'))
        dorsalMidMedullaPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem dorsal midline pons-medulla junction'))
        ventralMidMedullaPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem ventral midline pons-medulla junction'))
        dorsalMidMidbrainPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem dorsal midline midbrain-pons junction'))
        ventralMidMidbrainPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem ventral midline midbrain-pons junction'))

        #######################
        # CREATE MAIN BODY MESH
        #######################
        cylinderShape = CylinderShape.CYLINDER_SHAPE_FULL if not halfBrainStem else CylinderShape.CYLINDER_SHAPE_LOWER_HALF

        # Body coordinates
        cylinderCentralPath = CylinderCentralPath(region, centralPath,
                                                  elementsCountAlong)
        base = CylinderEnds(elementsCountAcrossMajor,
                            elementsCountAcrossMinor,
                            centre=[0.0, 0.0, 0.0],
                            alongAxis=cylinderCentralPath.alongAxis[0],
                            majorAxis=cylinderCentralPath.majorAxis[0],
                            minorRadius=cylinderCentralPath.minorRadii[0])

        cylinder1 = CylinderMesh(fm,
                                 coordinates,
                                 elementsCountAlong,
                                 base,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath,
                                 useCrossDerivatives=False)

        brainstem_coordinates = findOrCreateFieldCoordinates(
            fm, name="brainstem coordinates")

        # Brain coordinates
        tmp_region = region.createRegion()
        tmp_fm = tmp_region.getFieldmodule()
        tmp_brainstem_coordinates = findOrCreateFieldCoordinates(
            tmp_fm, name="brainstem coordinates")

        cylinderCentralPath1 = CylinderCentralPath(tmp_region, brainstemPath,
                                                   elementsCountAlong)

        base1 = CylinderEnds(elementsCountAcrossMajor,
                             elementsCountAcrossMinor,
                             centre=[0.0, 0.0, 0.0],
                             alongAxis=cylinderCentralPath1.alongAxis[0],
                             majorAxis=cylinderCentralPath1.majorAxis[0],
                             minorRadius=cylinderCentralPath1.minorRadii[0])

        cylinder2 = CylinderMesh(tmp_fm,
                                 tmp_brainstem_coordinates,
                                 elementsCountAlong,
                                 base1,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath1,
                                 useCrossDerivatives=False)

        # Write two coordinates
        sir = tmp_region.createStreaminformationRegion()
        srm = sir.createStreamresourceMemory()
        tmp_region.write(sir)
        result, buffer = srm.getBuffer()

        sir = region.createStreaminformationRegion()
        srm = sir.createStreamresourceMemoryBuffer(buffer)
        region.read(sir)

        del srm
        del sir
        del tmp_fm
        del tmp_brainstem_coordinates
        del tmp_region

        # Annotating groups
        iRegionBoundaries = [
            int(6 * elementsCountAlong / 15),
            int(13 * elementsCountAlong / 15)
        ]
        for elementIdentifier in range(1, mesh.getSize() + 1):
            element = mesh.findElementByIdentifier(elementIdentifier)
            brainstemMeshGroup.addElement(element)
            if elementIdentifier > (iRegionBoundaries[-1] * elementsPerLayer):
                midbrainMeshGroup.addElement(element)
            elif (elementIdentifier >
                  (iRegionBoundaries[0] * elementsPerLayer)) and (
                      elementIdentifier <=
                      (iRegionBoundaries[-1] * elementsPerLayer)):
                ponsMeshGroup.addElement(element)
            else:
                medullaMeshGroup.addElement(element)

        ################
        # point markers
        ################
        pointMarkers = [
            {
                "group": dorsalMidCaudalGroup,
                "marker_brainstem_coordinates": [0.0, 1.0, 0.0]
            },
            {
                "group": ventralMidCaudalGroup,
                "marker_brainstem_coordinates": [0.0, -1.0, 0.0]
            },
            {
                "group": dorsalMidCranGroup,
                "marker_brainstem_coordinates": [0.0, 1.0, 8.0]
            },
            {
                "group": ventralMidCranGroup,
                "marker_brainstem_coordinates": [0.0, -1.0, 8.0]
            },
            {
                "group": dorsalMidMedullaPonsJunction,
                "marker_brainstem_coordinates": [0.0, 1.0, 3.0]
            },
            {
                "group": ventralMidMedullaPonsJunction,
                "marker_brainstem_coordinates": [0.0, -1.0, 3.0]
            },
            {
                "group": dorsalMidMidbrainPonsJunction,
                "marker_brainstem_coordinates": [0.0, 1.0, 6.0]
            },
            {
                "group": ventralMidMidbrainPonsJunction,
                "marker_brainstem_coordinates": [0.0, -1.0, 6.0]
            },
        ]

        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()
        markerBrainstemCoordinates = findOrCreateFieldCoordinates(
            fm, name="marker_body_coordinates")
        markerTemplateInternal = nodes.createNodetemplate()
        markerTemplateInternal.defineField(markerName)
        markerTemplateInternal.defineField(markerLocation)
        markerTemplateInternal.defineField(markerBrainstemCoordinates)

        cache = fm.createFieldcache()

        brainstemNodesetGroup = brainstemGroup.getNodesetGroup(nodes)

        nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1)
        findMarkerLocation = fm.createFieldFindMeshLocation(
            markerBrainstemCoordinates, brainstem_coordinates, mesh)
        findMarkerLocation.setSearchMode(
            FieldFindMeshLocation.SEARCH_MODE_EXACT)

        for pointMarker in pointMarkers:
            group = pointMarker["group"]
            markerPoint = markerPoints.createNode(nodeIdentifier,
                                                  markerTemplateInternal)
            cache.setNode(markerPoint)

            markerBrainstemCoordinates.assignReal(
                cache, pointMarker["marker_brainstem_coordinates"])
            markerName.assignString(cache, group.getName())

            element, xi = findMarkerLocation.evaluateMeshLocation(cache, 3)
            markerLocation.assignMeshLocation(cache, element, xi)
            group.getNodesetGroup(nodes).addNode(markerPoint)
            brainstemNodesetGroup.addNode(markerPoint)
            nodeIdentifier += 1

        return annotationGroups

    @classmethod
    def refineMesh(cls, meshRefinement, options):
        """
        Refine source mesh into separate region, with change of basis.
        :param meshRefinement: MeshRefinement, which knows source and target region.
        :param options: Dict containing options. See getDefaultOptions().
        """
        assert isinstance(meshRefinement, MeshRefinement)
        refineElementsCountAcrossMajor = options[
            'Refine number of elements across major and minor']
        refineElementsCountAlong = options['Refine number of elements along']
        meshRefinement.refineAllElementsCubeStandard3d(
            refineElementsCountAcrossMajor, refineElementsCountAlong,
            refineElementsCountAcrossMajor)

    @classmethod
    def defineFaceAnnotations(cls, region, options, annotationGroups):
        """
        Add face annotation groups from the 1D mesh.
        :param region: Zinc region containing model.
        :param options: Dict containing options. See getDefaultOptions().
        :param annotationGroups: List of annotation groups for ventral-level elements.
        New point annotation groups are appended to this list.
        """
        # create  groups
        fm = region.getFieldmodule()
        mesh2d = fm.findMeshByDimension(2)
        is_exterior = fm.createFieldIsExterior()
        is_exterior_face_xi1 = fm.createFieldOr(
            fm.createFieldAnd(is_exterior,
                              fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0)),
            fm.createFieldAnd(is_exterior,
                              fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)))
        is_exterior_face_xi2 = fm.createFieldOr(
            fm.createFieldAnd(is_exterior,
                              fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0)),
            fm.createFieldAnd(is_exterior,
                              fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1)))
        is_exterior_face_xi3 = fm.createFieldOr(
            fm.createFieldAnd(is_exterior,
                              fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)),
            fm.createFieldAnd(is_exterior,
                              fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)))

        # external regions
        groupNames = ['brainstem', 'midbrain', 'medulla oblongata', 'pons']
        for groupName in groupNames:
            subGroup = AnnotationGroup(region, get_brainstem_term(groupName))
            issub = subGroup.getFieldElementGroup(mesh2d)
            is_subface_ext = fm.createFieldOr(
                fm.createFieldAnd(issub, is_exterior_face_xi1),
                fm.createFieldAnd(issub, is_exterior_face_xi3))
            subFaceGroup = findOrCreateAnnotationGroupForTerm(
                annotationGroups, region,
                get_brainstem_term(groupName + ' exterior'))
            subFaceGroup.getMeshGroup(mesh2d).addElementsConditional(
                is_subface_ext)

        # brainstem interface
        groupNames = [
            'brainstem-spinal cord interface', 'thalamus-brainstem interface'
        ]
        for groupName in groupNames:
            subGroupName = 'midbrain' if groupName == 'thalamus-brainstem interface' else 'medulla oblongata'
            subGroup = AnnotationGroup(region,
                                       get_brainstem_term(subGroupName))
            issub = subGroup.getFieldElementGroup(mesh2d)
            is_subface_ext = fm.createFieldAnd(issub, is_exterior_face_xi2)
            subFaceGroup = findOrCreateAnnotationGroupForTerm(
                annotationGroups, region, get_brainstem_term(groupName))
            subFaceGroup.getMeshGroup(mesh2d).addElementsConditional(
                is_subface_ext)
Exemplo n.º 18
0
    def test_user_annotation_groups(self):
        """
        Test user annotation group on heartatria1 scaffold with scaffold package.
        """
        scaffoldPackage = ScaffoldPackage(MeshType_3d_heartatria1)
        # can't add user annotation groups until generate is called()
        try:
            annotationGroup = scaffoldPackage.createUserAnnotationGroup()
            self.assertTrue(False)  # should never get here as above raises expection
        except:
            pass

        context = Context("Test")
        region = context.getDefaultRegion()
        self.assertTrue(region.isValid())
        fieldmodule = region.getFieldmodule()

        scaffoldPackage.generate(region)

        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(22, len(annotationGroups))

        endocardium_of_la = scaffoldPackage.findAnnotationGroupByName('endocardium of left atrium')
        self.assertTrue(isinstance(endocardium_of_la, AnnotationGroup))
        self.assertFalse(scaffoldPackage.isUserAnnotationGroup(endocardium_of_la))
        self.assertFalse(scaffoldPackage.deleteAnnotationGroup(endocardium_of_la))  # can't delete auto annotation groups

        annotationGroup1 = scaffoldPackage.createUserAnnotationGroup()
        self.assertEqual('group1', annotationGroup1.getName())  # default name
        self.assertIsNone(annotationGroup1.getId())
        self.assertTrue(scaffoldPackage.isUserAnnotationGroup(annotationGroup1))
        self.assertEqual(-1, annotationGroup1.getDimension())  # -1 = empty
        group = annotationGroup1.getGroup()
        self.assertTrue(group.isValid())
        mesh2d = fieldmodule.findMeshByDimension(2)
        meshGroup = group.createFieldElementGroup(mesh2d).getMeshGroup()
        mesh_group_add_identifier_ranges(meshGroup, [[1,2],[4,4]])
        self.assertEqual(3, meshGroup.getSize())
        self.assertEqual(2, annotationGroup1.getDimension())
        identifier_ranges_string = identifier_ranges_to_string(mesh_group_to_identifier_ranges(meshGroup))
        self.assertEqual('1-2,4', identifier_ranges_string)

        annotationGroup2 = scaffoldPackage.createUserAnnotationGroup(('bob', 'BOB:1'))
        self.assertEqual('bob', annotationGroup2.getName())
        self.assertEqual('BOB:1', annotationGroup2.getId())
        self.assertTrue(scaffoldPackage.isUserAnnotationGroup(annotationGroup2))
        group = annotationGroup2.getGroup()
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodesetGroup = group.createFieldNodeGroup(nodes).getNodesetGroup()
        nodeset_group_add_identifier_ranges(nodesetGroup, identifier_ranges_from_string('1,3-5,7'))
        self.assertEqual(5, nodesetGroup.getSize())
        self.assertEqual(0, annotationGroup2.getDimension())
        identifier_ranges_string = identifier_ranges_to_string(nodeset_group_to_identifier_ranges(nodesetGroup))
        self.assertEqual('1,3-5,7', identifier_ranges_string)

        annotationGroup3 = scaffoldPackage.createUserAnnotationGroup()
        self.assertEqual('group2', annotationGroup3.getName())  # default name
        self.assertTrue(scaffoldPackage.isUserAnnotationGroup(annotationGroup3))
        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(25, len(annotationGroups))

        # rename group1 to fred
        self.assertTrue(annotationGroup1.setName('fred'))
        self.assertTrue(annotationGroup1.setId('FRED:1'))
        self.assertEqual('fred', annotationGroup1.getName())
        self.assertEqual('FRED:1', annotationGroup1.getId())

        self.assertTrue(scaffoldPackage.deleteAnnotationGroup(annotationGroup3))
        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(24, len(annotationGroups))

        # test serialisation
        dct = scaffoldPackage.toDict()
        self.assertEqual('3D Heart Atria 1', dct['scaffoldTypeName'])
        scaffoldType = Scaffolds().findScaffoldTypeByName(dct['scaffoldTypeName'])

        scaffoldPackage2 = ScaffoldPackage(scaffoldType, dct)
        region2 = context.createRegion()
        fieldmodule2 = region2.getFieldmodule()

        scaffoldPackage2.generate(region2)

        annotationGroups2 = scaffoldPackage2.getAnnotationGroups()
        self.assertEqual(24, len(annotationGroups2))

        annotationGroup1 = scaffoldPackage2.findAnnotationGroupByName('fred')
        self.assertEqual('fred', annotationGroup1.getName())
        self.assertEqual('FRED:1', annotationGroup1.getId())
        self.assertTrue(scaffoldPackage2.isUserAnnotationGroup(annotationGroup1))
        self.assertEqual(2, annotationGroup1.getDimension())
        mesh2d2 = fieldmodule2.findMeshByDimension(2)
        meshGroup2 = annotationGroup1.getMeshGroup(mesh2d2)
        self.assertEqual(3, meshGroup2.getSize())
        identifier_ranges_string = identifier_ranges_to_string(mesh_group_to_identifier_ranges(meshGroup2))
        self.assertEqual('1-2,4', identifier_ranges_string)

        annotationGroup2 = scaffoldPackage2.findAnnotationGroupByName('bob')
        self.assertEqual('bob', annotationGroup2.getName())
        self.assertEqual('BOB:1', annotationGroup2.getId())
        self.assertTrue(scaffoldPackage2.isUserAnnotationGroup(annotationGroup2))
        self.assertEqual(0, annotationGroup2.getDimension())
        nodes2 = fieldmodule2.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodesetGroup2 = annotationGroup2.getNodesetGroup(nodes2)
        self.assertEqual(5, nodesetGroup.getSize())
        identifier_ranges_string = identifier_ranges_to_string(nodeset_group_to_identifier_ranges(nodesetGroup2))
        self.assertEqual('1,3-5,7', identifier_ranges_string)
Exemplo n.º 19
0
class MeshType_3d_bladder1(Scaffold_base):
    '''
    3-D bladder scaffold.
    '''
    ostiumDefaultScaffoldPackages = {
        'Ostium Cat 1': ScaffoldPackage(MeshType_3d_ostium1, {
            'scaffoldSettings': {
                'Number of vessels': 1,
                'Number of elements across common': 2,
                'Number of elements around ostium': 8,
                'Number of elements along': 2,
                'Number of elements through wall': 1,  # not implemented for > 1
                'Unit scale': 1.0,
                'Outlet': False,
                'Ostium diameter': 0.3,
                'Ostium length': 0.2,
                'Ostium wall thickness': 0.05,
                'Ostium inter-vessel distance': 0.8,
                'Ostium inter-vessel height': 0.0,
                'Use linear through ostium wall': True,
                'Vessel end length factor': 1.0,
                'Vessel inner diameter': 0.15,
                'Vessel wall thickness': 0.04,
                'Vessel angle 1 degrees': 0.0,
                'Vessel angle 1 spread degrees': 0.0,
                'Vessel angle 2 degrees': 0.0,
                'Use linear through vessel wall': True,
                # 'Use cross derivatives': False,
                'Refine': False,
                'Refine number of elements around': 4,
                'Refine number of elements along': 4,
                'Refine number of elements through wall': 1
                },
            }),
        'Ostium Rat 1': ScaffoldPackage(MeshType_3d_ostium1, {
            'scaffoldSettings': {
                'Number of vessels': 1,
                'Number of elements across common': 2,
                'Number of elements around ostium': 8,
                'Number of elements along': 1,
                'Number of elements through wall': 1,  # not implemented for > 1
                'Unit scale': 1.0,
                'Outlet': False,
                'Ostium diameter': 0.15,
                'Ostium length': 0.05,
                'Ostium wall thickness': 0.02,
                'Ostium inter-vessel distance': 0.8,
                'Ostium inter-vessel height': 0.0,
                'Use linear through ostium wall': True,
                'Vessel end length factor': 1.0,
                'Vessel inner diameter': 0.04,
                'Vessel wall thickness': 0.01,
                'Vessel angle 1 degrees': 0.0,
                'Vessel angle 1 spread degrees': 0.0,
                'Vessel angle 2 degrees': 0.0,
                'Use linear through vessel wall': True,
                # 'Use cross derivatives': False,
                'Refine': False,
                'Refine number of elements around': 4,
                'Refine number of elements along': 4,
                'Refine number of elements through wall': 1
                },
            })
        }

    @staticmethod
    def getName():
        return '3D Bladder 1'

    @staticmethod
    def getParameterSetNames():
        return [
            'Default',
            'Cat 1',
            'Rat 1']

    @classmethod
    def getDefaultOptions(cls, parameterSetName='Default'):
        if 'Rat' in parameterSetName:
            ostiumOption = cls.ostiumDefaultScaffoldPackages['Ostium Rat 1']
        else:
            ostiumOption = cls.ostiumDefaultScaffoldPackages['Ostium Cat 1']

        options = {
            'Number of elements up neck': 6,
            'Number of elements up body': 8,
            'Number of elements around': 8,  # should be even
            'Number of elements through wall': 1,
            'Number of elements around ostium': 8,  # implemented for 8
            'Number of elements radially on annulus': 1,
            'Height': 5.0,
            'Major diameter': 6.0,
            'Minor diameter': 6.0,
            'Bladder wall thickness': 0.05,
            'Urethra diameter': 1.0,
            'Ureter': copy.deepcopy(ostiumOption),
            'Ostium position around': 0.55,  # should be on the dorsal part
            'Ostium position up': 0.55,
            'Use cross derivatives': False,
            'Refine': False,
            'Refine number of elements around': 4,
            'Refine number of elements up': 4,
            'Refine number of elements through wall': 1
        }
        if 'Rat' in parameterSetName:
            options['Number of elements around'] = 16  # should be even
            options['Height'] = 3.0
            options['Major diameter'] = 5.0
            options['Minor diameter'] = 3.0
            options['Bladder wall thickness'] = 0.02
            options['Urethra diameter'] = 0.7
            options['Ostium position around'] = 0.65  # should be on the dorsal part
            options['Ostium position up'] = 0.75
        return options

    @staticmethod
    def getOrderedOptionNames():
        return [
            'Number of elements up neck',
            'Number of elements up body',
            'Number of elements around',
            'Number of elements through wall',
            'Number of elements radially on annulus',
            'Height',
            'Major diameter',
            'Minor diameter',
            'Bladder wall thickness',
            'Urethra diameter',
            'Ostium position around',
            'Ostium position up',
            'Ureter',
            'Use cross derivatives',
            'Refine',
            'Refine number of elements around',
            'Refine number of elements up',
            'Refine number of elements through wall'
        ]

    @classmethod
    def getOptionValidScaffoldTypes(cls, optionName):
        if optionName == 'Ureter':
            return [MeshType_3d_ostium1]
        return []

    @classmethod
    def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType):
        if optionName == 'Ureter':
            return list(cls.ostiumDefaultScaffoldPackages.keys())
        assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), cls.__name__ + '.getOptionScaffoldTypeParameterSetNames.  ' + \
            'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName()
        return scaffoldType.getParameterSetNames()

    @classmethod
    def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None):
        '''
        :param parameterSetName:  Name of valid parameter set for option Scaffold, or None for default.
        :return: ScaffoldPackage.
        '''
        if parameterSetName:
            assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \
                'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + ' in option ' + str(optionName) + ' of scaffold ' + cls.getName()
        if optionName == 'Ureter':
            if not parameterSetName:
                parameterSetName = list(cls.ostiumDefaultScaffoldPackages.keys())[0]
            return copy.deepcopy(cls.ostiumDefaultScaffoldPackages[parameterSetName])
        assert False, cls.__name__ + '.getOptionScaffoldPackage:  Option ' + optionName + ' is not a scaffold'

    @classmethod
    def checkOptions(cls, options):
        if not options['Ureter'].getScaffoldType() in cls.getOptionValidScaffoldTypes('Ureter'):
            options['Ureter'] = cls.getOptionScaffoldPackage('Ureter', MeshType_3d_ostium1)
        if (options['Number of elements up neck'] < 4):
            options['Number of elements up neck'] = 4
        if (options['Number of elements up body'] < 4):
            options['Number of elements up body'] = 4
        if (options['Number of elements around'] < 8):
            options['Number of elements around'] = 8
        elif (options['Number of elements around'] % 2) == 1:
            options['Number of elements around'] += 1
        if (options['Number of elements through wall'] != 1):
            options['Number of elements through wall'] = 1
        if(options['Number of elements radially on annulus'] > 2):
            options['Number of elements radially on annulus'] = 2
        if (options['Major diameter'] < options['Minor diameter']):
            options['Major diameter'] = options['Minor diameter']

    @classmethod
    def generateBaseMesh(cls, region, options):
        '''
        Generate the base bicubic Hermite mesh.
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: list of AnnotationGroup
        '''
        elementsCountUpNeck = options['Number of elements up neck']
        elementsCountUpBody = options['Number of elements up body']
        elementsCountAround = options['Number of elements around']
        height = options['Height']
        majorDiameter = options['Major diameter']
        minorDiameter = options['Minor diameter']
        radius = 0.5 * options['Urethra diameter']
        bladderWallThickness = options['Bladder wall thickness']
        useCrossDerivatives = options['Use cross derivatives']
        elementsCountAroundOstium = options['Number of elements around ostium']
        elementsCountAnnulusRadially = options['Number of elements radially on annulus']
        ostiumPositionAround = options['Ostium position around']
        ostiumPositionUp = options['Ostium position up']

        ostiumOptions = options['Ureter']
        ostiumDefaultOptions = ostiumOptions.getScaffoldSettings()

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = findOrCreateFieldCoordinates(fm)
        cache = fm.createFieldcache()

        mesh = fm.findMeshByDimension(3)

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodetemplateApex = nodes.createNodetemplate()
        nodetemplateApex.defineField(coordinates)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
        if useCrossDerivatives:
            nodetemplate = nodes.createNodetemplate()
            nodetemplate.defineField(coordinates)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)
        else:
            nodetemplate = nodetemplateApex

        eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives)
        eft = eftfactory.createEftBasic()

        elementtemplate = mesh.createElementtemplate()
        elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        elementtemplate.defineField(coordinates, -1, eft)

        neckGroup = AnnotationGroup(region, get_bladder_term("neck of urinary bladder"))
        bodyGroup = AnnotationGroup(region, get_bladder_term("dome of the bladder"))
        urinaryBladderGroup = AnnotationGroup(region, get_bladder_term("urinary bladder"))
        annotationGroups = [neckGroup, bodyGroup, urinaryBladderGroup]

        neckMeshGroup = neckGroup.getMeshGroup(mesh)
        bodyMeshGroup = bodyGroup.getMeshGroup(mesh)
        urinaryBladderMeshGroup = urinaryBladderGroup.getMeshGroup(mesh)

        # create nodes
        # create neck of the bladder
        nodeIdentifier = 1
        radiansPerElementAround = 2.0*math.pi/elementsCountAround
        radiansPerElementUpNeck = (math.pi/4)/elementsCountUpNeck

        # create lower part of the ellipsoidal
        neckHeight = height - height * math.cos(math.pi / 4)
        ellipsoidal_x = []
        ellipsoidal_d1 = []
        ellipsoidal_d2 = []
        for n2 in range(0, elementsCountUpNeck+1):
            radiansUp = n2 * radiansPerElementUpNeck
            cosRadiansUp = math.cos(radiansUp)
            sinRadiansUp = math.sin(radiansUp)
            majorRadius = 0.5 * majorDiameter * sinRadiansUp
            minorRadius = 0.5 * minorDiameter * sinRadiansUp
            if n2 == 0:
                for n1 in range(elementsCountAround):
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -majorRadius * sinRadiansAround,
                        minorRadius * cosRadiansAround,
                        -height - neckHeight
                    ]
                    dx_ds1 = [
                        -majorRadius * cosRadiansAround * radiansPerElementAround,
                        minorRadius * -sinRadiansAround * radiansPerElementAround,
                        0.0
                    ]
                    dx_ds2 = [
                        -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        height * sinRadiansUp * radiansPerElementUpNeck
                    ]
                    ellipsoidal_x.append(x)
                    ellipsoidal_d1.append(dx_ds1)
                    ellipsoidal_d2.append(dx_ds2)
            else:
                for n1 in range(elementsCountAround):
                    neckHeight = height - height * math.cos(math.pi/4)
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -majorRadius * sinRadiansAround,
                        minorRadius * cosRadiansAround,
                        -height - neckHeight + n2 * 2 * neckHeight / elementsCountUpNeck
                    ]
                    dx_ds1 = [
                        -majorRadius * cosRadiansAround * radiansPerElementAround,
                        minorRadius * -sinRadiansAround * radiansPerElementAround,
                        0.0
                    ]
                    dx_ds2 = [
                        -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        height * sinRadiansUp * radiansPerElementUpNeck
                    ]
                    ellipsoidal_x.append(x)
                    ellipsoidal_d1.append(dx_ds1)
                    ellipsoidal_d2.append(dx_ds2)

        # create tube nodes
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        tube_x = []
        tube_d1 = []
        tube_d2 = []
        for n2 in range(0, elementsCountUpNeck + 1):
            radiansUp = n2 * radiansPerElementUpNeck
            cosRadiansUp = math.cos(radiansUp)
            sinRadiansUp = math.sin(radiansUp)
            if n2 == 0:
                for n1 in range(elementsCountAround):
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -radius * sinRadiansAround,
                        radius * cosRadiansAround,
                        -height - neckHeight
                    ]
                    dx_ds1 = [
                        -radiansPerElementAround * radius * cosRadiansAround,
                        radiansPerElementAround * radius * -sinRadiansAround,
                        0.0
                    ]
                    dx_ds2 = [0, 0, height / (2 * elementsCountUpNeck)]
                    tube_x.append(x)
                    tube_d1.append(dx_ds1)
                    tube_d2.append(dx_ds2)
            else:
                for n1 in range(elementsCountAround):
                    neckHeight = height - height* math.cos(math.pi/4)
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -radius * sinRadiansAround,
                        radius * cosRadiansAround,
                        -height - neckHeight + n2 * 2 * neckHeight / elementsCountUpNeck
                    ]
                    dx_ds1 = [
                        -radiansPerElementAround * radius * cosRadiansAround,
                        radiansPerElementAround * radius * -sinRadiansAround,
                        0.0
                    ]
                    dx_ds2 = [0, 0, height / elementsCountUpNeck]
                    tube_x.append(x)
                    tube_d1.append(dx_ds1)
                    tube_d2.append(dx_ds2)

        # interpolation between the lower part of the ellipsoidal and the tube
        m1 = 0
        z_bottom = ellipsoidal_x[-1][2]
        z_top = ellipsoidal_x[0][2]
        delta_z = z_top - z_bottom
        interpolatedNodes = []
        interpolatedNodes_d1 = []
        interpolatedNodes_d2 = []
        for n2 in range(elementsCountUpNeck+1):
            xi = 1.0 - (ellipsoidal_x[m1][2] - z_bottom) / delta_z
            for n1 in range(elementsCountAround):
                phi_inner, _, phi_outer, _ = getCubicHermiteBasis(xi)
                x = [(phi_inner*tube_x[m1][c] + phi_outer*ellipsoidal_x[m1][c]) for c in range(3)]
                d1 = [(phi_inner*tube_d1[m1][c] + phi_outer*ellipsoidal_d1[m1][c]) for c in range(3)]
                d2 = [(phi_inner*tube_d2[m1][c] + phi_outer*ellipsoidal_d2[m1][c]) for c in range(3)]
                interpolatedNodes.append(x)
                interpolatedNodes_d1.append(d1)
                interpolatedNodes_d2.append(d2)
                m1 += 1

        # smoothing the derivatives
        sd2Raw = []
        for n1 in range(elementsCountAround):
            lineSmoothingNodes = []
            lineSmoothingNodes_d2 = []
            for n2 in range(elementsCountUpNeck+1):
                    lineSmoothingNodes.append(interpolatedNodes[n1 + n2 * elementsCountAround])
                    lineSmoothingNodes_d2.append(interpolatedNodes_d2[n1 + n2 * elementsCountAround])
            sd2 = smoothCubicHermiteDerivativesLine(lineSmoothingNodes, lineSmoothingNodes_d2,
                                                    fixAllDirections=False,
                                                    fixStartDerivative=True, fixEndDerivative=True,
                                                    fixStartDirection=False, fixEndDirection=False)
            sd2Raw.append(sd2)

        # re-arrange the derivatives order
        d2RearrangedList = []
        for n2 in range(elementsCountUpNeck+1):
            for n1 in range(elementsCountAround):
                d2 = sd2Raw[n1][n2]
                d2RearrangedList.append(d2)

        # create tracksurface at the outer layer of the neck
        nodesOnTrackSurface = []
        nodesOnTrackSurface_d1 = []
        nodesOnTrackSurface_d2 = []
        for n2 in range(elementsCountUpNeck+1):
            for n1 in range(elementsCountAround):
                if (n1 <= elementsCountAround / 2):
                    nodesOnTrackSurface.append(interpolatedNodes[n2 * elementsCountAround + n1])
                    nodesOnTrackSurface_d1.append(interpolatedNodes_d1[n2 * elementsCountAround + n1])
                    nodesOnTrackSurface_d2.append(d2RearrangedList[n2 * elementsCountAround + n1])

        # nodes and derivatives of the neck of the bladder
        listOuterNeck_x = []
        listOuterNeck_d1 = []
        listOuterNeck_d2 = []
        elementsCount1 = elementsCountAround // 2
        elementsCount2 = elementsCountUpNeck
        tracksurfaceOstium1 = TrackSurface(elementsCount1, elementsCount2, nodesOnTrackSurface, nodesOnTrackSurface_d1,
                                    nodesOnTrackSurface_d2)
        ostium1Position = tracksurfaceOstium1.createPositionProportion(ostiumPositionAround, ostiumPositionUp)
        ostium1Position.xi1 = 1.0
        ostium1Position.xi2 = 1.0
        ostiumElementPositionAround = ostium1Position.e1
        ostiumElementPositionUp = ostium1Position.e2
        for n2 in range(len(interpolatedNodes)):
            listOuterNeck_x.append(interpolatedNodes[n2])
            listOuterNeck_d1.append(interpolatedNodes_d1[n2])
            listOuterNeck_d2.append(d2RearrangedList[n2])

        # create body of the bladder
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        radiansPerElementUpBody = (3 * math.pi / 4) / elementsCountUpBody
        # create regular rows
        listOuterBody_x = []
        listOuterBody_d1 = []
        listOuterBody_d2 = []
        for n2 in range(1, elementsCountUpBody):
            radiansUp = (math.pi / 4) + n2 * radiansPerElementUpBody
            cosRadiansUp = math.cos(radiansUp)
            sinRadiansUp = math.sin(radiansUp)
            majorRadius = 0.5 * majorDiameter * sinRadiansUp
            minorRadius = 0.5 * minorDiameter * sinRadiansUp
            for n1 in range(elementsCountAround):
                radiansAround = n1 * radiansPerElementAround
                cosRadiansAround = math.cos(radiansAround)
                sinRadiansAround = math.sin(radiansAround)
                x = [
                    -majorRadius * sinRadiansAround,
                    minorRadius * cosRadiansAround,
                    -height * cosRadiansUp
                ]
                dx_ds1 = [
                    -majorRadius * cosRadiansAround * radiansPerElementAround,
                    minorRadius * -sinRadiansAround * radiansPerElementAround,
                    0.0
                ]
                dx_ds2 = [
                    -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpBody,
                    0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpBody,
                    height*sinRadiansUp * radiansPerElementUpBody
                ]
                listOuterBody_x.append(x)
                listOuterBody_d1.append(dx_ds1)
                listOuterBody_d2.append(dx_ds2)

        # create outer apex node
        outerApexNode_x = []
        outerApexNode_d1 = []
        outerApexNode_d2 = []
        x = [0.0, 0.0, height]
        dx_ds1 = [height*radiansPerElementUpBody/2, 0.0, 0.0]
        dx_ds2 = [0.0, height*radiansPerElementUpBody/2, 0.0]
        outerApexNode_x.append(x)
        outerApexNode_d1.append(dx_ds1)
        outerApexNode_d2.append(dx_ds2)

        # set nodes of outer layer of the bladder
        listTotalOuter_x = listOuterNeck_x + listOuterBody_x + outerApexNode_x
        listTotalOuter_d1 = listOuterNeck_d1 + listOuterBody_d1 + outerApexNode_d1
        listTotalOuter_d2 = listOuterNeck_d2 + listOuterBody_d2 + outerApexNode_d2

        outerLayer_x = []
        outerLayer_d1 = []
        outerLayer_d2 = []
        for n2 in range(len(listTotalOuter_x)):
            if (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + ostiumElementPositionAround + 1) and\
                    (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1):
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, listTotalOuter_x[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, listTotalOuter_d1[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, listTotalOuter_d2[n2])
                nodeIdentifier += 1
                outerLayer_x.append(listTotalOuter_x[n2])
                outerLayer_d1.append(listTotalOuter_d1[n2])
                outerLayer_d2.append(listTotalOuter_d2[n2])

        # create and set nodes of inner layer of the bladder
        listTotalInner_x = []
        listTotalInner_d1 = []
        listTotalInner_d2 = []
        for n2 in range(elementsCountUpNeck + elementsCountUpBody):
            loop_x = [listTotalOuter_x[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)]
            loop_d1 = [listTotalOuter_d1[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)]
            loop_d2 = [listTotalOuter_d2[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)]
            for n1 in range(elementsCountAround):
                x, d1, _, _ = interp.projectHermiteCurvesThroughWall(loop_x, loop_d1, loop_d2, n1,
                                                                     -bladderWallThickness, loop=True)
                listTotalInner_x.append(x)
                listTotalInner_d1.append(d1)

        listInner_d2 = []
        for n2 in range(elementsCountAround):
            nx = [listTotalOuter_x[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)]
            nd1 = [listTotalOuter_d1[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)]
            nd2 = [listTotalOuter_d2[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)]
            for n1 in range(elementsCountUpNeck + elementsCountUpBody):
                _, d2, _, _ = interp.projectHermiteCurvesThroughWall(nx, nd2, nd1, n1,
                                                                     bladderWallThickness, loop=False)
                listInner_d2.append(d2)

        # re-arrange the derivatives order
        for n2 in range(elementsCountUpNeck + elementsCountUpBody):
            for n1 in range(elementsCountAround):
                rearranged_d2 = listInner_d2[n1 * (elementsCountUpNeck + elementsCountUpBody) + n2]
                listTotalInner_d2.append(rearranged_d2)

        innerLayer_x = []
        innerLayer_d1 = []
        innerLayer_d2 = []
        for n2 in range(len(listTotalInner_x)):
            if (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + ostiumElementPositionAround + 1) and \
                    (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1):
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, listTotalInner_x[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, listTotalInner_d1[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, listTotalInner_d2[n2])
                nodeIdentifier += 1

                innerLayer_x.append(listTotalInner_x[n2])
                innerLayer_d1.append(listTotalInner_d1[n2])
                innerLayer_d2.append(listTotalInner_d2[n2])

        # create inner apex node
        x = [0.0, 0.0, height - bladderWallThickness]
        dx_ds1 = [height*radiansPerElementUpBody/2, 0.0, 0.0]
        dx_ds2 = [0.0, height*radiansPerElementUpBody/2, 0.0]
        node = nodes.createNode(nodeIdentifier, nodetemplate)
        cache.setNode(node)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
        listTotalInner_x.append(x)
        listTotalInner_d1.append(dx_ds1)
        listTotalInner_d2.append(dx_ds2)
        innerLayer_x.append(x)
        innerLayer_d1.append(dx_ds1)
        innerLayer_d2.append(dx_ds2)
        nodeIdentifier += 1

        # create ureters on the surface
        elementIdentifier = 1
        # ureter 1
        centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = tracksurfaceOstium1.evaluateCoordinates(ostium1Position, derivatives=True)
        td1, td2, td3 = calculate_surface_axes(centerUreter1_d1, centerUreter1_d2, [1.0, 0.0, 0.0])
        m1 = ostiumElementPositionUp * elementsCountAround + ostiumElementPositionAround
        ureter1StartCornerx = listOuterNeck_x[m1]
        v1 = [(ureter1StartCornerx[c] - centerUreter1_x[c]) for c in range(3)]
        ostium1Direction = vector.crossproduct3(td3, v1)
        nodeIdentifier, elementIdentifier, (o1_x, o1_d1, o1_d2, _, o1_NodeId, o1_Positions) = \
            generateOstiumMesh(region, ostiumDefaultOptions, tracksurfaceOstium1, ostium1Position, ostium1Direction,
            startNodeIdentifier=nodeIdentifier, startElementIdentifier=elementIdentifier)

        # ureter 2
        tracksurfaceOstium2 = tracksurfaceOstium1.createMirrorX()
        ostium2Position = TrackSurfacePosition(elementsCountAround - ostiumElementPositionAround, ostiumElementPositionUp - 1, 0.0, 1.0)
        centerUreter2_x, centerUreter2_d1, centerUreter2_d2 = tracksurfaceOstium2.evaluateCoordinates(ostium2Position, derivatives =True)
        ad1, ad2, ad3 = calculate_surface_axes(centerUreter2_d1, centerUreter2_d2, [1.0, 0.0, 0.0])
        if elementsCountAroundOstium == 4:
            m2 = ostiumElementPositionUp * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1
        else:
            m2 = ostiumElementPositionUp * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 2
        ureter2StartCornerx = listOuterNeck_x[m2]
        v2 = [(ureter2StartCornerx[c] - centerUreter2_x[c]) for c in range(3)]
        ostium2Direction = vector.crossproduct3(ad3, v2)
        nodeIdentifier, elementIdentifier, (o2_x, o2_d1, o2_d2, _, o2_NodeId, o2_Positions) = \
            generateOstiumMesh(region, ostiumDefaultOptions, tracksurfaceOstium2, ostium2Position, ostium2Direction,
            startNodeIdentifier=nodeIdentifier, startElementIdentifier=elementIdentifier)

        # create annulus mesh around ostium
        endPoints1_x = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints1_d1 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints1_d2 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endNode1_Id = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endDerivativesMap = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints2_x = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints2_d1 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints2_d2 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endNode2_Id = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]

        nodeCountsEachWallLayer = (elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1
        for n3 in range(2):
            n1 = 0
            endNode1_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround) + ostiumElementPositionAround + 1
            endNode1_Id[n3][n1 + 1] = endNode1_Id[n3][n1] + elementsCountAround
            endNode1_Id[n3][n1 + 2] = endNode1_Id[n3][n1 + 1] + elementsCountAround - 2
            endNode1_Id[n3][n1 + 3] = endNode1_Id[n3][n1 + 2] + 1
            endNode1_Id[n3][n1 + 4] = endNode1_Id[n3][n1 + 3] + 1
            endNode1_Id[n3][n1 + 5] = endNode1_Id[n3][n1 + 1] + 1
            endNode1_Id[n3][n1 + 6] = endNode1_Id[n3][n1] + 2
            endNode1_Id[n3][n1 + 7] = endNode1_Id[n3][n1] + 1
            if ostiumElementPositionAround == 0:
                endNode2_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround)\
                                        + elementsCountAround - ostiumElementPositionAround - 1
                endNode2_Id[n3][n1 + 1] = endNode2_Id[n3][n1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 2] = endNode2_Id[n3][n1 + 1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 3] = endNode2_Id[n3][n1 + 2] + 1
                endNode2_Id[n3][n1 + 4] = endNode2_Id[n3][n1 + 3] - elementsCountAround + 1
                endNode2_Id[n3][n1 + 5] = endNode2_Id[n3][n1 + 4] - elementsCountAround + 2
                endNode2_Id[n3][n1 + 6] = endNode2_Id[n3][n1 + 5] - elementsCountAround
                endNode2_Id[n3][n1 + 7] = endNode2_Id[n3][n1] + 1
            else:
                endNode2_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround)\
                                        + elementsCountAround - ostiumElementPositionAround - 1
                endNode2_Id[n3][n1 + 1] = endNode2_Id[n3][n1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 2] = endNode2_Id[n3][n1 + 1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 3] = endNode2_Id[n3][n1 + 2] + 1
                endNode2_Id[n3][n1 + 4] = endNode2_Id[n3][n1 + 3] + 1
                endNode2_Id[n3][n1 + 5] = endNode2_Id[n3][n1 + 1] + 1
                endNode2_Id[n3][n1 + 6] = endNode2_Id[n3][n1] + 2
                endNode2_Id[n3][n1 + 7] = endNode2_Id[n3][n1] + 1

        for n3 in range(2):
            for n1 in range(elementsCountAroundOstium):
                nc1 = endNode1_Id[n3][n1] - (1 - n3) * nodeCountsEachWallLayer - 1
                endPoints1_x[n3][n1] = innerLayer_x[nc1]
                endPoints1_d1[n3][n1] = innerLayer_d1[nc1]
                endPoints1_d2[n3][n1] = [innerLayer_d2[nc1][c] for c in range(3)]
                nc2 = endNode2_Id[n3][n1] - (1 - n3) * nodeCountsEachWallLayer - 1
                endPoints2_x[n3][n1] = innerLayer_x[nc2]
                endPoints2_d1[n3][n1] = innerLayer_d1[nc2]
                endPoints2_d2[n3][n1] = innerLayer_d2[nc2]

        for n1 in range(elementsCountAroundOstium):
            if n1 == 0:
                endDerivativesMap[0][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0))
                endDerivativesMap[1][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0))
            elif n1 == 1:
                endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 0, 0), None)
                endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 0, 0), None)
            elif n1 == 2:
                endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))
                endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))
            elif n1 == 3:
                endDerivativesMap[0][n1] = ((1, 0, 0), (0, 1, 0), None)
                endDerivativesMap[1][n1] = ((1, 0, 0), (0, 1, 0), None)
            elif n1 == 4:
                endDerivativesMap[0][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0))
                endDerivativesMap[1][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0))
            elif n1 == 5:
                endDerivativesMap[0][n1] = ((0, -1, 0), (1, 0, 0), None)
                endDerivativesMap[1][n1] = ((0, -1, 0), (1, 0, 0), None)
            elif n1 == 6:
                endDerivativesMap[0][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0))
                endDerivativesMap[1][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0))
            else:
                endDerivativesMap[0][n1] = ((-1, 0, 0), (0, -1, 0), None)
                endDerivativesMap[1][n1] = ((-1, 0, 0), (0, -1, 0), None)

        nodeIdentifier, elementIdentifier = createAnnulusMesh3d(
            nodes, mesh, nodeIdentifier, elementIdentifier,
            o1_x, o1_d1, o1_d2, None, o1_NodeId, None,
            endPoints1_x, endPoints1_d1, endPoints1_d2, None, endNode1_Id, endDerivativesMap,
            elementsCountRadial=elementsCountAnnulusRadially, meshGroups=[neckMeshGroup, urinaryBladderMeshGroup])

        nodeIdentifier, elementIdentifier = createAnnulusMesh3d(
            nodes, mesh, nodeIdentifier, elementIdentifier,
            o2_x, o2_d1, o2_d2, None, o2_NodeId, None,
            endPoints2_x, endPoints2_d1, endPoints2_d2, None, endNode2_Id, endDerivativesMap,
            elementsCountRadial=elementsCountAnnulusRadially, meshGroups=[neckMeshGroup, urinaryBladderMeshGroup])

        # create elements
        for e3 in range(1):
            newl = (e3 + 1) * ((elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1)
            # create bladder neck elements
            for e2 in range(elementsCountUpNeck):
                for e1 in range(elementsCountAround):
                    if e2 == ostiumElementPositionUp:
                        if (e1 == ostiumElementPositionAround or e1 == ostiumElementPositionAround + 1):
                            pass
                        elif (e1 == elementsCountAround - ostiumElementPositionAround - 2 or e1 == elementsCountAround - 1 - ostiumElementPositionAround):
                            pass
                        else:
                            bni1 = e2 * elementsCountAround + e1 + 1
                            bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                            if e1 < ostiumElementPositionAround:
                                bni3 = bni1 + elementsCountAround
                                bni4 = bni2 + elementsCountAround
                            elif (ostiumElementPositionAround + 1 < e1 < elementsCountAround - ostiumElementPositionAround - 2):
                                bni3 = bni1 + elementsCountAround - 1
                                bni4 = bni2 + elementsCountAround - 1
                            elif e1 > elementsCountAround - ostiumElementPositionAround - 1:
                                bni3 = bni1 + elementsCountAround - 2
                                if e1 == elementsCountAround - 1:
                                    bni4 = bni2 + elementsCountAround
                                else:
                                    bni4 = bni2 + elementsCountAround - 2
                            element = mesh.createElement(elementIdentifier, elementtemplate)
                            nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                               bni1, bni2, bni3, bni4]
                            result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                            neckMeshGroup.addElement(element)
                            urinaryBladderMeshGroup.addElement(element)
                            elementIdentifier += 1
                    elif e2 == ostiumElementPositionUp + 1:
                        if (e1 == ostiumElementPositionAround or e1 == ostiumElementPositionAround + 1):
                            pass
                        elif (e1 == elementsCountAround - ostiumElementPositionAround - 2 or e1 == elementsCountAround - 1 - ostiumElementPositionAround):
                            pass
                        else:
                            if e1 < ostiumElementPositionAround:
                                bni1 = e2 * elementsCountAround + e1 + 1
                                bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                                bni3 = bni1 + elementsCountAround - 2
                                bni4 = bni2 + elementsCountAround - 2
                            elif (ostiumElementPositionAround + 1 < e1 < elementsCountAround - ostiumElementPositionAround - 2):
                                bni1 = e2 * elementsCountAround + e1
                                bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround
                                bni3 = bni1 + elementsCountAround - 1
                                bni4 = bni2 + elementsCountAround - 1
                            elif e1 > elementsCountAround - ostiumElementPositionAround - 1:
                                bni1 = e2 * elementsCountAround + e1 - 1
                                bni3 = bni1 + elementsCountAround
                                if e1 == elementsCountAround - 1:
                                    bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                                    bni4 = bni2 + elementsCountAround - 2
                                else:
                                    bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1
                                    bni4 = bni2 + elementsCountAround
                            element = mesh.createElement(elementIdentifier, elementtemplate)
                            nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                               bni1, bni2, bni3, bni4]
                            result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                            neckMeshGroup.addElement(element)
                            urinaryBladderMeshGroup.addElement(element)
                            elementIdentifier += 1
                    elif e2 > ostiumElementPositionUp + 1:
                        element = mesh.createElement(elementIdentifier, elementtemplate)
                        bni1 = e2 * elementsCountAround + e1 - 1
                        bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1
                        bni3 = bni1 + elementsCountAround
                        bni4 = bni2 + elementsCountAround
                        nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                           bni1, bni2, bni3, bni4]
                        result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                        neckMeshGroup.addElement(element)
                        urinaryBladderMeshGroup.addElement(element)
                        elementIdentifier += 1
                    else:
                        element = mesh.createElement(elementIdentifier, elementtemplate)
                        bni1 = e2 * elementsCountAround + e1 + 1
                        bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                        bni3 = bni1 + elementsCountAround
                        bni4 = bni2 + elementsCountAround
                        nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                           bni1, bni2, bni3, bni4]
                        result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                        neckMeshGroup.addElement(element)
                        urinaryBladderMeshGroup.addElement(element)
                        elementIdentifier += 1

            # create bladder body elements
            for e2 in range(elementsCountUpNeck, (elementsCountUpNeck + elementsCountUpBody - 1)):
                for e1 in range(elementsCountAround):
                    element = mesh.createElement(elementIdentifier, elementtemplate)
                    bni1 = e2 * elementsCountAround + e1 - 1
                    bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1
                    bni3 = bni1 + elementsCountAround
                    bni4 = bni2 + elementsCountAround
                    nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                       bni1, bni2, bni3, bni4]
                    result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                    bodyMeshGroup.addElement(element)
                    urinaryBladderMeshGroup.addElement(element)
                    elementIdentifier += 1
            # create apex elements
            bni3 = (elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1
            elementtemplateApex = mesh.createElementtemplate()
            elementtemplateApex.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            for e1 in range(elementsCountAround):
                va = e1
                vb = (e1 + 1) % elementsCountAround
                eftApex = eftfactory.createEftShellPoleTop(va, vb)
                elementtemplateApex.defineField(coordinates, -1, eftApex)
                # redefine field in template for changes to eftApex:
                element = mesh.createElement(elementIdentifier, elementtemplateApex)
                bni1 = bni3 - elementsCountAround + e1
                bni2 = bni3 - elementsCountAround + (e1 + 1) % elementsCountAround
                nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni1, bni2, bni3]
                result = element.setNodesByIdentifier(eftApex, nodeIdentifiers)
                bodyMeshGroup.addElement(element)
                urinaryBladderMeshGroup.addElement(element)
                elementIdentifier += 1

        fm.endChange()
        return annotationGroups

    @classmethod
    def refineMesh(cls, meshrefinement, options):
        assert isinstance(meshrefinement, MeshRefinement)
        refineElementsCountAround = options['Refine number of elements around']
        refineElementsCountUp = options['Refine number of elements up']
        refineElementsCountThroughWall = options['Refine number of elements through wall']
        element = meshrefinement._sourceElementiterator.next()
        while element.isValid():
            numberInXi1 = refineElementsCountAround
            numberInXi2 = refineElementsCountUp
            numberInXi3 = refineElementsCountThroughWall
            meshrefinement.refineElementCubeStandard3d(element, numberInXi1, numberInXi2, numberInXi3)
            element = meshrefinement._sourceElementiterator.next()

    @classmethod
    def defineFaceAnnotations(cls, region, options, annotationGroups):
        """
        Add face annotation groups from the highest dimension mesh.
        Must have defined faces and added subelements for highest dimension groups.
        :param region: Zinc region containing model.
        :param options: Dict containing options. See getDefaultOptions().
        :param annotationGroups: List of annotation groups for top-level elements.
        New face annotation groups are appended to this list.
        """
        # create 2d surface mesh groups
        fm = region.getFieldmodule()
        neckGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("neck of urinary bladder"))
        bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("dome of the bladder"))
        urinaryBladderGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("urinary bladder"))
        mesh2d = fm.findMeshByDimension(2)
        is_exterior = fm.createFieldIsExterior()
        is_exterior_face_xi3_1 = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1))
        is_exterior_face_xi3_0 = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0))

        is_body = bodyGroup.getFieldElementGroup(mesh2d)
        is_body_serosa = fm.createFieldAnd(is_body, is_exterior_face_xi3_1)
        is_body_lumen = fm.createFieldAnd(is_body, is_exterior_face_xi3_0)

        is_neck = neckGroup.getFieldElementGroup(mesh2d)
        is_neck_serosa = fm.createFieldAnd(is_neck, is_exterior_face_xi3_1)
        is_neck_lumen = fm.createFieldAnd(is_neck, is_exterior_face_xi3_0)

        serosaOfBody = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of body of urinary bladder"))
        serosaOfBody.getMeshGroup(mesh2d).addElementsConditional(is_body_serosa)
        lumenOfBody = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("lumen of body of urinary bladder"))
        lumenOfBody.getMeshGroup(mesh2d).addElementsConditional(is_body_lumen)

        SerosaOfNeck =findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of neck of urinary bladder"))
        SerosaOfNeck.getMeshGroup(mesh2d).addElementsConditional(is_neck_serosa)
        lumenOfNeck = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("lumen of neck of urinary bladder"))
        lumenOfNeck.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen)
Exemplo n.º 20
0
    def test_user_marker_points(self):
        """
        Test user marker point on brainstem1 scaffold which defined "brainstem coordinates".
        """
        scaffoldPackage = ScaffoldPackage(MeshType_3d_brainstem1)

        context = Context("Test")
        region = context.getDefaultRegion()
        self.assertTrue(region.isValid())
        fieldmodule = region.getFieldmodule()
        mesh = fieldmodule.findMeshByDimension(3)
        nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)

        scaffoldPackage.generate(region)
        # 1 higher than last node in scaffold. Make marker nodes from this number
        nextNodeIdentifier = scaffoldPackage.getNextNodeIdentifier()

        brainstemCoordinatesField = fieldmodule.findFieldByName("brainstem coordinates")
        self.assertTrue(brainstemCoordinatesField.isValid())

        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(18, len(annotationGroups))
        TOL = 1.0E-6  # coarse to handle find xi tolerances

        # check a built-in non-marker annotation group
        ponsGroup = scaffoldPackage.findAnnotationGroupByName('pons')
        self.assertFalse(ponsGroup.isMarker())
        self.assertRaises(AssertionError, lambda: ponsGroup.getMarkerMaterialCoordinates())
        self.assertRaises(AssertionError, lambda: ponsGroup.getMarkerLocation())
        self.assertRaises(AssertionError, lambda: ponsGroup.createMarkerNode(nextNodeIdentifier))

        # check a built-in marker annotation group
        brainstemVentralCranialPointGroup = \
            scaffoldPackage.findAnnotationGroupByName('brainstem ventral midline cranial point')
        self.assertIsNotNone(brainstemVentralCranialPointGroup)
        self.assertTrue(brainstemVentralCranialPointGroup.isMarker())
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = \
            brainstemVentralCranialPointGroup.getMarkerMaterialCoordinates()
        self.assertEqual(brainstemCoordinatesFieldOut, brainstemCoordinatesField)
        assertAlmostEqualList(self, [0.0, -1.0, 8.0], brainstemCoordinatesValueOut, delta=TOL)
        elementOut, xiOut = brainstemVentralCranialPointGroup.getMarkerLocation()
        self.assertEqual(235, elementOut.getIdentifier())
        assertAlmostEqualList(self, [1.0, 1.0, 0.0], xiOut, delta=TOL)
        self.assertRaises(AssertionError, lambda: brainstemVentralCranialPointGroup.createMarkerNode(nextNodeIdentifier))

        # check a non-existant annotation group
        bobGroup = scaffoldPackage.findAnnotationGroupByName("bob")
        self.assertIsNone(bobGroup)

        # now make a marker annotation named "bob" at the default location
        bobGroup = scaffoldPackage.createUserAnnotationGroup(('bob', 'BOB:1'))
        self.assertTrue(scaffoldPackage.isUserAnnotationGroup(bobGroup))
        self.assertFalse(bobGroup.isMarker())
        node = bobGroup.createMarkerNode(nextNodeIdentifier)
        bobNodeIdentifier = node.getIdentifier()
        self.assertEqual(nextNodeIdentifier, bobNodeIdentifier)
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = bobGroup.getMarkerMaterialCoordinates()
        self.assertIsNone(brainstemCoordinatesFieldOut)
        self.assertIsNone(brainstemCoordinatesValueOut)
        elementOut, xiOut = bobGroup.getMarkerLocation()
        self.assertEqual(1, elementOut.getIdentifier())
        assertAlmostEqualList(self, [0.0, 0.0, 0.0], xiOut, delta=TOL)
        # now request brainstem coordinates and let the annotation group determine its values from element:xi
        bobGroup.setMarkerMaterialCoordinates(brainstemCoordinatesField)
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = bobGroup.getMarkerMaterialCoordinates()
        self.assertEqual(brainstemCoordinatesFieldOut, brainstemCoordinatesField)
        # these should be precisely cos(45) but are not due to ellipse approximations
        assertAlmostEqualList(self, [0.707016, -0.707198, 0], brainstemCoordinatesValueOut, delta=TOL)
        # set element:xi location and check brainstem coordinates change
        bobGroup.setMarkerLocation(mesh.findElementByIdentifier(33), [0.0, 0.5, 0.0])
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = bobGroup.getMarkerMaterialCoordinates()
        self.assertEqual(brainstemCoordinatesFieldOut, brainstemCoordinatesField)
        assertAlmostEqualList(self, [0.707016, -0.707198, 1.5], brainstemCoordinatesValueOut, delta=TOL)
        # assign brainstem coordinates and check element:xi has moved
        bobGroup.setMarkerMaterialCoordinates(brainstemCoordinatesField, [-0.1, -0.5, 2.2])
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = bobGroup.getMarkerMaterialCoordinates()
        self.assertEqual(brainstemCoordinatesFieldOut, brainstemCoordinatesField)
        assertAlmostEqualList(self, [-0.1, -0.5, 2.2], brainstemCoordinatesValueOut, delta=TOL)
        elementOut, xiOut = bobGroup.getMarkerLocation()
        self.assertEqual(82, elementOut.getIdentifier())
        assertAlmostEqualList(self, [0.305595, 0.2, 0.485941], xiOut, delta=TOL)

        # now make a marker annotation named "fred" with brainstem coordinates from the start
        fredGroup = scaffoldPackage.createUserAnnotationGroup(('fred', 'FRED:1'))
        # AnnotationGroup.createMarkerNode increments nextNodeIdentifier to one not used by existing node
        node = fredGroup.createMarkerNode(nextNodeIdentifier, brainstemCoordinatesField, [0.5, 0.5, 4])
        fredNodeIdentifier = node.getIdentifier()
        self.assertEqual(nextNodeIdentifier + 1, fredNodeIdentifier)
        del node
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = fredGroup.getMarkerMaterialCoordinates()
        self.assertEqual(brainstemCoordinatesFieldOut, brainstemCoordinatesField)
        assertAlmostEqualList(self, [0.5, 0.5, 4], brainstemCoordinatesValueOut, delta=TOL)
        elementOut, xiOut = fredGroup.getMarkerLocation()
        self.assertEqual(105, elementOut.getIdentifier())
        assertAlmostEqualList(self, [0.346095, 1, 0.66399], xiOut, delta=TOL)

        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(20, len(annotationGroups))

        # test deleting a marker annotation group
        scaffoldPackage.deleteAnnotationGroup(fredGroup)

        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(19, len(annotationGroups))

        # check node fred has gone
        node = nodes.findNodeByIdentifier(fredNodeIdentifier)
        self.assertFalse(node.isValid())

        # re-recreate fred with just element:xi location
        fredGroup = scaffoldPackage.createUserAnnotationGroup(('fred', 'FRED:1'))
        element = mesh.findElementByIdentifier(105)
        node = fredGroup.createMarkerNode(nextNodeIdentifier, element=element, xi=[0.346095, 1, 0.66399])
        self.assertEqual(fredNodeIdentifier, node.getIdentifier())
        del node
        elementOut, xiOut = fredGroup.getMarkerLocation()
        self.assertEqual(105, elementOut.getIdentifier())
        assertAlmostEqualList(self, [0.346095, 1, 0.66399], xiOut, delta=TOL)

        # check the total number of groups
        annotationGroups = scaffoldPackage.getAnnotationGroups()
        self.assertEqual(20, len(annotationGroups))

        # test serialisation
        dct = scaffoldPackage.toDict()
        scaffoldType = Scaffolds().findScaffoldTypeByName(dct['scaffoldTypeName'])

        scaffoldPackage2 = ScaffoldPackage(scaffoldType, dct)
        region2 = context.createRegion()
        fieldmodule2 = region2.getFieldmodule()

        scaffoldPackage2.generate(region2)

        brainstemCoordinatesField2 = fieldmodule2.findFieldByName("brainstem coordinates")
        self.assertTrue(brainstemCoordinatesField2.isValid())

        annotationGroups2 = scaffoldPackage2.getAnnotationGroups()
        self.assertEqual(20, len(annotationGroups2))

        # check user markers have been defined correctly for scaffoldPackage2

        bobGroup2 = scaffoldPackage2.findAnnotationGroupByName('bob')
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = bobGroup2.getMarkerMaterialCoordinates()
        self.assertEqual(brainstemCoordinatesFieldOut, brainstemCoordinatesField2)
        assertAlmostEqualList(self, [-0.1, -0.5, 2.2], brainstemCoordinatesValueOut, delta=TOL)
        elementOut, xiOut = bobGroup2.getMarkerLocation()
        self.assertEqual(82, elementOut.getIdentifier())
        assertAlmostEqualList(self, [0.305595, 0.2, 0.485941], xiOut, delta=TOL)

        fredGroup2 = scaffoldPackage2.findAnnotationGroupByName('fred')
        brainstemCoordinatesFieldOut, brainstemCoordinatesValueOut = fredGroup2.getMarkerMaterialCoordinates()
        self.assertIsNone(brainstemCoordinatesFieldOut)
        self.assertIsNone(brainstemCoordinatesValueOut)
        elementOut, xiOut = fredGroup2.getMarkerLocation()
        self.assertEqual(105, elementOut.getIdentifier())
        assertAlmostEqualList(self, [0.346095, 1, 0.66399], xiOut, delta=TOL)