Example #1
0
def load_profile():
    with_profile(env.profile)
    from HemeLbSetupTool.Model.Profile import Profile
    p = Profile()
    p.LoadFromFile(os.path.expanduser(os.path.join(env.job_profile_path_local,env.profile)+'.pro'))
    p.StlFile=os.path.expanduser(os.path.join(env.job_profile_path_local,env.profile)+'.stl')
    return p
Example #2
0
class SetupTool(wx.App):
    def __init__(self, args={}, profile=None, **kwargs):
        self.cmdLineArgs = args
        self.cmdLineProfileFile = profile
        
        wx.App.__init__(self, **kwargs)
        return
    
    def OnInit(self):
        # Model
        self.profile = Profile()
        
        self.pipeline = Pipeline()
        
        # Controller
        self.controller = ProfileController(self.profile)
        self.controller.Pipeline = PipelineController(self.pipeline, self.controller)
        
        # View
        self.view = MainWindow(self.controller)

        if self.cmdLineProfileFile is not None:
            # Load the profile
            self.profile.LoadFromFile(self.cmdLineProfileFile)
            pass
        
        # override any keys that have been set on cmdline.
        self.profile.UpdateAttributesBasedOnCmdLineArgs(self.cmdLineArgs)
        
        self.SetTopWindow(self.view)
        return True
    
    pass
Example #3
0
    def __init__(self, proFile):
        # Configurable parameters
        self.MaxAllowedMachNumber = 0.05
        self.InletReynoldsNumber = 300.0
        self.MinRadiusVoxels = 5.0
        
        # Read the profile
        self.FileName = proFile
        self._profile = Profile()
        self._profile.LoadFromFile(proFile)
        # Proxy its iolets
        self.Iolets = [IoletProxy(iolet, self) for iolet in self._profile.Iolets]

        # Work out where to store the centrelines
        base = os.path.splitext(self.StlFile)[0]        
        self.CentreLineFile = base + '_centrelines.vtp'

        # Sort iolets -> inlets/outlets
        inlets = []
        outlets = []
        for io in self.Iolets:
            if isinstance(io._iolet, Inlet):
                inlets.append(io)
            else:
                outlets.append(io)
                pass
            continue
        # Require only one inlet!
        assert len(inlets) == 1
        self.Inlet = inlets[0]
        # Require 1 or more outlet
        assert len(outlets) > 0
        self.Outlets = outlets
        
        return
Example #4
0
def temporary_profile(tmpdir, name):
    result = Profile()
    basename = tmpdir.join(name).strpath
    outGmyFileName = basename + '.gmy'
    outXmlFileName = basename + '.xml'
    result.OutputGeometryFile = outGmyFileName
    result.OutputXmlFile = outXmlFileName
    return result
Example #5
0
File: load.py Project: UCL/hemelb
def temporary_profile(tmpdir, name):
    result = Profile()
    basename = tmpdir.join(name).strpath
    outGmyFileName = basename + '.gmy'
    outXmlFileName = basename + '.xml'
    result.OutputGeometryFile = outGmyFileName
    result.OutputXmlFile = outXmlFileName
    return result
Example #6
0
def load_profile():
    with_profile(env.profile)
    from HemeLbSetupTool.Model.Profile import Profile
    p = Profile()
    p.LoadFromFile(
        os.path.expanduser(
            os.path.join(env.job_profile_path_local, env.profile) + '.pro'))
    return p
Example #7
0
def Upgrade(infilename, outfilename):
    oldProfile = LoadFakeProfile(infilename)
    UpdateProfileAttributes(oldProfile)

    # Create a new, genuine profile
    newPro = Profile()
    newPro.CloneFrom(oldProfile)

    newPro.Save(outfilename)
    return
Example #8
0
def Run(profileFile):
    """Process all the Inlets specified by profileFile, solving the Navier-
    Stokes equations for steady flow down an infinite channel with the cross
    section of the inlet. Writes the point ID and the fluid velocity at that 
    point, the velocity being such that the integral of the flow across the
    channel is unity.
    
    Currently output is written as vtkPolyData to files like
    "$GEOMETRYFILEBASENAME.inlet$ID.vtp"
    """
    # Load the profile
    profile = Profile()
    profile.LoadFromFile(profileFile)

    surfaceFile = profile.StlFile
    surfaceFileScale = profile.StlFileUnit.SizeInMetres

    print profile.OutputXmlFile
    ipFinder = GeometryInletPointFinder(profile.OutputXmlFile)
    inletPointIndices = ipFinder.GetInletData()
    if len(inletPointIndices) == 0:
        print "WARNING: no inletPointIndices found. This may mean that HemeLb is run with no inlets inside the simulation domain (e.g., the inlets defined in the xml file reside outside of the physical geometry)."

    intersectionFinder = SurfaceIntersectionFinder()
    intersectionFinder.SetFileName(surfaceFile)
    intersectionFinder.SetFileUnitLength(surfaceFileScale)

    for inletId, inlet in enumerate(io for io in profile.Iolets
                                    if isinstance(io, Inlet)):
        print inletId, len(profile.Iolets), len(inletPointIndices)
        inletPointPD = inletPointIndices[inletId]
        intersectionFinder.SetIolet(inlet)
        tesselator = Tesselator()
        tesselator.SetInlet(inlet)
        tesselator.SetEdgeConnection(intersectionFinder.GetOutputPort())
        tesselator.SetSitesConnection(inletPointPD.GetProducerPort())

        solver = PoiseuilleSolver()
        solver.SetInputConnection(tesselator.GetOutputPort())

        cellToPoint = vtk.vtkCellDataToPointData()
        cellToPoint.SetInputConnection(solver.GetOutputPort())
        cellToPoint.Update()
        #writer = vtk.vtkXMLPolyDataWriter()
        writer = vtk.vtkPolyDataWriter()
        writer.SetFileTypeToASCII()
        writer.SetInputConnection(cellToPoint.GetOutputPort())

        # print type(cellToPoint) #cellToPoint is of type vtkobject
        # print dir(cellToPoint)

        base, gmy = os.path.splitext(profile.OutputGeometryFile)
        writer.SetFileName(base + '.inlet%d.txt' % inletId)
        writer.Write()
        return base + '.inlet%d.txt' % inletId
Example #9
0
def Run(profileFile):
    """Process all the Inlets specified by profileFile, solving the Navier-
    Stokes equations for steady flow down an infinite channel with the cross
    section of the inlet. Writes the point ID and the fluid velocity at that 
    point, the velocity being such that the integral of the flow across the
    channel is unity.
    
    Currently output is written as vtkPolyData to files like
    "$GEOMETRYFILEBASENAME.inlet$ID.vtp"
    """
    # Load the profile
    profile = Profile()
    profile.LoadFromFile(profileFile)

    surfaceFile = profile.StlFile
    surfaceFileScale = profile.StlFileUnit.SizeInMetres

    ipFinder = GeometryInletPointFinder(profile.OutputGeometryFile)
    inletPointIndices = ipFinder.GetInletData()

    intersectionFinder = SurfaceIntersectionFinder()
    intersectionFinder.SetFileName(surfaceFile)
    intersectionFinder.SetFileUnitLength(surfaceFileScale)

    for inletId, inlet in enumerate(io for io in profile.Iolets
                                    if isinstance(io, Inlet)):
        inletPointPD = inletPointIndices[inletId]
        intersectionFinder.SetIolet(inlet)
        tesselator = Tesselator()
        tesselator.SetInlet(inlet)
        tesselator.SetEdgeConnection(intersectionFinder.GetOutputPort())
        tesselator.SetSitesConnection(inletPointPD.GetProducerPort())

        solver = PoiseuilleSolver()
        solver.SetInputConnection(tesselator.GetOutputPort())

        cellToPoint = vtk.vtkCellDataToPointData()
        cellToPoint.SetInputConnection(solver.GetOutputPort())
        cellToPoint.Update()
        writer = vtk.vtkXMLPolyDataWriter()
        writer.SetInputConnection(cellToPoint.GetOutputPort())

        base, gmy = os.path.splitext(profile.OutputGeometryFile)
        writer.SetFileName(base + '.inlet%d.vtp' % inletId)
        writer.Write()
 def test_regression(self, tmpdir):
     """Generate a gmy from a stored profile and check that the output is
     identical.
     """
     dataDir = os.path.join(os.path.split(__file__)[0], 'data')
     proFileName = os.path.join(dataDir, 'test.pro')
     
     p = Profile()
     p.LoadFromFile(proFileName)
     # Change the output to the tmpdir
     basename = tmpdir.join('test').strpath
     outGmyFileName = basename + '.gmy'
     outXmlFileName = basename + '.xml'
     p.OutputGeometryFile = outGmyFileName
     p.OutputXmlFile = outXmlFileName
     
     generator = OutputGeneration.PolyDataGenerator(p)
     generator.Execute()
     
     import filecmp
     assert filecmp.cmp(outGmyFileName, os.path.join(dataDir, 'test.gmy'))
     assert filecmp.cmp(outXmlFileName, os.path.join(dataDir, 'test.xml'))
    def test_regression(self, tmpdir):
        """Generate a gmy from a stored profile and check that the output is
        identical.
        """
        dataDir = os.path.join(os.path.split(__file__)[0], 'data')
        proFileName = os.path.join(dataDir, 'test.pro')

        p = Profile()
        p.LoadFromFile(proFileName)
        # Change the output to the tmpdir
        basename = tmpdir.join('test').strpath
        outGmyFileName = basename + '.gmy'
        outXmlFileName = basename + '.xml'
        p.OutputGeometryFile = outGmyFileName
        p.OutputXmlFile = outXmlFileName

        generator = OutputGeneration.PolyDataGenerator(p)
        generator.Execute()

        import filecmp
        assert filecmp.cmp(outGmyFileName, os.path.join(dataDir, 'test.gmy'))
        assert filecmp.cmp(outXmlFileName, os.path.join(dataDir, 'test.xml'))
Example #12
0
    def OnInit(self):
        # Model
        self.profile = Profile()
        
        self.pipeline = Pipeline()
        
        # Controller
        self.controller = ProfileController(self.profile)
        self.controller.Pipeline = PipelineController(self.pipeline, self.controller)
        
        # View
        self.view = MainWindow(self.controller)

        if self.cmdLineProfileFile is not None:
            # Load the profile
            self.profile.LoadFromFile(self.cmdLineProfileFile)
            pass
        
        # override any keys that have been set on cmdline.
        self.profile.UpdateAttributesBasedOnCmdLineArgs(self.cmdLineArgs)
        
        self.SetTopWindow(self.view)
        return True
Example #13
0
class ProfileProxy(HemeLbParameters):
    """Add a few properties to the Profile. Delegates unknown
    attribute look up to the profile.
    """
    _OutputUnitsMap = HemeLbParameters._UnitQuantitiesToMap([pq.mmHg])
    
    def __init__(self, proFile):
        # Configurable parameters
        self.MaxAllowedMachNumber = 0.05
        self.InletReynoldsNumber = 300.0
        self.MinRadiusVoxels = 5.0
        
        # Read the profile
        self.FileName = proFile
        self._profile = Profile()
        self._profile.LoadFromFile(proFile)
        # Proxy its iolets
        self.Iolets = [IoletProxy(iolet, self) for iolet in self._profile.Iolets]

        # Work out where to store the centrelines
        base = os.path.splitext(self.StlFile)[0]        
        self.CentreLineFile = base + '_centrelines.vtp'

        # Sort iolets -> inlets/outlets
        inlets = []
        outlets = []
        for io in self.Iolets:
            if isinstance(io._iolet, Inlet):
                inlets.append(io)
            else:
                outlets.append(io)
                pass
            continue
        # Require only one inlet!
        assert len(inlets) == 1
        self.Inlet = inlets[0]
        # Require 1 or more outlet
        assert len(outlets) > 0
        self.Outlets = outlets
        
        return
        
    def __getattr__(self, attr):
        # This is only called if self doesn't have the requested
        # attribute. We delegate to the real Profile object
        return getattr(self._profile, attr)
    
    @property
    def LengthUnit(self):
        return self.StlFileUnit.SizeInMetres * pq.metre
    
    @memo_property
    def CentreLinePolyData(self):
        """Compute centrelines based on the profile, reusing our
        memoed copy or reading from the cache file if possible.
        """
        if (os.path.exists(self.CentreLineFile ) and 
            os.path.getmtime(self.CentreLineFile ) > os.path.getmtime(self.StlFile) and
            os.path.getmtime(self.CentreLineFile ) > os.path.getmtime(self.FileName)):
            # Cached!
            reader = vtk.vtkXMLPolyDataReader()
            reader.SetFileName(self.CentreLineFile)
            reader.Update()
            return reader.GetOutput()

        # Have to compute it
        
        # Read the STL file
        reader = vtk.vtkSTLReader()
        reader.SetFileName(profile.StlFile)
        
        # Find the seed points for centreline calculation
        # Use points one iolet radius back along the normal.
        
        outletPts = []
        def scale(iolet):
            pt = (iolet.Centre - iolet.Radius * iolet.Normal)
            pt = pt / self.LengthUnit
            return pt.magnitude
        
        for iolet in self.Iolets:
            if isinstance(iolet._iolet, Inlet):
                inletPt = scale(iolet)
            else:
                outletPts.append(scale(iolet))
                pass
            continue
        
        srcPts, tgtPts = FindSeeds(reader, inletPt, outletPts)
        
        # Lazy import since it's so slow!
        from vmtk import vtkvmtk
        centreliner = vtkvmtk.vtkvmtkPolyDataCenterlines()
        centreliner.SetInputConnection(reader.GetOutputPort())
    
        centreliner.SetSourceSeedIds(srcPts)
        centreliner.SetTargetSeedIds(tgtPts)
        centreliner.SetRadiusArrayName("radius")
        centreliner.SetCostFunction("1/R")

        cleaner = vtk.vtkCleanPolyData()
        cleaner.SetInputConnection(centreliner.GetOutputPort())
        
        writer = vtk.vtkXMLPolyDataWriter()
        writer.SetInputConnection(cleaner.GetOutputPort())
        writer.SetFileName(self.CentreLineFile)
        writer.Write()
        return cleaner.GetOutput()

    @memo_property
    def CentreLinePointLocator(self):
        """A vtkAbstractPointLocator subclass that allows fast
        searching for points on the centreline.
        """
        clPtLoc = vtk.vtkOctreePointLocator()
        clPtLoc.SetDataSet(self.CentreLinePolyData)
        clPtLoc.BuildLocator()
        return clPtLoc

    @memo_property
    def CentreLinePoints(self):
        return vtk_to_numpy(self.CentreLinePolyData.GetPoints().GetData()) * self.LengthUnit

    @memo_property
    def CentreLineRadii(self):
        return vtk_to_numpy(self.CentreLinePolyData.GetPointData().GetArray('radius')) * self.LengthUnit

    @memo_property
    def VolumeEstimate(self):
        """Very rough estimate of the volume.  Assumes that the domain
        is made up of conical frustra corresponding to the line
        segments of the centrelines. Ignores any volume outside this
        (e.g. aneurysm sacs) and the overlap between successive
        segments at curves and bifurcations.
        """
        clPD = self.CentreLinePolyData
        nPts = clPD.GetNumberOfPoints()

        # List of the point IDs that have been done already.
        
        # Use this rather than the segments of the vessel tree as this
        # can cope with a network graph that has mergers as well as
        # bifurcations.
        seenPts = SortedVector(capacity=nPts, dtype=int)

        # Convert to numpy for easier access
        points = self.CentreLinePoints
        r = self.CentreLineRadii
                
        ans = 0.0 * pq.metre**3
        # Get the IDs for each path through the network
        for linePtIds in IterCellPtIds(clPD.GetLines()):
            # Iterate over adjacent IDs, each of which defines a segment.
            for i, j in IterPairs(linePtIds):
                if j in seenPts:
                    # Consider a segment done if the downstream end
                    # has been see before.
                    continue

                # Length of the segment
                delta_s = (np.sum((points[i] - points[j])**2))**0.5
                # s = distance along centreline
                # 
                #   r_i_____
                #    |      -------_____r_j
                #    |                   |
                #    |                   |
                # --s_i-----------------s_j-----> s Centreline
                # 
                #  V = \int_{s_i}^{s_j} \pi r^2 ds
                # 
                #  r(s) = (r_j - r_i) * (s - s_i) + r_i
                #         -----------
                #         (s_j - s_i)
                # 
                # so dr = (r_j - r_i) * ds
                #         -----------
                #         (s_j - s_i)
                # 
                # V = \pi (s_j - s_i) * \int_{r_i}^{r_j} r^2 dr
                #         -----------
                #         (r_j - r_i)
                #   = \pi (s_j - s_i) * (r_j^3 - r_i^3)
                #         -----------
                #         (r_j - r_i)
                delta_V = (r[i]**2 + r[i]*r[j] + r[j]**2) * np.pi * delta_s / 3.

                ans += delta_V
                # Mark the point as seen.
                seenPts.add(j)
        return ans
    
    def _AdjustIoletNormals(self):
        """Adjust the iolet normals such that they are parallel to the
        centre line at their location.
        """
        if hasattr(self, "_Adjusted") and self._Adjusted:
            return
        
        # Point IDs of them all
        closestIds = [iolet.ClosestPointId for iolet in self.Iolets]
        
        clinesPD = self.CentreLinePolyData
        points = vtk_to_numpy(clinesPD.GetPoints().GetData())

        # Going to iterate over all the paths through the system and
        # see which iolets have their closest point along each path.
        # We need to keep of which have been done to avoid repeating
        # ourselves. Then at each point we track back and forwards to
        # neighbouring points to get a better estimate of the local
        # normal. (Hence why we need the connectivity data of the
        # lines)
        adjustedPtIds = set()
        
        for linePtIds in IterCellPtIds(clinesPD.GetLines()):
            nPtsOnLine = len(linePtIds)
            # Find which iolets are closest to points on this line.
            for i, closest in enumerate(closestIds):
                match = np.where(linePtIds == closest)[0]
                if len(match) == 0:
                    # Not this iolet
                    continue
                
                # This iolet is closest to this line.
                iolet = self.Iolets[i]
                closestPointOnLine = match[0]
                
                if linePtIds[closestPointOnLine] in adjustedPtIds:
                    # But we've seen this one - next!
                    continue
                
                # Haven't seen this one, add it so don't repeat ourselves
                adjustedPtIds.add(linePtIds[closestPointOnLine])

                # Find the points connected to it on either side,
                # first as an index in this cell. (Note: make sure we
                # don't go outside the array bounds.)
                lo = max(closestPointOnLine - 1, 0)
                hi = min(closestPointOnLine + 1, nPtsOnLine - 1)
                # Then map the index-of-point-on-the-line to the point IDs
                lo = linePtIds[lo]
                hi = linePtIds[hi]
                # Now compute the normal along the line
                rLo = points[lo]
                rHi = points[hi]
                n = rHi - rLo
                n /= np.linalg.norm(n)
                # If it's the wrong way round, flip it.
                if np.dot(n, iolet.Normal) < 0:
                    n *= -1.
                    pass

                # Set in the iolet object.
                iolet.Normal = n
                # iolet.Normal.x = n[0]
                # iolet.Normal.y = n[1]
                # iolet.Normal.z = n[2]
                continue
            continue
        self._Adjusted = True
        return
        
    @property
    def InletRadius(self):
        return self.Inlet.ActualRadius
    
    @property
    @simplify
    def InletPeakVelocity(self):
        return self.KinematicViscosity * self.InletReynoldsNumber / (2. * self.InletRadius)

    @property
    @simplify
    def InletFlowRate(self):
        """In m^3 / s

        Using standard poiseuille flow results and Re = rho * U_max * dia / visc
        """
        return np.pi * self.InletReynoldsNumber * self.KinematicViscosity * self.InletRadius / 4.0
    
    @property
    def OutletPressure(self):
        return 0. * pq.mmHg

    @simplify
    def ResistanceForIds(self, idList):
        return SpecificResistance(self.CentreLinePoints[idList], self.CentreLineRadii[idList]) * self.DynamicViscosity
            
    def IndexForUnknown(self, unkn):
        return self.Unknowns.index(unkn)

    @memo_property
    def Unknowns(self):
        tmp = self.Tree
        return Unknowns

    @memo_property
    def EquationSystem(self):
        eqs = self.Tree.BuildSystem(self)
        assert len(eqs) == len(self.Unknowns)
        return eqs
    
    @memo_property
    def EquationSystemMatrix(self):
        # This needs to have uniform type, i.e. no quantities
        # Use SI base for everything.
        nUnknowns = len(self.Unknowns)
        eqs = self.EquationSystem
        ans = np.zeros((nUnknowns, nUnknowns+1))
        
        with self.SIunits():
            for i in xrange(nUnknowns):
                for j in xrange(nUnknowns):
                    ans[i,j] = self.ScaleForOutput(eqs[i].get(self.Unknowns[j], 0.))
                    continue
                ans[i, nUnknowns] = eqs[i]['rhs']
                continue
            pass
        return ans

    @memo_property
    def Matrix(self):
        return self.EquationSystemMatrix[:,:len(self.Unknowns)]
    @memo_property
    def Rhs(self):
        return self.EquationSystemMatrix[:, len(self.Unknowns)]
    
    @memo_property
    def Tree(self):
        # Before constructing the tree, must trim away the bits of
        # centreline beyond the iolets.
        inletPtId = self.Inlet.ClosestPointId
        outletPtIds = [out.ClosestPointId for out in self.Outlets]
        
        linePtIds = []
        for pathIdList in IterCellPtIds(self.CentreLinePolyData.GetLines()):
            # Find where the inlet is
            inletIndex = np.where(pathIdList == inletPtId)[0]
            assert inletIndex.shape == (1,)
            inletIndex = inletIndex[0]
            
            # Now the outlet
            # First figure out which outlet
            outletId = np.where(np.in1d(outletPtIds, pathIdList))[0]
            # Must be only one outlet.
            assert outletId.shape == (1,)            
            # Now where is this outlet's point ID in pathIdList?
            outletIndex = np.where(pathIdList == outletPtIds[outletId])[0]
            assert outletIndex.shape == (1,)
            outletIndex = outletIndex[0]
            
            linePtIds.append(pathIdList[inletIndex:outletIndex])
            continue

        # Now can split into the tree
        tree = VesselSegment(linePtIds)
        
        # To fully initialise the tree segments, need to BuildTerms
        self.Unknowns = tree.BuildTerms(flowRate=self.InletFlowRate, finalOutletPressure=self.OutletPressure)
        return tree
        
    @memo_property
    def SolutionVector(self):
        return np.linalg.solve(self.Matrix, self.Rhs)

    @simplify
    def SolutionForUnknown(self, unkn):
        i = self.IndexForUnknown(unkn)
        return self.SolutionVector[i] * unkn.units
    
    @memo_property
    def MaxVelocity(self):
        return self.Tree.MaxVelocity(self)

    @default_property
    @simplify
    def TimeStep(self):
        """MaxAllowedMachNumber = MaxVelocity / SpeedOfSound

        SpeedOfSound = VoxelSize / (sqrt(3) TimeStep)
        """
        return self.MaxAllowedMachNumber * self.VoxelSize / (np.sqrt(3) * self.MaxVelocity)

    @default_property
    def VoxelSize(self):
        return self.Tree.MinRadius(self) / self.MinRadiusVoxels
    
    @property
    def PulsatilePeriod(self):
        return 1.0 * pq.second
    
    @property
    def SimulationTime(self):
        return 10 * self.PulsatilePeriod

    @memo_property
    def MaxPathLength(self):
        return self.Tree.MaxLength(self)

    @property
    def MachNumber(self):
        return self.MaxVelocity / self.SpeedOfSound
    
    @property
    def WomersleyNumber(self):
        omega = 2.0 * np.pi / self.PulsatilePeriod
        return self.InletRadius * (omega / self.KinematicViscosity)**0.5
    
    @property
    @simplify
    def SoundTime(self):
        return self.MaxPathLength / self.SpeedOfSound
    
    @property
    @simplify
    def MomentumTime(self):
        return self.CentreLineRadii.max()**2 / self.KinematicViscosity

    @property
    @simplify
    def InletPressure(self):
        return self.SolutionForUnknown(self.Tree.InletPressure)

    @property
    def PressureDifference(self):
        return self.InletPressure - self.OutletPressure
    
    @property
    def TotalSups(self):
        return self.ScaleToLatticeUnits(self.VolumeEstimate) * self.ScaleToLatticeUnits(self.SimulationTime)

    @property
    def HectorTime(self):
        ans = pq.second * (self.TotalSups / 1e6)
        ans.units = pq.hour
        return ans

    def _UpdateProfile(self):
        self._AdjustIoletNormals()
        
        self._profile.TimeStepSeconds = self.ScaleForOutput(self.TimeStep)
        self._profile.DurationSeconds = self.ScaleForOutput(self.SimulationTime)
        
        self._profile.VoxelSize = float(self.VoxelSize / self.LengthUnit)
        return
    
    def RewriteProfile(self):
        self._UpdateProfile()
        
        base, ext = os.path.splitext(self.FileName)
        newFile = base + '_adjusted' + ext
        self.Save(newFile)
        return

    def Generate(self):
        self.RewriteProfile()
        self._profile.Generate()
        self.RewriteConfigXml()
        return
    
    def RewriteConfigXml(self):
        from HemeLbSetupTool.Model.XmlWriter import XmlWriter
        
        tree = ElementTree.parse(self.OutputXmlFile)
        root = tree.getroot()
        
        self.RewriteProperties(root)
        self.RewriteInletCondition(root)
        self.RewriteOutletConditions(root)

        XmlWriter.indent(root)
        
        tree.write(self.OutputXmlFile)
        return
    
    def RewriteInletCondition(self, root):
        condEl = root.find('inlets/inlet/condition')
        condEl.clear()
        
        condEl.set('type', 'pressure')
        condEl.set('subtype', 'cosine')
        
        inP = self.SolutionForUnknown(self.Tree.InletPressure)
        self.QuantityToXml(condEl, 'amplitude', inP)
        self.QuantityToXml(condEl, 'mean', inP)
        self.QuantityToXml(condEl, 'phase', np.pi * pq.radian)
        self.QuantityToXml(condEl, 'period', self.PulsatilePeriod)
        
        return

    def RewriteOutletConditions(self, root):
        for condEl in root.findall('outlets/outlet/condition'):
            condEl.clear()
            
            condEl.set('type', 'pressure')
            condEl.set('subtype', 'cosine')
            
            self.QuantityToXml(condEl, 'amplitude', self.OutletPressure)
            self.QuantityToXml(condEl, 'mean', self.OutletPressure)
            self.QuantityToXml(condEl, 'phase', 0.0 * pq.radian)
            self.QuantityToXml(condEl, 'period', self.PulsatilePeriod)
            continue
        return
    
    def RewriteProperties(self, root):
        # Remove any old ones
        propertiesEl = root.find('properties')
        while propertiesEl is not None:
            root.remove(propertiesEl)
            propertiesEl = root.find('properties')
            continue
        
        propertiesEl = ElementTree.SubElement(root, 'properties')

        # Get our plane centres
        planePtIds = self.Tree.SamplePtIds(50)
        nPoints = len(planePtIds)
        points = self.CentreLinePoints[planePtIds]
        # And the next point along for the normals
        nextPtIds = self.Tree.SamplePtIds(50, start=1)
        dx = self.CentreLinePoints[nextPtIds] - points
        normals = dx / np.sum(dx**2,axis=-1)[:, np.newaxis]**0.5
        # And the radius at that point, plus  a bit
        radii = self.CentreLineRadii[planePtIds]*1.1

        nDigits = int(np.ceil(np.log10(nPoints)))
        fileFmtStr = 'plane{:0%dd}.xtr' % nDigits
        periodStr = str(int(self.ScaleToLatticeUnits(self.PulsatilePeriod/25.)))
        for i in xrange(len(points)):
            poEl = ElementTree.SubElement(propertiesEl, 'propertyoutput',
                                          file=fileFmtStr.format(i),
                                          period=periodStr)
            geoEl = ElementTree.SubElement(poEl, 'geometry', type='plane')
            self.QuantityToXml(geoEl, 'point', points[i])
            self.QuantityToXml(geoEl, 'normal', normals[i])
            self.QuantityToXml(geoEl, 'radius', radii[i])

            ElementTree.SubElement(poEl, 'field', type='velocity')
            ElementTree.SubElement(poEl, 'field', type='pressure')
            
            continue
        return
    
    def QuantityToXml(self, parent, name, quantity):
        value = self.ScaleForOutput(quantity)
        if isinstance(value, np.ndarray):
            assert value.shape == (3,)
            value = '({},{},{})'.format(*value)
        else:
            value = str(value)
            pass

        try:
            units = self._OutputUnitsMap[quantity.dimensionality.simplified].symbol
        except KeyError:
            units = quantity.dimensionality.string
            pass
        
        return ElementTree.SubElement(parent, name, value=value, units=units)
    
    pass