@@ -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+ self .uncertainty .reaction_list .append (extra_reaction )
355+ try : # this will still error out if there's a problem, but will reset the reaction list so it doesn't affect other tests
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+ finally :
372+ self .uncertainty .reaction_list .pop () # remove the extra reaction so it doesn't affect other tests
373+
374+ # check that the diagonal elements of the correlated and uncorrelated covariance matrices are the same and equal to the squares of the input uncertainties
375+ np .testing .assert_allclose (np .diag (uncorrelated_thermo_covariance ), np .float_power (uncorrelated_thermo_inputs , 2.0 ), rtol = 1e-4 )
376+ np .testing .assert_allclose (np .diag (correlated_thermo_covariance ), np .float_power (uncorrelated_thermo_inputs , 2.0 ), rtol = 1e-4 )
377+ np .testing .assert_allclose (np .diag (uncorrelated_kinetic_covariance ), np .float_power (uncorrelated_kinetic_inputs , 2.0 ), rtol = 1e-4 )
378+ np .testing .assert_allclose (np .diag (correlated_kinetic_covariance ), np .float_power (uncorrelated_kinetic_inputs , 2.0 ), rtol = 1e-4 )
379+
380+ # check that the off-diagonal elements of the uncorrelated covariance matrix are zero
381+ off_diagonal_kinetic_uncorrelated = uncorrelated_kinetic_covariance - np .diag (np .diag (uncorrelated_kinetic_covariance ))
382+ assert np .allclose (off_diagonal_kinetic_uncorrelated , 0 , atol = 1e-8 )
383+ off_diagonal_thermo_uncorrelated = uncorrelated_thermo_covariance - np .diag (np .diag (uncorrelated_thermo_covariance ))
384+ assert np .allclose (off_diagonal_thermo_uncorrelated , 0 , atol = 1e-8 )
385+
386+ # check that the off-diagonal elements of the correlated covariance matrix are not all zero
387+ off_diagonal_kinetic_correlated = correlated_kinetic_covariance - np .diag (np .diag (correlated_kinetic_covariance ))
388+ assert not np .allclose (off_diagonal_kinetic_correlated , 0 , atol = 1e-8 )
389+ off_diagonal_thermo_correlated = correlated_thermo_covariance - np .diag (np .diag (correlated_thermo_covariance ))
390+ assert not np .allclose (off_diagonal_thermo_correlated , 0 , atol = 1e-8 )
391+
392+ # check that the correlated covariance matrices are symmetric
393+ assert np .allclose (correlated_kinetic_covariance , correlated_kinetic_covariance .T , atol = 1e-8 )
394+ assert np .allclose (correlated_thermo_covariance , correlated_thermo_covariance .T , atol = 1e-8 )
395+ assert np .allclose (Sigma_ww_kinetics , Sigma_ww_kinetics .T , atol = 1e-8 )
396+ assert np .allclose (Sigma_ww_thermo , Sigma_ww_thermo .T , atol = 1e-8 )
397+
398+ # check that the matrix is positive semi-definite by confirming that all eigenvalues are non-negative
399+ kinetic_eigenvalues = np .linalg .eigvals (correlated_kinetic_covariance )
400+ assert np .all (kinetic_eigenvalues >= - 1e-8 ) # allow for small numerical errors
401+ thermo_eigenvalues = np .linalg .eigvals (correlated_thermo_covariance )
402+ assert np .all (thermo_eigenvalues >= - 1e-8 ) # allow for small numerical errors
403+ intermediate_kinetic_eigenvalues = np .linalg .eigvals (Sigma_ww_kinetics )
404+ assert np .all (intermediate_kinetic_eigenvalues >= - 1e-8 )
405+ intermediate_thermo_eigenvalues = np .linalg .eigvals (Sigma_ww_thermo )
406+ assert np .all (intermediate_thermo_eigenvalues >= - 1e-8 )
407+
346408 def test_specific_species_uncertainties (self ):
347409 """
348410 Test uncertainties for a few specific examples
0 commit comments