Skip to content
Merged
2 changes: 2 additions & 0 deletions flow360/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
SI_unit_system,
imperial_unit_system,
)
from flow360.component.simulation.user_code import UserVariable
from flow360.component.simulation.user_defined_dynamics.user_defined_dynamics import (
UserDefinedDynamic,
)
Expand Down Expand Up @@ -275,4 +276,5 @@
"StreamlineOutput",
"Transformation",
"WallRotation",
"UserVariable",
]
2 changes: 1 addition & 1 deletion flow360/component/simulation/entity_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def get_registry(self, internal_registry, **_) -> EntityRegistry:
body_group_tag = self.body_group_tag

internal_registry = self._group_entity_by_tag(
"body", self.body_group_tag, registry=internal_registry
"body", body_group_tag, registry=internal_registry
)
return internal_registry

Expand Down
24 changes: 23 additions & 1 deletion flow360/component/simulation/simulation_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from __future__ import annotations

from typing import Annotated, List, Optional, Union
from typing import Annotated, Iterable, List, Optional, Union

import pydantic as pd

Expand Down Expand Up @@ -60,6 +60,7 @@
unit_system_manager,
unyt_quantity,
)
from flow360.component.simulation.user_code import UserVariable
from flow360.component.simulation.user_defined_dynamics.user_defined_dynamics import (
UserDefinedDynamic,
)
Expand Down Expand Up @@ -194,6 +195,12 @@ def _init_no_unit_context(self, filename, file_content, **kwargs):

unit_system = model_dict.get("unit_system")

# This is ugly but needed beacuse overloading __init__ nullfies all before validators of SimulationParams...
# TODO: Move this to a before field_validator when __init__ is not overloaded
cache_key = "private_attribute_asset_cache"
if cache_key in model_dict:
SimulationParams.initialize_variable_space(model_dict[cache_key])

with UnitSystem.from_dict(**unit_system): # pylint: disable=not-context-manager
super().__init__(**model_dict)

Expand All @@ -203,6 +210,13 @@ def _init_with_unit_context(self, **kwargs):
"""
# When treating dicts the updater is skipped.
kwargs = _ParamModelBase._init_check_unit_system(**kwargs)

# This is ugly but needed beacuse overloading __init__ nullfies all before validators of SimulationParams...
# TODO: Move this to a before field_validator when __init__ is not overloaded
cache_key = "private_attribute_asset_cache"
if cache_key in kwargs:
SimulationParams.initialize_variable_space(kwargs[cache_key])

super().__init__(unit_system=unit_system_manager.current, **kwargs)

# pylint: disable=super-init-not-called
Expand Down Expand Up @@ -366,6 +380,14 @@ def convert_unit(
converted = value.in_base(unit_system=target_system)
return converted

# A bit ugly but we have no way of forcing validator call order so this is a workaround
@classmethod
def initialize_variable_space(cls, value):
if "project_variables" in value and isinstance(value["project_variables"], Iterable):
for variable_dict in value["project_variables"]:
UserVariable(name=variable_dict["name"], value=variable_dict["value"])
return value

# pylint: disable=no-self-argument
@pd.field_validator("models", mode="after")
@classmethod
Expand Down
6 changes: 5 additions & 1 deletion flow360/component/simulation/translator/solver_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
convert_tuples_to_lists,
get_global_setting_from_first_instance,
has_instance_in_list,
inline_expressions_in_dict,
preprocess_input,
remove_units_in_dict,
replace_dict_key,
Expand Down Expand Up @@ -977,7 +978,10 @@ def get_solver_json(
translated = {}
##:: Step 1: Get geometry:
if input_params.reference_geometry:
geometry = remove_units_in_dict(dump_dict(input_params.reference_geometry))
geometry = inline_expressions_in_dict(
dump_dict(input_params.reference_geometry), input_params
)
geometry = remove_units_in_dict(geometry)
translated["geometry"] = {}
if input_params.reference_geometry.area is not None:
translated["geometry"]["refArea"] = geometry["area"]
Expand Down
33 changes: 33 additions & 0 deletions flow360/component/simulation/translator/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from collections import OrderedDict
from typing import Union

import numpy as np

from flow360.component.simulation.framework.entity_base import EntityBase, EntityList
from flow360.component.simulation.framework.unique_list import UniqueItemList
from flow360.component.simulation.primitives import (
Expand All @@ -15,6 +17,7 @@
)
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.component.simulation.unit_system import LengthType
from flow360.component.simulation.user_code import Expression
from flow360.component.simulation.utils import is_exact_instance


Expand Down Expand Up @@ -141,6 +144,36 @@ def remove_units_in_dict(input_dict):
return input_dict


def inline_expressions_in_dict(input_dict, input_params):
if isinstance(input_dict, dict):
new_dict = {}
if "expression" in input_dict.keys():
expression = Expression(expression=input_dict["expression"])
evaluated = expression.evaluate(strict=False)
converted = input_params.convert_unit(evaluated, "flow360").v
new_dict = converted
return new_dict
for key, value in input_dict.items():
# For number-type fields the schema should match dimensioned unit fields
# so remove_units_in_dict should handle them correctly...
if isinstance(value, dict) and "expression" in value.keys():
expression = Expression(expression=value["expression"])
evaluated = expression.evaluate(strict=False)
converted = input_params.convert_unit(evaluated, "flow360").v
if isinstance(converted, np.ndarray):
if converted.ndim == 0:
converted = float(converted)
else:
converted = converted.tolist()
new_dict[key] = converted
else:
new_dict[key] = inline_expressions_in_dict(value, input_params)
return new_dict
if isinstance(input_dict, list):
return [inline_expressions_in_dict(item, input_params) for item in input_dict]
return input_dict


def has_instance_in_list(obj_list: list, class_type):
"""Check if a list contains an instance of a given type."""
if obj_list is not None:
Expand Down
Loading
Loading