Source code for openmdao.util.parse_phoenixwrapper

"""
Parses the variable definition section of a Phoenix Integration ModelCenter
component wrapper and generates an OpenMDAO component stub.
"""

from pyparsing import Suppress, Word, alphanums, dictOf, oneOf, printables, \
                      removeQuotes, sglQuotedString, commaSeparatedList, \
                      ParseResults

#public symbols
__all__ = ["parse_phoenixwrapper"]
           
def _parse_phoenixline():
    """Parse a single line containing a variable definition."""
        
    data = ( Suppress("variable:") + \
             Word(alphanums).setResultsName("name") + \
             Word(printables).setResultsName("type") + \
             oneOf(""" input output """).setResultsName("iotype") + \
             dictOf(Word(alphanums), Suppress("=")+ \
             (Word(alphanums+","+'-'+'.')|commaSeparatedList)) )
    
    return data

def _parse_phoenixgroup():
    """Parse a single line containing a group definition."""
        
    data = ( Suppress(Word(alphanums)) + \
           sglQuotedString.setParseAction(removeQuotes).setResultsName("name") )
    
    return data

_phoenix2trait = {
    'boolean': ('Bool'),
    'double': ('Float'),
    'double[]': ('Array','float32'),
    'double[][]': ('Array','float32'),
    'file': ('File'),
    'integer': ('Int'),
    'integer[]': ('Array','int32'),
    'integer[][]': ('Array','int32'),
    'string': ('Str'),
    'string[]': ('Array','str')
}

_phoenix2default = {
    'boolean': "False",
    'double': "0.0",
    'integer': "0",
    'string': "''",
}

_phoenix2iotype = {
    'input': ('in'),
    'output': ('out')
}

# Note these are direct replacemnts, for ones that are too risky to str replace
_unit_full_replace = {
    'in': ('inch'),
    'in*in': ('inch*inch'),
    'R': ('degR'),
    'rpm': ('1/min'),
    'in2rpm2': ('(inch*inch)/(min*min)'),
    'ft-lb/lbm-R': ('(ft*lb)/(lb*degR)'),
}

# Note, these are string replacements, useful for formulas.
_unit_part_replace = {
    'mile': ('mi'),
    'hr': ('h'),
    'ft2': ('ft*ft'),
    'ft3': ('ft*ft*ft'),
    'in2': ('inch*inch'),
    'in3': ('inch*inch*inch'),
    'kt': ('nmi'),
    'years': ('year'),
    'deg F': ('degF'),
    'deg C': ('degC'),
    'lb/ft2': ('psf'),
    'mph': ('mi/h'),
    'sec': ('s'),
    'btu': ('Btu'),
    'lbm': ('lb'),
    '/engine': (''),
    'ft/s2': ('ft/(s*s)'),
}

# These units are replaced with unitless quantities.
_unit_ignore = ('%', 'EPNdB', 'dB')

def _gen_publicvar(data):
    """Generates the OpenMDAO variable line given a dictionary of info.
    """
    
    sep = ", "
    dtype = None
    
    name = data.name
    
    try:
        trait = _phoenix2trait[data.type]
    except KeyError:
        raise KeyError("Unhandled Modelcenter input type - %s" % data.type)

    iotype = "iotype='" + _phoenix2iotype[data.iotype] + "'"
    
    # Arrays use the Array trait, which has no default value
    default = ""
    if trait[0] == "Array":
        dtype = trait[1]
        trait = "Array"
    else:
        if data.default:
            
            default_value = data.default
            
            if trait == "Str":
                if default_value[-1] != default_value[0]:
                    default_value += default_value[0]
                    
            default = default_value + sep
            
            # Modelcenter doesn't capitalize True and False
            if trait == "Bool":
                default = default.replace("true","True")
                default = default.replace("false","False")
                
        elif data.type in _phoenix2default:
            default = _phoenix2default[data.type] + sep

    # Process Enums
    if data.enumValues:
        trait = "Enum"
        default = "(" +data.enumValues + "), "
        
    var = name + " = " + trait + "(" + default + iotype
    
    if trait == "Array":
        var += sep + "dtype=numpy_" + dtype
        
    if trait == "File" and _phoenix2iotype[data.iotype] == 'out':
        var += sep + "path='Insert_Filename_Here'"
        
    if data.units:
        
        unit = data.units
        unit = unit.strip("'")
        
        if unit not in _unit_ignore:
            if unit in _unit_full_replace:
                unit = _unit_full_replace[unit]
            else:
                for unit_mc, unit_op in _unit_part_replace.iteritems():
                    unit = unit.replace(unit_mc, unit_op)
                
            var += sep + "units='" + unit + "'"
    
    if data.description:
        
        # if you don't end with a quote, comma-seperated lists get
        # screwed up.
        if isinstance(data.description, ParseResults):
            desc = data.description[0]
            for item in data.description[1:]:
                desc += ', ' + item
            desc += "'"
        else:
            desc = data.description
            
        var += sep + "doc=" + desc

    
    if data.enumAliases:
        aliases = data.enumAliases
        
        # remove pesky quotes in Enum alias list
        for i, v in enumerate(aliases):
            aliases[i] = v.strip("'")
            
        var += sep + "aliases=" + str(tuple(aliases))
            
    var += ")\n"
    
    return var, trait, dtype
        
# Main Code

[docs]def parse_phoenixwrapper(infile, outfile, compname): """ Generates a dummy component given a Phoenix Integration Modelcenter script wrapper. The first section of this wrapper is parsed, and the appropriate variables and containers are placed in the new OpenMDAO component. infile - ModelCenter scriptwrapper. outfile - File containing new OpenMDAO component skeleton. compname - Name for new component. """ # Note: special processing since Python strings don't need escaped quotes. phoenixdata = open(infile,'r').read().replace('"',"'").replace("\\'",'"') openmdao = open(outfile,'w') group = "" groups = [] count_var = 0 imports = [] numpy_imports = [] variables = {} variables[group] = "" tab = " " for line in phoenixdata.split("\n"): # blank line: do nothing if not line: continue # Section separator, no more variables if line[0:4] == "####": break # Line is commented out: do nothing if line[0] == "#": continue # Group Header if line[0:8] == "setGroup" or line[0:8] == "setgroup": group = _parse_phoenixgroup().parseString(line) group = group.name if group not in groups: groups.append(group) variables[group] = "" else: # Variable fields = _parse_phoenixline().parseString(line) count_var += 1 (varline, trait, dtype) = _gen_publicvar(fields) if trait not in imports: imports.append(trait) if dtype and dtype not in numpy_imports: numpy_imports.append(dtype) variables[group] += tab + varline # Write output file openmdao.write("\"\"\"\n") openmdao.write("OpenMDAO Wrapper for %s\n" % compname) openmdao.write("Automatically generated from " + \ "%s with parse_phoenixwrapper.\n" % infile) openmdao.write("\"\"\"\n\n") text = "" for imp in numpy_imports: text += "from numpy import %s as numpy_%s\n" % (imp, imp) if len(groups) > 0: text += "\nfrom openmdao.main.api import Component, Container\n" else: text += "\nfrom openmdao.main.api import Component\n" sep = "" text += "from openmdao.lib.datatypes.api import " for imp in imports: text += sep + imp sep = ", " text += "\n" openmdao.write(text) # Create all missing containers constructs = {} constructs[""] = "" for group in sorted(groups): constructs[group] = "" containers = group.split('.') fullname = "" sep = "" for level in containers[0:-1]: fullname += sep + level sep = "." if fullname not in groups: groups.append(fullname) variables[fullname] = "" constructs[fullname] = "" # Prepare the container constructor calls for group in sorted(groups): containers = group.split('.') container_name = group.replace(".","_") parentname = "" if len(containers) > 1: sep = "" for level in containers[0:-1]: parentname += sep + level sep = "." childname = containers[-1] text = tab + tab + "self.add('%s', %s_%s())\n" % \ (childname, compname, container_name) constructs[parentname] += text # Write the variables. for group in sorted(groups, reverse=True): # Do top level last. if group != "": container_name = compname + "_" + group.replace(".","_") text = "\n" text += "class " + container_name + "(Container):\n" text += tab + '"""Container for %s"""\n\n' % str(group) text += tab + "# OpenMDAO Variables\n" openmdao.write(text) openmdao.write(variables[group]) # Write the constructors. if constructs[group] != "": text = "\n" text += tab + "def __init__(self, directory=''):\n" text += tab + tab + '"""Constructor for the %s component"""' \ % container_name text += "\n\n" text += tab + tab + "super(%s, self).__init__(directory)" \ % container_name text += "\n\n" text += tab + tab + "# Variable Containers\n" openmdao.write(text) openmdao.write(constructs[group]) # Top level class of the Wrapper. text = "\n\n" text += "class " + compname + "(Component):\n" text += tab + '"""Wrapper for %s"""\n\n' % compname text += tab + "# OpenMDAO Variables\n" openmdao.write(text) openmdao.write(variables[""]) text = "\n" text += tab + "def __init__(self, directory=''):\n" text += tab + tab + '"""Constructor for the %s component"""' % compname text += "\n\n" text += tab + tab + "super(%s, self).__init__(directory)" % compname text += "\n\n" text += tab + tab + "# Variable Containers\n" openmdao.write(text) openmdao.write(constructs[""]) print "Stats: %d groups, %d variables." % (len(groups), count_var)
OpenMDAO Home