Skip to content
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

Fix output for non-irf failure #446

Merged
merged 8 commits into from
Apr 18, 2024
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()

janvonrickenbach marked this conversation as resolved.
Show resolved Hide resolved
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()
janvonrickenbach marked this conversation as resolved.
Show resolved Hide resolved

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
Loading