Skip to content

Commit f829387

Browse files
Update 0.4 release branch (#449)
* Add tests for new pre-release version. * Fix output for non irf failure measures and add tests. * Correctly add Ref Surface suffix
1 parent 81913b9 commit f829387

File tree

13 files changed

+104
-4612
lines changed

13 files changed

+104
-4612
lines changed

.github/workflows/ci_cd.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ jobs:
7373
# if other containers must be tested.
7474
run: |
7575
docker pull ghcr.io/ansys/pydpf-composites:${{ env.CONTAINER_TAG }}
76+
docker pull ghcr.io/ansys/pydpf-composites:2024r2_pre1
7677
docker pull ghcr.io/ansys/pydpf-composites:2024r2_pre0
7778
docker pull ghcr.io/ansys/pydpf-composites:2024r1_pre0
7879
docker pull ghcr.io/ansys/pydpf-composites:2024r1

doc/source/intro.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ By default the DPF server is started from the latest Ansys installer. To choose
112112

113113
* - Server version
114114
- ansys.dpf.composites Python module version
115+
* - 8.1 (Ansys 2024 R2 pre1)
116+
- 0.3.0 and later
115117
* - 8.0 (Ansys 2024 R2 pre0)
116118
- 0.3.0 and later
117119
* - 7.0 (Ansys 2024 R1)

examples/006_filter_composite_data_example.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@
139139
)
140140

141141
value = stress_data[selected_indices][:, component]
142-
local_result_field.append(value, element_id)
142+
# value needs to be passed as list because dpf does not support numpy
143+
# slices in the append call.
144+
local_result_field.append(value.tolist(), element_id)
143145

144146
composite_model.get_mesh().plot(result_field)
145147

poetry.lock

Lines changed: 0 additions & 4567 deletions
This file was deleted.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
55
[tool.poetry]
66
# Check https://python-poetry.org/docs/pyproject/ for all available sections
77
name = "ansys-dpf-composites"
8-
version = "0.4.0"
8+
version = "0.4.1"
99
description = "Post-processing of composite structures based on Ansys DPF"
1010
license = "MIT"
1111
authors = ["ANSYS, Inc. <[email protected]>"]
@@ -14,7 +14,7 @@ readme = "README.rst"
1414
repository = "https://github.com/ansys/pydpf-composites"
1515
documentation = "https://composites.dpf.docs.pyansys.com"
1616
classifiers = [
17-
"Development Status :: 4 - Beta",
17+
"Development Status :: 5 - Production/Stable",
1818
"Programming Language :: Python :: 3",
1919
"License :: OSI Approved :: MIT License",
2020
"Operating System :: OS Independent",

release_checklist.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
* Add docker pull for the container with the new version tag in ci_cd.yml
66
* Add pytest run for the new version in tox.ini
77
* Update the compatibility in the docs: intro.rst / Compatibility
8+
* Revert to released version of dpf core in the pyproject.toml file
89
* Follow this guide (https://dev.docs.pyansys.com/how-to/releasing.html) to create a release branch and release. Also bump version in test_metadata.py test.

src/ansys/dpf/composites/_composite_model_impl.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
from .server_helpers import (
6161
upload_continuous_fiber_composite_files_to_server,
6262
version_equal_or_later,
63+
version_older_than,
6364
)
6465
from .unit_system import get_unit_system
6566

@@ -498,14 +499,6 @@ def evaluate_failure_criteria(
498499
min_container = minmax_el_op.outputs.field_min()
499500
max_container = minmax_el_op.outputs.field_max()
500501

501-
converter_op = dpf.Operator("composite::failure_measure_converter")
502-
converter_op.inputs.fields_container(min_container)
503-
converter_op.inputs.measure_type(measure.value)
504-
converter_op.run()
505-
506-
converter_op.inputs.fields_container(max_container)
507-
converter_op.run()
508-
509502
min_merger.connect(merge_index, min_container)
510503
max_merger.connect(merge_index, max_container)
511504
merge_index = merge_index + 1
@@ -514,28 +507,47 @@ def evaluate_failure_criteria(
514507
raise RuntimeError("No output is generated! Check the scope (element and ply IDs).")
515508

516509
if self._supports_reference_surface_operators():
510+
overall_max_container = max_merger.outputs.merged_fields_container()
511+
517512
self._map_to_reference_surface_operator.inputs.min_container(
518513
min_merger.outputs.merged_fields_container()
519514
)
520-
self._map_to_reference_surface_operator.inputs.max_container(
521-
max_merger.outputs.merged_fields_container()
515+
self._map_to_reference_surface_operator.inputs.max_container(overall_max_container)
516+
517+
ref_surface_max_container = (
518+
self._map_to_reference_surface_operator.outputs.max_container()
522519
)
523520

524-
if measure == FailureMeasureEnum.INVERSE_RESERVE_FACTOR:
525-
return _merge_containers(
526-
max_merger.outputs.merged_fields_container(),
527-
self._map_to_reference_surface_operator.outputs.max_container(),
528-
)
529-
else:
530-
return _merge_containers(
531-
min_merger.outputs.merged_fields_container(),
532-
self._map_to_reference_surface_operator.outputs.min_container(),
533-
)
521+
converter_op = dpf.Operator("composite::failure_measure_converter")
522+
converter_op.inputs.measure_type(measure.value)
523+
converter_op.inputs.fields_container(overall_max_container)
524+
converter_op.run()
525+
converter_op.inputs.fields_container(ref_surface_max_container)
526+
converter_op.run()
527+
528+
if version_older_than(self._server, "8.2"):
529+
# For versions before 8.2, the Reference Surface suffix
530+
# is not correctly preserved by the failure_measure_converter
531+
# We add the suffix manually here.
532+
for field in ref_surface_max_container:
533+
if (
534+
field.name.startswith("IRF")
535+
or field.name.startswith("SF")
536+
or field.name.startswith("SM")
537+
):
538+
assert not field.name.endswith(REF_SURFACE_NAME)
539+
# Set name in field definition, because setting
540+
# the name directly is not supported for older dpf versions
541+
field_definition = field.field_definition
542+
field_definition.name = field_definition.name + " " + REF_SURFACE_NAME
543+
544+
return _merge_containers(overall_max_container, ref_surface_max_container)
534545
else:
535-
if measure == FailureMeasureEnum.INVERSE_RESERVE_FACTOR:
536-
return max_merger.outputs.merged_fields_container()
537-
else:
538-
return min_merger.outputs.merged_fields_container()
546+
converter_op = dpf.Operator("composite::failure_measure_converter")
547+
converter_op.inputs.measure_type(measure.value)
548+
converter_op.inputs.fields_container(max_merger.outputs.merged_fields_container())
549+
converter_op.run()
550+
return max_container
539551

540552
@_deprecated_composite_definition_label
541553
def get_sampling_point(

src/ansys/dpf/composites/_composite_model_impl_2023r2.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,7 @@ def evaluate_failure_criteria(
336336

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

339-
if measure == FailureMeasureEnum.INVERSE_RESERVE_FACTOR:
340-
return failure_operator.outputs.fields_containerMax()
341-
else:
342-
return failure_operator.outputs.fields_containerMin()
339+
return failure_operator.outputs.fields_containerMax()
343340

344341
def get_sampling_point(
345342
self,

src/ansys/dpf/composites/server_helpers/_versions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class _DpfVersionInfo:
4545
"DPF Composites: reference surface support and \
4646
section data from RST",
4747
),
48+
"8.2": _DpfVersionInfo(
49+
"8.2",
50+
"2024 R2 pre 2",
51+
"DPF Composites: Failure measure conversion preserves Reference Surface suffix",
52+
),
4853
}
4954

5055

tests/composite_model_test.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,29 @@ def check_field_size(failure_label: FailureOutput):
177177
check_field_size(FailureOutput.MAX_SOLID_ELEMENT_ID)
178178

179179

180-
def test_assembly_model(dpf_server):
180+
_MAX_RESERVE_FACTOR = 1000.0
181+
182+
183+
def _get_margin_of_safety_from_irf(irf: float) -> float:
184+
return _get_reserve_factor_from_irf(irf) - 1.0
185+
186+
187+
def _get_reserve_factor_from_irf(irf: float) -> float:
188+
if irf > 0.0:
189+
return min(1.0 / irf, _MAX_RESERVE_FACTOR)
190+
else:
191+
return _MAX_RESERVE_FACTOR
192+
193+
194+
@pytest.mark.parametrize(
195+
"failure_measure",
196+
[
197+
FailureMeasureEnum.INVERSE_RESERVE_FACTOR,
198+
FailureMeasureEnum.RESERVE_FACTOR,
199+
FailureMeasureEnum.MARGIN_OF_SAFETY,
200+
],
201+
)
202+
def test_assembly_model(dpf_server, failure_measure):
181203
"""Verify the handling of assemblies."""
182204

183205
timer = Timer()
@@ -201,16 +223,26 @@ def test_assembly_model(dpf_server):
201223
failure_output = composite_model.evaluate_failure_criteria(
202224
combined_criterion=combined_failure_criterion,
203225
composite_scope=CompositeScope(),
226+
measure=failure_measure,
204227
)
205228

206229
timer.add("After get failure output")
207230

208-
def check_output(failure_label: FailureOutput, expected_output: dict[int, float]):
231+
def check_value_output(failure_label: FailureOutput, expected_output: dict[int, float]):
209232
for element_id, expected_value in expected_output.items():
233+
if failure_measure == FailureMeasureEnum.MARGIN_OF_SAFETY:
234+
expected_value = _get_margin_of_safety_from_irf(expected_value)
235+
elif failure_measure == FailureMeasureEnum.RESERVE_FACTOR:
236+
expected_value = _get_reserve_factor_from_irf(expected_value)
210237
failure_field = failure_output.get_field({FAILURE_LABEL: failure_label})
211238
assert failure_field.get_entity_data_by_id(element_id) == pytest.approx(expected_value)
212239

213-
expected_output = {
240+
def check_mode_or_layer_output(failure_label: FailureOutput, expected_output: dict[int, float]):
241+
for element_id, expected_value in expected_output.items():
242+
failure_field = failure_output.get_field({FAILURE_LABEL: failure_label})
243+
assert failure_field.get_entity_data_by_id(element_id) == pytest.approx(expected_value)
244+
245+
expected_irf_output = {
214246
1: 1.11311715,
215247
2: 1.11311715,
216248
5: 1.85777034,
@@ -220,7 +252,7 @@ def check_output(failure_label: FailureOutput, expected_output: dict[int, float]
220252
9: 0.62122959,
221253
10: 0.62122959,
222254
}
223-
check_output(FailureOutput.FAILURE_VALUE, expected_output)
255+
check_value_output(FailureOutput.FAILURE_VALUE, expected_irf_output)
224256

225257
expected_modes = {
226258
1: FailureModeEnum.s1t.value,
@@ -232,7 +264,7 @@ def check_output(failure_label: FailureOutput, expected_output: dict[int, float]
232264
9: FailureModeEnum.s2t.value,
233265
10: FailureModeEnum.s2t.value,
234266
}
235-
check_output(FailureOutput.FAILURE_MODE, expected_modes)
267+
check_mode_or_layer_output(FailureOutput.FAILURE_MODE, expected_modes)
236268

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

254-
check_output(FailureOutput.MAX_LAYER_INDEX, expected_layer_index)
286+
check_mode_or_layer_output(FailureOutput.MAX_LAYER_INDEX, expected_layer_index)
255287

256-
expected_output_ref_surface = {
288+
expected_output_irf_ref_surface = {
257289
1: 1.85777034,
258290
2: 1.85777034,
259291
3: 1.11311715,
260292
4: 1.11311715,
261293
}
262294

263295
if version_equal_or_later(dpf_server, "8.0"):
264-
check_output(FailureOutput.FAILURE_VALUE_REF_SURFACE, expected_output_ref_surface)
296+
check_value_output(FailureOutput.FAILURE_VALUE_REF_SURFACE, expected_output_irf_ref_surface)
265297

266298
expected_output_local_layer = {
267299
1: 2,
268300
2: 2,
269301
3: 2,
270302
4: 2,
271303
}
272-
check_output(FailureOutput.MAX_LOCAL_LAYER_IN_ELEMENT, expected_output_local_layer)
304+
check_mode_or_layer_output(
305+
FailureOutput.MAX_LOCAL_LAYER_IN_ELEMENT, expected_output_local_layer
306+
)
273307

274308
expected_output_global_layer = {
275309
1: 2,
276310
2: 2,
277311
3: 2,
278312
4: 2,
279313
}
280-
check_output(FailureOutput.MAX_GLOBAL_LAYER_IN_STACK, expected_output_global_layer)
314+
check_mode_or_layer_output(
315+
FailureOutput.MAX_GLOBAL_LAYER_IN_STACK, expected_output_global_layer
316+
)
281317

282318
expected_output_solid_element = {
283319
1: 5,
284320
2: 6,
285321
3: 1,
286322
4: 2,
287323
}
288-
check_output(FailureOutput.MAX_SOLID_ELEMENT_ID, expected_output_solid_element)
324+
check_mode_or_layer_output(
325+
FailureOutput.MAX_SOLID_ELEMENT_ID, expected_output_solid_element
326+
)
289327

290328
property_dict = composite_model.get_constant_property_dict(
291329
[MaterialProperty.Stress_Limits_Xt], composite_definition_label=solid_label

0 commit comments

Comments
 (0)