How to create and use python modules (files) in Mechanical

Mike.Thompson
Mike.Thompson Member, Employee Posts: 234
First Anniversary First Comment 5 Likes Ansys Employee
edited June 2023 in Structures

This relates to how to architect and create a solution that is maturing past something that is a snippet, or a set of routines into a code base where you have multiple files with routines and variables that may need to interact with each other.

"""
In this example lets say you have a module you want to use at:
   "C:\Users\MyName\MyData\Code\MyModule.py"
There is a method in this file called "DoMyStuff()"
"""
#import the sys module and add the path to the code to sys.path so you can import from that path
import sys
CodePath = r"C:\Users\MyName\MyData\Code"
if not CodePath in sys.path:
    sys.path.append(CodePath)

import MyModule   #Imports the module for use
reload(MyModule)  #Only required if you want to save and re-read the new code
MyModule.DoMyStuff()  #Run the method from your file

Comments

  • Ray_Greene
    Ray_Greene Member Posts: 5
    First Comment

    Mike:

    When I follow your procedure with the function below in the imported module it throws errors; can't find Ansys, MessageSeverityType.Info, etc. Same function in script window or in Python Code Object DPF works fine. Help please?

    Thanks,

    Ray

    def send_WB_msg(my_msg):
    """
    function to send a message to the user through a WB message - use as we do things test, test, test, for each step or thing, the python way...
    """
    try:
    print my_msg
    except:
    msg = Ansys.Mechanical.Application.Message(my_msg, MessageSeverityType.Info)
    ExtAPI.Application.Messages.Add(msg) #test #send_WB_msg('Hello from DPF!')

  • augustbrandberg
    augustbrandberg Member Posts: 6
    Name Dropper First Comment

    Hi!

    I initially thought the updated answer was a "catch-all" for the type of problem presented by Ray. However, I encountered some trouble and it took a while to realize it was just an example of how to write Initialize() to solve that specific problem. For others who might make the same mistake as me, I append a similar problem when referencing DataModel:

    Let's say I want to get the current ForceUnit in Mechanical. From the Python scripting prompt, I can write:

    forceUnit = DataModel.CurrentUnitFromQuantityName("Force")
    

    If I use the solution proposed by Mike on July 28th, and I have my module saved in forceChecker.py, then forceChecker.py should look like this if I understand the answer correctly:

    #forceChecker.py
    ExtAPI = None; Ansys = None
    from module_base import *
    
    def Initialize(MyExtAPI, MyAnsys):
        global ExtAPI; global Ansys; 
        ExtAPI = MyExtAPI; Ansys = MyAnsys; 
    
    def getForceUnit():
        ForceUnit = DataModel.CurrentUnitFromQuantityName("Force")
        print(ForceUnit)
        return ForceUnit
    

    and I would get my force unit by running the following code in the python scripting interface:

    # main.py    
    import sys
    CodePath = r"\\pathToModules\\"
    
    if not CodePath in sys.path:
        sys.path.append(CodePath)
    
    import forceChecker
    forceChecker.Initialize(ExtAPI, Ansys)
    currentUnit = forceChecker.getForceUnit()
    

    However, doing so yields the following error message:

    "global name 'DataModel' is not defined"

    DataModel also needs to be passed to Initialize(). If we update our code and pass the DataModel to the Initialize function, everything works as expected:

    #forceChecker_v2.py
    ExtAPI = None; Ansys = None; DataModel = None;
    from module_base import *
    
    def Initialize(MyExtAPI, MyAnsys, MyDataModel):
        global ExtAPI; global Ansys; global DataModel;
        ExtAPI = MyExtAPI; Ansys = MyAnsys; DataModel = MyDataModel;
    
    def getForceUnit():
        ForceUnit = DataModel.CurrentUnitFromQuantityName("Force")
        print(ForceUnit)
        return ForceUnit
    

    and

    # main_v2.py    
    import sys
    CodePath = r"\\pathToModules\\"
    
    if not CodePath in sys.path:
        sys.path.append(CodePath)
    
    import forceChecker
    forceChecker.Initialize(ExtAPI, Ansys, DataModel)
    currentUnit = forceChecker.getForceUnit()
    
  • Mike.Thompson
    Mike.Thompson Member, Employee Posts: 234
    First Anniversary First Comment 5 Likes Ansys Employee

    @augustbrandberg ,
    You are right. There is some loaded in shorthand in the mechanical scripting console. Things like: Model, Tree, DataModel and some others are ready in the global scope. All of these are derivative of ExtAPI like ExtAPI.DataModel, so if you have ExtAPI you can get to the variables you need.

    As pointed out, you can also set whatever global module variables you want and set them in the Initialize function so your module acts in the same way as the scripting console.

  • marksimbo
    marksimbo Member Posts: 1
    Name Dropper First Comment
    edited December 2023

    Hi @Mike.Thompson

    Most of the things I can access using the ExtAPI as you wrote. I still have a problem accessing things like 'Quantity', 'Field', 'DataModelObjectCategory', ... .

    (For the DataModelObjectCategory I have found a workaround using the Ansys.ACT.Automation.Mechanical., but that's all I could find out how to do)

    Is there a way to access Quantity or Field using ExtAPI or Ansys?
    I just want to avoid to put all of them into the 'Initialize()' function of the .py file

  • Mike.Thompson
    Mike.Thompson Member, Employee Posts: 234
    First Anniversary First Comment 5 Likes Ansys Employee

    Put this in your file. This will take care of things like quantities and DataModelObjectCategory. This will generally have all the enumerations you get in the scripting console. Check the globals() after importing these. There will probably be hundreds of imported objects.

    from module_base import *