From e2d89e36ac48242741a3f1ef212b85f3c4308388 Mon Sep 17 00:00:00 2001 From: Sean Pearson <93727996+seanpearsonuk@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:47:56 +0000 Subject: [PATCH] feat: wf speed (#3210) * move call out of loop * wotnot * wotnot * further optimise * Update src/ansys/fluent/core/workflow.py * Separate out classic meshing interface. * Add 'mark_as_updated'. * Update workflow access. * Update tests. * Fix and update tests. * Fix test. * Update docs. * Fix test. * Clean up __getattr__ * Update documentation. * Update doc/source/user_guide/meshing/new_meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing/new_meshing_workflows.rst * Update doc/source/user_guide/meshing/new_meshing_workflows.rst * Update doc/source/user_guide/meshing/new_meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update doc/source/user_guide/meshing/new_meshing_workflows.rst Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/session.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * Update src/ansys/fluent/core/workflow.py Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> * ci: auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Prithwish Mukherjee <109645853+prmukherj@users.noreply.github.com> Co-authored-by: Prithwish Mukherjee Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../user_guide/meshing/meshing_workflows.rst | 37 +--- .../meshing/new_meshing_workflows.rst | 57 ++++++ src/ansys/fluent/core/session.py | 4 +- src/ansys/fluent/core/workflow.py | 80 +++++---- tests/test_data_model_cache.py | 8 +- tests/test_datamodel_service.py | 6 +- tests/test_meshing_utilities.py | 1 + tests/test_meshing_workflow.py | 170 +----------------- tests/test_new_meshing_workflow.py | 121 +++++++++++++ 9 files changed, 233 insertions(+), 251 deletions(-) diff --git a/doc/source/user_guide/meshing/meshing_workflows.rst b/doc/source/user_guide/meshing/meshing_workflows.rst index 0107a8ab1f7..a97f4e987a0 100644 --- a/doc/source/user_guide/meshing/meshing_workflows.rst +++ b/doc/source/user_guide/meshing/meshing_workflows.rst @@ -830,39 +830,6 @@ Switch to solver mode Switching to solver mode is not allowed in 2D Meshing mode. -Sample use of ``CommandArguments`` ----------------------------------- -This simple example shows how to use the ``CommandArgument`` attributes and explicit -attribute access methods in a watertight geometry meshing workflow. - -.. Note:: - ``CommandArgument`` attributes are read-only. - -.. code:: python - - >>> import ansys.fluent.core as pyfluent - >>> from ansys.fluent.core import examples - - >>> import_file_name = examples.download_file("mixing_elbow.pmdb", "pyfluent/mixing_elbow") - >>> meshing = pyfluent.launch_fluent( - >>> mode=pyfluent.FluentMode.MESHING, - >>> precision=pyfluent.Precision.DOUBLE, - >>> processor_count=2 - >>> ) - >>> workflow = meshing.workflow - >>> workflow.InitializeWorkflow(WorkflowType="Watertight Geometry") - - >>> tasks = workflow.TaskObject - >>> import_geometry = tasks["Import Geometry"] - >>> import_geometry.CommandArguments() - >>> import_geometry.CommandArguments.FileName.is_read_only() - >>> import_geometry.CommandArguments.LengthUnit.is_active() - >>> import_geometry.CommandArguments.LengthUnit.allowed_values() - >>> import_geometry.CommandArguments.LengthUnit.default_value() - >>> import_geometry.CommandArguments.LengthUnit() - >>> import_geometry.CommandArguments.CadImportOptions.OneZonePer() - >>> import_geometry.CommandArguments.CadImportOptions.FeatureAngle.min() - State access ------------ You can call the ``TaskObject`` container to get its state: @@ -875,5 +842,5 @@ The ``TaskObject`` container supports dictionary semantics: .. code:: python - >>> for name, object in meshing.workflow.TaskObject.items(): - >>> print(f"Task name: {name}, state: {object()}") + >>> for name, object_dict in meshing.workflow.TaskObject.items(): + >>> print(f"Task name: {name}, state: {object_dict}") diff --git a/doc/source/user_guide/meshing/new_meshing_workflows.rst b/doc/source/user_guide/meshing/new_meshing_workflows.rst index d7aa7bff562..b384094314a 100644 --- a/doc/source/user_guide/meshing/new_meshing_workflows.rst +++ b/doc/source/user_guide/meshing/new_meshing_workflows.rst @@ -699,3 +699,60 @@ Duplicate tasks watertight.import_geometry.insertable_tasks.import_boi_geometry.insert() assert watertight.import_boi_geometry.arguments() assert watertight.import_boi_geometry_1.arguments() + + +Current meshing workflow +------------------------ +Use the ``current_workflow`` property to access an already loaded workflow. +The following example shows how to use this method. + +Current workflow +~~~~~~~~~~~~~~~~ + +.. code:: python + + meshing.current_workflow + +.. Note:: + The ``current_workflow`` property raises an attribute error when no workflow is initialized. + + +Mark as updated +--------------- +Use the ``mark_as_updated()`` method to forcefully mark a task as updated. + +.. code:: python + + watertight.import_geometry.mark_as_updated() + + +Sample use of ``arguments`` +---------------------------- +This simple example shows how to use the ``arguments`` attributes and explicit +attribute access methods in a watertight geometry meshing workflow. + +.. Note:: + The ``command_arguments()`` method is deprecated. + +.. code:: python + + >>> import ansys.fluent.core as pyfluent + >>> from ansys.fluent.core import examples + + >>> import_file_name = examples.download_file("mixing_elbow.pmdb", "pyfluent/mixing_elbow") + >>> meshing = pyfluent.launch_fluent( + >>> mode=pyfluent.FluentMode.MESHING, + >>> precision=pyfluent.Precision.DOUBLE, + >>> processor_count=2 + >>> ) + >>> watertight = meshing.watertight() + + >>> import_geometry = watertight.import_geometry + >>> import_geometry.arguments() + >>> import_geometry.arguments.file_name.is_read_only() + >>> import_geometry.arguments.length_unit.is_active() + >>> import_geometry.arguments.length_unit.allowed_values() + >>> import_geometry.arguments.length_unit.default_value() + >>> import_geometry.arguments.length_unit() + >>> import_geometry.arguments.cad_import_options.one_zone_per() + >>> import_geometry.arguments.cad_import_options.feature_angle.min() diff --git a/src/ansys/fluent/core/session.py b/src/ansys/fluent/core/session.py index a49d9a7a89f..9f10c196883 100644 --- a/src/ansys/fluent/core/session.py +++ b/src/ansys/fluent/core/session.py @@ -57,7 +57,9 @@ def __call__(self): class BaseSession: - """Instantiates a Fluent connection. + """Encapsulates a Fluent session. + + This class exposes methods for interacting with a Fluent session. Attributes ---------- diff --git a/src/ansys/fluent/core/workflow.py b/src/ansys/fluent/core/workflow.py index 88239a82c7c..e5e33cabce7 100644 --- a/src/ansys/fluent/core/workflow.py +++ b/src/ansys/fluent/core/workflow.py @@ -248,6 +248,12 @@ def get_direct_downstream_tasks(self) -> list: attr="outputs", other_attr="requiredInputs" ) + def mark_as_updated(self) -> None: + """Mark tasks in workflow as updated.""" + state = getattr(self, "state", None) + if state and "Forced-up-to-date" in state.allowed_values(): + state.set_state("Forced-up-to-date") + def tasks(self, recompute=True) -> list: """Get the ordered task list held by this task. @@ -419,6 +425,9 @@ def _get_camel_case_arg_keys(self): return camel_args def __getattr__(self, attr): + result = getattr(self._task, attr, None) + if result: + return result if self._dynamic_interface: if not attr.islower() and attr != "Arguments": raise AttributeError( @@ -433,12 +442,9 @@ def __getattr__(self, attr): else attr ) attr = camel_attr or attr - try: - result = getattr(self._task, attr) - if result: - return result - except AttributeError: - pass + result = getattr(self._task, attr, None) + if result: + return result try: return ArgumentWrapper(self, attr) except Exception as ex: @@ -659,7 +665,7 @@ def __iter__(self) -> Iterator[BaseTask]: def __getitem__(self, name): logger.debug(f"TaskContainer.__getitem__({name})") - return _makeTask(self._container, name) + return self._container._workflow.TaskObject[name] def __getattr__(self, attr): return getattr(self._task_container, attr) @@ -732,12 +738,13 @@ def update_dict(self, args: dict) -> None: """ self._assign(args, "update_dict") - def _camel_snake_arguments_map(self, input_dict): + def _camel_snake_arguments_map(self, input_dict, cmd_args=None): snake_case_state_dict = {} + cmd_args = self._task._command_arguments if cmd_args is None else cmd_args for key, val in input_dict.items(): self._snake_to_camel_map[camel_to_snake_case(key)] = key if isinstance( - getattr(self._task._command_arguments, key), + getattr(cmd_args, key), PySingletonCommandArgumentsSubItem, ): snake_case_state_dict[camel_to_snake_case(key)] = ( @@ -823,6 +830,8 @@ def _just_set_state(self, args): self._task.Arguments.set_state(args) def __getattr__(self, attr): + if self._dynamic_interface: + return getattr(self._task, attr) return getattr(self._task._command_arguments, attr) def __setattr__(self, key, value): @@ -834,7 +843,15 @@ def __setattr__(self, key, value): ) def __setitem__(self, key, value): - self._task._command_arguments.__setitem__(key, value) + if self._dynamic_interface: + getattr(self._task, key).set_state(value) + else: + self._task._command_arguments.__setitem__(key, value) + + def __getitem__(self, item): + if self._dynamic_interface: + return getattr(self._task, item).get_state() + return self._task._command_arguments.__getitem__(item) class ArgumentWrapper(PyCallableStateObject): @@ -898,6 +915,8 @@ def get_state(self, explicit_only: bool = False) -> Any: return state_dict def _get_camel_case_arg_keys(self): + if not isinstance(self(), dict): + return _args = self _camel_args = [] for arg in _args().keys(): @@ -915,7 +934,9 @@ def __getattr__(self, attr): "Camel case attribute access is not supported. " f"Try using '{camel_to_snake_case(attr)}' instead." ) - camel_attr = snake_to_camel_case(str(attr), self._get_camel_case_arg_keys()) + camel_attr = snake_to_camel_case( + str(attr), self._get_camel_case_arg_keys() or [] + ) attr = camel_attr or attr return getattr(self._arg, attr) @@ -925,7 +946,7 @@ def __setattr__(self, attr, value): else: if self._dynamic_interface: camel_attr = snake_to_camel_case( - str(attr), self._get_camel_case_arg_keys() + str(attr), self._get_camel_case_arg_keys() or [] ) attr = camel_attr or attr self.set_state({attr: value}) @@ -1259,7 +1280,6 @@ def compound_child(self, name: str): def _makeTask(command_source, name: str) -> BaseTask: task = command_source._workflow.TaskObject[name] kinds = { - None: BaseTask, # To support old fluent journals setting empty state to Task Objects "Simple": SimpleTask, "Compound Child": CompoundChild, "Compound": CompoundTask, @@ -1432,9 +1452,10 @@ def __getattr__(self, attr): ) camel_attr = snake_to_camel_case(str(attr), dir(self._workflow)) attr = camel_attr or attr - obj = self._attr_from_wrapped_workflow(attr) - if obj: - return obj + try: + return getattr(self._workflow, attr) + except AttributeError: + pass return super().__getattribute__(attr) def __setattr__(self, attr, value): @@ -1485,14 +1506,6 @@ def _task_by_id(self, task_id): workflow_state = self._workflow_state() return self._task_by_id_impl(task_id, workflow_state) - def _attr_from_wrapped_workflow(self, attr): - try: - result = getattr(self._workflow, attr) - if result: - return result - except AttributeError: - pass - def _activate_dynamic_interface(self, dynamic_interface: bool): self._dynamic_interface = dynamic_interface self._initialize_methods(dynamic_interface=dynamic_interface) @@ -1688,12 +1701,9 @@ def TaskObject(self) -> TaskContainer: def __getattr__(self, attr): """Delegate attribute lookup to the wrapped workflow object.""" - obj = self._attr_from_wrapped_workflow( - attr - ) # or self._task_with_cmd_matching_help_string(attr) - if obj: - return obj - else: + try: + return getattr(self._workflow, attr) + except AttributeError: return super().__getattribute__(attr) def __dir__(self): @@ -1707,17 +1717,9 @@ def __call__(self): """Delegate calls to the underlying workflow.""" return self._workflow() - def _attr_from_wrapped_workflow(self, attr): - try: - result = getattr(self._workflow, attr) - if result: - return result - except AttributeError: - pass - class ReadOnlyObject: - """Removes set_state() to implement read-only behaviour.""" + """Removes ``set_state()`` to implement read-only behavior.""" _unwanted_attr = ["set_state", "setState"] diff --git a/tests/test_data_model_cache.py b/tests/test_data_model_cache.py index b72b2dc439d..687fa89f638 100644 --- a/tests/test_data_model_cache.py +++ b/tests/test_data_model_cache.py @@ -212,15 +212,15 @@ def test_update_cache_internal_names_as_keys( assert cache_rules == final_cache -@pytest.mark.fluent_version(">=23.1") +@pytest.mark.fluent_version(">=23.2") @pytest.mark.codegen_required def test_get_cached_values_in_command_arguments(new_meshing_session): - new_meshing_session.workflow.InitializeWorkflow(WorkflowType="Watertight Geometry") + wt = new_meshing_session.watertight() geo_import = new_meshing_session.workflow.TaskObject["Import Geometry"] geo_import.Arguments = dict(FileName="Bob") geo_import.Arguments = dict(FileName=None) - assert "FileName" in geo_import.command_arguments() - assert geo_import.command_arguments()["FileName"] is None + assert "FileName" in wt.import_geometry.command_arguments() + assert wt.import_geometry.command_arguments()["FileName"] is None @pytest.fixture diff --git a/tests/test_datamodel_service.py b/tests/test_datamodel_service.py index 2f86a758fb1..1e80c536fea 100644 --- a/tests/test_datamodel_service.py +++ b/tests/test_datamodel_service.py @@ -163,7 +163,7 @@ def test_add_on_affected(new_meshing_session): lambda obj: data.append(True) ) assert data == [] - meshing.workflow.InitializeWorkflow(WorkflowType="Watertight Geometry") + wt = meshing.watertight() sleep(5) assert len(data) > 0 assert data[0] == True @@ -175,10 +175,10 @@ def test_add_on_affected(new_meshing_session): ) import_geom = meshing.workflow.TaskObject["Import Geometry"] assert "FileName" not in import_geom.Arguments() - assert import_geom.command_arguments()["FileName"] is None + assert wt.import_geometry.command_arguments()["FileName"] is None import_geom.Arguments = {"FileName": geom} assert import_geom.Arguments()["FileName"] == geom - assert import_geom.command_arguments()["FileName"] == geom + assert wt.import_geometry.command_arguments()["FileName"] == geom sleep(1) assert calls == [True] import_geom.Arguments = {"FileName": "dummy"} diff --git a/tests/test_meshing_utilities.py b/tests/test_meshing_utilities.py index 809f69dc91e..6291dd5f113 100644 --- a/tests/test_meshing_utilities.py +++ b/tests/test_meshing_utilities.py @@ -9,6 +9,7 @@ def pytest_approx(expected): return pytest.approx(expected=expected, rel=PYTEST_RELATIVE_TOLERANCE) +@pytest.mark.skip("Activate with: https://github.com/ansys/pyfluent/pull/3205") @pytest.mark.codegen_required @pytest.mark.nightly @pytest.mark.fluent_version(">=25.1") diff --git a/tests/test_meshing_workflow.py b/tests/test_meshing_workflow.py index 7cb0189b67c..c49b3915af7 100644 --- a/tests/test_meshing_workflow.py +++ b/tests/test_meshing_workflow.py @@ -185,122 +185,6 @@ def test_meshing_workflow_raises_exception_on_invalid_key_in_task_args_2( """ -@pytest.mark.fluent_version(">=23.1") -@pytest.mark.codegen_required -def test_command_args_datamodel_se(new_meshing_session): - session_new = new_meshing_session - w = session_new.workflow - w.InitializeWorkflow(WorkflowType="Watertight Geometry") - igt = w.TaskObject["Import Geometry"] - assert igt.arguments.CadImportOptions() - assert igt.arguments.CadImportOptions.OneZonePer() - assert igt.arguments.CadImportOptions.OneZonePer.getAttribValue("default") - - -@pytest.mark.fluent_version(">=23.1") -@pytest.mark.codegen_required -def test_command_args_including_task_object_datamodel_se(new_meshing_session): - session_new = new_meshing_session - w = session_new.workflow - w.InitializeWorkflow(WorkflowType="Watertight Geometry") - igt = w.TaskObject["Import Geometry"] - assert igt.Arguments() == {} - assert igt.arguments.CadImportOptions() - assert igt.arguments.CadImportOptions.OneZonePer() - assert igt.arguments.CadImportOptions.OneZonePer.getAttribValue("default") - - -@pytest.mark.fluent_version(">=23.1") -@pytest.mark.codegen_required -def test_attribute_query_list_types(new_meshing_session): - session_new = new_meshing_session - w = session_new.workflow - w.InitializeWorkflow(WorkflowType="Watertight Geometry") - igt = w.TaskObject["Import Geometry"] - assert ["CAD", "Mesh"] == igt.arguments.FileFormat.getAttribValue("allowedValues") - - -@pytest.mark.fluent_version(">=23.2") -@pytest.mark.codegen_required -def test_accessors_for_argument_sub_items(new_meshing_session): - session_new = new_meshing_session - - w = session_new.workflow - - w.InitializeWorkflow(WorkflowType="Watertight Geometry") - import_geom = w.TaskObject["Import Geometry"] - assert import_geom.arguments.LengthUnit.default_value() == "mm" - assert import_geom.arguments.LengthUnit.allowed_values() == [ - "m", - "cm", - "mm", - "in", - "ft", - "um", - "nm", - ] - assert import_geom.arguments.LengthUnit() == "mm" - import_geom.arguments.LengthUnit.set_state("cm") - assert import_geom.arguments.LengthUnit.get_state() == "cm" - import_geom.arguments.LengthUnit = "in" - assert import_geom.arguments.LengthUnit() == "in" - - assert not import_geom.arguments.MeshUnit.is_read_only() - assert import_geom.arguments.LengthUnit.is_active() - assert not import_geom.arguments.FileName.is_read_only() - assert not import_geom.arguments.FileName() - import_geom.arguments.FileName = "xyz.txt" - assert import_geom.arguments.FileName() == "xyz.txt" - with pytest.raises(AttributeError) as msg: - import_geom.arguments.File = "sample.txt" - assert msg.value.args[0] == "No attribute named 'File' in 'Import Geometry'." - assert not import_geom.arguments.CadImportOptions.OneZonePer.is_read_only() - - assert import_geom.arguments.CadImportOptions.OneZonePer() == "body" - import_geom.arguments.CadImportOptions.OneZonePer.set_state("face") - assert import_geom.arguments.CadImportOptions.OneZonePer() == "face" - - volume_mesh_gen = w.TaskObject["Generate the Volume Mesh"] - assert ( - volume_mesh_gen.arguments.VolumeFillControls.Type.default_value() == "Cartesian" - ) - - # Test particular to string type (allowed_values() only available in string types) - assert volume_mesh_gen.arguments.VolumeFillControls.Type.allowed_values() == [ - "Octree", - "Cartesian", - ] - feat_angle = import_geom.arguments.CadImportOptions.FeatureAngle - assert feat_angle.default_value() == 40.0 - - # Test particular to numerical type (min() only available in numerical types) - assert feat_angle.min() == 0.0 - - # Test intended to fail in numerical type (allowed_values() only available in string types) - with pytest.raises(AttributeError) as msg: - assert feat_angle.allowed_values() - assert ( - msg.value.args[0] - == "'PyNumericalCommandArgumentsSubItem' object has no attribute 'allowed_values'" - ) - - # Test intended to fail in numerical type (allowed_values() only available in string types) - with pytest.raises(AttributeError) as msg: - assert import_geom.arguments.NumParts.allowed_values() - assert ( - msg.value.args[0] - == "'PyNumericalCommandArgumentsSubItem' object has no attribute 'allowed_values'" - ) - - # Test intended to fail in string type (min() only available in numerical types) - with pytest.raises(AttributeError) as msg: - assert import_geom.arguments.LengthUnit.min() - assert ( - msg.value.args[0] - == "'PyTextualCommandArgumentsSubItem' object has no attribute 'min'" - ) - - @pytest.mark.skip("Wait for later implementation.") @pytest.mark.fluent_version(">=23.1") @pytest.mark.codegen_required @@ -321,26 +205,6 @@ def test_read_only_behaviour_of_command_arguments(new_meshing_session): assert "set_state" in dir(m().NumParts) -@pytest.mark.fluent_version(">=23.1") -@pytest.mark.codegen_required -def test_sample_use_of_command_arguments(new_meshing_session): - w = new_meshing_session.workflow - w.InitializeWorkflow(WorkflowType="Watertight Geometry") - - assert w.TaskObject["Import Geometry"].arguments.LengthUnit.allowed_values() == [ - "m", - "cm", - "mm", - "in", - "ft", - "um", - "nm", - ] - assert w.TaskObject["Import Geometry"].arguments.LengthUnit.default_value() == "mm" - w.TaskObject["Import Geometry"].Arguments = dict(LengthUnit="in") - assert w.TaskObject["Import Geometry"].arguments.LengthUnit() == "in" - - @pytest.mark.codegen_required def test_dummy_journal_data_model_methods(new_meshing_session): session_new = new_meshing_session @@ -361,38 +225,6 @@ def test_iterate_meshing_workflow_task_container(new_meshing_session): assert tasks[0].name() == "Import Geometry" -@pytest.mark.codegen_required -def test_modified_workflow(new_meshing_session): - meshing = new_meshing_session - meshing.workflow.InitializeWorkflow(WorkflowType="Watertight Geometry") - - task_object_display_names = { - "Import Geometry", - "Add Local Sizing", - "Generate the Surface Mesh", - "Describe Geometry", - "Apply Share Topology", - "Enclose Fluid Regions (Capping)", - "Update Boundaries", - "Create Regions", - "Update Regions", - "Add Boundary Layers", - "Generate the Volume Mesh", - } - - task_display_names = [] - for task in meshing.workflow.TaskObject: - task_display_names.append(task.display_name()) - - assert set(task_display_names) == task_object_display_names - - task_display_names = [] - for name, _ in meshing.workflow.TaskObject.items(): - task_display_names.append(name) - - assert set(task_display_names) == task_object_display_names - - @pytest.mark.codegen_required def test_nonexistent_attrs(new_meshing_session): meshing = new_meshing_session @@ -407,7 +239,7 @@ def test_nonexistent_attrs(new_meshing_session): def test_old_workflow_structure(new_meshing_session): meshing = new_meshing_session meshing.workflow.InitializeWorkflow(WorkflowType="Watertight Geometry") - assert meshing.workflow.TaskObject["Import Geometry"].arguments() + assert meshing.workflow.TaskObject["Import Geometry"] with pytest.raises(AttributeError) as msg: meshing.workflow.import_geometry assert ( diff --git a/tests/test_new_meshing_workflow.py b/tests/test_new_meshing_workflow.py index 9585770bba4..25586e499f2 100644 --- a/tests/test_new_meshing_workflow.py +++ b/tests/test_new_meshing_workflow.py @@ -1582,3 +1582,124 @@ def test_current_workflow(new_meshing_session): with pytest.raises(AttributeError): meshing.current_workflow.import_geometry + + +@pytest.mark.codegen_required +@pytest.mark.fluent_version(">=24.1") +def test_mark_as_updated(new_meshing_session): + meshing = new_meshing_session + + watertight = meshing.watertight() + + assert meshing.workflow.TaskObject["Import Geometry"].State() == "Out-of-date" + assert meshing.workflow.TaskObject["Describe Geometry"].State() == "Out-of-date" + assert meshing.workflow.TaskObject["Add Local Sizing"].State() == "Out-of-date" + + watertight.import_geometry.mark_as_updated() + watertight.describe_geometry.mark_as_updated() + watertight.add_local_sizing.mark_as_updated() + + assert meshing.workflow.TaskObject["Import Geometry"].State() == "Forced-up-to-date" + assert ( + meshing.workflow.TaskObject["Describe Geometry"].State() == "Forced-up-to-date" + ) + assert ( + meshing.workflow.TaskObject["Add Local Sizing"].State() == "Forced-up-to-date" + ) + + +@pytest.mark.fluent_version(">=23.2") +@pytest.mark.codegen_required +def test_accessors_for_argument_sub_items(new_meshing_session): + meshing = new_meshing_session + watertight = meshing.watertight() + + import_geom = watertight.import_geometry + assert import_geom.length_unit.default_value() == "mm" + assert import_geom.arguments.length_unit.allowed_values() == [ + "m", + "cm", + "mm", + "in", + "ft", + "um", + "nm", + ] + assert import_geom.arguments.length_unit() == "mm" + import_geom.length_unit.set_state("cm") + assert import_geom.arguments.length_unit.get_state() == "cm" + import_geom.arguments.length_unit = "in" + assert import_geom.arguments.length_unit() == "in" + import_geom.arguments["length_unit"] = "m" + assert import_geom.arguments["length_unit"] == "m" + meshing.workflow.TaskObject["Import Geometry"].Arguments = dict(LengthUnit="in") + assert import_geom.arguments.length_unit() == "in" + + assert not import_geom.arguments.mesh_unit.is_read_only() + assert import_geom.arguments.length_unit.is_active() + assert not import_geom.arguments.file_name.is_read_only() + assert not import_geom.arguments.file_name() + import_geom.arguments.file_name = "xyz.txt" + assert import_geom.arguments.file_name() == "xyz.txt" + with pytest.raises(AttributeError) as msg: + import_geom.arguments.file = "sample.txt" + assert msg.value.args[0] == "No attribute named 'file' in 'Import Geometry'." + with pytest.raises(AttributeError): + import_geom.arguments.CadImportOptions.OneZonePer = "face" + + assert import_geom.arguments.cad_import_options() + assert import_geom.arguments.cad_import_options.one_zone_per() + + assert import_geom.arguments.file_format.get_attrib_value("allowedValues") == [ + "CAD", + "Mesh", + ] + assert import_geom.arguments.file_format.allowed_values() == ["CAD", "Mesh"] + + assert not import_geom.arguments.cad_import_options.one_zone_per.is_read_only() + assert import_geom.arguments.cad_import_options.one_zone_per() == "body" + import_geom.arguments.cad_import_options.one_zone_per.set_state("face") + assert import_geom.arguments.cad_import_options.one_zone_per() == "face" + import_geom.arguments.cad_import_options.one_zone_per = "object" + assert import_geom.arguments.cad_import_options.one_zone_per() == "object" + + volume_mesh_gen = watertight.create_volume_mesh + assert ( + volume_mesh_gen.arguments.volume_fill_controls.type.default_value() + == "Cartesian" + ) + + # Test particular to string type (allowed_values() only available in string types) + assert volume_mesh_gen.arguments.volume_fill_controls.type.allowed_values() == [ + "Octree", + "Cartesian", + ] + feat_angle = import_geom.arguments.cad_import_options.feature_angle + assert feat_angle.default_value() == 40.0 + + # Test particular to numerical type (min() only available in numerical types) + assert feat_angle.min() == 0.0 + + # Test intended to fail in numerical type (allowed_values() only available in string types) + with pytest.raises(AttributeError) as msg: + assert feat_angle.allowed_values() + assert ( + msg.value.args[0] + == "'PyNumericalCommandArgumentsSubItem' object has no attribute 'allowed_values'" + ) + + # Test intended to fail in numerical type (allowed_values() only available in string types) + with pytest.raises(AttributeError) as msg: + assert import_geom.arguments.num_parts.allowed_values() + assert ( + msg.value.args[0] + == "'PyNumericalCommandArgumentsSubItem' object has no attribute 'allowed_values'" + ) + + # Test intended to fail in string type (min() only available in numerical types) + with pytest.raises(AttributeError) as msg: + assert import_geom.arguments.length_unit.min() + assert ( + msg.value.args[0] + == "'PyTextualCommandArgumentsSubItem' object has no attribute 'min'" + )