@@ -343,6 +343,68 @@ def test_local_analysis(self):
343343 assert np .isclose (sorted_thermo_variances , expected_correlated_thermo_variances ).all ()
344344 assert sorted_thermo_names == expected_correlated_thermo_labels
345345
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
407+
346408 def test_specific_species_uncertainties (self ):
347409 """
348410 Test uncertainties for a few specific examples
0 commit comments