Skip to content

Added unit test for project_variables and also simplified the translation unit test #1151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 11, 2025
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
4 changes: 3 additions & 1 deletion flow360/component/simulation/framework/param_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ class AssetCache(Flow360BaseModel):
use_geometry_AI: bool = pd.Field(
False, description="Flag whether user requested the use of GAI."
)
project_variables: Optional[List[UserVariable]] = pd.Field(None)
project_variables: Optional[List[UserVariable]] = pd.Field(
None, description="List of user variables that are used in all the `Expression` instances."
)

@property
def boundaries(self):
Expand Down
15 changes: 4 additions & 11 deletions flow360/component/simulation/translator/solver_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,21 +561,14 @@ def user_variable_to_udf(variable: UserVariable, input_params: SimulationParams)
raise ValueError("Constant value found in user variable.")

def _compute_coefficient_and_offset(source_unit: u.Unit, target_unit: u.Unit):
y2 = (2 * target_unit).in_units(source_unit).value
y1 = (1 * target_unit).in_units(source_unit).value
x2 = 2
x1 = 1
y2 = (2.0 * target_unit).in_units(source_unit).value
y1 = (1.0 * target_unit).in_units(source_unit).value
x2 = 2.0
x1 = 1.0

coefficient = (y2 - y1) / (x2 - x1)
offset = y1 / coefficient - x1

assert np.isclose(
123, (coefficient * (123 + offset) * source_unit).in_units(target_unit).value
)
assert np.isclose(
12, (coefficient * (12 + offset) * source_unit).in_units(target_unit).value
)

return coefficient, offset

def _get_output_unit(expression: Expression, input_params: SimulationParams):
Expand Down
67 changes: 39 additions & 28 deletions tests/simulation/test_expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1042,48 +1042,59 @@ def assert_ignore_space(expected: str, actual: str):
def test_udf_generator():
with SI_unit_system:
params = SimulationParams(
operating_condition=AerospaceCondition(
velocity_magnitude=10 * u.m / u.s,
reference_velocity_magnitude=10 * u.m / u.s,
operating_condition=LiquidOperatingCondition(
velocity_magnitude=5 * u.m / u.s,
),
private_attribute_asset_cache=AssetCache(project_length_unit=10 * u.m),
)
# Scalar output
result = user_variable_to_udf(
solution.mut.in_unit(new_name="mut_in_Imperial", new_unit="ft**2/s"), input_params=params
solution.mut.in_unit(new_name="mut_in_km", new_unit="km**2/s"), input_params=params
)
# velocity scale = 340.29400580821283 m/s, length scale = 10m, mut_scale = 3402.94 m**2/s -> 36628.94131344 *ft**2/s
pattern = r"mut_in_Imperial = \(mut \* ([\d\.]+)\);"
match = re.match(pattern, result.expression)
assert match is not None, f"Expression '{result.expression}' does not match expected pattern"
conversion_factor = float(match.group(1))
# Note: Ranged since the value depends on the OS.
assert (
36628.941 < conversion_factor < 36628.942
), f"Conversion factor {conversion_factor} outside expected range"
# velocity scale = 100 m/s, length scale = 10m, mut_scale = 1000 m**2/s -> 0.01 *km**2/s
assert result.expression == "mut_in_km = (mut * 0.001);"

# Vector output
result = user_variable_to_udf(
solution.velocity.in_unit(new_name="velocity_in_SI", new_unit="m/s"), input_params=params
)
# velocity scale = 340.29400580821283 m/s
pattern = r"velocity_in_SI\[0\] = \(solution\.velocity\[0\] \* ([\d\.]+)\); velocity_in_SI\[1\] = \(solution\.velocity\[1\] \* \1\); velocity_in_SI\[2\] = \(solution\.velocity\[2\] \* \1\);"
match = re.match(pattern, result.expression)
assert match is not None, f"Expression '{result.expression}' does not match expected pattern"
conversion_factor = float(match.group(1))
# velocity scale = 100 m/s,
assert (
340.294005 < conversion_factor < 340.2940064
), f"Conversion factor {conversion_factor} outside expected range"
result.expression
== "velocity_in_SI[0] = (solution.velocity[0] * 100.0); velocity_in_SI[1] = (solution.velocity[1] * 100.0); velocity_in_SI[2] = (solution.velocity[2] * 100.0);"
)

vel_cross_vec = UserVariable(
name="vel_cross_vec", value=math.cross(solution.velocity, [1, 2, 3] * u.cm)
).in_unit(new_unit="m*ft/s/min")
).in_unit(new_unit="m*km/s/s")
result = user_variable_to_udf(vel_cross_vec, input_params=params)
# velocity scale = 340.29400580821283 m/s --> 22795277.63562985 m*ft/s/min
pattern = r"vel_cross_vec\[0\] = \(\(\(\(solution\.velocity\[1\] \* 3\) \* 0\.001\) - \(\(solution\.velocity\[2\] \* 2\) \* 0\.001\)\) \* ([\d\.]+)\); vel_cross_vec\[1\] = \(\(\(\(solution\.velocity\[2\] \* 1\) \* 0\.001\) - \(\(solution\.velocity\[0\] \* 3\) \* 0\.001\)\) \* \1\); vel_cross_vec\[2\] = \(\(\(\(solution\.velocity\[0\] \* 2\) \* 0\.001\) - \(\(solution\.velocity\[1\] \* 1\) \* 0\.001\)\) \* \1\);"
match = re.match(pattern, result.expression)
assert match is not None, f"Expression '{result.expression}' does not match expected pattern"
conversion_factor = float(match.group(1))
assert (
22795277.635 < conversion_factor < 22795278 + 1e-8
), f"Conversion factor {conversion_factor} outside expected range"
result.expression
== "vel_cross_vec[0] = ((((solution.velocity[1] * 3) * 0.001) - ((solution.velocity[2] * 2) * 0.001)) * 10.0); vel_cross_vec[1] = ((((solution.velocity[2] * 1) * 0.001) - ((solution.velocity[0] * 3) * 0.001)) * 10.0); vel_cross_vec[2] = ((((solution.velocity[0] * 2) * 0.001) - ((solution.velocity[1] * 1) * 0.001)) * 10.0);"
)

# DOES NOT WORK
# vel_plus_vec = UserVariable(
# name="vel_cross_vec", value=solution.velocity + [1, 2, 3] * u.cm / u.ms
# ).in_unit(new_unit="cm/s")
# result = user_variable_to_udf(vel_plus_vec, input_params=params)
# print("4>>> result.expression", result.expression)


def test_project_variables():
aaa = UserVariable(name="aaa", value=solution.velocity + 12 * u.m / u.s)
with SI_unit_system:
params = SimulationParams(
operating_condition=AerospaceCondition(
velocity_magnitude=10 * u.m / u.s,
reference_velocity_magnitude=10 * u.m / u.s,
),
outputs=[
VolumeOutput(
output_fields=[
UserVariable(name="bbb", value=aaa + 14 * u.m / u.s),
]
)
],
)
assert params.private_attribute_asset_cache.project_variables == [aaa]