""" NX Journal: SIM File Introspection Tool ========================================= This journal performs deep introspection of an NX .sim file and extracts: - Solutions (name, type, solver) - Boundary conditions (SPCs, loads, etc.) - Subcases - Linked FEM files - Solution properties Usage: run_journal.exe introspect_sim.py [output_dir] Output: _introspection_sim.json - JSON with all extracted data Author: Atomizer Created: 2026-01-20 Version: 1.0 """ import sys import os import json import NXOpen import NXOpen.CAE def get_solutions(simSimulation): """Extract all solutions from the simulation.""" solutions = [] try: # Iterate through all solutions in the simulation # Solutions are accessed via FindObject with pattern "Solution[name]" # But we can also iterate if the simulation has a solutions collection # Try to get solution info by iterating through known solution names # Common patterns: "Solution 1", "Solution 2", etc. for i in range(1, 20): # Check up to 20 solutions sol_name = f"Solution {i}" try: sol = simSimulation.FindObject(f"Solution[{sol_name}]") if sol: sol_info = {"name": sol_name, "type": str(type(sol).__name__), "properties": {}} # Try to get common properties try: sol_info["properties"]["solver_type"] = ( str(sol.SolverType) if hasattr(sol, "SolverType") else None ) except: pass try: sol_info["properties"]["analysis_type"] = ( str(sol.AnalysisType) if hasattr(sol, "AnalysisType") else None ) except: pass solutions.append(sol_info) except: # Solution not found, stop looking if i > 5: # Give a few tries in case there are gaps break continue except Exception as e: solutions.append({"error": str(e)}) return solutions def get_boundary_conditions(simSimulation, workPart): """Extract boundary conditions from the simulation.""" bcs = {"constraints": [], "loads": [], "total_count": 0} try: # Try to access BC collections through the simulation object # BCs are typically stored in the simulation's children # Look for constraint groups constraint_names = [ "Constraint Group[1]", "Constraint Group[2]", "Constraint Group[3]", "SPC[1]", "SPC[2]", "SPC[3]", "Fixed Constraint[1]", "Fixed Constraint[2]", ] for name in constraint_names: try: obj = simSimulation.FindObject(name) if obj: bc_info = { "name": name, "type": str(type(obj).__name__), } bcs["constraints"].append(bc_info) except: pass # Look for load groups load_names = [ "Load Group[1]", "Load Group[2]", "Load Group[3]", "Force[1]", "Force[2]", "Pressure[1]", "Pressure[2]", "Enforced Displacement[1]", "Enforced Displacement[2]", ] for name in load_names: try: obj = simSimulation.FindObject(name) if obj: load_info = { "name": name, "type": str(type(obj).__name__), } bcs["loads"].append(load_info) except: pass bcs["total_count"] = len(bcs["constraints"]) + len(bcs["loads"]) except Exception as e: bcs["error"] = str(e) return bcs def get_sim_part_info(workPart): """Extract SIM part-level information.""" info = {"name": None, "full_path": None, "type": None, "fem_parts": [], "component_count": 0} try: info["name"] = workPart.Name info["full_path"] = workPart.FullPath if hasattr(workPart, "FullPath") else None info["type"] = str(type(workPart).__name__) # Check for component assembly (assembly FEM) try: root = workPart.ComponentAssembly.RootComponent if root: info["is_assembly"] = True # Count components try: children = root.GetChildren() info["component_count"] = len(children) if children else 0 # Get component names components = [] for child in children[:10]: # Limit to first 10 try: comp_info = { "name": child.Name if hasattr(child, "Name") else str(child), "type": str(type(child).__name__), } components.append(comp_info) except: pass info["components"] = components except: pass except: info["is_assembly"] = False except Exception as e: info["error"] = str(e) return info def get_cae_session_info(theSession): """Get CAE session information.""" cae_info = {"active_sim_part": None, "active_fem_part": None, "solver_types": []} try: # Get CAE session caeSession = theSession.GetExportedObject("NXOpen.CAE.CaeSession") if caeSession: cae_info["cae_session_exists"] = True except: cae_info["cae_session_exists"] = False return cae_info def explore_simulation_tree(simSimulation, workPart): """Explore the simulation tree structure.""" tree_info = {"simulation_objects": [], "found_types": set()} # Try to enumerate objects in the simulation # This is exploratory - we don't know the exact API try: # Try common child object patterns patterns = [ # Solutions "Solution[Solution 1]", "Solution[Solution 2]", "Solution[SOLUTION 1]", # Subcases "Subcase[Subcase 1]", "Subcase[Subcase - Static 1]", # Loads/BCs "LoadSet[LoadSet 1]", "ConstraintSet[ConstraintSet 1]", "BoundaryCondition[1]", # FEM reference "FemPart", "AssyFemPart", ] for pattern in patterns: try: obj = simSimulation.FindObject(pattern) if obj: obj_info = {"pattern": pattern, "type": str(type(obj).__name__), "found": True} tree_info["simulation_objects"].append(obj_info) tree_info["found_types"].add(str(type(obj).__name__)) except: pass tree_info["found_types"] = list(tree_info["found_types"]) except Exception as e: tree_info["error"] = str(e) return tree_info def main(args): """Main entry point for NX journal.""" if len(args) < 1: print("ERROR: No .sim file path provided") print("Usage: run_journal.exe introspect_sim.py [output_dir]") return False sim_file_path = args[0] output_dir = args[1] if len(args) > 1 else os.path.dirname(sim_file_path) sim_filename = os.path.basename(sim_file_path) print(f"[INTROSPECT-SIM] " + "=" * 60) print(f"[INTROSPECT-SIM] NX SIMULATION INTROSPECTION") print(f"[INTROSPECT-SIM] " + "=" * 60) print(f"[INTROSPECT-SIM] SIM File: {sim_filename}") print(f"[INTROSPECT-SIM] Output: {output_dir}") results = { "sim_file": sim_filename, "sim_path": sim_file_path, "success": False, "error": None, "part_info": {}, "solutions": [], "boundary_conditions": {}, "tree_structure": {}, "cae_info": {}, } try: theSession = NXOpen.Session.GetSession() # Set load options working_dir = os.path.dirname(sim_file_path) theSession.Parts.LoadOptions.ComponentLoadMethod = ( NXOpen.LoadOptions.LoadMethod.FromDirectory ) theSession.Parts.LoadOptions.SetSearchDirectories([working_dir], [True]) theSession.Parts.LoadOptions.ComponentsToLoad = NXOpen.LoadOptions.LoadComponents.All theSession.Parts.LoadOptions.PartLoadOption = NXOpen.LoadOptions.LoadOption.FullyLoad # Open the SIM file print(f"[INTROSPECT-SIM] Opening SIM file...") basePart, partLoadStatus = theSession.Parts.OpenActiveDisplay( sim_file_path, NXOpen.DisplayPartOption.AllowAdditional ) partLoadStatus.Dispose() workPart = theSession.Parts.Work print(f"[INTROSPECT-SIM] Loaded: {workPart.Name}") # Switch to SFEM application try: theSession.ApplicationSwitchImmediate("UG_APP_SFEM") print(f"[INTROSPECT-SIM] Switched to SFEM application") except Exception as e: print(f"[INTROSPECT-SIM] Note: Could not switch to SFEM: {e}") # Get part info print(f"[INTROSPECT-SIM] Extracting part info...") results["part_info"] = get_sim_part_info(workPart) print(f"[INTROSPECT-SIM] Part: {results['part_info'].get('name')}") print(f"[INTROSPECT-SIM] Is Assembly: {results['part_info'].get('is_assembly', False)}") # Get simulation object print(f"[INTROSPECT-SIM] Finding Simulation object...") try: simSimulation = workPart.FindObject("Simulation") print(f"[INTROSPECT-SIM] Found Simulation object: {type(simSimulation).__name__}") # Get solutions print(f"[INTROSPECT-SIM] Extracting solutions...") results["solutions"] = get_solutions(simSimulation) print(f"[INTROSPECT-SIM] Found {len(results['solutions'])} solutions") # Get boundary conditions print(f"[INTROSPECT-SIM] Extracting boundary conditions...") results["boundary_conditions"] = get_boundary_conditions(simSimulation, workPart) print( f"[INTROSPECT-SIM] Found {results['boundary_conditions'].get('total_count', 0)} BCs" ) # Explore tree structure print(f"[INTROSPECT-SIM] Exploring simulation tree...") results["tree_structure"] = explore_simulation_tree(simSimulation, workPart) print( f"[INTROSPECT-SIM] Found types: {results['tree_structure'].get('found_types', [])}" ) except Exception as e: print(f"[INTROSPECT-SIM] WARNING: Could not find Simulation object: {e}") results["simulation_object_error"] = str(e) # Get CAE session info print(f"[INTROSPECT-SIM] Getting CAE session info...") results["cae_info"] = get_cae_session_info(theSession) # List all loaded parts print(f"[INTROSPECT-SIM] Listing loaded parts...") loaded_parts = [] for part in theSession.Parts: try: loaded_parts.append( { "name": part.Name, "type": str(type(part).__name__), "leaf": part.Leaf if hasattr(part, "Leaf") else None, } ) except: pass results["loaded_parts"] = loaded_parts print(f"[INTROSPECT-SIM] {len(loaded_parts)} parts loaded") results["success"] = True print(f"[INTROSPECT-SIM] ") print(f"[INTROSPECT-SIM] INTROSPECTION COMPLETE!") print(f"[INTROSPECT-SIM] " + "=" * 60) except Exception as e: results["error"] = str(e) results["success"] = False print(f"[INTROSPECT-SIM] FATAL ERROR: {e}") import traceback traceback.print_exc() # Write results output_file = os.path.join(output_dir, "_introspection_sim.json") with open(output_file, "w") as f: json.dump(results, f, indent=2) print(f"[INTROSPECT-SIM] Results written to: {output_file}") return results["success"] if __name__ == "__main__": main(sys.argv[1:])