From 96a977ed10f198c63abcad8a71ac3e724c99e936 Mon Sep 17 00:00:00 2001 From: BenYuan Date: Mon, 30 Jun 2025 00:37:18 +0000 Subject: [PATCH] Deploy again --- flow360/component/simulation/services.py | 47 ++++++++++++++++- .../component/simulation/simulation_params.py | 50 +------------------ .../simulation/user_code/core/types.py | 2 +- tests/simulation/test_expressions.py | 2 +- 4 files changed, 49 insertions(+), 52 deletions(-) diff --git a/flow360/component/simulation/services.py b/flow360/component/simulation/services.py index b70198aae..e975c5a12 100644 --- a/flow360/component/simulation/services.py +++ b/flow360/component/simulation/services.py @@ -5,7 +5,7 @@ import os from enum import Enum from numbers import Number -from typing import Any, Collection, Dict, Literal, Optional, Tuple, Union +from typing import Any, Collection, Dict, Iterable, Literal, Optional, Tuple, Union import numpy as np import pydantic as pd @@ -323,6 +323,49 @@ def _insert_forward_compatibility_notice( return validation_errors +def initialize_variable_space(value: dict): + """Load all user variables from private attributes when a simulation params object is initialized""" + if "private_attribute_asset_cache" not in value.keys(): + return value + asset_cache: dict = value["private_attribute_asset_cache"] + if "project_variables" not in asset_cache.keys(): + return value + if not isinstance(asset_cache["project_variables"], Iterable): + return value + + clear_context() + + # ==== Build dependency graph and sort variables ==== + dependency_graph = DependencyGraph() + # Pad the project variables into proper schema + variable_list = [] + for var in asset_cache["project_variables"]: + if "expression" in var["value"]: + # Expression type + variable_list.append({"name": var["name"], "value": var["value"]["expression"]}) + else: + # Number type (#units ignored since it does not affect the dependency graph) + variable_list.append({"name": var["name"], "value": str(var["value"]["value"])}) + dependency_graph.load_from_list(variable_list) + sorted_variables = dependency_graph.topology_sort() + + for variable_name in sorted_variables: + variable_dict = next( + (var for var in asset_cache["project_variables"] if var["name"] == variable_name), + None, + ) + if variable_dict is None: + continue + value_or_expression = { + key: value for key, value in variable_dict["value"].items() if key != "postProcessing" + } + UserVariable( + name=variable_dict["name"], + value=value_or_expression, + ) + return value + + def validate_model( *, params_as_dict, @@ -376,7 +419,7 @@ def validate_model( updated_param_as_dict = parse_model_dict(updated_param_as_dict, globals()) - SimulationParams.initialize_variable_space(updated_param_as_dict) + initialize_variable_space(updated_param_as_dict) additional_info = ParamsValidationInfo(param_as_dict=updated_param_as_dict) with ValidationContext(levels=validation_levels_to_use, info=additional_info): diff --git a/flow360/component/simulation/simulation_params.py b/flow360/component/simulation/simulation_params.py index 396c18906..644dec16c 100644 --- a/flow360/component/simulation/simulation_params.py +++ b/flow360/component/simulation/simulation_params.py @@ -4,12 +4,11 @@ from __future__ import annotations -from typing import Annotated, Iterable, List, Optional, Union +from typing import Annotated, List, Optional, Union import pydantic as pd import unyt as u -from flow360.component.simulation.blueprint.core.dependency_graph import DependencyGraph from flow360.component.simulation.conversion import ( LIQUID_IMAGINARY_FREESTREAM_MACH, unit_converter, @@ -71,7 +70,6 @@ unit_system_manager, unyt_quantity, ) -from flow360.component.simulation.user_code.core.types import UserVariable from flow360.component.simulation.user_defined_dynamics.user_defined_dynamics import ( UserDefinedDynamic, ) @@ -380,55 +378,11 @@ def convert_unit( converted = value.in_base(unit_system=target_system) return converted - # We have no way forcing validator call order so this is a workaround - @classmethod - def initialize_variable_space(cls, value: dict): - """Load all user variables from private attributes when a simulation params object is initialized""" - if "private_attribute_asset_cache" not in value.keys(): - return value - asset_cache: dict = value["private_attribute_asset_cache"] - if "project_variables" not in asset_cache.keys(): - return value - if not isinstance(asset_cache["project_variables"], Iterable): - return value - - # ==== Build dependency graph and sort variables ==== - dependency_graph = DependencyGraph() - # Pad the project variables into proper schema - variable_list = [] - for var in asset_cache["project_variables"]: - if "expression" in var["value"]: - # Expression type - variable_list.append({"name": var["name"], "value": var["value"]["expression"]}) - else: - # Number type (#units ignored since it does not affect the dependency graph) - variable_list.append({"name": var["name"], "value": str(var["value"]["value"])}) - dependency_graph.load_from_list(variable_list) - sorted_variables = dependency_graph.topology_sort() - - for variable_name in sorted_variables: - variable_dict = next( - (var for var in asset_cache["project_variables"] if var["name"] == variable_name), - None, - ) - if variable_dict is None: - continue - value_or_expression = { - key: value - for key, value in variable_dict["value"].items() - if key != "postProcessing" - } - UserVariable( - name=variable_dict["name"], - value=value_or_expression, - ) - return value - # pylint: disable=no-self-argument @pd.field_validator("models", mode="after") @classmethod def apply_default_fluid_settings(cls, v): - """Apply default Fluid() settings if not found in models""" + """Apply default Fluid() settings if not found in mode`ls""" if v is None: v = [] assert isinstance(v, list) diff --git a/flow360/component/simulation/user_code/core/types.py b/flow360/component/simulation/user_code/core/types.py index ac894be62..3a411b5c2 100644 --- a/flow360/component/simulation/user_code/core/types.py +++ b/flow360/component/simulation/user_code/core/types.py @@ -273,7 +273,7 @@ def set_value(cls, values): if diff: raise ValueError( - f"Redeclaring user variable {values['name']} with new value: {new_value}. " + f"Redeclaring user variable '{values['name']}' with new value: {new_value}. " f"Previous value: {default_context.get(values['name'])}" ) # Call the setter diff --git a/tests/simulation/test_expressions.py b/tests/simulation/test_expressions.py index 8a2a95be6..c856a9136 100644 --- a/tests/simulation/test_expressions.py +++ b/tests/simulation/test_expressions.py @@ -980,7 +980,7 @@ def test_overwriting_project_variables(): with pytest.raises( ValueError, - match="Redeclaring user variable a with new value: 2.0. Previous value: 1.0", + match="Redeclaring user variable 'a' with new value: 2.0. Previous value: 1.0", ): UserVariable(name="a", value=2)