Skip to content

Commit

Permalink
Fix output for non-irf failure (#446)
Browse files Browse the repository at this point in the history
* Fix output for non irf failure measures and add tests.
* Correctly add Ref Surface suffix
  • Loading branch information
janvonrickenbach authored Apr 18, 2024
1 parent 3612ad3 commit 9bcd733
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 39 deletions.
60 changes: 36 additions & 24 deletions src/ansys/dpf/composites/_composite_model_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from .server_helpers import (
upload_continuous_fiber_composite_files_to_server,
version_equal_or_later,
version_older_than,
)
from .unit_system import get_unit_system

Expand Down Expand Up @@ -498,14 +499,6 @@ def evaluate_failure_criteria(
min_container = minmax_el_op.outputs.field_min()
max_container = minmax_el_op.outputs.field_max()

converter_op = dpf.Operator("composite::failure_measure_converter")
converter_op.inputs.fields_container(min_container)
converter_op.inputs.measure_type(measure.value)
converter_op.run()

converter_op.inputs.fields_container(max_container)
converter_op.run()

min_merger.connect(merge_index, min_container)
max_merger.connect(merge_index, max_container)
merge_index = merge_index + 1
Expand All @@ -514,28 +507,47 @@ def evaluate_failure_criteria(
raise RuntimeError("No output is generated! Check the scope (element and ply IDs).")

if self._supports_reference_surface_operators():
overall_max_container = max_merger.outputs.merged_fields_container()

self._map_to_reference_surface_operator.inputs.min_container(
min_merger.outputs.merged_fields_container()
)
self._map_to_reference_surface_operator.inputs.max_container(
max_merger.outputs.merged_fields_container()
self._map_to_reference_surface_operator.inputs.max_container(overall_max_container)

ref_surface_max_container = (
self._map_to_reference_surface_operator.outputs.max_container()
)

if measure == FailureMeasureEnum.INVERSE_RESERVE_FACTOR:
return _merge_containers(
max_merger.outputs.merged_fields_container(),
self._map_to_reference_surface_operator.outputs.max_container(),
)
else:
return _merge_containers(
min_merger.outputs.merged_fields_container(),
self._map_to_reference_surface_operator.outputs.min_container(),
)
converter_op = dpf.Operator("composite::failure_measure_converter")
converter_op.inputs.measure_type(measure.value)
converter_op.inputs.fields_container(overall_max_container)
converter_op.run()
converter_op.inputs.fields_container(ref_surface_max_container)
converter_op.run()

if version_older_than(self._server, "8.2"):
# For versions before 8.2, the Reference Surface suffix
# is not correctly preserved by the failure_measure_converter
# We add the suffix manually here.
for field in ref_surface_max_container:
if (
field.name.startswith("IRF")
or field.name.startswith("SF")
or field.name.startswith("SM")
):
assert not field.name.endswith(REF_SURFACE_NAME)
# Set name in field definition, because setting
# the name directly is not supported for older dpf versions
field_definition = field.field_definition
field_definition.name = field_definition.name + " " + REF_SURFACE_NAME

return _merge_containers(overall_max_container, ref_surface_max_container)
else:
if measure == FailureMeasureEnum.INVERSE_RESERVE_FACTOR:
return max_merger.outputs.merged_fields_container()
else:
return min_merger.outputs.merged_fields_container()
converter_op = dpf.Operator("composite::failure_measure_converter")
converter_op.inputs.measure_type(measure.value)
converter_op.inputs.fields_container(max_merger.outputs.merged_fields_container())
converter_op.run()
return max_container

@_deprecated_composite_definition_label
def get_sampling_point(
Expand Down
5 changes: 1 addition & 4 deletions src/ansys/dpf/composites/_composite_model_impl_2023r2.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,7 @@ def evaluate_failure_criteria(

failure_operator.inputs.result_definition(rd.to_json())

if measure == FailureMeasureEnum.INVERSE_RESERVE_FACTOR:
return failure_operator.outputs.fields_containerMax()
else:
return failure_operator.outputs.fields_containerMin()
return failure_operator.outputs.fields_containerMax()

def get_sampling_point(
self,
Expand Down
5 changes: 5 additions & 0 deletions src/ansys/dpf/composites/server_helpers/_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class _DpfVersionInfo:
"DPF Composites: reference surface support and \
section data from RST",
),
"8.2": _DpfVersionInfo(
"8.2",
"2024 R2 pre 2",
"DPF Composites: Failure measure conversion preserves Reference Surface suffix",
),
}


Expand Down
60 changes: 49 additions & 11 deletions tests/composite_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,29 @@ def check_field_size(failure_label: FailureOutput):
check_field_size(FailureOutput.MAX_SOLID_ELEMENT_ID)


def test_assembly_model(dpf_server):
_MAX_RESERVE_FACTOR = 1000.0


def _get_margin_of_safety_from_irf(irf: float) -> float:
return _get_reserve_factor_from_irf(irf) - 1.0


def _get_reserve_factor_from_irf(irf: float) -> float:
if irf > 0.0:
return min(1.0 / irf, _MAX_RESERVE_FACTOR)
else:
return _MAX_RESERVE_FACTOR


@pytest.mark.parametrize(
"failure_measure",
[
FailureMeasureEnum.INVERSE_RESERVE_FACTOR,
FailureMeasureEnum.RESERVE_FACTOR,
FailureMeasureEnum.MARGIN_OF_SAFETY,
],
)
def test_assembly_model(dpf_server, failure_measure):
"""Verify the handling of assemblies."""

timer = Timer()
Expand All @@ -201,16 +223,26 @@ def test_assembly_model(dpf_server):
failure_output = composite_model.evaluate_failure_criteria(
combined_criterion=combined_failure_criterion,
composite_scope=CompositeScope(),
measure=failure_measure,
)

timer.add("After get failure output")

def check_output(failure_label: FailureOutput, expected_output: dict[int, float]):
def check_value_output(failure_label: FailureOutput, expected_output: dict[int, float]):
for element_id, expected_value in expected_output.items():
if failure_measure == FailureMeasureEnum.MARGIN_OF_SAFETY:
expected_value = _get_margin_of_safety_from_irf(expected_value)
elif failure_measure == FailureMeasureEnum.RESERVE_FACTOR:
expected_value = _get_reserve_factor_from_irf(expected_value)
failure_field = failure_output.get_field({FAILURE_LABEL: failure_label})
assert failure_field.get_entity_data_by_id(element_id) == pytest.approx(expected_value)

expected_output = {
def check_mode_or_layer_output(failure_label: FailureOutput, expected_output: dict[int, float]):
for element_id, expected_value in expected_output.items():
failure_field = failure_output.get_field({FAILURE_LABEL: failure_label})
assert failure_field.get_entity_data_by_id(element_id) == pytest.approx(expected_value)

expected_irf_output = {
1: 1.11311715,
2: 1.11311715,
5: 1.85777034,
Expand All @@ -220,7 +252,7 @@ def check_output(failure_label: FailureOutput, expected_output: dict[int, float]
9: 0.62122959,
10: 0.62122959,
}
check_output(FailureOutput.FAILURE_VALUE, expected_output)
check_value_output(FailureOutput.FAILURE_VALUE, expected_irf_output)

expected_modes = {
1: FailureModeEnum.s1t.value,
Expand All @@ -232,7 +264,7 @@ def check_output(failure_label: FailureOutput, expected_output: dict[int, float]
9: FailureModeEnum.s2t.value,
10: FailureModeEnum.s2t.value,
}
check_output(FailureOutput.FAILURE_MODE, expected_modes)
check_mode_or_layer_output(FailureOutput.FAILURE_MODE, expected_modes)

expected_layer_index = {
1: 2,
Expand All @@ -251,41 +283,47 @@ def check_output(failure_label: FailureOutput, expected_output: dict[int, float]
# at 0 instead of 1
expected_layer_index[element_id] -= 1

check_output(FailureOutput.MAX_LAYER_INDEX, expected_layer_index)
check_mode_or_layer_output(FailureOutput.MAX_LAYER_INDEX, expected_layer_index)

expected_output_ref_surface = {
expected_output_irf_ref_surface = {
1: 1.85777034,
2: 1.85777034,
3: 1.11311715,
4: 1.11311715,
}

if version_equal_or_later(dpf_server, "8.0"):
check_output(FailureOutput.FAILURE_VALUE_REF_SURFACE, expected_output_ref_surface)
check_value_output(FailureOutput.FAILURE_VALUE_REF_SURFACE, expected_output_irf_ref_surface)

expected_output_local_layer = {
1: 2,
2: 2,
3: 2,
4: 2,
}
check_output(FailureOutput.MAX_LOCAL_LAYER_IN_ELEMENT, expected_output_local_layer)
check_mode_or_layer_output(
FailureOutput.MAX_LOCAL_LAYER_IN_ELEMENT, expected_output_local_layer
)

expected_output_global_layer = {
1: 2,
2: 2,
3: 2,
4: 2,
}
check_output(FailureOutput.MAX_GLOBAL_LAYER_IN_STACK, expected_output_global_layer)
check_mode_or_layer_output(
FailureOutput.MAX_GLOBAL_LAYER_IN_STACK, expected_output_global_layer
)

expected_output_solid_element = {
1: 5,
2: 6,
3: 1,
4: 2,
}
check_output(FailureOutput.MAX_SOLID_ELEMENT_ID, expected_output_solid_element)
check_mode_or_layer_output(
FailureOutput.MAX_SOLID_ELEMENT_ID, expected_output_solid_element
)

property_dict = composite_model.get_constant_property_dict(
[MaterialProperty.Stress_Limits_Xt], composite_definition_label=solid_label
Expand Down

0 comments on commit 9bcd733

Please sign in to comment.