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

Odbclient history output #43

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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