Example #1
0
    def get_parameter(self, key, try_json=True, parameters=None):
        if parameters is None:
            parameters = self._parameters

        if key not in parameters:
            return None

        parameter, typ = parameters[key]

        return convert_parameter(parameter, typ, try_json)
Example #2
0
    def get_parameter(self, key, try_json=True, parameters=None):
        if parameters is None:
            parameters = self._parameters

        if key not in parameters:
            return None

        parameter, typ = parameters[key]

        return convert_parameter(parameter, typ, try_json)
Example #3
0
    def to_mesh_xml(self):
        root = lxml.etree.Element('gssf')
        root.set('name', 'elmer_libnuma')
        root.set('version', '1.0.2')

        # Start by creating a geometry section
        geometry = lxml.etree.Element('geometry')
        root.append(geometry)

        # We get the location of the simulation centre from the parameters
        centre_location = self.get_parameter("CENTRE_LOCATION")

        # If it isn't there, then the centre will be the first needle tip, or
        # if we have been given "centroid-of-tips" instead of a set of
        # coordinates, calculate the centroid of all the tips
        if centre_location is None or centre_location == "first-needle":
            if self._needles:
                centre_location = self.get_needle_parameter(0, "NEEDLE_TIP_LOCATION")
        elif centre_location == "centroid-of-tips":
            if self._needles:
                needle_tips = [self.get_needle_parameter(i, "NEEDLE_TIP_LOCATION") for i in range(len(self._needles))]
                needle_tips = zip(*needle_tips)
                centre_location = [sum(tips) / len(self._needles) for tips in needle_tips]

        # If we have a needle, then use it to set the `needleaxis`
        if self._needles:
            needle_axis_node = lxml.etree.Element('needleaxis')

            # Get the entry and tip of the first needle
            tip_location = self.get_needle_parameter(0, "NEEDLE_TIP_LOCATION")
            entry_location = self.get_needle_parameter(0, "NEEDLE_ENTRY_LOCATION")
            norm = 0
            vec = []
            for c, vt, ve in zip(('x', 'y', 'z'), tip_location, entry_location):
                needle_axis_node.set(c, str(ve - vt))
                vec.append(ve - vt)
                norm += (ve - vt) * (ve - vt)
            # Based on the calculated axis, add this to the geometry
            geometry.append(needle_axis_node)

            # FIXME: is this supposed to be inside this if??
            # Use the CENTRE_OFFSET parameter to shift the geometry if required
            offset = self.get_parameter("CENTRE_OFFSET")
            if offset is not None:
                for c, v in enumerate(centre_location):
                    centre_location[c] = v + offset * vec[c] / math.sqrt(norm)

        # After all the calculations above, use the finally chosen centre in the
        # geometry section
        centre_location_node = lxml.etree.Element("centre")
        for c, v in zip(('x', 'y', 'z'), centre_location):
            centre_location_node.set(c, str(v))
        geometry.append(centre_location_node)

        # If we have a simulation scaling parameter, that goes into the geometry
        # section also
        if self.get_parameter("SIMULATION_SCALING") is not None:
            lxml.etree.SubElement(geometry, "simulationscaling") \
                .set("ratio",
                     str(self.get_parameter("SIMULATION_SCALING")))

        # Each region goes into the regions section, fairly intuitively
        regions = lxml.etree.SubElement(root, "regions")
        for name, region in self._regions.items():
            regionNode = lxml.etree.SubElement(regions, region["format"])
            regionNode.set("name", name)
            regionNode.set("input", os.path.join("input/", region["input"]))
            regionNode.set("groups", "; ".join(region["groups"]))

        # Add the parameters wholesale
        parameters = lxml.etree.SubElement(root, "constants")
        for key, parameterPair in self._parameters.items():
            parameter, typ = parameterPair
            parameterNode = lxml.etree.SubElement(parameters, "parameter")
            parameterNode.set("name", key)
            p = convert_parameter(parameter, typ)
            parameterNode.set("value", json.dumps(p))
            if typ is not None:
                parameterNode.set("type", typ)

        name_needle_regions = False

        # The needlelibrary needs to know if we have solid needles
        needlelibrary = lxml.etree.SubElement(root, 'needlelibrary')
        solid_needles = self.get_parameter("SETTING_SOLID_NEEDLES")
        if solid_needles is not None:
            needlelibrary.set("zones", "true" if solid_needles is True else "false")
            name_needle_regions = True

        # The outer mesh, as far as we are concerned, is always CGAL
        mesher = lxml.etree.SubElement(root, "mesher")
        mesher.set('type', 'CGAL')
        # RMV: should this be reinserted?
        # if self.get_parameter("SETTING_SOLID_NEEDLES") is True or self.get_parameter("SETTING_ZONE_BOUNDARIES") is True:
        mesher.set("zone_boundaries", "true")

        # If we have an inner mesh, add it to the mesher
        mesher_inner = self.get_parameter("SETTING_AXISYMMETRIC_INNER")
        if mesher_inner is not None:
            innerNode = lxml.etree.SubElement(mesher, "inner")
            innerNode.set("type", "axisymmetric")
            innerNode.set("template", mesher_inner)

        # Coarse inner, similarly
        mesher_inner_coarse = self.get_parameter("SETTING_AXISYMMETRIC_INNER_COARSE")
        if mesher_inner_coarse is not None:
            innerNode = lxml.etree.SubElement(mesher, "inner")
            innerNode.set("type", "axisymmetric")
            innerNode.set("name", "coarse")
            innerNode.set("template", mesher_inner_coarse)

        # The extent we assume is a sphere of radius in parameters
        extent = lxml.etree.SubElement(mesher, 'extent')
        radius = self.get_parameter("SIMULATION_DOMAIN_RADIUS")
        if radius is not None:
            extent.set('radius', str(radius))
        else:
            extent.set('radius', '50')

        # Adding the empty centre element tells the mesher we want a denser
        # centre than the boundaries
        lxml.etree.SubElement(mesher, 'centre')

        # Start going through the length scales
        lengthscales = lxml.etree.SubElement(mesher, 'lengthscales')

        # Two sets of fairly sensible defaults for the usual meshing case
        if self.get_parameter('RESOLUTION_HIGH'):
            lengthscale_settings = [
                ('nearfield', '1.0'), ('farfield', '2.0'), ('zonefield', '1.0'),
                ('vessels', 'far')
            ]
        else:
            lengthscale_settings = [
                ('nearfield', '2.0'), ('farfield', '5.0'), ('zonefield', '2.0'),
                ('vessels', 'far')
            ]

        # Allow them to be overridden
        nearfield = self.get_parameter('RESOLUTION_FIELD_NEAR')
        needlezonefield = self.get_parameter('RESOLUTION_FIELD_NEEDLE_ZONE')
        if not needlezonefield:
            needlezonefield = self.get_parameter('RESOLUTION_NEEDLE_ZONE_FIELD')
        farfield = self.get_parameter('RESOLUTION_FIELD_FAR')
        zonefield = self.get_parameter('RESOLUTION_FIELD_ZONE')

        if nearfield:
            lengthscale_settings[0] = ('nearfield', nearfield)
        if farfield:
            lengthscale_settings[1] = ('farfield', farfield)
        if zonefield:
            lengthscale_settings[2] = ('zonefield', zonefield)

        for k, v in lengthscale_settings:
            lengthscales.set(k, str(v))
        if needlezonefield:
            lengthscales.set("needlezonefield", str(needlezonefield))

        farfield = str(lengthscale_settings[1][1])
        zonefield = str(lengthscale_settings[2][1])

        # Each region may need to be added to the mesher section
        for idx, region in self._regions.items():
            # If we have an organ, it should appear as a zone or organ
            if region['meaning'] == 'organ':
                if self.get_parameter('SETTING_ORGAN_AS_SUBDOMAIN'):
                    zone = lxml.etree.SubElement(mesher, 'zone')
                    zone.set('region', idx)
                    zone.set('priority', '100')
                    zone.set('characteristic_length', farfield)
                else:
                    lxml.etree.SubElement(mesher, 'organ').set('region', idx)
            # If we have a zone, not excluded from meshing, then it goes in too
            elif region['format'] == 'zone' and not (set(region['groups']) & self._nonmeshing_groups):
                zone = lxml.etree.SubElement(mesher, 'zone')
                zone.set('region', idx)
                zone.set('priority', '1')
                zone.set('characteristic_length', zonefield)
            # If we have vessels, they get added also
            elif 'vessels' in region['groups'] or 'bronchi' in region['groups']:
                # FIXME: surely this should be a surface/vessel tag?
                zone = lxml.etree.SubElement(mesher, 'zone')
                zone.set('region', idx)
                zone.set('priority', '2')
                zone.set('characteristic_length', zonefield)

        # These are standard entries
        lxml.etree.SubElement(root, 'optimizer')

        # The register of needles must be filled in
        globalNeedlesNode = lxml.etree.SubElement(root, "needles")

        if not needlezonefield:
            needlezonefield = zonefield

        # Make sure all needles are int-castable, or, if not,
        # just make up our own ordering
        try:
            needle_indices = [int(ix.replace('needle', '')) for ix in self._needles]
        except ValueError:
            self._needles  = {str(i + 1): v for i, v in enumerate(self._needles.values())}
            needle_indices = [int(ix) for ix in self._needles]
        augment = (0 in needle_indices)

        for ix, needle in self._needles.items():
            # Add a needle node and set the name to be our index (if we have
            # been given, say, 'needle-3' as an index, it becomes '3')
            globalNeedleNode = lxml.etree.SubElement(globalNeedlesNode, "needle")
            l = int(ix.replace('needle', ''))
            if augment:
                l += 1
            globalNeedleNode.set("name", str(l))

            # If this needle is a boundary type (the only type for the
            # moment)...
            if needle['class'] in ('solid-boundary', 'boundary'):
                # The 'file' attribute in GSSA should be a colon-separated pair
                # indicating what type of definition it is and the specifics
                # required
                location = needle['file'].split(':', 1)

                needle_mesh = None
                # If we aren't using a library type, then we need to get the
                # region
                if location[0] in ('surface', 'zone', 'both'):
                    needleNode = lxml.etree.SubElement(regions, location[0])
                    needleNode.set("name", str(l))
                    needleNode.set("input", os.path.join("input/", location[1]))
                    needleNode.set("groups", "needles")

                    # TODO: surely this might be a surface?
                    needle_mesh = lxml.etree.SubElement(mesher, 'zone')
                    needle_mesh.set('region', str(l))
                    needle_mesh.set('characteristic_length', str(needlezonefield))
                    needle_mesh.set('priority', '0')
                else:
                    needleNode = lxml.etree.SubElement(needlelibrary, 'needle')

                    if name_needle_regions:
                        needleNode.set("name", str(l))

                    # If this is a library type, set its ID
                    if location[0] == 'library':
                        needleNode.set("id", location[1])
                    needleNode.set("name", str(l))

                    # Calculate the offset and axis for this needle
                    tip_location = self.get_needle_parameter(ix, "NEEDLE_TIP_LOCATION")
                    entry_location = self.get_needle_parameter(ix, "NEEDLE_ENTRY_LOCATION")
                    needleNode.set("offset", " ".join(map(lambda c: str(c[0] - c[1]), zip(tip_location, centre_location))))
                    needleNode.set("axis", " ".join(map(lambda c: str(c[0] - c[1]), zip(entry_location, tip_location))))

                    # Add any needle-specific parameters
                    parameters = lxml.etree.SubElement(globalNeedleNode, "parameters")
                    for key, parameterPair in needle["parameters"].items():
                        parameter, typ = parameterPair
                        parameterNode = lxml.etree.SubElement(parameters, "constant")
                        parameterNode.set("name", key)
                        parameterNode.set("value", str(convert_parameter(parameter, typ)))

                # Set active region if needs be
                needle_active_length = self.get_needle_parameter(ix, "NEEDLE_ACTIVE_LENGTH")
                global_active_length = self.get_needle_parameter(ix, "CONSTANT_GLOBAL_ACTIVE_LENGTH")
                if needle_active_length is None:
                    needle_active_length = global_active_length
                if needle_active_length is not None:
                    if needle_mesh is None:
                        needle_mesh = lxml.etree.SubElement(mesher, 'zone')
                        needle_mesh.set('characteristic_length', str(needlezonefield))
                        needle_mesh.set('priority', '0')
                        needle_mesh.set('region', 'needle-' + str(l))
                    activity = lxml.etree.SubElement(needle_mesh, 'activity')
                    tip_location = self.get_needle_parameter(ix, "NEEDLE_TIP_LOCATION")
                    for c, vt, vc in zip(('x', 'y', 'z'), tip_location, centre_location):
                        activity.set(c, str(vt - vc))
                    activity.set('r', str(needle_active_length))

        self._mesher_xml = root

        return self._mesher_xml
Example #4
0
    def to_mesh_xml(self):
        root = lxml.etree.Element('gssf')
        root.set('name', 'elmer_libnuma')
        root.set('version', '1.0.2')

        # Start by creating a geometry section
        geometry = lxml.etree.Element('geometry')
        root.append(geometry)

        # We get the location of the simulation centre from the parameters
        centre_location = self.get_parameter("CENTRE_LOCATION")

        # If it isn't there, then the centre will be the first needle tip, or
        # if we have been given "centroid-of-tips" instead of a set of
        # coordinates, calculate the centroid of all the tips
        if centre_location is None or centre_location == "first-needle":
            if self._needles:
                centre_location = self.get_needle_parameter(
                    0, "NEEDLE_TIP_LOCATION")
        elif centre_location == "centroid-of-tips":
            if self._needles:
                needle_tips = [
                    self.get_needle_parameter(i, "NEEDLE_TIP_LOCATION")
                    for i in range(len(self._needles))
                ]
                needle_tips = zip(*needle_tips)
                centre_location = [
                    sum(tips) / len(self._needles) for tips in needle_tips
                ]

        # If we have a needle, then use it to set the `needleaxis`
        if self._needles:
            needle_axis_node = lxml.etree.Element('needleaxis')

            # Get the entry and tip of the first needle
            tip_location = self.get_needle_parameter(0, "NEEDLE_TIP_LOCATION")
            entry_location = self.get_needle_parameter(
                0, "NEEDLE_ENTRY_LOCATION")
            norm = 0
            vec = []
            for c, vt, ve in zip(('x', 'y', 'z'), tip_location,
                                 entry_location):
                needle_axis_node.set(c, str(ve - vt))
                vec.append(ve - vt)
                norm += (ve - vt) * (ve - vt)
            # Based on the calculated axis, add this to the geometry
            geometry.append(needle_axis_node)

            # FIXME: is this supposed to be inside this if??
            # Use the CENTRE_OFFSET parameter to shift the geometry if required
            offset = self.get_parameter("CENTRE_OFFSET")
            if offset is not None:
                for c, v in enumerate(centre_location):
                    centre_location[c] = v + offset * vec[c] / math.sqrt(norm)

        # After all the calculations above, use the finally chosen centre in the
        # geometry section
        centre_location_node = lxml.etree.Element("centre")
        for c, v in zip(('x', 'y', 'z'), centre_location):
            centre_location_node.set(c, str(v))
        geometry.append(centre_location_node)

        # If we have a simulation scaling parameter, that goes into the geometry
        # section also
        if self.get_parameter("SIMULATION_SCALING") is not None:
            lxml.etree.SubElement(geometry, "simulationscaling") \
                .set("ratio",
                     str(self.get_parameter("SIMULATION_SCALING")))

        # Each region goes into the regions section, fairly intuitively
        regions = lxml.etree.SubElement(root, "regions")
        for name, region in self._regions.items():
            regionNode = lxml.etree.SubElement(regions, region["format"])
            regionNode.set("name", name)
            regionNode.set("input", os.path.join("input/", region["input"]))
            regionNode.set("groups", "; ".join(region["groups"]))

        # Add the parameters wholesale
        parameters = lxml.etree.SubElement(root, "constants")
        for key, parameterPair in self._parameters.items():
            parameter, typ = parameterPair
            parameterNode = lxml.etree.SubElement(parameters, "parameter")
            parameterNode.set("name", key)
            p = convert_parameter(parameter, typ)
            parameterNode.set("value", json.dumps(p))
            if typ is not None:
                parameterNode.set("type", typ)

        name_needle_regions = False

        # The needlelibrary needs to know if we have solid needles
        needlelibrary = lxml.etree.SubElement(root, 'needlelibrary')
        solid_needles = self.get_parameter("SETTING_SOLID_NEEDLES")
        if solid_needles is not None:
            needlelibrary.set("zones",
                              "true" if solid_needles is True else "false")
            name_needle_regions = True

        # The outer mesh, as far as we are concerned, is always CGAL
        mesher = lxml.etree.SubElement(root, "mesher")
        mesher.set('type', 'CGAL')
        # RMV: should this be reinserted?
        # if self.get_parameter("SETTING_SOLID_NEEDLES") is True or self.get_parameter("SETTING_ZONE_BOUNDARIES") is True:
        mesher.set("zone_boundaries", "true")

        # If we have an inner mesh, add it to the mesher
        mesher_inner = self.get_parameter("SETTING_AXISYMMETRIC_INNER")
        if mesher_inner is not None:
            innerNode = lxml.etree.SubElement(mesher, "inner")
            innerNode.set("type", "axisymmetric")
            innerNode.set("template", mesher_inner)

        # Coarse inner, similarly
        mesher_inner_coarse = self.get_parameter(
            "SETTING_AXISYMMETRIC_INNER_COARSE")
        if mesher_inner_coarse is not None:
            innerNode = lxml.etree.SubElement(mesher, "inner")
            innerNode.set("type", "axisymmetric")
            innerNode.set("name", "coarse")
            innerNode.set("template", mesher_inner_coarse)

        # The extent we assume is a sphere of radius in parameters
        extent = lxml.etree.SubElement(mesher, 'extent')
        radius = self.get_parameter("SIMULATION_DOMAIN_RADIUS")
        if radius is not None:
            extent.set('radius', str(radius))
        else:
            extent.set('radius', '50')

        # Adding the empty centre element tells the mesher we want a denser
        # centre than the boundaries
        lxml.etree.SubElement(mesher, 'centre')

        # Start going through the length scales
        lengthscales = lxml.etree.SubElement(mesher, 'lengthscales')

        # Two sets of fairly sensible defaults for the usual meshing case
        if self.get_parameter('RESOLUTION_HIGH'):
            lengthscale_settings = [('nearfield', '1.0'), ('farfield', '2.0'),
                                    ('zonefield', '1.0'), ('vessels', 'far')]
        else:
            lengthscale_settings = [('nearfield', '2.0'), ('farfield', '5.0'),
                                    ('zonefield', '2.0'), ('vessels', 'far')]

        # Allow them to be overridden
        nearfield = self.get_parameter('RESOLUTION_FIELD_NEAR')
        needlezonefield = self.get_parameter('RESOLUTION_FIELD_NEEDLE_ZONE')
        if not needlezonefield:
            needlezonefield = self.get_parameter(
                'RESOLUTION_NEEDLE_ZONE_FIELD')
        farfield = self.get_parameter('RESOLUTION_FIELD_FAR')
        zonefield = self.get_parameter('RESOLUTION_FIELD_ZONE')

        if nearfield:
            lengthscale_settings[0] = ('nearfield', nearfield)
        if farfield:
            lengthscale_settings[1] = ('farfield', farfield)
        if zonefield:
            lengthscale_settings[2] = ('zonefield', zonefield)

        for k, v in lengthscale_settings:
            lengthscales.set(k, str(v))
        if needlezonefield:
            lengthscales.set("needlezonefield", str(needlezonefield))

        farfield = str(lengthscale_settings[1][1])
        zonefield = str(lengthscale_settings[2][1])

        # Each region may need to be added to the mesher section
        for idx, region in self._regions.items():
            # If we have an organ, it should appear as a zone or organ
            if region['meaning'] == 'organ':
                if self.get_parameter('SETTING_ORGAN_AS_SUBDOMAIN'):
                    zone = lxml.etree.SubElement(mesher, 'zone')
                    zone.set('region', idx)
                    zone.set('priority', '100')
                    zone.set('characteristic_length', farfield)
                else:
                    lxml.etree.SubElement(mesher, 'organ').set('region', idx)
            # If we have a zone, not excluded from meshing, then it goes in too
            elif region['format'] == 'zone' and not (
                    set(region['groups']) & self._nonmeshing_groups):
                zone = lxml.etree.SubElement(mesher, 'zone')
                zone.set('region', idx)
                zone.set('priority', '1')
                zone.set('characteristic_length', zonefield)
            # If we have vessels, they get added also
            elif 'vessels' in region['groups'] or 'bronchi' in region['groups']:
                # FIXME: surely this should be a surface/vessel tag?
                zone = lxml.etree.SubElement(mesher, 'zone')
                zone.set('region', idx)
                zone.set('priority', '2')
                zone.set('characteristic_length', zonefield)

        # These are standard entries
        lxml.etree.SubElement(root, 'optimizer')

        # The register of needles must be filled in
        globalNeedlesNode = lxml.etree.SubElement(root, "needles")

        if not needlezonefield:
            needlezonefield = zonefield

        # Make sure all needles are int-castable, or, if not,
        # just make up our own ordering
        try:
            needle_indices = [
                int(ix.replace('needle', '')) for ix in self._needles
            ]
        except ValueError:
            self._needles = {
                str(i + 1): v
                for i, v in enumerate(self._needles.values())
            }
            needle_indices = [int(ix) for ix in self._needles]
        augment = (0 in needle_indices)

        for ix, needle in self._needles.items():
            # Add a needle node and set the name to be our index (if we have
            # been given, say, 'needle-3' as an index, it becomes '3')
            globalNeedleNode = lxml.etree.SubElement(globalNeedlesNode,
                                                     "needle")
            l = int(ix.replace('needle', ''))
            if augment:
                l += 1
            globalNeedleNode.set("name", str(l))

            # If this needle is a boundary type (the only type for the
            # moment)...
            if needle['class'] in ('solid-boundary', 'boundary'):
                # The 'file' attribute in GSSA should be a colon-separated pair
                # indicating what type of definition it is and the specifics
                # required
                location = needle['input'].split(':', 1)

                needle_mesh = None
                # If we aren't using a library type, then we need to get the
                # region
                if location[0] in ('surface', 'zone', 'both'):
                    needleNode = lxml.etree.SubElement(regions, location[0])
                    needleNode.set("name", str(l))
                    needleNode.set("input",
                                   os.path.join("input/", location[1]))
                    needleNode.set("groups", "needles")

                    # TODO: surely this might be a surface?
                    needle_mesh = lxml.etree.SubElement(mesher, 'zone')
                    needle_mesh.set('region', str(l))
                    needle_mesh.set('characteristic_length',
                                    str(needlezonefield))
                    needle_mesh.set('priority', '0')
                else:
                    needleNode = lxml.etree.SubElement(needlelibrary, 'needle')

                    if name_needle_regions:
                        needleNode.set("name", str(l))

                    # If this is a library type, set its ID
                    if location[0] == 'library':
                        needleNode.set("id", location[1])
                    else:
                        needleNode.set("input", location[1])
                    needleNode.set("name", str(l))

                    # Calculate the offset and axis for this needle
                    tip_location = self.get_needle_parameter(
                        ix, "NEEDLE_TIP_LOCATION")
                    entry_location = self.get_needle_parameter(
                        ix, "NEEDLE_ENTRY_LOCATION")
                    needleNode.set(
                        "offset", " ".join(
                            map(lambda c: str(c[0] - c[1]),
                                zip(tip_location, centre_location))))
                    needleNode.set(
                        "axis", " ".join(
                            map(lambda c: str(c[0] - c[1]),
                                zip(entry_location, tip_location))))
                    needle_scaling = self.get_needle_parameter(
                        ix, "NEEDLE_SCALING")
                    if needle_scaling:
                        needleNode.set("scaling", str(needle_scaling))

                    # Add any needle-specific parameters
                    parameters = lxml.etree.SubElement(globalNeedleNode,
                                                       "parameters")
                    for key, parameterPair in needle["parameters"].items():
                        parameter, typ = parameterPair
                        parameterNode = lxml.etree.SubElement(
                            parameters, "constant")
                        parameterNode.set("name", key)
                        parameterNode.set(
                            "value", str(convert_parameter(parameter, typ)))

                # Set active region if needs be
                needle_active_length = self.get_needle_parameter(
                    ix, "NEEDLE_ACTIVE_LENGTH")
                global_active_length = self.get_needle_parameter(
                    ix, "CONSTANT_GLOBAL_ACTIVE_LENGTH")
                if needle_active_length is None:
                    needle_active_length = global_active_length
                if needle_active_length is not None:
                    if needle_mesh is None:
                        needle_mesh = lxml.etree.SubElement(mesher, 'zone')
                        needle_mesh.set('characteristic_length',
                                        str(needlezonefield))
                        needle_mesh.set('priority', '0')
                        needle_mesh.set('region', 'needle-' + str(l))
                    activity = lxml.etree.SubElement(needle_mesh, 'activity')
                    tip_location = self.get_needle_parameter(
                        ix, "NEEDLE_TIP_LOCATION")
                    for c, vt, vc in zip(('x', 'y', 'z'), tip_location,
                                         centre_location):
                        activity.set(c, str(vt - vc))
                    activity.set('r', str(needle_active_length))

        self._mesher_xml = root

        return self._mesher_xml