Additive MPBF FEA Process Simulation API Sample

Description

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.

Code Samples

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()))