Skip to content

Commit

Permalink
Add example cycles_to_failure from McMunich (#591)
Browse files Browse the repository at this point in the history
* Functioning cyclic_to_failure examples

* Update style

* Add test_examples::test_download_cycles_to_failure

* Comments from Jenna

Signed-off-by: paul.profizi <paul.profizi@ansys.com>

* Update title of the example

* Apply suggestions from code review

* Remove section separation for a visually better example

* Rework formatting of the explanation paragraph

* Update examples/02-modal-harmonic/06-cycles_to_failure.py

* Fix formatting of the description

* Fix formatting of the description

* Revert "Remove section separation for a visually better example"

This reverts commit 52d1b1d.

* Reformat sections

* Reformat

* Reformat

* Reformat

* Reformat

* Reformat

* Apply suggestions from code review

Signed-off-by: paul.profizi <paul.profizi@ansys.com>
  • Loading branch information
PProfizi committed Dec 1, 2022
1 parent daa943f commit 48f58c0
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 34 deletions.
109 changes: 75 additions & 34 deletions ansys/dpf/core/examples/downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ def download_transient_result(should_upload: bool = True, server=None,
return_local_path=False) -> str:
"""Download an example transient result file and return the download path
available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -118,8 +118,8 @@ def download_all_kinds_of_complexity(should_upload: bool = True, server=None
, return_local_path=False) -> str:
"""Download an example static result and return the download path
available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -158,8 +158,8 @@ def download_all_kinds_of_complexity_modal(should_upload: bool = True, server=No
return_local_path=False) -> str:
"""Download an example result file from a static modal analysis and
return the download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -197,8 +197,8 @@ def download_all_kinds_of_complexity_modal(should_upload: bool = True, server=No
def download_pontoon(should_upload: bool = True, server=None, return_local_path=False) -> str:
"""Download an example result file from a static modal analsys and
return the download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -236,8 +236,8 @@ def download_multi_harmonic_result(should_upload: bool = True, server=None,
return_local_path=False) -> str:
"""Download an example multi-harmonic result file and return the
download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -273,10 +273,10 @@ def download_multi_harmonic_result(should_upload: bool = True, server=None,

def download_multi_stage_cyclic_result(should_upload: bool = True, server=None,
return_local_path=False) -> str:
"""Download an example multi stage result file and return the
"""Download an example multi-stage result file and return the
download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -313,8 +313,8 @@ def download_multi_stage_cyclic_result(should_upload: bool = True, server=None,
def download_sub_file(should_upload: bool = True, server=None, return_local_path=False) -> str:
"""Download an example .sub result file containing matrices and return the
download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -352,8 +352,8 @@ def download_msup_files_to_dict(should_upload: bool = True, server=None,
return_local_path=False) -> dict:
"""Download all the files necessary for a msup expansion and return the
download paths available server side into a dictionary extension->path.
If the server is remote (or doesn't share the memory), the files are uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -397,8 +397,8 @@ def download_distributed_files(should_upload: bool = True, server=None,
return_local_path=False) -> dict:
"""Download distributed rst files and return the
download paths into a dictionary domain id->path.
If the server is remote (or doesn't share the memory), the files are uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -439,8 +439,8 @@ def download_distributed_files(should_upload: bool = True, server=None,
def download_fluent_files(should_upload: bool = True, server=None, return_local_path=False) -> dict:
"""Download the cas and dat file of a fluent analysis and return the
download paths into a dictionary extension->path.
If the server is remote (or doesn't share the memory), the files are uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -481,9 +481,9 @@ def download_fluent_files(should_upload: bool = True, server=None, return_local_
def download_extrapolation_3d_result(should_upload: bool = True, server=None,
return_local_path=False) -> dict:
"""Download example static results of reference and integrated points
for extrapolation of 3d-element and return return the dictionary of 2 download paths.
If the server is remote (or doesn't share the memory), the files are uploaded or made available
server side.
for extrapolation of 3d-element and return the dictionary of 2 download paths.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -532,8 +532,8 @@ def download_extrapolation_2d_result(should_upload: bool = True, server=None,
return_local_path=False) -> dict:
"""Download example static results of reference and integrated points
for extrapolation of 2d-element and return the dictionary of 2 download paths.
If the server is remote (or doesn't share the memory), the files are uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -581,8 +581,8 @@ def download_extrapolation_2d_result(should_upload: bool = True, server=None,
def download_hemisphere(should_upload: bool = True, server=None, return_local_path=False) -> str:
"""Download an example result file from a static analysis and
return the download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -620,8 +620,8 @@ def download_example_asme_result(should_upload: bool = True, server=None,
return_local_path=False) -> str:
"""Download an example result file from a static analysis and
return the download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -657,8 +657,8 @@ def download_example_asme_result(should_upload: bool = True, server=None,
def download_crankshaft(should_upload: bool = True, server=None, return_local_path=False) -> str:
"""Download the result file of an example of a crankshaft
under load and return the download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -695,8 +695,8 @@ def download_crankshaft(should_upload: bool = True, server=None, return_local_pa
def download_piston_rod(should_upload: bool = True, server=None, return_local_path=False) -> str:
"""Download the result file of an example of a piston rod
under load and return the download path available server side.
If the server is remote (or doesn't share the memory), the file is uploaded or made available
server side.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Expand Down Expand Up @@ -846,3 +846,44 @@ def download_binout_glstat(should_upload: bool = True, server=None, return_local
"""
return _download_file("binout", "binout_glstat", should_upload, server, return_local_path)


def download_cycles_to_failure(should_upload: bool = True,
server=None,
return_local_path=False) -> str:
"""Download an example result file from a cyclic analysis and
return the download path.
If the server is remote (or doesn't share memory), the file is uploaded or made available
on the server side.
Examples files are downloaded to a persistent cache to avoid
re-downloading the same file twice.
Parameters
----------
should_upload : bool, optional (default True)
Whether the file should be uploaded server side when the server is remote.
server : server.DPFServer, optional
Server with channel connected to the remote or local instance. When
``None``, attempts to use the global server.
return_local_path: bool, optional
If ``True``, the local path is returned as is, without uploading, nor searching
for mounted volumes.
Returns
-------
str
Path to the example file.
Examples
--------
Download an example result file and return the path of the file
>>> from ansys.dpf.core import examples
>>> path = examples.download_cycles_to_failure()
>>> path
'C:/Users/user/AppData/local/temp/cycles_to_failure.rst'
"""
return _download_file("cyclic", "cyclic_to_failure.rst",
should_upload, server, return_local_path)
124 changes: 124 additions & 0 deletions examples/02-modal-harmonic/06-cycles_to_failure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""
.. _ref_cycles_to_failure:
Calculate the number of cycles to fatigue failure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This example shows how to generate and use a result file to calculate the
cycles to failure result for a simple model.
Material data is manually imported, Structural Steel from Ansys Mechanical:
- Youngs Modulus (youngsSteel)
- Poisson's Ratio (prxySteel)
- SN curve (sn_data)
The first step is to generate a simple model with high stress and save the
results .rst file locally to myDir (default is "C:\\\\temp").
For this, we provide a short pyMAPDL script.
.. line-block::
The second step uses PyDPF-Core to generate the cycles to failure result:
The locally saved .rst file is imported and plotted.
Then the von Mises stress is generated and plotted with DPF operators.
The NumPy python package is then used to interpolate the cycles to failure values.
The nodal von Mises equivalent stress value is used in the interpolation.
(Note that the cycles to failure data must be manipulated to use NumPy interpolation)
An empty field is then created and filled with the resulting cycles to failure values.
The cycles to failure result is finally plotted.
The cycles to failure result is the (interpolated) negative of the stress result.
The higher the stress result, the lower the number of cycles to failure.
"""

from ansys.dpf import core as dpf
from ansys.dpf.core import examples
import numpy as np

###############################################################################
# The first step is to generate a simple model with high stress

# # Material parameters from Ansys Mechanical Structural Steel
youngsSteel = 200e9
prxySteel = 0.3
sn_data = np.empty((11, 2)) # initialize empty np matrix
sn_data[:, 0] = [10, 20, 50, 100, 200, 2000, 10000, 20000, 1e5, 2e5, 1e6]
sn_data[:, 1] = [3.999e9, 2.8327e9, 1.896e9, 1.413e9, 1.069e9, 4.41e8, 2.62e8, 2.14e8, 1.38e8,
1.14e8, 8.62e7]

###############################################################################
# The .rst file used is already available, but can be obtained using the short pyMAPDL code below:

# # ### Launch pymapdl to generate rst file in myDir
# from ansys.mapdl.core import launch_mapdl
# import os
#
#
# mapdl = launch_mapdl()
# mapdl.prep7()
# # Model
# mapdl.cylind(0.5, 0, 10, 0)
# mapdl.mp("EX", 1, youngsSteel)
# mapdl.mp("PRXY", 1, prxySteel)
# mapdl.mshape(key=1, dimension='3d')
# mapdl.et(1, "SOLID186")
# mapdl.esize(0.3)
# mapdl.vmesh('ALL')
#
# # #### Boundary Conditions: fixed constraint
# mapdl.nsel(type_='S', item='LOC', comp='Z', vmin=0)
# mapdl.d("all", "all")
# mapdl.nsel(type_='S', item='LOC', comp='Z', vmin=10)
# nnodes = mapdl.get("NumNodes", "NODE", 0, "COUNT")
# mapdl.f(node="ALL", lab="fy", value=-13e6 / nnodes)
# mapdl.allsel()
#
# # #### Solve
# mapdl.run("/SOLU")
# sol_output = mapdl.solve()
# rst = os.path.join(mapdl.directory, 'file.rst')
# mapdl.exit()
# print('apdl model solved.')

###############################################################################
# PyDPF-Core is then used to post-process the .rst file to estimate the cycles to failure.

# Comment the following line if solving the MAPDL problem first.
rst = examples.download_cycles_to_failure()

# Import the result as a DPF Model object.
model = dpf.Model(rst)
print(model)

###############################################################################
# Get the von mises equivalent stress, requires an operator.
s_eqv_op = dpf.operators.result.stress_von_mises(data_sources=model)
vm_stress_fc = s_eqv_op.eval()
vm_stress_field = vm_stress_fc[0]
vm_stress_field.plot(text="VM stress field")

###############################################################################
# Use NumPy to interpolate the results.

# Inverse the sn_data
x_values = sn_data[:, 1][::-1] # the x values are the stress ranges in ascending order
y_values = sn_data[:, 0][::-1] # y values are inverted cycles to failure

# Interpolate cycles to failure for the VM values
cycles_to_failure = np.interp(vm_stress_field.data, x_values, y_values)

###############################################################################
# Generate a cycles_to_failure DPF Field

# Create an empty field
cycles_to_failure_field = dpf.Field(nentities=len(vm_stress_field.scoping),
nature=dpf.natures.scalar,
location=dpf.locations.nodal)
# Populate the field
cycles_to_failure_field.scoping = vm_stress_field.scoping
cycles_to_failure_field.meshed_region = vm_stress_field.meshed_region
cycles_to_failure_field.data = cycles_to_failure

# Plot the field
sargs = dict(title="cycles", fmt="%.2e")
cycles_to_failure_field.plot(text="Cycles to failure", scalar_bar_args=sargs)
5 changes: 5 additions & 0 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ def test_download_piston_rod():
assert isinstance(Model(path), Model)


def test_download_cycles_to_failure():
path = examples.download_cycles_to_failure()
assert isinstance(Model(path), Model)


list_examples = [
"simple_bar",
"static_rst",
Expand Down

0 comments on commit 48f58c0

Please sign in to comment.