expressions/scalar_component_expr.py# ----------------------------------------------------------------------------
# CLASSES: nightly
#
# Test Case: scalar_component_expr.py
#
# Tests: Automatic construction of vector, tensor and symmetric tensor
# expressions from related scalar component variable names.
#
# Uses small 2D and 3D legacy VTK files containing deliberately
# named nodal and zonal scalar variables. The tests confirm that
# the expected aggregate expressions are created, that known
# false-positive names are not created, and that representative
# inferred vector and tensor expressions can be plotted without
# expression-evaluation errors.
#
# ChatGPT via Mark C. Miller, Sun Aug 16 2026
#
# ----------------------------------------------------------------------------
#
# Return the names of all expressions advertised in database metadata.
#
def GetExpressions(db):
md = GetMetaData(db)
exprs = {}
for i in range(md.GetExprList().GetNumExpressions()):
e = md.GetExprList().GetExpressions(i)
exprs[e.GetName()] = e.GetType()
return exprs
#
# Test that all names in expected are present and all names in forbidden
# are absent.
#
def TestExpressionNamesAndTypes(section, db, expected, forbidden):
expr_type_names = {
Expression().VectorMeshVar: "VectorMeshVar",
Expression().TensorMeshVar: "TensorMeshVar",
Expression().SymmetricTensorMeshVar: "SymmetricTensorMeshVar"
}
exprs = GetExpressions(db)
TestSection("%s - correctly defined"%section)
for name, expectedType in expected.items():
TestValueEQ("%s: expression '%s' exists" % (section, name),
name in exprs, True)
if name in exprs:
TestValueEQ("%s: expression '%s' type" % (section, name),
expr_type_names.get(exprs[name]),
expr_type_names.get(expectedType))
TestSection("%s - not accidentally defined"%section)
for name in forbidden:
TestValueEQ("%s: expression '%s' absent" % (section, name),
name in exprs, False)
#
# Exercise expression evaluation. We deliberately do not call Test() because
# the rendered image is not important here. Success means the plot can be
# added and drawn without an expression error.
#
def TestExpressionPlot(section, plotType, varName):
TestSection(section)
addResult = AddPlot(plotType, varName)
TestValueEQ("%s: AddPlot" % section, addResult, 1)
drawResult = DrawPlots()
TestValueEQ("%s: DrawPlots" % section, drawResult, 1)
DeleteAllPlots()
# ----------------------------------------------------------------------------
# 2D
# ----------------------------------------------------------------------------
def test_2d_scalar_comp_exprs():
db2d = data_path("vtk_test_data/scalar_aggregate_test_2d.vtk")
OpenDatabase(db2d)
#
# Expected automatic expressions:
#
# disp disp1, disp2
# vel vel_x, vel_y, vel_z
# v vx, vy (1-char, no sep, out of order)
# e00 e000, e001 (handle leading zeros)
# stress stress_xx, stress_xy, stress_yx, stress_yy
# strain strain.11, strain.12, strain.22
# metric metric_11, metric_21, metric_22
# q qxx, qxy, qyx, qyy (1-char, nosep, out of order)
#
# temp and zonev already exist as scalars and therefore must not be replaced
# by inferred aggregate expressions. badvec and partial are incomplete.
#
expected2d = {
"disp" : Expression().VectorMeshVar,
"vel" : Expression().VectorMeshVar,
"v" : Expression().VectorMeshVar,
"e00" : Expression().VectorMeshVar,
"stress" : Expression().TensorMeshVar,
"strain" : Expression().SymmetricTensorMeshVar,
"metric" : Expression().SymmetricTensorMeshVar,
"q" : Expression().TensorMeshVar
}
forbidden2d = (
"badvec",
"partial",
# Existing scalar names must prevent aggregate expressions of same name.
"temp",
"zonev",
# False positives produced by interpreting the final character of a
# tensor component name as a vector component.
"metric_2",
"partial_1",
"strain.1",
"stress_x",
"stress_y",
# False positives formerly produced by the no-separator tensor RE.
"metric_",
"strain.",
"stress_",
# e should be seen as e00 vector, not e0 tensor
"e0",
# gorfo.compA , gorfo_compB should not be accepted due to differing sep char
"gorfo"
)
TestExpressionNamesAndTypes("2D automatic aggregate expressions",
db2d, expected2d, forbidden2d)
TestExpressionPlot("2D inferred vector plots", "Vector", "vel")
TestExpressionPlot("2D inferred tensor plots", "Tensor", "stress")
CloseDatabase(db2d)
# ----------------------------------------------------------------------------
# 3D
# ----------------------------------------------------------------------------
def test_3d_scalar_comp_exprs():
db3d = data_path("vtk_test_data/scalar_aggregate_test_3d.vtk")
OpenDatabase(db3d)
#
# Expected automatic expressions:
#
# disp disp1, disp2, disp3
# vel vel_x, vel_y, vel_z
# v vx, vy, vz (1-char, no-sep, defined out of order)
# e00 e000, e001, e002 (handles leading zeros)
# stress complete 3x3 Cartesian tensor
# strain 6-component upper-triangular symmetric tensor
# metric 6-component lower-triangular u/v/w symmetric tensor
# q qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz (1-char, no sep, out of order)
expected3d = {
"disp" : Expression().VectorMeshVar,
"vel" : Expression().VectorMeshVar,
"v" : Expression().VectorMeshVar,
"e00" : Expression().VectorMeshVar,
"stress" : Expression().TensorMeshVar,
"strain" : Expression().SymmetricTensorMeshVar,
"metric" : Expression().SymmetricTensorMeshVar,
"q" : Expression().TensorMeshVar
}
forbidden3d = (
# Only x/y components are present; a 3D vector requires z too.
"badvec",
# Five components are insufficient for the 3D symmetric tensor.
"partial",
# Existing scalar names must win.
"temp",
"zonev",
# Vector interpretations of tensor component names.
"metric_u",
"metric_v",
"metric_w",
"partial1",
"partial2",
"strain.1",
"strain.2",
"strain.3",
"stress_x",
"stress_y",
"stress_z",
# Greedy/no-separator tensor false positives.
"metric_",
"strain.",
"stress_",
# q should be seen as tensor/matrix, not vector
"qx",
"qy",
# e should be seen as e00 vector, not e0 tensor
"e0",
# gorfo.dirI , gorfo_dirJ, gorfo.dirK should not be accepted due to differing sep char
"gorfo"
)
TestExpressionNamesAndTypes("3D automatic aggregate expressions",
db3d, expected3d, forbidden3d)
TestExpressionPlot("3D inferred vector plots", "Vector", "vel")
TestExpressionPlot("3D inferred tensor plots", "Tensor", "stress")
CloseDatabase(db3d)
test_2d_scalar_comp_exprs()
test_3d_scalar_comp_exprs()
Exit()