Skip to content

Commit 3ab601a

Browse files
pyansys-ci-botmbbatukanpre-commit-ci[bot]germa89
authored
feat: (PR 4122) adding OSRESULT command to the misc loads (#4131)
* Adding OSRESULT command to the misc loads * ci: auto fixes from pre-commit.com hooks. for more information, see https://pre-commit.ci * chore: adding changelog file 4131.miscellaneous.md [dependabot-skip] * chore: adding changelog file 4131.miscellaneous.md [dependabot-skip] * feat: add osresult method to retrieve and format command output. Also adding tests * feat: add test for osresult output to list and DataFrame using pandas * fix: remove redundant outres call in test_osresult_presol --------- Co-authored-by: Mehmet Baris Batukan <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: German <[email protected]>
1 parent 10b2e1b commit 3ab601a

File tree

5 files changed

+237
-1
lines changed

5 files changed

+237
-1
lines changed

doc/changelog.d/4131.miscellaneous.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Feat: (pr 4122) adding osresult command to the misc loads

src/ansys/mapdl/core/_commands/solution/miscellaneous_loads.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,138 @@ def outres(self, item="", freq="", cname="", nsvar="", dsubres="", **kwargs):
10001000
command = f"OUTRES,{item},{freq},{cname},{nsvar},{dsubres}"
10011001
return self.run(command, **kwargs)
10021002

1003+
def osresult(self, item="", comp="", freq="", cname="", **kwargs):
1004+
"""Controls the selected result data written to the database.
1005+
1006+
APDL Command: OSRESULT
1007+
1008+
Parameters
1009+
----------
1010+
item
1011+
Item to output to the database:
1012+
1013+
ERASE - Erases all selected result output specifications
1014+
1015+
STATUS - Lists the current set of selected result output specifications
1016+
1017+
SVAR - State variable number
1018+
1019+
FLD - User-defined field variables
1020+
1021+
S - Component and derived (principal, intensity, equivalent) stresses
1022+
1023+
EPEL - Component and derived (principal, intensity, equivalent) elastic strains
1024+
1025+
EPPL - Component and derived (principal, intensity, equivalent) plastic strains
1026+
1027+
EPCR - Component and derived (principal, intensity, equivalent) creep strains
1028+
1029+
EPTH - Component and derived (principal, intensity, equivalent) thermal strains
1030+
1031+
CDM - Mullins effect damage variable and maximum previous strain energy
1032+
1033+
PMSV - Pore mechanics state variables for coupled pore-pressure-thermal elements
1034+
1035+
FFLX - Fluid flow flux components in poromechanics
1036+
1037+
FGRA - Fluid pore pressure gradient components in poromechanics
1038+
1039+
BKS - Total nonlinear kinematic backstress components
1040+
1041+
GDMG - Generalized and ductile damage values
1042+
1043+
comp
1044+
Component of Item to output to the database:
1045+
1046+
For S, EPEL, EPPL, EPCR, EPTH: X, Y, Z, XY, YZ, XZ, 1, 2, 3, INT, EQV
1047+
1048+
For SVAR: 1, 2, 3, ..., N (state variable number)
1049+
1050+
For FLD: UF01, UF02, ..., UF09 (user-defined field variables)
1051+
1052+
For CDM: DMG, LM
1053+
1054+
For PMSV: VRAT, PPRE, DSAT, RPER
1055+
1056+
For FFLX, FGRA: X, Y, Z
1057+
1058+
For BKS: X, Y, Z, XY, YZ, XZ
1059+
1060+
For GDMG: ETA, ETAC, IDRA, DUCT (or blank for generalized damage)
1061+
1062+
freq
1063+
Frequency to output to the database:
1064+
1065+
n - Writes every nth and last substep of each load step
1066+
1067+
-n - Writes up to n equally spaced substeps of each load step
1068+
1069+
ALL - Writes every substep
1070+
1071+
LAST - Writes the last substep of each load step (default)
1072+
1073+
cname
1074+
The name of an element component (CM) defining the set of elements
1075+
for which this specification is active. If not specified, the set
1076+
is all elements.
1077+
1078+
Notes
1079+
-----
1080+
OSRESULT controls output to the results database for the selected result
1081+
defined by the item and component combination. The command activates output
1082+
of the selected result for the specified substeps and elements. Multiple
1083+
commands for the same result are cumulative. No selected results are written
1084+
to the database unless specified via OSRESULT.
1085+
1086+
The saved selected quantities are accessible via standard postprocessing
1087+
commands (ANSOL, ETABLE, ESOL, PLESOL, PLNSOL, PRESOL, and PRNSOL).
1088+
1089+
OSRESULT,ERASE deletes the existing output specifications.
1090+
1091+
OSRESULT,STATUS lists the current set of selected result specifications.
1092+
1093+
The output of selected results is valid for static (ANTYPE,STATIC) and
1094+
transient (ANTYPE,TRANS) analysis types.
1095+
1096+
To select other results to output to the database, see OUTRES. (Element
1097+
quantities specified via OUTRES can be redundant to those specified via
1098+
OSRESULT. Avoid specifying redundant quantities, as they are stored and
1099+
processed separately.)
1100+
1101+
All OSRESULT results are in the solution coordinate system.
1102+
1103+
This command is also valid in PREP7.
1104+
1105+
Examples
1106+
--------
1107+
Enter solution
1108+
1109+
>>> mapdl.slashsolu()
1110+
1111+
Suppress all solution results output first
1112+
1113+
>>> mapdl.outres("ALL", "NONE")
1114+
1115+
Use OSRESULT to specify selected results
1116+
1117+
>>> mapdl.osresult("S", "Y", "ALL") # Stress Y component
1118+
>>> mapdl.osresult("S", "EQV", "ALL") # Equivalent stress
1119+
>>> mapdl.osresult("EPPL", "INT", "ALL") # Plastic strain intensity
1120+
1121+
Enter postprocessor
1122+
1123+
>>> mapdl.post1()
1124+
1125+
Retrieve selected results using PRESOL and PRNSOL
1126+
1127+
>>> mapdl.presol("SRES", "SY") # Print selected stress Y
1128+
>>> mapdl.presol("SRES", "SEQV") # Print selected equivalent stress
1129+
>>> mapdl.prnsol("SRES", "EPPLINT") # Print selected plastic strain intensity
1130+
1131+
"""
1132+
command = f"OSRESULT,{item},{comp},{freq},{cname}"
1133+
return self.run(command, **kwargs)
1134+
10031135
def rescontrol(
10041136
self,
10051137
action="",

src/ansys/mapdl/core/mapdl_extended.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from functools import wraps
2424
import os
2525
import pathlib
26+
import re
2627
import tempfile
2728
from typing import Union
2829
import warnings
@@ -31,7 +32,7 @@
3132
from numpy.typing import DTypeLike, NDArray
3233

3334
from ansys.mapdl.core import LOG as logger
34-
from ansys.mapdl.core.commands import CommandListingOutput
35+
from ansys.mapdl.core.commands import CommandListingOutput, CommandOutput
3536
from ansys.mapdl.core.errors import (
3637
CommandDeprecated,
3738
ComponentDoesNotExits,
@@ -2375,6 +2376,26 @@ def parain(
23752376
**kwargs,
23762377
)
23772378

2379+
@wraps(_MapdlCore.osresult)
2380+
def osresult(self, item="", comp="", freq="", cname="", **kwargs) -> CommandOutput:
2381+
result = super().osresult(item, comp, freq, cname, **kwargs)
2382+
if item.upper().strip() == "STATUS":
2383+
from ansys.mapdl.core.misc import expand_all_inner_lists
2384+
2385+
columns_names = ["ITEM", "FREQUENCY", "COMPONENT"]
2386+
result = CommandListingOutput(result)
2387+
table = re.search(
2388+
r"ITEM\s+FREQUENCY\s+COMPONENT(.*)", result, flags=re.DOTALL
2389+
).group(1)
2390+
table = [each.split() for each in table.splitlines() if each.strip()]
2391+
table = expand_all_inner_lists(
2392+
table, target_length=len(columns_names), fill_value=""
2393+
)
2394+
2395+
result._columns_names = columns_names
2396+
result._cache = np.array(table)
2397+
return result
2398+
23782399

23792400
class _MapdlExtended(_MapdlCommandExtended):
23802401
"""Extend Mapdl class with new functions"""

src/ansys/mapdl/core/misc.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import tempfile
3434
from threading import Thread
3535
from typing import (
36+
Any,
3637
Callable,
3738
Dict,
3839
Iterable,
@@ -717,3 +718,13 @@ def get_ip_hostname(ip: str) -> Tuple[str, str]:
717718
hostname = ip
718719

719720
return ip, hostname
721+
722+
723+
def expand_all_inner_lists(
724+
lst: list[list[Any]], target_length: int, fill_value: str = ""
725+
) -> list[list[Any]]:
726+
for inner in lst:
727+
missing = target_length - len(inner)
728+
if missing > 0:
729+
inner.extend([fill_value] * missing)
730+
return lst

tests/test_mapdl.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,3 +3172,74 @@ def test_rpsd(mapdl, psd_analysis):
31723172
output = mapdl.rpsd(3, 2, "", 1, 2)
31733173
assert isinstance(output, np.ndarray)
31743174
assert output.shape == (64, 1)
3175+
3176+
3177+
def test_osresult(mapdl, solved_box):
3178+
mapdl.solution()
3179+
3180+
mapdl.outres("ALL", "NONE")
3181+
mapdl.osresult("S", "Y", "ALL") # Stress Y component
3182+
mapdl.osresult("S", "EQV", "ALL") # Equivalent stress
3183+
mapdl.osresult("EPPL", "INT", "ALL") # Plastic strain intensity
3184+
3185+
assert "S" in mapdl.osresult("STATUS")
3186+
assert "Y" in mapdl.osresult("STATUS")
3187+
assert "EQV" in mapdl.osresult("STATUS")
3188+
assert "EPPL" in mapdl.osresult("STATUS")
3189+
assert "INT" in mapdl.osresult("STATUS")
3190+
3191+
3192+
@requires("pandas")
3193+
def test_osresult_to_list_and_dataframe(mapdl, solved_box):
3194+
import pandas as pd
3195+
3196+
mapdl.solution()
3197+
3198+
mapdl.outres("ALL", "NONE")
3199+
mapdl.osresult("S", "Y", "ALL") # Stress Y component
3200+
mapdl.osresult("S", "EQV", "ALL") # Equivalent stress
3201+
mapdl.osresult("EPPL", "INT", "ALL") # Plastic strain intensity
3202+
3203+
expected_list = [["SY", "ALL", ""], ["SEQV", "ALL", ""], ["EPPLINT", "ALL", ""]]
3204+
assert (
3205+
mapdl.osresult("STATUS").to_list() == expected_list
3206+
), "Status should contain the requested results"
3207+
3208+
expected_df = pd.DataFrame(
3209+
data=expected_list, columns=["ITEM", "FREQUENCY", "COMPONENT"]
3210+
)
3211+
assert (
3212+
mapdl.osresult("STATUS").to_dataframe().equals(expected_df)
3213+
), "DataFrame should match the expected results"
3214+
3215+
3216+
@pytest.mark.xfail(
3217+
reason="Bug in v25R2, osresult lists results as many times as issued",
3218+
strict=True,
3219+
)
3220+
def test_osresult_multiple(mapdl, solved_box):
3221+
mapdl.solution()
3222+
3223+
mapdl.outres("ALL", "NONE")
3224+
for i in range(3):
3225+
mapdl.osresult("EPPL", "INT", "ALL")
3226+
3227+
assert (
3228+
mapdl.osresult("STATUS").count("EPPLINT") == 1
3229+
), "EPPLINT should only be listed once in the status"
3230+
3231+
3232+
@pytest.mark.xfail(
3233+
reason="Bug? PRESOL cannot retrieve osresult data",
3234+
strict=True,
3235+
)
3236+
def test_osresult_presol(mapdl, solved_box):
3237+
mapdl.solution()
3238+
3239+
mapdl.outres("ALL", "NONE")
3240+
mapdl.osresult("S", "Y", "ALL") # Stress Y component
3241+
3242+
mapdl.solve()
3243+
mapdl.post1()
3244+
3245+
assert mapdl.presol("SRES", "SY")

0 commit comments

Comments
 (0)