diff --git a/src/scaffoldmaker/annotation/bladder_terms.py b/src/scaffoldmaker/annotation/bladder_terms.py index 7deccd52..a7c9febc 100644 --- a/src/scaffoldmaker/annotation/bladder_terms.py +++ b/src/scaffoldmaker/annotation/bladder_terms.py @@ -44,7 +44,8 @@ ("urethra junction of ventral bladder neck", "ILX:0738410") ] -def get_bladder_term(name : str): + +def get_bladder_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -52,5 +53,5 @@ def get_bladder_term(name : str): """ for term in bladder_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Bladder annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/annotation/colon_terms.py b/src/scaffoldmaker/annotation/colon_terms.py index 6ac2951a..13ca29e9 100644 --- a/src/scaffoldmaker/annotation/colon_terms.py +++ b/src/scaffoldmaker/annotation/colon_terms.py @@ -4,29 +4,30 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names colon_terms = [ - ( "ascending colon", "UBERON:0001156", "FMA:14545", "ILX:0734393"), - ( "descending colon", "UBERON:0001158", "FMA:14547", "ILX:0724444"), - ( "caecum", "UBERON:0001153", "FMA:14541", "ILX:0732270"), - ( "circular muscle layer of colon", "ILX:0772428"), - ( "colon", "UBERON:0001155", "FMA:14543", "ILX:0736005"), - ( "colonic mucosa", "UBERON:0000317", "FMA:14984", "ILX:0731046"), - ( "distal colon", "UBERON:0008971", "ILX:0727523"), - ( "longitudinal muscle layer of colon", "ILX:0775554"), - ( "luminal surface of the colonic mucosa", "ILX:0793083"), - ( "mesenteric zone", "None"), - ( "non-mesenteric zone", "None"), - ( "proximal colon", "UBERON:0008972", "ILX:0733240"), - ( "serosa of colon", "UBERON:0003335", "FMA:14990", "ILX:0736932"), - ( "spiral colon", "UBERON:0010239", "ILX:0735018"), - ( "submucosa of colon", "UBERON:0003331", "FMA:14985", "ILX:0728041"), - ( "taenia coli", "UBERON:0012419", "FMA:15041", "ILX:0731555"), - ( "taenia libera", "ILX:0739285"), - ( "taenia mesocolica", "ILX:0739284"), - ( "taenia omentalis", "ILX:0739286"), - ( "transverse colon", "UBERON:0001157", "FMA:14546", "ILX:0728767") + ("ascending colon", "UBERON:0001156", "FMA:14545", "ILX:0734393"), + ("descending colon", "UBERON:0001158", "FMA:14547", "ILX:0724444"), + ("caecum", "UBERON:0001153", "FMA:14541", "ILX:0732270"), + ("circular muscle layer of colon", "ILX:0772428"), + ("colon", "UBERON:0001155", "FMA:14543", "ILX:0736005"), + ("colonic mucosa", "UBERON:0000317", "FMA:14984", "ILX:0731046"), + ("distal colon", "UBERON:0008971", "ILX:0727523"), + ("longitudinal muscle layer of colon", "ILX:0775554"), + ("luminal surface of the colonic mucosa", "ILX:0793083"), + ("mesenteric zone", "None"), + ("non-mesenteric zone", "None"), + ("proximal colon", "UBERON:0008972", "ILX:0733240"), + ("serosa of colon", "UBERON:0003335", "FMA:14990", "ILX:0736932"), + ("spiral colon", "UBERON:0010239", "ILX:0735018"), + ("submucosa of colon", "UBERON:0003331", "FMA:14985", "ILX:0728041"), + ("taenia coli", "UBERON:0012419", "FMA:15041", "ILX:0731555"), + ("taenia libera", "ILX:0739285"), + ("taenia mesocolica", "ILX:0739284"), + ("taenia omentalis", "ILX:0739286"), + ("transverse colon", "UBERON:0001157", "FMA:14546", "ILX:0728767") ] -def get_colon_term(name : str): + +def get_colon_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -34,5 +35,5 @@ def get_colon_term(name : str): """ for term in colon_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Colon annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/annotation/smallintestine_terms.py b/src/scaffoldmaker/annotation/smallintestine_terms.py index 63e1d00b..460de25c 100644 --- a/src/scaffoldmaker/annotation/smallintestine_terms.py +++ b/src/scaffoldmaker/annotation/smallintestine_terms.py @@ -4,13 +4,14 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names smallintestine_terms = [ - ( "duodenum", "UBERON:0002114", "FMA:7206", "ILX:0726125"), - ( "ileum", "UBERON:0002116", "FMA:7208", "ILX:0728151"), - ( "jejunum", "UBERON:0002115", "FMA:7207", "ILX:0724224"), - ( "small intestine", "UBERON:0002108", "FMA:7200", "ILX:0726770") + ("duodenum", "UBERON:0002114", "FMA:7206", "ILX:0726125"), + ("ileum", "UBERON:0002116", "FMA:7208", "ILX:0728151"), + ("jejunum", "UBERON:0002115", "FMA:7207", "ILX:0724224"), + ("small intestine", "UBERON:0002108", "FMA:7200", "ILX:0726770") ] -def get_smallintestine_term(name : str): + +def get_smallintestine_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -18,5 +19,5 @@ def get_smallintestine_term(name : str): """ for term in smallintestine_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Small intestine annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/annotation/stomach_terms.py b/src/scaffoldmaker/annotation/stomach_terms.py index b3f3bf48..f4b003d6 100644 --- a/src/scaffoldmaker/annotation/stomach_terms.py +++ b/src/scaffoldmaker/annotation/stomach_terms.py @@ -4,40 +4,81 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names stomach_terms = [ - ( "antrum on serosa split margin", "None"), - ( "body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), - ( "body on serosa split margin", "None"), - ( "cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), - ( "dorsal stomach", "None"), - ( "duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), - ( "duodenum on greater curvature", "None"), - ( "esophagus", "UBERON:0001043", "FMA: 7131", "ILX:0735017"), - ( "esophagus on serosa split margin", "None"), - ( "esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), - ( "forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), - ( "forestomach-glandular stomach junction on inner wall", "None"), - ( "forestomach-glandular stomach junction on outer wall", "None"), - ( "fundus of stomach", "UBERON:0001160", " FMA:14559", "ILX:0724443"), - ( "fundus on serosa split margin", "None"), - ( "gastro-esophagal junction on lesser curvature", "None"), - #( "greater curvature of stomach", "UBERON:0001164", "FMA: 14574", "ILX:0724395"), - ( "junction between fundus and body on greater curvature", "None"), - # ( "lesser curvature of stomach", "UBERON:0001163", "FMA: 14572", "ILX:0733753"), - ( "limiting ridge on greater curvature", "None"), - ( "mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), - ( "pylorus", "UBERON:0001166", " FMA:14581", "ILX:0734150"), - ( "pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), - ( "pylorus on greater curvature", "None"), - ( "pylorus on serosa split margin", "None"), - ( "serosa of dorsal stomach", "None"), - ( "serosa of stomach", "UBERON:0001201", "FMA:14914", "ILX:0735818"), - ( "serosa split margin", "None"), - ( "serosa of ventral stomach", "None"), - ( "stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), - ( "ventral stomach", "None"), + ("body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), + ("body-antrum junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793127"), + ("body-antrum junction along the greater curvature on luminal surface", "ILX:0793128"), + ("body-antrum junction along the greater curvature on serosa", "ILX:0793087"), + ("cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), + ("circular muscle layer of stomach", "ILX:0774731"), + ("circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment", "ILX:0793088"), + ("circular-longitudinal muscle interface of dorsal stomach", "ILX:0793089"), + ("circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment", "ILX:0793090"), + ("circular-longitudinal muscle interface of esophagus along the cut margin", "ILX:0793091"), + ("circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment", "ILX:0793092"), + ("circular-longitudinal muscle interface of gastroduodenal junction", "ILX:0793093"), + ("circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment", "ILX:0793094"), + ("circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment", "ILX:0793095"), + ("circular-longitudinal muscle interface of stomach", "ILX:0793096"), + ("circular-longitudinal muscle interface of ventral stomach", "ILX:0793097"), + ("dorsal stomach", "ILX:0793086"), + ("duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), + ("esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), + ("esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793098"), + ("esophagogastric junction along the greater curvature on luminal surface", "ILX:0793099"), + ("esophagogastric junction along the greater curvature on serosa", "ILX:0793100"), + ("esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", "ILX:0793101"), + ("esophagogastric junction along the lesser curvature on luminal surface", "ILX:0793102"), + ("esophagogastric junction along the lesser curvature on serosa", "ILX:0793103"), + ("esophagus", "UBERON:0001043", "FMA:7131", "ILX:0735017"), + ("esophagus mucosa", "UBERON:0002469", "FMA:62996", "ILX:0725079"), + ("esophagus smooth muscle circular layer", "UBERON:0009960", "FMA:67605", "ILX:0735992"), + ("esophagus smooth muscle longitudinal layer", "UBERON:0009961", "FMA:63573", "ILX:0727608"), + ("forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), + ("fundus of stomach", "UBERON:0001160", " FMA:14559", "ILX:0724443"), + ("fundus-body junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793104"), + ("fundus-body junction along the greater curvature on luminal surface", "ILX:0793105"), + ("fundus-body junction along the greater curvature on serosa", "ILX:0793106"), + ("gastroduodenal junction", "UBERON:0012650", "FMA:17046", "ILX:0725406"), + ("gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793107"), + ("gastroduodenal junction along the greater curvature on luminal surface", "ILX:0793108"), + ("gastroduodenal junction along the greater curvature on serosa", "ILX:0793109"), + ("gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", "ILX:0793110"), + ("gastroduodenal junction along the lesser curvature on luminal surface", "ILX:0793111"), + ("gastroduodenal junction along the lesser curvature on serosa", "ILX:0793112"), + ("limiting ridge along the greater curvature on circular-longitudinal muscle interface", "ILX:0793113"), + ("limiting ridge along the greater curvature on luminal surface", "ILX:0793114"), + ("limiting ridge along the greater curvature on serosa", "ILX:0793115"), + ("limiting ridge on circular-longitudinal muscle interface", "ILX:0793116"), + ("limiting ridge on luminal surface", "ILX:0793117"), + ("limiting ridge on serosa", "ILX:0793118"), + ("longitudinal muscle layer of stomach", "ILX:0772619"), + ("luminal surface of body of stomach", "ILX:0793119"), + ("luminal surface of cardia of stomach", "ILX:0793120"), + ("luminal surface of duodenum", "ILX:0793121"), + ("luminal surface of esophagus", "ILX:0793122"), + ("luminal surface of fundus of stomach", "ILX:0793123"), + ("luminal surface of pyloric antrum", "ILX:0793124"), + ("luminal surface of pyloric canal", "ILX:0793125"), + ("luminal surface of stomach", "ILX:0793126"), + ("mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), + ("pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), + ("pyloric canal", "UBERON:0008858", "FMA:14580", "ILX:0735898"), + ("serosa of body of stomach", "ILX:0771402"), + ("serosa of cardia of stomach", "ILX:0776646"), + ("serosa of duodenum", "UBERON:0003336", "FMA:14948", "ILX:0732373"), + ("serosa of esophagus", "UBERON:0001975", "FMA:63057", "ILX:0725745"), + ("serosa of fundus of stomach", "UBERON:0012503", "FMA:17073", "ILX:0726906"), + ("serosa of pyloric antrum", "ILX:0777005"), + ("serosa of pyloric canal", "ILX:0775898"), + ("serosa of stomach", "UBERON:0001201", "FMA:14914", "ILX:0735818"), + ("stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), + ("submucosa of esophagus", "UBERON:0001972", "FMA:62997", "ILX:0728662"), + ("submucosa of stomach", "UBERON:0001200", "FMA:14908", "ILX:0732950"), + ("ventral stomach", "ILX:0793085"), ] -def get_stomach_term(name : str): + +def get_stomach_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -45,5 +86,5 @@ def get_stomach_term(name : str): """ for term in stomach_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Stomach annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py index 61c888e2..f1a3faaf 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py @@ -1,17 +1,19 @@ -''' +""" Generates 3D bladder and urethra meshes along the central path, with variable numbers of elements around, along and through wall. -''' +""" import copy import math -from opencmiss.utils.zinc.field import findOrCreateFieldGroup, findOrCreateFieldNodeGroup, findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString +from opencmiss.utils.zinc.field import findOrCreateFieldGroup, findOrCreateFieldNodeGroup, \ + findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString from opencmiss.zinc.element import Element from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node -from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findOrCreateAnnotationGroupForTerm, getAnnotationGroupForTerm +from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findOrCreateAnnotationGroupForTerm, \ + getAnnotationGroupForTerm from scaffoldmaker.annotation.bladder_terms import get_bladder_term from scaffoldmaker.meshtypes.meshtype_1d_path1 import MeshType_1d_path1, extractPathParametersFromRegion from scaffoldmaker.meshtypes.meshtype_3d_ostium1 import MeshType_3d_ostium1, generateOstiumMesh @@ -29,10 +31,10 @@ class MeshType_3d_bladderurethra1(Scaffold_base): - ''' + """ Generates 3D bladder and urethra meshes with variable numbers of elements around, along the central path, and through the wall. - ''' + """ centralPathDefaultScaffoldPackages_LUT = { 'Cat 1': ScaffoldPackage(MeshType_1d_path1, { 'scaffoldSettings': { @@ -221,10 +223,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 2.2, 'Ostium length': 0.5, 'Ostium wall thickness': 0.5, + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.8, 'Vessel wall thickness': 0.25, + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -247,10 +251,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 3.0, 'Ostium length': 0.5, 'Ostium wall thickness': 0.5, + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 1.0, 'Vessel wall thickness': 0.1, + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -273,10 +279,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 1.3, 'Ostium length': 0.25, 'Ostium wall thickness': 0.3, + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.45, 'Vessel wall thickness': 0.1, + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -299,10 +307,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 2.0, 'Ostium length': 0.2, 'Ostium wall thickness': 0.4, + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.9, 'Vessel wall thickness': 0.2, + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -325,10 +335,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 1.0, 'Ostium length': 0.25, 'Ostium wall thickness': 0.02, + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.3, 'Vessel wall thickness': 0.1, + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -507,19 +519,21 @@ def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType): return list(cls.centralPathDefaultScaffoldPackages_LUT.keys()) if optionName == 'Ureter': return list(cls.ostiumDefaultScaffoldPackages.keys()) - assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), cls.__name__ + '.getOptionScaffoldTypeParameterSetNames. ' + \ - 'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName() + 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() + 'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \ + ' in option ' + str(optionName) + ' of scaffold ' + cls.getName() if optionName == 'Central path LUT': if not parameterSetName: parameterSetName = list(cls.centralPathDefaultScaffoldPackages_LUT.keys())[0] @@ -547,9 +561,6 @@ def checkOptions(cls, options): options[key] = 1 if options['Number of elements around'] % 2 != 0: options['Number of elements around'] += 1 - if options['Number of elements through wall'] > 1: - if options['Include ureter']: - options['Number of elements through wall'] = 1 if options['Ureter position around'] < 0.5: options['Ureter position around'] = 0.5 # ureters are on the dorsal part of the bladder elif options['Ureter position around'] > 0.9: @@ -561,22 +572,24 @@ def checkOptions(cls, options): @classmethod def updateSubScaffoldOptions(cls, options): - ''' + """ Update ostium sub-scaffold options which depend on parent options. - ''' + """ bladderWallThickness = options['Wall thickness'] + elementsCountThroughWall = options['Number of elements through wall'] ureterOptions = options['Ureter'] ureterDefaultOptions = ureterOptions.getScaffoldSettings() ureterDefaultOptions['Ostium wall thickness'] = bladderWallThickness + ureterDefaultOptions['Number of elements through wall'] = elementsCountThroughWall @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 - ''' + """ cls.updateSubScaffoldOptions(options) centralPath = options['Central path LUT'] elementsCountAlongBladder = options['Number of elements along bladder'] @@ -610,7 +623,6 @@ def generateBaseMesh(cls, region, options): fm = region.getFieldmodule() fm.beginChange() - nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) mesh = fm.findMeshByDimension(3) # Annotation fiducial point @@ -630,16 +642,20 @@ def generateBaseMesh(cls, region, options): # Bladder part tmpRegion = region.createRegion() centralPath.generate(tmpRegion) - cx_bladder, cd1_bladder, cd2_bladder, cd12_bladder = extractPathParametersFromRegion(tmpRegion, - [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], groupName='urinary bladder') + cx_bladder, cd1_bladder, cd2_bladder, cd12_bladder = \ + extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], + groupName='urinary bladder') # for i in range(len(cx_bladder)): # print(i, '[', cx_bladder[i], ',', cd1_bladder[i], ',', cd2_bladder[i], ',', cd12_bladder[i], '],') del tmpRegion # Urethra part tmpRegion = region.createRegion() centralPath.generate(tmpRegion) - cx_urethra, cd1_urethra, cd2_urethra, cd12_urethra = extractPathParametersFromRegion(tmpRegion, - [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], groupName='urethra') + cx_urethra, cd1_urethra, cd2_urethra, cd12_urethra = \ + extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], + groupName='urethra') # for i in range(len(cx_urethra)): # print(i, '[', cx_urethra[i], ',', cd1_urethra[i], ',', cd2_urethra[i], ',', cd12_urethra[i], '],') del tmpRegion @@ -649,14 +665,16 @@ def generateBaseMesh(cls, region, options): bladderLength = 0.0 elementsCountInBladder = len(cx_bladder) - 1 for e in range(elementsCountInBladder): - arcLength = interp.getCubicHermiteArcLength(cx_bladder[e], cd1_bladder[e], cx_bladder[e + 1], cd1_bladder[e + 1]) + arcLength = interp.getCubicHermiteArcLength(cx_bladder[e], cd1_bladder[e], + cx_bladder[e + 1], cd1_bladder[e + 1]) bladderLength += arcLength bladderSegmentLength = bladderLength / elementsCountAlongBladder # Urethra part urethraLength = 0.0 elementsCountInUrethra = len(cx_urethra) - 1 for e in range(elementsCountInUrethra): - arcLength = interp.getCubicHermiteArcLength(cx_urethra[e], cd1_urethra[e], cx_urethra[e + 1], cd1_urethra[e + 1]) + arcLength = interp.getCubicHermiteArcLength(cx_urethra[e], cd1_urethra[e], + cx_urethra[e + 1], cd1_urethra[e + 1]) urethraLength += arcLength urethraSegmentLength = urethraLength / elementsCountAlongUrethra @@ -749,7 +767,8 @@ def generateBaseMesh(cls, region, options): innerNodes_d1 += [nd1] * elementsCountAround for n1 in range(0, len(nx_max)): xAround, d1Around = createEllipsePoints([0.0, 0.0, nx_max[n1][2]], 2 * math.pi, [nx_max[n1][1], 0.0, 0.0], - [0.0, nx_min[n1][1], 0.0], elementsCountAround, startRadians=-math.pi/2) + [0.0, nx_min[n1][1], 0.0], elementsCountAround, + startRadians=-math.pi/2) innerNodes_x += xAround if n1 >= 1: innerNodes_d1 += d1Around @@ -894,7 +913,8 @@ def generateBaseMesh(cls, region, options): d2Final = [] d3Final = [] for n3 in range(elementsCountThroughWall + 1): - xApex = [xApexInner[c] + d3ApexUnit[c] * bladderWallThickness / elementsCountThroughWall * n3 for c in range(3)] + xApex = [xApexInner[c] + + d3ApexUnit[c] * bladderWallThickness / elementsCountThroughWall * n3 for c in range(3)] xFinal.append(xApex) d1Final.append(d1ApexInner) d2Final.append(d2ApexInner) @@ -918,9 +938,11 @@ def generateBaseMesh(cls, region, options): outerNodes_d2 = [] for n2 in range(elementsCountAlongBladder + 1): for n1 in range(elementsCountAround): - outerNodes_x.append(xList[(2 * n2 + 1) * elementsCountAround + n1]) - outerNodes_d1.append(d1List[(2 * n2 + 1) * elementsCountAround + n1]) - outerNodes_d2.append(d2List[(2 * n2 + 1) * elementsCountAround + n1]) + idx = n2 * (elementsCountThroughWall + 1) * elementsCountAround + \ + elementsCountThroughWall * elementsCountAround + n1 + outerNodes_x.append(xList[idx]) + outerNodes_d1.append(d1List[idx]) + outerNodes_d2.append(d2List[idx]) elementsCount1 = elementsCountAround // 2 elementsCount2 = elementsCountAlongBladder @@ -939,8 +961,9 @@ def generateBaseMesh(cls, region, options): ureter1Position = trackSurfaceUreter1.createPositionProportion(ureterPositionAround, ureterPositionDown) ureterElementPositionAround = ureter1Position.e1 ureterElementPositionDown = ureter1Position.e2 - elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) \ - + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) + elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * \ + (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) + \ + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) elementIndex = elementToDeleteStartIdx1 - elementsCountAround * ureterElementPositionDown @@ -959,9 +982,11 @@ def generateBaseMesh(cls, region, options): trackSurfaceUreter2 = TrackSurface(elementsCount1, elementsCount2, nodesOnTrackSurface2_x, nodesOnTrackSurface2_d1, nodesOnTrackSurface2_d2) - ureter2Position = TrackSurfacePosition(elementsCountAround // 2 - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0 else 0), + ureter2Position = TrackSurfacePosition(elementsCountAround // 2 - + ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0 else 0), ureterElementPositionDown, - (1 - ureter1Position.xi1) if ureter1Position.xi1 > 0 else ureter1Position.xi1, + (1 - ureter1Position.xi1) if ureter1Position.xi1 > 0 + else ureter1Position.xi1, ureter1Position.xi2) if includeUreter: @@ -1000,13 +1025,11 @@ def generateBaseMesh(cls, region, options): annotationGroupsThroughWall.append([]) neckMeshGroup = neckGroup. getMeshGroup(mesh) - bodyMeshGroup = bodyGroup.getMeshGroup(mesh) urinaryBladderMeshGroup = bladderGroup.getMeshGroup(mesh) - urethraMeshGroup = urethraGroup. getMeshGroup(mesh) ureterMeshGroup = ureterGroup. getMeshGroup(mesh) # Create nodes and elements - nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements( + nodeIdentifier, elementIdentifier, annotationGroups = tubemesh.createNodesAndElements( region, xFinal, d1Final, d2Final, d3Final, xFlat, d1Flat, d2Flat, xOrgan, d1Organ, d2Organ, None, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, @@ -1052,45 +1075,61 @@ def generateBaseMesh(cls, region, options): if includeUreter: bladderMeshGroup = [neckMeshGroup, urinaryBladderMeshGroup, dorsalBladderMeshGroup] - generateUreterInlets(region, nodes, mesh, ureterDefaultOptions, elementsCountAround, elementsCountThroughWall, - elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, trackSurfaceUreter2, - ureter2Position, ureterElementPositionDown, ureterElementPositionAround, xFinal, d1Final, - d2Final, nextNodeIdentifier, nextElementIdentifier, elementsCountUreterRadial, - ureterMeshGroup, bladderMeshGroup) + nodeIdentifier = generateUreterInlets(region, nodes, mesh, ureterDefaultOptions, elementsCountAround, + elementsCountThroughWall, + elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, + trackSurfaceUreter2, + ureter2Position, ureterElementPositionDown, + ureterElementPositionAround, xFinal, d1Final, + d2Final, nodeIdentifier, elementIdentifier, + elementsCountUreterRadial, + ureterMeshGroup, bladderMeshGroup) # Define markers for apex, ureter and urethra junctions with bladder - apexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("apex of urinary bladder")) - leftUreterGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("left ureter junction with bladder")) - rightUreterGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("right ureter junction with bladder")) - dorsalUrethraGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("urethra junction of dorsal bladder neck")) - ventralUrethraGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("urethra junction of ventral bladder neck")) + apexGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("apex of urinary bladder")) + leftUreterGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("left ureter junction with bladder")) + rightUreterGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("right ureter junction with bladder")) + dorsalUrethraGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("urethra junction of dorsal bladder neck")) + ventralUrethraGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("urethra junction of ventral bladder neck")) idx1 = 1 xi1 = [0.0, 0.0, 0.0] markerList = [] markerList.append({"group": apexGroup, "elementId": idx1, "xi": xi1}) if includeUreter: - idx2 = elementsCountAlong * elementsCountAround + elementsCountAroundUreter + idx2 = elementsCountAlong * elementsCountAround * elementsCountThroughWall + elementsCountAroundUreter xi2 = [0.0, 1.0, 0.0] markerList.append({"group": leftUreterGroup, "elementId": idx2, "xi": xi2}) - idx3 = elementsCountAlong * elementsCountAround + 2 * elementsCountAroundUreter + idx3 = elementsCountAlong * elementsCountAround * elementsCountThroughWall + \ + elementsCountThroughWall * elementsCountAroundUreter + elementsCountAroundUreter xi3 = [0.0, 1.0, 0.0] markerList.append({"group": rightUreterGroup, "elementId": idx3, "xi": xi3}) else: - idx2 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + ureterElementPositionAround + 1 + idx2 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + \ + ureterElementPositionAround + 1 xi2 = [ureter1Position.xi1, ureter1Position.xi2, 0.0] markerList.append({"group": leftUreterGroup, "elementId": idx2, "xi": xi2}) - idx3 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + elementsCountAround - ureterElementPositionAround + idx3 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + \ + elementsCountAround - ureterElementPositionAround xi3 = [1 - ureter1Position.xi1, ureter1Position.xi2, 0.0] markerList.append({"group": rightUreterGroup, "elementId": idx3, "xi": xi3}) - idx4 = (elementsCountAlongBladder - 1) * elementsCountAround * elementsCountThroughWall + elementsCountAround // 2 + idx4 = (elementsCountAlongBladder - 1) * elementsCountAround * elementsCountThroughWall + \ + elementsCountAround // 2 xi4 = [1.0, 1.0, 0.0] markerList.append({"group": dorsalUrethraGroup, "elementId": idx4, "xi": xi4}) idx5 = (elementsCountAlongBladder - 1) * elementsCountAround * elementsCountThroughWall + 1 xi5 = [0.0, 1.0, 0.0] markerList.append({"group": ventralUrethraGroup, "elementId": idx5, "xi": xi5}) - nodeIdentifier = nextNodeIdentifier + elementsCountAroundUreter * 4 * 2 + elementsCountAroundUreter * 2 * 2 * elementsCountUreterRadial bladderNodesetGroup = bladderGroup.getNodesetGroup(nodes) for marker in markerList: annotationGroup = marker["group"] @@ -1107,11 +1146,11 @@ def generateBaseMesh(cls, region, options): @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(). - ''' + """ refineElementsCountAlong = options['Refine number of elements along'] refineElementsCountAround = options['Refine number of elements around'] refineElementsCountThroughWall = options['Refine number of elements through wall'] @@ -1122,14 +1161,14 @@ def refineMesh(cls, meshrefinement, options): @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() bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("dome of the bladder")) @@ -1159,72 +1198,105 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_dorsal_bladder = bladderDorsalGroup.getFieldElementGroup(mesh2d) is_ventral_bladder = bladderVentralGroup.getFieldElementGroup(mesh2d) - serosaOfUrinaryBladder = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of urinary bladder")) + serosaOfUrinaryBladder = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of urinary bladder")) serosaOfUrinaryBladder.getMeshGroup(mesh2d).addElementsConditional(is_urinaryBladder_serosa) - lumenOfUrinaryBladder = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("bladder lumen")) + lumenOfUrinaryBladder = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("bladder lumen")) lumenOfUrinaryBladder.getMeshGroup(mesh2d).addElementsConditional(is_urinaryBladder_lumen) - serosaOfBody = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of body of urinary bladder")) + 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 = \ + 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 = 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 = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("lumen of neck of urinary bladder")) lumenOfNeck.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen) is_bladder_serosa_dorsal = fm.createFieldAnd(is_urinaryBladder_serosa, is_dorsal_bladder) - serosaOfBladder_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of urinary bladder")) + serosaOfBladder_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of urinary bladder")) serosaOfBladder_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_bladder_serosa_dorsal) is_bladder_serosa_ventral = fm.createFieldAnd(is_urinaryBladder_serosa, is_ventral_bladder) - serosaOfBladder_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of urinary bladder")) + serosaOfBladder_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of urinary bladder")) serosaOfBladder_ventral.getMeshGroup(mesh2d).addElementsConditional(is_bladder_serosa_ventral) is_bladder_lumen_dorsal = fm.createFieldAnd(is_urinaryBladder_lumen, is_dorsal_bladder) - lumenOfBladder_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of urinary bladder lumen")) + lumenOfBladder_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of urinary bladder lumen")) lumenOfBladder_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_bladder_lumen_dorsal) is_bladder_lumen_ventral = fm.createFieldAnd(is_urinaryBladder_lumen, is_ventral_bladder) - lumenOfBladder_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of urinary bladder lumen")) + lumenOfBladder_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of urinary bladder lumen")) lumenOfBladder_ventral.getMeshGroup(mesh2d).addElementsConditional(is_bladder_lumen_ventral) is_body_serosa_dorsal = fm.createFieldAnd(is_body_serosa, is_dorsal_bladder) - serosaOfBody_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of body of urinary bladder")) + serosaOfBody_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of body of urinary bladder")) serosaOfBody_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_body_serosa_dorsal) is_body_serosa_ventral = fm.createFieldAnd(is_body_serosa, is_ventral_bladder) - serosaOfBody_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of body of urinary bladder")) + serosaOfBody_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of body of urinary bladder")) serosaOfBody_ventral.getMeshGroup(mesh2d).addElementsConditional(is_body_serosa_ventral) is_body_lumen_dorsal = fm.createFieldAnd(is_body_lumen, is_dorsal_bladder) - lumenOfBody_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of lumen of body of urinary bladder")) + lumenOfBody_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of lumen of body of urinary bladder")) lumenOfBody_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_body_lumen_dorsal) is_body_lumen_ventral = fm.createFieldAnd(is_body_lumen, is_ventral_bladder) - lumenOfBody_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of lumen of body of urinary bladder")) + lumenOfBody_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of lumen of body of urinary bladder")) lumenOfBody_ventral.getMeshGroup(mesh2d).addElementsConditional(is_body_lumen_ventral) is_neck_serosa_dorsal = fm.createFieldAnd(is_neck_serosa, is_dorsal_bladder) - serosaOfNeck_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of neck of urinary bladder")) + serosaOfNeck_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of neck of urinary bladder")) serosaOfNeck_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_neck_serosa_dorsal) is_neck_serosa_ventral = fm.createFieldAnd(is_neck_serosa, is_ventral_bladder) - serosaOfNeck_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of neck of urinary bladder")) + serosaOfNeck_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of neck of urinary bladder")) serosaOfNeck_ventral.getMeshGroup(mesh2d).addElementsConditional(is_neck_serosa_ventral) is_neck_lumen_dorsal = fm.createFieldAnd(is_neck_lumen, is_dorsal_bladder) - lumenOfNeck_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of lumen of neck of urinary bladder")) + lumenOfNeck_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of lumen of neck of urinary bladder")) lumenOfNeck_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen_dorsal) is_neck_lumen_ventral = fm.createFieldAnd(is_neck_lumen, is_ventral_bladder) - lumenOfNeck_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of lumen of neck of urinary bladder")) + lumenOfNeck_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of lumen of neck of urinary bladder")) lumenOfNeck_ventral.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen_ventral) if options['Include urethra'] == True: urethraGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("urethra")) - urethraVentralGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("ventral part of urethra")) + urethraVentralGroup = getAnnotationGroupForTerm(annotationGroups, + get_bladder_term("ventral part of urethra")) urethraDorsalGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("dorsal part of urethra")) is_urethra = urethraGroup.getFieldElementGroup(mesh2d) @@ -1234,41 +1306,60 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_dorsal_urethra = urethraDorsalGroup.getFieldElementGroup(mesh2d) is_ventral_urethra = urethraVentralGroup.getFieldElementGroup(mesh2d) - serosaOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of urethra")) + serosaOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("serosa of urethra")) serosaOfUrethra.getMeshGroup(mesh2d).addElementsConditional(is_urethra_serosa) - lumenOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("lumen of urethra")) + lumenOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("lumen of urethra")) lumenOfUrethra.getMeshGroup(mesh2d).addElementsConditional(is_urethra_lumen) is_urethra_serosa_dorsal = fm.createFieldAnd(is_urethra_serosa, is_dorsal_urethra) - serosaOfUrethra_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of urethra")) + serosaOfUrethra_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of urethra")) serosaOfUrethra_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_urethra_serosa_dorsal) is_urethra_serosa_ventral = fm.createFieldAnd(is_urethra_serosa, is_ventral_urethra) - serosaOfUrethra_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of urethra")) + serosaOfUrethra_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of urethra")) serosaOfUrethra_ventral.getMeshGroup(mesh2d).addElementsConditional(is_urethra_serosa_ventral) is_urethra_lumen_dorsal = fm.createFieldAnd(is_urethra_lumen, is_dorsal_urethra) - lumenOfUrethra_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of lumen of urethra")) + lumenOfUrethra_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of lumen of urethra")) lumenOfUrethra_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_urethra_lumen_dorsal) is_urethra_lumen_ventral = fm.createFieldAnd(is_urethra_lumen, is_ventral_urethra) - lumenOfUrethra_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of lumen of urethra")) + lumenOfUrethra_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of lumen of urethra")) lumenOfUrethra_ventral.getMeshGroup(mesh2d).addElementsConditional(is_urethra_lumen_ventral) -def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCountAround, elementsCountThroughWall, - elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, trackSurfaceUreter2, - ureter2Position, ureterElementPositionDown, ureterElementPositionAround, - xBladder, d1Bladder, d2Bladder, nextNodeIdentifier, nextElementIdentifier, - elementsCountUreterRadial, ureterMeshGroup, bladderMeshGroup): + +def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions, elementsCountAround, elementsCountThroughWall, + elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, trackSurfaceUreter2, + ureter2Position, ureterElementPositionDown, ureterElementPositionAround, + xBladder, d1Bladder, d2Bladder, nextNodeIdentifier, nextElementIdentifier, + elementsCountUreterRadial, ureterMeshGroup, bladderMeshGroup): + + # Update ostium and vessel wall relative thickness with elementsCountThroughWall + # Set to uniform layer thicknesses now, can be changed to varying thickness later + ureterDefaultOptions['Ostium wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * \ + elementsCountThroughWall + ureterDefaultOptions['Vessel wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * \ + elementsCountThroughWall # Create ureters on the surface # Ureter 1 - centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = trackSurfaceUreter1.evaluateCoordinates(ureter1Position, - derivatives=True) + centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = \ + trackSurfaceUreter1.evaluateCoordinates(ureter1Position, derivatives=True) td1, td2, td3 = calculate_surface_axes(centerUreter1_d1, centerUreter1_d2, [1.0, 0.0, 0.0]) - endPointStartId1 = elementsCountThroughWall + 1 \ - + (elementsCountThroughWall + 1) * elementsCountAround * (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) \ - + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) + endPointStartId1 = \ + elementsCountThroughWall + 1 + (elementsCountThroughWall + 1) * elementsCountAround * \ + (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) + \ + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) ureter1StartCornerx = xBladder[endPointStartId1 - 1] v1 = [(ureter1StartCornerx[c] - centerUreter1_x[c]) for c in range(3)] ureter1Direction = vector.crossproduct3(td3, v1) @@ -1281,10 +1372,10 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount centerUreter2_x, centerUreter2_d1, centerUreter2_d2 = trackSurfaceUreter2.evaluateCoordinates(ureter2Position, derivatives=True) ad1, ad2, ad3 = calculate_surface_axes(centerUreter2_d1, centerUreter2_d2, [1.0, 0.0, 0.0]) - endPointStartId2 = elementsCountThroughWall + 1 \ - + (elementsCountThroughWall + 1) * elementsCountAround * \ - (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) \ - + elementsCountAround - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0.5 else 0) + endPointStartId2 = elementsCountThroughWall + 1 + \ + (elementsCountThroughWall + 1) * elementsCountAround * \ + (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) + \ + elementsCountAround - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0.5 else 0) ureter2StartCornerx = xBladder[endPointStartId2 - 1] v2 = [(ureter2StartCornerx[c] - centerUreter2_x[c]) for c in range(3)] ureter2Direction = vector.crossproduct3(ad3, v2) @@ -1294,15 +1385,15 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount vesselMeshGroups=[[ureterMeshGroup]], ostiumMeshGroups=bladderMeshGroup) # Create annulus mesh around ureters - endPoints1_x = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints1_d1 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints1_d2 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endNode1_Id = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endDerivativesMap = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints2_x = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints2_d1 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints2_d2 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endNode2_Id = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] + endPoints1_x = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints1_d1 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints1_d2 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endNode1_Id = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endDerivativesMap = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints2_x = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints2_d1 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints2_d2 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endNode2_Id = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] count = 0 for n2 in range(3): @@ -1311,23 +1402,21 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount count += 1 for n1 in range(2): endNode1_Id[0][count] = endNode1_Id[0][count - 1] + 1 - endNode2_Id[0][count] = endNode2_Id[0][count - 1] + 1 - \ - (0 if (endNode2_Id[0][count - 1] - 2) % elementsCountAround > 0 - else elementsCountAround) + endNode2_Id[0][count] = endNode2_Id[0][count - 1] + 1 count += 1 for n2 in range(2): endNode1_Id[0][count] = endNode1_Id[0][count - 1] - (elementsCountThroughWall + 1) * elementsCountAround endNode2_Id[0][count] = endNode2_Id[0][count - 1] - (elementsCountThroughWall + 1) * elementsCountAround count += 1 endNode1_Id[0][count] = endNode1_Id[0][count - 1] - 1 - endNode2_Id[0][count] = endNode2_Id[0][count - 1] - 1 + \ - (0 if (endNode2_Id[0][count - 1] - 3) % elementsCountAround > 0 else elementsCountAround) + endNode2_Id[0][count] = endNode2_Id[0][count - 1] - 1 - for n in range(len(endNode1_Id[0])): - endNode1_Id[1][n] = endNode1_Id[0][n] + elementsCountAround - endNode2_Id[1][n] = endNode2_Id[0][n] + elementsCountAround + for n3 in range(1, elementsCountThroughWall + 1): + for n in range(len(endNode1_Id[0])): + endNode1_Id[n3][n] = endNode1_Id[0][n] + elementsCountAround * n3 + endNode2_Id[n3][n] = endNode2_Id[0][n] + elementsCountAround * n3 - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountAroundUreter): nc1 = endNode1_Id[n3][n1] - 1 endPoints1_x[n3][n1] = xBladder[nc1] @@ -1338,31 +1427,24 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount endPoints2_d1[n3][n1] = d1Bladder[nc2] endPoints2_d2[n3][n1] = d2Bladder[nc2] - for n1 in range(elementsCountAroundUreter): - 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) + for n3 in range(elementsCountThroughWall + 1): + for n1 in range(elementsCountAroundUreter): + if n1 == 0: + endDerivativesMap[n3][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0)) + elif n1 == 1: + endDerivativesMap[n3][n1] = ((0, 1, 0), (-1, 0, 0), None) + elif n1 == 2: + endDerivativesMap[n3][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) + elif n1 == 3: + endDerivativesMap[n3][n1] = ((1, 0, 0), (0, 1, 0), None) + elif n1 == 4: + endDerivativesMap[n3][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) + elif n1 == 5: + endDerivativesMap[n3][n1] = ((0, -1, 0), (1, 0, 0), None) + elif n1 == 6: + endDerivativesMap[n3][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0)) + else: + endDerivativesMap[n3][n1] = ((-1, 0, 0), (0, -1, 0), None) startProportions1 = [] for n in range(len(o1_Positions)): @@ -1385,9 +1467,9 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount firstIdxAround2 = elementsCountAround//2 - ureterElementPositionAround - (2 if ureter1Position.xi1 > 0.5 else 1) for n in range(3): - endProportions1.append([(firstIdxAround1)/elementsAroundTrackSurface1, + endProportions1.append([firstIdxAround1/elementsAroundTrackSurface1, (firstIdxAlong + n)/elementsAlongTrackSurface1]) - endProportions2.append([(firstIdxAround2) / elementsAroundTrackSurface2, + endProportions2.append([firstIdxAround2 / elementsAroundTrackSurface2, (firstIdxAlong + n) / elementsAlongTrackSurface2]) for n in range(2): endProportions1.append([(firstIdxAround1 + n + 1) / elementsAroundTrackSurface1, @@ -1399,7 +1481,7 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount (firstIdxAlong - n + 1) / elementsAlongTrackSurface1]) endProportions2.append([(firstIdxAround2 + 2) / elementsAroundTrackSurface2, (firstIdxAlong - n + 1) / elementsAlongTrackSurface2]) - endProportions1.append([(firstIdxAround1 + 1 )/ elementsAroundTrackSurface1, + endProportions1.append([(firstIdxAround1 + 1) / elementsAroundTrackSurface1, firstIdxAlong / elementsAlongTrackSurface1]) endProportions2.append([(firstIdxAround2 + 1) / elementsAroundTrackSurface2, firstIdxAlong / elementsAlongTrackSurface2]) @@ -1422,18 +1504,24 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount # Delete elements under annulus mesh element_identifiers = [] - elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) \ - + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) - elementToDeleteStartIdx2 = elementsCountThroughWall * elementsCountAround * (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) \ - + elementsCountAround - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0.5 else 0) + elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * \ + (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) + \ + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) + elementToDeleteStartIdx2 = elementsCountThroughWall * elementsCountAround * \ + (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) + \ + elementsCountAround - ureterElementPositionAround + \ + (-1 if ureter1Position.xi1 > 0.5 else 0) elementToDeleteStartIdxList = [elementToDeleteStartIdx1, elementToDeleteStartIdx2] for i in range(2): elementToDeleteStart = elementToDeleteStartIdxList[i] - elementsToDelete = [elementToDeleteStart, elementToDeleteStart + 1, - elementToDeleteStart + elementsCountAround, - elementToDeleteStart + 1 + elementsCountAround] - element_identifiers += elementsToDelete + baseElementsToDelete = [elementToDeleteStart, + elementToDeleteStart + 1, + elementToDeleteStart + elementsCountAround * elementsCountThroughWall, + elementToDeleteStart + 1 + elementsCountAround * elementsCountThroughWall] + for e3 in range(elementsCountThroughWall): + elementsToDelete = [c + elementsCountAround * e3 for c in baseElementsToDelete] + element_identifiers += elementsToDelete mesh_destroy_elements_and_nodes_by_identifiers(mesh, element_identifiers) - return + return nodeIdentifier diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py index 8fe0f004..4ae1810c 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py @@ -12,8 +12,8 @@ from opencmiss.zinc.element import Element from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node -from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, mergeAnnotationGroups, findOrCreateAnnotationGroupForTerm, findAnnotationGroupByName, \ - getAnnotationGroupForTerm +from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, mergeAnnotationGroups, \ + findOrCreateAnnotationGroupForTerm, findAnnotationGroupByName, getAnnotationGroupForTerm from scaffoldmaker.annotation.colon_terms import get_colon_term from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils import interpolation as interp @@ -26,7 +26,7 @@ class MeshType_3d_colonsegment1(Scaffold_base): - ''' + """ Generates a single 3-D colon segment mesh with variable numbers of tenia coli, elements around, along the central line, and through wall. The cross-section profile of the colon @@ -37,7 +37,7 @@ class MeshType_3d_colonsegment1(Scaffold_base): Human (Default): Three tenia coli, triangular profile with rounded corners at the inter-haustral septa, and a clover profile in the intra-haustral region. - ''' + """ @staticmethod def getName(): @@ -74,12 +74,12 @@ def getDefaultOptions(parameterSetName='Default'): 'Start tenia coli width derivative': 0.0, 'End tenia coli width': 10.0, 'End tenia coli width derivative': 0.0, - 'Tenia coli thickness': 1.6, + 'Tenia coli thickness': 0.6, 'Wall thickness': 1.6, - 'Mucosa relative thickness': 0.3, - 'Submucosa relative thickness': 0.3, - 'Circular muscle layer relative thickness': 0.3, - 'Longitudinal muscle layer relative thickness': 0.1, + 'Mucosa relative thickness': 0.18, + 'Submucosa relative thickness': 0.25, + 'Circular muscle layer relative thickness': 0.52, + 'Longitudinal muscle layer relative thickness': 0.05, 'Use cross derivatives': False, 'Use linear through wall': True, 'Refine': False, @@ -344,10 +344,10 @@ def generateBaseMesh(cls, region, options): [circularMuscleGroup], [longitudinalMuscleGroup]] # Create coordinates and derivatives - xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, - d2WarpedList, d3WarpedUnitList, contractedWallThicknessList, relativeThicknessList, - elementsCountAround, elementsCountAlongSegment, elementsCountThroughWall, - transitElementList) + xList, d1List, d2List, d3List, curvatureList = \ + tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList, + contractedWallThicknessList, relativeThicknessList, elementsCountAround, + elementsCountAlongSegment, elementsCountThroughWall, transitElementList) xColonSegment = d1ColonSegment = d2ColonSegment = [] @@ -406,14 +406,14 @@ def refineMesh(cls, meshrefinement, options): @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) @@ -427,7 +427,8 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): serosa = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_colon_term("serosa of colon")) serosa.getMeshGroup(mesh2d).addElementsConditional(is_serosa) is_mucosaInnerSurface = fm.createFieldAnd(is_colon, is_exterior_face_xi3_0) - mucosaInnerSurface = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_colon_term("luminal surface of the colonic mucosa")) + mucosaInnerSurface = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_colon_term("luminal surface of the colonic mucosa")) mucosaInnerSurface.getMeshGroup(mesh2d).addElementsConditional(is_mucosaInnerSurface) @@ -477,8 +478,9 @@ def getColonSegmentTubeMeshInnerPoints(self, nSegment): xInner, d1Inner, d2Inner, transitElementList, xiSegment, relaxedLengthSegment, contractedWallThicknessSegment, \ segmentAxis, annotationGroupsAround = \ getColonSegmentInnerPoints(self._region, - self._elementsCountAroundTC, self._elementsCountAroundHaustrum, self._elementsCountAlongSegment, - self._tcCount, self._segmentLengthEndDerivativeFactor, self._segmentLengthMidDerivativeFactor, + self._elementsCountAroundTC, self._elementsCountAroundHaustrum, + self._elementsCountAlongSegment, self._tcCount, + self._segmentLengthEndDerivativeFactor, self._segmentLengthMidDerivativeFactor, self._segmentLength, self._wallThickness, self._cornerInnerRadiusFactor, haustrumInnerRadiusFactorSegmentList, radiusSegmentList, dRadiusSegmentList, tcWidthSegmentList, @@ -497,8 +499,7 @@ def getColonSegmentTubeMeshInnerPoints(self, nSegment): contractedWallThickness = contractedWallThicknessSegment[startIdx:self._elementsCountAlongSegment + 1] self._contractedWallThicknessList += contractedWallThickness - return xInner, d1Inner, d2Inner, transitElementList, segmentAxis, \ - annotationGroupsAround + return xInner, d1Inner, d2Inner, transitElementList, segmentAxis, annotationGroupsAround def getTubeTCWidthList(self): return self._tubeTCWidthList @@ -510,13 +511,11 @@ def getContractedWallThicknessList(self): return self._contractedWallThicknessList -def getColonSegmentInnerPoints(region, elementsCountAroundTC, - elementsCountAroundHaustrum, elementsCountAlongSegment, - tcCount, segmentLengthEndDerivativeFactor, - segmentLengthMidDerivativeFactor, segmentLength, wallThickness, - cornerInnerRadiusFactor, haustrumInnerRadiusFactorSegmentList, - radiusSegmentList, dRadiusSegmentList, tcWidthSegmentList, - startPhase): +def getColonSegmentInnerPoints(region, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlongSegment, + tcCount, segmentLengthEndDerivativeFactor, segmentLengthMidDerivativeFactor, + segmentLength, wallThickness, cornerInnerRadiusFactor, + haustrumInnerRadiusFactorSegmentList, radiusSegmentList, dRadiusSegmentList, + tcWidthSegmentList, startPhase): """ Generates a 3-D colon segment mesh with variable numbers of tenia coli, numbers of elements around, along the central path, and through wall. @@ -572,9 +571,6 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, transitElementList = transitElementListHaustrum * tcCount # create nodes - x = [0.0, 0.0, 0.0] - dx_ds1 = [0.0, 0.0, 0.0] - dx_ds2 = [0.0, 0.0, 0.0] sampleElementOut = 20 segmentAxis = [0.0, 0.0, 1.0] @@ -598,8 +594,9 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, radius = radiusSegmentList[n2] tcWidth = tcWidthSegmentList[n2] - xHalfSet, d1HalfSet = createHalfSetInterHaustralSegment(elementsCountAroundTC, - elementsCountAroundHaustrum, tcCount, tcWidth, radius, cornerInnerRadiusFactor, sampleElementOut) + xHalfSet, d1HalfSet = \ + createHalfSetInterHaustralSegment(elementsCountAroundTC, elementsCountAroundHaustrum, tcCount, tcWidth, + radius, cornerInnerRadiusFactor, sampleElementOut) for i in range(len(xHalfSet)): d2HalfSet.append([0.0, 0.0, 0.0]) @@ -780,7 +777,8 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, dx_ds1InnerAroundList = [] if startPhase == 0.0 and n2 == 0: d1Corrected = d1Phase0FirstFace - elif startPhase == 0.0 and elementsCountAlongSegment % 2 == 0 and n2 == int(elementsCountAlongSegment * 0.5): + elif startPhase == 0.0 and elementsCountAlongSegment % 2 == 0 and \ + n2 == int(elementsCountAlongSegment * 0.5): dx_ds1InnerAroundList = dx_ds1InnerAroundList + d1Phase0MidFace elif startPhase == 0.0 and n2 > elementsCountAlongSegment - 1: d1Corrected = d1Phase0LastFace @@ -789,7 +787,8 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, dx_ds1InnerAroundList = dx_ds1InnerAroundList + d1180FirstFace elif startPhase == 180.0 and n2 > elementsCountAlongSegment - 1: dx_ds1InnerAroundList = dx_ds1InnerAroundList + d1180LastFace - elif startPhase == 180.0 and elementsCountAlongSegment % 2 == 0 and n2 == int(elementsCountAlongSegment * 0.5): + elif startPhase == 180.0 and elementsCountAlongSegment % 2 == 0 and \ + n2 == int(elementsCountAlongSegment * 0.5): d1Corrected = d1180MidFace else: @@ -824,9 +823,10 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, tcCount) # Calculate xiList for relaxed state - xHalfSetRelaxed, d1HalfSetRelaxed = createHalfSetIntraHaustralSegment(elementsCountAroundTC, - elementsCountAroundHaustrum, tcCount, tcWidthSegmentList[n2], radiusSegmentList[n2], - cornerInnerRadiusFactor, sampleElementOut, haustrumInnerRadiusFactor) + xHalfSetRelaxed, d1HalfSetRelaxed = \ + createHalfSetIntraHaustralSegment(elementsCountAroundTC, elementsCountAroundHaustrum, tcCount, + tcWidthSegmentList[n2], radiusSegmentList[n2], + cornerInnerRadiusFactor, sampleElementOut, haustrumInnerRadiusFactor) xRelaxed, d1Relaxed, _ = getFullProfileFromHalfHaustrum(xHalfSetRelaxed, d1HalfSetRelaxed, d2Around, tcCount) xiFace, relaxedLengthAroundFace = getXiListFromOuterLengthProfile(xRelaxed, d1Relaxed, segmentAxis, @@ -923,7 +923,8 @@ def createHalfSetInterHaustralSegment(elementsCountAroundTC, elementsCountAround # Sample haustrum into equally sized elements xHaustrum, d1Haustrum, _, _ = sampleHaustrum(xLoop, d1Loop, xTC[-1], d1TC[-1], - arcLength / numberOfHalves, arcDistanceTCEdge, elementsCountAroundHaustrum) + arcLength / numberOfHalves, arcDistanceTCEdge, + elementsCountAroundHaustrum) xHalfSetInterHaustra = xHalfSetInterHaustra + xTC + xHaustrum[1:] d1HalfSetInterHaustra = d1HalfSetInterHaustra + d1TC + d1Haustrum[1:] @@ -932,7 +933,8 @@ def createHalfSetInterHaustralSegment(elementsCountAroundTC, elementsCountAround def createHalfSetIntraHaustralSegment(elementsCountAroundTC, elementsCountAroundHaustrum, - tcCount, tcWidth, radius, cornerInnerRadiusFactor, sampleElementOut, haustrumInnerRadiusFactor): + tcCount, tcWidth, radius, cornerInnerRadiusFactor, sampleElementOut, + haustrumInnerRadiusFactor): """ Find locations and derivative of nodes in half of an intra-haustral segment. Bow-tie profile for segment with two tenia coli and @@ -1295,8 +1297,9 @@ def getXiListFromOuterLengthProfile(xInner, d1Inner, segmentAxis, arcLengthList = [] for n1 in range(len(xOuter)): - arcLengthPerElement = interp.getCubicHermiteArcLength(xOuter[n1], d1Outer[n1], - xOuter[(n1 + 1) % len(xOuter)], d1Outer[(n1 + 1) % len(xOuter)]) + arcLengthPerElement = \ + interp.getCubicHermiteArcLength(xOuter[n1], d1Outer[n1], + xOuter[(n1 + 1) % len(xOuter)], d1Outer[(n1 + 1) % len(xOuter)]) arcLengthList.append(arcLengthPerElement) # Total arcLength @@ -1361,8 +1364,9 @@ def getTeniaColi(region, xList, d1List, d2List, d3List, curvatureList, d3TCRaw = [] tcWidth = tubeTCWidthList[n2] for N in range(tcCount): - idxTCMid = elementsCountThroughWall + 1 + (n2 - 1) * elementsCountAround * (elementsCountThroughWall + 1) \ - + elementsCountAround * elementsCountThroughWall + \ + idxTCMid = elementsCountThroughWall + 1 + (n2 - 1) * \ + elementsCountAround * (elementsCountThroughWall + 1) + \ + elementsCountAround * elementsCountThroughWall + \ N * (elementsCountAroundTC + elementsCountAroundHaustrum) if closedProximalEnd \ else n2 * elementsCountAround * (elementsCountThroughWall + 1) + \ elementsCountAround * elementsCountThroughWall + \ @@ -1371,7 +1375,8 @@ def getTeniaColi(region, xList, d1List, d2List, d3List, curvatureList, xMid = [xList[idxTCMid][i] + unitNorm[i] * tcThickness for i in range(3)] d1Mid = d1List[idxTCMid] TCStartIdx = idxTCMid - int(elementsCountAroundTC * 0.5) if N > 0 else \ - idxTCMid + tcCount * (elementsCountAroundTC + elementsCountAroundHaustrum) - int(elementsCountAroundTC * 0.5) + idxTCMid + tcCount * (elementsCountAroundTC + elementsCountAroundHaustrum) - \ + int(elementsCountAroundTC * 0.5) TCEndIdx = idxTCMid + int(elementsCountAroundTC * 0.5) if closedProximalEnd: tcWidth = vector.magnitude([xList[TCStartIdx][c] - xList[TCEndIdx][c] for c in range(3)]) @@ -1560,7 +1565,7 @@ def createFlatCoordinatesTeniaColi(xiList, relaxedLengthList, nx = [v1, v2, v3] nd1 = [d1, d2, d3] sx, sd1, _, _, _ = interp.sampleCubicHermiteCurves(nx, nd1, elementsCountAroundTC) - if N > 0 and N < tcCount: + if 0 < N < tcCount: w = sx[1:-1] dw = sd1[1:-1] if elementsCountAroundTC > 2 else nd1[1:-1] elif N == 0: @@ -1591,8 +1596,10 @@ def createFlatCoordinatesTeniaColi(xiList, relaxedLengthList, d2FlatListTC = d2FlatListTC + d2FlatListTC[-((elementsCountAroundTC - 1) * tcCount + 1):] xFlat, d1Flat, d2Flat, _ = combineTeniaColiWithColon(xFlatColon, d1FlatColon, d2FlatColon, [], - xFlatListTC, d1FlatListTC, d2FlatListTC, [], (elementsCountAroundTC - 1) * tcCount + 1, - elementsCountAround + 1, elementsCountAlong, elementsCountThroughWall, closedProximalEnd) + xFlatListTC, d1FlatListTC, d2FlatListTC, [], + (elementsCountAroundTC - 1) * tcCount + 1, + elementsCountAround + 1, elementsCountAlong, + elementsCountThroughWall, closedProximalEnd) return xFlat, d1Flat, d2Flat @@ -1638,7 +1645,6 @@ def createColonCoordinatesTeniaColi(xiList, relativeThicknessList, lengthToDiame elementsCountAround * elementsCountThroughWall xTCRaw = [] d1TCRaw = [] - d2TCRaw = [] for N in range(tcCount): TCMidIdx = faceStartIdx + N * (elementsCountAroundTC + elementsCountAroundHaustrum) TCStartIdx = TCMidIdx - int(elementsCountAroundTC * 0.5) if N > 0 else \ @@ -1685,7 +1691,7 @@ def createNodesAndElementsTeniaColi(region, Create nodes and elements for the coordinates and flat coordinates fields. Note that flat coordinates not implemented for closedProximalEnd yet. :param x, d1, d2, d3: coordinates and derivatives of coordinates field. - :param xFlat, d1Flat, d2Flat, d3Flat: coordinates and derivatives of + :param xFlat, d1Flat, d2Flat: coordinates and derivatives of flat coordinates field. :param xOrgan, d1Organ, d2Organ, d3Organ, organCoordinateFieldName: coordinates, derivatives and name of organ coordinates field. @@ -1741,7 +1747,7 @@ def createNodesAndElementsTeniaColi(region, elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) - result = elementtemplate.defineField(coordinates, -1, eft) + elementtemplate.defineField(coordinates, -1, eft) # Tenia coli edge elements elementtemplate1 = mesh.createElementtemplate() @@ -1864,9 +1870,12 @@ def createNodesAndElementsTeniaColi(region, flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if n1 == 0: # print('NodeIdentifier', nodeIdentifier, 'version 2', xFlatList[i+elementsCountAround]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, xFlat[i + elementsCountAround]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Flat[i + elementsCountAround]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Flat[i + elementsCountAround]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, + xFlat[i + elementsCountAround]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, + d1Flat[i + elementsCountAround]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, + d2Flat[i + elementsCountAround]) if useCrossDerivatives: flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) nodeIdentifier = nodeIdentifier + 1 @@ -1883,9 +1892,12 @@ def createNodesAndElementsTeniaColi(region, if useCrossDerivatives: flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if nTC == 0: - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, xFlat[j + (elementsCountAroundTC - 1) * tcCount]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Flat[j + (elementsCountAroundTC - 1) * tcCount]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Flat[j + (elementsCountAroundTC - 1) * tcCount]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, + xFlat[j + (elementsCountAroundTC - 1) * tcCount]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, + d1Flat[j + (elementsCountAroundTC - 1) * tcCount]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, + d2Flat[j + (elementsCountAroundTC - 1) * tcCount]) if useCrossDerivatives: flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) nodeIdentifier = nodeIdentifier + 1 @@ -1950,7 +1962,7 @@ def createNodesAndElementsTeniaColi(region, math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround ] - result = element.setScaleFactors(eft3, scalefactors) + element.setScaleFactors(eft3, scalefactors) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAround[e1] + annotationGroupsAlong[0] + \ annotationGroupsThroughWall[e3] @@ -1986,7 +1998,8 @@ def createNodesAndElementsTeniaColi(region, elementtemplate6.defineField(coordinates, -1, eft6) nodeIdentifiers = [bni21, bni22, bni23, bni31, bni31 + 1] - element = mesh.createElement(elementIdentifier, elementtemplate4 if tetrahedralElement else elementtemplate6) + element = \ + mesh.createElement(elementIdentifier, elementtemplate4 if tetrahedralElement else elementtemplate6) element.setNodesByIdentifier(eft4 if tetrahedralElement else eft6, nodeIdentifiers) element.setScaleFactors(eft4 if tetrahedralElement else eft6, scalefactors) elementIdentifier = elementIdentifier + 1 @@ -2013,10 +2026,11 @@ def createNodesAndElementsTeniaColi(region, bni21 = elementsCountThroughWall + 1 bni22 = bni21 + elementsCountAround * elementsCountThroughWall + int(elementsCountAroundTC * 0.5) + \ - elementsCountAroundHaustrum + N * (elementsCountAroundTC + elementsCountAroundHaustrum) + eTC + 1 + elementsCountAroundHaustrum + N * (elementsCountAroundTC + elementsCountAroundHaustrum) + \ + eTC + 1 bni23 = bni22 + 1 - bni31 = bni21 + elementsCountAround * (elementsCountThroughWall + 1) + int(elementsCountAroundTC * 0.5) + \ - N * (elementsCountAroundTC - 1) + eTC + 1 + bni31 = bni21 + elementsCountAround * (elementsCountThroughWall + 1) + \ + int(elementsCountAroundTC * 0.5) + N * (elementsCountAroundTC - 1) + eTC + 1 if eTC == 0: eft5 = eftfactory.createEftTetrahedronXi1Zero(va * 10000, vb * 10000) @@ -2026,7 +2040,7 @@ def createNodesAndElementsTeniaColi(region, element.setNodesByIdentifier(eft5, nodeIdentifiers) element.setScaleFactors(eft5, scalefactors) - elif eTC > 0 and eTC < elementsCountAroundTC - 1: + elif 0 < eTC < elementsCountAroundTC - 1: eft6 = eftfactory.createEftPyramidBottomSimple(va * 10000, vb * 10000) elementtemplate6.defineField(coordinates, -1, eft6) nodeIdentifiers = [bni21, bni22, bni23, bni31 - 1, bni31] @@ -2043,9 +2057,10 @@ def createNodesAndElementsTeniaColi(region, element.setScaleFactors(eft4, scalefactors) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAround[elementsCountAround + int( - elementsCountAroundTC * 0.5) + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[0] + \ - ([longitudinalMuscleGroup] if longitudinalMuscle else []) + annotationGroups = \ + annotationGroupsAround[elementsCountAround + int(elementsCountAroundTC * 0.5) + + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[0] + \ + ([longitudinalMuscleGroup] if longitudinalMuscle else []) if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2088,7 +2103,8 @@ def createNodesAndElementsTeniaColi(region, elementtemplate6.defineField(coordinates, -1, eft6) nodeIdentifiers = [bni21, bni22, bni23, bni31, bni32] - element = mesh.createElement(elementIdentifier, elementtemplate5 if tetrahedralElement else elementtemplate6) + element = \ + mesh.createElement(elementIdentifier, elementtemplate5 if tetrahedralElement else elementtemplate6) element.setNodesByIdentifier(eft5 if tetrahedralElement else eft6, nodeIdentifiers) element.setScaleFactors(eft5 if tetrahedralElement else eft6, scalefactors) elementIdentifier = elementIdentifier + 1 @@ -2144,10 +2160,10 @@ def createNodesAndElementsTeniaColi(region, # Add elements for tenia coli for eTC in range(int(elementsCountAroundTC * 0.5)): if closedProximalEnd: - bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - eTC + 1 + tcOffset1 - bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - eTC + 2 + tcOffset1 + bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + eTC + 1 + tcOffset1 + bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + eTC + 2 + tcOffset1 bni31 = elementsCountThroughWall + 1 + e2 * now + eTC + 1 + tcOffset1 bni32 = elementsCountThroughWall + 1 + e2 * now + eTC + 2 + tcOffset1 else: @@ -2162,14 +2178,20 @@ def createNodesAndElementsTeniaColi(region, else: nodeIdentifiers = [bni21, bni22, bni21 + now + tcOffset, bni22 + now + tcOffset, bni31, bni31 + now + tcOffset] - element = mesh.createElement(elementIdentifier, elementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else elementtemplate1) + element = \ + mesh.createElement(elementIdentifier, + elementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else elementtemplate1) element.setNodesByIdentifier(eft if eTC < int(elementsCountAroundTC * 0.5) - 1 else eft1, nodeIdentifiers) if xFlat: - element.merge(flatElementtemplate1 if eTC < int(elementsCountAroundTC * 0.5) - 1 else flatElementtemplate3) - element.setNodesByIdentifier(eftFlat3 if eTC < int(elementsCountAroundTC * 0.5) - 1 else eftFlat5, nodeIdentifiers) + element.merge(flatElementtemplate1 if eTC < int(elementsCountAroundTC * 0.5) - 1 else + flatElementtemplate3) + element.setNodesByIdentifier(eftFlat3 if eTC < int(elementsCountAroundTC * 0.5) - 1 else + eftFlat5, nodeIdentifiers) if xOrgan: - element.merge(organElementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else organElementtemplate1) - element.setNodesByIdentifier(eftOrgan if eTC < int(elementsCountAroundTC * 0.5) - 1 else eftOrgan1, nodeIdentifiers) + element.merge(organElementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else + organElementtemplate1) + element.setNodesByIdentifier(eftOrgan if eTC < int(elementsCountAroundTC * 0.5) - 1 else + eftOrgan1, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAround[elementsCountAround + eTC] + annotationGroupsAlong[e2] + \ ([longitudinalMuscleGroup] if longitudinalMuscle else []) @@ -2187,8 +2209,8 @@ def createNodesAndElementsTeniaColi(region, + eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) + \ (N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC bni22 = elementsCountThroughWall + 1 + ( - e2 - 1) * now + elementsCountThroughWall * elementsCountAround \ - + eTC + 2 + tcOffset1 + int(elementsCountAroundTC * 0.5) + \ + e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ + eTC + 2 + tcOffset1 + int(elementsCountAroundTC * 0.5) + \ (N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC bni31 = elementsCountThroughWall + 1 + e2 * now + eTC + 1 + tcOffset1 + \ int(elementsCountAroundTC * 0.5) - 1 + N * (elementsCountAroundTC - 1) @@ -2199,8 +2221,8 @@ def createNodesAndElementsTeniaColi(region, int(elementsCountAroundTC * 0.5) + ( N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC bni22 = e2 * now + elementsCountThroughWall * elementsCountAround + eTC + 2 + tcOffset1 + \ - int(elementsCountAroundTC * 0.5) + ( - N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC + int(elementsCountAroundTC * 0.5) + (N + 1) * elementsCountAroundHaustrum + \ + N * elementsCountAroundTC bni31 = (e2 + 1) * now + eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) - 1 + \ N * (elementsCountAroundTC - 1) bni32 = (e2 + 1) * now + eTC + 2 + tcOffset1 + int(elementsCountAroundTC * 0.5) - 1 + \ @@ -2216,7 +2238,7 @@ def createNodesAndElementsTeniaColi(region, if xOrgan: element.merge(organElementtemplate2) element.setNodesByIdentifier(eftOrgan2, nodeIdentifiers) - elif eTC > 0 and eTC < elementsCountAroundTC - 1: + elif 0 < eTC < elementsCountAroundTC - 1: nodeIdentifiers = [bni21, bni22, bni21 + now + tcOffset, bni22 + now + tcOffset, bni31, bni32, bni31 + now + tcOffset, bni32 + now + tcOffset] element = mesh.createElement(elementIdentifier, elementtemplate) @@ -2239,9 +2261,10 @@ def createNodesAndElementsTeniaColi(region, element.merge(organElementtemplate1) element.setNodesByIdentifier(eftOrgan1, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAround[elementsCountAround + int( - elementsCountAroundTC * 0.5) + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[e2] + \ - ([longitudinalMuscleGroup] if longitudinalMuscle else []) + annotationGroups = \ + annotationGroupsAround[elementsCountAround + int(elementsCountAroundTC * 0.5) + + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[e2] + \ + ([longitudinalMuscleGroup] if longitudinalMuscle else []) if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2250,21 +2273,23 @@ def createNodesAndElementsTeniaColi(region, for eTC in range(int(elementsCountAroundTC * 0.5)): if closedProximalEnd: - bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) + tcCount * elementsCountAroundHaustrum + \ + bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + eTC + 1 + tcOffset1 + \ + int(elementsCountAroundTC * 0.5) + tcCount * elementsCountAroundHaustrum + \ (tcCount - 1) * elementsCountAroundTC - bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - 1 + tcOffset1 if eTC == int(elementsCountAroundTC * 0.5 - 1) else bni21 + 1 + bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + 1 + \ + tcOffset1 if eTC == int(elementsCountAroundTC * 0.5 - 1) else bni21 + 1 bni31 = elementsCountThroughWall + 1 + e2 * now + eTC + 1 + tcOffset1 + \ int(elementsCountAroundTC * 0.5) - 1 + (tcCount - 1) * (elementsCountAroundTC - 1) bni32 = elementsCountThroughWall + 1 + e2 * now + 1 + tcOffset1 if eTC == int( elementsCountAroundTC * 0.5 - 1) \ else bni31 + 1 else: - bni21 = e2 * now + (elementsCountThroughWall) * elementsCountAround + eTC + 1 + tcOffset1 + \ + bni21 = e2 * now + elementsCountThroughWall * elementsCountAround + eTC + 1 + tcOffset1 + \ int(elementsCountAroundTC * 0.5) + tcCount * elementsCountAroundHaustrum + \ (tcCount - 1) * elementsCountAroundTC - bni22 = e2 * now + (elementsCountThroughWall) * elementsCountAround + 1 + tcOffset1 if eTC == int( + bni22 = e2 * now + elementsCountThroughWall * elementsCountAround + 1 + tcOffset1 if eTC == int( elementsCountAroundTC * 0.5 - 1) else bni21 + 1 bni31 = (e2 + 1) * now + eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) - 1 + \ (tcCount - 1) * (elementsCountAroundTC - 1) @@ -2289,9 +2314,9 @@ def createNodesAndElementsTeniaColi(region, element.merge(organElementtemplate if eTC > 0 else organElementtemplate2) element.setNodesByIdentifier(eftOrgan if eTC > 0 else eftOrgan2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAround[ - elementsCountAround + int(elementsCountAroundTC * (tcCount - 0.5)) + eTC] + \ - annotationGroupsAlong[e2] + ([longitudinalMuscleGroup] if longitudinalMuscle else []) + annotationGroups = \ + annotationGroupsAround[elementsCountAround + int(elementsCountAroundTC * (tcCount - 0.5)) + eTC] + \ + annotationGroupsAlong[e2] + ([longitudinalMuscleGroup] if longitudinalMuscle else []) if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index 396aa1df..c1b65f34 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -21,41 +21,43 @@ class MeshType_3d_ostium1(Scaffold_base): - ''' + """ Generates a 3-D single or double/common ostium inlet or outlet. - ''' + """ @staticmethod def getName(): return '3D Ostium 1' - @staticmethod - def getDefaultOptions(parameterSetName='Default'): + @classmethod + def getDefaultOptions(cls, parameterSetName='Default'): return { - 'Number of vessels' : 2, - 'Number of elements across common' : 2, - 'Number of elements around ostium' : 10, - 'Number of elements along' : 1, - 'Number of elements through wall' : 1, # not implemented for > 1 - 'Unit scale' : 1.0, - 'Outlet' : False, - 'Ostium diameter' : 1.0, - 'Ostium length' : 0.4, - 'Ostium wall thickness' : 0.08, - 'Ostium inter-vessel distance' : 0.8, - 'Ostium inter-vessel height' : 0.0, - 'Use linear through ostium wall' : False, - 'Vessel end length factor' : 1.0, - 'Vessel inner diameter' : 0.6, - '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 + 'Number of vessels': 2, + 'Number of elements across common': 2, + 'Number of elements around ostium': 10, + 'Number of elements along': 1, + 'Number of elements through wall': 1, + 'Unit scale': 1.0, + 'Outlet': False, + 'Ostium diameter': 1.0, + 'Ostium length': 0.4, + 'Ostium wall thickness': 0.08, + 'Ostium wall relative thicknesses': [1.0], + 'Ostium inter-vessel distance': 0.8, + 'Ostium inter-vessel height': 0.0, + 'Use linear through ostium wall': False, + 'Vessel end length factor': 1.0, + 'Vessel inner diameter': 0.6, + 'Vessel wall thickness': 0.04, + 'Vessel wall relative thicknesses': [1.0], + '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 @@ -65,33 +67,35 @@ def getOrderedOptionNames(): 'Number of elements across common', 'Number of elements around ostium', 'Number of elements along', - 'Number of elements through wall', # not implemented for > 1 + 'Number of elements through wall', 'Unit scale', 'Outlet', 'Ostium diameter', 'Ostium length', 'Ostium wall thickness', + 'Ostium wall relative thicknesses', 'Ostium inter-vessel distance', 'Ostium inter-vessel height', 'Use linear through ostium wall', 'Vessel end length factor', 'Vessel inner diameter', 'Vessel wall thickness', + 'Vessel wall relative thicknesses', 'Vessel angle 1 degrees', 'Vessel angle 1 spread degrees', 'Vessel angle 2 degrees', 'Use linear through vessel wall', - #'Use cross derivatives', # not implemented + # 'Use cross derivatives', # not implemented 'Refine', 'Refine number of elements around', 'Refine number of elements along', 'Refine number of elements through wall' ] - @staticmethod - def checkOptions(options): + @classmethod + def checkOptions(cls, options): dependentChanges = False - vesselsCount = options['Number of vessels'] + vesselsCount = options['Number of vessels'] if vesselsCount < 1: vesselsCount = options['Number of vessels'] = 1 elif vesselsCount > 3: @@ -105,15 +109,13 @@ def checkOptions(options): 'Refine number of elements through wall']: if options[key] < 1: options[key] = 1 - if (options['Number of elements around ostium'] < 2*vesselsCount) : - options['Number of elements around ostium'] = 2*vesselsCount + if options['Number of elements around ostium'] < 2 * vesselsCount: + options['Number of elements around ostium'] = 2 * vesselsCount dependentChanges = True # because can happen by changing number of vessels # currently must have even number around ostium if multiple vessels: if (vesselsCount > 1) and (options['Number of elements around ostium'] % 2): options['Number of elements around ostium'] += 1 dependentChanges = True # because can happen by changing number of vessels - if options['Number of elements through wall'] > 1: - options['Number of elements through wall'] = 1 for key in [ 'Unit scale', 'Ostium length', @@ -121,10 +123,33 @@ def checkOptions(options): 'Ostium inter-vessel distance', 'Vessel inner diameter', 'Vessel wall thickness']: - if options[key] < 0.0: + if options[key] < 0.0: options[key] = 0.0 if options['Ostium diameter'] <= 0.0: options['Ostium diameter'] = 0.000001 # avoid division by zero + elementsThroughWall = options['Number of elements through wall'] + ostiumThicknessProportionsCountKey = 'Ostium wall relative thicknesses' + vesselThicknessProportionsCountKey = 'Vessel wall relative thicknesses' + ostiumWallCount = len(options[ostiumThicknessProportionsCountKey]) + vesselWallCount = len(options[vesselThicknessProportionsCountKey]) + if elementsThroughWall == 1: + options[ostiumThicknessProportionsCountKey] = options[vesselThicknessProportionsCountKey] = [1.0] + if ostiumWallCount < elementsThroughWall: + options[ostiumThicknessProportionsCountKey] += \ + [options[ostiumThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - ostiumWallCount)] + dependentChanges = True + elif ostiumWallCount > elementsThroughWall: + options[ostiumThicknessProportionsCountKey] = \ + options[ostiumThicknessProportionsCountKey][:elementsThroughWall] + dependentChanges = True + if vesselWallCount < elementsThroughWall: + options[vesselThicknessProportionsCountKey] += \ + [options[vesselThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - vesselWallCount)] + dependentChanges = True + elif vesselWallCount > elementsThroughWall: + options[vesselThicknessProportionsCountKey] = \ + options[vesselThicknessProportionsCountKey][:elementsThroughWall] + dependentChanges = True return dependentChanges @classmethod @@ -136,20 +161,22 @@ def generateBaseMesh(cls, region, options): :return: [] empty list of AnnotationGroup """ unitScale = options['Unit scale'] - ostiumRadius = 0.5*unitScale*options['Ostium diameter'] - interVesselDistance = unitScale*options['Ostium inter-vessel distance'] - - scale = 1.1*(ostiumRadius*2.0 + interVesselDistance) - nx = [ [ -scale, -scale, 0.0 ], [ scale, -scale, 0.0 ], [ -scale, scale, 0.0 ], [ scale, scale, 0.0 ] ] - nd1 = [ [ 2.0*scale, 0.0, 0.0 ] ]*4 - nd2 = [ [ 0.0, 2.0*scale, 0.0 ] ]*4 - # curve track surface: - #zf = 2.0 - #nd1 = [ [ 2.0*scale, 0.0, zf*scale ], [ 2.0*scale, 0.0, -zf*scale ], [ 2.0*scale, 0.0, zf*scale ], [ 2.0*scale, 0.0, -zf*scale ] ] - #nd2 = [ [ 0.0, 2.0*scale, zf*scale ], [ 0.0, 2.0*scale, zf*scale ], [ 0.0, 2.0*scale, -zf*scale ], [ 0.0, 2.0*scale, -zf*scale ] ] + ostiumRadius = 0.5 * unitScale * options['Ostium diameter'] + interVesselDistance = unitScale * options['Ostium inter-vessel distance'] + + scale = 1.1 * (ostiumRadius * 2.0 + interVesselDistance) + nx = [[-scale, -scale, 0.0], [scale, -scale, 0.0], [-scale, scale, 0.0], [scale, scale, 0.0]] + nd1 = [[2.0 * scale, 0.0, 0.0]] * 4 + nd2 = [[0.0, 2.0 * scale, 0.0]] * 4 + # # curve track surface: + # zf = 2.0 + # nd1 = [ [ 2.0 * scale, 0.0, zf * scale ], [ 2.0 * scale, 0.0, -zf * scale ], + # [ 2.0 * scale, 0.0, zf * scale ], [ 2.0 * scale, 0.0, -zf * scale ] ] + # nd2 = [ [ 0.0, 2.0 * scale, zf * scale ], [ 0.0, 2.0 * scale, zf * scale ], + # [ 0.0, 2.0 * scale, -zf * scale ], [ 0.0, 2.0 * scale, -zf * scale ] ] trackSurface = TrackSurface(1, 1, nx, nd1, nd2) centrePosition = TrackSurfacePosition(0, 0, 0.5, 0.5) - axis1 = [ 1.0, 0.0, 0.0 ] + axis1 = [1.0, 0.0, 0.0] generateOstiumMesh(region, options, trackSurface, centrePosition, axis1) return [] # no annotation groups @@ -163,29 +190,30 @@ def refineMesh(cls, meshrefinement, options): 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) + meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, + refineElementsCountThroughWall) def getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount): - ''' + """ Determine numbers of elements around each vessel to fit numbers of elements around ostium. :param elementsCountAroundOstium: Number of elements around outside of ostium. - Minimum of 2 + 2*vesselsCount. Must be even if more than 1 vessels. + Minimum of 2 + 2 * vesselsCount. Must be even if more than 1 vessels. :param elementsCountAcross: Number of elements across ostium between multiple vessels. Unused if vesselsCount is 1. :param vesselsCount: Number of vessels from 1 to 3. :return: List of numbers of elements around each vessel to fit numbers around ostium, number of elements mid side (non-zero only for 3+ vessels). - ''' + """ assert 1 <= vesselsCount <= 3 - assert elementsCountAroundOstium >= 2*vesselsCount - assert (1 == vesselsCount) or (elementsCountAroundOstium%2 == 0) + assert elementsCountAroundOstium >= 2 * vesselsCount + assert (1 == vesselsCount) or (elementsCountAroundOstium % 2 == 0) assert elementsCountAcross > 0 if vesselsCount == 1: - return [ elementsCountAroundOstium ], 0 + return [elementsCountAroundOstium], 0 if vesselsCount == 2: - count = elementsCountAroundOstium//2 + elementsCountAcross - return [ count, count ], 0 + count = elementsCountAroundOstium // 2 + elementsCountAcross + return [count, count], 0 # vesselsCount == 3: # Around oinc Total:Vessels 1 Total:Vessels 2 # 6 1 8: 3-4-3 10: 4-6-4 @@ -194,48 +222,55 @@ def getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCoun # 12 2 14: 5-6-5 16: 6-8-6 # 14 2 16: 6-6-6 18: 7-8-7 # 16 2 18: 7-6-7 20: 8-8-8 - elementsCountAroundMid = ((elementsCountAroundOstium + 1)//6) - countInner = 2*(elementsCountAroundMid + elementsCountAcross) - countOuter = (elementsCountAroundOstium - 2*elementsCountAroundMid)//2 + elementsCountAcross - return [ countOuter, countInner, countOuter ], elementsCountAroundMid + elementsCountAroundMid = ((elementsCountAroundOstium + 1) // 6) + countInner = 2 * (elementsCountAroundMid + elementsCountAcross) + countOuter = (elementsCountAroundOstium - 2 * elementsCountAroundMid) // 2 + elementsCountAcross + return [countOuter, countInner, countOuter], elementsCountAroundMid -def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier = 1, startElementIdentifier = 1, - vesselMeshGroups = None, ostiumMeshGroups = None): - ''' +def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier=1, + startElementIdentifier=1, vesselMeshGroups=None, ostiumMeshGroups=None, + wallAnnotationGroups=None): + """ :param vesselMeshGroups: List (over number of vessels) of list of mesh groups to add vessel elements to. :param ostiumMeshGroups: List of mesh groups to add only row of elements at ostium end to. + :param wallAnnotationGroups: list of annotation groups to add to wall elements. :return: nextNodeIdentifier, nextElementIdentifier, Ostium points tuple (ox[n3][n1][c], od1[n3][n1][c], od2[n3][n1][c], od3[n3][n1][c], oNodeId[n3][n1], oPositions). - ''' + """ vesselsCount = options['Number of vessels'] elementsCountAroundOstium = options['Number of elements around ostium'] elementsCountAcross = options['Number of elements across common'] - elementsCountsAroundVessels, elementsCountAroundMid = getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount) - elementsCountAroundEnd = (elementsCountAroundOstium - 2*elementsCountAroundMid)//2 - #print('\nvesselsCount', vesselsCount, 'elementsCountsAroundOstium', elementsCountAroundOstium, 'elementsCountAcross', elementsCountAcross) - #print('--> elementsCountsAroundVessels', elementsCountsAroundVessels, 'elementsCountAroundMid', elementsCountAroundMid) + elementsCountsAroundVessels, elementsCountAroundMid = \ + getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount) + elementsCountAroundEnd = (elementsCountAroundOstium - 2 * elementsCountAroundMid) // 2 + # print('\nvesselsCount', vesselsCount, 'elementsCountsAroundOstium', elementsCountAroundOstium, + # 'elementsCountAcross', elementsCountAcross) + # print('--> elementsCountsAroundVessels', elementsCountsAroundVessels, + # 'elementsCountAroundMid', elementsCountAroundMid) elementsCountAlong = options['Number of elements along'] elementsCountThroughWall = options['Number of elements through wall'] unitScale = options['Unit scale'] isOutlet = options['Outlet'] - ostiumRadius = 0.5*unitScale*options['Ostium diameter'] - ostiumLength = unitScale*options['Ostium length'] - ostiumWallThickness = unitScale*options['Ostium wall thickness'] - interVesselHeight = unitScale*options['Ostium inter-vessel height'] - interVesselDistance = unitScale*options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 - halfInterVesselDistance = 0.5*interVesselDistance + ostiumRadius = 0.5 * unitScale * options['Ostium diameter'] + ostiumLength = unitScale * options['Ostium length'] + ostiumWallThickness = unitScale * options['Ostium wall thickness'] + ostiumWallThicknessProportionsUI = copy.deepcopy(options['Ostium wall relative thicknesses']) + interVesselHeight = unitScale * options['Ostium inter-vessel height'] + interVesselDistance = unitScale * options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 + halfInterVesselDistance = 0.5 * interVesselDistance useCubicHermiteThroughOstiumWall = not(options['Use linear through ostium wall']) - vesselEndDerivative = ostiumLength*options['Vessel end length factor']/elementsCountAlong - vesselInnerRadius = 0.5*unitScale*options['Vessel inner diameter'] - vesselWallThickness = unitScale*options['Vessel wall thickness'] + vesselEndDerivative = ostiumLength * options['Vessel end length factor'] / elementsCountAlong + vesselInnerRadius = 0.5 * unitScale * options['Vessel inner diameter'] + vesselWallThickness = unitScale * options['Vessel wall thickness'] + vesselWallThicknessProportionsUI = copy.deepcopy(options['Vessel wall relative thicknesses']) vesselOuterRadius = vesselInnerRadius + vesselWallThickness vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees']) vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees']) vesselAngle2Radians = math.radians(options['Vessel angle 2 degrees']) useCubicHermiteThroughVesselWall = not(options['Use linear through vessel wall']) - useCrossDerivatives = False # options['Use cross derivatives'] # not implemented + # useCrossDerivatives = False # options['Use cross derivatives'] # not implemented fm = region.getFieldmodule() fm.beginChange() @@ -265,32 +300,34 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # get directions in plane of surface at centre: cx, cd1, cd2 = trackSurface.evaluateCoordinates(centrePosition, True) trackDirection1, trackDirection2, centreNormal = calculate_surface_axes(cd1, cd2, axis1) - trackDirection2reverse = [ -d for d in trackDirection2 ] + trackDirection2reverse = [-d for d in trackDirection2] - halfCircumference = math.pi*ostiumRadius - circumference = 2.0*halfCircumference + halfCircumference = math.pi * ostiumRadius + circumference = 2.0 * halfCircumference distance = 0.0 elementLengthAroundOstiumMid = 0.0 - vesselsSpanAll = interVesselDistance*(vesselsCount - 1) - vesselsSpanMid = interVesselDistance*(vesselsCount - 2) + vesselsSpanAll = interVesselDistance * (vesselsCount - 1) + vesselsSpanMid = interVesselDistance * (vesselsCount - 2) if vesselsCount == 1: - elementLengthAroundOstiumEnd = circumference/elementsCountAroundOstium - vesselOstiumPositions = [ centrePosition ] - ocx = [ cx ] - ocd1 = [ trackDirection1 ] - ocd2 = [ trackDirection2 ] - ocd3 = [ centreNormal ] + elementLengthAroundOstiumEnd = circumference / elementsCountAroundOstium + vesselOstiumPositions = [centrePosition] + ocx = [cx] + ocd1 = [trackDirection1] + ocd2 = [trackDirection2] + ocd3 = [centreNormal] else: - elementLengthAroundOstiumEnd = (circumference + 2.0*interVesselDistance)/(elementsCountAroundOstium - 2*elementsCountAroundMid) + elementLengthAroundOstiumEnd = (circumference + 2.0 * interVesselDistance) / \ + (elementsCountAroundOstium - 2 * elementsCountAroundMid) if elementsCountAroundMid > 0: - elementLengthAroundOstiumMid = interVesselDistance*(vesselsCount - 2)/elementsCountAroundMid + elementLengthAroundOstiumMid = interVesselDistance * (vesselsCount - 2) / elementsCountAroundMid vesselOstiumPositions = [] - ocx = [] + ocx = [] ocd1 = [] ocd2 = [] ocd3 = [] for v in range(vesselsCount): - vesselOstiumPositions.append(trackSurface.trackVector(centrePosition, trackDirection1, (v/(vesselsCount - 1) - 0.5)*vesselsSpanAll)) + vesselOstiumPositions.append(trackSurface.trackVector(centrePosition, trackDirection1, + (v / (vesselsCount - 1) - 0.5) * vesselsSpanAll)) x, d1, d2 = trackSurface.evaluateCoordinates(vesselOstiumPositions[-1], -1) d1, d2, d3 = calculate_surface_axes(d1, d2, trackDirection1) ocx .append(x) @@ -299,37 +336,50 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ocd3.append(d3) # coordinates around ostium - ox = [ [], [] ] - od1 = [ [], [] ] - od2 = [ [], [] ] - od3 = [ [], [] ] + ox = [[] for n3 in range(elementsCountThroughWall + 1)] + od1 = [[] for n3 in range(elementsCountThroughWall + 1)] + od2 = [[] for n3 in range(elementsCountThroughWall + 1)] + od3 = [[] for n3 in range(elementsCountThroughWall + 1)] oPositions = [] + ostiumWallThicknessProportions = [ostiumWallThicknessProportion / sum(ostiumWallThicknessProportionsUI) + for ostiumWallThicknessProportion in ostiumWallThicknessProportionsUI] + vesselWallThicknessProportions = [vesselWallThicknessProportion / sum(vesselWallThicknessProportionsUI) + for vesselWallThicknessProportion in vesselWallThicknessProportionsUI] + + ostiumWallThicknessProportions.append(ostiumWallThicknessProportions[-1]) + vesselWallThicknessProportions.append(vesselWallThicknessProportions[-1]) + for n1 in range(elementsCountAroundOstium): elementLength = elementLengthAroundOstiumEnd if distance <= (vesselsSpanMid + halfInterVesselDistance): - position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid - distance) + position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5 * vesselsSpanMid - distance) sideDirection = trackDirection2reverse if n1 < elementsCountAroundMid: elementLength = elementLengthAroundOstiumMid elif distance < (vesselsSpanMid + halfInterVesselDistance + halfCircumference): position = vesselOstiumPositions[0] - angleRadians = (distance - (vesselsSpanMid + halfInterVesselDistance))/ostiumRadius + angleRadians = (distance - (vesselsSpanMid + halfInterVesselDistance)) / ostiumRadius w1 = -math.sin(angleRadians) w2 = -math.cos(angleRadians) - sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ] - elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance): - position = trackSurface.trackVector(centrePosition, trackDirection1, distance - (1.5*vesselsSpanMid + interVesselDistance + halfCircumference)) + sideDirection = [(w1 * trackDirection1[c] + w2 * trackDirection2[c]) for c in range(3)] + elif distance < (2.0 * vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance): + position = trackSurface.trackVector(centrePosition, trackDirection1, distance - + (1.5 * vesselsSpanMid + interVesselDistance + halfCircumference)) sideDirection = trackDirection2 if 0 <= (n1 - elementsCountAroundEnd - elementsCountAroundMid) < elementsCountAroundMid: elementLength = elementLengthAroundOstiumMid - elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + circumference + interVesselDistance): + elif distance < (2.0 * vesselsSpanMid + halfInterVesselDistance + circumference + interVesselDistance): position = vesselOstiumPositions[-1] - angleRadians = (distance - (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance))/ostiumRadius + angleRadians = (distance - (2.0 * vesselsSpanMid + halfInterVesselDistance + halfCircumference + + interVesselDistance)) / ostiumRadius w1 = math.sin(angleRadians) w2 = math.cos(angleRadians) - sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ] + sideDirection = [(w1 * trackDirection1[c] + w2 * trackDirection2[c]) for c in range(3)] else: - position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid + (circumference + 2.0*(vesselsSpanMid + interVesselDistance)) - distance) + position = \ + trackSurface.trackVector(centrePosition, trackDirection1, + 0.5 * vesselsSpanMid + (circumference + 2.0 * + (vesselsSpanMid + interVesselDistance)) - distance) sideDirection = trackDirection2reverse position = trackSurface.trackVector(position, sideDirection, ostiumRadius) oPositions.append(position) @@ -337,76 +387,100 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta pd2, pd1, pd3 = calculate_surface_axes(d1, d2, sideDirection) # get outer coordinates opx = px - opd1 = vector.setMagnitude([ -d for d in pd1 ], elementLengthAroundOstiumEnd) + opd1 = vector.setMagnitude([-d for d in pd1], elementLengthAroundOstiumEnd) opd2 = vector.setMagnitude(pd2, elementLengthAroundOstiumEnd) # smoothed later opd3 = vector.setMagnitude(pd3, ostiumWallThickness) - # set inner and outer coordinates (use copy to avoid references to same list later) - ox [0].append([ (opx[c] - opd3[c]) for c in range(3) ]) - od1[0].append(copy.copy(opd1)) - od2[0].append(copy.copy(opd2)) - ox [1].append(opx) - od1[1].append(opd1) - od2[1].append(opd2) - if useCubicHermiteThroughOstiumWall: - od3[0].append(copy.copy(opd3)) - od3[1].append(opd3) + + ostiumTotalXi3 = 0.0 + vesselTotalXi3 = 0.0 + ostiumWallThicknessXi3List = [0.0] + vesselWallThicknessXi3List = [0.0] + + for n3 in range(elementsCountThroughWall): + ostiumTotalXi3 += ostiumWallThicknessProportions[n3] + vesselTotalXi3 += vesselWallThicknessProportions[n3] + ostiumWallThicknessXi3List.append(ostiumTotalXi3) + vesselWallThicknessXi3List.append(vesselTotalXi3) + + # set coordinates through wall (use copy to avoid references to same list later) + for n3 in range(elementsCountThroughWall + 1): + xi3 = 1 - ostiumWallThicknessXi3List[n3] + ox[n3].append([(opx[c] - opd3[c] * xi3) for c in range(3)]) + od1[n3].append(copy.copy(opd1)) + od2[n3].append(copy.copy(opd2)) + if useCubicHermiteThroughOstiumWall: + od3[n3].append([opd3[c] * ostiumWallThicknessProportions[n3] for c in range(3)]) distance += elementLength - for n3 in range(2): - od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections = True) + for n3 in range(elementsCountThroughWall + 1): + od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections=True) - xx = [] + xx = [] xd1 = [] xd2 = [] xd3 = [] if (vesselWallThickness > 0.0) and (ostiumWallThickness > 0.0): - commonOstiumWallThickness = 2.0/(1.0/vesselWallThickness + 1.0/ostiumWallThickness) + commonOstiumWallThickness = 2.0 / (1.0 / vesselWallThickness + 1.0 / ostiumWallThickness) + commonOstiumWallThicknessProportions = [] + commonOstiumWallTotalXi3 = 0.0 + commonOstiumWallThicknessXi3List = [0.0] + for c in range(elementsCountThroughWall + 1): + commonOstiumWallThicknessProportions.append( + (vesselWallThicknessProportions[c] + ostiumWallThicknessProportions[c]) * 0.5) + for n3 in range(elementsCountThroughWall): + commonOstiumWallTotalXi3 += commonOstiumWallThicknessProportions[n3] + commonOstiumWallThicknessXi3List.append(commonOstiumWallTotalXi3) else: commonOstiumWallThickness = vesselWallThickness + commonOstiumWallThicknessProportions = vesselWallThicknessProportions + commonOstiumWallThicknessXi3List = vesselWallThicknessXi3List # coordinates across common ostium, between vessels nodesCountFreeEnd = elementsCountsAroundVessels[0] + 1 - elementsCountAcross - oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid//(vesselsCount - 2) + oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid // (vesselsCount - 2) for iv in range(vesselsCount - 1): - xx .append([ None, None ]) - xd1.append([ None, None ]) - xd2.append([ None, None ]) - xd3.append([ None, None ]) - oa = elementsCountAroundMid - iv*oinc - ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc - nx = [ ox[1][oa], ox[1][ob] ] - nd1 = [ [ -d for d in od1[1][oa] ], od1[1][ob] ] - nd2 = [ [ -d for d in od2[1][oa] ], od2[1][ob] ] + xx .append([None for n3 in range(elementsCountThroughWall + 1)]) + xd1.append([None for n3 in range(elementsCountThroughWall + 1)]) + xd2.append([None for n3 in range(elementsCountThroughWall + 1)]) + xd3.append([None for n3 in range(elementsCountThroughWall + 1)]) + oa = elementsCountAroundMid - iv * oinc + ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv * oinc + nx = [ox[elementsCountThroughWall][oa], ox[elementsCountThroughWall][ob]] + nd1 = [[-d for d in od1[elementsCountThroughWall][oa]], od1[elementsCountThroughWall][ob]] + nd2 = [[-d for d in od2[elementsCountThroughWall][oa]], od2[elementsCountThroughWall][ob]] + if elementsCountAcross > 1: # add centre point, displaced by interVesselHeight if vesselsCount == 2: position = centrePosition else: - position = trackSurface.trackVector(centrePosition, trackDirection1, (iv/(vesselsCount - 2) - 0.5)*vesselsSpanMid) - mx, d1, d2 = trackSurface.evaluateCoordinates(position, derivatives = True) + position = trackSurface.trackVector(centrePosition, trackDirection1, + (iv / (vesselsCount - 2) - 0.5) * vesselsSpanMid) + mx, d1, d2 = trackSurface.evaluateCoordinates(position, derivatives=True) md1, md2, md3 = calculate_surface_axes(d1, d2, trackDirection1) - nx .insert(1, [ (mx[c] + interVesselHeight*md3[c]) for c in range(3) ]) - nd1.insert(1, vector.setMagnitude(md1, elementLengthAroundOstiumMid if (0 < iv < (vesselsCount - 2)) else elementLengthAroundOstiumEnd)) + nx .insert(1, [(mx[c] + interVesselHeight * md3[c]) for c in range(3)]) + nd1.insert(1, vector.setMagnitude(md1, elementLengthAroundOstiumMid if (0 < iv < (vesselsCount - 2)) else + elementLengthAroundOstiumEnd)) nd2.insert(1, vector.setMagnitude(md2, ostiumRadius)) - nd2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixAllDirections = True) + nd2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixAllDirections=True) px, pd2, pe, pxi = interp.sampleCubicHermiteCurves(nx, nd2, elementsCountAcross)[0:4] pd1 = interp.interpolateSampleLinear(nd1, pe, pxi) - pd3 = [ vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness) for n2 in range(elementsCountAcross + 1) ] - lx = [ ([ (px[n2][c] - pd3[n2][c]) for c in range(3) ]) for n2 in range(elementsCountAcross + 1) ] - ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections = True) - xx [iv][0] = lx [1:elementsCountAcross] - xd1[iv][0] = copy.deepcopy(pd1[1:elementsCountAcross]) # to be smoothed later - xd2[iv][0] = ld2[1:elementsCountAcross] - xx [iv][1] = px [1:elementsCountAcross] - xd1[iv][1] = pd1[1:elementsCountAcross] # to be smoothed later - xd2[iv][1] = pd2[1:elementsCountAcross] - if useCubicHermiteThroughOstiumWall: - xd3[iv][0] = copy.deepcopy(pd3[1:elementsCountAcross]) - xd3[iv][1] = pd3[1:elementsCountAcross] - # set smoothed d2 on ostium circumference - od2[0][oa] = [ -d for d in ld2[0] ] - od2[1][oa] = [ -d for d in pd2[0] ] - od2[0][ob] = ld2[-1] - od2[1][ob] = pd2[-1] + pd3 = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness) + for n2 in range(elementsCountAcross + 1)] + for n3 in range(elementsCountThroughWall + 1): + xi3 = 1 - commonOstiumWallThicknessXi3List[n3] + lx = [([(px[n2][c] - xi3 * pd3[n2][c]) for c in range(3)]) for n2 in range(elementsCountAcross + 1)] + ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections=True) + xx[iv][n3] = lx[1:elementsCountAcross] + xd1[iv][n3] = copy.deepcopy(pd1[1:elementsCountAcross]) # to be smoothed later + xd2[iv][n3] = ld2[1:elementsCountAcross] + # set smoothed d2 on ostium circumference + od2[n3][oa] = [-d for d in ld2[0]] + od2[n3][ob] = ld2[-1] + if useCubicHermiteThroughOstiumWall: + pd3Element = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), + commonOstiumWallThickness * commonOstiumWallThicknessProportions[n3]) + for n2 in range(elementsCountAcross + 1)] + xd3[iv][n3] = copy.deepcopy(pd3Element[1:elementsCountAcross]) # get positions of vessel end centres and rings vcx = [] @@ -419,14 +493,15 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod3 = [] for v in range(vesselsCount): elementsCountAroundVessel = elementsCountsAroundVessels[v] - radiansPerElementVessel = 2.0*math.pi/elementsCountAroundVessel + radiansPerElementVessel = 2.0 * math.pi / elementsCountAroundVessel useVesselAngleRadians = vesselAngle1Radians if vesselsCount > 1: - useVesselAngleRadians += (v/(vesselsCount - 1) - 0.5)*vesselAngle1SpreadRadians - vx, vd1, vd2, vd3 = getCircleProjectionAxes(ocx[v], ocd1[v], ocd2[v], ocd3[v], ostiumLength, useVesselAngleRadians, vesselAngle2Radians) - vd1 = [ vesselOuterRadius*d for d in vd1 ] - vd2 = [ -vesselOuterRadius*d for d in vd2 ] - vd3 = [ -vesselEndDerivative*d for d in vd3 ] + useVesselAngleRadians += (v / (vesselsCount - 1) - 0.5) * vesselAngle1SpreadRadians + vx, vd1, vd2, vd3 = getCircleProjectionAxes(ocx[v], ocd1[v], ocd2[v], ocd3[v], ostiumLength, + useVesselAngleRadians, vesselAngle2Radians) + vd1 = [vesselOuterRadius * d for d in vd1] + vd2 = [-vesselOuterRadius * d for d in vd2] + vd3 = [-vesselEndDerivative * d for d in vd3] vcx.append(vx) vcd1.append(vd1) vcd2.append(vd2) @@ -435,169 +510,175 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod1.append([]) vod2.append([]) vod3.append([]) - for n3 in range(2): - radius = vesselInnerRadius if (n3 == 0) else vesselOuterRadius + for n3 in range(elementsCountThroughWall + 1): + radius = vesselInnerRadius + vesselWallThicknessXi3List[n3] * vesselWallThickness vAxis1 = vector.setMagnitude(vd1, radius) vAxis2 = vector.setMagnitude(vd2, radius) if vesselsCount == 1: - startRadians = 0.5*math.pi + startRadians = 0.5 * math.pi else: - startRadians = 0.5*radiansPerElementVessel*elementsCountAcross + startRadians = 0.5 * radiansPerElementVessel * elementsCountAcross if v == (vesselsCount - 1): startRadians -= math.pi px, pd1 = createCirclePoints(vx, vAxis1, vAxis2, elementsCountAroundVessel, startRadians) - vox [-1].append(px) + vox[-1].append(px) vod1[-1].append(pd1) - vod2[-1].append([ vd3 ]*elementsCountAroundVessel) + vod2[-1].append([vd3] * elementsCountAroundVessel) if useCubicHermiteThroughVesselWall: - vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness) for d1 in pd1 ]) + vod3[-1].append([vector.setMagnitude(vector.crossproduct3(d1, vd3), + vesselWallThickness * vesselWallThicknessProportions[n3]) + for d1 in pd1]) # calculate common ostium vessel node derivatives map - mvPointsx = [ None ]*vesselsCount - mvPointsd1 = [ None ]*vesselsCount - mvPointsd2 = [ None ]*vesselsCount - mvPointsd3 = [ None ]*vesselsCount - mvDerivativesMap = [ None ]*vesselsCount - mvMeanCount = [ None ]*vesselsCount # stores 1 if first reference to common point between vessels, 2 if second. Otherwise 0. + mvPointsx = [None] * vesselsCount + mvPointsd1 = [None] * vesselsCount + mvPointsd2 = [None] * vesselsCount + mvPointsd3 = [None] * vesselsCount + mvDerivativesMap = [None] * vesselsCount + mvMeanCount = [None] * vesselsCount # stores 1 if first reference to common point between vessels, 2 if second. Otherwise 0. for v in range(vesselsCount): if vesselsCount == 1: mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvDerivativesMap[v] = \ ox, od1, od2, od3 if useCubicHermiteThroughOstiumWall else None, None - mvMeanCount[v] = [ 0 ]*elementsCountsAroundVessels[v] + mvMeanCount[v] = [0] * elementsCountsAroundVessels[v] else: iv = max(0, v - 1) - oa = elementsCountAroundMid - iv*oinc - ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc - mvPointsx [v] = [] + oa = elementsCountAroundMid - iv * oinc + ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv * oinc + mvPointsx[v] = [] mvPointsd1[v] = [] mvPointsd2[v] = [] mvPointsd3[v] = [] if useCubicHermiteThroughOstiumWall else None mvDerivativesMap[v] = [] - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): mvPointsd1[v].append([]) mvPointsd2[v].append([]) - mvPointsx [v].append([]) + mvPointsx[v].append([]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v].append([]) mvDerivativesMap[v].append([]) if v == 0: # first end vessel mvPointsd1[v][n3] += od1[n3][oa:ob + 1] mvPointsd2[v][n3] += od2[n3][oa:ob + 1] - mvPointsx [v][n3] += ox [n3][oa:ob + 1] + mvPointsx[v][n3] += ox[n3][oa:ob + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][oa:ob + 1] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(nodesCountFreeEnd - 2): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) - mvPointsx [v][n3] += reversed(xx [iv][n3]) + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) + mvPointsx[v][n3] += reversed(xx[iv][n3]) mvPointsd1[v][n3] += reversed(xd1[iv][n3]) mvPointsd2[v][n3] += reversed(xd2[iv][n3]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += reversed(xd3[iv][n3]) for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, -1, 0), (1, 0, 0), None)) if n3 == 0: - mvMeanCount[v] = [ 1 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 1 ]*elementsCountAcross + mvMeanCount[v] = [1] + [0] * (nodesCountFreeEnd - 2) + [1] * elementsCountAcross elif v < (vesselsCount - 1): # middle vessels # left: - mvPointsx [v][n3] += ox [n3][oa - oinc:oa + 1] + mvPointsx[v][n3] += ox[n3][oa - oinc:oa + 1] mvPointsd1[v][n3] += od1[n3][oa - oinc:oa + 1] mvPointsd2[v][n3] += od2[n3][oa - oinc:oa + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][oa - oinc:oa + 1] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(oinc - 1): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) # across - mvPointsx [v][n3] += xx [iv][n3] + mvPointsx[v][n3] += xx[iv][n3] mvPointsd1[v][n3] += xd1[iv][n3] mvPointsd2[v][n3] += xd2[iv][n3] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += xd3[iv][n3] for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 0, 0), None)) # right - mvPointsx [v][n3] += ox [n3][ob:ob + oinc + 1] + mvPointsx[v][n3] += ox[n3][ob:ob + oinc + 1] mvPointsd1[v][n3] += od1[n3][ob:ob + oinc + 1] mvPointsd2[v][n3] += od2[n3][ob:ob + oinc + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][ob:ob + oinc + 1] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(oinc - 1): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) # across reverse - mvPointsx [v][n3] += reversed(xx [iv + 1][n3]) + mvPointsx[v][n3] += reversed(xx[iv + 1][n3]) mvPointsd1[v][n3] += reversed(xd1[iv + 1][n3]) mvPointsd2[v][n3] += reversed(xd2[iv + 1][n3]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += reversed(xd3[iv + 1][n3]) for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, -1, 0), (1, 0, 0), None)) if n3 == 0: - mvMeanCount[v] = [ 1 ] + [ 0 ]*(oinc - 1) + [ 2 ]*(elementsCountAcross + 1) + [ 0 ]*(oinc - 1) + [ 1 ]*elementsCountAcross + mvMeanCount[v] = [1] + [0] * (oinc - 1) + [2] * (elementsCountAcross + 1) + [0] * (oinc - 1) + \ + [1] * elementsCountAcross else: # last end vessel - mvPointsx [v][n3] += ox [n3][ob:] + [ ox [n3][0] ] - mvPointsd1[v][n3] += od1[n3][ob:] + [ od1[n3][0] ] - mvPointsd2[v][n3] += od2[n3][ob:] + [ od2[n3][0] ] + mvPointsx[v][n3] += ox[n3][ob:] + [ox[n3][0]] + mvPointsd1[v][n3] += od1[n3][ob:] + [od1[n3][0]] + mvPointsd2[v][n3] += od2[n3][ob:] + [od2[n3][0]] if useCubicHermiteThroughOstiumWall: - mvPointsd3[v][n3] += od3[n3][ob:] + [ od3[n3][0] ] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvPointsd3[v][n3] += od3[n3][ob:] + [od3[n3][0]] + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(nodesCountFreeEnd - 2): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) - mvPointsx [v][n3] += xx [iv][n3] + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) + mvPointsx[v][n3] += xx[iv][n3] mvPointsd1[v][n3] += xd1[iv][n3] mvPointsd2[v][n3] += xd2[iv][n3] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += xd3[iv][n3] for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 0, 0), None)) if n3 == 0: - mvMeanCount[v] = [ 2 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 2 ]*elementsCountAcross + mvMeanCount[v] = [2] + [0] * (nodesCountFreeEnd - 2) + [2] * elementsCountAcross # calculate derivative 2 around free sides of inlets to fit vessel derivatives for v in range(vesselsCount): - for n3 in [1]: # was range(2), now using curvature for inside: - #print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v]) - #print('mvPointsx [v][n3]', mvPointsx [v][n3]) - #print('mvPointsd1[v][n3]', mvPointsd1[v][n3]) - #print('mvPointsd2[v][n3]', mvPointsd2[v][n3]) - #print('mvDerivativesMap[v][n3]', mvDerivativesMap[v][n3]) + for n3 in [elementsCountThroughWall]: # was range(2), now using curvature for inside: + # print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v]) + # print('mvPointsx [v][n3]', mvPointsx [v][n3]) + # print('mvPointsd1[v][n3]', mvPointsd1[v][n3]) + # print('mvPointsd2[v][n3]', mvPointsd2[v][n3]) + # print('mvDerivativesMap[v][n3]', mvDerivativesMap[v][n3]) for n1 in range(elementsCountsAroundVessels[v]): - d2Map = mvDerivativesMap[v][n3][n1][1] if (mvDerivativesMap[v] and mvDerivativesMap[v][n3][n1]) else None + d2Map = mvDerivativesMap[v][n3][n1][1] if (mvDerivativesMap[v] and mvDerivativesMap[v][n3][n1]) \ + else None sf1 = d2Map[0] if d2Map else 0.0 sf2 = d2Map[1] if d2Map else 1.0 - nx = [ vox[v][n3][n1], mvPointsx[v][n3][n1] ] - nd2 = [ [ d*elementsCountAlong for d in vod2[v][n3][n1] ], [ (sf1*mvPointsd1[v][n3][n1][c] + sf2*mvPointsd2[v][n3][n1][c]) for c in range(3) ] ] - nd2f = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative = True, fixEndDirection = True) - ndf = [ d/elementsCountAlong for d in nd2f[1] ] + nx = [vox[v][n3][n1], mvPointsx[v][n3][n1]] + nd2 = [[d * elementsCountAlong for d in vod2[v][n3][n1]], + [(sf1 * mvPointsd1[v][n3][n1][c] + sf2 * mvPointsd2[v][n3][n1][c]) for c in range(3)]] + nd2f = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative=True, fixEndDirection=True) + ndf = [d / elementsCountAlong for d in nd2f[1]] # assign components to set original values: if sf1 == 0: for c in range(3): - mvPointsd2[v][n3][n1][c] = sf2*ndf[c] + mvPointsd2[v][n3][n1][c] = sf2 * ndf[c] elif sf2 == 0: if mvMeanCount[v][n1] < 2: for c in range(3): - mvPointsd1[v][n3][n1][c] = sf1*ndf[c] + mvPointsd1[v][n3][n1][c] = sf1 * ndf[c] else: # take mean of values from this and last vessel for c in range(3): - mvPointsd1[v][n3][n1][c] = 0.5*(mvPointsd1[v][n3][n1][c] + sf1*ndf[c]) + mvPointsd1[v][n3][n1][c] = 0.5 * (mvPointsd1[v][n3][n1][c] + sf1 * ndf[c]) else: - #print('v', v, 'n3', n3, 'n1', n1, ':', vector.magnitude(ndf), 'vs.', vector.magnitude(nd2[1]), 'd2Map', d2Map) + # print('v', v, 'n3', n3, 'n1', n1, ':', vector.magnitude(ndf), 'vs.', vector.magnitude(nd2[1]), + # 'd2Map', d2Map) pass # calculate inner d2 derivatives around ostium from outer using track surface curvature factor = 1.0 for n1 in range(elementsCountAroundOstium): - trackDirection = vector.normalise(od2[1][n1]) - trackDistance = factor*vector.magnitude(od2[1][n1]) - tx = [ None, ox[1][n1], None ] - td1 = [ None, vector.setMagnitude(od2[1][n1], trackDistance), None ] - td2 = [ None, vector.setMagnitude(od1[1][n1], -trackDistance), None ] + trackDirection = vector.normalise(od2[elementsCountThroughWall][n1]) + trackDistance = factor * vector.magnitude(od2[elementsCountThroughWall][n1]) + tx = [None, ox[elementsCountThroughWall][n1], None] + td1 = [None, vector.setMagnitude(od2[elementsCountThroughWall][n1], trackDistance), None] + td2 = [None, vector.setMagnitude(od1[elementsCountThroughWall][n1], -trackDistance), None] positionBackward = trackSurface.trackVector(oPositions[n1], trackDirection, -trackDistance) tx[0], d1, d2 = trackSurface.evaluateCoordinates(positionBackward, derivatives=True) sd1, sd2, sd3 = calculate_surface_axes(d1, d2, trackDirection) @@ -608,11 +689,13 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta sd1, sd2, sd3 = calculate_surface_axes(d1, d2, trackDirection) td1[2] = vector.setMagnitude(sd1, trackDistance) td2[2] = vector.setMagnitude(sd2, trackDistance) - newd2 = interp.projectHermiteCurvesThroughWall(tx, td1, td2, 1, -ostiumWallThickness)[1] - # assign components to set in all lists: - for c in range(3): - od2[0][n1][c] = newd2[c]/factor - #for n in [ 0, 1, 2 ]: + for n3 in range(elementsCountThroughWall): + xi3 = 1 - ostiumWallThicknessXi3List[n3] + newd2 = interp.projectHermiteCurvesThroughWall(tx, td1, td2, 1, -ostiumWallThickness * xi3)[1] + # assign components to set in all lists: + for c in range(3): + od2[n3][n1][c] = newd2[c] / factor + # for n in [ 0, 1, 2 ]: # node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, tx [n]) @@ -623,7 +706,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta if isOutlet: # reverse directions of d1 and d2 on vessels and ostium base for c in range(3): - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountAroundOstium): od1[n3][n1][c] = -od1[n3][n1][c] od2[n3][n1][c] = -od2[n3][n1][c] @@ -643,12 +726,13 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ############## oNodeId = [] - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): oNodeId.append([]) for n1 in range(elementsCountAroundOstium): - node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) + node = nodes.createNode(nodeIdentifier, + nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, ox [n3][n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, ox[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, od1[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, od2[n3][n1]) if useCubicHermiteThroughOstiumWall: @@ -659,12 +743,13 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta xNodeId = [] for iv in range(vesselsCount - 1): xNodeId.append([]) - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): xNodeId[iv].append([]) for n2 in range(elementsCountAcross - 1): - node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) + node = nodes.createNode(nodeIdentifier, + nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xx [iv][n3][n2]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xx[iv][n3][n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, xd1[iv][n3][n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, xd2[iv][n3][n2]) if useCubicHermiteThroughOstiumWall: @@ -672,7 +757,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta xNodeId[iv][n3].append(nodeIdentifier) nodeIdentifier += 1 - #for v in range(vesselsCount): + # for v in range(vesselsCount): # node = nodes.createNode(nodeIdentifier, nodetemplate) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vcx [v]) @@ -680,9 +765,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, vcd2[v]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, vcd3[v]) # nodeIdentifier += 1 - # for n3 in range(2): + # for n3 in range(elementsCountThroughWall + 1): # for n1 in range(elementsCountsAroundVessels[v]): - # node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3) + # node = nodes.createNode(nodeIdentifier, + # nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vox [v][n3][n1]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, vod1[v][n3][n1]) @@ -693,37 +779,40 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # nodeIdentifier += 1 # get identifiers of nodes around each vessel at ostium end - mvNodeId = [ None ]*vesselsCount + mvNodeId = [None] * vesselsCount for v in range(vesselsCount): if vesselsCount == 1: mvNodeId[v] = oNodeId else: iv = max(0, v - 1) - mvNodeId[v] = [ None, None ] - oa = elementsCountAroundMid - iv*oinc - ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc - for n3 in range(2): + mvNodeId[v] = [None for n3 in range(elementsCountThroughWall + 1)] + oa = elementsCountAroundMid - iv * oinc + ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv * oinc + for n3 in range(elementsCountThroughWall + 1): if v == 0: # first end vessel - mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) + mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + \ + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) elif v == (vesselsCount - 1): # last end vessels - mvNodeId[v][n3] = oNodeId[n3][ob:] + [ oNodeId[n3][0] ] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) + mvNodeId[v][n3] = oNodeId[n3][ob:] + [oNodeId[n3][0]] + \ + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) else: # mid vessels - mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3])) + mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + \ + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3])) ################# - # Create elementss + # Create elements ################# - tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) - #tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) - - #eft = tricubichermite.createEftBasic() - #elementtemplate = mesh.createElementtemplate() - #elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) - #elementtemplate.defineField(coordinates, -1, eft) - - #elementtemplateX = mesh.createElementtemplate() - #elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) + # tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) + # tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) + # + # eft = tricubichermite.createEftBasic() + # elementtemplate = mesh.createElementtemplate() + # elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) + # elementtemplate.defineField(coordinates, -1, eft) + # + # elementtemplateX = mesh.createElementtemplate() + # elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) for v in range(vesselsCount): if vesselMeshGroups or ostiumMeshGroups: @@ -738,18 +827,19 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap = \ vox[v], vod1[v], vod2[v], vod3[v] if useCubicHermiteThroughVesselWall else None, None, None # reverse order of nodes around: - for px in [ startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, \ - endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap ]: + for px in [startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, + endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap]: if px: - for n3 in range(2): - px[n3] = [ px[n3][0] ] + px[n3][len(px[n3]) - 1:0:-1] + for n3 in range(elementsCountThroughWall + 1): + px[n3] = [px[n3][0]] + px[n3][len(px[n3]) - 1:0:-1] if vesselsCount > 1: # must switch in and out xi1 maps around corners in startDerivativesMap - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountsAroundVessels[v]): derivativesMap = startDerivativesMap[n3][n1] if len(derivativesMap) == 4: - startDerivativesMap[n3][n1] = derivativesMap[3], derivativesMap[1], derivativesMap[2], derivativesMap[0] + startDerivativesMap[n3][n1] = derivativesMap[3], derivativesMap[1], \ + derivativesMap[2], derivativesMap[0] if ostiumMeshGroups: rowMeshGroups[0] += ostiumMeshGroups else: @@ -760,22 +850,20 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta if ostiumMeshGroups: rowMeshGroups[-1] += ostiumMeshGroups - #print('endPointsx ', endPointsx ) - #print('endPointsd1', endPointsd1) - #print('endPointsd2', endPointsd2) - #print('endPointsd3', endPointsd3) - #print('endNodeId', endNodeId) - #print('endDerivativesMap', endDerivativesMap) + # print('endPointsx ', endPointsx ) + # print('endPointsd1', endPointsd1) + # print('endPointsd2', endPointsd2) + # print('endPointsd3', endPointsd3) + # print('endNodeId', endNodeId) + # print('endDerivativesMap', endDerivativesMap) nodeIdentifier, elementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, - forceMidLinearXi3 = not useCubicHermiteThroughVesselWall, - maxStartThickness = vesselWallThickness, - maxEndThickness = vesselWallThickness, - elementsCountRadial = elementsCountAlong, - meshGroups = rowMeshGroups) + forceMidLinearXi3=not useCubicHermiteThroughVesselWall, + maxStartThickness=vesselWallThickness, maxEndThickness=vesselWallThickness, + elementsCountRadial=elementsCountAlong, meshGroups=rowMeshGroups, wallAnnotationGroups=wallAnnotationGroups) fm.endChange() return nodeIdentifier, elementIdentifier, (ox, od1, od2, od3, oNodeId, oPositions) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 1715d938..721ed3c1 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -34,7 +34,7 @@ remapEftNodeValueLabelsVersion from scaffoldmaker.utils.geometry import createEllipsePoints from scaffoldmaker.utils.tracksurface import TrackSurface -from scaffoldmaker.utils.zinc_utils import exnodeStringFromNodeValues +from scaffoldmaker.utils.zinc_utils import exnodeStringFromNodeValues, mesh_destroy_elements_and_nodes_by_identifiers class MeshType_3d_stomach1(Scaffold_base): @@ -91,8 +91,8 @@ class MeshType_3d_stomach1(Scaffold_base): '_AnnotationGroup': True, 'dimension': 1, 'identifierRanges': '6', - 'name': get_stomach_term('pylorus')[0], - 'ontId': get_stomach_term('pylorus')[1] + 'name': get_stomach_term('pyloric canal')[0], + 'ontId': get_stomach_term('pyloric canal')[1] }, { '_AnnotationGroup': True, @@ -149,8 +149,8 @@ class MeshType_3d_stomach1(Scaffold_base): '_AnnotationGroup': True, 'dimension': 1, 'identifierRanges': '7', - 'name': get_stomach_term('pylorus')[0], - 'ontId': get_stomach_term('pylorus')[1] + 'name': get_stomach_term('pyloric canal')[0], + 'ontId': get_stomach_term('pyloric canal')[1] }, { '_AnnotationGroup': True, @@ -170,15 +170,15 @@ class MeshType_3d_stomach1(Scaffold_base): }, '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], [ - [ [ 12.4, 11.9, 0.0 ], [ -0.1, -14.6, 0.0 ], [ 8.7, -1.4, 0.0 ], [ -0.7, -3.5, 0.0 ], [ 0.0, 0.0, 7.4 ], [ 0.0, 0.0, 1.5] ], - [ [ 9.5, -0.2, 0.0 ], [ -5.7, -9.0, 0.0 ], [ 6.6, -5.1, 0.0 ], [ -3.5, -3.9, 0.0 ], [ 0.0, 0.0, 8.5 ], [ 0.0, 0.0, 0.7] ], - [ [ 2.8, -5.5, 0.0 ], [ -6.6, -2.6, 0.0 ], [ 2.1, -9.1, 0.0 ], [ -4.0, -1.6, 0.0 ], [ 0.0, 0.0, 9.0 ], [ 0.0, 0.0, 0.1] ], - [ [ -2.6, -6.0, 0.0 ], [ -5.7, 0.7, 0.0 ], [ -1.6, -9.2, 0.0 ], [ -4.0, 0.5, 0.0 ], [ 0.0, 0.0, 8.8 ], [ 0.0, 0.0, -0.4] ], - [ [ -8.2, -4.1, 0.0 ], [ -5.3, 3.9, 0.0 ], [ -6.0, -8.0, 0.0 ], [ -2.6, 2.7, 0.0 ], [ 0.0, 0.0, 8.1 ], [ 0.0, 0.0, -1.2] ], - [ [ -12.3, 1.5, 0.0 ], [ -1.5, 7.0, 0.0 ], [ -6.4, -3.5, 0.0 ], [ 1.5, 4.0, 0.0 ], [ 0.0, 0.0, 6.2 ], [ 0.0, 0.0, -2.8] ], - [ [ -10.8, 8.5, 0.0 ], [ 0.6, 5.4, 0.0 ], [ -2.9, 0.0, 0.0 ], [ 0.8, 1.0, 0.0 ], [ 0.0, 0.0, 2.4 ], [ 0.0, 0.0, -0.6] ], - [ [ -10.7, 12.2, 0.0 ], [ -0.3, 3.2, 0.0 ], [ -3.5, -0.3, 0.0 ], [ -0.3, -0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, 0.4] ], - [ [ -11.3, 14.8, 0.0 ], [ -0.9, 2.0, 0.0 ], [ -3.5, -0.3, 0.0 ], [ 0.3, 0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, -0.4] ] ] ), + [ [ 11.3, 13.4, 0.0 ], [ 0.3, -13.4, 0.0 ], [ 9.4, -1.3, 0.0 ], [ -1.0, -5.6, 0.0 ], [ 0.0, 0.0, 7.4 ], [ 0.0, 0.0, 1.5 ] ], + [ [ 9.3, 2.1, 0.0 ], [ -4.4, -8.7, 0.0 ], [ 7.4, -6.1, 0.0 ], [ -3.0, -3.9, 0.0 ], [ 0.0, 0.0, 8.5 ], [ 0.0, 0.0, 0.7 ] ], + [ [ 4.0, -3.6, 0.0 ], [ -6.8, -3.8, 0.0 ], [ 3.7, -9.4, 0.0 ], [ -4.9, -2.4, 0.0 ], [ 0.0, 0.0, 9.0 ], [ 0.0, 0.0, 0.1 ] ], + [ [ -3.4, -5.1, 0.0 ], [ -6.4, 0.6, 0.0 ], [ -2.4, -10.9, 0.0 ], [ -5.0, 0.9, 0.0 ], [ 0.0, 0.0, 8.8 ], [ 0.0, 0.0, -0.5 ] ], + [ [ -8.1, -3.2, 0.0 ], [ -4.4, 3.8, 0.0 ], [ -6.7, -8.3, 0.0 ], [ -2.5, 3.4, 0.0 ], [ 0.0, 0.0, 8.1 ], [ 0.0, 0.0, -1.2 ] ], + [ [ -11.4, 2.3, 0.0 ], [ -1.4, 6.4, 0.0 ], [ -6.9, -4.0, 0.0 ], [ 1.9, 4.2, 0.0 ], [ 0.0, 0.0, 6.2 ], [ 0.0, 0.0, -2.8 ] ], + [ [ -10.7, 8.9, 0.0 ], [ 0.3, 5.0, 0.0 ], [ -2.9, 0.0, 0.0 ], [ 0.9, 1.1, 0.0 ], [ 0.0, 0.0, 2.4 ], [ 0.0, 0.0, -0.6 ] ], + [ [ -10.7, 12.2, 0.0 ], [ -0.3, 3.0, 0.0 ], [ -3.5, -0.3, 0.0 ], [ -0.3, -0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, 0.4 ] ], + [ [ -11.3, 14.8, 0.0 ], [ -0.9, 2.2, 0.0 ], [ -3.5, -0.3, 0.0 ], [ 0.3, 0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, -0.4 ] ] ] ), 'userAnnotationGroups': [ { @@ -206,8 +206,8 @@ class MeshType_3d_stomach1(Scaffold_base): '_AnnotationGroup': True, 'dimension': 1, 'identifierRanges': '7', - 'name': get_stomach_term('pylorus')[0], - 'ontId': get_stomach_term('pylorus')[1] + 'name': get_stomach_term('pyloric canal')[0], + 'ontId': get_stomach_term('pyloric canal')[1] }, { '_AnnotationGroup': True, @@ -226,18 +226,20 @@ class MeshType_3d_stomach1(Scaffold_base): '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 + 'Number of elements through wall': 4, 'Unit scale': 1.0, 'Outlet': False, 'Ostium diameter': 25.0, 'Ostium length': 15.0, 'Ostium wall thickness': 5.0, + 'Ostium wall relative thicknesses': [0.55, 0.15, 0.25, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 5.0, 'Vessel wall thickness': 5.0, + 'Vessel wall relative thicknesses': [0.55, 0.15, 0.25, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -255,18 +257,20 @@ class MeshType_3d_stomach1(Scaffold_base): 'Number of elements across common': 2, 'Number of elements around ostium': 12, 'Number of elements along': 2, - 'Number of elements through wall': 1, # not implemented for > 1 + 'Number of elements through wall': 4, 'Unit scale': 1.0, 'Outlet': False, 'Ostium diameter': 1.5, 'Ostium length': 1.5, - 'Ostium wall thickness': 0.25, + 'Ostium wall thickness': 0.45, + 'Ostium wall relative thicknesses': [0.75, 0.05, 0.15, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 0.5, - 'Vessel wall thickness': 0.2, + 'Vessel wall thickness': 0.45, + 'Vessel wall relative thicknesses': [0.75, 0.05, 0.15, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -284,18 +288,20 @@ class MeshType_3d_stomach1(Scaffold_base): 'Number of elements across common': 2, 'Number of elements around ostium': 12, 'Number of elements along': 2, - 'Number of elements through wall': 1, # not implemented for > 1 + 'Number of elements through wall': 4, 'Unit scale': 1.0, 'Outlet': False, - 'Ostium diameter': 4.0, - 'Ostium length': 3.5, + 'Ostium diameter': 5.0, + 'Ostium length': 5.0, 'Ostium wall thickness': 0.5, + 'Ostium wall relative thicknesses': [0.65, 0.12, 0.18, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, - 'Vessel inner diameter': 1.25, + 'Vessel inner diameter': 2.0, 'Vessel wall thickness': 0.5, + 'Vessel wall relative thicknesses': [0.65, 0.12, 0.18, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -339,7 +345,12 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Number of elements around duodenum': 12, 'Number of elements between cardia and duodenum': 6, 'Number of elements across cardia': 1, + 'Number of elements through wall': 4, 'Wall thickness': 5.0, + 'Mucosa relative thickness': 0.55, + 'Submucosa relative thickness': 0.15, + 'Circular muscle layer relative thickness': 0.25, + 'Longitudinal muscle layer relative thickness': 0.05, 'Limiting ridge': False, 'Gastro-esophagal junction': copy.deepcopy(ostiumOption), 'Gastro-esophagal junction position along factor': 0.35, @@ -353,7 +364,11 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Number of elements around esophagus'] = 12 options['Number of elements around duodenum'] = 14 options['Number of elements between cardia and duodenum'] = 2 - options['Wall thickness'] = 0.25 + options['Wall thickness'] = 0.45 + options['Mucosa relative thickness'] = 0.75 + options['Submucosa relative thickness'] = 0.05 + options['Circular muscle layer relative thickness'] = 0.15 + options['Longitudinal muscle layer relative thickness'] = 0.05 options['Gastro-esophagal junction position along factor'] = 0.53 options['Cardia derivative factor'] = 0.3 options['Limiting ridge'] = True @@ -362,6 +377,10 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Number of elements around duodenum'] = 14 options['Number of elements between cardia and duodenum'] = 2 options['Wall thickness'] = 0.5 + options['Mucosa relative thickness'] = 0.65 + options['Submucosa relative thickness'] = 0.12 + options['Circular muscle layer relative thickness'] = 0.18 + options['Longitudinal muscle layer relative thickness'] = 0.05 options['Gastro-esophagal junction position along factor'] = 0.55 options['Cardia derivative factor'] = 0.2 options['Limiting ridge'] = True @@ -377,7 +396,12 @@ def getOrderedOptionNames(): 'Number of elements around duodenum', 'Number of elements between cardia and duodenum', 'Number of elements across cardia', + 'Number of elements through wall', 'Wall thickness', + 'Mucosa relative thickness', + 'Submucosa relative thickness', + 'Circular muscle layer relative thickness', + 'Longitudinal muscle layer relative thickness', 'Limiting ridge', 'Gastro-esophagal junction', 'Gastro-esophagal junction position along factor', @@ -408,10 +432,10 @@ def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType): @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()) + \ @@ -444,6 +468,8 @@ def checkOptions(cls, options): options['Number of elements around duodenum'] += 1 if options['Number of elements between cardia and duodenum'] < 2: options['Number of elements between cardia and duodenum'] = 2 + if options['Number of elements through wall'] != (1 or 4): + options['Number of elements through wall'] = 4 if options['Cardia derivative factor'] <= 0.0: options['Cardia derivative factor'] = 0.1 for key in [ @@ -456,13 +482,26 @@ def checkOptions(cls, options): @classmethod def updateSubScaffoldOptions(cls, options): - ''' + """ Update ostium sub-scaffold options which depend on parent options. - ''' + """ wallThickness = options['Wall thickness'] ostiumOptions = options['Gastro-esophagal junction'] ostiumSettings = ostiumOptions.getScaffoldSettings() ostiumSettings['Ostium wall thickness'] = wallThickness + elementsCountThroughWall = options['Number of elements through wall'] + ostiumSettings['Number of elements through wall'] = elementsCountThroughWall + if elementsCountThroughWall == 1: + ostiumSettings['Ostium wall relative thicknesses'] = [1.0] + ostiumSettings['Vessel wall relative thicknesses'] = [1.0] + else: + mucosaRelThickness = options['Mucosa relative thickness'] + submucosaRelThickness = options['Submucosa relative thickness'] + circularRelThickness = options['Circular muscle layer relative thickness'] + longRelThickness = options['Longitudinal muscle layer relative thickness'] + relThicknesses = [mucosaRelThickness, submucosaRelThickness, circularRelThickness, longRelThickness] + ostiumSettings['Ostium wall relative thicknesses'] = relThicknesses + ostiumSettings['Vessel wall relative thicknesses'] = relThicknesses @classmethod def generateBaseMesh(cls, region, options): @@ -477,8 +516,12 @@ def generateBaseMesh(cls, region, options): elementsCountAroundEso = options['Number of elements around esophagus'] elementsCountAroundDuod = options['Number of elements around duodenum'] elementsAlongCardiaToDuod = options['Number of elements between cardia and duodenum'] - elementsCountThroughWall = 1 + 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 = False useCubicHermiteThroughWall = not (options['Use linear through wall']) @@ -525,7 +568,8 @@ def generateBaseMesh(cls, region, options): # Extract length of each group along stomach from central path arcLengthOfGroupsAlong = [] - stomachTermsAlong = [None, 'fundus of stomach', 'body of stomach', 'pyloric antrum', 'pylorus', 'duodenum'] + stomachTermsAlong = [None, 'fundus of stomach', 'body of stomach', + 'pyloric antrum', 'pyloric canal', 'duodenum'] for i in range(len(stomachTermsAlong)): tmpRegion = region.createRegion() centralPath.generate(tmpRegion) @@ -567,7 +611,7 @@ def generateBaseMesh(cls, region, options): fundusGroup = AnnotationGroup(region, get_stomach_term("fundus of stomach")) bodyGroup = AnnotationGroup(region, get_stomach_term("body of stomach")) antrumGroup = AnnotationGroup(region, get_stomach_term("pyloric antrum")) - pylorusGroup = AnnotationGroup(region, get_stomach_term("pylorus")) + pylorusGroup = AnnotationGroup(region, get_stomach_term("pyloric canal")) duodenumGroup = AnnotationGroup(region, get_stomach_term("duodenum")) annotationGroupAlong = [[stomachGroup, fundusGroup], @@ -576,6 +620,19 @@ def generateBaseMesh(cls, region, options): [stomachGroup, pylorusGroup], [stomachGroup, duodenumGroup]] + longitudinalMuscleGroup = AnnotationGroup(region, get_stomach_term("longitudinal muscle layer of stomach")) + circularMuscleGroup = AnnotationGroup(region, get_stomach_term("circular muscle layer of stomach")) + submucosaGroup = AnnotationGroup(region, get_stomach_term("submucosa of stomach")) + mucosaGroup = AnnotationGroup(region, get_stomach_term("mucosa of stomach")) + + if elementsCountThroughWall == 1: + annotationGroupsThroughWall = [[]] + else: + annotationGroupsThroughWall = [[mucosaGroup], + [submucosaGroup], + [circularMuscleGroup], + [longitudinalMuscleGroup]] + # annotation fiducial points markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") @@ -712,10 +769,28 @@ def generateBaseMesh(cls, region, options): stomachMeshGroup = stomachGroup.getMeshGroup(mesh) allAnnotationGroups += [esophagusGroup, esophagogastricJunctionGroup] + ostiumWallAnnotationGroups = [] + if elementsCountThroughWall == 4: + esophagusMucosaGroup = AnnotationGroup(region, get_stomach_term("esophagus mucosa")) + esophagusSubmucosaGroup = AnnotationGroup(region, get_stomach_term("submucosa of esophagus")) + esophagusCircularGroup = AnnotationGroup(region, get_stomach_term("esophagus smooth muscle circular layer")) + esophagusLongitudinalGroup = AnnotationGroup(region, + get_stomach_term("esophagus smooth muscle longitudinal layer")) + + ostiumWallAnnotationGroups = [[esophagusMucosaGroup, mucosaGroup], + [esophagusSubmucosaGroup, submucosaGroup], + [esophagusCircularGroup, circularMuscleGroup], + [esophagusLongitudinalGroup, longitudinalMuscleGroup]] + + allAnnotationGroups += [esophagusMucosaGroup, esophagusSubmucosaGroup, + esophagusCircularGroup, esophagusLongitudinalGroup] + nextNodeIdentifier, nextElementIdentifier, (o1_x, o1_d1, o1_d2, o1_d3, o1_NodeId, o1_Positions) = \ generateOstiumMesh(region, GEJSettings, trackSurfaceStomach, GEJPosition, axis1, - nodeIdentifier, elementIdentifier, vesselMeshGroups=[[stomachMeshGroup, esophagusMeshGroup]], - ostiumMeshGroups=[stomachMeshGroup, esophagogastricJunctionMeshGroup]) + nodeIdentifier, elementIdentifier, + vesselMeshGroups=[[stomachMeshGroup, esophagusMeshGroup]], + ostiumMeshGroups=[stomachMeshGroup, esophagogastricJunctionMeshGroup], + wallAnnotationGroups=ostiumWallAnnotationGroups) stomachStartNode = nextNodeIdentifier stomachStartElement = nextElementIdentifier @@ -723,7 +798,8 @@ def generateBaseMesh(cls, region, options): elementIdentifier = nextElementIdentifier fundusEndPosition = trackSurfaceStomach.createPositionProportion(0.0, fundusEndPositionAlongFactor) - xFundusEnd, d1FundusEnd, d2FundusEnd = trackSurfaceStomach.evaluateCoordinates(fundusEndPosition, derivatives=True) + xFundusEnd, d1FundusEnd, d2FundusEnd = trackSurfaceStomach.evaluateCoordinates(fundusEndPosition, + derivatives=True) elementsAlongFundus = elementsAroundQuarterEso + (0 if limitingRidge else 1) # From fundus end to duodenum @@ -770,7 +846,8 @@ def generateBaseMesh(cls, region, options): xAlongGCEsoToFundusEnd, d2AlongGCEsoToFundusEnd = \ interp.sampleCubicHermiteCurvesSmooth(xAlongUpFundus, d2AlongUpFundus, elementsAlongGCFromEsoToFundusEnd, - derivativeMagnitudeStart=vector.magnitude(d2AlongUpFundus[0]))[0:2] + derivativeMagnitudeStart=vector.magnitude( + d2AlongUpFundus[0]))[0:2] # Sample from limiting ridge to duodenum xAlongDownFundus[0] = xAlongGCEsoToFundusEnd[-1] @@ -882,7 +959,8 @@ def generateBaseMesh(cls, region, options): getSmoothedSampledPointsOnTrackSurface(trackSurfaceStomach, 0.0, GCProportion2, endProportion1, endProportion2, elementsAroundHalfDuod + 1, startDerivative=d1GC, endDerivative=d1EndOstium, - endDerivativeMagnitude=cardiaDerivativeFactor * vector.magnitude(d2))[0] + endDerivativeMagnitude=cardiaDerivativeFactor * + vector.magnitude(d2))[0] # Second half ostiumIdx2 = -n2 startPosition = o1_Positions[ostiumIdx2] @@ -910,11 +988,14 @@ def generateBaseMesh(cls, region, options): closestIdxOnCentralPath = interp.getNearestPointIndex(sx, xStart) if 0 < closestIdxOnCentralPath < len(sx) - 1: # Check if xStart is closer to upstream or downstream of closestIdx - xOnGCPrevElem = [sx[closestIdxOnCentralPath - 1][c] + sd2[closestIdxOnCentralPath - 1][c] for c in range(3)] + xOnGCPrevElem = [sx[closestIdxOnCentralPath - 1][c] + sd2[closestIdxOnCentralPath - 1][c] for c in + range(3)] distBetweenXOnGCPrevElem = vector.magnitude([xStart[c] - xOnGCPrevElem[c] for c in range(3)]) - xOnGCNextElem = [sx[closestIdxOnCentralPath + 1][c] + sd2[closestIdxOnCentralPath + 1][c] for c in range(3)] + xOnGCNextElem = [sx[closestIdxOnCentralPath + 1][c] + sd2[closestIdxOnCentralPath + 1][c] for c in + range(3)] distBetweenXOnGCNextElem = vector.magnitude([xStart[c] - xOnGCNextElem[c] for c in range(3)]) - eiLowerLimit = closestIdxOnCentralPath - (1 if distBetweenXOnGCNextElem > distBetweenXOnGCPrevElem else 0) + eiLowerLimit = closestIdxOnCentralPath - ( + 1 if distBetweenXOnGCNextElem > distBetweenXOnGCPrevElem else 0) elif closestIdxOnCentralPath == len(sx) - 1: eiLowerLimit = closestIdxOnCentralPath - 1 elif closestIdxOnCentralPath == 0: @@ -940,11 +1021,14 @@ def generateBaseMesh(cls, region, options): for iter in range(100): xiGuess = 0.5 * (xiLowerLimit + xiUpperLimit) x = interp.interpolateCubicHermite(xLowerLimit, d1LowerLimit, xUpperLimit, d1UpperLimit, xiGuess) - d2 = interp.interpolateCubicHermite(d2LowerLimit, d12LowerLimit, d2UpperLimit, d12UpperLimit, xiGuess) + d2 = interp.interpolateCubicHermite(d2LowerLimit, d12LowerLimit, d2UpperLimit, d12UpperLimit, + xiGuess) xGuess = [x[c] + d2[c] for c in range(3)] distBetweenXAndXStart = vector.magnitude([xStart[c] - xGuess[c] for c in range(3)]) - distBetweenXOnGCLowerLimitAndXStart = vector.magnitude([xStart[c] - xOnGCLowerLimit[c] for c in range(3)]) - distBetweenXOnGCUpperLimitAndXStart = vector.magnitude([xStart[c] - xOnGCUpperLimit[c] for c in range(3)]) + distBetweenXOnGCLowerLimitAndXStart = vector.magnitude( + [xStart[c] - xOnGCLowerLimit[c] for c in range(3)]) + distBetweenXOnGCUpperLimitAndXStart = vector.magnitude( + [xStart[c] - xOnGCUpperLimit[c] for c in range(3)]) if abs(distBetweenXAndXStart - distBetweenXAndXStartPrev) < tol: xProjection = x @@ -976,8 +1060,11 @@ def generateBaseMesh(cls, region, options): xAround, d1Around = createEllipsePoints(sx[-1], 2 * math.pi, sd2[-1], sd3[-1], elementsCountAroundDuod, startRadians=0.0) - d1Around = interp.smoothCubicHermiteDerivativesLoop(xAround, d1Around, - magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + d1Around = \ + interp.smoothCubicHermiteDerivativesLoop(xAround, d1Around, + magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN + ) + xAroundEllipse.append(xAround) d1AroundEllipse.append(d1Around) @@ -992,10 +1079,12 @@ def generateBaseMesh(cls, region, options): if n == elementsAroundHalfDuod: endPosition = o1_Positions[elementsAroundHalfEso] else: - endPosition = trackSurfaceStomach.findNearestPosition(xAroundBefore6Pt[n + (0 if n < elementsAroundHalfDuod else 1)]) + endPosition = trackSurfaceStomach.findNearestPosition( + xAroundBefore6Pt[n + (0 if n < elementsAroundHalfDuod else 1)]) endProportion1, endProportion2 = trackSurfaceStomach.getProportion(endPosition) xSampled = getSmoothedSampledPointsOnTrackSurface(trackSurfaceStomach, startProportion1, - startProportion2, endProportion1, endProportion2, 2)[0] + startProportion2, endProportion1, endProportion2, + 2)[0] xAve.append(xSampled[1]) # Find 6 pt junction @@ -1068,7 +1157,8 @@ def generateBaseMesh(cls, region, options): for n1 in range(1, elementsCountAroundDuod + 1): startPosition = trackSurfaceStomach.findNearestPosition(xAlongAround[-1][n1]) startProportion1, startProportion2 = trackSurfaceStomach.getProportion(startPosition) - endPosition = trackSurfaceStomach.findNearestPosition(xAroundEllipse[0][n1 + (0 if n1 < elementsAroundHalfDuod + 1 else -1)]) + endPosition = trackSurfaceStomach.findNearestPosition( + xAroundEllipse[0][n1 + (0 if n1 < elementsAroundHalfDuod + 1 else -1)]) endProportion1, endProportion2 = trackSurfaceStomach.getProportion(endPosition) xSampled = getSmoothedSampledPointsOnTrackSurface(trackSurfaceStomach, startProportion1, startProportion2, endProportion1, endProportion2, 2)[0] @@ -1110,7 +1200,8 @@ def generateBaseMesh(cls, region, options): elementsCountAlongTrackSurface)[1] if GCIdx < nodesCountFromEsoToApex: rotFrame = matrix.getRotationMatrixFromAxisAngle(vector.normalise(d2EsoToDuodGC[GCIdx]), math.pi) - d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in range(3)] + d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in + range(3)] d2GC = d2GCRot for nSide in range(2): @@ -1120,16 +1211,19 @@ def generateBaseMesh(cls, region, options): xAlongAround[0][elementsAroundHalfDuod - nLoop][c] for c in range(3)] else: rotFrame = matrix.getRotationMatrixFromAxisAngle(vector.normalise(d2EsoToDuodGC[GCIdx]), math.pi) - d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in range(3)] + d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in + range(3)] d2GC = d2GCRot xEnd = xAlongAround[0][elementsAroundHalfDuod + 1 + nLoop] d2End = [xAlongAround[1][elementsAroundHalfDuod + nLoop + 1][c] - - xAlongAround[0][elementsAroundHalfDuod + (1 if elementsCountAroundEso > 8 else 2) + nLoop][c] for c in range(3)] + xAlongAround[0][elementsAroundHalfDuod + + (1 if elementsCountAroundEso > 8 else 2) + nLoop][c] for c in range(3)] nx = [xEsoToDuodGC[GCIdx], xEnd] nd2 = [d2GC, d2End] - x, d2 = interp.sampleCubicHermiteCurves(nx, nd2, elementsAroundQuarterEso + 2, arcLengthDerivatives=True)[0:2] + x, d2 = interp.sampleCubicHermiteCurves(nx, nd2, elementsAroundQuarterEso + 2, + arcLengthDerivatives=True)[0:2] # Find closest sampled points onto track surface xProjectedPoints = [] @@ -1226,7 +1320,8 @@ def generateBaseMesh(cls, region, options): for n in range(len(xLoopsRight) - 1): xAroundRight.append(xLoopsRight[-(1 + n)][loopIdx]) - d1AroundRight.append(findDerivativeBetweenPoints(xLoopsRight[-(1 + n)][loopIdx], xLoopsRight[-(1 + n + 1)][loopIdx])) + d1AroundRight.append(findDerivativeBetweenPoints(xLoopsRight[-(1 + n)][loopIdx], + xLoopsRight[-(1 + n + 1)][loopIdx])) if loopIdx < elementsAroundQuarterEso: # additional elements upstream of triple point xLoop = xLoopsRight[0][loopIdx] @@ -1273,7 +1368,8 @@ def generateBaseMesh(cls, region, options): for n in range(len(xLoopsLeft) - 1): xAroundLeft.append(xLoopsLeft[n][loopIdx]) - d1AroundLeft.append(findDerivativeBetweenPoints(xLoopsLeft[n][loopIdx], xLoopsLeft[n + 1][loopIdx])) + d1AroundLeft.append( + findDerivativeBetweenPoints(xLoopsLeft[n][loopIdx], xLoopsLeft[n + 1][loopIdx])) xOnLastLoopLeft = xLoopsLeft[-1][loopIdx] d1OnLastLoopLeft = findDerivativeBetweenPoints(xLoopsLeft[-2][loopIdx], xLoopsLeft[-1][loopIdx]) @@ -1421,7 +1517,7 @@ def generateBaseMesh(cls, region, options): xAround = [xEsoToDuodGC[i + n2 + 1]] + xRow2Right[1:] + xRow2Left[:-1] d1Around = [d2EsoToDuodGC[i + n2 + 1]] + d1Row2Right[1:] + d1Row2Left[:-1] - elif n2 > 1 and n2 < elementsAroundQuarterEso + 2: + elif 1 < n2 < elementsAroundQuarterEso + 2: xAround = xUp[countUp] if n2 < elementsAroundQuarterEso: # upstream of triple pt d1Around = d1Up[countUp] @@ -1447,7 +1543,8 @@ def generateBaseMesh(cls, region, options): elif n2 == elementsAroundHalfEso + 1: # 6 point junction ring # take smoothed d1 from dSmoothedTripleTo6Pt - startRightIdx = int(len(xBifurcationRings[0]) * 0.5 + elementsAroundQuarterEso + len(xAlongAround[junctionIdx]) * 0.5) + startRightIdx = int(len(xBifurcationRings[0]) * 0.5 + elementsAroundQuarterEso + + len(xAlongAround[junctionIdx]) * 0.5) endRightIdx = startRightIdx + int(len(xAlongAround[junctionIdx]) * 0.5) + 1 startLeftIdx = startRightIdx - int(len(xAlongAround[junctionIdx]) * 0.5) + 1 d1Around = dSmoothLoopTripleTo6Pt[startRightIdx: endRightIdx] + \ @@ -1472,7 +1569,8 @@ def generateBaseMesh(cls, region, options): d1RegularRightLoop.append(d1Outer[idx][int(len(xOuter[idx]) * 0.5 - 1 - n1)]) xRegularLoop.append(xEsoToDuodGC[n1 + 2]) for n2 in range(elementsCountAlong): - xRegularLoop.append(xOuter[n2 + 1][int(len(xOuter[n2 + 1]) * 0.5 + n1 + (1 if n2 >= elementsAroundHalfEso else 2))]) + xRegularLoop.append( + xOuter[n2 + 1][int(len(xOuter[n2 + 1]) * 0.5 + n1 + (1 if n2 >= elementsAroundHalfEso else 2))]) for n in range(len(xRegularLoop) - 1): d = findDerivativeBetweenPoints(xRegularLoop[n], xRegularLoop[n + 1]) @@ -1484,10 +1582,12 @@ def generateBaseMesh(cls, region, options): # Switch direction on right side for n2 in range(elementsCountAlong): - rotAxis = vector.normalise(vector.crossproduct3(vector.normalise(d1RegularRightLoop[n2]), d2SmoothRegularLoop[n2])) + rotAxis = vector.normalise( + vector.crossproduct3(vector.normalise(d1RegularRightLoop[n2]), d2SmoothRegularLoop[n2])) rotFrame = matrix.getRotationMatrixFromAxisAngle(rotAxis, math.pi) d = d2SmoothRegularLoop[n2] - d2SmoothRegularLoop[n2] = [rotFrame[j][0] * d[0] + rotFrame[j][1] * d[1] + rotFrame[j][2] * d[2] for j in range(3)] + d2SmoothRegularLoop[n2] = [rotFrame[j][0] * d[0] + rotFrame[j][1] * d[1] + + rotFrame[j][2] * d[2] for j in range(3)] xRegularLoops.append(xRegularLoop) d2RegularLoops.append(d2SmoothRegularLoop) d2RegularOrderedLoops.append(d2SmoothRegularOrderedLoop) @@ -1579,9 +1679,12 @@ def generateBaseMesh(cls, region, options): # right point on annulus d2 = dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) - n2] - rotAxis = vector.normalise(vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), vector.normalise(d2))) + rotAxis = vector.normalise( + vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), + vector.normalise(d2))) rotFrame = matrix.getRotationMatrixFromAxisAngle(rotAxis, math.pi) - d2Around.append([rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3)]) + d2Around.append( + [rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3)]) # left point on annulus d2Around.append(dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) + n2]) @@ -1589,7 +1692,7 @@ def generateBaseMesh(cls, region, options): for n1 in range(nextIdx + 1): d2Around.append(d2RegularLoops[n1][int(len(d2RegularLoops[n1]) * 0.5) + n2]) - elif n2 > 1 and n2 < elementsAroundQuarterEso + 2: + elif 1 < n2 < elementsAroundQuarterEso + 2: # GC before triple point & triple point d2Around.append(d2GC[len(xOuter[0]) + n2]) @@ -1601,10 +1704,12 @@ def generateBaseMesh(cls, region, options): d2Around.append(d2RegularLoops[n1][int(len(d2RegularLoops[n1]) * 0.5) - n2]) # Annulus right - d2 = dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) - n2 + (1 if n2 > elementsAroundQuarterEso else 0)] + d2 = dSmoothLoopGCTriplePt[ + int(len(xLoopGCTriplePt) * 0.5) - n2 + (1 if n2 > elementsAroundQuarterEso else 0)] if n2 <= elementsAroundQuarterEso: # Rotate to point towards duodenum - rotAxis = vector.normalise(vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), - vector.normalise(d2))) + rotAxis = vector.normalise( + vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), + vector.normalise(d2))) rotFrame = matrix.getRotationMatrixFromAxisAngle(rotAxis, math.pi) d2Around.append( [rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3)]) @@ -1612,7 +1717,8 @@ def generateBaseMesh(cls, region, options): d2Around.append(d2) # just take d2 as-is cos we are going to remove this point later # Annulus left - d2Around.append(dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) + n2 - (1 if n2 > elementsAroundQuarterEso else 0)]) + d2Around.append(dSmoothLoopGCTriplePt[ + int(len(xLoopGCTriplePt) * 0.5) + n2 - (1 if n2 > elementsAroundQuarterEso else 0)]) # Regular down left for n1 in range(nextIdx + 1): @@ -1785,7 +1891,7 @@ def generateBaseMesh(cls, region, options): for n1 in range(nextIdx + 1): d2CurvatureAround.append(curvatureRegularLoops[n1][int(len(curvatureRegularLoops[n1]) * 0.5) + n2]) - elif n2 > 1 and n2 < elementsAroundQuarterEso + 2: # Before triple pt & triple point + elif 1 < n2 < elementsAroundQuarterEso + 2: # Before triple pt & triple point xAround = xOuter[n2] if n2 < elementsAroundQuarterEso: # upstream of triple pt d1Around = d1Outer[n2] @@ -1811,7 +1917,8 @@ def generateBaseMesh(cls, region, options): d2CurvatureAround.append(curvatureRegularLoops[n1][int(len(curvatureRegularLoops[n1]) * 0.5) - n2]) # Annulus right d2CurvatureAround.append(curvatureLoopGCTriplePt[ - int(len(curvatureLoopGCTriplePt) * 0.5) - n2 + (1 if n2 > elementsAroundQuarterEso else 0)]) + int(len(curvatureLoopGCTriplePt) * 0.5) - n2 + + (1 if n2 > elementsAroundQuarterEso else 0)]) # Annulus left d2CurvatureAround.append(curvatureLoopGCTriplePt[int(len(curvatureLoopGCTriplePt) * 0.5) + n2 - ( 1 if n2 > elementsAroundQuarterEso else 0)]) @@ -1841,7 +1948,8 @@ def generateBaseMesh(cls, region, options): if n2 > elementsAroundHalfEso + 1: # closed rings beyond 6 point junction xLoop = xAround[int(len(xAround) * 0.5 + 1):] + xAround[: int(len(xAround) * 0.5 + 1)] d1Loop = d1Around[int(len(d1Around) * 0.5 + 1):] + d1Around[: int(len(d1Around) * 0.5 + 1)] - normsLoop = normsAround[int(len(normsAround) * 0.5 + 1):] + normsAround[: int(len(normsAround) * 0.5 + 1)] + normsLoop = normsAround[int(len(normsAround) * 0.5 + 1):] + \ + normsAround[: int(len(normsAround) * 0.5 + 1)] curvature = findCurvatureAroundLoop(xLoop, d1Loop, normsLoop) # Rearrange to correct order d1CurvatureAround = curvature[int(len(xLoop) * 0.5) - 1:] + curvature[: int(len(xAround) * 0.5) - 1] @@ -1881,10 +1989,22 @@ def generateBaseMesh(cls, region, options): nodeIdx = stomachStartNode idxMat = [] + if elementsCountThroughWall > 1: + thicknessProportionsUI = [0.0, mucosaRelThickness, submucosaRelThickness, circularRelThickness, + longitudinalRelThickness, longitudinalRelThickness] + thicknessProportions = [thicknessProportion / sum(thicknessProportionsUI[:-1]) + for thicknessProportion in thicknessProportionsUI] + + xi3List = [] + xi3 = 0.0 + for i in range(len(thicknessProportions) - 1): + xi3 += thicknessProportions[i] + xi3List.append(xi3) + for n2 in range(elementsCountAlong + 1): idxThroughWall = [] for n3 in range(elementsCountThroughWall + 1): - xi3 = 1 / elementsCountThroughWall * n3 + xi3 = xi3List[n3] if elementsCountThroughWall > 1 else 1.0 / elementsCountThroughWall * n3 idxAround = [] for n1 in range(len(xOuter[n2])): # Coordinates @@ -1906,7 +2026,8 @@ def generateBaseMesh(cls, region, options): d2List.append(d2) # d3 - d3 = [c * wallThickness / elementsCountThroughWall for c in norm] + d3 = [c * wallThickness * (thicknessProportions[n3 + 1] if elementsCountThroughWall > 1 else 1.0) + for c in norm] d3List.append(d3) idxAround.append(nodeIdx) @@ -1944,6 +2065,7 @@ def generateBaseMesh(cls, region, options): nodeIdentifier += 1 # Create element + fundusMucosaElementIdentifiers = [] elementIdxMat = [] n = 0 for n2 in range(elementsAlongEsophagus): @@ -1964,7 +2086,7 @@ def generateBaseMesh(cls, region, options): elementtemplateStandard = mesh.createElementtemplate() elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) - result = elementtemplateStandard.defineField(coordinates, -1, eftStandard) + elementtemplateStandard.defineField(coordinates, -1, eftStandard) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) @@ -2025,16 +2147,19 @@ def generateBaseMesh(cls, region, options): bni22 = bni21 + 1 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, - bni12 + (elementsCountAround1 if e1 < elementsCountAround1 * 2 else elementsCountAround2), + bni12 + (elementsCountAround1 if e1 < elementsCountAround1 * 2 + else elementsCountAround2), bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) + if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: + fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2067,10 +2192,14 @@ def generateBaseMesh(cls, region, options): bni22 + elementsCountAround2] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [1])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [])]) elif e1 == 1: # Bottom right wedge scaleFactors = [-1.0] nodeIdentifiers = [bni11, bni21, bni22, @@ -2081,11 +2210,12 @@ def generateBaseMesh(cls, region, options): elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX - elif e1 > 1 and e1 < elementsCountAround1: + elif 1 < e1 < elementsCountAround1: bni11 = startNode + e3 * elementsCountAround1 + e1 - 1 bni12 = startNode + e3 * elementsCountAround1 + e1 % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + ( + e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] @@ -2094,7 +2224,8 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 - 2 bni12 = startNode + e3 * elementsCountAround1 + (e1 - 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + ( + e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 if e1 == elementsCountAround1: # Bottom left wedge nodeIdentifiers = [bni12, bni21, bni22, bni12 + elementsCountAround1, @@ -2110,18 +2241,22 @@ def generateBaseMesh(cls, region, options): bni22 + elementsCountAround2] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) + remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [])]) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) + if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: + fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2131,7 +2266,7 @@ def generateBaseMesh(cls, region, options): elementIdxMat.append(elementIdxThroughWall) # Additional elements between second and upstream bifurcation ring - elif e2 > 1 and e2 < elementsAroundQuarterEso: + elif 1 < e2 < elementsAroundQuarterEso: elementIdxThroughWall = [] for e3 in range(elementsCountThroughWall): elementIdxAround = [] @@ -2143,18 +2278,21 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) + if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: + fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2176,7 +2314,8 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 if e1 < int(elementsCountAround1 * 0.5) - 1: nodeIdentifiers = [bni11, bni12, bni21, bni22, @@ -2202,20 +2341,21 @@ def generateBaseMesh(cls, region, options): elif e1 > int(elementsCountAround1 * 0.5) + 1: bni21 = bni21 - 2 - bni22 = startNode + elementsAroundThroughWall + (e1 - 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 - 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) - if e1 == 0: - fundusBodyJunctionElementIdentifier = elementIdentifier + element.setScaleFactors(eft1, scaleFactors) + if e3 == 0 and e1 == 0: + fundusBodyJunctionInnerElementIdentifier = elementIdentifier elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2236,34 +2376,41 @@ def generateBaseMesh(cls, region, options): if e1 < int(elementsCountAround1 * 0.5) + 1: bni11 = startNode + e3 * elementsCountAround1 + e1 elif e1 == int(elementsCountAround1 * 0.5) + 1: - bni11 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + e3 * len(xOuter[e2 - 1]) + e1 + 1 + bni11 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + \ + e3 * len(xOuter[e2 - 1]) + e1 + 1 elif e1 > int(elementsCountAround1 * 0.5) + 1: bni11 = startNode + e3 * elementsCountAround1 + e1 - 1 if e1 < int(elementsCountAround1 * 0.5): bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 elif e1 == int(elementsCountAround1 * 0.5): - bni12 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + e3 * len(xOuter[e2 - 1]) + e1 + 1 + bni12 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + \ + e3 * len(xOuter[e2 - 1]) + e1 + 1 elif e1 > int(elementsCountAround1 * 0.5): bni12 = startNode + e3 * elementsCountAround1 + e1 % elementsCountAround1 if e1 > int(elementsCountAround1 * 0.5): bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 + 1 - bni22 = startNode + elementsAroundThroughWall + (e1 + 2) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 2) % elementsCountAround2 + elementsCountAround2 * e3 else: bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, - bni11 + (len(xOuter[e2 - 1]) if e1 == int(elementsCountAround1 * 0.5) + 1 else elementsCountAround1), - bni12 + (len(xOuter[e2 - 1]) if e1 == int(elementsCountAround1 * 0.5) else elementsCountAround1), + bni11 + (len(xOuter[e2 - 1]) if e1 == int( + elementsCountAround1 * 0.5) + 1 else elementsCountAround1), + bni12 + (len(xOuter[e2 - 1]) if e1 == int( + elementsCountAround1 * 0.5) else elementsCountAround1), bni21 + elementsCountAround2, bni22 + elementsCountAround2] if e1 == int(elementsCountAround1 * 0.5): scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX @@ -2272,18 +2419,19 @@ def generateBaseMesh(cls, region, options): scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2298,18 +2446,23 @@ def generateBaseMesh(cls, region, options): for e3 in range(elementsCountThroughWall): elementIdxAround = [] for e1 in range(elementsCountAround1 - 1): - bni11 = startNode + e3 * elementsCountAround1 + e1 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) - bni12 = startNode + e3 * elementsCountAround1 + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 - bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) - bni22 = startNode + elementsAroundThroughWall + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround2 + elementsCountAround2 * e3 + bni11 = startNode + e3 * elementsCountAround1 + e1 +\ + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) + bni12 = startNode + e3 * elementsCountAround1 + \ + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 + bni21 = startNode + elementsAroundThroughWall + e1 + \ + elementsCountAround2 * e3 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround2 + \ + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplateStandard) - result = element.setNodesByIdentifier(eftStandard, nodeIdentifiers) + element.setNodesByIdentifier(eftStandard, nodeIdentifiers) elementIdxAround.append(elementIdentifier) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2328,15 +2481,19 @@ def generateBaseMesh(cls, region, options): eft1 = eftStandard elementtemplate1 = elementtemplateStandard if e2 == elementsAroundHalfEso: - bni11 = startNode + e3 * elementsCountAround1 + e1 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) - bni12 = startNode + e3 * elementsCountAround1 + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 + bni11 = startNode + e3 * elementsCountAround1 + \ + e1 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) + bni12 = startNode + e3 * elementsCountAround1 + \ + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 # Remap elements next to annulus if e1 == int(elementsCountAround1 * 0.5) - 1: scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS2, ([(Node.VALUE_LABEL_D_DS1, [])])) + remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS2, + ([(Node.VALUE_LABEL_D_DS1, [])])) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX @@ -2344,8 +2501,8 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + ( - e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] @@ -2355,24 +2512,27 @@ def generateBaseMesh(cls, region, options): scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, + ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX elif e1 == int(elementsCountAround1 * 0.5): eft1 = eftfactory.createEftNoCrossDerivatives() - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, + ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2383,24 +2543,23 @@ def generateBaseMesh(cls, region, options): # Annulus # Assemble endPoints for annulus - endPoints_x = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endPoints_d1 = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endPoints_d2 = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endNode_Id = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endDerivativesMap = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] + endPoints_x = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endPoints_d1 = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endPoints_d2 = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endNode_Id = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endDerivativesMap = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] endProportions = [] - thicknessIdx = [0, -1] for nAround in range(elementsCountAroundEso): - for n3 in range(len(thicknessIdx)): + for n3 in range(elementsCountThroughWall + 1): if nAround == 0: - idx = idxMat[nAround][thicknessIdx[n3]][0] + idx = idxMat[nAround][n3][0] elif nAround <= elementsAroundQuarterEso: - idx = idxMat[nAround][thicknessIdx[n3]][int((len(xOuter[nAround]) - 1) * 0.5)] + idx = idxMat[nAround][n3][int((len(xOuter[nAround]) - 1) * 0.5)] elif elementsAroundQuarterEso < nAround < elementsAroundHalfEso: - idx = idxMat[nAround + 1][thicknessIdx[n3]][int((len(xOuter[nAround + 1]) - 1) * 0.5)] + idx = idxMat[nAround + 1][n3][int((len(xOuter[nAround + 1]) - 1) * 0.5)] elif nAround == elementsAroundHalfEso: - idx = idxMat[nAround + 1][thicknessIdx[n3]][int(len(xOuter[nAround + 1]) * 0.5)] + idx = idxMat[nAround + 1][n3][int(len(xOuter[nAround + 1]) * 0.5)] elif nAround > elementsAroundHalfEso: idx = endNode_Id[n3][elementsAroundHalfEso - (nAround - elementsAroundHalfEso)] + 1 @@ -2409,23 +2568,24 @@ def generateBaseMesh(cls, region, options): endPoints_d2[n3][nAround] = d2List[idx - stomachStartNode] endNode_Id[n3][nAround] = idx - if n3 == len(thicknessIdx) - 1: # outer layer + if n3 == elementsCountThroughWall: # outer layer endPosition = trackSurfaceStomach.findNearestPosition(endPoints_x[n3][nAround]) endProportions.append(trackSurfaceStomach.getProportion(endPosition)) - for nAround in range(elementsCountAroundEso): - if nAround == 0: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, -1, 0), (1, 0, 0), None) - elif nAround == elementsAroundQuarterEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) - elif 0 < nAround < elementsAroundHalfEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, 1, 0), (-1, 0, 0), None) - elif nAround == elementsAroundHalfEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) - elif nAround == int(elementsCountAroundEso * 0.75): - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) - elif elementsAroundHalfEso < nAround < elementsCountAroundEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, -1, 0), (1, 0, 0), None) + for n3 in range(elementsCountThroughWall + 1): + for nAround in range(elementsCountAroundEso): + if nAround == 0: + endDerivativesMap[n3][nAround] = ((0, -1, 0), (1, 0, 0), None) + elif nAround == elementsAroundQuarterEso: + endDerivativesMap[n3][nAround] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) + elif 0 < nAround < elementsAroundHalfEso: + endDerivativesMap[n3][nAround] = ((0, 1, 0), (-1, 0, 0), None) + elif nAround == elementsAroundHalfEso: + endDerivativesMap[n3][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) + elif nAround == int(elementsCountAroundEso * 0.75): + endDerivativesMap[n3][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) + elif elementsAroundHalfEso < nAround < elementsCountAroundEso: + endDerivativesMap[n3][nAround] = ((0, -1, 0), (1, 0, 0), None) startProportions = [] for n in range(elementsCountAroundEso): @@ -2437,12 +2597,31 @@ def generateBaseMesh(cls, region, options): lastDuodenumElementIdentifier = elementIdentifier + stomachWallAnnotationGroups = [] + if elementsCountThroughWall == 4: + stomachWallAnnotationGroups = [[mucosaGroup], [submucosaGroup], [circularMuscleGroup], + [longitudinalMuscleGroup]] + + # Remove mucosa layer from annulus + if elementsCountThroughWall == 4 and limitingRidge: + o1_x = o1_x[1:] + o1_d1 = o1_d1[1:] + o1_d2 = o1_d2[1:] + o1_NodeId = o1_NodeId[1:] + endPoints_x = endPoints_x[1:] + endPoints_d1 = endPoints_d1[1:] + endPoints_d2 = endPoints_d2[1:] + endNode_Id = endNode_Id[1:] + endDerivativesMap = endDerivativesMap[1:] + stomachWallAnnotationGroups = stomachWallAnnotationGroups[1:] + nextNodeIdentifier, nextElementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, o1_x, o1_d1, o1_d2, None, o1_NodeId, None, endPoints_x, endPoints_d1, endPoints_d2, None, endNode_Id, endDerivativesMap, elementsCountRadial=elementsCountAcrossCardia, meshGroups=[stomachMeshGroup, cardiaMeshGroup], - tracksurface=trackSurfaceStomach, startProportions=startProportions, endProportions=endProportions, + wallAnnotationGroups=stomachWallAnnotationGroups, tracksurface=trackSurfaceStomach, + startProportions=startProportions, endProportions=endProportions, rescaleStartDerivatives=True, rescaleEndDerivatives=True) elementIdxThroughWall = [] @@ -2457,55 +2636,69 @@ def generateBaseMesh(cls, region, options): nodeIdentifier = nextNodeIdentifier + # delete mucosa layer in fundus when there is a limiting ridge + mesh_destroy_elements_and_nodes_by_identifiers(mesh, fundusMucosaElementIdentifiers) + # annotation fiducial points for embedding in whole body - GEJLCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("gastro-esophagal junction on lesser curvature")) - GEJLCElement = mesh.findElementByIdentifier(stomachStartElement - elementsAroundHalfEso - 1) - GEJLCXi = [1.0, 1.0, 1.0] - cache.setMeshLocation(GEJLCElement, GEJLCXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, GEJLCGroup.getName()) - markerLocation.assignMeshLocation(cache, GEJLCElement, GEJLCXi) - for group in [stomachGroup, GEJLCGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) - - fundusBodyJunctionGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term( - "limiting ridge on greater curvature" if limitingRidge else "junction between fundus and body on greater curvature")) - fundusBodyJunctionElement = mesh.findElementByIdentifier(fundusBodyJunctionElementIdentifier) - fundusBodyJunctionXi = [0.0, 0.0 if limitingRidge else 1.0, 1.0] - cache.setMeshLocation(fundusBodyJunctionElement, fundusBodyJunctionXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, fundusBodyJunctionGroup.getName()) - markerLocation.assignMeshLocation(cache, fundusBodyJunctionElement, fundusBodyJunctionXi) - for group in [stomachGroup, fundusBodyJunctionGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) - - pylorusGCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("pylorus on greater curvature")) - pylorusGCElement = mesh.findElementByIdentifier(lastDuodenumElementIdentifier - (elementsCountAlongGroups[-1] + 1) * elementsCountAroundDuod) - pylorusGCXi = [0.0, 1.0, 1.0] - cache.setMeshLocation(pylorusGCElement, pylorusGCXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, pylorusGCGroup.getName()) - markerLocation.assignMeshLocation(cache, pylorusGCElement, pylorusGCXi) - for group in [stomachGroup, pylorusGCGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) - - duodenumGCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("duodenum on greater curvature")) - duodenumGCElement = mesh.findElementByIdentifier(lastDuodenumElementIdentifier - elementsCountAroundDuod) - duodenumGCXi = [0.0, 1.0, 1.0] - cache.setMeshLocation(duodenumGCElement, duodenumGCXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, duodenumGCGroup.getName()) - markerLocation.assignMeshLocation(cache, duodenumGCElement, duodenumGCXi) - for group in [stomachGroup, duodenumGCGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) + markerNames = [["esophagogastric junction along the greater curvature on luminal surface", + "esophagogastric junction along the lesser curvature on luminal surface", + "gastroduodenal junction along the greater curvature on luminal surface", + "gastroduodenal junction along the lesser curvature on luminal surface", + "body-antrum junction along the greater curvature on luminal surface", + "limiting ridge along the greater curvature on luminal surface" if limitingRidge else + "fundus-body junction along the greater curvature on luminal surface"]] + if elementsCountThroughWall == 4: + markerNames.append( + ["esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", + "esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", + "gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", + "gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", + "body-antrum junction along the greater curvature on circular-longitudinal muscle interface", + "limiting ridge along the greater curvature on circular-longitudinal muscle interface" if limitingRidge + else "fundus-body junction along the greater curvature on circular-longitudinal muscle interface"]) + markerNames.append(["esophagogastric junction along the greater curvature on serosa", + "esophagogastric junction along the lesser curvature on serosa", + "gastroduodenal junction along the greater curvature on serosa", + "gastroduodenal junction along the lesser curvature on serosa", + "body-antrum junction along the greater curvature on serosa", + "limiting ridge along the greater curvature on serosa" if limitingRidge else + "fundus-body junction along the greater curvature on serosa"]) + + markerInnerElementIdentifiers = [stomachStartElement - elementsCountThroughWall * elementsCountAroundEso, + stomachStartElement - (elementsCountThroughWall - 1) * elementsCountAroundEso - + elementsAroundHalfEso, + lastDuodenumElementIdentifier - elementsCountThroughWall * + elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1), + lastDuodenumElementIdentifier - elementsCountThroughWall * + elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1) + + elementsAroundHalfDuod, + lastDuodenumElementIdentifier - elementsCountThroughWall * + elementsCountAroundDuod * (sum(elementsCountAlongGroups[-3:]) + 1), + fundusBodyJunctionInnerElementIdentifier] + + elementsCountAroundLayer = [elementsCountAroundEso, elementsCountAroundEso, + elementsCountAroundDuod, elementsCountAroundDuod, + elementsCountAroundDuod, elementsCountAroundDuod] + + for n3 in range(len(markerNames)): + for n in range(len(markerNames[n3])): + markerGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, + get_stomach_term(markerNames[n3][n])) + markerElementIdentifier = \ + markerInnerElementIdentifiers[n] + \ + (0 if n3 == 0 or elementsCountThroughWall == 1 else elementsCountAroundLayer[n] * + (elementsCountThroughWall - 1)) + markerElement = mesh.findElementByIdentifier(markerElementIdentifier) + markerXi = [0.0, 1.0, 0.0 if n3 != len(markerNames) - 1 else 1.0] if n < len(markerNames[n3]) - 1 else \ + [0.0, 0.0 if limitingRidge else 1.0, 0.0 if n3 != len(markerNames) - 1 else 1.0] + 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 [stomachGroup, markerGroup]: + group.getNodesetGroup(nodes).addNode(markerPoint) # Create annotation groups for dorsal and ventral parts of the stomach dorsalGroup = AnnotationGroup(region, get_stomach_term("dorsal stomach")) @@ -2519,9 +2712,9 @@ def generateBaseMesh(cls, region, options): elementIdx = elementIdxMat[e2][e3][e1] element = mesh.findElementByIdentifier(elementIdx) if e1 < 0.5 * len(elementIdxMat[e2][e3]): - dorsalMeshGroup.addElement(element) - else: ventralMeshGroup.addElement(element) + else: + dorsalMeshGroup.addElement(element) allAnnotationGroups.append(dorsalGroup) allAnnotationGroups.append(ventralGroup) @@ -2531,9 +2724,12 @@ def generateBaseMesh(cls, region, options): for n2 in range(elementsAlongEsophagus + 1): for n3 in range(elementsThroughEsophagusWall + 1): - nodeIdxOnGCMargin = 1 + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + n3 * elementsCountAroundEso + nodeIdxOnGCMargin = 1 + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + \ + n3 * elementsCountAroundEso nodesOnSplitMargin.append(nodeIdxOnGCMargin) - nodeIdxOnLCMargin = 1 + elementsAroundHalfEso + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + n3 * elementsCountAroundEso + nodeIdxOnLCMargin = 1 + elementsAroundHalfEso + \ + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + \ + n3 * elementsCountAroundEso nodesOnSplitMargin.append(nodeIdxOnLCMargin) nodesOnLCMargin.append(nodeIdxOnLCMargin) nodesOnSplitMargin += nodeIdxGC + nodeIdxLC @@ -2652,14 +2848,14 @@ def generateBaseMesh(cls, region, options): eft2 = eft remapEftNodeValueLabelsVersion(eft2, lnRemapV2, allValueLabels, 2) - result1 = splitElementtemplate2.defineField(splitCoordinates, -1, eft2) - result2 = element.merge(splitElementtemplate2) + splitElementtemplate2.defineField(splitCoordinates, -1, eft2) + element.merge(splitElementtemplate2) element.setNodesByIdentifier(eft2, nodeIdentifiers) if eft2.getNumberOfLocalScaleFactors() > 0: element.setScaleFactor(eft2, 1, -1.0) else: - result1 = splitElementtemplate1.defineField(splitCoordinates, -1, eft) - result2 = element.merge(splitElementtemplate1) + splitElementtemplate1.defineField(splitCoordinates, -1, eft) + element.merge(splitElementtemplate1) element.setNodesByIdentifier(eft, nodeIdentifiers) if eft.getNumberOfLocalScaleFactors() == 1: element.setScaleFactors(eft, [-1.0]) @@ -2687,129 +2883,217 @@ def refineMesh(cls, meshrefinement, options): @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. - ''' + """ limitingRidge = options['Limiting ridge'] + elementsCountThroughWall = options['Number of elements through wall'] - if limitingRidge: - fm = region.getFieldmodule() - fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) - bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) - cardiaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("cardia of stomach")) - antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) + stomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("stomach")) + dorsalStomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("dorsal stomach")) + ventralStomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("ventral stomach")) + bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) + cardiaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("cardia of stomach")) + duodenumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("duodenum")) + fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) + antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) + pylorusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric canal")) + esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) - limitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction")) - innerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on inner wall")) - outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on outer wall")) + # Create new groups + stomachLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of stomach")) + stomachSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of stomach")) + bodyLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of body of stomach")) + bodySerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of body of stomach")) + cardiaLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "luminal surface of cardia of stomach")) + cardiaSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of cardia of stomach")) + duodenumLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of duodenum")) + duodenumSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of duodenum")) + esoLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of esophagus")) + esoSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of esophagus")) + fundusLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "luminal surface of fundus of stomach")) + fundusSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of fundus of stomach")) + antrumLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of pyloric antrum")) + antrumSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of pyloric antrum")) + pylorusLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of pyloric canal")) + pylorusSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of pyloric canal")) + gastroduodenalJunctionGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("gastroduodenal junction")) - mesh2d = fm.findMeshByDimension(2) - is_fundus = fundusGroup.getGroup() + fm = region.getFieldmodule() + mesh2d = fm.findMeshByDimension(2) + mesh1d = fm.findMeshByDimension(1) + + is_exterior = fm.createFieldIsExterior() + is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) + is_exterior_face_inner = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) + + is_gastroduod = fm.createFieldAnd(duodenumGroup.getGroup(), pylorusGroup.getGroup()) + gastroduodenalJunctionGroup.getMeshGroup(mesh2d).addElementsConditional(is_gastroduod) + + if elementsCountThroughWall == 4: + is_dorsal = dorsalStomachGroup.getGroup() + is_ventral = ventralStomachGroup.getGroup() + is_curvatures = fm.createFieldAnd(is_dorsal, is_ventral) + CMLMInterfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of stomach")) + circularMuscleGroup = getAnnotationGroupForTerm(annotationGroups, + get_stomach_term("circular muscle layer of stomach")) + longitudinalMuscleGroup = \ + getAnnotationGroupForTerm(annotationGroups, get_stomach_term("longitudinal muscle layer of stomach")) + is_CM = circularMuscleGroup.getGroup() + is_LM = longitudinalMuscleGroup.getGroup() + is_CMLMInterface = fm.createFieldAnd(is_CM, is_LM) + CMLMInterfaceGroup.getMeshGroup(mesh2d).addElementsConditional(is_CMLMInterface) + + dorsalStomach_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of dorsal stomach")) + ventralStomach_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of ventral stomach")) + is_dorsal_CMLM = fm.createFieldAnd(is_dorsal, is_CMLMInterface) + dorsalStomach_CMLMGroup.getMeshGroup(mesh2d).addElementsConditional(is_dorsal_CMLM) + is_ventral_CMLM = fm.createFieldAnd(is_ventral, is_CMLMInterface) + ventralStomach_CMLMGroup.getMeshGroup(mesh2d).addElementsConditional(is_ventral_CMLM) + + gastroduod_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of gastroduodenal junction")) + is_gastroduod_CMLM = fm.createFieldAnd(is_gastroduod, is_CMLMInterface) + gastroduod_CMLMGroup.getMeshGroup(mesh1d).addElementsConditional(is_gastroduod_CMLM) + + is_curvatures_CMLM = fm.createFieldAnd(is_curvatures, is_CMLMInterface) + bodyCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment")) + duodenumCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment")) + esoCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of esophagus along the cut margin")) + fundusCurvaturesCMLMGroup =\ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment")) + antrumCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment")) + pylorusCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment")) + + sectionCurvaturesCMLMGroups = [None, bodyCurvaturesCMLMGroup, None, duodenumCurvaturesCMLMGroup, + esoCurvaturesCMLMGroup, fundusCurvaturesCMLMGroup, antrumCurvaturesCMLMGroup, + pylorusCurvaturesCMLMGroup] + + sectionGroups = [stomachGroup, bodyGroup, cardiaGroup, duodenumGroup, esoGroup, fundusGroup, antrumGroup, + pylorusGroup] + sectionSerosaGroups = [stomachSerosaGroup, bodySerosaGroup, cardiaSerosaGroup, duodenumSerosaGroup, + esoSerosaGroup, fundusSerosaGroup, antrumSerosaGroup, + pylorusSerosaGroup] + sectionLuminalGroups = [stomachLuminalGroup, bodyLuminalGroup, cardiaLuminalGroup, duodenumLuminalGroup, + esoLuminalGroup, fundusLuminalGroup, antrumLuminalGroup, + pylorusLuminalGroup] + + for i in range(len(sectionGroups)): + is_section = sectionGroups[i].getGroup() + is_sectionSerosa = fm.createFieldAnd(is_section, is_exterior_face_outer) + sectionSerosaGroups[i].getMeshGroup(mesh2d).addElementsConditional(is_sectionSerosa) + is_sectionLuminal = fm.createFieldAnd(is_section, is_exterior_face_inner) + sectionLuminalGroups[i].getMeshGroup(mesh2d).addElementsConditional(is_sectionLuminal) + + if elementsCountThroughWall == 4: + is_sectionCurvaturesCMLM = fm.createFieldAnd(is_section, is_curvatures_CMLM) + if sectionCurvaturesCMLMGroups[i]: + sectionCurvaturesCMLMGroups[i].getMeshGroup(mesh1d).addElementsConditional(is_sectionCurvaturesCMLM) + + if limitingRidge: + limitingRidgeGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("forestomach-glandular stomach junction")) + innerLimitingRidgeGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("limiting ridge on luminal surface")) + outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("limiting ridge on serosa")) + + is_antrum = antrumGroup.getGroup() is_body = bodyGroup.getGroup() is_cardia = cardiaGroup.getGroup() - is_antrum = antrumGroup.getGroup() + is_fundus = fundusGroup.getGroup() is_limitingRidgeBody = fm.createFieldAnd(is_fundus, is_body) is_limitingRidgeCardia = fm.createFieldAnd(is_body, is_cardia) is_limitingRidgeAntrum = fm.createFieldAnd(is_antrum, is_cardia) is_limitingRidgeBodyCardia = fm.createFieldOr(is_limitingRidgeBody, is_limitingRidgeCardia) is_limitingRidgeAntrumCardia = fm.createFieldOr(is_limitingRidgeAntrum, is_limitingRidgeCardia) is_limitingRidge = fm.createFieldOr(is_limitingRidgeBodyCardia, is_limitingRidgeAntrumCardia) + + if elementsCountThroughWall == 4: + mucosaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("mucosa of stomach")) + is_mucosa = mucosaGroup.getGroup() + is_bodyMucosa = fm.createFieldAnd(is_body, is_mucosa) + is_antrumMucosa = fm.createFieldAnd(is_antrum, is_mucosa) + is_bodyAntrumMucosa = fm.createFieldOr(is_bodyMucosa, is_antrumMucosa) + + is_xi1Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) + is_xi1All = fm.createFieldOr(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) + is_xi1BodyAntrumMucosaAll = fm.createFieldAnd(is_bodyAntrumMucosa, is_xi1All) + is_limitingRidgeAroundCardia = fm.createFieldAnd(is_xi1BodyAntrumMucosaAll, + fm.createFieldNot(is_xi1Interior)) + + is_xi2Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1)) + is_xi2ZeroBodyMucosa = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), is_bodyMucosa) + is_limitingRidgeBodyBoundary = fm.createFieldAnd(is_xi2ZeroBodyMucosa, + fm.createFieldNot(is_xi2Interior)) + + is_limitingRidgeMucosa = fm.createFieldOr(is_limitingRidgeAroundCardia, is_limitingRidgeBodyBoundary) + is_limitingRidge = fm.createFieldOr(is_limitingRidge, is_limitingRidgeMucosa) + limitingRidgeGroup.getMeshGroup(mesh2d).addElementsConditional(is_limitingRidge) - mesh1d = fm.findMeshByDimension(1) - is_exterior = fm.createFieldIsExterior() - is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_exterior_face_inner = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) + is_xi3Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) + is_xi3ZeroLimitingRidge = fm.createFieldAnd(is_limitingRidge, + fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) + is_xi3OneLimitingRidge = fm.createFieldAnd(is_limitingRidge, + fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_limitingRidgeInner = fm.createFieldAnd(is_limitingRidge, is_exterior_face_inner) + is_limitingRidgeInner = fm.createFieldAnd(is_xi3ZeroLimitingRidge, fm.createFieldNot(is_xi3Interior)) innerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeInner) - is_limitingRidgeOuter = fm.createFieldAnd(is_limitingRidge, is_exterior_face_outer) + is_limitingRidgeOuter = fm.createFieldAnd(is_xi3OneLimitingRidge, fm.createFieldNot(is_xi3Interior)) outerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeOuter) - serosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of stomach")) - mucosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("mucosa of stomach")) - - outerSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa split margin")) - esoSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("esophagus on serosa split margin")) - fundusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("fundus on serosa split margin")) - bodySplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("body on serosa split margin")) - antrumSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("antrum on serosa split margin")) - pylorusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("pylorus on serosa split margin")) - - serosaDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of dorsal stomach")) - serosaVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of ventral stomach")) - - stomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("stomach")) - bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) - esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) - fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) - antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) - pylorusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pylorus")) - esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) - esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) - dorsalGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("dorsal stomach")) - ventralGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("ventral stomach")) - - fm = region.getFieldmodule() - mesh2d = fm.findMeshByDimension(2) - is_stomach = stomachGroup.getGroup() - is_exterior = fm.createFieldIsExterior() - is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_exterior_face_inner = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) - is_serosa = fm.createFieldAnd(is_stomach, is_exterior_face_outer) - is_mucosa = fm.createFieldAnd(is_stomach, is_exterior_face_inner) - serosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_serosa) - mucosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_mucosa) - is_dorsal = dorsalGroup.getGroup() - is_ventral = ventralGroup.getGroup() - is_dorsalSerosa = fm.createFieldAnd(is_dorsal, is_serosa) - is_ventralSerosa = fm.createFieldAnd(is_ventral, is_serosa) - serosaDorsalGroup.getMeshGroup(mesh2d).addElementsConditional(is_dorsalSerosa) - serosaVentralGroup.getMeshGroup(mesh2d).addElementsConditional(is_ventralSerosa) - - is_margin = fm.createFieldAnd(is_dorsal, is_ventral) - mesh1d = fm.findMeshByDimension(1) - is_exterior = fm.createFieldIsExterior() - is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_marginOuter = fm.createFieldAnd(is_margin, is_exterior_face_outer) - outerSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_marginOuter) - - is_eso = esoGroup.getGroup() - is_esoMargin = fm.createFieldAnd(is_marginOuter, is_eso) - esoSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_esoMargin) - - is_fundus = fundusGroup.getGroup() - is_fundusMargin = fm.createFieldAnd(is_marginOuter, is_fundus) - fundusSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_fundusMargin) - - is_body = bodyGroup.getGroup() - is_bodyMargin = fm.createFieldAnd(is_marginOuter, is_body) - bodySplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_bodyMargin) - - is_antrum = antrumGroup.getGroup() - is_antrumMargin = fm.createFieldAnd(is_marginOuter, is_antrum) - antrumSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_antrumMargin) - - is_pylorus = pylorusGroup.getGroup() - is_pylorusMargin = fm.createFieldAnd(is_marginOuter, is_pylorus) - pylorusSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_pylorusMargin) + if elementsCountThroughWall == 4: + limitingRidge_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "limiting ridge on circular-longitudinal muscle interface")) + is_limitingRidgeCMLM = fm.createFieldAnd(is_CMLMInterface, is_limitingRidge) + limitingRidge_CMLMGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeCMLM) def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxProportion1, elementsCountAlongTrackSurface): @@ -2825,7 +3109,8 @@ def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxPropor :return: position and derivative of point around track surface """ closestIdxOnNx = interp.getNearestPointIndex(nx, x) - closestPositionToPoint = trackSurface.createPositionProportion(nxProportion1, closestIdxOnNx / elementsCountAlongTrackSurface) + closestPositionToPoint = trackSurface.createPositionProportion(nxProportion1, + closestIdxOnNx / elementsCountAlongTrackSurface) xPosition = trackSurface.findNearestPosition(x, closestPositionToPoint) d = trackSurface.evaluateCoordinates(xPosition, derivatives=True)[1] @@ -2846,7 +3131,7 @@ def getSmoothedSampledPointsOnTrackSurface(trackSurface, startProportion1, start :param startDerivative, endDerivative: optional derivative vectors in 3-D world coordinates to match at the start and end of the curves. If omitted, fits in with other derivative or is in a straight line from a to b - :param derivativeMagnitudeStart, derivativeMagnitudeEnd: optional magnitude of derivatives to match at the start and + :param startDerivativeMagnitude, endDerivativeMagnitude: optional magnitude of derivatives to match at the start and end of the curves :return: coordinates and derivative of sampled points """ diff --git a/src/scaffoldmaker/utils/annulusmesh.py b/src/scaffoldmaker/utils/annulusmesh.py index 9cdbd105..78a34a28 100644 --- a/src/scaffoldmaker/utils/annulusmesh.py +++ b/src/scaffoldmaker/utils/annulusmesh.py @@ -1,6 +1,6 @@ -''' +""" Utility functions for generating annulus mesh between start and end loops of points. -''' +""" from __future__ import division import copy @@ -16,71 +16,74 @@ from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite -def derivativeSignsToExpressionTerms(valueLabels, signs, scaleFactorIdx = None): - ''' - Return remap expression terms for summing derivative[i]*sign[i]*scaleFactor +def derivativeSignsToExpressionTerms(valueLabels, signs, scaleFactorIdx=None): + """ + Return remap expression terms for summing derivative[i] * sign[i] * scaleFactor :param valueLabels: List of node value labels to possibly include. :param signs: List of 1 (no scaling), -1 (scale by scale factor 1) or 0 (no term). - :param scaleFactorIdx: Optional index of local scale factor to scale all non-zero terms. Default None means no extra scaling. - ''' + :param scaleFactorIdx: Optional index of local scale factor to scale all non-zero terms. Default None means no + extra scaling. + """ expressionTerms = [] for i in range(len(valueLabels)): if signs[i] == 1: - expressionTerms.append((valueLabels[i], ([ scaleFactorIdx ] if scaleFactorIdx else []))) + expressionTerms.append((valueLabels[i], ([scaleFactorIdx] if scaleFactorIdx else []))) elif signs[i] == -1: expressionTerms.append((valueLabels[i], ([1, scaleFactorIdx] if scaleFactorIdx else [1]))) return expressionTerms + def getMappedD1D2(gds, derivativesMaps): - ''' + """ Get vector combinations of d1In, d2In, d3In indicated by derivativesMap. :param gds: List of global d1, d2 and optionally d3. :param derivativesMaps: List over d1, d2, d3, and optionally d1b (for different d1 exiting global node) of list of 3 weights of gds, each limited to -1.0, 0.0, or -1.0. :return: Effective d1, d2. Where d1 is around, d2 is radial. - ''' + """ dslimit = len(gds) if not (derivativesMaps and derivativesMaps[0]): d1 = gds[0] else: derivativesMap = derivativesMaps[0] - d1 = [ 0.0, 0.0, 0.0 ] + d1 = [0.0, 0.0, 0.0] for ds in range(dslimit): if derivativesMap[ds] != 0.0: for c in range(3): - d1[c] += derivativesMap[ds]*gds[ds][c] + d1[c] += derivativesMap[ds] * gds[ds][c] if len(derivativesMaps) > 3: # average with d1 map for other side derivativesMap = derivativesMaps[3] - d1 = [ 0.5*d for d in d1 ] + d1 = [0.5 * d for d in d1] if not derivativesMap: for c in range(3): - d1[c] += 0.5*gds[0][c] + d1[c] += 0.5 * gds[0][c] else: for ds in range(dslimit): if derivativesMap[ds] != 0.0: for c in range(3): - d1[c] += 0.5*derivativesMap[ds]*gds[ds][c] + d1[c] += 0.5 * derivativesMap[ds] * gds[ds][c] if not (derivativesMaps and derivativesMaps[1]): d2 = gds[1] else: derivativesMap = derivativesMaps[1] - d2 = [ 0.0, 0.0, 0.0 ] + d2 = [0.0, 0.0, 0.0] for ds in range(dslimit): if derivativesMap[ds] != 0.0: for c in range(3): - d2[c] += derivativesMap[ds]*gds[ds][c] + d2[c] += derivativesMap[ds] * gds[ds][c] return d1, d2 -def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, - startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, - endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, - forceStartLinearXi3 = False, forceMidLinearXi3 = False, forceEndLinearXi3 = False, - maxStartThickness = None, maxEndThickness = None, useCrossDerivatives = False, - elementsCountRadial = 1, meshGroups = None, tracksurface = None, startProportions = None, endProportions = None, - rescaleStartDerivatives = False, rescaleEndDerivatives = False, sampleBlend = 0.0): +def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, startPointsx, startPointsd1, + startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, + endPointsd2, endPointsd3, endNodeId, endDerivativesMap, + forceStartLinearXi3=False, forceMidLinearXi3=False, forceEndLinearXi3=False, + maxStartThickness=None, maxEndThickness=None, useCrossDerivatives=False, + elementsCountRadial=1, meshGroups=None, wallAnnotationGroups=None, + tracksurface=None, startProportions=None, endProportions=None, + rescaleStartDerivatives=False, rescaleEndDerivatives=False, sampleBlend=0.0): """ Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to a loop of end points/nodes with specified derivative mappings. @@ -93,16 +96,17 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, :param mesh: The mesh to create elements in. :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment. :param startPointsx, startPointsd1, startPointsd2, startPointsd3, endPointsx, endPointsd1, endPointsd2, endPointsd3: - List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to d3. - If both ends are linear through the wall, interior points are linear through the wall. + List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to + d3. If both ends are linear through the wall, interior points are linear through the wall. :param startNodeId, endNodeId: List array [n3][n1] of existing node identifiers to use at start/end. Pass None for argument if no nodes are specified at end. These arguments are 'all or nothing'. - :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at start/end of form: - ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be used. + :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at + start/end of form: + ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be + used. None means use default e.g. d/dxi2 = d/ds2. Pass None for the entire argument to use the defaults d/dxi1 = d/ds1, d/dxi2 = d/ds2, d/dxi3 = d/ds3. Pass a 4th mapping to apply to d/dxi1 on other side of node; if not supplied first mapping applies both sides. - :param nodetemplate: Full tricubic Hermite node template, can omit cross derivatives. :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or end elements to be linear through the wall, even if d3 is supplied at either end. Can only use forceMidLinearXi3 only if at least one end is linear in d3. @@ -112,6 +116,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, :param meshGroups: Optional sequence of Zinc MeshGroup for adding all new elements to, or a sequence of length elementsCountRadial containing sequences of mesh groups to add rows of radial elements to from start to end. + :param wallAnnotationGroups: Annotation groups for adding all new elements to a sequence + of groups to add to elements through wall. :param tracksurface: Description for outer surface representation used for creating annulus mesh. Provides information for creating radial nodes on annulus that sit on tracksurface. Need startProportions and endProportions to work. @@ -119,11 +125,11 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, around as for startPoints. Values only given for tracksurface for outer layer (xi3 == 1). :param endProportions: Proportion around and along of endPoints on track surface. These vary with nodes around as for endPoints. Values only given for tracksurface for outer layer (xi3 == 1). - :param rescaleStartDerivatives, rescaleEndDerivatives: Optional flags to compute and multiply additional scale factors - on start, end or both radial derivatives to fit arc length, needed if derivatives are of the wrong scale for the radial - distances and the chosen elementsCountRadial. If either is True, derivatives and sampled radial nodes are spaced for a - gradual change of derivative from that at the other end. If both are True, scaling is set to give even sampling and arc - length derivatives. + :param rescaleStartDerivatives, rescaleEndDerivatives: Optional flags to compute and multiply additional scale + factors on start, end or both radial derivatives to fit arc length, needed if derivatives are of the wrong scale + for the radial distances and the chosen elementsCountRadial. If either is True, derivatives and sampled radial + nodes are spaced for a gradual change of derivative from that at the other end. If both are True, scaling is set to + give even sampling and arclength derivatives. :param sampleBlend: Real value varying from 0.0 to 1.0 controlling weighting of start and end derivatives when interpolating extra points in-between, where 0.0 = sample with equal end derivatives, and 1.0 = proportional to current magnitudes, interpolated in between. @@ -135,43 +141,51 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, midLinearXi3 = (startLinearXi3 and endLinearXi3) or ((startLinearXi3 or endLinearXi3) and forceMidLinearXi3) # get list whether each row of nodes in elements is linear in Xi3 # this is for element use; start/end nodes may have d3 even if element is linear - rowLinearXi3 = [ startLinearXi3 ] + [ midLinearXi3 ]*(elementsCountRadial - 1) + [ endLinearXi3 ] + rowLinearXi3 = [startLinearXi3] + [midLinearXi3] * (elementsCountRadial - 1) + [endLinearXi3] assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \ 'createAnnulusMesh3d: Cannot use cross derivatives with derivatives map' - elementsCountWall = 1 - nodesCountWall = elementsCountWall + 1 - assert (len(startPointsx) == nodesCountWall) and (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ - (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ - (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and (len(endPointsd2) == nodesCountWall) and \ - (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ - ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ - ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ - ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ - ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)), \ + nodesCountWall = len(startPointsx) + assert (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ + (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ + (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and \ + (len(endPointsd2) == nodesCountWall) and (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ + ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ + ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ + ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ + ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)),\ 'createAnnulusMesh3d: Mismatch in number of layers through wall' elementsCountAround = nodesCountAround = len(startPointsx[0]) assert (nodesCountAround > 1), 'createAnnulusMesh3d: Invalid number of points/nodes around annulus' for n3 in range(nodesCountWall): - assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and (len(startPointsd2[n3]) == nodesCountAround) and \ - (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and \ - (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and (len(endPointsd2[n3]) == nodesCountAround) and \ - (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ - ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and \ - ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ - ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ - ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ + assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and \ + (len(startPointsd2[n3]) == nodesCountAround) and \ + (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and\ + (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and \ + (len(endPointsd2[n3]) == nodesCountAround) and \ + (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ + ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and\ + ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ + ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ + ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ 'createAnnulusMesh3d: Mismatch in number of points/nodes in layers through wall' rowMeshGroups = meshGroups if meshGroups: assert isinstance(meshGroups, Sequence), 'createAnnulusMesh3d: Mesh groups is not a sequence' if (len(meshGroups) == 0) or (not isinstance(meshGroups[0], Sequence)): - rowMeshGroups = [ meshGroups ]*elementsCountRadial + rowMeshGroups = [meshGroups] * elementsCountRadial else: - assert len(meshGroups) == elementsCountRadial, 'createAnnulusMesh3d: Length of meshGroups sequence does not equal elementsCountRadial' + assert len(meshGroups) == elementsCountRadial, \ + 'createAnnulusMesh3d: Length of meshGroups sequence does not equal elementsCountRadial' + if wallAnnotationGroups: + assert len(wallAnnotationGroups) == nodesCountWall - 1, \ + 'createAnnulusMesh3d: Length of wallAnnotationGroups sequence does not equal elementsCountThroughWall' if tracksurface: - assert startProportions and endProportions, 'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface' - assert len(startProportions) == nodesCountAround, 'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround' - assert len(endProportions) == nodesCountAround, 'createAnnulusMesh3d: Length of endProportions does not equal nodesCountAround' + assert startProportions and endProportions, \ + 'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface' + assert len(startProportions) == nodesCountAround, \ + 'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround' + assert len(endProportions) == nodesCountAround, \ + 'createAnnulusMesh3d: Length of endProportions does not equal nodesCountAround' fm = mesh.getFieldmodule() fm.beginChange() @@ -179,148 +193,210 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, coordinates = findOrCreateFieldCoordinates(fm) # Build arrays of points from start to end - px = [ [], [] ] - pd1 = [ [], [] ] - pd2 = [ [], [] ] - pd3 = [ [], [] ] - for n3 in range(2): - px [n3] = [ startPointsx [n3], endPointsx [n3] ] - pd1[n3] = [ startPointsd1[n3], endPointsd1[n3] ] - pd2[n3] = [ startPointsd2[n3], endPointsd2[n3] ] - pd3[n3] = [ startPointsd3[n3] if (startPointsd3 is not None) else None, \ - endPointsd3[n3] if (endPointsd3 is not None) else None ] + px = [[] for n3 in range(nodesCountWall)] + pd1 = [[] for n3 in range(nodesCountWall)] + pd2 = [[] for n3 in range(nodesCountWall)] + pd3 = [[] for n3 in range(nodesCountWall)] + + # Find total wall thickness + thicknessProportions = [] + thicknesses = [] + thicknesses.append([vector.magnitude([(startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) + for c in range(3)]) for n1 in range(nodesCountAround)]) + for n2 in range(1, elementsCountRadial): + thicknesses.append([None] * nodesCountAround) + thicknesses.append([vector.magnitude([(endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) + for c in range(3)]) for n1 in range(nodesCountAround)]) + + for n3 in range(nodesCountWall): + px[n3] = [startPointsx[n3], endPointsx[n3]] + pd1[n3] = [startPointsd1[n3], endPointsd1[n3]] + pd2[n3] = [startPointsd2[n3], endPointsd2[n3]] + pd3[n3] = [startPointsd3[n3] if (startPointsd3 is not None) else None, + endPointsd3[n3] if (endPointsd3 is not None) else None] + + startThicknessList = \ + [vector.magnitude([(startPointsx[n3][n1][c] - startPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) + for c in range(3)]) for n1 in range(len(startPointsx[n3]))] + endThicknessList = \ + [vector.magnitude([(endPointsx[n3][n1][c] - endPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) + for c in range(3)]) for n1 in range(len(endPointsx[n3]))] + thicknessList = [startThicknessList, endThicknessList] # thickness of each layer + + startThicknessProportions = [thicknessList[0][c] / thicknesses[0][c] for c in range(nodesCountAround)] + endThicknessProportions = [thicknessList[1][c] / thicknesses[-1][c] for c in range(nodesCountAround)] + thicknessProportions.append([startThicknessProportions, endThicknessProportions]) if rescaleStartDerivatives: - scaleFactorMapStart = [ [] for n2 in range(2) ] + scaleFactorMapStart = [[] for n3 in range(nodesCountWall)] if rescaleEndDerivatives: - scaleFactorMapEnd = [ [] for n2 in range(2) ] + scaleFactorMapEnd = [[] for n3 in range(nodesCountWall)] # following code adds in-between points, but also handles rescaling for 1 radial element - for n3 in range(2): + for n3 in range(nodesCountWall): for n2 in range(1, elementsCountRadial): - px [n3].insert(n2, [ None ]*nodesCountAround) - pd1[n3].insert(n2, [ None ]*nodesCountAround) - pd2[n3].insert(n2, [ None ]*nodesCountAround) - pd3[n3].insert(n2, None if midLinearXi3 else [ None ]*nodesCountAround) - # compute on outside / n3 = 1, then map to inside using thickness - thicknesses = [] - thicknesses.append([ vector.magnitude([ (startPointsx[1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) + px[n3].insert(n2, [None] * nodesCountAround) + pd1[n3].insert(n2, [None] * nodesCountAround) + pd2[n3].insert(n2, [None] * nodesCountAround) + pd3[n3].insert(n2, None if midLinearXi3 else [None] * nodesCountAround) + thicknessProportions[n3].insert(n2, [None] * nodesCountAround) + if maxStartThickness: for n1 in range(nodesCountAround): thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness) - for n2 in range(1, elementsCountRadial): - thicknesses.append([ None ]*nodesCountAround) - thicknesses.append([ vector.magnitude([ (endPointsx[1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxEndThickness: for n1 in range(nodesCountAround): thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) - n3 == 1 + n3 = nodesCountWall - 1 for n1 in range(nodesCountAround): - ax = startPointsx[n3][n1] - ad1, ad2 = getMappedD1D2([ startPointsd1[n3][n1], startPointsd2[n3][n1] ] + ([ startPointsd3[n3][n1] ] if startPointsd3 else []), + ax = startPointsx[n3][n1] + ad1, ad2 = getMappedD1D2([startPointsd1[n3][n1], startPointsd2[n3][n1]] + + ([startPointsd3[n3][n1]] if startPointsd3 else []), startDerivativesMap[n3][n1] if startDerivativesMap else None) - bx = endPointsx[n3][n1] - bd1, bd2 = getMappedD1D2([ endPointsd1[n3][n1], endPointsd2[n3][n1] ] + ([ endPointsd3[n3][n1] ] if endPointsd3 else []), + bx = endPointsx[n3][n1] + bd1, bd2 = getMappedD1D2([endPointsd1[n3][n1], endPointsd2[n3][n1]] + + ([endPointsd3[n3][n1]] if endPointsd3 else []), endDerivativesMap[n3][n1] if endDerivativesMap else None) # sample between start and end points and derivatives # scaling end derivatives to arc length gives even curvature along the curve aMag = vector.magnitude(ad2) bMag = vector.magnitude(bd2) - ad2Scaled = vector.setMagnitude(ad2, 0.5*((1.0 + sampleBlend)*aMag + (1.0 - sampleBlend)*bMag)) - bd2Scaled = vector.setMagnitude(bd2, 0.5*((1.0 + sampleBlend)*bMag + (1.0 - sampleBlend)*aMag)) + ad2Scaled = vector.setMagnitude(ad2, 0.5 * ((1.0 + sampleBlend) * aMag + (1.0 - sampleBlend) * bMag)) + bd2Scaled = vector.setMagnitude(bd2, 0.5 * ((1.0 + sampleBlend) * bMag + (1.0 - sampleBlend) * aMag)) scaling = interp.computeCubicHermiteDerivativeScaling(ax, ad2Scaled, bx, bd2Scaled) - ad2Scaled = [ d*scaling for d in ad2Scaled ] - bd2Scaled = [ d*scaling for d in bd2Scaled ] + ad2Scaled = [d * scaling for d in ad2Scaled] + bd2Scaled = [d * scaling for d in bd2Scaled] derivativeMagnitudeStart = None if rescaleStartDerivatives else vector.magnitude(ad2) derivativeMagnitudeEnd = None if rescaleEndDerivatives else vector.magnitude(bd2) if tracksurface: mx, md2, md1, md3, mProportions = \ tracksurface.createHermiteCurvePoints(startProportions[n1][0], startProportions[n1][1], - endProportions[n1][0], endProportions[n1][1], - elementsCountRadial, - derivativeStart=[ d/elementsCountRadial for d in ad2Scaled ], - derivativeEnd=[ d/elementsCountRadial for d in bd2Scaled ]) - mx, md2, md1 = tracksurface.resampleHermiteCurvePointsSmooth(mx, md2, md1, md3, mProportions, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:3] + endProportions[n1][0], endProportions[n1][1], elementsCountRadial, + derivativeStart=[d / elementsCountRadial for d in ad2Scaled], + derivativeEnd=[d / elementsCountRadial for d in bd2Scaled]) + mx, md2, md1 = \ + tracksurface.resampleHermiteCurvePointsSmooth(mx, md2, md1, md3, mProportions, + derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:3] # interpolate thicknesses using xi calculated from radial arclength distances to points - arcLengthInsideToRadialPoint = [ 0.0 ] + [ interp.getCubicHermiteArcLength(mx[n2], md2[n2], mx[n2 + 1], md2[n2 + 1]) for n2 in range(elementsCountRadial) ] + arcLengthInsideToRadialPoint = \ + [0.0] + [interp.getCubicHermiteArcLength(mx[n2], md2[n2], mx[n2 + 1], md2[n2 + 1]) + for n2 in range(elementsCountRadial)] arclengthInsideToOutside = sum(arcLengthInsideToRadialPoint) thi = [] for n2 in range(elementsCountRadial + 1): - xi = arcLengthInsideToRadialPoint[n2 - 1]/arclengthInsideToOutside - thi.append(thicknesses[0][n1]*xi + thicknesses[-1][n1]*(1.0 - xi)) + xi2 = arcLengthInsideToRadialPoint[n2 - 1] / arclengthInsideToOutside + thi.append(thicknesses[-1][n1] * xi2 + thicknesses[0][n1] * (1.0 - xi2)) + thiProportion = [] + for m3 in range(nodesCountWall): + thiProportionRadial = [] + for n2 in range(elementsCountRadial + 1): + xi2 = arcLengthInsideToRadialPoint[n2 - 1] / arclengthInsideToOutside + thiProportionRadial.append(thicknessProportions[m3][-1][n1] * xi2 + + thicknessProportions[m3][0][n1] * (1.0 - xi2)) + thiProportion.append(thiProportionRadial) else: - mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ ax, bx ], [ ad2Scaled, bd2Scaled ], elementsCountRadial, - derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:4] - md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi) - thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi) + mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ax, bx], [ad2Scaled, bd2Scaled], + elementsCountRadial, derivativeMagnitudeStart, + derivativeMagnitudeEnd)[0:4] + md1 = interp.interpolateSampleLinear([ad1, bd1], me, mxi) + thi = interp.interpolateSampleLinear([thicknesses[0][n1], thicknesses[-1][n1]], me, mxi) + thiProportion = [] + for m3 in range(nodesCountWall): + thiProportion.append(interp.interpolateSampleLinear([thicknessProportions[m3][0][n1], + thicknessProportions[m3][-1][n1]], me, mxi)) # set scalefactors if rescaling, make same on inside for now if rescaleStartDerivatives: - scaleFactor = vector.magnitude(md2[0])/vector.magnitude(ad2) + scaleFactor = vector.magnitude(md2[0]) / vector.magnitude(ad2) scaleFactorMapStart[n3].append(scaleFactor) if rescaleEndDerivatives: - scaleFactor = vector.magnitude(md2[-1])/vector.magnitude(bd2) + scaleFactor = vector.magnitude(md2[-1]) / vector.magnitude(bd2) scaleFactorMapEnd[n3].append(scaleFactor) for n2 in range(1, elementsCountRadial): - px [n3][n2][n1] = mx [n2] + px[n3][n2][n1] = mx[n2] pd1[n3][n2][n1] = md1[n2] pd2[n3][n2][n1] = md2[n2] thicknesses[n2][n1] = thi[n2] + for m3 in range(nodesCountWall): + thicknessProportions[m3][n2][n1] = thiProportion[m3][n2] + + xi3List = [[[[] for n1 in range(nodesCountAround)] for n2 in range(elementsCountRadial + 1)] for n3 in + range(nodesCountWall)] + for n1 in range(nodesCountAround): + for n2 in range(elementsCountRadial + 1): + xi3 = 0.0 + for n3 in range(nodesCountWall): + xi3 += thicknessProportions[n3][n2][n1] + xi3List[n3][n2][n1] = xi3 # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): # first smooth derivative 1 around outer loop - pd1[1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[1][n2], pd1[1][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) - - for n1 in range(nodesCountAround): - normal = vector.normalise(vector.crossproduct3(pd1[1][n2][n1], pd2[1][n2][n1])) - thickness = thicknesses[n2][n1] - d3 = [ d*thickness for d in normal ] - px [0][n2][n1] = [ (px [1][n2][n1][c] - d3[c]) for c in range(3) ] - # calculate inner d1 from curvature around - n1m = n1 - 1 - n1p = (n1 + 1)%nodesCountAround - curvature = 0.5*( - interp.getCubicHermiteCurvature(px[1][n2][n1m], pd1[1][n2][n1m], px[1][n2][n1 ], pd1[1][n2][n1 ], normal, 1.0) + - interp.getCubicHermiteCurvature(px[1][n2][n1 ], pd1[1][n2][n1 ], px[1][n2][n1p], pd1[1][n2][n1p], normal, 0.0)) - factor = 1.0 + curvature*thickness - pd1[0][n2][n1] = [ factor*d for d in pd1[1][n2][n1] ] - # calculate inner d2 from curvature radially - n2m = n2 - 1 - n2p = n2 + 1 - curvature = 0.5*( - interp.getCubicHermiteCurvature(px[1][n2m][n1], pd2[1][n2m][n1], px[1][n2 ][n1], pd2[1][n2 ][n1], normal, 1.0) + - interp.getCubicHermiteCurvature(px[1][n2 ][n1], pd2[1][n2 ][n1], px[1][n2p][n1], pd2[1][n2p][n1], normal, 0.0)) - factor = 1.0 + curvature*thickness - pd2[0][n2][n1] = [ factor*d for d in pd2[1][n2][n1] ] - d2Scaled = [factor*d for d in pd2[1][n2][n1]] - if vector.dotproduct(vector.normalise(pd2[1][n2][n1]), vector.normalise(d2Scaled)) == -1: - pd2[0][n2][n1] = [-factor * d for d in pd2[1][n2][n1]] - if not midLinearXi3: - pd3[0][n2][n1] = pd3[1][n2][n1] = d3 - - # smooth derivative 1 around inner loop - pd1[0][n2] = interp.smoothCubicHermiteDerivativesLoop(px[0][n2], pd1[0][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) - - for n3 in range(0, 1): # was (0, nodesCountWall) + pd1[-1][n2] = \ + interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2], + magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + + for n3 in range(0, nodesCountWall - 1): + for n1 in range(nodesCountAround): + xi3 = 1 - xi3List[n3][n2][n1] + normal = vector.normalise(vector.crossproduct3(pd1[-1][n2][n1], pd2[-1][n2][n1])) + thickness = thicknesses[n2][n1] * xi3 + d3 = [d * thickness for d in normal] + px[n3][n2][n1] = [(px[-1][n2][n1][c] - d3[c]) for c in range(3)] + # calculate inner d1 from curvature around + n1m = n1 - 1 + n1p = (n1 + 1) % nodesCountAround + curvature = 0.5 * ( + interp.getCubicHermiteCurvature(px[-1][n2][n1m], pd1[-1][n2][n1m], + px[-1][n2][n1], pd1[-1][n2][n1], normal, 1.0) + + interp.getCubicHermiteCurvature(px[-1][n2][n1], pd1[-1][n2][n1], px[-1][n2][n1p], + pd1[-1][n2][n1p], normal, 0.0)) + factor = 1.0 + curvature * thickness + pd1[n3][n2][n1] = [factor * d for d in pd1[-1][n2][n1]] + # calculate inner d2 from curvature radially + n2m = n2 - 1 + n2p = n2 + 1 + curvature = 0.5 * ( + interp.getCubicHermiteCurvature(px[-1][n2m][n1], pd2[-1][n2m][n1], + px[-1][n2][n1], pd2[-1][n2][n1], normal, 1.0) + + interp.getCubicHermiteCurvature(px[-1][n2][n1], pd2[-1][n2][n1], + px[-1][n2p][n1], pd2[-1][n2p][n1], normal, 0.0)) + factor = 1.0 + curvature * thickness + pd2[n3][n2][n1] = [factor * d for d in pd2[-1][n2][n1]] + d2Scaled = [factor * d for d in pd2[-1][n2][n1]] + if vector.dotproduct(vector.normalise(pd2[-1][n2][n1]), vector.normalise(d2Scaled)) == -1: + pd2[n3][n2][n1] = [-factor * d for d in pd2[-1][n2][n1]] + if not midLinearXi3: + pd3[n3][n2][n1] = pd3[-1][n2][n1] = \ + [d * thicknesses[n2][n1] * thicknessProportions[n3 + 1][n2][n1] for d in normal] + + # smooth derivative 1 around inner loop + pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop(px[n3][n2], pd1[n3][n2], + magnitudeScalingMode=interp.DerivativeScalingMode. + HARMONIC_MEAN) + + for n3 in range(0, nodesCountWall): # smooth derivative 2 radially/along annulus for n1 in range(nodesCountAround): - mx = [ px [n3][n2][n1] for n2 in range(elementsCountRadial + 1) ] - md2 = [ pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1) ] + mx = [px[n3][n2][n1] for n2 in range(elementsCountRadial + 1)] + md2 = [pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1)] # replace mapped start/end d2 - md2[0] = getMappedD1D2([ startPointsd1[n3][n1], startPointsd2[n3][n1] ] + ([ startPointsd3[n3][n1] ] if startPointsd3 else []), + md2[0] = getMappedD1D2([startPointsd1[n3][n1], startPointsd2[n3][n1]] + + ([startPointsd3[n3][n1]] if startPointsd3 else []), startDerivativesMap[n3][n1] if startDerivativesMap else None)[1] - md2[-1] = getMappedD1D2([ endPointsd1[n3][n1], endPointsd2[n3][n1] ] + ([ endPointsd3[n3][n1] ] if endPointsd3 else []), + md2[-1] = getMappedD1D2([endPointsd1[n3][n1], endPointsd2[n3][n1]] + + ([endPointsd3[n3][n1]] if endPointsd3 else []), endDerivativesMap[n3][n1] if endDerivativesMap else None)[1] - sd2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixAllDirections = True, - fixStartDerivative = not rescaleStartDerivatives, - fixStartDirection = rescaleStartDerivatives, - fixEndDerivative = not rescaleEndDerivatives, - fixEndDirection = rescaleEndDerivatives, - magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) + sd2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixAllDirections=True, + fixStartDerivative=not rescaleStartDerivatives, + fixStartDirection=rescaleStartDerivatives, + fixEndDerivative=not rescaleEndDerivatives, + fixEndDirection=rescaleEndDerivatives, + magnitudeScalingMode=interp.DerivativeScalingMode. + HARMONIC_MEAN) if rescaleStartDerivatives: scaleFactor = vector.magnitude(sd2[0]) / vector.magnitude(md2[0]) scaleFactorMapStart[n3].append(scaleFactor) @@ -356,9 +432,9 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodeIdentifier = nextNodeIdentifier - nodeId = [ [], [] ] - for n3 in range(2): - for n2 in range(elementsCountRadial + 1): + nodeId = [[] for n3 in range(nodesCountWall)] + for n2 in range(elementsCountRadial + 1): + for n3 in range(nodesCountWall): if (n2 == 0) and (startNodeId is not None): rowNodeId = copy.deepcopy(startNodeId[n3]) elif (n2 == elementsCountRadial) and (endNodeId is not None): @@ -391,6 +467,7 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) + elementsCountWall = nodesCountWall - 1 for e2 in range(elementsCountRadial): nonlinearXi3 = (not rowLinearXi3[e2]) or (not rowLinearXi3[e2 + 1]) @@ -401,146 +478,196 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2] mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (endDerivativesMap or rescaleEndDerivatives) mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1] - mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or mapEndDerivatives or mapEndLinearDerivativeXi3 - for e1 in range(elementsCountAround): - en = (e1 + 1)%elementsCountAround - nids = [ nodeId[0][e2][e1], nodeId[0][e2][en], nodeId[0][e2 + 1][e1], nodeId[0][e2 + 1][en], - nodeId[1][e2][e1], nodeId[1][e2][en], nodeId[1][e2 + 1][e1], nodeId[1][e2 + 1][en] ] - scaleFactors = [] - - if mapDerivatives: - eft1 = eftFactory.createEftNoCrossDerivatives() - # work out if scaling by global -1 - scaleMinus1 = mapStartLinearDerivativeXi3 or mapEndLinearDerivativeXi3 - if (not scaleMinus1) and mapStartDerivatives and startDerivativesMap: + mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or \ + mapEndDerivatives or mapEndLinearDerivativeXi3 + for e3 in range(elementsCountWall): + for e1 in range(elementsCountAround): + en = (e1 + 1) % elementsCountAround + nids = [nodeId[e3][e2][e1], nodeId[e3][e2][en], nodeId[e3][e2 + 1][e1], nodeId[e3][e2 + 1][en], + nodeId[e3 + 1][e2][e1], nodeId[e3 + 1][e2][en], nodeId[e3 + 1][e2 + 1][e1], + nodeId[e3 + 1][e2 + 1][en]] + scaleFactors = [] + + if mapDerivatives: + eft1 = eftFactory.createEftNoCrossDerivatives() + # work out if scaling by global -1 + scaleMinus1 = mapStartLinearDerivativeXi3 or mapEndLinearDerivativeXi3 + if (not scaleMinus1) and mapStartDerivatives and startDerivativesMap: + for n3 in range(2): + n3Idx = n3 + e3 + # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) + for map in startDerivativesMap[n3Idx][e1][-3:]: + if map and (-1 in map): + scaleMinus1 = True + break + for map in startDerivativesMap[n3Idx][en][:3]: + if map and (-1 in map): + scaleMinus1 = True + break + if (not scaleMinus1) and mapEndDerivatives and endDerivativesMap: + for n3 in range(2): + n3Idx = n3 + e3 + # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) + for map in endDerivativesMap[n3Idx][e1][-3:]: + if map and (-1 in map): + scaleMinus1 = True + break + for map in endDerivativesMap[n3Idx][en][:3]: + if map and (-1 in map): + scaleMinus1 = True + break + # make node scale factors vary fastest by local node varying across lower xi + nodeScaleFactorIds = [] for n3 in range(2): - # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) - for map in startDerivativesMap[n3][e1][-3:]: - if map and (-1 in map): - scaleMinus1 = True - break - for map in startDerivativesMap[n3][en][:3]: - if map and (-1 in map): - scaleMinus1 = True - break - if (not scaleMinus1) and mapEndDerivatives and endDerivativesMap: + n3Idx = n3 + e3 + if mapStartDerivatives and rescaleStartDerivatives: + for i in range(2): + derivativesMap = (startDerivativesMap[n3Idx][e1][1] if (i == 0) else + startDerivativesMap[n3Idx][en][1]) if startDerivativesMap else None + nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else + (0, 1, 0))) + if mapEndDerivatives and rescaleEndDerivatives: + for i in range(2): + derivativesMap = (endDerivativesMap[n3Idx][e1][1] if (i == 0) else + endDerivativesMap[n3Idx][en][1]) if endDerivativesMap else None + nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else + (0, 1, 0))) + setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [], nodeScaleFactorIds) + firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1 + firstStartNodeScaleFactorIndex = \ + firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None + firstEndNodeScaleFactorIndex = \ + (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) \ + if (mapEndDerivatives and rescaleEndDerivatives) else None + layerNodeScaleFactorIndexOffset = \ + 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 + if scaleMinus1: + scaleFactors.append(-1.0) for n3 in range(2): - # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) - for map in endDerivativesMap[n3][e1][-3:]: - if map and (-1 in map): - scaleMinus1 = True - break - for map in endDerivativesMap[n3][en][:3]: - if map and (-1 in map): - scaleMinus1 = True - break - # make node scale factors vary fastest by local node varying across lower xi - nodeScaleFactorIds = [] - for n3 in range(2): - if mapStartDerivatives and rescaleStartDerivatives: + n3Idx = n3 + e3 + if firstStartNodeScaleFactorIndex: + scaleFactors.append(scaleFactorMapStart[n3Idx][e1]) + scaleFactors.append(scaleFactorMapStart[n3Idx][en]) + if firstEndNodeScaleFactorIndex: + scaleFactors.append(scaleFactorMapEnd[n3Idx][e1]) + scaleFactors.append(scaleFactorMapEnd[n3Idx][en]) + + if mapStartLinearDerivativeXi3: + eftFactory.setEftLinearDerivative2(eft1, [1, 5, 2, 6], Node.VALUE_LABEL_D_DS3, + [Node.VALUE_LABEL_D2_DS1DS3]) + if mapStartDerivatives: for i in range(2): - derivativesMap = (startDerivativesMap[n3][e1][1] if (i == 0) else startDerivativesMap[n3][en][1]) if startDerivativesMap else None - nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) - if mapEndDerivatives and rescaleEndDerivatives: + lns = [1, 5] if (i == 0) else [2, 6] + for n3 in range(2): + n3Idx = n3 + e3 + derivativesMap = \ + (startDerivativesMap[n3Idx][e1] if (i == 0) else startDerivativesMap[n3Idx][en]) \ + if startDerivativesMap else (None, None, None) + # handle different d1 on each side of node + d1Map = \ + derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] + d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) + d3Map = derivativesMap[2] + # use temporary to safely swap DS1 and DS2: + ln = [lns[n3]] + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D2_DS1DS2, [])]) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D2_DS2DS3, [])]) + if d2Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d2Map, + (firstStartNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleStartDerivatives else None)) + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d1Map)) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d3Map)) + if mapEndLinearDerivativeXi3: + eftFactory.setEftLinearDerivative2(eft1, [3, 7, 4, 8], Node.VALUE_LABEL_D_DS3, + [Node.VALUE_LABEL_D2_DS1DS3]) + if mapEndDerivatives: for i in range(2): - derivativesMap = (endDerivativesMap[n3][e1][1] if (i == 0) else endDerivativesMap[n3][en][1]) if endDerivativesMap else None - nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) - setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [], nodeScaleFactorIds) - firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1 - firstStartNodeScaleFactorIndex = firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None - firstEndNodeScaleFactorIndex = (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) if (mapEndDerivatives and rescaleEndDerivatives) else None - layerNodeScaleFactorIndexOffset = 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 - if scaleMinus1: - scaleFactors.append(-1.0) - for n3 in range(2): - if firstStartNodeScaleFactorIndex: - scaleFactors.append(scaleFactorMapStart[n3][e1]) - scaleFactors.append(scaleFactorMapStart[n3][en]) - if firstEndNodeScaleFactorIndex: - scaleFactors.append(scaleFactorMapEnd[n3][e1]) - scaleFactors.append(scaleFactorMapEnd[n3][en]) - - if mapStartLinearDerivativeXi3: - eftFactory.setEftLinearDerivative2(eft1, [ 1, 5, 2, 6 ], Node.VALUE_LABEL_D_DS3, [ Node.VALUE_LABEL_D2_DS1DS3 ]) - if mapStartDerivatives: - for i in range(2): - lns = [ 1, 5 ] if (i == 0) else [ 2, 6 ] - for n3 in range(2): - derivativesMap = (startDerivativesMap[n3][e1] if (i == 0) else startDerivativesMap[n3][en]) if startDerivativesMap else (None, None, None) - # handle different d1 on each side of node - d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] - d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) - d3Map = derivativesMap[2] - # use temporary to safely swap DS1 and DS2: - ln = [ lns[n3] ] - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) - if d2Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, - Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3 ), d2Map, - (firstStartNodeScaleFactorIndex + i + n3*layerNodeScaleFactorIndexOffset) if rescaleStartDerivatives else None) ) - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) - if mapEndLinearDerivativeXi3: - eftFactory.setEftLinearDerivative2(eft1, [ 3, 7, 4, 8 ], Node.VALUE_LABEL_D_DS3, [ Node.VALUE_LABEL_D2_DS1DS3 ]) - if mapEndDerivatives: - for i in range(2): - lns = [ 3, 7 ] if (i == 0) else [ 4, 8 ] - for n3 in range(2): - derivativesMap = (endDerivativesMap[n3][e1] if (i == 0) else endDerivativesMap[n3][en]) if endDerivativesMap else (None, None, None) - # handle different d1 on each side of node - d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] - d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) - d3Map = derivativesMap[2] - - # use temporary to safely swap DS1 and DS2: - ln = [ lns[n3] ] - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) - if d2Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, - Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3), d2Map, - (firstEndNodeScaleFactorIndex + i + n3*layerNodeScaleFactorIndexOffset) if rescaleEndDerivatives else None) ) - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) - - elementtemplateX.defineField(coordinates, -1, eft1) - elementtemplate1 = elementtemplateX - else: - eft1 = eftStandard - elementtemplate1 = elementtemplateStandard - - element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nids) - if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors); - #print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), result2, result3 if scaleFactors else None, nids) - elementIdentifier += 1 - - if rowMeshGroups: - for meshGroup in rowMeshGroups[e2]: - meshGroup.addElement(element) + lns = [3, 7] if (i == 0) else [4, 8] + for n3 in range(2): + n3Idx = n3 + e3 + derivativesMap = \ + (endDerivativesMap[n3Idx][e1] if (i == 0) else endDerivativesMap[n3Idx][en]) \ + if endDerivativesMap else (None, None, None) + # handle different d1 on each side of node + d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else \ + derivativesMap[3] + d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) + d3Map = derivativesMap[2] + + # use temporary to safely swap DS1 and DS2: + ln = [lns[n3]] + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D2_DS1DS2, [])]) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D2_DS2DS3, [])]) + if d2Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d2Map, + (firstEndNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleEndDerivatives else None)) + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d1Map)) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d3Map)) + + elementtemplateX.defineField(coordinates, -1, eft1) + elementtemplate1 = elementtemplateX + else: + eft1 = eftStandard + elementtemplate1 = elementtemplateStandard + + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scaleFactors: + result3 = element.setScaleFactors(eft1, scaleFactors) + # print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), + # result2, result3 if scaleFactors else None, nids) + elementIdentifier += 1 + + if rowMeshGroups: + for meshGroup in rowMeshGroups[e2]: + meshGroup.addElement(element) + + if wallAnnotationGroups: + for annotationGroup in wallAnnotationGroups[e3]: + meshGroup = annotationGroup.getMeshGroup(mesh) + meshGroup.addElement(element) fm.endChange() return nodeIdentifier, elementIdentifier + def getQuadrantID(d): """ Returns a scale factor ID based on direction of the derivative. Index starts @@ -550,7 +677,8 @@ def getQuadrantID(d): """ maps = [(1, 0, 0), (1, 1, 0), (0, 1, 0), (-1, 1, 0), (-1, 0, 0), (-1, -1, 0), (0, -1, 0), (1, -1, 0), # d3 = 0 (1, 0, 1), (1, 1, 1), (0, 1, 1), (-1, 1, 1), (-1, 0, 1), (-1, -1, 1), (0, -1, 1), (1, -1, 1), (0, 0, 1), # d3 = 1 - (1, 0, -1), (1, 1, -1), (0, 1, -1), (-1, 1, -1), (-1, 0, -1), (-1, -1, -1), (0, -1, -1), (1, -1, -1), (0, 0, -1)] # d3 = -1 + (1, 0, -1), (1, 1, -1), (0, 1, -1), (-1, 1, -1), (-1, 0, -1), (-1, -1, -1), (0, -1, -1), (1, -1, -1), + (0, 0, -1)] # d3 = -1 for i in range(len(maps)): if d == maps[i]: return i + 1 diff --git a/tests/test_bladder.py b/tests/test_bladder.py index 7cff9694..9d51085d 100644 --- a/tests/test_bladder.py +++ b/tests/test_bladder.py @@ -24,7 +24,7 @@ def test_bladderurethra1(self): """ scaffold = MeshType_3d_bladderurethra1 parameterSetNames = MeshType_3d_bladderurethra1.getParameterSetNames() - self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Rat 1", "Human 1"]) + self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Human 1", "Mouse 1", "Pig 1", "Rat 1"]) options = scaffold.getDefaultOptions("Cat 1") self.assertEqual(26, len(options)) self.assertEqual(12, options.get("Number of elements along bladder")) @@ -67,7 +67,7 @@ def test_bladderurethra1(self): self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) assertAlmostEqualList(self, minimums, [-15.48570141314588, -12.992184072505665, -0.5], 1.0E-6) - assertAlmostEqualList(self, maximums, [15.485696373577879, 13.837600783918127, 127.68599990411343], 1.0E-6) + assertAlmostEqualList(self, maximums, [15.485696373577879, 13.837536258199144, 127.68631532717487], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -79,10 +79,10 @@ def test_bladderurethra1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 5335.535739164492, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 5334.9516480055845, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 2555.81694845211, delta=1.0E-6) + self.assertAlmostEqual(volume, 2555.560965374249, delta=1.0E-6) # check some annotationGroups: expectedSizes3d = { @@ -111,7 +111,7 @@ def test_bladderurethra1(self): cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) self.assertEqual(1, element.getIdentifier()) - assertAlmostEqualList(self, xi, [ 0.0, 0.0, 0.0 ], 1.0E-10) + assertAlmostEqualList(self, xi, [0.0, 0.0, 0.0], 1.0E-10) apexGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("apex of urinary bladder")) self.assertTrue(apexGroup.getNodesetGroup(nodes).containsNode(node)) @@ -119,7 +119,8 @@ def test_bladderurethra1(self): # first remove any surface annotation groups as they are re-added by defineFaceAnnotations removeAnnotationGroups = [] for annotationGroup in annotationGroups: - if (not annotationGroup.hasMeshGroup(mesh3d)) and (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): + if (not annotationGroup.hasMeshGroup(mesh3d)) and \ + (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): removeAnnotationGroups.append(annotationGroup) for annotationGroup in removeAnnotationGroups: @@ -179,7 +180,8 @@ def test_bladderurethra1(self): cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) self.assertEqual(1, element.getIdentifier()) - assertAlmostEqualList(self, xi, [ 0.0, 0.0, 0.0 ], 1.0E-10) + assertAlmostEqualList(self, xi, [0.0, 0.0, 0.0], 1.0E-10) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_cecum.py b/tests/test_cecum.py index 70c4a5c8..715d6a83 100644 --- a/tests/test_cecum.py +++ b/tests/test_cecum.py @@ -19,7 +19,7 @@ def test_cecum1(self): Test creation of cecum scaffold. """ parameterSetNames = MeshType_3d_cecum1.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Pig 1" ]) + self.assertEqual(parameterSetNames, ["Default", "Pig 1"]) options = MeshType_3d_cecum1.getDefaultOptions("Pig 1") self.assertEqual(30, len(options)) self.assertEqual(5, options.get("Number of segments")) @@ -82,10 +82,11 @@ def test_cecum1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 65959.34314511667, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 65960.20655074248, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 127894.8597203706, delta=1.0E-6) + self.assertAlmostEqual(volume, 127905.28250502056, delta=1.0E-6) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_colon.py b/tests/test_colon.py index 0fd391b3..9537c220 100644 --- a/tests/test_colon.py +++ b/tests/test_colon.py @@ -24,12 +24,13 @@ def test_colon1(self): Test creation of colon scaffold. """ parameterSetNames = MeshType_3d_colon1.getParameterSetNames() - self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Human 1", "Human 2", "Mouse 1", "Mouse 2", "Pig 1", "Pig 2"]) + self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Human 1", "Human 2", "Mouse 1", "Mouse 2", "Pig 1", + "Pig 2"]) centralPathDefaultScaffoldPackages = { 'Test line': ScaffoldPackage(MeshType_1d_path1, { 'scaffoldSettings': { 'Coordinate dimensions': 3, - 'D2 derivatives':True, + 'D2 derivatives': True, 'Length': 1.0, 'Number of elements': 1 }, @@ -72,7 +73,7 @@ def test_colon1(self): 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(0.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")) @@ -89,8 +90,8 @@ def test_colon1(self): centralPath.generate(tmpRegion) cx = extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE])[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) + 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) @@ -115,19 +116,19 @@ def test_colon1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ 108.02506479907721, -36.876103983560014, -25.89741158484918 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 185.46457506076914, 48.1011574894518, 34.995316052158934 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [108.02506479907721, -36.405037279268456, -25.89741158484918], 1.0E-6) + assertAlmostEqualList(self, maximums, [185.46457506076914, 48.1011574894518, 34.05259862880112], 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.41781871321301, 3.2000000000000006 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [0.0, 0.0, 0.0], 1.0E-6) + assertAlmostEqualList(self, maximums, [186.72988844629867, 77.41781871321301, 2.2], 1.0E-6) colonCoordinates = fieldmodule.findFieldByName("colon coordinates").castFiniteElement() minimums, maximums = evaluateFieldNodesetRange(colonCoordinates, nodes) assertAlmostEqualList(self, minimums, [-0.6, 0.0, -0.6], 1.0E-4) - assertAlmostEqualList(self, maximums, [ 0.6, 24.0, 0.625], 1.0E-4) + assertAlmostEqualList(self, maximums, [0.6, 24.0, 0.625], 1.0E-4) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -139,10 +140,10 @@ def test_colon1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 14612.416789097502, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 14342.540002125375, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 26825.42839677291, delta=1.0E-6) + self.assertAlmostEqual(volume, 25983.483155342656, delta=1.0E-6) def test_mousecolon1(self): """ @@ -186,5 +187,6 @@ def test_mousecolon1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(colonVolume, 8.290058800222006, delta=1.0E-6) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_colonsegment.py b/tests/test_colonsegment.py index ad73a193..786181d3 100644 --- a/tests/test_colonsegment.py +++ b/tests/test_colonsegment.py @@ -19,7 +19,7 @@ def test_humancolonsegment1(self): Test creation of human colon segment scaffold. """ parameterSetNames = MeshType_3d_colonsegment1.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Cattle 1", "Human 1", "Mouse 1", "Pig 1" ]) + self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Human 1", "Mouse 1", "Pig 1"]) options = MeshType_3d_colonsegment1.getDefaultOptions("Human 1") self.assertEqual(31, len(options)) self.assertEqual(0.0, options.get("Start phase")) @@ -59,14 +59,14 @@ def test_humancolonsegment1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -2.172286248499807e-15, -58.95670186936737, -55.54662267827035 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 50.0, 50.52621132610023, 55.54662267827035 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [-2.172286248499807e-15, -58.95670186936737, -55.54662267827035], 1.0E-6) + assertAlmostEqualList(self, maximums, [50.0, 50.52621132610023, 55.54662267827035], 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, [397.2736607240895, 50.0, 3.2000000000000006], 1.0E-6) + assertAlmostEqualList(self, minimums, [0.0, 0.0, 0.0], 1.0E-6) + assertAlmostEqualList(self, maximums, [397.2736607240895, 50.0, 2.2], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -78,10 +78,10 @@ def test_humancolonsegment1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 21129.564192298032, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 21035.8818469729, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 40783.41115796618, delta=1.0E-6) + self.assertAlmostEqual(volume, 39781.704358310606, delta=1.0E-6) def test_mousecolonsegment1(self): """ @@ -119,5 +119,6 @@ def test_mousecolonsegment1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(flatSurfaceArea, surfaceArea, delta=1.0E-3) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_smallintestine.py b/tests/test_smallintestine.py index 2ce42950..5e5e1025 100644 --- a/tests/test_smallintestine.py +++ b/tests/test_smallintestine.py @@ -68,8 +68,8 @@ def test_smallintestine1(self): 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) + 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) @@ -91,14 +91,14 @@ def test_smallintestine1(self): 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) + 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) + 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) @@ -121,5 +121,6 @@ def test_smallintestine1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(flatSurfaceArea, 171.37026123844635, delta=1.0E-3) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_stomach.py b/tests/test_stomach.py index 021055e8..90c1dbe2 100644 --- a/tests/test_stomach.py +++ b/tests/test_stomach.py @@ -24,9 +24,9 @@ def test_stomach1(self): """ scaffold = MeshType_3d_stomach1 parameterSetNames = scaffold.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Rat 1" ]) + self.assertEqual(parameterSetNames, ["Default", "Human 1", "Mouse 1", "Rat 1"]) options = scaffold.getDefaultOptions("Rat 1") - self.assertEqual(15, len(options)) + self.assertEqual(19, len(options)) self.assertEqual(12, options.get("Number of elements around esophagus")) self.assertEqual(14, options.get("Number of elements around duodenum")) self.assertEqual(2, options.get("Number of elements between cardia and duodenum")) @@ -36,13 +36,15 @@ def test_stomach1(self): ostiumOptions = options['Gastro-esophagal junction'] ostiumSettings = ostiumOptions.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(4.0, ostiumSettings.get("Ostium diameter")) - self.assertEqual(3.5, ostiumSettings.get("Ostium length")) + self.assertEqual(12, ostiumSettings.get("Number of elements around ostium")) + self.assertEqual(4, ostiumSettings.get("Number of elements through wall")) + self.assertEqual(5.0, ostiumSettings.get("Ostium diameter")) + self.assertEqual(5.0, ostiumSettings.get("Ostium length")) self.assertEqual(0.5, ostiumSettings.get("Ostium wall thickness")) - self.assertEqual(1.25, ostiumSettings.get("Vessel inner diameter")) + self.assertEqual([0.65, 0.12, 0.18, 0.05], ostiumSettings.get("Ostium wall relative thicknesses")) + self.assertEqual(2.0, ostiumSettings.get("Vessel inner diameter")) self.assertEqual(0.5, ostiumSettings.get("Vessel wall thickness")) + self.assertEqual([0.65, 0.12, 0.18, 0.05], ostiumSettings.get("Vessel wall relative thicknesses")) self.assertEqual(0.0, ostiumSettings.get("Vessel angle 1 degrees")) self.assertEqual(0.55, options.get("Gastro-esophagal junction position along factor")) self.assertEqual(0.2, options.get("Cardia derivative factor")) @@ -51,26 +53,26 @@ def test_stomach1(self): region = context.getDefaultRegion() self.assertTrue(region.isValid()) annotationGroups = scaffold.generateBaseMesh(region, options) - self.assertEqual(13, len(annotationGroups)) + self.assertEqual(37, len(annotationGroups)) fieldmodule = region.getFieldmodule() self.assertEqual(RESULT_OK, fieldmodule.defineAllFaces()) mesh3d = fieldmodule.findMeshByDimension(3) - self.assertEqual(158, mesh3d.getSize()) + self.assertEqual(582, mesh3d.getSize()) mesh2d = fieldmodule.findMeshByDimension(2) - self.assertEqual(643, mesh2d.getSize()) + self.assertEqual(1965, mesh2d.getSize()) mesh1d = fieldmodule.findMeshByDimension(1) - self.assertEqual(823, mesh1d.getSize()) + self.assertEqual(2195, mesh1d.getSize()) nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(342, nodes.getSize()) + self.assertEqual(830, 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, [-18.535034704449842, -14.989550140413265, -8.726949180639382], 1.0E-6) - assertAlmostEqualList(self, maximums, [18.39909561216101, 15.187335864125645, 8.727618869278126], 1.0E-6) + assertAlmostEqualList(self, minimums, [-18.238549598577396, -16.033751319943754, -8.905924748773598], 1.0E-6) + assertAlmostEqualList(self, maximums, [18.285156743233415, 15.214807824088728, 8.905433142848109], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -82,21 +84,21 @@ def test_stomach1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 2452.212186914634, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 2557.832902256128, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 1175.367658574881, delta=1.0E-6) + self.assertAlmostEqual(volume, 809.0349219398828, delta=1.0E-6) # check some annotationGroups: expectedSizes3d = { - "body of stomach" : 28, - "cardia of stomach" : 12, - "duodenum" : 14, - "esophagus" : 24, - "fundus of stomach" : 38, - "pyloric antrum" : 28, - "pylorus": 14, - "stomach": 158 + "body of stomach": 112, + "cardia of stomach": 36, + "duodenum": 56, + "esophagus": 96, + "fundus of stomach": 114, + "pyloric antrum": 112, + "pyloric canal": 56, + "stomach": 582 } for name in expectedSizes3d: group = getAnnotationGroupForTerm(annotationGroups, get_stomach_term(name)) @@ -107,12 +109,13 @@ def test_stomach1(self): # first remove any surface annotation groups as they are re-added by defineFaceAnnotations removeAnnotationGroups = [] for annotationGroup in annotationGroups: - if (not annotationGroup.hasMeshGroup(mesh3d)) and (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): + if (not annotationGroup.hasMeshGroup(mesh3d)) and \ + (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): removeAnnotationGroups.append(annotationGroup) for annotationGroup in removeAnnotationGroups: annotationGroups.remove(annotationGroup) - self.assertEqual(13, len(annotationGroups)) + self.assertEqual(37, len(annotationGroups)) refineRegion = region.createRegion() refineFieldmodule = refineRegion.getFieldmodule() @@ -130,16 +133,16 @@ def test_stomach1(self): for annotation in annotationGroups: if annotation not in oldAnnotationGroups: annotationGroup.addSubelements() - self.assertEqual(16, len(annotationGroups)) + self.assertEqual(68, len(annotationGroups)) # mesh3d = refineFieldmodule.findMeshByDimension(3) - self.assertEqual(10112, mesh3d.getSize()) + self.assertEqual(37248, mesh3d.getSize()) mesh2d = refineFieldmodule.findMeshByDimension(2) - self.assertEqual(33232, mesh2d.getSize()) + self.assertEqual(115248, mesh2d.getSize()) mesh1d = refineFieldmodule.findMeshByDimension(1) - self.assertEqual(36028, mesh1d.getSize()) + self.assertEqual(118796, mesh1d.getSize()) nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(12936, nodes.getSize()) + self.assertEqual(40814, nodes.getSize()) datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) self.assertEqual(0, datapoints.getSize()) @@ -153,18 +156,20 @@ def test_stomach1(self): markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() - self.assertEqual(4, markerNodes.getSize()) + self.assertEqual(18, markerNodes.getSize()) markerName = refineFieldmodule.findFieldByName("marker_name") self.assertTrue(markerName.isValid()) markerLocation = refineFieldmodule.findFieldByName("marker_location") self.assertTrue(markerLocation.isValid()) cache = refineFieldmodule.createFieldcache() - node = findNodeWithName(markerNodes, markerName, "gastro-esophagal junction on lesser curvature") + node = findNodeWithName(markerNodes, markerName, + "esophagogastric junction along the lesser curvature on serosa") self.assertTrue(node.isValid()) cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) - self.assertEqual(1213, element.getIdentifier()) - assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + self.assertEqual(5821, element.getIdentifier()) + assertAlmostEqualList(self, xi, [0.0, 1.0, 1.0], 1.0E-10) + if __name__ == "__main__": unittest.main()