Demonstrates how to automate an additive Metal Powder Bed Fusion (MPBF) thermo-mechanical process simulation using the Additive FEA API.
The script creates a new Fusion design containing a simple box body, switches to the MANUFACTURE workspace, and creates an Additive setup using the Autodesk Generic MPBF machine and the MPBF 60 micron print setting from Fusion's library.
It then constructs an FEA input deck with the AdditiveFEADeckBuilder, configuring a thermo-mechanical analysis (layers per element, coarsening generations, adaptivity, STL tolerance, ambient and final temperatures, build plate Z bounds and XY extension, convection, and an STL map). The deck is applied to a process simulation operation, the PRM file derived from the print settings is attached, and the simulation is run via generateToolpath. The script parses signalFileContents to confirm a successful finish, then builds a second deck and uses AdditiveFEAModifyUtility.warpTriangleMesh to generate a warped triangle mesh from the simulation results.
import adsk.core, adsk.fusion, adsk.cam, traceback, json
def run(context):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
# Create a new document with a box body
app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
design = app.activeProduct
sketches = design.rootComponent.sketches
sketch = sketches.add(design.rootComponent.xYConstructionPlane)
lines = sketch.sketchCurves.sketchLines
lines.addTwoPointRectangle(adsk.core.Point3D.create(0, 0, 0),
adsk.core.Point3D.create(8, 6, 0))
prof = sketch.profiles.item(0)
extrudes = design.rootComponent.features.extrudeFeatures
extInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
extInput.setDistanceExtent(False, adsk.core.ValueInput.createByReal(4))
extrudes.add(extInput)
# Switch to the CAM workspace
ui.workspaces.itemById('CAMEnvironment').activate()
cam = adsk.cam.CAM.cast(app.activeDocument.products.itemByProductType('CAMProductType'))
# Find the Autodesk Generic MPBF machine and MPBF 60 micron print setting
camManager = adsk.cam.CAMManager.get()
libraryManager = camManager.libraryManager
machineLibrary = libraryManager.machineLibrary
printSettingLibrary = libraryManager.printSettingLibrary
machineQuery = machineLibrary.createQuery(
adsk.cam.LibraryLocations.Fusion360LibraryLocation, "", "")
printSettingQuery = printSettingLibrary.createQuery(
adsk.cam.LibraryLocations.Fusion360LibraryLocation)
printSettingQuery.technology = ""
printSettingQuery.name = ""
machine = None
for m in machineQuery.execute():
if "Autodesk" in m.vendor and "Generic MPBF" in m.model:
machine = m
break
printSetting = None
for ps in printSettingQuery.execute():
if "MPBF 60 micron" in ps.name:
printSetting = ps
break
# Create an MPBF additive setup
models = [cam.designRootOccurrence.bRepBodies.item(0)]
setupInput = cam.setups.createInput(adsk.cam.OperationTypes.AdditiveOperation)
setupInput.models = models
setupInput.machine = machine
setupInput.printSetting = printSetting
setup = cam.setups.add(setupInput)
# FEA mesh and simulation parameters
pbpa = 26 # layers per element
pblr = 3 # coarsening generations
adap = 4 # adaptivity level
stol = 0.05 # STL tolerance
sbxyLeft = sbxyRight = sbxyFront = sbxyBack = 5
zTopPlate = 0
zBotPlate = -25
# Aliases for readability
CARD = adsk.cam.AdditiveFEACard
ANTP = adsk.cam.AdditiveFEAAnalysisType
STLM = adsk.cam.AdditiveFEASTLConfiguration
stlFileBasename = "part0.stl"
prmFileBasename = "prm0.prm"
# Build the FEA input deck
deck = adsk.cam.AdditiveFEADeckBuilder.create()
stlMap = deck.createSTLMap()
stlMap.append(STLM.Part, 1, 1, 1.0)
conv = deck.createConvection()
conv.append(25.0e-6, 25.0)
cards = [
deck.createStringCard(CARD.TitleCard, "Additive FEA sample - thermo-mechanical analysis"),
deck.createIntCard(CARD.AnalysisTypeCard, ANTP.ThermoMechanical),
deck.createIntCard(CARD.LayersPerElementCard, pbpa),
deck.createIntCard(CARD.CoarseningGenerationsCard, pblr),
deck.createIntCard(CARD.AdaptivityCard, adap),
deck.createDoubleCard(CARD.STLToleranceCard, stol),
deck.createDoubleCard(CARD.AmbientTemperatureCard, 25.0),
deck.createDoubleCard(CARD.FinalTemperatureCard, 25.0),
deck.createVoidCard(CARD.BinaryOutputCard),
deck.createVoidCard(CARD.EnsightOutputCard),
deck.createVoidCard(CARD.NoOffCoreCard),
deck.createVoidCard(CARD.OnCore1Card),
deck.createBuildPlateZBoundsCard(zTopPlate, zBotPlate),
deck.createSTLMapCard(stlMap),
deck.createStringArrayCard(CARD.PRMsCard, [prmFileBasename]),
deck.createStringArrayCard(CARD.STLsCard, [stlFileBasename]),
deck.createBuildPlateXYExtensionCard(sbxyLeft, sbxyRight, sbxyFront, sbxyBack),
deck.createConvectionCard(conv),
deck.createDiskCheckCard(-1, -1.0),
deck.createVoidCard(CARD.EndCard)
]
for card in cards:
deck.append(card)
# Create the simulation operation and apply the deck
simInput = setup.operations.createInput("additive_process_simulation")
simInput.applyDeck(deck)
# Attach the PRM file derived from the print settings
simInput.addSolverFileContent(prmFileBasename, simInput.base64PRMData)
simOp = setup.operations.add(simInput)
# Run the simulation
future = cam.generateToolpath(simOp)
while not future.isGenerationCompleted:
adsk.doEvents()
if simOp.hasError:
ui.messageBox('FEA simulation failed: ' + simOp.error)
return
# Parse the signal file to confirm successful completion
isSuccess = False
for line in simOp.signalFileContents.splitlines():
line = line.strip()
if not line:
continue
signal = json.loads(line)
if 'information' in signal and signal['information'] == 'successful finish':
isSuccess = True
if not isSuccess:
ui.messageBox('FEA simulation finished but did not report a successful completion.')
return
# Generate a warped triangle mesh from the simulation results
warpDeck = adsk.cam.AdditiveFEADeckBuilder.create()
warpCards = [
warpDeck.createWarpInputCard(ANTP.Mechanical),
warpDeck.createDoubleCard(CARD.WarpCard, 10.0), # exaggerated warp factor for visible results
warpDeck.createIntCard(CARD.MaxRefinementLevelCard, 5),
warpDeck.createIntCard(CARD.STLIndexCard, 1),
warpDeck.createIntCard(CARD.IncrementOffsetCard, -2) # next to last time step
]
for warpCard in warpCards:
warpDeck.append(warpCard)
feaUtility = simOp.modifyUtility(adsk.cam.ModifyUtilityTypes.AdditiveFEAModifyUtility)
warpResult = feaUtility.warpTriangleMesh(warpDeck)
if not warpResult:
ui.messageBox('warpTriangleMesh returned false.')
return
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))