Skip to content

Commit

Permalink
Skip manual shell layer extraction for nodal results. Add more tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
janvonrickenbach committed Dec 10, 2024
1 parent 8564cd8 commit 787c3df
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 8 deletions.
13 changes: 10 additions & 3 deletions src/ansys/dpf/post/result_workflows/_build_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,23 @@ def _create_result_workflows(
The resulting workflows are stored in a ResultWorkflows object.
"""
force_elemental_nodal = (
create_workflow_inputs.averaging_workflow_inputs.force_elemental_nodal
)

is_nodal = (
create_workflow_inputs.averaging_workflow_inputs.location == locations.nodal
and not force_elemental_nodal
)

initial_result_wf = _create_initial_result_workflow(
name=create_workflow_inputs.base_name,
server=server,
is_nodal=is_nodal,
shell_layer=create_workflow_inputs.shell_layer,
create_operator_callable=create_operator_callable,
)

force_elemental_nodal = (
create_workflow_inputs.averaging_workflow_inputs.force_elemental_nodal
)
average_wf = _create_averaging_workflow(
location=create_workflow_inputs.averaging_workflow_inputs.location,
has_skin=create_workflow_inputs.has_skin,
Expand Down
14 changes: 9 additions & 5 deletions src/ansys/dpf/post/result_workflows/_sub_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def _create_initial_result_workflow(
name: str,
server,
shell_layer: Optional[shell_layers],
is_nodal: bool,
create_operator_callable: _CreateOperatorCallable,
):
initial_result_workflow = Workflow(server=server)
Expand All @@ -190,12 +191,15 @@ def _create_initial_result_workflow(
initial_result_workflow.set_input_name(_WfNames.shell_layer, forward_shell_layer_op)

# The next section is only needed, because the shell_layer selection does not
# work for elemental results. If elemental results are requested with a chosen
# shell layer, the shell layer is not selected and the results are split into solids
# work for elemental and elemental nodal results.
# If elemental results are requested with a chosen shell layer,
# the shell layer is not selected and the results are split into solids
# and shells. Here, we add an additional shell layer selection and merge_shell_solid
# operator to manually merge the results. If the shell layer was already selected, this
# should do nothing.
if shell_layer is not None:
# operator to manually merge the results.
# Note that we have to skip this step if the location is nodal, because
# the resulting location is wrong when the shell layer is selected again manually, after
# it was already selected by the initial result operator.
if shell_layer is not None and not is_nodal:
merge_shell_solid_fields = create_operator_callable(
name="merge::solid_shell_fields"
)
Expand Down
20 changes: 20 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,26 @@ def mixed_shell_solid_model():
)


@pytest.fixture()
def mixed_shell_solid_with_contact_model():
"""Resolve the path of the "mixed_shell_solid_with_contact" result file."""
return _download_file(
"result_files/extract_shell_layer",
"mixed_shell_solid_with_contact.rst",
True,
None,
False,
)


@pytest.fixture()
def two_cubes_contact_model():
"""Resolve the path of the "two_cubes_contact" result file."""
return _download_file(
"result_files/extract_shell_layer", "two_cubes_contact.rst", True, None, False
)


@pytest.fixture()
def complex_model():
"""Resolve the path of the "msup/plate1.rst" result file."""
Expand Down
88 changes: 88 additions & 0 deletions tests/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,22 @@ def mixed_shell_solid_simulation(mixed_shell_solid_model):
)


@fixture
def mixed_shell_solid_with_contact_simulation(mixed_shell_solid_with_contact_model):
return post.load_simulation(
data_sources=mixed_shell_solid_with_contact_model,
simulation_type=AvailableSimulationTypes.static_mechanical,
)


@fixture
def two_cubes_contact_simulation(two_cubes_contact_model):
return post.load_simulation(
data_sources=two_cubes_contact_model,
simulation_type=AvailableSimulationTypes.static_mechanical,
)


@fixture
def transient_simulation(plate_msup):
return post.load_simulation(
Expand Down Expand Up @@ -1452,6 +1468,78 @@ def test_shell_layer_extraction(
assert checked_elements == 36


@pytest.mark.parametrize(
"average_per_body",
[
False,
pytest.param(
True,
marks=pytest.mark.xfail(
reason="Failing because scopings without results"
" are not handled correctly in the current implementation."
),
),
],
)
@pytest.mark.parametrize("on_skin", [True, False])
# Note: shell_layer selection with multiple layers (e.g top/bottom) currently not working correctly
# for mixed models.
@pytest.mark.parametrize("shell_layer", [shell_layers.top, shell_layers.bottom])
@pytest.mark.parametrize("location", [locations.elemental, locations.nodal])
@pytest.mark.parametrize(
"simulation_str",
[
"two_cubes_contact_simulation",
pytest.param(
"mixed_shell_solid_with_contact_simulation",
marks=pytest.mark.xfail(
reason="Failing because scopings without results"
" are not handled correctly in the current implementation."
),
),
],
)
def test_shell_layer_extraction_contacts(
simulation_str, average_per_body, on_skin, shell_layer, location, request
):
# Test some models with contacts, because models with contacts
# result in fields without results which can cause problems in conjunction
# with shell layer extraction.
simulation = request.getfixturevalue(simulation_str)

if not SERVERS_VERSION_GREATER_THAN_OR_EQUAL_TO_9_1:
return

if average_per_body:
averaging_config = AveragingConfig(
body_defining_properties=["mat"], average_per_body=True
)
else:
averaging_config = AveragingConfig(
body_defining_properties=None, average_per_body=False
)

res = simulation._get_result(
base_name="S",
skin=on_skin,
components=["X"],
location=location,
category=ResultCategory.equivalent,
shell_layer=shell_layer,
averaging_config=averaging_config,
)

# Just do a rough comparison.
# This test is mainly to check if the
# workflow runs without errors because of
# empty fields for some materials
max_val = res.max().array[0]
if simulation_str == "two_cubes_contact_simulation":
assert max_val > 1 and max_val < 1.1
else:
assert max_val > 7.7 and max_val < 7.8


@pytest.mark.parametrize("skin", all_configuration_ids)
@pytest.mark.parametrize("result_name", ["stress", "elastic_strain", "displacement"])
@pytest.mark.parametrize("mode", [None, "principal", "equivalent"])
Expand Down

0 comments on commit 787c3df

Please sign in to comment.