|
28 | 28 | ############################################################################### |
29 | 29 |
|
30 | 30 | import os |
31 | | - |
| 31 | +import copy |
32 | 32 |
|
33 | 33 | import numpy as np |
34 | 34 |
|
@@ -302,43 +302,108 @@ def test_local_analysis(self): |
302 | 302 |
|
303 | 303 | # -------------------- repeat the exact same test for new formulation -------------------------- |
304 | 304 | # uncorrelated analysis first |
305 | | - self.uncertainty.assign_intermediate_uncertainties(correlated=False) |
306 | | - output = self.uncertainty.local_analysis_intermediate(sensitive_species=sensitive_species, correlated=False) |
| 305 | + self.uncertainty.assign_intermediate_uncertainties() |
| 306 | + output = self.uncertainty.local_analysis_intermediate(sensitive_species=sensitive_species) |
307 | 307 | total_variance, kinetic_uncertainty, thermo_uncertainty = output[sensitive_species[0]] |
308 | 308 | assert np.isclose(total_variance, expected_uncorrelated_total_variance) |
309 | 309 |
|
310 | | - # order of kinetic or thermo uncertainty is not guaranteed, this sorts by name |
| 310 | + # order of kinetic or thermo uncertainty is not guaranteed, this sorts by contribution |
311 | 311 | kinetic_variances = [r[2] for r in kinetic_uncertainty] |
312 | 312 | kinetics_names = [r[0] for r in kinetic_uncertainty] |
313 | | - sorted_kinetic_variances = [x for _, x in sorted(zip(kinetics_names, kinetic_variances))] |
| 313 | + sorted_kinetics_names = [x for _, x in sorted(zip(kinetic_variances, kinetics_names))][::-1] |
| 314 | + sorted_kinetic_variances = sorted(kinetic_variances, reverse=True) |
314 | 315 | assert np.isclose(sorted_kinetic_variances, expected_uncorrelated_kinetics_variances).all() |
315 | | - assert set(kinetics_names) == expected_uncorrelated_kinetics_labels |
| 316 | + assert sorted_kinetics_names == expected_uncorrelated_kinetics_labels |
316 | 317 |
|
317 | 318 | thermo_variances = [s[2] for s in thermo_uncertainty] |
318 | 319 | thermo_names = [s[0] for s in thermo_uncertainty] |
319 | | - sorted_thermo_variances = [x for _, x in sorted(zip(thermo_names, thermo_variances))] |
| 320 | + sorted_thermo_names = [x for _, x in sorted(zip(thermo_variances, thermo_names))][::-1] |
| 321 | + sorted_thermo_variances = sorted(thermo_variances, reverse=True) |
320 | 322 | assert np.isclose(sorted_thermo_variances, expected_uncorrelated_thermo_variances).all() |
321 | | - assert set(thermo_names) == expected_uncorrelated_thermo_labels |
| 323 | + assert sorted_thermo_names == expected_uncorrelated_thermo_labels |
322 | 324 |
|
323 | 325 | # now repeat for correlated analysis |
324 | 326 | self.uncertainty.assign_intermediate_uncertainties(correlated=True) |
325 | 327 | output = self.uncertainty.local_analysis_intermediate(sensitive_species=sensitive_species, correlated=True) |
326 | 328 | total_variance, kinetic_uncertainty, thermo_uncertainty = output[sensitive_species[0]] |
327 | 329 | assert np.isclose(total_variance, expected_correlated_total_variance) |
328 | 330 |
|
329 | | - # order of kinetic or thermo uncertainty is not guaranteed, this sorts by name |
| 331 | + # order of kinetic or thermo uncertainty is not guaranteed, this sorts by contribution |
330 | 332 | kinetic_variances = [r[2] for r in kinetic_uncertainty] |
331 | 333 | kinetics_names = [r[0] for r in kinetic_uncertainty] |
332 | | - sorted_kinetic_variances = [x for _, x in sorted(zip(kinetics_names, kinetic_variances))] |
| 334 | + sorted_kinetic_variances = sorted(kinetic_variances, reverse=True) |
| 335 | + sorted_kinetics_names = [x for _, x in sorted(zip(kinetic_variances, kinetics_names))][::-1] |
333 | 336 | assert np.isclose(sorted_kinetic_variances, expected_correlated_kinetics_variances).all() |
334 | | - assert set(kinetics_names) == expected_correlated_kinetics_labels |
| 337 | + assert sorted_kinetics_names == expected_correlated_kinetics_labels |
335 | 338 |
|
336 | 339 | thermo_variances = [s[2] for s in thermo_uncertainty] |
337 | 340 | thermo_names = [s[0] for s in thermo_uncertainty] |
338 | | - sorted_thermo_variances = [x for _, x in sorted(zip(thermo_names, thermo_variances))] |
| 341 | + sorted_thermo_variances = sorted(thermo_variances, reverse=True) |
| 342 | + sorted_thermo_names = [x for _, x in sorted(zip(thermo_variances, thermo_names))][::-1] |
339 | 343 | assert np.isclose(sorted_thermo_variances, expected_correlated_thermo_variances).all() |
340 | | - assert set(thermo_names) == expected_correlated_thermo_labels |
| 344 | + assert sorted_thermo_names == expected_correlated_thermo_labels |
341 | 345 |
|
| 346 | + def test_covariance_matrices(self): |
| 347 | + """ |
| 348 | + Test that the covariance matrices are being constructed correctly, and that the correlated uncertainties are different from the uncorrelated ones |
| 349 | + """ |
| 350 | + |
| 351 | + # have to add an extra reaction to see any kinetic correlations |
| 352 | + # copy reaction 4 and change the index so it is a new reaction, but with the same source (rate rule) as the original reaction |
| 353 | + extra_reaction = copy.deepcopy(self.uncertainty.reaction_list[4]) |
| 354 | + extra_reaction.index = len(self.uncertainty.reaction_list) |
| 355 | + self.uncertainty.reaction_list.append(extra_reaction) |
| 356 | + self.uncertainty.extract_sources_from_model() # this will assign the same source to the new reaction as the original reaction |
| 357 | + |
| 358 | + self.uncertainty.assign_parameter_uncertainties(correlated=False) |
| 359 | + uncorrelated_thermo_inputs = np.array(self.uncertainty.thermo_input_uncertainties) |
| 360 | + uncorrelated_kinetic_inputs = np.array(self.uncertainty.kinetic_input_uncertainties) |
| 361 | + |
| 362 | + self.uncertainty.assign_intermediate_uncertainties(correlated=False) |
| 363 | + uncorrelated_thermo_covariance = self.uncertainty.get_thermo_covariance_matrix() |
| 364 | + uncorrelated_kinetic_covariance = self.uncertainty.get_kinetic_covariance_matrix() |
| 365 | + |
| 366 | + self.uncertainty.assign_intermediate_uncertainties(correlated=True) |
| 367 | + correlated_thermo_covariance = self.uncertainty.get_thermo_covariance_matrix() |
| 368 | + correlated_kinetic_covariance = self.uncertainty.get_kinetic_covariance_matrix() |
| 369 | + Sigma_ww_thermo = self.uncertainty._get_intermediate_thermo_covariance_matrix() |
| 370 | + Sigma_ww_kinetics = self.uncertainty._get_intermediate_kinetics_covariance_matrix() |
| 371 | + |
| 372 | + # check that the diagonal elements of the correlated and uncorrelated covariance matrices are the same and equal to the squares of the input uncertainties |
| 373 | + np.testing.assert_allclose(np.diag(uncorrelated_thermo_covariance), np.float_power(uncorrelated_thermo_inputs, 2.0), rtol=1e-4) |
| 374 | + np.testing.assert_allclose(np.diag(correlated_thermo_covariance), np.float_power(uncorrelated_thermo_inputs, 2.0), rtol=1e-4) |
| 375 | + np.testing.assert_allclose(np.diag(uncorrelated_kinetic_covariance), np.float_power(uncorrelated_kinetic_inputs, 2.0), rtol=1e-4) |
| 376 | + np.testing.assert_allclose(np.diag(correlated_kinetic_covariance), np.float_power(uncorrelated_kinetic_inputs, 2.0), rtol=1e-4) |
| 377 | + |
| 378 | + # check that the off-diagonal elements of the uncorrelated covariance matrix are zero |
| 379 | + off_diagonal_kinetic_uncorrelated = uncorrelated_kinetic_covariance - np.diag(np.diag(uncorrelated_kinetic_covariance)) |
| 380 | + assert np.allclose(off_diagonal_kinetic_uncorrelated, 0, atol=1e-8) |
| 381 | + off_diagonal_thermo_uncorrelated = uncorrelated_thermo_covariance - np.diag(np.diag(uncorrelated_thermo_covariance)) |
| 382 | + assert np.allclose(off_diagonal_thermo_uncorrelated, 0, atol=1e-8) |
| 383 | + |
| 384 | + # check that the off-diagonal elements of the correlated covariance matrix are not all zero |
| 385 | + off_diagonal_kinetic_correlated = correlated_kinetic_covariance - np.diag(np.diag(correlated_kinetic_covariance)) |
| 386 | + assert not np.allclose(off_diagonal_kinetic_correlated, 0, atol=1e-8) |
| 387 | + off_diagonal_thermo_correlated = correlated_thermo_covariance - np.diag(np.diag(correlated_thermo_covariance)) |
| 388 | + assert not np.allclose(off_diagonal_thermo_correlated, 0, atol=1e-8) |
| 389 | + |
| 390 | + # check that the correlated covariance matrices are symmetric |
| 391 | + assert np.allclose(correlated_kinetic_covariance, correlated_kinetic_covariance.T, atol=1e-8) |
| 392 | + assert np.allclose(correlated_thermo_covariance, correlated_thermo_covariance.T, atol=1e-8) |
| 393 | + assert np.allclose(Sigma_ww_kinetics, Sigma_ww_kinetics.T, atol=1e-8) |
| 394 | + assert np.allclose(Sigma_ww_thermo, Sigma_ww_thermo.T, atol=1e-8) |
| 395 | + |
| 396 | + # check that the matrix is positive semi-definite by confirming that all eigenvalues are non-negative |
| 397 | + kinetic_eigenvalues = np.linalg.eigvals(correlated_kinetic_covariance) |
| 398 | + assert np.all(kinetic_eigenvalues >= -1e-8) # allow for small numerical errors |
| 399 | + thermo_eigenvalues = np.linalg.eigvals(correlated_thermo_covariance) |
| 400 | + assert np.all(thermo_eigenvalues >= -1e-8) # allow for small numerical errors |
| 401 | + intermediate_kinetic_eigenvalues = np.linalg.eigvals(Sigma_ww_kinetics) |
| 402 | + assert np.all(intermediate_kinetic_eigenvalues >= -1e-8) |
| 403 | + intermediate_thermo_eigenvalues = np.linalg.eigvals(Sigma_ww_thermo) |
| 404 | + assert np.all(intermediate_thermo_eigenvalues >= -1e-8) |
| 405 | + |
| 406 | + self.uncertainty.reaction_list.pop() # remove the extra reaction so it doesn't affect other tests |
342 | 407 |
|
343 | 408 | def test_specific_species_uncertainties(self): |
344 | 409 | """ |
|
0 commit comments