Exemple #1
0
def get_fault_source_nodes(source):
    """
    Returns list of nodes of attributes common to all fault source classes
    :param source:
        Fault source as instance of :class:
        openquake.hazardlib.source.simple_fault.SimpleFaultSource or :class:
        openquake.hazardlib.source.complex_fault.ComplexFaultSource
    :returns:
        List of instances of :class: openquake.commonlib.node.Node
    """
    source_nodes = []
    #  parse msr
    source_nodes.append(
        LiteralNode(
            "magScaleRel",
            text=source.magnitude_scaling_relationship.__class__.__name__))
    # Parse aspect ratio
    source_nodes.append(
        LiteralNode("ruptAspectRatio", text=source.rupture_aspect_ratio))
    # Parse MFD
    source_nodes.append(obj_to_node(source.mfd))
    # Parse Rake
    source_nodes.append(LiteralNode("rake", text=source.rake))
    if len(getattr(source, 'hypo_list', [])):
        source_nodes.append(build_hypo_list_node(source.hypo_list))
    if len(getattr(source, 'slip_list', [])):
        source_nodes.append(build_slip_list_node(source.slip_list))

    return source_nodes
Exemple #2
0
def get_distributed_seismicity_source_nodes(source):
    """
    Returns list of nodes of attributes common to all distributed seismicity
    source classes
    :param source:
        Seismic source as instance of :class:
        openquake.hazardlib.source.area.AreaSource or :class:
        openquake.hazardlib.source.point.PointSource
    :returns:
        List of instances of :class: openquake.commonlib.node.Node
    """
    source_nodes = []
    #  parse msr
    source_nodes.append(
        LiteralNode("magScaleRel", text=
                    source.magnitude_scaling_relationship.__class__.__name__))
    # Parse aspect ratio
    source_nodes.append(
        LiteralNode("ruptAspectRatio", text=source.rupture_aspect_ratio))
    # Parse MFD
    source_nodes.append(obj_to_node(source.mfd))
    # Parse nodal plane distribution
    source_nodes.append(
        build_nodal_plane_dist(source.nodal_plane_distribution))
    # Parse hypocentral depth distribution
    source_nodes.append(
        build_hypo_depth_dist(source.hypocenter_distribution))
    return source_nodes
Exemple #3
0
def build_characteristic_fault_source_node(source):
    source_nodes = [obj_to_node(source.mfd)]
    source_nodes.append(LiteralNode("rake", text=source.rake))
    source_nodes.append(source.surface_node)
    return LiteralNode('characteristicFaultSource',
                       get_source_attributes(source),
                       nodes=source_nodes)
Exemple #4
0
def get_exposure_lazy(fname):
    """
    :param fname:
        path of the XML file containing the exposure
    :returns:
        a pair (Exposure instance, list of asset nodes)
    """
    [exposure] = nrml.read_lazy(fname, ['assets'])
    description = exposure.description
    try:
        conversions = exposure.conversions
    except NameError:
        conversions = LiteralNode('conversions',
                                  nodes=[LiteralNode('costTypes', [])])
    try:
        inslimit = conversions.insuranceLimit
    except NameError:
        inslimit = LiteralNode('insuranceLimit', text=True)
    try:
        deductible = conversions.deductible
    except NameError:
        deductible = LiteralNode('deductible', text=True)
    try:
        area = conversions.area
    except NameError:
        area = LiteralNode('area', dict(type=''))
    return Exposure(exposure['id'], exposure['category'], ~description,
                    [ct.attrib for ct in conversions.costTypes], ~inslimit,
                    ~deductible, area.attrib, [], set()), exposure.assets
Exemple #5
0
def build_complex_fault_geometry(fault_source):
    """
    Returns the complex fault source geometry as a Node
    :param fault_source:
        Complex fault source model as an instance of the :class:
        openquake.hazardlib.source.complex_fault.ComplexFaultSource
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    num_edges = len(fault_source.edges)
    edge_nodes = []
    for iloc, edge in enumerate(fault_source.edges):
        if iloc == 0:
            # Top Edge
            node_name = "faultTopEdge"

        elif iloc == (num_edges - 1):
            # Bottom edge
            node_name = "faultBottomEdge"
        else:
            # Intermediate edge
            node_name = "intermediateEdge"
        edge_nodes.append(
            LiteralNode(node_name,
                        nodes=[build_linestring_node(edge, with_depth=True)])
            )
    return LiteralNode("complexFaultGeometry", nodes=edge_nodes)
Exemple #6
0
def build_slip_list_node(slip_list):
    """
    :param slip_list:
       an array of shape (N, 2) with columns (slip, weight)
    :returns:
        a hypoList node containing N slip nodes
    """
    sliplist = LiteralNode('slipList', {})
    for row in slip_list:
        sliplist.append(LiteralNode('slip', dict(weight=row[1]), row[0]))
    return sliplist
def build_vf_node(vf):
    """
    Convert a VulnerabilityFunction object into a LiteralNode suitable
    for XML conversion.
    """
    nodes = [LiteralNode('imls', {'imt': vf.imt}, vf.imls),
             LiteralNode('meanLRs', {}, vf.mean_loss_ratios),
             LiteralNode('covLRs', {}, vf.covs)]
    return LiteralNode(
        'vulnerabilityFunction',
        {'id': vf.id, 'dist': vf.distribution_name}, nodes=nodes)
Exemple #8
0
def get_exposure_lazy(fname, ok_cost_types):
    """
    :param fname:
        path of the XML file containing the exposure
    :param ok_cost_types:
        a set of cost types (as strings)
    :returns:
        a pair (Exposure instance, list of asset nodes)
    """
    [exposure] = nrml.read_lazy(fname, ['assets'])
    description = exposure.description
    try:
        conversions = exposure.conversions
    except NameError:
        conversions = LiteralNode('conversions',
                                  nodes=[LiteralNode('costTypes', [])])
    try:
        inslimit = conversions.insuranceLimit
    except NameError:
        inslimit = LiteralNode('insuranceLimit', text=True)
    try:
        deductible = conversions.deductible
    except NameError:
        deductible = LiteralNode('deductible', text=True)
    try:
        area = conversions.area
    except NameError:
        # NB: the area type cannot be an empty string because when sending
        # around the CostCalculator object one runs into this numpy bug on
        # pickling dictionaries with empty strings:
        # https://github.com/numpy/numpy/pull/5475
        area = LiteralNode('area', dict(type='?'))

    # read the cost types and make some check
    cost_types = []
    for ct in conversions.costTypes:
        if ct['name'] in ok_cost_types:
            with context(fname, ct):
                cost_types.append(
                    (ct['name'], valid_cost_type(ct['type']), ct['unit']))
    if 'occupants' in ok_cost_types:
        cost_types.append(('occupants', 'per_area', 'people'))
    cost_types.sort(key=operator.itemgetter(0))
    time_events = set()
    exp = Exposure(exposure['id'], exposure['category'], ~description,
                   numpy.array(cost_types, cost_type_dt), time_events,
                   ~inslimit, ~deductible, area.attrib, [], set(), [])
    cc = riskmodels.CostCalculator({}, {}, exp.deductible_is_absolute,
                                   exp.insurance_limit_is_absolute)
    for ct in exp.cost_types:
        name = ct['name']  # structural, nonstructural, ...
        cc.cost_types[name] = ct['type']  # aggregated, per_asset, per_area
        cc.area_types[name] = exp.area['type']
    return exp, exposure.assets, cc
Exemple #9
0
def build_arbitrary_mfd(mfd):
    """
    Parses the arbitrary MFD as a Node
    param mfd:
        MFD as instance of :class:
        openquake.hazardlib.mfd.arbitrary.ArbitraryMFD
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    magnitudes = LiteralNode("magnitudes", text=mfd.magnitudes)
    occur_rates = LiteralNode("occurRates", text=mfd.occurrence_rates)
    return LiteralNode("arbitraryMFD", nodes=[magnitudes, occur_rates])
Exemple #10
0
def build_slip_list_node(slip_list):
    """
    :param slip_list:
       an array of shape (N, 2) with columns (slip, weight)
    :returns:
        a hypoList node containing N slip nodes
    """
    sliplist = LiteralNode('slipList', {})
    for row in slip_list:
        sliplist.append(
            LiteralNode('slip', dict(weight=row[1]), row[0]))
    return sliplist
Exemple #11
0
def build_hypo_list_node(hypo_list):
    """
    :param hypo_list:
       an array of shape (N, 3) with columns (alongStrike, downDip, weight)
    :returns:
        a hypoList node containing N hypo nodes
    """
    hypolist = LiteralNode('hypoList', {})
    for row in hypo_list:
        n = LiteralNode(
            'hypo', dict(alongStrike=row[0], downDip=row[1], weight=row[2]))
        hypolist.append(n)
    return hypolist
Exemple #12
0
def build_evenly_discretised_mfd(mfd):
    """
    Returns the evenly discretized MFD as a Node
    :param mfd:
        MFD as instance of :class:
        openquake.hazardlib.mfd.evenly_discretized.EvenlyDiscretizedMFD
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    occur_rates = LiteralNode("occurRates", text=mfd.occurrence_rates)
    return LiteralNode("incrementalMFD",
                       {"binWidth": mfd.bin_width, "minMag": mfd.min_mag},
                       nodes=[occur_rates])
Exemple #13
0
def build_hypo_depth_dist(hdd):
    """
    Returns the hypocentral depth distribution as a Node instance
    :param hdd:
        Hypocentral depth distribution as an instance of :class:
        openuake.hzardlib.pmf.PMF
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    hdds = []
    for (prob, depth) in hdd.data:
        hdds.append(
            LiteralNode("hypoDepth", {"depth": depth, "probability": prob}))
    return LiteralNode("hypoDepthDist", nodes=hdds)
Exemple #14
0
def build_nonparametric_source_node(source):
    rup_nodes = []
    for rup, pmf in source.data:
        probs = [prob for (prob, no) in pmf.data]
        rup_nodes.append(build_rupture_node(rup, probs))
    return LiteralNode('nonParametricSeismicSource',
                       get_source_attributes(source), nodes=rup_nodes)
Exemple #15
0
def read(source, chatty=True):
    """
    Convert a NRML file into a validated LiteralNode object. Keeps
    the entire tree in memory.

    :param source:
        a file name or file object open for reading
    """
    nrml = xmlparse(source).getroot()
    assert striptag(nrml.tag) == 'nrml', nrml.tag
    # extract the XML namespace URL ('http://openquake.org/xmlns/nrml/0.5')
    xmlns = nrml.tag.split('}')[0][1:]
    if xmlns != NRML05 and chatty:
        # for the moment NRML04 is still supported, so we hide the warning
        logging.debug('%s is at an outdated version: %s', source, xmlns)
    subnodes = []
    for elem in nrml:
        nodecls = nodefactory[striptag(elem.tag)]
        try:
            subnodes.append(node_from_elem(elem, nodecls))
        except ValueError as exc:
            raise ValueError('%s of %s' % (exc, source))
    return LiteralNode(
        'nrml', {'xmlns': xmlns, 'xmlns:gml': GML_NAMESPACE},
        nodes=subnodes)
Exemple #16
0
def build_nodal_plane_dist(npd):
    """
    Returns the nodal plane distribution as a Node instance
    :param npd:
        Nodal plane distribution as instance of :class:
        openquake.hazardlib.pmf.PMF
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    npds = []
    for prob, npd in npd.data:
        nodal_plane = LiteralNode(
            "nodalPlane", {"dip": npd.dip, "probability": prob,
                           "strike": npd.strike, "rake": npd.rake})
        npds.append(nodal_plane)
    return LiteralNode("nodalPlaneDist", nodes=npds)
Exemple #17
0
def build_linestring_node(line, with_depth=False):
    """
    Parses a line to a Node class
    :param line:
        Line as instance of :class: openquake.hazardlib.geo.line.Line
    :param bool with_depth:
        Include the depth values (True) or not (False):
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    if with_depth:
        geom_str = ["%s %s %s" % (p.x, p.y, p.z) for p in line.points]
    else:
        geom_str = ["%s %s" % (p.x, p.y) for p in line.points]
    poslist_node = LiteralNode("gml:posList", text=geom_str)
    return LiteralNode("gml:LineString", nodes=[poslist_node])
Exemple #18
0
def build_point_source_geometry(point_source):
    """
    Returns the poing source geometry as a Node
    :param point_source:
        Point source model as an instance of the :class:
        openquake.hazardlib.source.point.PointSource
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    xy = point_source.location.x, point_source.location.y
    pos_node = LiteralNode("gml:pos", text=xy)
    point_node = LiteralNode("gml:Point", nodes=[pos_node])
    upper_depth_node = LiteralNode("upperSeismoDepth",
                                   text=point_source.upper_seismogenic_depth)
    lower_depth_node = LiteralNode("lowerSeismoDepth",
                                   text=point_source.lower_seismogenic_depth)
    return LiteralNode("pointGeometry",
                       nodes=[point_node, upper_depth_node, lower_depth_node])
Exemple #19
0
def get_exposure_lazy(fname, ok_cost_types):
    """
    :param fname:
        path of the XML file containing the exposure
    :param ok_cost_types:
        a set of cost types (as strings)
    :returns:
        a pair (Exposure instance, list of asset nodes)
    """
    [exposure] = nrml.read_lazy(fname, ['assets'])
    description = exposure.description
    try:
        conversions = exposure.conversions
    except NameError:
        conversions = LiteralNode('conversions',
                                  nodes=[LiteralNode('costTypes', [])])
    try:
        inslimit = conversions.insuranceLimit
    except NameError:
        inslimit = LiteralNode('insuranceLimit', text=True)
    try:
        deductible = conversions.deductible
    except NameError:
        deductible = LiteralNode('deductible', text=True)
    try:
        area = conversions.area
    except NameError:
        area = LiteralNode('area', dict(type=''))

    # read the cost types and make some check
    cost_types = []
    for ct in conversions.costTypes:
        if ct['name'] in ok_cost_types:
            with context(fname, ct):
                cost_types.append(
                    (ct['name'], valid_cost_type(ct['type']), ct['unit']))
    if 'occupants' in ok_cost_types:
        cost_types.append(('occupants', 'per_area', 'people'))
    cost_types.sort(key=operator.itemgetter(0))
    time_events = set()
    return Exposure(
        exposure['id'], exposure['category'],
        ~description, numpy.array(cost_types, cost_type_dt), time_events,
        ~inslimit, ~deductible, area.attrib, [], set()), exposure.assets
Exemple #20
0
def build_simple_fault_geometry(fault_source):
    """
    Returns the simple fault source geometry as a Node
    :param fault_source:
        Simple fault source model as an instance of the :class:
        openquake.hazardlib.source.simple_fault.SimpleFaultSource
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    linestring_node = build_linestring_node(fault_source.fault_trace,
                                            with_depth=False)
    dip_node = LiteralNode("dip", text=fault_source.dip)
    upper_depth_node = LiteralNode(
        "upperSeismoDepth", text=fault_source.upper_seismogenic_depth)
    lower_depth_node = LiteralNode(
        "lowerSeismoDepth", text=fault_source.lower_seismogenic_depth)
    return LiteralNode("simpleFaultGeometry",
                       nodes=[linestring_node, dip_node, upper_depth_node,
                              lower_depth_node])
Exemple #21
0
def upgrade_file(path):
    """Upgrade to the latest NRML version"""
    node0 = nrml.read(path, chatty=False)[0]
    shutil.copy(path, path + '.bak')  # make a backup of the original file
    if striptag(node0.tag) == 'vulnerabilityModel':
        vf_dict, cat_dict = get_vulnerability_functions_04(path)
        node0 = LiteralNode(
            'vulnerabilityModel', cat_dict,
            nodes=list(map(riskmodels.obj_to_node, list(vf_dict.values()))))
    with open(path, 'w') as f:
        nrml.write([node0], f)
Exemple #22
0
    def test_zero_node(self):
        s = BytesIO()
        node = LiteralNode('zero', {}, 0)
        with StreamingXMLWriter(s) as writer:
            writer.serialize(node)
        self.assertEqual(s.getvalue(), '''\
<?xml version="1.0" encoding="utf-8"?>
<zero>
    0
</zero>
''')
Exemple #23
0
def build_rupture_node(rupt, probs_occur):
    """
    :param rupt: a hazardlib rupture object
    :param probs_occur: a list of floats with sum 1
    """
    h = rupt.hypocenter
    hp_dict = dict(lon=h.longitude, lat=h.latitude, depth=h.depth)
    rupt_nodes = [LiteralNode('magnitude', {}, rupt.mag),
                  LiteralNode('rake', {}, rupt.rake),
                  LiteralNode('hypocenter', hp_dict)]
    rupt_nodes.extend(rupt.surface_nodes)
    geom = rupt.surface_nodes[0].tag.split('}')[1]
    if len(rupt.surface_nodes) > 1:
        name = 'multiPlanesRupture'
    elif geom == 'planarSurface':
        name = 'singlePlaneRupture'
    elif geom == 'simpleFaultGeometry':
        name = 'simpleFaultRupture'
    elif geom == 'complexFaultGeometry':
        name = 'complexFaultRupture'
    return LiteralNode(name, {'probs_occur': probs_occur}, nodes=rupt_nodes)
Exemple #24
0
def build_truncated_gr_mfd(mfd):
    """
    Parses the truncated Gutenberg Richter MFD as a Node
    :param mfd:
        MFD as instance of :class:
        openquake.hazardlib.mfd.truncated_gr.TruncatedGRMFD
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    return LiteralNode("truncGutenbergRichterMFD",
                       {"aValue": mfd.a_val, "bValue": mfd.b_val,
                        "minMag": mfd.min_mag, "maxMag": mfd.max_mag})
Exemple #25
0
def build_area_source_node(area_source):
    """
    Parses an area source to a Node class
    :param area_source:
        Area source as instance of :class:
        openquake.hazardlib.source.area.AreaSource
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    # parse geometry
    source_nodes = [build_area_source_geometry(area_source)]
    # parse common distributed attributes
    source_nodes.extend(get_distributed_seismicity_source_nodes(area_source))
    return LiteralNode(
        "areaSource", get_source_attributes(area_source), nodes=source_nodes)
def upgrade_file(path):
    """Upgrade to the latest NRML version"""
    node0 = nrml.read(path, chatty=False)[0]
    shutil.copy(path, path + '.bak')  # make a backup of the original file
    tag = striptag(node0.tag)
    if tag == 'vulnerabilityModel':
        vf_dict, cat_dict = get_vulnerability_functions_04(path)
        # below I am converting into a NRML 0.5 vulnerabilityModel
        node0 = LiteralNode(
            'vulnerabilityModel', cat_dict,
            nodes=list(map(riskmodels.obj_to_node, list(vf_dict.values()))))
    elif tag == 'fragilityModel':
        node0 = riskmodels.convert_fragility_model_04(
            nrml.read(path)[0], path)
    with open(path, 'w') as f:
        nrml.write([node0], f)
Exemple #27
0
def build_complex_fault_source_node(fault_source):
    """
    Parses a complex fault source to a Node class
    :param fault_source:
        Simple fault source as instance of :class:
        openquake.hazardlib.source.complex_fault.ComplexFaultSource
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    # Parse geometry
    source_nodes = [build_complex_fault_geometry(fault_source)]
    # Parse common fault source attributes
    source_nodes.extend(get_fault_source_nodes(fault_source))
    return LiteralNode("complexFaultSource",
                       get_source_attributes(fault_source),
                       nodes=source_nodes)
Exemple #28
0
def build_point_source_node(point_source):
    """
    Parses a point source to a Node class
    :param point_source:
        Point source as instance of :class:
        openquake.hazardlib.source.point.PointSource
    :returns:
        Instance of :class: openquake.commonlib.node.Node

    """
    # parse geometry
    source_nodes = [build_point_source_geometry(point_source)]
    # parse common distributed attributes
    source_nodes.extend(get_distributed_seismicity_source_nodes(point_source))
    return LiteralNode("pointSource",
                       get_source_attributes(point_source),
                       nodes=source_nodes)
Exemple #29
0
def write_source_model(dest, sources, name=None):
    """
    Writes a source model to XML.

    :param str dest:
        Destination path
    :param list sources:
        Source model as list of instance of the
        :class:`openquake.hazardlib.source.base.BaseSeismicSource`
    :param str name:
        Name of the source model (if missing, extracted from the filename)
    """
    name = name or os.path.splitext(os.path.basename(dest))[0]
    nodes = list(map(obj_to_node, sorted(sources, key=lambda src: src.source_id)))
    source_model = LiteralNode("sourceModel", {"name": name}, nodes=nodes)
    with open(dest, 'w') as f:
        nrml.write([source_model], f, '%s')
    return dest
Exemple #30
0
def build_youngs_coppersmith_mfd(mfd):
    """
    Parses the Youngs & Coppersmith MFD as a node. Note that the MFD does
    not hold the total moment rate, but only the characteristic rate. Therefore
    the node is written to the characteristic rate version regardless of
    whether or not it was originally created from total moment rate
    :param mfd:
        MFD as instance of :class:
        openquake.hazardlib.mfd.youngs_coppersmith_1985.
        YoungsCoppersmith1985MFD
    :returns:
        Instance of :class: openquake.commonlib.node.Node
    """
    return LiteralNode(
        "YoungsCoppersmithMFD", {
            "minMag": mfd.min_mag,
            "bValue": mfd.b_val,
            "characteristicMag": mfd.char_mag,
            "characteristicRate": mfd.char_rate,
            "binWidth": mfd.bin_width
        })
Exemple #31
0
def read(source, chatty=True):
    """
    Convert a NRML file into a validated LiteralNode object. Keeps
    the entire tree in memory.

    :param source:
        a file name or file object open for reading
    """
    nrml = parse(source).getroot()
    assert striptag(nrml.tag) == 'nrml', nrml.tag
    # extract the XML namespace URL ('http://openquake.org/xmlns/nrml/0.5')
    xmlns = nrml.tag.split('}')[0][1:]
    if xmlns != NRML05 and chatty:
        logging.warn('%s is at an outdated version: %s', source, xmlns)
    subnodes = []
    for elem in nrml:
        nodecls = nodefactory[striptag(elem.tag)]
        subnodes.append(node_from_elem(elem, nodecls))
    return LiteralNode('nrml', {
        'xmlns': xmlns,
        'xmlns:gml': GML_NAMESPACE
    },
                       nodes=subnodes)
def convert_fragility_model_04(node, fname, fmcounter=itertools.count(1)):
    """
    :param node:
        an :class:`openquake.commonib.node.LiteralNode` in NRML 0.4
    :param fname:
        path of the fragility file
    :returns:
        an :class:`openquake.commonib.node.LiteralNode` in NRML 0.5
    """
    convert_type = {"lognormal": "logncdf"}
    new = LiteralNode('fragilityModel',
                      dict(assetCategory='building',
                           lossCategory='structural',
                           id='fm_%d_converted_from_NRML_04' %
                           next(fmcounter)))
    with context(fname, node):
        fmt = node['format']
        descr = ~node.description
        limit_states = ~node.limitStates
    new.append(LiteralNode('description', {}, descr))
    new.append((LiteralNode('limitStates', {}, ' '.join(limit_states))))
    for ffs in node[2:]:
        IML = ffs.IML
        # NB: noDamageLimit = None is different than zero
        nodamage = ffs.attrib.get('noDamageLimit')
        ff = LiteralNode('fragilityFunction', {'format': fmt})
        ff['id'] = ~ffs.taxonomy
        ff['shape'] = convert_type[ffs.attrib.get('type', 'lognormal')]
        if fmt == 'continuous':
            with context(fname, IML):
                ff.append(LiteralNode('imls', dict(imt=IML['IMT'],
                                                   minIML=IML['minIML'],
                                                   maxIML=IML['maxIML'],
                                                   noDamageLimit=nodamage)))
            for ffc in ffs[2:]:
                with context(fname, ffc):
                    ls = ffc['ls']
                    param = ffc.params
                with context(fname, param):
                    m, s = param['mean'], param['stddev']
                ff.append(LiteralNode('params', dict(ls=ls, mean=m, stddev=s)))
        else:  # discrete
            with context(fname, IML):
                imls = ' '.join(map(str, (~IML)[1]))
                attr = dict(imt=IML['IMT'])
            if nodamage is not None:
                attr['noDamageLimit'] = nodamage
            ff.append(LiteralNode('imls', attr, imls))
            for ffd in ffs[2:]:
                ls = ffd['ls']
                with context(fname, ffd):
                    poes = ' '.join(map(str, ~ffd.poEs))
                ff.append(LiteralNode('poes', dict(ls=ls), poes))
        new.append(ff)
    return new