EvMapping internals

Help
Anonymous
2012-12-10
2013-11-04

  • Anonymous
    2012-12-10

    I'm trying to debug an issue with transitions in a HybridModel. The problem is as follows (simplified):
    I have to state variables, say x_1 and x_2. When the event occurs, they have values x_1 = 1.0, x_2 = 2.0. The EvMapping defines the maps

    "x_2": "0"
     "x_1": "x_1/x_2"
    

    After the eventmapping is applied, I would expext x_1 to be 0.5 and x_2 to be 0. However, I get x_1 = NaN, making me believe that a division-by-zero has occurred.

    In the internals of EvMapping, a function evmapping(…) is created, but as far as I can tell it implements exactly the mapping that I define.

    Is it possible that in applying the map, the numerical values are used that are obtain while applying the map? This would explain why I get a division-by-zero.

     
  • Rob Clewley
    Rob Clewley
    2012-12-10

    Hi, thanks for your question. It's possible you're right about the source of the zero division error. The test is to try setting x_2 to 0.1 in your mapping and seeing that you don't get an error. The order will matter because the operations are acting directly on a dictionary of state values. The ordering of these is not defined as it involves an iteration through a dictionary. I will document that more clearly. If you need a temp variable or want to enforce the order then just provide an explicit string in the optional defString argument to the EvMapping initializer, something like

    defString="""
    xdict['x_1'] = xdict['x_1']/xdict['x_2']
    xdict['x_2'] = 0
    """
    

    Please let me know how this goes.

     

  • Anonymous
    2012-12-11

    I did some more debugging and can confirm it is the division-by-zero that is the problem.

    I tried to patch the makeCallFn() method in the EvMapping class:

    --- ModelConstructor.py 2012-04-06 02:24:22 +0000
    +++ ModelConstructor.py 2012-12-11 09:40:28 +0000
    @@ -1540,8 +1540,12 @@
                             raise TypeError("Assignment dictionary for event "
                                             "mapping must consist of strings for "
                                             "both keys and values")
    -                fnString += "\n" + indent + ("\n"+indent).join(["%s = %s"%(l,r) \
    +                fnString += "\n" + indent + "new_xdict = xdict.copy()"
    +                fnString += "\n" + indent + "new_pdict = pdict.copy()"
    +                fnString += "\n" + indent + ("\n"+indent).join(["new_%s = %s"%(l,r) \
                                         for l, r in self.assignDict.items()])
    +                fnString += "\n" + indent + "xdict = new_xdict"
    +                fnString += "\n" + indent + "pdict = new_pdict"
                 if len(self.defString) > 0:
                     fnString += "\n" + indent + ("\n"+indent).join(self.defString.split("\n"))
                 if len(self.activeDict) > 0:
    

    However, this broke all my simulations in spectacular fashion, so I think I have been too naive in applying this patch. Are there internals that I should know of if I want to contribute to this?

    I will try the defString method you outline, and let you know how this works out.

     

  • Anonymous
    2012-12-12

    The defString method resolves my issues. I have written a small helper function that, given a list of equations, and a list of variable names and parameter names, creates an appropriated defString. It replaces variable (parameter) names with xdict (pdict) items and concatenates the list elements to a function definition:

    def evmapstr(eqs, parnames, varnames):
        eqs_strings = []
        for e in eqs:
            s = str(e)
            
            for p in parnames:
                s = re.sub(r'(\A|[ +\-*/=(]+?)(' + p + r')([ +\-*/=)]+?|\Z)', r'\1pdict[' + "'" + p + "'" + r']\3', s)
            for v in varnames:
                s = re.sub(r'(\A|[ +\-*/=(]+?)(' + v + r')([ +\-*/=)]+?|\Z)', r'\1xdict[' + "'" + v + "'" + r']\3', s)
            
            eqs_strings.append(s)
            
        map_string = '\n'.join(eqs_strings)
        
        return map_string
    

    Maybe it can be of use.