Skip to content

Commit 4737943

Browse files
Revathyvenugopal162pyansys-ci-botpre-commit-ci[bot]
authored
feat: enable contact card options (#873)
Co-authored-by: pyansys-ci-bot <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent bfa2da0 commit 4737943

File tree

3 files changed

+192
-10
lines changed

3 files changed

+192
-10
lines changed

doc/changelog/873.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enable contact card options

src/ansys/dyna/core/lib/option_card.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,36 @@ def active(self) -> bool:
188188
@active.setter
189189
def active(self, value: bool) -> None:
190190
option_spec: OptionSpec = self._options_api.get_option_spec(self._name)
191+
191192
if value:
192193
self._options_api.activate_option(self._name)
193-
# deactivate all other options with the same card order and title order
194-
# since they will be mutually exclusive
195-
for any_option_spec in self._options_api.option_specs:
196-
if any_option_spec.name == self._name:
197-
continue
198-
if (
199-
any_option_spec.title_order == option_spec.title_order
200-
and any_option_spec.card_order == option_spec.card_order
201-
):
202-
self._options_api.deactivate_option(any_option_spec.name)
194+
195+
# Determine if we should use cascading activation based on card order
196+
# If title_order exists (not None/0), use title-based mutual exclusion
197+
# If only card_order exists, use card_order-based logic
198+
if option_spec.title_order:
199+
# Title-based behavior: deactivate mutually exclusive options
200+
for any_option_spec in self._options_api.option_specs:
201+
if any_option_spec.name == self._name:
202+
continue
203+
if (
204+
any_option_spec.title_order == option_spec.title_order
205+
and any_option_spec.card_order == option_spec.card_order
206+
):
207+
self._options_api.deactivate_option(any_option_spec.name)
208+
else:
209+
# Card order-based logic
210+
current_card_order = option_spec.card_order
211+
for any_option_spec in self._options_api.option_specs:
212+
if any_option_spec.name == self._name:
213+
continue
214+
if any_option_spec.title_order == 0: # Only affect options without title_order
215+
if any_option_spec.card_order == current_card_order:
216+
# Same card_order: mutually exclusive
217+
self._options_api.deactivate_option(any_option_spec.name)
218+
elif any_option_spec.card_order < current_card_order:
219+
# Lower card_order: cascading activation (prerequisite)
220+
self._options_api.activate_option(any_option_spec.name)
203221
else:
204222
self._options_api.deactivate_option(self._name)
205223

tests/test_contact_options.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
from ansys.dyna.core.keywords.keyword_classes.auto.contact_automatic_single_surface import ContactAutomaticSingleSurface
2+
from ansys.dyna.core.keywords.keyword_classes.auto.contact_automatic_single_surface_mortar import ContactAutomaticSingleSurfaceMortar
3+
from ansys.dyna.core.keywords.keyword_classes.auto.contact_automatic_single_surface_tiebreak import ContactAutomaticSingleSurfaceTiebreak
4+
import pytest
5+
from ansys.dyna.core.lib.option_card import Options
6+
7+
@pytest.mark.keywords
8+
def test_contact_automatic_single_surface_option_dependencies():
9+
"""Test basic option dependencies for ContactAutomaticSingleSurface"""
10+
# Create instance of keyword
11+
keyword = ContactAutomaticSingleSurface()
12+
13+
# Wrap with Options interface
14+
options = Options(keyword)
15+
16+
# Start by activating "D"
17+
options["D"].active = True
18+
19+
# Assert cascading dependencies
20+
assert options["D"].active is True
21+
assert options["C"].active is True
22+
assert options["B"].active is True
23+
assert options["A"].active is True
24+
25+
# Other options should not be active
26+
assert options["E"].active is False
27+
assert options["F"].active is False
28+
assert options["G"].active is False
29+
30+
# Activate G, all others should be activated
31+
options["G"].active = True
32+
33+
for opt in ["A", "B", "C", "D", "E", "F", "G"]:
34+
assert options[opt].active is True
35+
36+
37+
@pytest.mark.keywords
38+
def test_contact_option_cascading_activation():
39+
"""Test cascading activation for all contact option levels."""
40+
test_cases = [
41+
("A", ["A"]),
42+
("B", ["A", "B"]),
43+
("C", ["A", "B", "C"]),
44+
("D", ["A", "B", "C", "D"]),
45+
("E", ["A", "B", "C", "D", "E"]),
46+
("F", ["A", "B", "C", "D", "E", "F"]),
47+
("G", ["A", "B", "C", "D", "E", "F", "G"]),
48+
]
49+
50+
for option_to_activate, expected_active in test_cases:
51+
keyword = ContactAutomaticSingleSurface()
52+
options = Options(keyword)
53+
54+
options[option_to_activate].active = True
55+
56+
57+
for opt in ["A", "B", "C", "D", "E", "F", "G"]:
58+
if opt in expected_active:
59+
assert options[opt].active is True, f"Option {opt} should be active when {option_to_activate} is activated"
60+
else:
61+
assert options[opt].active is False, f"Option {opt} should not be active when {option_to_activate} is activated"
62+
63+
@pytest.mark.keywords
64+
def test_contact_option_deactivation():
65+
"""Test that options can be deactivated properly"""
66+
keyword = ContactAutomaticSingleSurface()
67+
options = Options(keyword)
68+
69+
# Activate G (which activates all)
70+
options["G"].active = True
71+
72+
# Verify all are active
73+
for opt in ["A", "B", "C", "D", "E", "F", "G"]:
74+
assert options[opt].active is True
75+
76+
# Deactivate D
77+
options["D"].active = False
78+
79+
# Only D should be deactivated
80+
assert options["D"].active is False
81+
for opt in ["A", "B", "C", "E", "F", "G"]:
82+
assert options[opt].active is True
83+
84+
85+
@pytest.mark.keywords
86+
def test_multiple_contact_card_types():
87+
"""Test that the cascading logic works for different contact card types"""
88+
contact_classes = [
89+
ContactAutomaticSingleSurface,
90+
ContactAutomaticSingleSurfaceMortar,
91+
ContactAutomaticSingleSurfaceTiebreak
92+
]
93+
94+
for contact_class in contact_classes:
95+
keyword = contact_class()
96+
options = Options(keyword)
97+
98+
# Test activating option C
99+
options["C"].active = True
100+
101+
# Should activate A, B, C but not D, E, F, G
102+
for opt in ["A", "B", "C"]:
103+
assert options[opt].active is True, f"Option {opt} should be active for {contact_class.__name__}"
104+
105+
for opt in ["D", "E", "F", "G"]:
106+
assert options[opt].active is False, f"Option {opt} should not be active for {contact_class.__name__}"
107+
108+
109+
@pytest.mark.keywords
110+
def test_contact_card_writing_with_options():
111+
"""Test that contact cards write properly with options activated"""
112+
keyword = ContactAutomaticSingleSurface()
113+
114+
# Test writing with no options
115+
output_no_options = keyword.write()
116+
assert "*CONTACT_AUTOMATIC_SINGLE_SURFACE" in output_no_options
117+
assert "sofscl" not in output_no_options # No options should be present
118+
119+
# Activate some options
120+
keyword.options["A"].active = True
121+
assert "sofscl" in keyword.write() # Should include A option
122+
assert "dtpchk" not in keyword.write() # DTPCHK should not be present without option D
123+
keyword.options["D"].active = True
124+
assert "dtpchk" in keyword.write() # DTPCHK should be present
125+
assert "sofscl" in keyword.write() # A should still be present
126+
assert "ignore" in keyword.write() # Ignore should be present option C
127+
assert "snlog" in keyword.write() # SNLOG should be present option B
128+
129+
130+
@pytest.mark.keywords
131+
def test_contact_card_option_persistence():
132+
"""Test that option states persist correctly"""
133+
keyword = ContactAutomaticSingleSurface()
134+
options = Options(keyword)
135+
136+
# Set some options
137+
options["C"].active = True
138+
139+
# Create a new Options interface to the same keyword
140+
options2 = Options(keyword)
141+
142+
# Check that the options are still active
143+
assert options2["A"].active is True
144+
assert options2["B"].active is True
145+
assert options2["C"].active is True
146+
assert options2["D"].active is False
147+
148+
149+
@pytest.mark.keywords
150+
def test_contact_card_with_parameters_and_options():
151+
"""Test contact card with both parameters and options"""
152+
keyword = ContactAutomaticSingleSurface()
153+
154+
options = Options(keyword)
155+
options["B"].active = True
156+
157+
158+
assert options["A"].active is True
159+
assert options["B"].active is True
160+
assert options["C"].active is False
161+
162+
output = keyword.write()
163+
assert "*CONTACT_AUTOMATIC_SINGLE_SURFACE" in output

0 commit comments

Comments
 (0)