Skip to content

Commit 4291841

Browse files
Merge pull request #159 from boschresearch/135-abaqus-2024
odbclient for Abaqus 2024
2 parents 7d45355 + 2353f0a commit 4291841

13 files changed

+262
-116
lines changed

Diff for: setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ unit-testing =
6666
setuptools
6767
pytest
6868
pytest-cov
69+
pytest-timeout
6970
hypothesis
7071

7172
testing =

Diff for: tools/odbclient/setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ exclude =
5757
testing =
5858
setuptools
5959
pytest
60+
pytest-timout
6061
pytest-cov
6162

6263
[options.entry_points]

Diff for: tools/odbclient/src/odbclient/odbclient.py

+32-11
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,20 @@ class OdbClient:
129129
"""
130130

131131
def __init__(self, odb_file, abaqus_bin=None, python_env_path=None):
132+
abaqus_bin = abaqus_bin or _guess_abaqus_bin()
133+
132134
self._proc = None
133-
env = os.environ
134-
env['PYTHONPATH'] = _guess_pythonpath(python_env_path)
135135

136+
env = os.environ | {"PYTHONPATH": _guess_pythonpath(python_env_path, abaqus_bin)}
136137
lock_file_exists = os.path.isfile(os.path.splitext(odb_file)[0] + '.lck')
137138

138-
abaqus_bin = abaqus_bin or _guess_abaqus_bin()
139-
140-
self._proc = sp.Popen([abaqus_bin, 'python', '-m', 'odbserver', odb_file],
141-
stdout=sp.PIPE, stdin=sp.PIPE, stderr=sp.PIPE,
142-
env=env)
139+
self._proc = sp.Popen(
140+
[abaqus_bin, 'python', '-m', 'odbserver', odb_file],
141+
stdout=sp.PIPE,
142+
stdin=sp.PIPE,
143+
stderr=sp.PIPE,
144+
env=env,
145+
)
143146

144147
if lock_file_exists:
145148
self._gulp_lock_file_warning()
@@ -491,9 +494,9 @@ def _parse_response(self):
491494
pickle_data = b''
492495
while True:
493496
line = self._proc.stdout.readline().rstrip() + b'\n'
494-
pickle_data += line
495-
if line == b'.\n':
497+
if line == b'\n':
496498
break
499+
pickle_data += line
497500
return pickle.loads(pickle_data, encoding='bytes')
498501

499502
def __del__(self):
@@ -548,21 +551,39 @@ def _guess_abaqus_bin_windows():
548551
r"C:/Program Files/SIMULIA/2020/EstProducts/win_b64/code/bin/ABQLauncher.exe",
549552
r"C:/Program Files/SIMULIA/2020/Products/win_b64/code/bin/ABQLauncher.exe",
550553
r"C:/Program Files/SIMULIA/2021/EstProducts/win_b64/code/bin/ABQLauncher.exe",
554+
r"C:/Program Files/SIMULIA/2022/EstProducts/win_b64/code/bin/SMALauncher.exe",
555+
r"C:/Program Files/SIMULIA/2023/EstProducts/win_b64/code/bin/SMALauncher.exe",
556+
r"C:/Program Files/SIMULIA/2024/EstProducts/win_b64/code/bin/SMALauncher.exe",
551557
]
552558
for guess in guesses:
553559
if os.path.exists(guess):
554560
return guess
555561
raise OSError("Could not guess abaqus binary path! Please submit as abaqus_bin parameter!")
556562

557563

558-
def _guess_pythonpath(python_env_path):
564+
def _guess_pythonpath(python_env_path, abaqus_bin):
559565
python_env_path = _guess_python_env_path(python_env_path)
560566
if python_env_path is None:
561567
raise OSError("No odbserver environment found.\n"
562568
"Please see https://github.com/boschresearch/pylife/blob/develop/tools/odbserver/README.md")
563569
if sys.platform == 'win32':
564570
return os.path.join(python_env_path, 'lib', 'site-packages')
565-
return os.path.join(python_env_path, 'lib', 'python2.7', 'site-packages')
571+
572+
python_version = _determine_server_python_version(abaqus_bin)
573+
return os.path.join(python_env_path, 'lib', f'python{python_version}', 'site-packages')
574+
575+
576+
def _determine_server_python_version(abaqus_bin):
577+
proc = sp.Popen(
578+
[abaqus_bin, 'python', '--version'],
579+
stdout=sp.PIPE,
580+
stdin=sp.PIPE,
581+
stderr=sp.PIPE,
582+
)
583+
msg = proc.stdout.readline() or proc.stderr.readline()
584+
version_string = msg.decode().split(" ")[1]
585+
return version_string[:version_string.rfind(".")]
586+
566587

567588

568589
def _guess_python_env_path(python_env_path):

Diff for: tools/odbclient/tests/beam_3d_hex_quad-2023.odb

163 KB
Binary file not shown.

Diff for: tools/odbclient/tests/beam_3d_hex_quad-2024.odb

163 KB
Binary file not shown.

Diff for: tools/odbclient/tests/history_output_test-2023.odb

542 KB
Binary file not shown.

Diff for: tools/odbclient/tests/history_output_test-2024.odb

542 KB
Binary file not shown.

Diff for: tools/odbclient/tests/test_odbclient.py

+150-29
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
need an Abaqus installation to run.
2121
"""
2222

23+
import sys
2324
import os
2425
import pytest
2526
import json
27+
import shutil
28+
from pathlib import Path
2629

2730
import numpy as np
2831
import pandas as pd
@@ -32,8 +35,32 @@
3235
from odbclient.odbclient import OdbServerError
3336

3437
@pytest.fixture
35-
def client():
36-
return odbclient.OdbClient('tests/beam_3d_hex_quad.odb')
38+
def datapath():
39+
base = os.path.dirname(__file__)
40+
41+
def join_path(filename):
42+
return os.path.join(base, filename)
43+
44+
return join_path
45+
46+
47+
@pytest.fixture(params=["2022", "2023", "2024"])
48+
def abaqus_version(request):
49+
return request.param
50+
51+
52+
@pytest.fixture
53+
def abaqus_bin(abaqus_version):
54+
if sys.platform == 'win32':
55+
return f"C:/Program Files/SIMULIA/{abaqus_version}/EstProducts/win_b64/code/bin/SMALauncher.exe"
56+
return shutil.which(f"abaqus{abaqus_version}")
57+
58+
59+
@pytest.fixture
60+
def client(datapath, abaqus_version, abaqus_bin):
61+
python_path = os.path.join(Path.home(), ".conda", "envs", f"odbserver-{abaqus_version}")
62+
odb_file = datapath(f"beam_3d_hex_quad-{abaqus_version}.odb")
63+
return odbclient.OdbClient(odb_file, abaqus_bin=abaqus_bin, python_env_path=python_path)
3764

3865

3966
def test_not_existing_odbserver_env():
@@ -54,8 +81,9 @@ def test_odbclient_invalid_instance(client):
5481
with pytest.raises(KeyError):
5582
client.node_coordinates("FOO-1-1")
5683

57-
def test_odbclient_node_coordinates(client):
58-
expected = pd.read_csv('tests/node_coordinates.csv', index_col='node_id')
84+
85+
def test_odbclient_node_coordinates(client, datapath):
86+
expected = pd.read_csv(datapath('node_coordinates.csv'), index_col='node_id')
5987
pd.testing.assert_frame_equal(client.node_coordinates('PART-1-1'), expected)
6088

6189

@@ -106,10 +134,8 @@ def test_odbclient_elset_names_invalid_instance_name(client):
106134
client.elset_names('nonexistent')
107135

108136

109-
def test_element_connectivity(client):
110-
expected = pd.read_json(
111-
'tests/connectivity.json', orient="index"
112-
)
137+
def test_element_connectivity(client, datapath):
138+
expected = pd.read_json(datapath('connectivity.json'), orient="index")
113139
expected.index.names = ["element_id"]
114140

115141
result = client.element_connectivity('PART-1-1')
@@ -141,8 +167,8 @@ def test_variable_names(client):
141167
np.testing.assert_array_equal(result, expected)
142168

143169

144-
def test_variable_stress_element_nodal(client):
145-
expected = pd.read_csv('tests/stress_element_nodal.csv', index_col=['node_id', 'element_id'])
170+
def test_variable_stress_element_nodal(client, datapath):
171+
expected = pd.read_csv(datapath('stress_element_nodal.csv'), index_col=['node_id', 'element_id'])
146172
result = client.variable('S', 'PART-1-1', 'Load', 1)
147173

148174
pd.testing.assert_frame_equal(result, expected)
@@ -160,17 +186,20 @@ def test_variable_invalid_instance_name(client):
160186
client.variable('S', 'nonexistent', 'Load', 1)
161187

162188

163-
def test_variable_stress_integration_point(client):
164-
expected = pd.read_csv('tests/stress_integration_point.csv',
165-
index_col=['element_id', 'ipoint_id'])
189+
def test_variable_stress_integration_point(client, datapath):
190+
expected = pd.read_csv(
191+
datapath('stress_integration_point.csv'), index_col=['element_id', 'ipoint_id']
192+
)
166193
result = client.variable('S', 'PART-1-1', 'Load', 1, position='INTEGRATION POINTS')
167-
result.to_csv('tests/stress_integration_point.csv')
194+
result.to_csv(datapath('stress_integration_point.csv'))
168195
pd.testing.assert_frame_equal(result, expected)
169196

170197

171-
@pytest.fixture
172-
def client_history():
173-
return odbclient.OdbClient('tests/history_output_test.odb')
198+
@pytest.fixture()
199+
def client_history(datapath, abaqus_version, abaqus_bin):
200+
python_path = os.path.join(Path.home(), ".conda", "envs", f"odbserver-{abaqus_version}")
201+
odb_file = datapath(f"history_output_test-{abaqus_version}.odb")
202+
return odbclient.OdbClient(odb_file, abaqus_bin=abaqus_bin, python_env_path=python_path)
174203

175204

176205
def test_history_region_empty(client):
@@ -202,19 +231,111 @@ def test_history_outputs(client_history):
202231
'CUR3',
203232
]
204233

234+
205235
def test_history_output_values(client_history):
206236
assert client_history.history_output_values("Step-1", 'Element ASSEMBLY.1', 'CTF1').array[1] == pytest.approx(0.09999854117631912)
207237

208238

209-
def test_history_region_description(client_history):
210-
assert (
211-
client_history.history_region_description("Step-1", 'Element ASSEMBLY.1')
212-
== "Output at assembly ASSEMBLY instance ASSEMBLY element 1"
213-
)
214-
215-
216-
def test_history_info(client_history):
217-
expected = json.loads("""
218-
{"Output at assembly ASSEMBLY instance ASSEMBLY node 1 region RP-1": {"History Outputs": ["RF1", "RF2", "RF3", "RM1", "RM2", "RM3", "U1", "U2", "U3", "UR1", "UR2", "UR3"], "History Region": "Node ASSEMBLY.1", "Steps ": ["Step-1", "Step-2"]}, "Output at assembly ASSEMBLY instance ASSEMBLY element 1": {"History Outputs": ["CTF1", "CTF2", "CTF3", "CTM1", "CTM2", "CTM3", "CU1", "CU2", "CU3", "CUR1", "CUR2", "CUR3"], "History Region": "Element ASSEMBLY.1", "Steps ": ["Step-1", "Step-2"]}, "Output at assembly ASSEMBLY instance ASSEMBLY node 2 region SET-5": {"History Outputs": ["RF1", "RF2", "RF3", "RM1", "RM2", "RM3", "U1", "U2", "U3", "UR1", "UR2", "UR3"], "History Region": "Node ASSEMBLY.2", "Steps ": ["Step-1", "Step-2"]}, "Output at assembly ASSEMBLY": {"History Outputs": ["ALLAE", "ALLCCDW", "ALLCCE", "ALLCCEN", "ALLCCET", "ALLCCSD", "ALLCCSDN", "ALLCCSDT", "ALLCD", "ALLDMD", "ALLDTI", "ALLEE", "ALLFD", "ALLIE", "ALLJD", "ALLKE", "ALLKL", "ALLPD", "ALLQB", "ALLSD", "ALLSE", "ALLVD", "ALLWK", "ETOTAL"], "History Region": "Assembly ASSEMBLY", "Steps ": ["Step-1", "Step-2"]}}
219-
""")
220-
assert client_history.history_info() == expected
239+
def test_history_region_description(client_history, abaqus_version):
240+
result = client_history.history_region_description("Step-1", 'Element ASSEMBLY.1')
241+
expected = "Output at assembly ASSEMBLY instance ASSEMBLY element 1"
242+
if abaqus_version == "2024":
243+
expected += " region _PICKEDSET21"
244+
assert result == expected
245+
246+
247+
def test_history_info(client_history, abaqus_version):
248+
expected = {
249+
"Output at assembly ASSEMBLY": {
250+
"History Outputs": [
251+
"ALLAE",
252+
"ALLCCDW",
253+
"ALLCCE",
254+
"ALLCCEN",
255+
"ALLCCET",
256+
"ALLCCSD",
257+
"ALLCCSDN",
258+
"ALLCCSDT",
259+
"ALLCD",
260+
"ALLDMD",
261+
"ALLDTI",
262+
"ALLEE",
263+
"ALLFD",
264+
"ALLIE",
265+
"ALLJD",
266+
"ALLKE",
267+
"ALLKL",
268+
"ALLPD",
269+
"ALLQB",
270+
"ALLSD",
271+
"ALLSE",
272+
"ALLVD",
273+
"ALLWK",
274+
"ETOTAL",
275+
],
276+
"History Region": "Assembly ASSEMBLY",
277+
"Steps ": ["Step-1", "Step-2"],
278+
},
279+
"Output at assembly ASSEMBLY instance ASSEMBLY node 1 region RP-1": {
280+
"History Outputs": [
281+
"RF1",
282+
"RF2",
283+
"RF3",
284+
"RM1",
285+
"RM2",
286+
"RM3",
287+
"U1",
288+
"U2",
289+
"U3",
290+
"UR1",
291+
"UR2",
292+
"UR3",
293+
],
294+
"History Region": "Node ASSEMBLY.1",
295+
"Steps ": ["Step-1", "Step-2"],
296+
},
297+
"Output at assembly ASSEMBLY instance ASSEMBLY element 1": {
298+
"History Outputs": [
299+
"CTF1",
300+
"CTF2",
301+
"CTF3",
302+
"CTM1",
303+
"CTM2",
304+
"CTM3",
305+
"CU1",
306+
"CU2",
307+
"CU3",
308+
"CUR1",
309+
"CUR2",
310+
"CUR3",
311+
],
312+
"History Region": "Element ASSEMBLY.1",
313+
"Steps ": ["Step-1", "Step-2"],
314+
},
315+
"Output at assembly ASSEMBLY instance ASSEMBLY node 2 region SET-5": {
316+
"History Outputs": [
317+
"RF1",
318+
"RF2",
319+
"RF3",
320+
"RM1",
321+
"RM2",
322+
"RM3",
323+
"U1",
324+
"U2",
325+
"U3",
326+
"UR1",
327+
"UR2",
328+
"UR3",
329+
],
330+
"History Region": "Node ASSEMBLY.2",
331+
"Steps ": ["Step-1", "Step-2"],
332+
},
333+
}
334+
if abaqus_version == "2024":
335+
expected["Output at assembly ASSEMBLY region Whole Model"] = expected.pop("Output at assembly ASSEMBLY")
336+
element_1 = expected.pop("Output at assembly ASSEMBLY instance ASSEMBLY element 1")
337+
expected["Output at assembly ASSEMBLY instance ASSEMBLY element 1 region _PICKEDSET21"] = element_1
338+
expected["Output at assembly ASSEMBLY instance ASSEMBLY element 1 region _PICKEDSET22"] = element_1
339+
340+
result = client_history.history_info()
341+
assert result == expected

0 commit comments

Comments
 (0)