Skip to content

Commit

Permalink
Merge pull request #43 from boschresearch/odbclient_history_output
Browse files Browse the repository at this point in the history
Odbclient history output
  • Loading branch information
johannes-mueller authored Nov 24, 2023
2 parents f9eb3f1 + 3547a22 commit 3cad0a0
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 5 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ In this file noteworthy changes of new releases of pyLife are documented since

### New features

* Introduce `WoehlerCurve.miner_original()`
* History output for `odbclient`

* `WoehlerCurve.miner_original()`


### Breaking changes

Expand Down
93 changes: 90 additions & 3 deletions tools/odbclient/src/odbclient/odbclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,88 @@ def variable(self, variable_name, instance_name, step_name, frame_id, nset_name=
column_names = _ascii(_decode, labels)
return pd.DataFrame(values, index=index, columns=column_names)

def history_regions(self, step_name):
"""Query the history Regions of a given step.
Parameters
----------
step_name : string
The name of the step
Returns
-------
historyRegions : list of strings
The name of history regions, which are in the required step.
"""
return self._query('get_history_regions', step_name)

def history_outputs(self, step_name, history_region_name):
"""Query the history Outputs of a given step in a given history region.
Parameters
----------
step_name : string
The name of the step
history_region_name: string
The name of the history region
Returns
-------
historyOutputs : list of strings
The name of the history outputs, which are in the required step and under the required history region
"""
hisoutputs = self._query("get_history_outputs", (step_name, history_region_name))

return hisoutputs


def history_output_values(self, step_name, history_region_name, historyoutput_name):
"""Query the history Regions of a given step.
Parameters
----------
step_name : string
The name of the step
Returns
-------
historyRegions : list of strings
The name of the step the history regions are in.
"""
hisoutput_valuesx, hisoutput_valuesy = self._query("get_history_output_values", (step_name, history_region_name, historyoutput_name))
history_region_description = self._query("get_history_region_description", (step_name, history_region_name))
historyoutput_data = pd.Series(hisoutput_valuesy, index = hisoutput_valuesx, name = history_region_description + ": " + historyoutput_name)

return historyoutput_data

def history_region_description(self, step_name, history_region_name):
"""Query the description of a history Regions of a given step.
Parameters
----------
step_name : string
The name of the step
history_region_name: string
The name of the history region
Returns
-------
historyRegion_description : list of strings
The description of the history region.
"""
history_region_description = self._query("get_history_region_description", (step_name, history_region_name))
return history_region_description

def history_info(self):
"""Query all the information about the history outputs in a given odb.
Returns
-------
dictionary : ldictionary which contains history information
"""
dictionary = _decode(self._query("get_history_info"))
return dictionary

def _query(self, command, args=None):
args = _ascii(_encode, args)
self._send_command(command, args)
Expand Down Expand Up @@ -436,8 +518,13 @@ def _encode(arg):


def _decode(arg):
return arg.decode('ascii') if isinstance(arg, bytes) else arg

if isinstance(arg, bytes):
return arg.decode('ascii')
if isinstance(arg, dict):
return {_decode(key): _decode(value) for key, value in arg.items()}
if isinstance(arg, list):
return [_decode(element) for element in arg]
return arg

def _guess_abaqus_bin():
if sys.platform == 'win32':
Expand All @@ -455,7 +542,7 @@ def _guess_abaqus_bin_windows():
for guess in guesses:
if os.path.exists(guess):
return guess
return None
raise OSError("Could not guess abaqus binary path! Please submit as abaqus_bin parameter!")


def _guess_pythonpath(python_env_path):
Expand Down
Binary file modified tools/odbclient/tests/beam_3d_hex_quad.odb
Binary file not shown.
Binary file added tools/odbclient/tests/history_output_test.odb
Binary file not shown.
27 changes: 27 additions & 0 deletions tools/odbclient/tests/test_odbclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import os
import pytest
import json

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -158,3 +159,29 @@ def test_variable_stress_integration_point(client):
result = client.variable('S', 'PART-1-1', 'Load', 1, position='INTEGRATION POINTS')
result.to_csv('tests/stress_integration_point.csv')
pd.testing.assert_frame_equal(result, expected)

@pytest.fixture
def client_history():
return odbclient.OdbClient('tests/history_output_test.odb')

def test_history_region_empty(client):
assert client.history_regions("Load") == ['Assembly ASSEMBLY']

def test_history_region_non_empty(client_history):
assert client_history.history_regions("Step-1") == ['Assembly ASSEMBLY', 'Element ASSEMBLY.1', 'Node ASSEMBLY.1', 'Node ASSEMBLY.2']

def test_history_outputs(client_history):
assert client_history.history_outputs("Step-1", 'Element ASSEMBLY.1') == ['CTF1', 'CTF2', 'CTF3', 'CTM1', 'CTM2', 'CTM3', 'CU1', 'CU2', 'CU3', 'CUR1', 'CUR2', 'CUR3']

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

def test_history_region_description(client_history):
assert client_history.history_region_description("Step-1", 'Element ASSEMBLY.1') == "Output at assembly ASSEMBLY instance ASSEMBLY element 1"


def test_history_info(client_history):
expected = json.loads("""
{"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"]}}
""")
assert client_history.history_info() == expected
31 changes: 30 additions & 1 deletion tools/odbserver/odbserver/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ def __init__(self, odbfile):
"get_node_set": self.node_set,
"get_element_set": self.element_set,
"get_variable_names": self.variable_names,
"get_variable": self.variable
"get_variable": self.variable,
"get_history_regions": self.history_regions,
"get_history_outputs": self.history_outputs,
"get_history_output_values": self.history_output_values,
"get_history_region_description": self.history_region_description,
"get_history_info": self.history_info
}

def instances(self, _args):
Expand Down Expand Up @@ -102,6 +107,30 @@ def variable(self, args):
else:
_send_response(variable)

def history_regions(self, step_name):
_send_response(self._odb.history_regions(str(step_name)))

def history_outputs(self, args):
step_name, historyregion_name = args
step_name = str(step_name)
historyregion_name = str(historyregion_name)
_send_response(self._odb.history_outputs(step_name, historyregion_name))

def history_output_values(self, args):
step_name, historyregion_name, historyoutput_name = args
step_name = str(step_name)
historyregion_name = str(historyregion_name)
historyoutput_name = str(historyoutput_name)
_send_response(self._odb.history_output_values(step_name, historyregion_name, historyoutput_name))

def history_region_description(self, args):
step_name, historyregion_name = args
step_name = str(step_name)
historyregion_name = str(historyregion_name)
_send_response(self._odb.history_region_description(step_name, historyregion_name))

def history_info(self, args):
_send_response(self._odb.history_info())

def _send_response(pickle_data, numpy_arrays=None):
numpy_arrays = numpy_arrays or []
Expand Down
167 changes: 167 additions & 0 deletions tools/odbserver/odbserver/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import sys
import numpy as np
import odbAccess as ODB
import json


class OdbInterface:
Expand Down Expand Up @@ -245,6 +246,172 @@ def _instance_or_rootasm(self, instance_name):
except Exception as e:
return e

def history_regions(self, step_name):
"""Get history regions, which belongs to the given step.
Parameters
----------
step_name : Abaqus steps
It is always required.
Returns
-------
histRegions : history regions, which belong to the given step. In case of error it gives an error message.
It is a list of hist regions
"""
try:

required_step = self._odb.steps[step_name]
histRegions = required_step.historyRegions.keys()

return histRegions

except Exception as e:
return e


def history_outputs(self, step_name, historyregion_name):
"""Get history outputs, which belongs to the given step and history region.
Parameters
----------
step_name : Abaqus steps
It is always required.
historyregion_name: Abaqus history region
It is always required.
Returns
-------
history_data : history data, which belong to the given step and history region. In case of error it gives an error message.
It is a list of history outputs.
"""
try:
required_step = self._odb.steps[step_name]

history_data = required_step.historyRegions[historyregion_name].historyOutputs.keys()

return history_data

except Exception as e:
return e


def history_output_values(self, step_name, historyregion_name, historyoutput_name):
"""Get history output values, which belongs to the given step, history region and history output.
Parameters
----------
step_name : Abaqus steps
It is always required.
historyregion_name: Abaqus history region
It is always required.
historyoutput_name: Abaqus history output
It is always required.
Returns
-------
x : time values of a history output. In case of error it gives an error message.
It is a list of data.
y : values of a history output. In case of error it gives an error message.
It is a list of data.
"""
try:
required_step = self._odb.steps[step_name]

history_data = required_step.historyRegions[historyregion_name].historyOutputs[historyoutput_name].data

step_time = required_step.totalTime

xdata = []
ydata = []
for ith in history_data:
xdata.append(ith[0]+step_time)
ydata.append(ith[1])

x = np.array(xdata)
y = np.array(ydata)
return x,y

except Exception as e:
return e

def history_region_description(self, step_name, historyregion_name):
"""Get history region description, which belongs to the given step and history region.
Parameters
----------
step_name : Abaqus steps
It is always required.
historyregion_name: Abaqus history region
It is always required.
Returns
-------
history_description : description for history region, which is visible in Abaqus. In case of error it gives an error message.
It is string.
"""
try:
required_step = self._odb.steps[step_name]

history_description = required_step.historyRegions[historyregion_name].description

return history_description

except Exception as e:
return e


def history_info(self):
"""Get steps, history regions, history outputs and write into a dictionary.
Returns
-------
A dictionary which contains information about the history of a given odb file.
In case of error it gives an error message.
"""
dict1 = {}
try:
steps = self._odb.steps.keys()

for istep in steps:
regions = self.history_regions(step_name=istep)

for iregion in regions:

idescription = self.history_region_description(step_name= istep, historyregion_name= iregion)

outputs = self.history_outputs(step_name= istep, historyregion_name= iregion)

for ioutputs in outputs:
if "Repeated: key" in ioutputs:
outputs.remove(ioutputs)

steplist = []
for istep2 in steps:
try:
self._odb.steps[istep2].historyRegions[iregion].description
steplist.append(istep2)
except Exception as e:
continue

dict5 = {
"History Region" : iregion,
"History Outputs" : outputs,
"Steps " : steplist
}

dict1[idescription] = dict(dict5)

return dict1
except Exception as e:
return e


def _set_position(field, user_request=None):
"""Translate string to symbolic constant and define default behavior.
Expand Down

0 comments on commit 3cad0a0

Please sign in to comment.