Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions flow360/component/simulation/outputs/output_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,33 @@ def append_component_to_output_fields(output_fields: List[str]) -> List[str]:
if field == "vorticity" and "vorticityMagnitude" not in output_fields:
output_fields_with_component.append("vorticityMagnitude")
return output_fields_with_component


# In the C++ solver, "primitiveVars" expands to DataArrays named "rho", "velocity", and "p".
# "pressure" and "velocity" individually produce DataArrays with the same names ("p" and
# "velocity"). Having both creates duplicate DataArray names in VTK output, which causes
# ParaView to fail when loading a subset of fields.
_FIELDS_SUBSUMED_BY_PRIMITIVE_VARS = {"pressure", "velocity"}


def remove_fields_subsumed_by_primitive_vars(output_fields: List[str]) -> List[str]:
"""
Remove output fields that are already included as sub-fields of ``primitiveVars``.

Must be called after :func:`append_component_to_output_fields` so that auto-appended
fields like ``velocity_magnitude`` are already in the list before ``velocity`` is removed.

Parameters:
-----------
output_fields : List[str]
The list of output fields to deduplicate.

Returns:
--------
List[str]
The deduplicated list with ``pressure`` and ``velocity`` removed when
``primitiveVars`` is present.
"""
if "primitiveVars" not in output_fields:
return output_fields
return [f for f in output_fields if f not in _FIELDS_SUBSUMED_BY_PRIMITIVE_VARS]
3 changes: 3 additions & 0 deletions flow360/component/simulation/translator/solver_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
PREDEFINED_UDF_EXPRESSIONS,
append_component_to_output_fields,
generate_predefined_udf,
remove_fields_subsumed_by_primitive_vars,
)
from flow360.component.simulation.outputs.outputs import (
AeroAcousticOutput,
Expand Down Expand Up @@ -304,6 +305,7 @@ def translate_output_fields(
output_fields.append(output_field.name)
# Filter out the UserVariable Dicts
output_fields = [item for item in output_fields if isinstance(item, str)]
output_fields = remove_fields_subsumed_by_primitive_vars(output_fields)
return {"outputFields": sorted(output_fields)}


Expand Down Expand Up @@ -532,6 +534,7 @@ def translate_volume_output(
output_fields.append(output_field.name)
# Filter out the UserVariable Dicts
output_fields = [item for item in output_fields if isinstance(item, str)]
output_fields = remove_fields_subsumed_by_primitive_vars(output_fields)
volume_output.update(
{
"outputFields": sorted(output_fields),
Expand Down
49 changes: 48 additions & 1 deletion tests/simulation/outputs/test_output_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import flow360 as fl
from flow360 import SI_unit_system, u
from flow360.component.simulation.outputs.output_fields import generate_predefined_udf
from flow360.component.simulation.outputs.output_fields import (
generate_predefined_udf,
remove_fields_subsumed_by_primitive_vars,
)


@pytest.fixture
Expand Down Expand Up @@ -137,3 +140,47 @@ def test_generate_field_udf_no_match(simulation_params):
"""Test behavior when no matching predefined expression is found."""
result = generate_predefined_udf("non_existent_field_for_udf", simulation_params)
assert result is None


class TestRemoveFieldsSubsumedByPrimitiveVars:
def test_removes_pressure_and_velocity_when_primitive_vars_present(self):
fields = ["Cp", "primitiveVars", "pressure", "velocity", "Mach"]
result = remove_fields_subsumed_by_primitive_vars(fields)
assert "primitiveVars" in result
assert "pressure" not in result
assert "velocity" not in result
assert "Cp" in result
assert "Mach" in result

def test_keeps_pressure_and_velocity_when_no_primitive_vars(self):
fields = ["pressure", "velocity", "Cp"]
result = remove_fields_subsumed_by_primitive_vars(fields)
assert result == ["pressure", "velocity", "Cp"]

def test_preserves_velocity_magnitude_when_velocity_removed(self):
fields = ["primitiveVars", "velocity", "velocity_magnitude", "pressure"]
result = remove_fields_subsumed_by_primitive_vars(fields)
assert "velocity" not in result
assert "pressure" not in result
assert "velocity_magnitude" in result
assert "primitiveVars" in result

def test_no_op_on_empty_list(self):
assert remove_fields_subsumed_by_primitive_vars([]) == []

def test_primitive_vars_only(self):
fields = ["primitiveVars"]
result = remove_fields_subsumed_by_primitive_vars(fields)
assert result == ["primitiveVars"]

def test_removes_pressure_only_when_velocity_absent(self):
fields = ["primitiveVars", "pressure", "Cp"]
result = remove_fields_subsumed_by_primitive_vars(fields)
assert "pressure" not in result
assert result == ["primitiveVars", "Cp"]

def test_removes_velocity_only_when_pressure_absent(self):
fields = ["primitiveVars", "velocity", "Mach"]
result = remove_fields_subsumed_by_primitive_vars(fields)
assert "velocity" not in result
assert result == ["primitiveVars", "Mach"]
4 changes: 0 additions & 4 deletions tests/simulation/translator/test_output_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ def volume_output_config(vel_in_km_per_hr):
"betMetrics",
"primitiveVars",
"qcriterion",
"velocity",
"velocity_in_km_per_hr",
"velocity_magnitude",
"vorticity",
Expand Down Expand Up @@ -125,7 +124,6 @@ def avg_volume_output_config(vel_in_km_per_hr):
"betMetrics",
"primitiveVars",
"qcriterion",
"velocity",
"velocity_in_km_per_hr",
"velocity_magnitude",
],
Expand Down Expand Up @@ -172,7 +170,6 @@ def test_volume_output(volume_output_config, avg_volume_output_config):
"betMetrics",
"primitiveVars",
"qcriterion",
"velocity",
"velocity_in_km_per_hr",
"velocity_magnitude",
"vorticity",
Expand All @@ -190,7 +187,6 @@ def test_volume_output(volume_output_config, avg_volume_output_config):
"betMetrics",
"primitiveVars",
"qcriterion",
"velocity",
"velocity_in_km_per_hr",
"velocity_magnitude",
],
Expand Down
Loading