Skip to content

Commit 9163166

Browse files
sotorrio1ksbeattie
andauthored
adding some UQ tests (#1272)
* adding some UQ tests * remove test_data_processor.py * rolling back some changes * fixing typos * issues with tests in Windows * remove mark.skip for test_genheatmap * update to macos-14 since macos-13 is being deprecated. actions/runner-images#13046 * add UQ RS Analysis tests * unpinning pytest version and adding exc_type argument to pytest.importorskip * pinning pytest again * removing exc_type argument (not valid in this version of pytest) * removing exc_type argument (not valid in this version of pytest) --------- Co-authored-by: Keith Beattie <ksbeattie@lbl.gov>
1 parent 6e71bc5 commit 9163166

29 files changed

Lines changed: 3012 additions & 62 deletions

foqus_lib/framework/graph/graph.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,13 +1574,13 @@ def adjLists(self):
15741574
def getEdgeIndex(self, vkey, wkey):
15751575
# get index of edge from v to w returns None if no edge from v to w
15761576
# multiple edges are not allowed
1577-
indx = None
1577+
index = None
15781578
for i in range(0, len(self.edges)):
15791579
if self.edges[i].start == vkey:
15801580
if self.edges[i].end == wkey:
1581-
indx = i
1581+
index = i
15821582
break
1583-
return indx
1583+
return index
15841584

15851585
def setTearSet(self, tSet):
15861586
# mark the given list of edges as tear edges

foqus_lib/framework/optimizer/SM_Optimizer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,10 +936,10 @@ def optimize(self):
936936
]
937937
nodevar = str(nodeName) + "_" + str(outVarName)
938938
if nodevar in [str(v) for v in self.surrout_names_pyomo]:
939-
indx = [str(v) for v in self.surrout_names_pyomo].index(
939+
index = [str(v) for v in self.surrout_names_pyomo].index(
940940
nodevar
941941
)
942-
surrin_outputvals[indx] = surrout_value
942+
surrin_outputvals[index] = surrout_value
943943

944944
surrin_samples_values.append(surrin_outputvals)
945945
latin_hypercube_samples_values = np.array(surrin_samples_values)

foqus_lib/framework/uq/SampleData.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,12 @@ def saveDict(self):
196196
sd["outputData"] = self.outputData.tolist()
197197
else:
198198
sd["outputData"] = self.outputData
199-
sd["runState"] = self.runState.tolist()
199+
200+
if isinstance(self.runState, numpy.ndarray):
201+
sd["runState"] = self.runState.tolist()
202+
else:
203+
sd["runState"] = self.runState
204+
200205
sd["legendreOrder"] = self.legendreOrder
201206
sd["fromFile"] = self.fromFile
202207
sd["sampleRSType"] = ResponseSurfaces.getPsuadeName(self.sampleRSType)

foqus_lib/framework/uq/test/__init__.py

Whitespace-only changes.
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#################################################################################
2+
# FOQUS Copyright (c) 2012 - 2025, by the software owners: Oak Ridge Institute
3+
# for Science and Education (ORISE), TRIAD National Security, LLC., Lawrence
4+
# Livermore National Security, LLC., The Regents of the University of
5+
# California, through Lawrence Berkeley National Laboratory, Battelle Memorial
6+
# Institute, Pacific Northwest Division through Pacific Northwest National
7+
# Laboratory, Carnegie Mellon University, West Virginia University, Boston
8+
# University, the Trustees of Princeton University, The University of Texas at
9+
# Austin, URS Energy & Construction, Inc., et al. All rights reserved.
10+
#
11+
# Please see the file LICENSE.md for full copyright and license information,
12+
# respectively. This file is also available online at the URL
13+
# "https://github.com/CCSI-Toolset/FOQUS".
14+
#################################################################################
15+
import os
16+
import tempfile
17+
import unittest
18+
from unittest.mock import Mock, patch
19+
20+
from foqus_lib.framework.uq.Common import Common
21+
22+
23+
class TestCommon(unittest.TestCase):
24+
"""Test cases for the Common class"""
25+
26+
def test_get_file_name_root(self):
27+
"""Test extracting filename root"""
28+
self.assertEqual(Common.getFileNameRoot("test.dat"), "test")
29+
self.assertEqual(Common.getFileNameRoot("/path/to/file.psuade"), "file")
30+
self.assertEqual(Common.getFileNameRoot("complex.file.name.txt"), "complex")
31+
32+
def test_get_local_filename(self):
33+
"""Test generating local filename"""
34+
result = Common.getLocalFileName("/tmp", "test.dat", "_processed")
35+
expected = "/tmp" + os.path.sep + "test_processed"
36+
self.assertEqual(result, expected)
37+
38+
@patch("os.path.exists")
39+
@patch("os.mkdir")
40+
@patch("os.listdir")
41+
@patch("os.unlink")
42+
def test_init_folder(self, mock_unlink, mock_listdir, mock_mkdir, mock_exists):
43+
"""Test folder initialization"""
44+
# Test creating new folder
45+
mock_exists.return_value = False
46+
Common.initFolder("/test/path")
47+
mock_mkdir.assert_called_once_with("/test/path")
48+
49+
# Test cleaning existing folder
50+
mock_exists.return_value = True
51+
mock_listdir.return_value = ["file1.txt", "file2.dat"]
52+
with patch("os.path.isfile", return_value=True):
53+
Common.initFolder("/test/path", deleteFiles=True)
54+
self.assertEqual(mock_unlink.call_count, 2)
55+
56+
@patch("foqus_lib.framework.uq.Common.usePyside", False)
57+
@patch("builtins.print")
58+
def test_show_error_no_pyside(self, mock_print):
59+
"""Test error display without PyQt"""
60+
Common.showError("Test error message")
61+
mock_print.assert_called_once_with("Test error message")
62+
63+
def test_get_user_regression_output_name(self):
64+
"""Test getting user regression output name"""
65+
# Create a mock data object
66+
mock_data = Mock()
67+
68+
# Test case 1: When labels contain the modified name (with underscore)
69+
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
70+
f.write("# Test regression file\n")
71+
f.write("labels = ['node_output1', 'node_output2']\n")
72+
temp_file1 = f.name
73+
74+
try:
75+
mock_data.getNamesIncludeNodes.return_value = False
76+
result = Common.getUserRegressionOutputName(
77+
"node.output1", temp_file1, mock_data
78+
)
79+
self.assertEqual(result, "node_output1") # Should return the modified name
80+
81+
# Even when getNamesIncludeNodes is True, if the modified name is found in labels,
82+
# it should still return the modified name
83+
mock_data.getNamesIncludeNodes.return_value = True
84+
result = Common.getUserRegressionOutputName(
85+
"node.output1", temp_file1, mock_data
86+
)
87+
self.assertEqual(
88+
result, "node_output1"
89+
) # Should still return modified name because it's in labels
90+
finally:
91+
os.unlink(temp_file1)
92+
93+
# Test case 2: When data has names that include nodes but labels don't match
94+
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
95+
f.write("# Test regression file\n")
96+
f.write(
97+
"labels = ['other_output1', 'other_output2']\n"
98+
) # Different labels that don't match
99+
temp_file2 = f.name
100+
101+
try:
102+
mock_data.getNamesIncludeNodes.return_value = True
103+
result = Common.getUserRegressionOutputName(
104+
"node.output1", temp_file2, mock_data
105+
)
106+
self.assertEqual(result, "output1") # Should strip the node part
107+
108+
# Test case 3: When data doesn't include nodes and no match in labels
109+
mock_data.getNamesIncludeNodes.return_value = False
110+
result = Common.getUserRegressionOutputName(
111+
"node.output1", temp_file2, mock_data
112+
)
113+
self.assertEqual(result, "node.output1") # Should return original name
114+
finally:
115+
os.unlink(temp_file2)
116+
117+
def test_get_user_regression_output_name_no_labels(self):
118+
"""Test getting user regression output name when no labels line exists"""
119+
mock_data = Mock()
120+
mock_data.getNamesIncludeNodes.return_value = True
121+
122+
# Create a temporary regression file without labels
123+
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
124+
f.write("# Test regression file\n")
125+
f.write("# No labels line here\n")
126+
temp_file = f.name
127+
128+
try:
129+
result = Common.getUserRegressionOutputName(
130+
"node.output1", temp_file, mock_data
131+
)
132+
self.assertEqual(
133+
result, "output1"
134+
) # Should strip node part when names include nodes
135+
finally:
136+
os.unlink(temp_file)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#################################################################################
2+
# FOQUS Copyright (c) 2012 - 2025, by the software owners: Oak Ridge Institute
3+
# for Science and Education (ORISE), TRIAD National Security, LLC., Lawrence
4+
# Livermore National Security, LLC., The Regents of the University of
5+
# California, through Lawrence Berkeley National Laboratory, Battelle Memorial
6+
# Institute, Pacific Northwest Division through Pacific Northwest National
7+
# Laboratory, Carnegie Mellon University, West Virginia University, Boston
8+
# University, the Trustees of Princeton University, The University of Texas at
9+
# Austin, URS Energy & Construction, Inc., et al. All rights reserved.
10+
#
11+
# Please see the file LICENSE.md for full copyright and license information,
12+
# respectively. This file is also available online at the URL
13+
# "https://github.com/CCSI-Toolset/FOQUS".
14+
#################################################################################
15+
import unittest
16+
from unittest.mock import Mock, patch
17+
18+
19+
class TestCorrelationAnalysis(unittest.TestCase):
20+
"""Test cases for CorrelationAnalysis"""
21+
22+
def setUp(self):
23+
"""Set up test correlation analysis"""
24+
from foqus_lib.framework.uq.CorrelationAnalysis import CorrelationAnalysis
25+
26+
# Mock ensemble and output
27+
self.mock_ensemble = Mock()
28+
self.mock_output = "test_output"
29+
30+
self.ca = CorrelationAnalysis(self.mock_ensemble, self.mock_output)
31+
32+
def test_initialization(self):
33+
"""Test CorrelationAnalysis initialization"""
34+
# Test that the object was created successfully
35+
self.assertIsNotNone(self.ca)
36+
37+
# Test that ensemble and outputs are set correctly
38+
self.assertEqual(self.ca.ensemble, self.mock_ensemble)
39+
self.assertEqual(self.ca.outputs, ["test_output"])
40+
41+
# Test that moments is initialized to None
42+
self.assertIsNone(self.ca.moments)
43+
44+
# If there's a way to check the analysis type, we can test it
45+
# For now, we'll just verify the object is properly instantiated
46+
47+
@patch("foqus_lib.framework.uq.CorrelationAnalysis.RawDataAnalyzer.performCA")
48+
@patch("foqus_lib.framework.uq.CorrelationAnalysis.Common.initFolder")
49+
def test_analyze(self, mock_init_folder, mock_perform_ca):
50+
"""Test correlation analysis"""
51+
# Mock valid samples
52+
mock_data = Mock()
53+
mock_data.getModelName.return_value = "test_model"
54+
mock_data.writeToPsuade = Mock()
55+
self.mock_ensemble.getValidSamples.return_value = mock_data
56+
57+
mock_perform_ca.return_value = "results.m"
58+
59+
result = self.ca.analyze()
60+
61+
mock_init_folder.assert_called_once()
62+
mock_perform_ca.assert_called_once()
63+
self.assertEqual(result, "results.m")
64+
65+
@patch("foqus_lib.framework.uq.CorrelationAnalysis.RawDataAnalyzer.plotCA")
66+
def test_show_results(self, mock_plot_ca):
67+
"""Test showing correlation analysis results"""
68+
# Mock the archiveFile and restoreFromArchive methods
69+
self.ca.archiveFile = Mock()
70+
self.ca.restoreFromArchive = Mock()
71+
72+
# Call showResults
73+
self.ca.showResults()
74+
75+
# Verify that restoreFromArchive was called with the correct filename
76+
self.ca.restoreFromArchive.assert_called_once_with("matlabca.m")
77+
78+
# Verify that plotCA was called with correct parameters
79+
mock_plot_ca.assert_called_once_with(
80+
self.mock_ensemble, "test_output", "matlabca.m"
81+
)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#################################################################################
2+
# FOQUS Copyright (c) 2012 - 2025, by the software owners: Oak Ridge Institute
3+
# for Science and Education (ORISE), TRIAD National Security, LLC., Lawrence
4+
# Livermore National Security, LLC., The Regents of the University of
5+
# California, through Lawrence Berkeley National Laboratory, Battelle Memorial
6+
# Institute, Pacific Northwest Division through Pacific Northwest National
7+
# Laboratory, Carnegie Mellon University, West Virginia University, Boston
8+
# University, the Trustees of Princeton University, The University of Texas at
9+
# Austin, URS Energy & Construction, Inc., et al. All rights reserved.
10+
#
11+
# Please see the file LICENSE.md for full copyright and license information,
12+
# respectively. This file is also available online at the URL
13+
# "https://github.com/CCSI-Toolset/FOQUS".
14+
#################################################################################
15+
import unittest
16+
17+
from foqus_lib.framework.uq.Distribution import Distribution
18+
19+
20+
class TestDistribution(unittest.TestCase):
21+
"""Test cases for the Distribution class"""
22+
23+
def test_static_methods(self):
24+
"""Test all static methods"""
25+
# Test getFullName
26+
self.assertEqual(Distribution.getFullName(Distribution.UNIFORM), "Uniform")
27+
self.assertEqual(Distribution.getFullName(Distribution.NORMAL), "Normal")
28+
self.assertEqual(Distribution.getFullName(Distribution.LOGNORMAL), "Lognormal")
29+
30+
# Test getPsuadeName
31+
self.assertEqual(Distribution.getPsuadeName(Distribution.UNIFORM), "U")
32+
self.assertEqual(Distribution.getPsuadeName(Distribution.NORMAL), "N")
33+
self.assertEqual(Distribution.getPsuadeName(Distribution.BETA), "B")
34+
35+
# Test getInfo
36+
info = Distribution.getInfo(Distribution.GAMMA)
37+
self.assertEqual(info, ("Gamma", "G"))
38+
39+
# Test getEnumValue
40+
self.assertEqual(Distribution.getEnumValue("Uniform"), Distribution.UNIFORM)
41+
self.assertEqual(Distribution.getEnumValue("N"), Distribution.NORMAL)
42+
self.assertEqual(Distribution.getEnumValue("T"), Distribution.TRIANGLE)
43+
44+
# Test getParameterNames
45+
params = Distribution.getParameterNames(Distribution.NORMAL)
46+
self.assertEqual(params, ("Mean", "Std Dev"))
47+
48+
params = Distribution.getParameterNames(Distribution.UNIFORM)
49+
self.assertEqual(params, (None, None))
50+
51+
params = Distribution.getParameterNames(Distribution.EXPONENTIAL)
52+
self.assertEqual(params, ("Lambda", None))
53+
54+
def test_distribution_creation_with_enum(self):
55+
"""Test creating distribution with enum values"""
56+
dist = Distribution(Distribution.NORMAL)
57+
self.assertEqual(dist.getDistributionType(), Distribution.NORMAL)
58+
59+
def test_distribution_creation_with_string(self):
60+
"""Test creating distribution with string values"""
61+
dist = Distribution("Normal")
62+
self.assertEqual(dist.getDistributionType(), Distribution.NORMAL)
63+
64+
dist = Distribution("L") # Lognormal
65+
self.assertEqual(dist.getDistributionType(), Distribution.LOGNORMAL)
66+
67+
def test_parameter_values(self):
68+
"""Test setting and getting parameter values"""
69+
dist = Distribution(Distribution.NORMAL)
70+
71+
# Initially None
72+
self.assertEqual(dist.getParameterValues(), (None, None))
73+
74+
# Set both parameters
75+
dist.setParameterValues(10.0, 2.5)
76+
self.assertEqual(dist.getParameterValues(), (10.0, 2.5))
77+
78+
# Set only first parameter
79+
dist.setParameterValues(15.0)
80+
self.assertEqual(dist.getParameterValues(), (15.0, None))
81+
82+
def test_repr(self):
83+
"""Test string representation"""
84+
dist = Distribution(Distribution.NORMAL)
85+
self.assertIn("N Distribution", repr(dist))
86+
87+
dist.setParameterValues(10.0, 2.0)
88+
repr_str = repr(dist)
89+
self.assertIn("N Distribution", repr_str)
90+
self.assertIn("10.0", repr_str)
91+
self.assertIn("2.0", repr_str)
92+
93+
def test_save_load_dict(self):
94+
"""Test saving and loading distribution to/from dictionary"""
95+
dist = Distribution(Distribution.GAMMA)
96+
dist.setParameterValues(2.0, 1.5)
97+
98+
# Save to dict
99+
saved = dist.saveDict()
100+
self.assertEqual(saved["type"], "G")
101+
self.assertEqual(saved["firstParamValue"], 2.0)
102+
self.assertEqual(saved["secondParamValue"], 1.5)
103+
104+
# Load from dict
105+
new_dist = Distribution(Distribution.UNIFORM) # Start with different type
106+
new_dist.loadDict(saved)
107+
self.assertEqual(new_dist.getDistributionType(), Distribution.GAMMA)
108+
self.assertEqual(new_dist.getParameterValues(), (2.0, 1.5))
109+
110+
def test_distribution_type_changes(self):
111+
"""Test changing distribution type"""
112+
dist = Distribution(Distribution.UNIFORM)
113+
self.assertEqual(dist.getDistributionType(), Distribution.UNIFORM)
114+
115+
dist.setDistributionType(Distribution.WEIBULL)
116+
self.assertEqual(dist.getDistributionType(), Distribution.WEIBULL)
117+
118+
dist.setDistributionType("Beta")
119+
self.assertEqual(dist.getDistributionType(), Distribution.BETA)

0 commit comments

Comments
 (0)