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
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.
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
xdict['x_1'] = xdict['x_1']/xdict['x_2']
xdict['x_2'] = 0
Please let me know how this goes.
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.
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)
map_string = '\n'.join(eqs_strings)
Maybe it can be of use.