""" NX Journal Script to Extract Part Mass and Material Properties This script extracts: - Mass (kg) - Volume (mm^3) - Surface area (mm^2) - Center of gravity (mm) - Material name (if assigned) - Density (kg/mm^3) Results are written to _temp_part_properties.json in the working directory. Usage: run_journal.exe extract_part_mass_material.py [output_dir] NX Open APIs Used: - NXOpen.MeasureManager.NewMassProperties() - NXOpen.MeasureBodies - NXOpen.Body.GetBodies() - NXOpen.PhysicalMaterial Author: Atomizer Created: 2025-12-05 Version: 1.0 """ import sys import os import json import NXOpen import NXOpen.UF def get_all_solid_bodies(part): """Get all solid bodies from the part.""" bodies = [] try: # Get bodies from the part for body in part.Bodies: if body.IsSolidBody: bodies.append(body) except Exception as e: print(f"[JOURNAL] Warning getting bodies: {e}") return bodies def get_material_info(part, body): """Extract material information from a body if assigned.""" material_info = { 'name': None, 'density': None, 'density_unit': 'kg/mm^3' } # Method 1: Try to get physical material directly from body try: phys_mat = body.GetPhysicalMaterial() if phys_mat: material_info['name'] = phys_mat.Name print(f"[JOURNAL] Material from body: {phys_mat.Name}") # Try to get density property try: density_prop = phys_mat.GetPropertyValue("Density") if density_prop: material_info['density'] = float(density_prop) print(f"[JOURNAL] Density: {density_prop}") except Exception as de: print(f"[JOURNAL] Could not get density: {de}") except Exception as e: print(f"[JOURNAL] GetPhysicalMaterial failed: {e}") # Method 2: If no material from body, try part-level material assignment if material_info['name'] is None: try: # Check if part has PhysicalMaterialManager pmm = part.PhysicalMaterialManager if pmm: # Get all materials in the part materials = pmm.GetAllPhysicalMaterials() if materials and len(materials) > 0: # Use the first material as default mat = materials[0] material_info['name'] = mat.Name print(f"[JOURNAL] Material from PhysicalMaterialManager: {mat.Name}") try: density_prop = mat.GetPropertyValue("Density") if density_prop: material_info['density'] = float(density_prop) except: pass except Exception as e: print(f"[JOURNAL] PhysicalMaterialManager failed: {e}") # Method 3: Try using body attributes if material_info['name'] is None: try: # Some NX versions store material as an attribute attrs = body.GetUserAttributes() for attr in attrs: if 'material' in attr.Title.lower(): material_info['name'] = attr.StringValue print(f"[JOURNAL] Material from attribute: {attr.StringValue}") break except Exception as e: print(f"[JOURNAL] Body attributes failed: {e}") return material_info def extract_mass_properties(theSession, part, bodies): """ Extract mass properties using MeasureManager. API Reference: https://www.nxjournaling.com/content/mass-properties-using-python Returns dict with mass, volume, area, center of gravity. """ results = { 'mass_kg': 0.0, 'volume_mm3': 0.0, 'surface_area_mm2': 0.0, 'center_of_gravity_mm': [0.0, 0.0, 0.0], 'moments_of_inertia': None, 'num_bodies': len(bodies) } if not bodies: print("[JOURNAL] No solid bodies found in part") return results try: # Get the measure manager measureManager = part.MeasureManager # Convert bodies list to array for NX API bodyArray = bodies if isinstance(bodies, list) else list(bodies) # Get unit collection and build mass_units array # API requires: [Area, Volume, Mass, Length] base units uc = part.UnitCollection mass_units = [ uc.GetBase("Area"), uc.GetBase("Volume"), uc.GetBase("Mass"), uc.GetBase("Length") ] # Create mass properties measurement # Signature: NewMassProperties(mass_units, accuracy, objects) measureBodies = measureManager.NewMassProperties(mass_units, 0.99, bodyArray) print("[JOURNAL] Using NewMassProperties(mass_units, accuracy, bodies) API") # Get the results if measureBodies: # Mass try: results['mass_kg'] = measureBodies.Mass print(f"[JOURNAL] Raw mass value: {measureBodies.Mass}") except AttributeError as e: print(f"[JOURNAL] Mass attribute error: {e}") # Volume try: results['volume_mm3'] = measureBodies.Volume except AttributeError: pass # Surface area try: results['surface_area_mm2'] = measureBodies.Area except AttributeError: pass # Center of gravity try: cog = measureBodies.Centroid if cog: results['center_of_gravity_mm'] = [cog.X, cog.Y, cog.Z] except AttributeError: pass # Moments of inertia - NewMassProperties doesn't provide inertia tensors # Would need different API for that results['moments_of_inertia'] = None # Dispose try: measureBodies.Dispose() except: pass except Exception as e: print(f"[JOURNAL] Error in mass properties extraction: {e}") import traceback traceback.print_exc() return results def main(args): """ Main entry point for NX journal. Args: args[0]: .prt file path args[1]: output directory (optional, defaults to prt directory) """ if len(args) < 1: print("ERROR: No .prt file path provided") print("Usage: run_journal.exe extract_part_mass_material.py [output_dir]") return False prt_file_path = args[0] output_dir = args[1] if len(args) > 1 else os.path.dirname(prt_file_path) prt_filename = os.path.basename(prt_file_path) print(f"[JOURNAL] " + "="*60) print(f"[JOURNAL] NX PART MASS & MATERIAL EXTRACTOR") print(f"[JOURNAL] " + "="*60) print(f"[JOURNAL] Part file: {prt_filename}") print(f"[JOURNAL] Output dir: {output_dir}") results = { 'part_file': prt_filename, 'part_path': prt_file_path, 'mass_kg': 0.0, 'mass_g': 0.0, 'volume_mm3': 0.0, 'surface_area_mm2': 0.0, 'center_of_gravity_mm': [0.0, 0.0, 0.0], 'moments_of_inertia': None, 'material': { 'name': None, 'density': None, 'density_unit': 'kg/mm^3' }, 'num_bodies': 0, 'success': False, 'error': None } try: theSession = NXOpen.Session.GetSession() # Set load options working_dir = os.path.dirname(prt_file_path) theSession.Parts.LoadOptions.ComponentLoadMethod = NXOpen.LoadOptions.LoadMethod.FromDirectory theSession.Parts.LoadOptions.SetSearchDirectories([working_dir], [True]) # Open the part file print(f"[JOURNAL] Opening part file...") basePart, partLoadStatus = theSession.Parts.OpenActiveDisplay( prt_file_path, NXOpen.DisplayPartOption.AllowAdditional ) partLoadStatus.Dispose() workPart = theSession.Parts.Work print(f"[JOURNAL] Loaded part: {workPart.Name}") # Get all solid bodies bodies = get_all_solid_bodies(workPart) print(f"[JOURNAL] Found {len(bodies)} solid bodies") if bodies: # Extract mass properties mass_props = extract_mass_properties(theSession, workPart, bodies) results.update(mass_props) results['mass_g'] = results['mass_kg'] * 1000.0 # Get material from first body (typically all bodies have same material) material_info = get_material_info(workPart, bodies[0]) results['material'] = material_info results['success'] = True print(f"[JOURNAL] ") print(f"[JOURNAL] RESULTS:") print(f"[JOURNAL] Mass: {results['mass_kg']:.6f} kg ({results['mass_g']:.2f} g)") print(f"[JOURNAL] Volume: {results['volume_mm3']:.2f} mm^3") print(f"[JOURNAL] Surface Area: {results['surface_area_mm2']:.2f} mm^2") print(f"[JOURNAL] CoG: [{results['center_of_gravity_mm'][0]:.2f}, {results['center_of_gravity_mm'][1]:.2f}, {results['center_of_gravity_mm'][2]:.2f}] mm") if material_info['name']: print(f"[JOURNAL] Material: {material_info['name']}") if material_info['density']: print(f"[JOURNAL] Density: {material_info['density']} {material_info['density_unit']}") else: results['error'] = "No solid bodies found in part" print(f"[JOURNAL] ERROR: No solid bodies found") # Write results to JSON file output_file = os.path.join(output_dir, "_temp_part_properties.json") with open(output_file, 'w') as f: json.dump(results, f, indent=2) print(f"[JOURNAL] Results written to: {output_file}") # Also write simple mass value for backward compatibility mass_file = os.path.join(output_dir, "_temp_mass.txt") with open(mass_file, 'w') as f: f.write(str(results['mass_kg'])) print(f"[JOURNAL] Mass written to: {mass_file}") return True except Exception as e: results['error'] = str(e) results['success'] = False print(f"[JOURNAL] FATAL ERROR: {e}") import traceback traceback.print_exc() # Still write results file even on error output_file = os.path.join(output_dir, "_temp_part_properties.json") try: with open(output_file, 'w') as f: json.dump(results, f, indent=2) except: pass return False if __name__ == '__main__': main(sys.argv[1:])