Skip to content

feat: Expose both object and method classes for builtin commands #4340

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 5 commits into from
Aug 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
1 change: 1 addition & 0 deletions doc/changelog.d/4340.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Expose both object and method classes for builtin commands
66 changes: 44 additions & 22 deletions src/ansys/fluent/core/codegen/builtin_settingsgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@

"""Generate builtin setting classes."""

import re

from ansys.fluent.core import FluentVersion, config
from ansys.fluent.core.solver.flobject import CreatableNamedObjectMixin, NamedObject
from ansys.fluent.core.solver.flobject import (
CreatableNamedObjectMixin,
NamedObject,
_ChildNamedObjectAccessorMixin,
)
from ansys.fluent.core.solver.settings_builtin_data import DATA
from ansys.fluent.core.utils.fluent_version import all_versions

Expand All @@ -41,6 +47,13 @@ def _get_settings_root(version: str):
return settings.root


def _convert_camel_case_to_snake_case(name: str) -> str:
"""Convert CamelCase to snake_case."""
# Replace uppercase letters with lowercase and prepend an underscore
name = re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
return name


def _get_named_objects_in_path(root, path, kind):
named_objects = []
cls = root
Expand All @@ -52,6 +65,8 @@ def _get_named_objects_in_path(root, path, kind):
cls = cls.child_object_type
final_type = ""
if kind == "NamedObject":
if not issubclass(cls, (NamedObject, _ChildNamedObjectAccessorMixin)):
raise TypeError(f"{cls.__name__} is not NamedObject type.")
if issubclass(cls, CreatableNamedObjectMixin):
final_type = "Creatable"
else:
Expand All @@ -72,8 +87,11 @@ def generate(version: str):
"from ansys.fluent.core.solver.flobject import SettingsBase\n\n\n"
)
f.write("__all__ = [\n")
for name, _ in DATA.items():
for name, (kind, _) in DATA.items():
f.write(f' "{name}",\n')
if kind == "Command":
command_name = _convert_camel_case_to_snake_case(name)
f.write(f' "{command_name}",\n')
f.write("]\n\n")
for name, v in DATA.items():
kind, path = v
Expand All @@ -86,35 +104,39 @@ def generate(version: str):
if kind == "NamedObject":
kind = f"{final_type}NamedObject"
f.write(f"class {name}(_{kind}Setting):\n")
doc_kind = "command" if kind == "Command" else "setting"
doc_kind = "command object" if kind == "Command" else "setting"
f.write(f' """{name} {doc_kind}."""\n\n')
f.write(f' _db_name = "{name}"\n\n')
f.write(" def __init__(self")
for named_object in named_objects:
f.write(f", {named_object}: str")
f.write(", settings_source: SettingsBase | Solver | None = None")
if kind == "NonCreatableNamedObject":
f.write(", name: str = None")
elif kind == "CreatableNamedObject":
f.write(", name: str = None, new_instance_name: str = None")
f.write("):\n")
f.write(" super().__init__(settings_source=settings_source")
if kind == "NonCreatableNamedObject":
f.write(", name=name")
elif kind == "CreatableNamedObject":
f.write(", name=name, new_instance_name=new_instance_name")
for named_object in named_objects:
f.write(f", {named_object}={named_object}")
f.write(")\n\n")
if kind == "Command":
command_name = _convert_camel_case_to_snake_case(name)
f.write(f"class {command_name}(_{kind}Setting):\n")
f.write(f' """{command_name} command."""\n\n')
f.write(f' _db_name = "{name}"\n\n')
f.write(
" def __new__(cls, settings_source: SettingsBase | Solver | None = None, **kwargs):\n"
)
f.write(" instance = super().__new__(cls)\n")
f.write(
" instance.__init__(settings_source=settings_source, **kwargs)\n"
)
f.write(" return instance(**kwargs")
else:
f.write(" def __init__(self")
for named_object in named_objects:
f.write(f", {named_object}: str")
f.write(", settings_source: SettingsBase | Solver | None = None")
if kind == "NonCreatableNamedObject":
f.write(", name: str = None")
elif kind == "CreatableNamedObject":
f.write(", name: str = None, new_instance_name: str = None")
f.write("):\n")
f.write(" super().__init__(settings_source=settings_source")
if kind == "NonCreatableNamedObject":
f.write(", name=name")
elif kind == "CreatableNamedObject":
f.write(", name=name, new_instance_name=new_instance_name")
for named_object in named_objects:
f.write(f", {named_object}={named_object}")
f.write(")\n\n")
f.write(" return instance(**kwargs)\n\n")

with open(_PYI_FILE, "w") as f:
for version in FluentVersion:
Expand Down
1 change: 1 addition & 0 deletions src/ansys/fluent/core/launcher/fluent_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ def configure_container_dict(
environment={
"ANSYSLMD_LICENSE_FILE": license_server,
"REMOTING_PORTS": f"{container_grpc_port}/portspan=2",
"FLUENT_ALLOW_REMOTE_GRPC_CONNECTION": "1",
}
)

Expand Down
1 change: 1 addition & 0 deletions src/ansys/fluent/core/launcher/slurm_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ def _prepare(self):
launch_cmd += _build_journal_argument(
self._argvals["topy"], self._argvals["journal_file_names"]
)
launch_cmd += ' --setenv="FLUENT_ALLOW_REMOTE_GRPC_CONNECTION=1"'

logger.debug(f"Launching Fluent with command: {launch_cmd}")
proc = subprocess.Popen(launch_cmd, **kwargs)
Expand Down
6 changes: 3 additions & 3 deletions src/ansys/fluent/core/solver/settings_builtin_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ def is_root_obj(obj):


def _get_settings_obj(settings_root, builtin_settings_obj):
builtin_cls_name = builtin_settings_obj.__class__.__name__
builtin_cls_db_name = builtin_settings_obj.__class__._db_name
obj = settings_root
path = DATA[builtin_cls_name][1]
path = DATA[builtin_cls_db_name][1]
found_path = None
if isinstance(path, dict):
version = FluentVersion(obj._version)
Expand All @@ -63,7 +63,7 @@ def _get_settings_obj(settings_root, builtin_settings_obj):
found_path = p
break
if found_path is None:
raise RuntimeError(f"{builtin_cls_name} is not supported in {version}.")
raise RuntimeError(f"{builtin_cls_db_name} is not supported in {version}.")
elif isinstance(path, str):
found_path = path
comps = found_path.split(".")
Expand Down
16 changes: 2 additions & 14 deletions src/ansys/fluent/core/solver/settings_builtin_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,26 +225,14 @@
"VelocityInlet": ("NamedObject", "setup.boundary_conditions.velocity_inlet"),
"WallBoundaries": ("Singleton", "setup.boundary_conditions.wall"),
"WallBoundary": ("NamedObject", "setup.boundary_conditions.wall"),
"NonReflectingBoundaries": (
"Singleton",
{
since(FluentVersion.v241): "setup.boundary_conditions.non_reflecting_bc",
},
),
"NonReflectingBoundary": (
"NamedObject",
{
since(FluentVersion.v241): "setup.boundary_conditions.non_reflecting_bc",
},
),
"PerforatedWallBoundaries": (
"Singleton",
{
since(FluentVersion.v241): "setup.boundary_conditions.perforated_wall",
since(FluentVersion.v241): "setup.boundary_conditions.non_reflecting_bc",
},
),
"PerforatedWallBoundary": (
"NamedObject",
"Singleton",
{
since(FluentVersion.v241): "setup.boundary_conditions.perforated_wall",
},
Expand Down
27 changes: 26 additions & 1 deletion tests/test_builtin_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,31 @@ def test_builtin_settings(mixing_elbow_case_data_session):
)
== solver.parametric_studies["mixing_elbow-Solve"].design_points["Base DP"]
)
assert ReadCase(settings_source=solver) == solver.file.read_case
assert ReadData(settings_source=solver) == solver.file.read_data
assert ReadCaseData(settings_source=solver) == solver.file.read_case_data
if fluent_version >= FluentVersion.v241:
assert WriteCase(settings_source=solver) == solver.file.write_case
assert WriteData(settings_source=solver) == solver.file.write_data
assert WriteCaseData(settings_source=solver) == solver.file.write_case_data
else:
with pytest.raises(RuntimeError):
WriteCase(settings_source=solver)
with pytest.raises(RuntimeError):
WriteData(settings_source=solver)
with pytest.raises(RuntimeError):
WriteCaseData(settings_source=solver)
assert (
Initialize(settings_source=solver) == solver.solution.initialization.initialize
)
assert (
Calculate(settings_source=solver) == solver.solution.run_calculation.calculate
)
assert Iterate(settings_source=solver) == solver.solution.run_calculation.iterate
assert (
DualTimeIterate(settings_source=solver)
== solver.solution.run_calculation.dual_time_iterate
)


@pytest.mark.codegen_required
Expand Down Expand Up @@ -706,5 +731,5 @@ def test_context_manager_2(new_solver_session):
)

with using(solver):
ReadCase(file_name=import_filename)
read_case(file_name=import_filename)
assert Viscous().model() == "k-omega"
Loading