Skip to content

Latest commit

 

History

History
1307 lines (991 loc) · 38.7 KB

File metadata and controls

1307 lines (991 loc) · 38.7 KB

Moldflow API

PyPI version Python versions License CI Status

Overview

Moldflow API is a Python wrapper library for the Synergy API, designed to simplify interactions with Autodesk Moldflow Synergy. This library provides a clean, pythonic interface to Moldflow's simulation capabilities, making it easier to integrate Moldflow functionality into your Python applications.

Requirements

  • Windows 10/11
  • Python 3.10.x - 3.13.x
  • Autodesk Moldflow Synergy 2026.0.1 or later

Installation

From PyPI

The easiest way to install Moldflow API is using pip:

pip install moldflow

From Source

If you want to install from source or contribute to the project:

git clone https://github.com/Autodesk/moldflow-api.git
cd moldflow-api
python -m pip install -r requirements.txt

Quick Start

Here's a simple example to get you started:

from moldflow import Synergy

# Initialize the API
synergy = Synergy()

# Example: Get version information
version = synergy.version
build = synergy.build
print(f"Moldflow Synergy version: {version}")
print(f"Build: {build}")

Usage Examples

Simulation Workflow

This example shows a complete simulation setup from CAD import to analysis:

import os
from moldflow import Synergy
from moldflow.common import MeshType, MoldingProcess

# Initialize Synergy
synergy = Synergy()

# Create new project and study
synergy.new_project("test", "C:\\temp\\new_project")
project = synergy.project
project.new_study("injection_molding_analysis")

# Get study document and set basic parameters
study_doc = synergy.study_doc
study_doc.molding_process = MoldingProcess.THERMOPLASTIC_INJECTION_MOLDING
study_doc.mesh_type = MeshType.MESH_FUSION

# Import CAD file (example)
cad_file = "C:\\path\\to\\your\\part.stp"
if os.path.exists(cad_file):
    import_opts = synergy.import_options
    study_doc.add_file(cad_file, import_opts, show_logs=True)

# Set up mesh generation
mesh_generator = synergy.mesh_generator
mesh_editor = synergy.mesh_editor

# Generate and validate mesh
mesh_generator.generate()
mesh_editor.auto_fix()
mesh_editor.purge_nodes()

# Get mesh diagnostics
diagnosis_mgr = synergy.diagnosis_manager
mesh_summary = diagnosis_mgr.get_mesh_summary(element_only=True)

print(f"Mesh Quality Report:")
print(f"  Elements: {mesh_summary.triangles_count}")
print(f"  Nodes: {mesh_summary.nodes_count}")

# Save the configured study
project.save_all()

print("Simulation setup complete, running analysis...")

study_doc.analyze_now(check=False, solve=True)

Material Management

Find and analyze materials with specific properties:

from moldflow import Synergy
from moldflow.common import MaterialDatabase, MaterialDatabaseType

synergy = Synergy()
material_finder = synergy.material_finder

# Search through different material databases
databases = [
    (MaterialDatabase.THERMOPLASTIC, "Thermoplastics"),
    (MaterialDatabase.THERMOSET_MATERIAL, "Thermoset Materials"),
    (MaterialDatabase.UNDERFILL_ENCAPSULANT, "Underfill Encapsulants")
]

material_catalog = {}

for db_type, db_name in databases:
    print(f"\n=== {db_name} Materials ===")
    material_finder.set_data_domain(db_type, MaterialDatabaseType.SYSTEM)

    materials = []
    material = material_finder.get_first_material()
    count = 0

    while material and count < 10:  # Limit for example
        materials.append({
            'id': material.id,
            'name': material.name,
            'type': material.type
        })
        print(f"  {material.name} (ID: {material.id})")
        material = material_finder.get_next_material(material)
        count += 1

    material_catalog[db_name] = materials

print(f"\nTotal material categories found: {len(material_catalog)}")

Mesh Analysis

Perform detailed mesh quality analysis and diagnostics:

from moldflow import Synergy

synergy = Synergy()
diagnosis_mgr = synergy.diagnosis_manager
mesh_editor = synergy.mesh_editor

# Get comprehensive mesh summary
mesh_summary = diagnosis_mgr.get_mesh_summary(
    element_only=False,
    inc_beams=True,
    inc_match=True,
    recalculate=True
)

# Detailed mesh quality report
print("=== Comprehensive Mesh Analysis ===")
print(f"Geometry:")
print(f"  Triangles: {mesh_summary.triangles_count:,}")
print(f"  Tetrahedra: {mesh_summary.tetras_count:,}")
print(f"  Beams: {mesh_summary.beams_count:,}")
print(f"  Nodes: {mesh_summary.nodes_count:,}")

print(f"\nQuality Metrics:")
print(f"  Aspect Ratio - Min: {mesh_summary.min_aspect_ratio:.3f}")
print(f"  Aspect Ratio - Max: {mesh_summary.max_aspect_ratio:.3f}")
print(f"  Aspect Ratio - Avg: {mesh_summary.ave_aspect_ratio:.3f}")
print(f"  Max Dihedral Angle: {mesh_summary.max_dihedral_angle:.1f}°")
print(f"  Max Volume Ratio: {mesh_summary.max_volume_ratio:.3f}")

print(f"\nMesh Integrity:")
print(f"  Free Edges: {mesh_summary.free_edges_count}")
print(f"  Manifold Edges: {mesh_summary.manifold_edges_count}")
print(f"  Non-Manifold Edges: {mesh_summary.non_manifold_edges_count}")
print(f"  Connectivity Regions: {mesh_summary.connectivity_regions}")

print(f"\nMesh Issues:")
print(f"  Intersection Elements: {mesh_summary.intersection_elements}")
print(f"  Overlap Elements: {mesh_summary.overlap_elements}")
print(f"  Zero Triangles: {mesh_summary.zero_triangles}")
print(f"  Zero Beams: {mesh_summary.zero_beams}")
print(f"  Unoriented Elements: {mesh_summary.unoriented}")

# Perform mesh diagnostics and repairs
if mesh_summary.intersection_elements > 0:
    print(f"\n⚠️  Found {mesh_summary.intersection_elements} intersection elements")
    print("Running auto-fix...")
    fixed_elements = mesh_editor.auto_fix()
    print(f"Fixed {fixed_elements} elements")

# Additional mesh operations
entity_list = mesh_editor.create_entity_list()
print(f"Created entity list for further operations")

# Assess overall mesh quality
if mesh_summary.ave_aspect_ratio > 10:
    print("⚠️  Average aspect ratio is high - consider mesh refinement")

if mesh_summary.free_edges_count > mesh_summary.triangles_count * 0.1:
    print("⚠️  High number of free edges detected - check mesh integrity")

print(f"\n✓ Mesh analysis complete")

Project and Study Management

Project management with multiple studies and configurations:

from moldflow import Synergy
from moldflow.common import MoldingProcess, MeshType

synergy = Synergy()
project = synergy.project

# Create multiple studies for different scenarios
study_configs = [
    ("thermoplastic_study", MoldingProcess.THERMOPLASTIC_INJECTION_MOLDING, "Standard thermoplastic injection"),
    ("gas_assisted_study", MoldingProcess.GAS_ASSISTED_INJECTION_MOLDING, "Gas-assisted injection molding"),
    ("overmolding_study", MoldingProcess.THERMOPLASTICS_OVERMOLDING, "Thermoplastic overmolding"),
    ("compression_study", MoldingProcess.THERMOPLASTICS_INJECTION_COMPRESSION_MOLDING, "Injection-compression molding")
]

created_studies = []

for study_name, process, description in study_configs:
    print(f"Creating study: {study_name}")

    # Create new study
    success = project.new_study(study_name)
    if success:
        study_doc = synergy.study_doc
        study_doc.molding_process = process

        # Set mesh type based on study type
        if "compression" in study_name:
            study_doc.mesh_type = MeshType.MESH_3D
        else:
            study_doc.mesh_type = MeshType.MESH_MIDPLANE

        created_studies.append({
            'name': study_name,
            'process': process,
            'mesh_type': study_doc.mesh_type,
            'description': description
        })

        print(f"  ✓ {study_name} configured with {process.value}")
    else:
        print(f"  ✗ Failed to create {study_name}")

# Save all studies
project.save_all()

print(f"\nProject Summary:")
print(f"Successfully created {len(created_studies)} studies:")
for study in created_studies:
    print(f"  - {study['name']}: {study['description']}")

Batch Processing Example

Process multiple files or configurations automatically:

import os
import glob
from moldflow import Synergy

def process_cad_files(file_pattern):
    """Process multiple CAD files through Moldflow analysis setup."""

    synergy = Synergy()
    cad_files = glob.glob(file_pattern)

    if not cad_files:
        print(f"No files found matching pattern: {file_pattern}")
        return

    print(f"Found {len(cad_files)} CAD files to process")

    results = []

    for i, cad_file in enumerate(cad_files, 1):
        filename = os.path.basename(cad_file)
        study_name = f"auto_{os.path.splitext(filename)[0]}"

        print(f"\n[{i}/{len(cad_files)}] Processing: {filename}")

        try:
            # Create new study
            project = synergy.project
            project.new_study(study_name)

            # Import CAD file
            import_opts = synergy.import_options
            study_doc = synergy.study_doc

            success = study_doc.add_file(cad_file, import_opts, show_logs=False)

            if success:
                # Quick mesh setup
                mesh_generator = synergy.mesh_generator
                mesh_generator.generate()

                mesh_editor = synergy.mesh_editor
                mesh_editor.auto_fix()

                # Get basic mesh info
                diagnosis_mgr = synergy.diagnosis_manager
                mesh_summary = diagnosis_mgr.get_mesh_summary(element_only=True)

                result = {
                    'file': filename,
                    'study': study_name,
                    'elements': mesh_summary.triangles_count,
                    'nodes': mesh_summary.nodes_count,
                    'quality': mesh_summary.ave_aspect_ratio,
                    'status': 'Success'
                }

                print(f"  ✓ Mesh: {mesh_summary.triangles_count:,} elements")
                print(f"  ✓ Quality: {mesh_summary.ave_aspect_ratio:.2f} avg aspect ratio")

            else:
                result = {
                    'file': filename,
                    'study': study_name,
                    'status': 'Failed - Import Error'
                }
                print(f"  ✗ Failed to import CAD file")

            results.append(result)

        except Exception as e:
            print(f"  ✗ Error processing {filename}: {str(e)}")
            results.append({
                'file': filename,
                'status': f'Error: {str(e)}'
            })

    # Save all work
    synergy.project.save_all()

    # Summary report
    print(f"\n=== Batch Processing Summary ===")
    successful = len([r for r in results if r.get('status') == 'Success'])
    print(f"Successfully processed: {successful}/{len(results)} files")

    for result in results:
        if result.get('status') == 'Success':
            print(f"  ✓ {result['file']}: {result['elements']:,} elements")
        else:
            print(f"  ✗ {result['file']}: {result['status']}")

    return results

# Example usage:
results = process_cad_files("C:\\CAD_Files\\*.stp", "C:\\Moldflow_Results\\")

Advanced Utilities

Unit Conversion

from moldflow import Synergy
from moldflow.common import SystemUnits

synergy = Synergy()
uc = synergy.unit_conversion  # UnitConversion

si = uc.convert_to_si("mm", 25.4)
inch = uc.convert_to_unit("in", SystemUnits.ENGLISH, si)
desc = uc.get_unit_description("mm", SystemUnits.METRIC)

print(f"25.4 mm in SI: {si}")
print(f"SI back to inches: {inch}")
print(f"Unit description (Metric): {desc}")

Create Plot by Dataset and Export Results

from moldflow import Synergy
from moldflow.common import PlotType, SystemUnits

synergy = Synergy()
pm = synergy.plot_manager  # PlotManager

# Derive a dataset id from the first available plot (avoids hard-coding)
plot = pm.get_first_plot()
if plot:
    ds_id = plot.data_id
    plot = pm.create_plot_by_ds_id(ds_id, PlotType.PLOT_DEFAULT_PLOT_FOR_DATA_ID)
if plot:
    print(f"Plot created: {plot.name}")

    # Inspect data display format
    fmt = pm.get_data_display_format(plot.data_id)
    print(f"Display format: {fmt}")

    # Save result data as XML
    ok = pm.save_result_data_in_xml(plot.data_id, "results.xml", SystemUnits.STANDARD)
    print(f"Saved XML: {ok}")

Viewer Controls and Screenshot

from moldflow import Synergy
from moldflow.common import ViewModes, StandardViews

synergy = Synergy()
viewer = synergy.viewer

viewer.reset()
viewer.set_view_mode(ViewModes.PERSPECTIVE_PROJECTION)
viewer.fit()
viewer.go_to_standard_view(StandardViews.ISOMETRIC)
viewer.save_image("C:\\temp\\snapshot.png", x=1920, y=1080, result=True, legend=True, axis=True)

Iterate Plots and Export Overlays

from moldflow import Synergy

synergy = Synergy()
pm = synergy.plot_manager
viewer = synergy.viewer

plot = pm.get_first_plot()
while plot:
    print(f"Plot: {plot.name}")
    try:
        viewer.save_plot_scale_image(f"C:\\temp\\{plot.name}_scale.png")
        viewer.save_axis_image(f"C:\\temp\\{plot.name}_axis.png")
    except Exception as e:
        print(f"Overlay export not available: {e}")
    plot = pm.get_next_plot(plot)

More Advanced Examples

Derived results from existing plots (absolute temperature)

from moldflow import Synergy
from moldflow.common import UserPlotType, TransformScalarOperations

synergy = Synergy()
pm = synergy.plot_manager
dt = synergy.data_transform

# Absolute temperature:K = Bulk temperature:13 + 273
ds_id = pm.find_dataset_id_by_name("Bulk temperature")
all_times = synergy.create_double_array()
pm.get_indp_values(ds_id, all_times)
target_time = 13.0
closest_time = min(all_times.to_list(), key=lambda t: abs(t - target_time))
indp = synergy.create_double_array()
indp.add_double(closest_time)

ent = synergy.create_integer_array()
vals = synergy.create_double_array()
pm.get_scalar_data(ds_id, indp, ent, vals)

dt.scalar(ent, vals, TransformScalarOperations.ADD, 273.0, ent, vals)

up = pm.create_user_plot()
up.set_name("Absolute Temperature")
up.set_data_type(UserPlotType.ELEMENT_DATA)
up.set_dept_unit_name("K")
up.set_scalar_data(ent, vals)
up.build()

Vector difference with absolute value

from moldflow import Synergy
from moldflow.common import TransformOperations, TransformFunctions, UserPlotType

synergy = Synergy()
pm = synergy.plot_manager
dt = synergy.data_transform

name = "Average velocity"  # 3-component vector
ds_id = pm.find_dataset_id_by_name(name)

times = synergy.create_double_array()
pm.get_indp_values(ds_id, times)
t1, t2 = 1.1, 2.0
c1 = min(times.to_list(), key=lambda t: abs(t - t1))
c2 = min(times.to_list(), key=lambda t: abs(t - t2))

def get_vec_at(t):
    indp = synergy.create_double_array()
    indp.add_double(t)
    ent = synergy.create_integer_array()
    vx = synergy.create_double_array()
    vy = synergy.create_double_array()
    vz = synergy.create_double_array()
    pm.get_vector_data(ds_id, indp, ent, vx, vy, vz)
    return ent, (vx, vy, vz)

ent1, (v1x, v1y, v1z) = get_vec_at(c1)
ent2, (v2x, v2y, v2z) = get_vec_at(c2)

vdx = synergy.create_double_array()
vdy = synergy.create_double_array()
vdz = synergy.create_double_array()
for a, b, out in [(v1x, v2x, vdx), (v1y, v2y, vdy), (v1z, v2z, vdz)]:
    dt.op(ent1, a, TransformOperations.SUBTRACT, ent2, b, ent1, out)
    dt.func(TransformFunctions.ABSOLUTE, ent1, out, ent1, out)

up = pm.create_user_plot()
up.set_name("Difference in Velocity")
up.set_data_type(UserPlotType.ELEMENT_DATA)
up.set_vector_as_displacement(False)
up.set_dept_unit_name("m/s")
up.set_vector_data(ent1, vdx, vdy, vdz)
up.build()

Operate on current plot

from moldflow import Synergy
from moldflow.common import TransformFunctions, UserPlotType

synergy = Synergy()
pm = synergy.plot_manager
viewer = synergy.viewer
dt = synergy.data_transform

active = viewer.active_plot
if active is None:
    raise RuntimeError("No active plot")
ds_id = active.data_id

cols = pm.get_data_nb_components(ds_id)
dtype = pm.get_data_type(ds_id)

times = synergy.create_double_array()
pm.get_indp_values(ds_id, times)
indp = None
if times.size > 0:
    indp = synergy.create_double_array()
    indp.add_double(times.to_list()[0])

ent = synergy.create_integer_array()
arrs = [synergy.create_double_array() for _ in range(max(cols, 1))]

if cols == 1:
    pm.get_scalar_data(ds_id, indp, ent, arrs[0])
elif cols == 3:
    pm.get_vector_data(ds_id, indp, ent, arrs[0], arrs[1], arrs[2])
elif cols == 6:
    pm.get_tensor_data(ds_id, indp, ent, *arrs)
else:
    raise RuntimeError("Unsupported component count")

for a in arrs[:cols]:
    dt.func(TransformFunctions.ABSOLUTE, ent, a, ent, a)

up = pm.create_user_plot()
up.set_name("ABS of current plot")
up.set_data_type(UserPlotType.ELEMENT_DATA if dtype == "ELDT" else UserPlotType.NODE_DATA)
up.set_vector_as_displacement(dtype == "NDDT")
if cols == 1:
    up.set_scalar_data(ent, arrs[0])
elif cols == 3:
    up.set_vector_data(ent, arrs[0], arrs[1], arrs[2])
elif cols == 6:
    up.set_tensor_data(ent, *arrs[:6])
up.build()

Normalize by max at a time

from moldflow import Synergy
from moldflow.common import TransformScalarOperations, UserPlotType

synergy = Synergy()
pm = synergy.plot_manager
dt = synergy.data_transform

name = "Pressure"
ds_id = pm.find_dataset_id_by_name(name)

times = synergy.create_double_array()
pm.get_indp_values(ds_id, times)
target_time = 3.5
closest = min(times.to_list(), key=lambda t: abs(t - target_time))
indp = synergy.create_double_array()
indp.add_double(closest)

ent = synergy.create_integer_array()
vals = synergy.create_double_array()
pm.get_scalar_data(ds_id, indp, ent, vals)

max_v = max(vals.to_list())
out = synergy.create_double_array()
dt.scalar(ent, vals, TransformScalarOperations.DIVIDE, max_v, ent, out)

up = pm.create_user_plot()
up.set_name("Pre/Max")
up.set_data_type(UserPlotType.ELEMENT_DATA)
up.set_dept_unit_name("%")
up.set_scalar_data(ent, out)
up.build()

Average/min/max diagnostics to console

from moldflow import Synergy

synergy = Synergy()
diag = synergy.diagnosis_manager

ent = synergy.create_integer_array()
vals = synergy.create_double_array()
count = diag.get_aspect_ratio_diagnosis(0.0, 0.0, True, False, ent, vals)
if count > 0:
    data = vals.to_list()
    ave = sum(data) / len(data)
    print(f"Average aspect ratio: {ave:.3f}")

Entity selection strings

from moldflow import Synergy

synergy = Synergy()
el = synergy.study_doc.create_entity_list()
el.select_from_string("N1:2 N5 N8:9")
print(el.size)
print(el.convert_to_string())

Configure Import Options Before CAD Import

from moldflow import Synergy
from moldflow.common import MeshType, ImportUnits

synergy = Synergy()
io = synergy.import_options  # ImportOptions

# Set import preferences
io.mesh_type = MeshType.MESH_FUSION
io.units = ImportUnits.MM
io.use_mdl = True
io.mdl_mesh = True
io.mdl_surfaces = True
io.mdl_auto_edge_select = True

# Use with study import
study_doc = synergy.study_doc
ok = study_doc.add_file("C:\\models\\part.stp", io, show_logs=True)
print(f"Import success: {ok}")

Tune Plot Appearance and Export

from moldflow import Synergy

synergy = Synergy()
pm = synergy.plot_manager

# Create or fetch a plot without hard-coded ids
plot = pm.get_first_plot()

if plot:
    # Adjust plot attributes
    plot.name = "Analysis Plot"
    plot.number_of_frames = 30
    plot.number_of_contours = 12
    plot.mesh_fill = 0.35

    # Regenerate and export results data as XML
    plot.regenerate()
    pm.save_result_data_in_xml(plot.data_id, "results.xml", "Metric")

Viewer: Animation, Clipping Planes, and Bookmarks

from moldflow import Synergy
from moldflow.common import AnimationSpeed, StandardViews, ViewModes

synergy = Synergy()
viewer = synergy.viewer

# Camera setup
viewer.reset()
viewer.set_view_mode(ViewModes.PERSPECTIVE_PROJECTION)
viewer.fit()
viewer.go_to_standard_view(StandardViews.ISOMETRIC)

# Save a quick animation
viewer.save_animation("C:\\temp\\turntable.mp4", AnimationSpeed.MEDIUM, prompts=False)

# Create a default clipping plane (avoids needing an id)
viewer.create_default_clipping_plane()

# Create a bookmark for the current view (vectors can be None to use current state)
viewer.add_bookmark(
    name="IsoView",
    normal_view=None,
    up_view=None,
    focal_point=None,
    eye_position=None,
    clipping_range_min=0.0,
    clipping_range_max=1.0,
    view_angle=30.0,
    parallel_scale=1.0,
)

Publish Shared View (LMV)

from moldflow import Synergy

synergy = Synergy()
url = synergy.export_lmv_shared_views("My Shared Analysis View")
print(f"Shared view published at: {url}")

API Coverage Examples

Boundary Conditions

from moldflow import Synergy
from moldflow.common import AnalysisType

synergy = Synergy()
bc = synergy.boundary_conditions
entity_list = synergy.study_doc.get_first_node()
# Create pin constraints for WARP
created = bc.create_pin_constraints(entity_list, AnalysisType.WARP)
print(f"Pin constraints created: {created}")

Circuit Generator

from moldflow import Synergy

 synergy = Synergy()
 cg = synergy.circuit_generator
 cg.diameter = 4.0
 cg.distance = 3.0
 cg.spacing = 12.0
 cg.num_channels = 4
 cg.delete_old = True
 cg.use_hoses = True
 ok = cg.generate()
 print(f"Circuit generated: {ok}")

Data Transform

from moldflow import Synergy
from moldflow.common import TransformScalarOperations

synergy = Synergy()
dt = synergy.data_transform
ia = synergy.create_integer_array()
ia.from_list([1, 2, 3])
da = synergy.create_double_array()
da.from_list([0.5, 1.5, 2.5])
ib = synergy.create_integer_array()
db = synergy.create_double_array()
ok = dt.scalar(ia, da, TransformScalarOperations.MULTIPLY, 2.0, ib, db)
print(f"Scalar transform ok: {ok}")

Folder Manager

from moldflow import Synergy

synergy = Synergy()
fm = synergy.folder_manager
root = fm.create_entity_list()
fm.create_child_folder(root)
fm.create_child_layer(root)
first = fm.get_first()
while first:
    print("Folder/Layer found")
    first = fm.get_next(first)

Layer Manager

from moldflow import Synergy

synergy = Synergy()
lm = synergy.layer_manager
lm.create_layer_by_name("MyLayer")
lyr = lm.get_first()
while lyr:
    print("Layer found")
    lyr = lm.get_next(lyr)

Material Selector

from moldflow import Synergy
from moldflow.common import MaterialIndex

synergy = Synergy()
ms = synergy.material_selector
# Query current material file for first molding material
print(ms.get_material_file(MaterialIndex.FIRST))

Mesh Generator

from moldflow import Synergy

synergy = Synergy()
mg = synergy.mesh_generator
mg.edge_length = 2.0
mg.merge_tolerance = 0.05
mg.match = True
ok = mg.generate()
print(f"Mesh generated: {ok}")

Model Duplicator

from moldflow import Synergy

synergy = Synergy()
md = synergy.model_duplicator
md.num_cavities = 2
md.by_columns = True
md.num_cols = 2
md.num_rows = 1
md.x_spacing = 50.0
md.y_spacing = 40.0
ok = md.generate()
print(f"Model duplicated: {ok}")

Modeler

from moldflow import Synergy

synergy = Synergy()
modeler = synergy.modeler
v = synergy.create_vector()
v.set_xyz(0.0, 0.0, 0.0)
node_list = modeler.create_node_by_xyz(v)
print("Created node list")

Mold Surface Generator

from moldflow import Synergy

synergy = Synergy()
msg = synergy.mold_surface_generator
msg.centered = True
dim = synergy.create_vector()
dim.set_xyz(100.0, 80.0, 60.0)
msg.dimensions = dim
ok = msg.generate()
print(f"Mold surface generated: {ok}")

Predicate Manager

from moldflow import Synergy

synergy = Synergy()
pmgr = synergy.predicate_manager
pmgr.create_thickness_predicate(0.5, 3.0)
print("Created thickness predicate")

Runner Generator

from moldflow import Synergy

synergy = Synergy()
rg = synergy.runner_generator
rg.sprue_x = 0.0
rg.sprue_y = 0.0
rg.sprue_length = 30.0
rg.sprue_diameter = 6.0
rg.sprue_taper_angle = 2.0
ok = rg.generate()
print(f"Runner generated: {ok}")

Build PowerPoint Report (using python-pptx)

This example emulates a report workflow directly from Python, using python-pptx to author a PPTX. See python-pptx on PyPI: https://pypi.org/project/python-pptx

import os
import tempfile
from moldflow import Synergy
from moldflow.common import StandardViews

# pip install python-pptx
from pptx import Presentation
from pptx.util import Inches, Pt

synergy = Synergy()
sd = synergy.study_doc
pm = synergy.plot_manager
viewer = synergy.viewer
project = synergy.project

# Ensure a visible viewport for captures
viewer.set_view_size(1600, 900)

# Result overlay flags
PLOT_RESULT = True
PLOT_LEGEND = True
PLOT_AXIS = True
PLOT_ROTATION = True
PLOT_SCALE_BAR = True
PLOT_PLOT_INFO = True
PLOT_STUDY_TITLE = True
PLOT_RULER = True
PLOT_LOGO = True

def capture_plot_image(plot, orientation: str | None, width_px=1600, height_px=900) -> str:
    # Apply orientation and fit the view
    if orientation and orientation.upper() != "CURRENT":
        target = orientation.title() if isinstance(orientation, str) else orientation
        viewer.go_to_standard_view(target)
    # Show plot (if provided) or ensure geometry-only capture
    if plot is not None:
        viewer.show_plot(plot)
        # Ensure plot is generated and a valid frame is visible
        try:
            plot.regenerate()
        except Exception:
            pass
        try:
            # Show the last frame if available
            frames = viewer.get_number_frames_by_name(plot.name)
            if isinstance(frames, int) and frames > 0:
                viewer.show_plot_frame(plot, frames - 1)
        except Exception:
            pass
    else:
        # Hide any active plot to avoid empty result layer when capturing geometry
        ap = viewer.active_plot
        if ap is not None:
            viewer.hide_plot(ap)
    viewer.fit()

    tmp = tempfile.NamedTemporaryFile(prefix="mf_plot_", suffix=".png", delete=False)
    tmp.close()

    # Use full overlay flags when capturing results; turn them off for geometry-only
    if plot is not None:
        viewer.save_image(
            tmp.name,
            x=width_px,
            y=height_px,
            result=PLOT_RESULT,
            legend=PLOT_LEGEND,
            axis=PLOT_AXIS,
            rotation=PLOT_ROTATION,
            scale_bar=PLOT_SCALE_BAR,
            plot_info=PLOT_PLOT_INFO,
            study_title=PLOT_STUDY_TITLE,
            ruler=PLOT_RULER,
            logo=PLOT_LOGO,
        )
    else:
        viewer.save_image(tmp.name, x=width_px, y=height_px, result=False, legend=False, axis=False)

    return tmp.name

prs = Presentation()
title_layout = prs.slide_layouts[0]
title_only_layout = prs.slide_layouts[5]

def add_title(slide, text: str):
    if slide.shapes.title is not None:
        slide.shapes.title.text = text
        slide.shapes.title.text_frame.paragraphs[0].font.size = Pt(28)

def add_picture_centered(slide, image_path: str, max_width_in=10.0, top_in=1.5):
    pic = slide.shapes.add_picture(image_path, Inches(0), Inches(top_in))
    page_w = prs.slide_width
    max_w = Inches(max_width_in)
    if pic.width > max_w:
        scale = max_w / pic.width
        pic.width = int(pic.width * scale)
        pic.height = int(pic.height * scale)
    pic.left = int((page_w - pic.width) / 2)
    return pic

slide = prs.slides.add_slide(title_layout)
add_title(slide, f"Report: {sd.study_name}")
if slide.placeholders and len(slide.placeholders) > 1:
    slide.placeholders[1].text = f"Project: {project.path}"

geo_slide = prs.slides.add_slide(title_only_layout)
add_title(geo_slide, "Geometry Overview")
viewer.reset()
viewer.go_to_standard_view(StandardViews.ISOMETRIC)
viewer.fit()
geo_img = capture_plot_image(plot=None, orientation="CURRENT")
add_picture_centered(geo_slide, geo_img)

diag = synergy.diagnosis_manager
summary = diag.get_mesh_summary(element_only=False)
mesh_slide = prs.slides.add_slide(title_only_layout)
add_title(mesh_slide, "Mesh Summary")
tf = mesh_slide.shapes.add_textbox(Inches(1), Inches(1.5), Inches(8), Inches(3)).text_frame
tf.word_wrap = True
lines = [
    f"Nodes: {summary.nodes_count}",
    f"Triangles: {summary.triangles_count}",
    f"Tetras: {summary.tetras_count}",
    f"Beams: {summary.beams_count}",
    f"AspectRatio avg/min/max: {summary.ave_aspect_ratio:.3f} / {summary.min_aspect_ratio:.3f} / {summary.max_aspect_ratio:.3f}",
]
for i, line in enumerate(lines):
    p = tf.add_paragraph() if i else tf.paragraphs[0]
    p.text = line
    p.font.size = Pt(16)

def add_plot_slide(plot_obj, title: str, orientation: str = "ISOMETRIC"):
    slide = prs.slides.add_slide(title_only_layout)
    add_title(slide, title)
    img = capture_plot_image(plot_obj, orientation)
    if img:
        add_picture_centered(slide, img)
        try:
            os.remove(img)
        except Exception:
            pass
    else:
        tx = slide.shapes.add_textbox(Inches(1), Inches(1.5), Inches(8), Inches(1.0)).text_frame
        tx.text = "Skipped non-mesh/unsupported plot for 3D capture"
        tx.paragraphs[0].font.size = Pt(14)

def add_plot_four_views(plot_obj, title: str):
    slide = prs.slides.add_slide(title_only_layout)
    add_title(slide, f"{title} - Four Views")
    views = ["ISOMETRIC", "FRONT", "LEFT", "TOP"]
    cols = 2
    x0, y0 = Inches(0.7), Inches(1.3)
    cell_w, cell_h = Inches(4.5), Inches(3.2)
    for idx, v in enumerate(views):
        img = capture_plot_image(plot_obj, v, width_px=1000, height_px=600)
        col = idx % cols
        row = idx // cols
        left = x0 + col * (cell_w + Inches(0.2))
        top = y0 + row * (cell_h + Inches(0.2))
        if img:
            pic = slide.shapes.add_picture(img, left, top)
            if pic.width > cell_w:
                scale = cell_w / pic.width
                pic.width = int(pic.width * scale)
                pic.height = int(pic.height * scale)
            if pic.height > cell_h:
                scale = cell_h / pic.height
                pic.width = int(pic.width * scale)
                pic.height = int(pic.height * scale)
            try:
                os.remove(img)
            except Exception:
                pass

plot = pm.get_first_plot()
count = 0
while plot:
    name = plot.name
    add_plot_slide(plot, name, orientation="ISOMETRIC")
    if count < 2:
        add_plot_four_views(plot, name)
    plot = pm.get_next_plot(plot)
    count += 1

out_path = os.path.join(project.path, f"{os.path.splitext(sd.study_name)[0]}_report.pptx")
prs.save(out_path)
print(f"Report saved: {out_path}")

Study Document

from moldflow import Synergy

synergy = Synergy()
sd = synergy.study_doc
print(f"Study name: {sd.study_name}")
saved = sd.save()
print(f"Saved: {saved}")

System Messages

from moldflow import Synergy
from moldflow.common import SystemUnits

synergy = Synergy()
sm = synergy.system_message
sa = synergy.create_string_array()
sa.from_list(["Diameter", "Length"])
da = synergy.create_double_array()
da.from_list([6.0, 30.0])
msg = sm.get_data_message(100, sa, da, SystemUnits.METRIC)
print(msg)

Arrays and Geometry Helpers

from moldflow import Synergy

synergy = Synergy()
ia = synergy.create_integer_array()
ia.from_list([1, 2, 3])
print(ia.size)
da = synergy.create_double_array()
da.from_list([0.1, 0.2])
sa = synergy.create_string_array()
sa.from_list(["A", "B"])
vec = synergy.create_vector()
vec.set_xyz(1.0, 2.0, 3.0)
va = synergy.create_vector_array()

API Reference

For detailed API documentation, see the :doc:`moldflow` section.

Development Setup

If you're interested in contributing to the project:

# Clone the repository
git clone https://github.com/Autodesk/moldflow-api.git
cd moldflow-api

# Install development dependencies
python -m pip install -r requirements.txt

# Run tests
python run.py test

# Run linting
python run.py lint

# Format code
python run.py format

Available Commands

The project includes a run.py script with several useful commands:

  • python run.py build - Build the package
  • python run.py test - Run tests
  • python run.py lint - Run code linting
  • python run.py format - Format code with black
  • python run.py build-docs - Build versioned documentation (HTML uses git tags for the navigation dropdown; run git fetch --tags locally before building)
    • --skip-build (-s): Skip building the package before generating docs
    • --local (-l): Build documentation locally for a single version (skips multi-version build)

Note

When releasing a new version, update switcher.json in docs/source/_static/ to include the new tag in the version dropdown.

Viewing Documentation Locally

After building, serve the documentation using Python's built-in HTTP server:

cd docs/build/html
python -m http.server 8000

Then open http://localhost:8000 in your browser.

Versioned Documentation Features:

  • Each git tag creates a separate documentation version (e.g., /v26.0.5/)
  • A /latest/ directory points to the newest version (symlink on Unix, copy on Windows)
  • Root (/) automatically redirects to /latest/ for convenience
  • Version switcher dropdown in the navigation bar allows switching between versions

Contributing

We welcome contributions! Please see our Contributing Guide for details on how to contribute to this project.

Reporting Issues

If you encounter any problems or have feature requests, please file an issue on our GitHub Issues page.

For security vulnerabilities, please see our Security Policy.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Links