|
28 | 28 | ############################################################################### |
29 | 29 |
|
30 | 30 | import os |
| 31 | +import re |
31 | 32 |
|
32 | 33 | import numpy as np |
33 | 34 |
|
@@ -165,19 +166,35 @@ def get_uncertainty_value(self, source): |
165 | 166 |
|
166 | 167 | dlnk += self.dlnk_family ** 2 |
167 | 168 | N = len(rule_weights) + len(training_weights) |
168 | | - if not exact: |
169 | | - # nonexactness contribution increases as N increases |
| 169 | + if 'node_std_dev' in source_dict: |
| 170 | + # Handle autogen BM trees |
| 171 | + if source_dict['node_std_dev'] < 0: |
| 172 | + raise ValueError('Invalid value for std dev of kinetics family rule node') |
| 173 | + dlnk += source_dict['node_std_dev'] |
| 174 | + if source_dict['node_n_train'] < 0: |
| 175 | + raise ValueError('Invalid number of training reactions for kinetics family rule node') |
| 176 | + N = source_dict['node_n_train'] |
| 177 | + |
| 178 | + # Technically every lookup in the autogenerated trees is an "exact" match because |
| 179 | + # every node template has its own fitted rate rule by definition, but here we use the |
| 180 | + # number of training reactions as an approximation of the node's specificity/generality |
| 181 | + # and add a penalty for being too general (large # of training reactions) |
170 | 182 | dlnk += np.log10(N + 1) * self.dlnk_nonexact |
| 183 | + else: |
| 184 | + # Handle hand-made trees |
| 185 | + if not exact: |
| 186 | + # nonexactness contribution increases as N increases |
| 187 | + dlnk += np.log10(N + 1) * self.dlnk_nonexact |
171 | 188 |
|
172 | | - # Add the contributions from rules |
173 | | - dlnk += np.sum([weight * self.dlnk_rule for weight in rule_weights]) |
174 | | - # Add the contributions from training |
175 | | - # Even though these source from training reactions, we actually |
176 | | - # use the uncertainty for rate rules, since these are now approximations |
177 | | - # of the original reaction. We consider these to be independent of original the training |
178 | | - # parameters because the rate rules may be reversing the training reactions, |
179 | | - # which leads to more complicated dependence |
180 | | - dlnk += np.sum([weight * self.dlnk_rule for weight in training_weights]) |
| 189 | + # Add the contributions from rules |
| 190 | + dlnk += np.sum([weight * self.dlnk_rule for weight in rule_weights]) |
| 191 | + # Add the contributions from training |
| 192 | + # Even though these source from training reactions, we actually |
| 193 | + # use the uncertainty for rate rules, since these are now approximations |
| 194 | + # of the original reaction. We consider these to be independent of original the training |
| 195 | + # parameters because the rate rules may be reversing the training reactions, |
| 196 | + # which leads to more complicated dependence |
| 197 | + dlnk += np.sum([weight * self.dlnk_rule for weight in training_weights]) |
181 | 198 |
|
182 | 199 | return dlnk |
183 | 200 |
|
@@ -413,8 +430,20 @@ def extract_sources_from_model(self): |
413 | 430 | # Do nothing here because training source already saves the entry from the training reaction |
414 | 431 | pass |
415 | 432 | elif 'Rate Rules' in source: |
416 | | - # Do nothing |
417 | | - pass |
| 433 | + # Fetch standard deviation if autogenerated tree |
| 434 | + if source['Rate Rules'][1]['autogenerated']: |
| 435 | + long_desc = source['Rate Rules'][1]['rules'][0][0].long_desc |
| 436 | + std_dev_matches = re.search(r'Standard Deviation in ln\(k\): ([0-9]*.[0-9]*)', long_desc) |
| 437 | + std_dev = 1.329 # Default value is uniform distribution with upper bound of 10x the nominal value |
| 438 | + if std_dev_matches is not None: |
| 439 | + std_dev = float(std_dev_matches[1]) |
| 440 | + |
| 441 | + n_train_matches = re.search('rule fitted to ([0-9]*) training reactions', long_desc) |
| 442 | + n_train = -1 |
| 443 | + if n_train_matches is not None: |
| 444 | + n_train = int(n_train_matches[1]) |
| 445 | + source['Rate Rules'][1]['node_std_dev'] = std_dev |
| 446 | + source['Rate Rules'][1]['node_n_train'] = n_train |
418 | 447 | else: |
419 | 448 | raise Exception('Source of kinetics must be either Library, PDep, Training, or Rate Rules') |
420 | 449 | self.reaction_sources_dict[reaction] = source |
|
0 commit comments