Prerequisites
This demo requires you to have the following on your local machine:
- Ansys v2024R1 including: i) Ansys Mechanical ii) Ansys Dynamic Reporting (ADR)
- Python3 including the following modules: i) ansys-mechanical-core ii) ansys-dynamicreporting-core
- A Mechanical database file (.mechdb or .mechdat).
User inputs
First, let's define the user inputs: the path to Mechanical database file and the path to a working directory.
print("User Inputs")
mech_db_file_path = r"D:\No_Backup\embedded_mech_to_ADR\test_case1.mechdat"
working_dir = r"D:\Temp"
print(" Section Completed")
Import Python modules
Next we will import our required Python modules
print("Import Python Modules")
import ansys.dynamicreporting.core as adr
from ansys.mechanical.core import App
from ansys.mechanical.core import global_variables
from ansys.mechanical.core.embedding import add_mechanical_python_libraries
import os
import shutil
from datetime import datetime
print(" Section Completed")
Define constants and create sub-folders
We will set our Ansys version to 24.1, which corresponds to 2024R1, and then get the Ansys installation directory, We will have 2 sub-folders in our working directory. 'Exports' will contain all the report files generated from Mechanical. 'Nexus' will the contain the Nexus database that is required by ADR, and needs to initially be empty so that ADR can automatically create the new Nexus database. To start from scratch, we will delete these folders if they already exist. If an existing Nexus folder cannot be deleted, it likely means that the server is still running.
print("Define constants and create subfolders")
version = 241
ansys_ins = os.getenv('AWP_ROOT'+str(version))
adr_db_directory = os.path.join(working_dir,"Nexus")
export_directory = os.path.join(working_dir,"Exports")
if os.path.exists(adr_db_directory):
try:
shutil.rmtree(adr_db_directory)
except:
print(f"Cannot delete adr_db directory: {adr_db_directory}")
try:
os.mkdir(adr_db_directory)
except:
print(f"Cannot mkdir adr_db directory: {adr_db_directory}")
else:
print(f" Directory created: {adr_db_directory}")
if os.path.exists(export_directory):
try:
shutil.rmtree(export_directory)
except:
print(f"Cannot delete export directory: {export_directory}")
try:
os.mkdir(export_directory)
except:
print(f"Cannot mkdir export directory: {export_directory}")
else:
print(f" Directory created: {export_directory}")
print(" Section Completed")
Go to your working directory (defined in User Inputs section) and confirm that you have 2 empty folders: 'Nexus' and 'Exports'.
Start ADR service
We will start a new Ansys Dynamic Reporting (ADR) instance based on a new Nexus database. This may take a minute.
print("Start ADR service")
adr_service = adr.Service(ansys_installation=ansys_ins, db_directory=adr_db_directory)
try:
session_guid = adr_service.start(create_db=True)
except:
print("ADR server not started properly. Please ensure that the 'Nexus' folder in your working directory is empty.")
print("If an old Nexus server is still running, use Task Manager to end any nginx.exe processes.")
print("Or create a new, empty working directory.")
server=adr_service.serverobj
print(adr_service.url)
print(" Section Completed")
The preceding code takes a minute to run. While it is running, the 'Nexus' folder in your working directory should be filling up with some files and folders. Note that you will not be able to delete some of these files and folders while the ADR server is running.
The preceding code block should print a URL. Copy/paste the URL in to a web browser and confirm that the ADR server is running.
The next step takes a minute to run. While it is running, you can explore the ADR server's webpage. For example, click Reports > Generate Reports to see a list of available report templates. For now, the only available template is 'No Template'.
Launch Mechanical and import Mechanical scripting variables
We will launch Mechanical using embedded PyMechanical, import the global variables from Mechanical scripting language to our instance of Python, then finally open the saved Mechanical project. This code make take a minute to run.
print("Launch Mechanical and import Mechanical scripting variables")
app = App(version=version)
globals().update(global_variables(app))
add_mechanical_python_libraries(version)
print(f"Reading Mechanical Database: {mech_db_file_path}..")
app.open(mech_db_file_path)
print(f"Mechanical details:\n{app}")
print(" Section Completed")
Define Mechanical helper functions
Let's define some reusable functions for creating report items from Mechanical
print("Define Mechanical helper functions")
def ExportAVZ(obj,export_directory):
obj.Activate()
# export_directory = wbjn.ExecuteCommand(ExtAPI,'returnValue(GetUserFilesDirectory())')
filename = os.path.join(export_directory,'{}.avz'.format(obj.Name))
ExtAPI.Graphics.Export3D(filename)
return filename
def ExportPNG(obj,export_directory):
obj.Activate()
filename = os.path.join(export_directory,'{}.png'.format(obj.Name))
image_export_format = Ansys.Mechanical.DataModel.Enums.GraphicsImageExportFormat.PNG
settings_720p = Ansys.Mechanical.Graphics.GraphicsImageExportSettings()
ExtAPI.Graphics.Camera.SetFit()
ExtAPI.Graphics.ExportImage(filename, image_export_format, settings_720p)
return filename
print(" Mechanical helper functions created.")
Define ADR helper functions
Similarly, let's define some reusable functions for pushing report items to ADR. Let's also create a function for exporting an ADR report as a PDF.
print("Define ADR helper functions")
def pushTree(tree,adr_service,tags=''):
item = adr_service.create_item()
item.item_tree = tree
item.set_tags(tags)
def PushAVZ(avz,adr_service,tags='',name='Scene_from_AVZ'):
a = adr_service.create_item()
a.item_scene = avz
a.add_tag(tags)
def PushPNG(file,adr_service,tags='',name='Image_from_PNG'):
item = adr_service.create_item(obj_name = name)
item.item_image = file
item.add_tag(tags)
def ExportPDF(adr_service,report,filename,version=241):
ansys_ins = os.getenv('AWP_ROOT'+str(version))
cei_directory= os.path.join(ansys_ins,'CEI')
adr_service.serverobj.export_report_as_pdf(report_guid=report.report.guid, file_name=filename, exec_basis=cei_directory, ansys_version=version)
print("ADR helper functions created.")
Create report items and push them to ADR
3D and 2D images
For several objects in the Mechanical tree (geometry, mesh, and results objects), we will create both AVZ and PNG files and push them to ADR with appropriate tags. The tags will be used later to place report items in to report templates.
print("Create Report Items and push them to ADR")
geometry = ExtAPI.DataModel.Project.Model.Geometry
geom_avz = ExportAVZ(geometry,export_directory)
PushAVZ(geom_avz,adr_service,'Geom')
geom_png = ExportPNG(geometry,export_directory)
PushPNG(geom_png,adr_service,'Geom Static')
mesh = ExtAPI.DataModel.Project.Model.Mesh
mesh_avz = ExportAVZ(mesh,export_directory)
PushAVZ(mesh_avz,adr_service,'Mesh')
mesh_png = ExportPNG(mesh,export_directory)
PushPNG(mesh_png,adr_service,'Mesh Static')
results =ExtAPI.DataModel.GetObjectsByType(Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Result)
for result in results:
avz = ExportAVZ(result,export_directory)
PushAVZ(avz,adr_service,'Result '+result.Name)
png = ExportPNG(result,export_directory)
PushPNG(png,adr_service,'Result Static'+result.Name)
print(" Section Completed")
In your working directory's "Exports" sub-folder, you should now see several .png and .avz files. You should also be able to view these files on the ADR server by running the following command:
adr_service.visualize_report()
The above command will open your default web browser with URL to create a report with no template and filter. With no template or filter all report items will displayed without any formatting.
Collapsible trees
Let's also create some collapsible trees. The first will contain material data for each body in Mechanical. The second will contain some simulation details.
print("Create tree items and push them to ADR")
partList = []
for part in ExtAPI.DataModel.GeoData.Assemblies[0].Parts:
bodyList = []
for body in part.Bodies:
if body.Suppressed:
continue
mat = body.Material
bodyList.append(dict(key='child', name=body.Name, value=mat.DisplayName, state="collapsed"))
partList.append(dict(key='child', name='Part: '+part.Name, value=None, children=bodyList,state="collapsed"))
MaterialTree = [(dict(key='root', name='Material Data', value=None, children=partList,state="collapsed"))]
pushTree(MaterialTree,adr_service,tags='MaterialProperties')
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
RunDetails = [['CAD File',geometry.Source],['Report Time',timestamp]]
RunDetailsDict = [dict(key='child', name=x, value=y) for x, y in RunDetails]
RunDetailsTree = [dict(key='root', name='FEA Details', children=RunDetailsDict, value=None)]
pushTree(RunDetailsTree,adr_service,tags='RunDetails')
print(" Section Completed")
If you re-run the command to visual the no template report (or refresh the previous page), you should now see the collapsible trees at the bottom of the page.
adr_service.visualize_report()
You can also return the ADR homepage (use the below command to reprint the URL if needed) and click Query > Report Items > Run Query to see a list of report items.
print(adr_service.url)
Create the report templates
Next, we will create report templates to make our reports more legible.
We are going to create 2 versions of the report template. The first version will use 3D image files (AVZ) to allow us to pan, zoom, and rotate while viewing the report in a web browser. The second version is very similar, but will use 2D image files (PNG) to create a report that is more suitable to export to .pdf
These 2 code blocks were not written by hand. The templates were created in the ADR Template Editor and then exported as Python files. We first created the dynamic version, then made a copy and slightly modified it to create the static version.
Report templates do not have to be created using Python. They can be created and modified using a GUI, as mentioned above, and stored in a Nexus database. They can be exported/imported directly from one Nexus database or ADR server to another. However, for this example we wanted to create everything "on the fly" using Python.
Dynamic template
print("Create the dynamic report template")
template_00=server.create_template(name="Mechanical Basic Report", parent=None, report_type="Layout:basic")
server.put_objects(template_00)
template_13=server.create_template(name="Logo", parent=template_00, report_type="Layout:basic")
template_13.set_filter("A|i_type|cont|image;A|i_tags|cont|img=AnsysLogo;")
server.put_objects(template_13)
server.put_objects(template_00)
template_02=server.create_template(name="TOC", parent=template_00, report_type="Layout:toc")
template_02.params='{"TOCitems": 1, "properties": {"TOCItem": "0"}, "HTML": "<h1>Table of Contents</h1>"}'
template_02.set_filter("A|i_name|eq|__NonexistantName__;")
server.put_objects(template_02)
server.put_objects(template_00)
template_geom=server.create_template(name="Geom", parent=template_00, report_type="Layout:panel")
template_geom.params='{"HTML": "<h1>Geometry</h1>", "properties": {"TOCLevel": "0", "TOCItem": "1"}}'
template_geom.set_filter("A|i_tags|cont|Geom;")
server.put_objects(template_geom)
server.put_objects(template_00)
template_geom_scene =server.create_template(name="3Dscene", parent=template_geom, report_type="Layout:basic")
template_geom_scene.params='{"properties": {"TOCItem": "0"}}'
template_geom_scene.set_filter("A|i_type|cont|scene;")
server.put_objects(template_geom_scene)
server.put_objects(template_geom)
server.put_objects(template_00)
template_03=server.create_template(name="Mesh", parent=template_00, report_type="Layout:panel")
template_03.params='{"HTML": "<h1>Mesh</h1>", "properties": {"TOCLevel": "0", "TOCItem": "1"}}'
template_03.set_filter("A|i_tags|cont|Mesh;")
server.put_objects(template_03)
server.put_objects(template_00)
template_04=server.create_template(name="3Dscene", parent=template_03, report_type="Layout:basic")
template_04.params='{"properties": {"TOCItem": "0"}}'
template_04.set_filter("A|i_type|cont|scene;")
server.put_objects(template_04)
server.put_objects(template_03)
server.put_objects(template_00)
template_05=server.create_template(name="FEA Details", parent=template_00, report_type="Layout:panel")
template_05.params='{"HTML": "<h1>FEA Details</h1>\\nDetails of the model", "properties": {"TOCItem": "1"}}'
template_05.set_filter("A|i_type|cont|tree;A|i_tags|cont|RunDetails;")
server.put_objects(template_05)
server.put_objects(template_00)
template_06=server.create_template(name="Materials", parent=template_00, report_type="Layout:panel")
template_06.params='{"HTML": "<h1>Materials</h1>", "properties": {"TOCItem": "1"}}'
template_06.set_filter("A|i_type|cont|tree;A|i_tags|cont|MaterialProperties;")
server.put_objects(template_06)
server.put_objects(template_00)
template_10=server.create_template(name="Static Structural", parent=template_00, report_type="Layout:panel")
template_10.params='{"HTML": "<h1>Static Structural</h1>", "properties": {"TOCItem": "1"}}'
template_10.set_filter("A|i_tags|cont|Result;")
server.put_objects(template_10)
server.put_objects(template_00)
template_14=server.create_template(name="Total Deformation", parent=template_10, report_type="Layout:basic")
template_14.params='{"HTML": "<h2>Total Deformation</h2>", "properties": {"TOCLevel": "1"}}'
template_14.set_filter("A|i_tags|cont|Deformation;")
server.put_objects(template_14)
server.put_objects(template_10)
server.put_objects(template_00)
template_07=server.create_template(name="3Dscene", parent=template_14, report_type="Layout:basic")
template_07.params='{"properties": {"TOCItem": "0"}}'
template_07.set_filter("A|i_type|cont|scene;")
server.put_objects(template_07)
server.put_objects(template_14)
server.put_objects(template_10)
server.put_objects(template_00)
template_08=server.create_template(name="table", parent=template_14, report_type="Layout:basic")
template_08.params='{"properties": {"TOCItem": "0"}}'
template_08.set_filter("A|i_type|cont|table;")
server.put_objects(template_08)
server.put_objects(template_14)
server.put_objects(template_10)
server.put_objects(template_00)
template_11=server.create_template(name="Equivalent Stress", parent=template_10, report_type="Layout:basic")
template_11.params='{"HTML": "<h2>Equivalent Stress</h2>", "properties": {"TOCLevel": "1"}}'
template_11.set_filter("A|i_tags|cont|Stress;")
server.put_objects(template_11)
server.put_objects(template_10)
server.put_objects(template_00)
template_12=server.create_template(name="3Dscene", parent=template_11, report_type="Layout:basic")
template_12.params='{"properties": {"TOCItem": "0"}}'
template_12.set_filter("A|i_type|cont|scene;")
server.put_objects(template_12)
server.put_objects(template_11)
server.put_objects(template_10)
server.put_objects(template_00)
template_09=server.create_template(name="table", parent=template_11, report_type="Layout:basic")
template_09.params='{"properties": {"TOCItem": "0"}}'
template_09.set_filter("A|i_type|cont|table;")
server.put_objects(template_09)
server.put_objects(template_11)
server.put_objects(template_10)
server.put_objects(template_00)
print(f" Template Created: {template_00}")
Static template
print("Create the static report template")
template_00=server.create_template(name="Mechanical Basic Report (Static)", parent=None, report_type="Layout:basic")
server.put_objects(template_00)
template_13=server.create_template(name="Logo", parent=template_00, report_type="Layout:basic")
template_13.set_filter("A|i_type|cont|image;A|i_tags|cont|img=AnsysLogo;")
server.put_objects(template_13)
server.put_objects(template_00)
template_02=server.create_template(name="TOC", parent=template_00, report_type="Layout:toc")
template_02.params='{"TOCitems": 1, "properties": {"TOCItem": "0"}, "HTML": "<h1>Table of Contents</h1>"}'
template_02.set_filter("A|i_name|eq|__NonexistantName__;")
server.put_objects(template_02)
server.put_objects(template_00)
template_geom=server.create_template(name="Geom", parent=template_00, report_type="Layout:panel")
template_geom.params='{"HTML": "<h1>Geometry</h1>", "properties": {"TOCLevel": "0", "TOCItem": "1"}}'
template_geom.set_filter("A|i_tags|cont|Geom;")
server.put_objects(template_geom)
server.put_objects(template_00)
template_geom_scene =server.create_template(name="Static Image", parent=template_geom, report_type="Layout:basic")
template_geom_scene.params='{"properties": {"TOCItem": "0"}}'
template_geom_scene.set_filter("A|i_type|ncont|scene;")
server.put_objects(template_geom_scene)
server.put_objects(template_geom)
server.put_objects(template_00)
template_03=server.create_template(name="Mesh", parent=template_00, report_type="Layout:panel")
template_03.params='{"HTML": "<h1>Mesh</h1>", "properties": {"TOCLevel": "0", "TOCItem": "1"}}'
template_03.set_filter("A|i_tags|cont|Mesh;")
server.put_objects(template_03)
server.put_objects(template_00)
template_04=server.create_template(name="Static Image", parent=template_03, report_type="Layout:basic")
template_04.params='{"properties": {"TOCItem": "0"}}'
template_04.set_filter("A|i_type|ncont|scene;")
server.put_objects(template_04)
server.put_objects(template_03)
server.put_objects(template_00)
template_05=server.create_template(name="FEA Details", parent=template_00, report_type="Layout:panel")
template_05.params='{"HTML": "<h1>FEA Details</h1>\\nDetails of the model", "properties": {"TOCItem": "1"}}'
template_05.set_filter("A|i_type|cont|tree;A|i_tags|cont|RunDetails;")
server.put_objects(template_05)
server.put_objects(template_00)
template_06=server.create_template(name="Materials", parent=template_00, report_type="Layout:panel")
template_06.params='{"HTML": "<h1>Materials</h1>", "properties": {"TOCItem": "1"}}'
template_06.set_filter("A|i_type|cont|tree;A|i_tags|cont|MaterialProperties;")
server.put_objects(template_06)
server.put_objects(template_00)
template_10=server.create_template(name="Static Structural", parent=template_00, report_type="Layout:panel")
template_10.params='{"HTML": "<h1>Static Structural</h1>", "properties": {"TOCItem": "1"}}'
template_10.set_filter("A|i_tags|cont|Result;")
server.put_objects(template_10)
server.put_objects(template_00)
template_14=server.create_template(name="Total Deformation", parent=template_10, report_type="Layout:basic")
template_14.params='{"HTML": "<h2>Total Deformation</h2>", "properties": {"TOCLevel": "1"}}'
template_14.set_filter("A|i_tags|cont|Deformation;")
server.put_objects(template_14)
server.put_objects(template_10)
server.put_objects(template_00)
template_07=server.create_template(name="Static Image", parent=template_14, report_type="Layout:basic")
template_07.params='{"properties": {"TOCItem": "0"}}'
template_07.set_filter("A|i_type|ncont|scene;")
server.put_objects(template_07)
server.put_objects(template_14)
server.put_objects(template_10)
server.put_objects(template_00)
template_08=server.create_template(name="table", parent=template_14, report_type="Layout:basic")
template_08.params='{"properties": {"TOCItem": "0"}}'
template_08.set_filter("A|i_type|cont|table;")
server.put_objects(template_08)
server.put_objects(template_14)
server.put_objects(template_10)
server.put_objects(template_00)
template_11=server.create_template(name="Equivalent Stress", parent=template_10, report_type="Layout:basic")
template_11.params='{"HTML": "<h2>Equivalent Stress</h2>", "properties": {"TOCLevel": "1"}}'
template_11.set_filter("A|i_tags|cont|Stress;")
server.put_objects(template_11)
server.put_objects(template_10)
server.put_objects(template_00)
template_12=server.create_template(name="Static Image", parent=template_11, report_type="Layout:basic")
template_12.params='{"properties": {"TOCItem": "0"}}'
template_12.set_filter("A|i_type|ncont|scene;")
server.put_objects(template_12)
server.put_objects(template_11)
server.put_objects(template_10)
server.put_objects(template_00)
template_09=server.create_template(name="table", parent=template_11, report_type="Layout:basic")
template_09.params='{"properties": {"TOCItem": "0"}}'
template_09.set_filter("A|i_type|cont|table;")
server.put_objects(template_09)
server.put_objects(template_11)
server.put_objects(template_10)
server.put_objects(template_00)
print(f" Template Created: {template_00}")
print(" Section Completed")
View the reports
Let's look at the dynamic report and then create a PDF.
Dynamic report
This code will open the dynamic report in your web browser.
print("View the reports")
adr_service.visualize_report('Mechanical Basic Report',new_tab = True)
PDF report
Now let's create a PDF version of the report. The PDF will be created in your working directory's 'Exports' folder
static_report = adr_service.get_report(report_name='Mechanical Basic Report (Static)')
pdf_file = os.path.join(export_directory,'Report.pdf')
ExportPDF(adr_service,static_report,pdf_file)
print(" Section Completed")
We can also view the reports from the ADR homepage. Click Reports > Generate Report. Then select a report template and click on the 'Generate Report' button.
Clean up
When we are done viewing reports, we can stop the Nexus server and close Mechanical:
print("Stop the Nexus server and close Mechanical")
adr_service.stop()
print(" Nexus server stopped.")
app.close()
print(" Mechanical closed.")
If you fail to stop the ADR server before closing your Python interpreter, you can use Task Manager to end any nginx.exe processes. If you do not stop the ADR server, you will not be able to re-run this demo in the same working directory, because you will not be able to delete the old 'Nexus' sub-folder.
If you fail to stop the ADR server before closing your Python interpreter, you can use Task Manager to end any nginx.exe processes. If you do not close Mechanical, you will not be able to re-run this demo on the same Mechanical model, because the Mechanical database file will remain locked.