From ec5e42d733801418278799963b74288b68d2165a Mon Sep 17 00:00:00 2001 From: Antoine Date: Fri, 28 Nov 2025 16:30:15 -0500 Subject: [PATCH] feat: Add M1 mirror Zernike optimization with correct RMS calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major improvements to telescope mirror optimization workflow: Assembly FEM Workflow (solve_simulation.py): - Fixed multi-part assembly FEM update sequence - Use ImportFromFile() for reliable expression updates - Add DuplicateNodesCheckBuilder with MergeOccurrenceNodes=True - Switch to Foreground solve mode for multi-subcase solutions - Add detailed logging and diagnostics for node merge operations Zernike RMS Calculation: - CRITICAL FIX: Use correct surface-based RMS formula - Global RMS = sqrt(mean(W^2)) from actual WFE values - Filtered RMS = sqrt(mean(W_residual^2)) after removing low-order fit - This matches zernike_Post_Script_NX.py (optical standard) - Previous WRONG formula was: sqrt(sum(coeffs^2)) - Add compute_rms_filter_j1to3() for optician workload metric Subcase Mapping: - Fix subcase mapping to match NX model: - Subcase 1 = 90 deg (polishing orientation) - Subcase 2 = 20 deg (reference) - Subcase 3 = 40 deg - Subcase 4 = 60 deg New Study: M1 Mirror Zernike Optimization - Full optimization config with 11 design variables - 3 objectives: rel_filtered_rms_40_vs_20, rel_filtered_rms_60_vs_20, mfg_90_optician_workload - Neural surrogate support for accelerated optimization Documentation: - Update ZERNIKE_INTEGRATION.md with correct RMS formula - Update ASSEMBLY_FEM_WORKFLOW.md with expression import and node merge details - Add reference scripts from original zernike_Post_Script_NX.py 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ASSEMBLY_FEM_WORKFLOW.md | 188 + docs/ZERNIKE_INTEGRATION.md | 313 + .../nx_post_each_iter.py | 332 + .../zernike_Post_Script_NX.py | 1012 +++ .../optimization_config_zernike_mirror.json | 124 + .../journal_afem_update_workflow_exemple.py | 5510 +++++++++++++++++ optimization_engine/extractors/__init__.py | 26 +- .../extractors/extract_zernike.py | 860 +++ .../extractors/zernike_helpers.py | 403 ++ optimization_engine/nx_solver.py | 13 +- optimization_engine/solve_simulation.py | 799 ++- .../1_setup/optimization_config.json | 236 + .../2_results/study.db | Bin 0 -> 258048 bytes .../DASHBOARD.md | 134 + .../m1_mirror_zernike_optimization/README.md | 429 ++ .../run_optimization.py | 1377 ++++ 16 files changed, 11452 insertions(+), 304 deletions(-) create mode 100644 docs/06_PROTOCOLS_DETAILED/ASSEMBLY_FEM_WORKFLOW.md create mode 100644 docs/ZERNIKE_INTEGRATION.md create mode 100644 examples/Zernike_old_reference/nx_post_each_iter.py create mode 100644 examples/Zernike_old_reference/zernike_Post_Script_NX.py create mode 100644 examples/optimization_config_zernike_mirror.json create mode 100644 nx_journals/user_generated_journals/journal_afem_update_workflow_exemple.py create mode 100644 optimization_engine/extractors/extract_zernike.py create mode 100644 optimization_engine/extractors/zernike_helpers.py create mode 100644 studies/m1_mirror_zernike_optimization/1_setup/optimization_config.json create mode 100644 studies/m1_mirror_zernike_optimization/2_results/study.db create mode 100644 studies/m1_mirror_zernike_optimization/DASHBOARD.md create mode 100644 studies/m1_mirror_zernike_optimization/README.md create mode 100644 studies/m1_mirror_zernike_optimization/run_optimization.py diff --git a/docs/06_PROTOCOLS_DETAILED/ASSEMBLY_FEM_WORKFLOW.md b/docs/06_PROTOCOLS_DETAILED/ASSEMBLY_FEM_WORKFLOW.md new file mode 100644 index 00000000..86b4a79e --- /dev/null +++ b/docs/06_PROTOCOLS_DETAILED/ASSEMBLY_FEM_WORKFLOW.md @@ -0,0 +1,188 @@ +# Assembly FEM Optimization Workflow + +This document describes the multi-part assembly FEM workflow used when optimizing complex assemblies with `.afm` (Assembly FEM) files. + +## Overview + +Assembly FEMs have a more complex dependency chain than single-part simulations: + +``` +.prt (geometry) → _fem1.fem (component mesh) → .afm (assembly mesh) → .sim (solution) +``` + +Each level must be updated in sequence when design parameters change. + +## When This Workflow Applies + +This workflow is automatically triggered when: +- The working directory contains `.afm` files +- Multiple `.fem` files exist (component meshes) +- Multiple `.prt` files exist (component geometry) + +Examples: +- M1 Mirror assembly (M1_Blank + M1_Vertical_Support_Skeleton) +- Multi-component mechanical assemblies +- Any NX assembly where components have separate FEM files + +## The 4-Step Workflow + +### Step 1: Update Expressions in Geometry Part (.prt) + +``` +Open M1_Blank.prt +├── Find and update design expressions +│ ├── whiffle_min = 42.5 +│ ├── whiffle_outer_to_vertical = 75.0 +│ └── inner_circular_rib_dia = 550.0 +├── Rebuild geometry (DoUpdate) +└── Save part +``` + +The `.prt` file contains the parametric CAD model with expressions that drive dimensions. These expressions are updated with new design parameter values, then the geometry is rebuilt. + +### Step 2: Update Component FEM Files (.fem) + +``` +For each component FEM: +├── Open M1_Blank_fem1.fem +│ ├── UpdateFemodel() - regenerates mesh from updated geometry +│ └── Save FEM +├── Open M1_Vertical_Support_Skeleton_fem1.fem +│ ├── UpdateFemodel() +│ └── Save FEM +└── ... (repeat for all component FEMs) +``` + +Each component FEM is linked to its source geometry. `UpdateFemodel()` regenerates the mesh based on the updated geometry. + +### Step 3: Update Assembly FEM (.afm) + +``` +Open ASSY_M1_assyfem1.afm +├── UpdateFemodel() - updates assembly mesh +├── Merge coincident nodes (at component interfaces) +├── Resolve labeling conflicts (duplicate node/element IDs) +└── Save AFM +``` + +The assembly FEM combines component meshes. This step: +- Reconnects meshes at shared interfaces +- Resolves numbering conflicts between component meshes +- Ensures mesh continuity for accurate analysis + +### Step 4: Solve Simulation (.sim) + +``` +Open ASSY_M1_assyfem1_sim1.sim +├── Execute solve +│ ├── Foreground mode for all solutions +│ └── or Background mode for specific solution +└── Save simulation +``` + +The simulation file references the assembly FEM and contains solution setup (loads, constraints, subcases). + +## File Dependencies + +``` +M1 Mirror Example: + +M1_Blank.prt ─────────────────────> M1_Blank_fem1.fem ─────────┐ + │ │ │ + │ (expressions) │ (component mesh) │ + ↓ ↓ │ +M1_Vertical_Support_Skeleton.prt ──> M1_..._Skeleton_fem1.fem ─┤ + │ + ↓ + ASSY_M1_assyfem1.afm ──> ASSY_M1_assyfem1_sim1.sim + (assembly mesh) (solution) +``` + +## API Functions Used + +| Step | NX API Call | Purpose | +|------|-------------|---------| +| 1 | `OpenBase()` | Open .prt file | +| 1 | `ImportFromFile()` | Import expressions from .exp file (preferred) | +| 1 | `DoUpdate()` | Rebuild geometry | +| 2-3 | `UpdateFemodel()` | Regenerate mesh from geometry | +| 3 | `DuplicateNodesCheckBuilder` | Merge coincident nodes at interfaces | +| 3 | `MergeOccurrenceNodes = True` | Critical: enables cross-component merge | +| 4 | `SolveAllSolutions()` | Execute FEA (Foreground mode recommended) + +### Expression Update Method + +The recommended approach uses expression file import: + +```python +# Write expressions to .exp file +with open(exp_path, 'w') as f: + for name, value in expressions.items(): + unit = get_unit_for_expression(name) + f.write(f"[{unit}]{name}={value}\n") + +# Import into part +modified, errors = workPart.Expressions.ImportFromFile( + exp_path, + NXOpen.ExpressionCollection.ImportMode.Replace +) +``` + +This is more reliable than `EditExpressionWithUnits()` for batch updates. + +## Error Handling + +Common issues and solutions: + +### "Update undo happened" +- Geometry update failed due to constraint violations +- Check expression values are within valid ranges +- May need to adjust parameter bounds + +### "This operation can only be done on the work part" +- Work part not properly set before operation +- Use `SetWork()` to make target part the work part + +### Node merge warnings +- Manual intervention may be needed for complex interfaces +- Check mesh connectivity in NX after solve + +### "Billion nm" RMS values +- Indicates node merging failed - coincident nodes not properly merged +- Check `MergeOccurrenceNodes = True` is set +- Verify tolerance (0.01 mm recommended) +- Run node merge after every FEM update, not just once + +## Configuration + +The workflow auto-detects assembly FEMs, but you can configure behavior: + +```json +{ + "nx_settings": { + "expression_part": "M1_Blank", // Override auto-detection + "component_fems": [ // Explicit list of FEMs to update + "M1_Blank_fem1.fem", + "M1_Vertical_Support_Skeleton_fem1.fem" + ], + "afm_file": "ASSY_M1_assyfem1.afm" + } +} +``` + +## Implementation Reference + +See `optimization_engine/solve_simulation.py` for the full implementation: + +- `detect_assembly_fem()` - Detects if assembly workflow needed +- `update_expressions_in_part()` - Step 1 implementation +- `update_fem_part()` - Step 2 implementation +- `update_assembly_fem()` - Step 3 implementation +- `solve_simulation_file()` - Step 4 implementation + +## Tips + +1. **Start with baseline solve**: Before optimization, manually verify the full workflow completes in NX +2. **Check mesh quality**: Poor mesh quality after updates can cause solve failures +3. **Monitor memory**: Assembly FEMs with many components use significant memory +4. **Use Foreground mode**: For multi-subcase solutions, Foreground mode ensures all subcases complete diff --git a/docs/ZERNIKE_INTEGRATION.md b/docs/ZERNIKE_INTEGRATION.md new file mode 100644 index 00000000..f5768b6d --- /dev/null +++ b/docs/ZERNIKE_INTEGRATION.md @@ -0,0 +1,313 @@ +# Zernike Wavefront Analysis Integration + +This document describes how to use Atomizer's Zernike analysis capabilities for telescope mirror optimization. + +## Overview + +Atomizer includes a full Zernike polynomial decomposition system for analyzing wavefront errors (WFE) in telescope mirror FEA simulations. The system: + +- Extracts nodal displacements from NX Nastran OP2 files +- Fits Zernike polynomials using Noll indexing (optical standard) +- Computes RMS metrics (global and filtered) +- Analyzes individual aberrations (astigmatism, coma, trefoil, etc.) +- Supports multi-subcase analysis (different gravity orientations) + +## Quick Start + +### Simple Extraction + +```python +from optimization_engine.extractors import extract_zernike_from_op2 + +# Extract Zernike metrics for a single subcase +result = extract_zernike_from_op2( + op2_file="model-solution_1.op2", + subcase="20" # 20 degree elevation +) + +print(f"Global RMS: {result['global_rms_nm']:.2f} nm") +print(f"Filtered RMS: {result['filtered_rms_nm']:.2f} nm") +print(f"Astigmatism: {result['astigmatism_rms_nm']:.2f} nm") +``` + +### In Optimization Objective + +```python +from optimization_engine.extractors.zernike_helpers import create_zernike_objective + +# Create objective function +zernike_obj = create_zernike_objective( + op2_finder=lambda: sim_dir / "model-solution_1.op2", + subcase="20", + metric="filtered_rms_nm" +) + +# Use in Optuna trial +def objective(trial): + # ... suggest parameters ... + # ... run simulation ... + + rms = zernike_obj() + return rms +``` + +## RMS Calculation Method + +**IMPORTANT**: Atomizer uses the correct surface-based RMS calculation matching optical standards: + +```python +# Global RMS = sqrt(mean(W^2)) - RMS of actual WFE surface values +global_rms = sqrt(mean(W_nm ** 2)) + +# Filtered RMS = sqrt(mean(W_residual^2)) +# where W_residual = W_nm - Z[:, :4] @ coeffs[:4] (low-order fit subtracted) +filtered_rms = sqrt(mean(W_residual ** 2)) +``` + +This is **different** from summing Zernike coefficients! The RMS is computed from the actual WFE surface values, not from `sqrt(sum(coeffs^2))`. + +## Available Metrics + +### RMS Metrics +| Metric | Description | +|--------|-------------| +| `global_rms_nm` | RMS of entire WFE surface: `sqrt(mean(W^2))` | +| `filtered_rms_nm` | RMS after removing modes 1-4 (piston, tip, tilt, defocus) | +| `rms_filter_j1to3_nm` | RMS after removing only modes 1-3 (keeps defocus) - "optician workload" | + +### Aberration Magnitudes +| Metric | Zernike Modes | Description | +|--------|--------------|-------------| +| `defocus_nm` | J4 | Focus error | +| `astigmatism_rms_nm` | J5 + J6 | Combined astigmatism | +| `coma_rms_nm` | J7 + J8 | Combined coma | +| `trefoil_rms_nm` | J9 + J10 | Combined trefoil | +| `spherical_nm` | J11 | Primary spherical | + +## Multi-Subcase Analysis + +For telescope mirrors, gravity orientation affects surface shape. Standard subcases: + +| Subcase | Description | +|---------|-------------| +| 20 | Low elevation (operational) | +| 40 | Mid-low elevation | +| 60 | Mid-high elevation | +| 90 | Horizontal (polishing orientation) | + +### Extract All Subcases + +```python +from optimization_engine.extractors import ZernikeExtractor + +extractor = ZernikeExtractor("model.op2") +results = extractor.extract_all_subcases(reference_subcase="20") + +for label, metrics in results.items(): + print(f"Subcase {label}: {metrics['filtered_rms_nm']:.1f} nm") +``` + +### Relative Analysis + +Compare deformation between orientations: + +```python +from optimization_engine.extractors.zernike_helpers import create_relative_zernike_objective + +# Minimize deformation at 20 deg relative to polishing position (90 deg) +relative_obj = create_relative_zernike_objective( + op2_finder=lambda: sim_dir / "model.op2", + target_subcase="20", + reference_subcase="90" +) + +relative_rms = relative_obj() +``` + +## Optimization Configuration + +### Example: Single Objective (Filtered RMS) + +```json +{ + "objectives": [ + { + "name": "filtered_rms", + "direction": "minimize", + "extractor": "zernike", + "extractor_config": { + "subcase": "20", + "metric": "filtered_rms_nm" + } + } + ] +} +``` + +### Example: Multi-Objective (RMS + Mass) + +```json +{ + "objectives": [ + { + "name": "filtered_rms_20deg", + "direction": "minimize", + "extractor": "zernike", + "extractor_config": { + "subcase": "20", + "metric": "filtered_rms_nm" + } + }, + { + "name": "mass", + "direction": "minimize", + "extractor": "mass_from_expression" + } + ], + "optimization_settings": { + "sampler": "NSGA-II", + "protocol": 11 + } +} +``` + +### Example: Constrained (Stress + Aberration Limits) + +```json +{ + "constraints": [ + { + "name": "astigmatism_limit", + "type": "upper_bound", + "threshold": 50.0, + "extractor": "zernike", + "extractor_config": { + "subcase": "90", + "metric": "astigmatism_rms_nm" + } + } + ] +} +``` + +## Advanced: ZernikeObjectiveBuilder + +For complex multi-subcase objectives: + +```python +from optimization_engine.extractors.zernike_helpers import ZernikeObjectiveBuilder + +builder = ZernikeObjectiveBuilder( + op2_finder=lambda: sim_dir / "model.op2" +) + +# Weight operational positions more heavily +builder.add_subcase_objective("20", "filtered_rms_nm", weight=1.0) +builder.add_subcase_objective("40", "filtered_rms_nm", weight=0.5) +builder.add_subcase_objective("60", "filtered_rms_nm", weight=0.5) + +# Create combined objective (weighted sum) +objective = builder.build_weighted_sum() + +# Or: worst-case across subcases +worst_case_obj = builder.build_max() +``` + +## Zernike Settings + +### Configuration Options + +| Setting | Default | Description | +|---------|---------|-------------| +| `n_modes` | 50 | Number of Zernike modes to fit | +| `filter_orders` | 4 | Low-order modes to filter (1-4 = piston through defocus) | +| `displacement_unit` | "mm" | Unit of displacement in OP2 ("mm", "m", "um", "nm") | + +### Unit Conversions + +Wavefront error (WFE) is computed as: + +``` +WFE_nm = 2 * displacement * unit_conversion +``` + +Where `unit_conversion` converts to nanometers: +- mm: 1e6 +- m: 1e9 +- um: 1e3 + +The factor of 2 accounts for the optical convention (surface error doubles as wavefront error for reflection). + +## NX Nastran Setup + +### Required Subcases + +Your NX Nastran model should have subcases for each gravity orientation: + +``` +SUBCASE 20 + SUBTITLE=20 deg elevation + LOAD = ... + +SUBCASE 40 + SUBTITLE=40 deg elevation + LOAD = ... +``` + +The extractor identifies subcases by: +1. Numeric value in SUBTITLE (preferred) +2. SUBCASE ID number + +### Output Requests + +Ensure displacement output is requested: + +``` +SET 999 = ALL +DISPLACEMENT(SORT1,REAL) = 999 +``` + +## Migration from Legacy Scripts + +If you were using `zernike_Post_Script_NX.py`: + +| Old Approach | Atomizer Equivalent | +|--------------|---------------------| +| Manual OP2 parsing | `ZernikeExtractor` | +| `compute_zernike_coeffs_chunked()` | `compute_zernike_coefficients()` | +| `write_exp_file()` | Configure as objective/constraint | +| HTML reports | Dashboard visualization (TBD) | +| RMS log CSV | Optuna database + export | + +### Key Differences + +1. **Integration**: Zernike is now an extractor like displacement/stress +2. **Optimization**: Direct use as objectives/constraints in Optuna +3. **Multi-objective**: Native NSGA-II support for RMS + mass Pareto optimization +4. **Neural Acceleration**: Can train surrogate on Zernike metrics (Protocol 12) + +## Example Study Structure + +``` +studies/ + mirror_optimization/ + 1_setup/ + optimization_config.json + model/ + ASSY_M1.prt + ASSY_M1_assyfem1.afm + ASSY_M1_assyfem1_sim1.sim + 2_results/ + study.db + zernike_analysis/ + trial_001_zernike.json + trial_002_zernike.json + ... + run_optimization.py +``` + +## See Also + +- [examples/optimization_config_zernike_mirror.json](../examples/optimization_config_zernike_mirror.json) - Full example configuration +- [optimization_engine/extractors/extract_zernike.py](../optimization_engine/extractors/extract_zernike.py) - Core implementation +- [optimization_engine/extractors/zernike_helpers.py](../optimization_engine/extractors/zernike_helpers.py) - Helper functions diff --git a/examples/Zernike_old_reference/nx_post_each_iter.py b/examples/Zernike_old_reference/nx_post_each_iter.py new file mode 100644 index 00000000..5aa37f0b --- /dev/null +++ b/examples/Zernike_old_reference/nx_post_each_iter.py @@ -0,0 +1,332 @@ +# nx_post_each_iter.py +import os, subprocess +import NXOpen +from datetime import datetime +import csv, re + +# --- SETTINGS --- +TEST_ENV_PY = r"C:\Users\antoi\anaconda3\envs\test_env\python.exe" +SCRIPT_NAME = "zernike_Post_Script_NX.py" # your script in the .sim folder +OP2_NAME = "assy_m1_assyfem1_sim1-solution_1.op2" +EXP_NAME = "Iteration_results_expression.exp" +TIMEOUT = None # e.g., 900 for 15 min +# Option A: set via env NX_GEOM_PART_NAME, else hardcode your CAD part name here. +GEOM_PART_NAME = os.environ.get("NX_GEOM_PART_NAME", "ASSY_M1_assyfem1") + +# --------------- + +def import_iteration_results_exp(exp_path: str, lw) -> bool: + """Import EXP into current Work part (Replace) and update.""" + theSession = NXOpen.Session.GetSession() + workPart = theSession.Parts.BaseWork + + if not os.path.isfile(exp_path): + lw.WriteLine(f"[EXP][ERROR] File not found: {exp_path}") + return False + + mark_import = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Import Expressions") + try: + modified, err_msgs = workPart.Expressions.ImportFromFile( + exp_path, NXOpen.ExpressionCollection.ImportMode.Replace + ) + # surface any parsing messages + try: + if err_msgs: + for m in err_msgs: + lw.WriteLine(f"[EXP][WARN] {m}") + except Exception: + pass + + mark_update = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update") + nErrs = theSession.UpdateManager.DoUpdate(mark_update) + theSession.DeleteUndoMark(mark_update, "NX update") + + theSession.SetUndoMarkName(mark_import, "Expressions") + theSession.DeleteUndoMark(mark_import, None) + + lw.WriteLine(f"[EXP] Imported OK (modified={modified}, nErrs={nErrs})") + return True + except Exception as ex: + lw.WriteLine(f"[EXP][FATAL] {ex}") + try: + theSession.DeleteUndoMark(mark_import, None) + except Exception: + pass + return False + +def export_all_named_expressions_to_exp(workPart, out_path, lw): + """ + Export expressions to an .exp file using the 3-arg signature: + ExportToFile(, , ) + Works across NX versions where enums live under either: + NXOpen.ExpressionCollection.ExportMode / SortType + or + NXOpen.ExpressionCollectionExportMode / ExpressionCollectionSortType + """ + try: + if not out_path.lower().endswith(".exp"): + out_path += ".exp" + + mode_cls = getattr(NXOpen.ExpressionCollection, "ExportMode", + getattr(NXOpen, "ExpressionCollectionExportMode", None)) + sort_cls = getattr(NXOpen.ExpressionCollection, "SortType", + getattr(NXOpen, "ExpressionCollectionSortType", None)) + if mode_cls is None or sort_cls is None: + raise RuntimeError("Unsupported NX/Open version: ExportMode/SortType enums not found") + + workPart.Expressions.ExportToFile(mode_cls.WorkPart, out_path, sort_cls.AlphaNum) + lw.WriteLine(f"[EXP-EXPORT] Wrote: {out_path}") + return True + except Exception as ex: + lw.WriteLine(f"[EXP-EXPORT][ERROR] {ex}") + return False + + + + + +def parse_exp_file_to_dict(exp_path): + """ + Parse NX .exp lines like: + // comments + [MilliMeter]SomeName=0.001234 + SomeOther=42 + into { 'SomeName': numeric_or_str, ... }. + """ + out = {} + num = re.compile(r'^[+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$') + + with open(exp_path, 'r', encoding='utf-8', errors='ignore') as f: + for line in f: + s = line.strip() + if not s or s.startswith('//'): + continue + if '=' not in s: + continue + left, right = s.split('=', 1) + # strip optional [Unit] prefixes on left side + left = re.sub(r'\[[^\]]*\]\s*', '', left).strip() + key = left + val = right.strip() + + # try numeric + v = val + if num.match(val): + try: + v = float(val) + except Exception: + pass + out[key] = v + return out + +def append_named_exprs_row(results_dir, run_id, run_dt, expr_dict, lw, source, part_name): + """ + Appends one row to Results/NX_named_expressions_log.csv + Columns auto-extend for new expression names. + Adds metadata: RunID, RunDateTimeLocal, Source ('SIM'|'PART'), PartName. + """ + log_csv = os.path.join(results_dir, "NX_named_expressions_log.csv") + meta = { + "RunID": run_id, + "RunDateTimeLocal": run_dt.strftime("%Y-%m-%d %H:%M:%S"), + "Source": source, + "PartName": part_name, + } + row = {**meta, **expr_dict} + + # Create or extend header as needed + if not os.path.exists(log_csv): + fieldnames = list(meta.keys()) + sorted(expr_dict.keys()) + with open(log_csv, "w", newline="", encoding="utf-8") as f: + w = csv.DictWriter(f, fieldnames=fieldnames) + w.writeheader() + w.writerow({k: row.get(k, "") for k in fieldnames}) + lw.WriteLine(f"[EXP-EXPORT] Created CSV log: {log_csv}") + return + + with open(log_csv, "r", newline="", encoding="utf-8") as f: + r = csv.reader(f) + existing = list(r) + + if not existing: + fieldnames = list(meta.keys()) + sorted(expr_dict.keys()) + with open(log_csv, "w", newline="", encoding="utf-8") as f: + w = csv.DictWriter(f, fieldnames=fieldnames) + w.writeheader() + w.writerow({k: row.get(k, "") for k in fieldnames}) + lw.WriteLine(f"[EXP-EXPORT] Rebuilt CSV log: {log_csv}") + return + + header = existing[0] + known = set(header) + new_cols = [c for c in meta.keys() if c not in known] + \ + sorted([k for k in expr_dict.keys() if k not in known]) + if new_cols: + header = header + new_cols + + with open(log_csv, "w", newline="", encoding="utf-8") as f: + w = csv.DictWriter(f, fieldnames=header) + w.writeheader() + # Rewrite old rows (padding any new columns) + for data in existing[1:]: + old_row = {h: (data[i] if i < len(data) else "") for i, h in enumerate(existing[0])} + for c in new_cols: + old_row.setdefault(c, "") + w.writerow({k: old_row.get(k, "") for k in header}) + # Append new row + w.writerow({k: row.get(k, "") for k in header}) + + lw.WriteLine(f"[EXP-EXPORT] Appended CSV log: {log_csv}") + +def export_geometry_named_expressions(sim_part, results_dir, run_id, lw): + """ + Switch display to the geometry part (like in your journal), export expressions, then restore. + GEOM_PART_NAME must be resolvable via Session.Parts.FindObject. + """ + theSession = NXOpen.Session.GetSession() + original_display = theSession.Parts.BaseDisplay + original_work = theSession.Parts.BaseWork + + try: + if not GEOM_PART_NAME: + lw.WriteLine("[EXP-EXPORT][WARN] GEOM_PART_NAME not set; skipping geometry export.") + return False, None, None + + try: + part1 = theSession.Parts.FindObject(GEOM_PART_NAME) + except Exception: + lw.WriteLine(f"[EXP-EXPORT][WARN] Geometry part not found by name: {GEOM_PART_NAME}") + return False, None, None + + mark = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part") + status, pls = theSession.Parts.SetActiveDisplay( + part1, + NXOpen.DisplayPartOption.AllowAdditional, + NXOpen.PartDisplayPartWorkPartOption.UseLast + ) + # Switch to Modeling, like your journal + try: + theSession.ApplicationSwitchImmediate("UG_APP_MODELING") + except Exception: + pass + + workPart = theSession.Parts.Work + out_exp = os.path.join(results_dir, f"NamedExpressions_PART_{run_id}.exp") + ok = export_all_named_expressions_to_exp(workPart, out_exp, lw) + + if pls is not None: + pls.Dispose() + theSession.DeleteUndoMark(mark, None) + + # Part name for logging + part_name = os.path.splitext(os.path.basename(workPart.FullPath))[0] if workPart and workPart.FullPath else GEOM_PART_NAME + return ok, out_exp if ok else None, part_name + + except Exception as ex: + lw.WriteLine(f"[EXP-EXPORT][ERROR] Geometry export failed: {ex}") + return False, None, None + + finally: + # Try to restore prior display/work part and CAE app + try: + if original_display is not None: + theSession.Parts.SetActiveDisplay( + original_display, + NXOpen.DisplayPartOption.AllowAdditional, + NXOpen.PartDisplayPartWorkPartOption.UseLast + ) + except Exception: + pass + try: + theSession.ApplicationSwitchImmediate("UG_APP_SFEM") # back to CAE if applicable + except Exception: + pass + + + +def run_post(sim_dir, lw, run_id, results_dir): + post_script = os.path.join(sim_dir, SCRIPT_NAME) + op2 = os.path.join(sim_dir, OP2_NAME) + + if not os.path.exists(TEST_ENV_PY): + lw.WriteLine(f"[ERROR] test_env python not found: {TEST_ENV_PY}") + return 3 + if not os.path.exists(post_script): + lw.WriteLine(f"[ERROR] Post script not found: {post_script}") + return 4 + if not os.path.exists(op2): + lw.WriteLine(f"[ERROR] OP2 not found: {op2}") + return 2 + + cmd = [TEST_ENV_PY, post_script, "--op2", op2] + lw.WriteLine("[POST] " + " ".join(cmd)) + lw.WriteLine(f"[POST] cwd={sim_dir}") + + env = os.environ.copy() + env["ZERNIKE_RUN_ID"] = run_id + env["ZERNIKE_RESULTS_DIR"] = results_dir + + proc = subprocess.run( + cmd, cwd=sim_dir, capture_output=True, text=True, + shell=False, timeout=TIMEOUT, env=env + ) + if proc.stdout: + lw.WriteLine(proc.stdout) + if proc.stderr: + lw.WriteLine("[STDERR]\n" + proc.stderr) + lw.WriteLine(f"[INFO] Post finished (rc={proc.returncode})") + return proc.returncode + + + + + +def main(): + s = NXOpen.Session.GetSession() + lw = s.ListingWindow; lw.Open() + sim_part = s.Parts.BaseWork + sim_dir = os.path.dirname(sim_part.FullPath) + + # --- New: Results folder + a run id/timestamp we can also hand to Zernike --- + results_dir = os.path.join(sim_dir, "Results") + os.makedirs(results_dir, exist_ok=True) + run_dt = datetime.now() + run_id = run_dt.strftime("%Y%m%d_%H%M%S") + + # --- Run the Zernike post (hand it the same run id & results dir via env) --- + rc = run_post(sim_dir, lw, run_id, results_dir) + + if rc != 0: + lw.WriteLine(f"[POST] Zernike post failed (rc={rc}). Skipping EXP import and NX expr logging.") + return # or 'pass' if you prefer to continue anyway + + # Import EXP if it exists — prefer Results/, then fall back to the sim folder + exp_candidates = [ + os.path.join(results_dir, EXP_NAME), + os.path.join(sim_dir, EXP_NAME), + ] + for exp_path in exp_candidates: + if os.path.isfile(exp_path): + import_iteration_results_exp(exp_path, lw) + break + else: + lw.WriteLine(f"[EXP] Skipped: not found → {exp_candidates[0]}") + + + # --- Export SIM (work CAE part) expressions and append log --- + sim_part_name = os.path.splitext(os.path.basename(sim_part.FullPath))[0] if sim_part and sim_part.FullPath else "SIM" + named_exp_sim = os.path.join(results_dir, f"NamedExpressions_SIM_{run_id}.exp") + if export_all_named_expressions_to_exp(sim_part, named_exp_sim, lw): + exprs_sim = parse_exp_file_to_dict(named_exp_sim) + append_named_exprs_row(results_dir, run_id, run_dt, exprs_sim, lw, source="SIM", part_name=sim_part_name) + + # --- Export GEOMETRY (modeling) part expressions like your journal, and append log --- + ok_part, part_exp_path, part_name = export_geometry_named_expressions(sim_part, results_dir, run_id, lw) + if ok_part and part_exp_path: + exprs_part = parse_exp_file_to_dict(part_exp_path) + append_named_exprs_row(results_dir, run_id, run_dt, exprs_part, lw, source="PART", part_name=part_name) + + + +if __name__ == "__main__": + main() diff --git a/examples/Zernike_old_reference/zernike_Post_Script_NX.py b/examples/Zernike_old_reference/zernike_Post_Script_NX.py new file mode 100644 index 00000000..ae8bbee0 --- /dev/null +++ b/examples/Zernike_old_reference/zernike_Post_Script_NX.py @@ -0,0 +1,1012 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +""" +Zernike per Subcase (relative to 20 deg) -> HTML + CSVs (HEADLESS) + dual-reference RMS (vs 20 deg and vs 90 deg) + +- Auto-targets OP2: + * default: m1_optimization_baseline_Isogrid_fem1_i_fem1_sim1_test2-solution_1.op2 in CWD + * or: pass --op2 FULLPATH +- Auto-loads matching .dat/.bdf (same-basename preferred; else first found) +- Extracts subcases by label/ID (expects 90,20,40,60) +- Uses 20 deg as the reference for plots/pages and the *_Rel20 fields (vs 20 deg) +- Also computes RMS relative to 90 deg and exports them: + * EXP: RMS__Global_Rel20, RMS__Filtered_Rel20, RMS__Global_Rel90, RMS__Filtered_Rel90 + * LOG: Rel_GlobalRMS_nm__vs20, Rel_FilteredRMS_nm__vs20, Rel_GlobalRMS_nm__vs90, Rel_FilteredRMS_nm__vs90 +- Outputs: + * Combined nodal CSV (abs 20 deg + relative others vs 20 deg) + * One HTML for 20 deg (absolute) and one HTML per other subcase (relative to 20 deg), + with each relative page's RMS table also showing the absolute RMS for that angle + * One HTML for 90 deg (absolute) with manufacturing metrics (numeric only) + * Zernike coeff CSVs (|coeff| per page; with labels and optional bar chart) + * RMS summary CSV (ABS for all; REL20 for non-reference) + * EXP file with 90/20/40/60 ABS; REL20 (vs 20 deg); and REL90 (vs 90 deg) + * Appends a row to Results/RMS_log.csv (ABS & REL vs 20 deg & REL vs 90 deg) +- HTML auto-open controlled by OPEN_HTML flag + +Requires: numpy pandas plotly pyNastran matplotlib +""" + +import os, webbrowser, re, argparse, sys +import numpy as np +import pandas as pd +from math import factorial +from numpy.linalg import LinAlgError +from datetime import datetime + +# Best-effort UTF-8 console (ignored if not supported) +try: + sys.stdout.reconfigure(encoding="utf-8") + sys.stderr.reconfigure(encoding="utf-8") +except Exception: + pass + +# Try messagebox for friendlier fatal popup if available; otherwise print-only +try: + from tkinter import messagebox # optional +except Exception: + messagebox = None + +# Plotly / mesh +import plotly.graph_objects as go +from plotly.subplots import make_subplots +from matplotlib.tri import Triangulation + +# Nastran IO +from pyNastran.op2.op2 import OP2 +from pyNastran.bdf.bdf import BDF + +# ============ MONKEY PATCH FOR UNKNOWN NASTRAN VERSIONS ============ +import pyNastran.op2.op2_interface.version as pynastran_version + +_original_parse = pynastran_version.parse_nastran_version + +def patched_parse_nastran_version(data, version, encoding, log): + try: + return _original_parse(data, version, encoding, log) + except RuntimeError as e: + if 'unknown version' in str(e): + version_str = str(e).split('=')[1].strip("')") + log.warning(f'Unknown Nastran version {version_str}, attempting to parse anyway') + return 'msc', version_str # Assume MSC Nastran format + raise + +pynastran_version.parse_nastran_version = patched_parse_nastran_version +print("[INFO] Applied pyNastran version compatibility patch", flush=True) +# =================================================================== + + +# ---------------- Config ---------------- +N_MODES = 50 +AMP = 2.0 # visual scale for residual plot +PANCAKE = 10.0 # flattens Z range for viewing +PLOT_DOWNSAMPLE = 10000 +FILTER_LOW_ORDERS = 4 # piston, tip, tilt, defocus +SHOW_ZERNIKE_BAR = True # adds a horizontal bar chart of |coeff| with labels + +REQUIRED_SUBCASES = [90, 20, 40, 60] + +# Labels +POLISH_LABEL = "90" # polishing orientation (horizontal) +POLISH_TITLE = "90 deg" # for plot/table titles +REF_LABEL = "20" # reference subcase label as string +REF_TITLE = "20 deg" # for plot/table titles + +# Displacement unit in OP2 -> nm scale for WFE = 2*Disp_Z +DISP_SRC_UNIT = "mm" # "mm" or "m" +NM_PER_UNIT = 1e6 if DISP_SRC_UNIT == "mm" else 1e9 # 1 mm = 1e6 nm, 1 m = 1e9 nm + +# Name of fixed OP2 (used when --op2 is not supplied) +OP2_FIXED_NAME = "assy_m1_assyfem1_sim1-solution_1.op2" + +# Auto-open HTML reports? +OPEN_HTML = False +# ---------------------------------------- + + +# --------- Zernike utilities ---------- +def noll_indices(j: int): + if j < 1: + raise ValueError("Noll index j must be >= 1") + count = 0 + n = 0 + while True: + if n == 0: + ms = [0] + elif n % 2 == 0: + ms = [0] + [m for k in range(1, n//2 + 1) for m in (-2*k, 2*k)] + else: + ms = [m for k in range(0, (n+1)//2) for m in (-(2*k+1), (2*k+1))] + for m in ms: + count += 1 + if count == j: + return n, m + n += 1 + + +def zernike_noll(j, r, th): + n, m = noll_indices(j) + R = np.zeros_like(r) + for s in range((n-abs(m))//2 + 1): + c = ((-1)**s * factorial(n-s) / + (factorial(s) * + factorial((n+abs(m))//2 - s) * + factorial((n-abs(m))//2 - s))) + R += c * r**(n-2*s) + if m == 0: + return R + return R * (np.cos(m*th) if m > 0 else np.sin(-m*th)) + +def compute_zernike_coeffs_chunked(X, Y, vals, n_modes, chunk_size=100000): + Xc, Yc = X - np.mean(X), Y - np.mean(Y) + R = float(np.max(np.hypot(Xc, Yc))) + r = np.hypot(Xc/R, Yc/R).astype(np.float32) + th = np.arctan2(Yc, Xc).astype(np.float32) + mask = (r <= 1.0) & ~np.isnan(vals) + if not np.any(mask): + raise RuntimeError("No valid points inside unit disk.") + idx = np.nonzero(mask)[0] + m = int(n_modes) + G = np.zeros((m, m), dtype=np.float64) # Z^T Z + h = np.zeros((m,), dtype=np.float64) # Z^T v) + v = vals.astype(np.float64) + + for start in range(0, len(idx), chunk_size): + sl = idx[start:start+chunk_size] + r_b, th_b, v_b = r[sl], th[sl], v[sl] + Zb = np.column_stack([zernike_noll(j, r_b, th_b).astype(np.float32) + for j in range(1, m+1)]) + G += (Zb.T @ Zb).astype(np.float64) + h += (Zb.T @ v_b).astype(np.float64) + try: + coeffs = np.linalg.solve(G, h) + except LinAlgError: + coeffs = np.linalg.lstsq(G, h, rcond=None)[0] + return coeffs, R +# -------------------------------------- + + +# ------------- IO helpers ------------- +def find_dat_or_bdf(op2_path: str): + folder = os.path.dirname(op2_path) + base = os.path.splitext(os.path.basename(op2_path))[0] + for ext in (".dat", ".bdf"): + cand = os.path.join(folder, base + ext) + if os.path.exists(cand): + return cand + for name in os.listdir(folder): + if name.lower().endswith((".dat", ".bdf")): + return os.path.join(folder, name) + raise FileNotFoundError("No .dat or .bdf found in folder; geometry required.") + +def pick_files_cli(): + ap = argparse.ArgumentParser() + ap.add_argument("--op2", default=None, help="Full path to OP2 (optional)") + ap.add_argument("--simdir", default=None, help="Directory to search (optional)") + args = ap.parse_args() + + if args.op2: + op2_path = args.op2 + else: + base_dir = args.simdir or os.getcwd() + op2_path = os.path.join(base_dir, OP2_FIXED_NAME) + + if not os.path.exists(op2_path): + raise FileNotFoundError(f"OP2 not found: {op2_path}") + + dat_path = find_dat_or_bdf(op2_path) + return op2_path, dat_path + +def read_geometry(dat_path): + bdf = BDF() + bdf.read_bdf(dat_path) + node_geo = {int(nid): node.get_position() for nid, node in bdf.nodes.items()} + return node_geo + +def _extract_first_int(text: str): + if not isinstance(text, str): + return None + m = re.search(r'-?\d+', text) + return int(m.group(0)) if m else None + +def read_displacements_by_subcase(op2_path): + """ + Return dict with keys {'90','20','40','60'}. + Priority: subtitle numeric match, else isubcase numeric match. + If exact set not found: + - If 4+ numeric subcases exist, remap the 4 smallest to 90,20,40,60 (logged) + - Else: raise with a detailed dump. + Also writes an index CSV next to the OP2 for inspection. + """ + want = REQUIRED_SUBCASES[:] # [90,20,40,60] + out_exact = {} + debug_rows = [] + + op2 = OP2() + op2.read_op2(op2_path) + if not op2.displacements: + raise RuntimeError("No displacement subcases found in OP2.") + + for key, darr in op2.displacements.items(): + data = darr.data + dmat = data[0] if data.ndim == 3 else (data if data.ndim == 2 else None) + if dmat is None: + continue + + ngt = darr.node_gridtype.astype(int) + node_ids = ngt if ngt.ndim == 1 else ngt[:, 0] + + subtitle_raw = getattr(darr, 'subtitle', None) + isub_raw = getattr(darr, 'isubcase', None) + isub_int = int(isub_raw) if isinstance(isub_raw, int) else None + sub_from_text = _extract_first_int(subtitle_raw) if isinstance(subtitle_raw, str) else None + + debug_rows.append({ + "table_key": str(key), + "isubcase": isub_int, + "subtitle": str(subtitle_raw) if subtitle_raw is not None else "", + "subtitle_int": sub_from_text if sub_from_text is not None else "", + "nnodes": int(len(node_ids)), + "_node_ids": node_ids.astype(int), + "_dmat": dmat.copy(), + }) + + if isinstance(sub_from_text, int) and sub_from_text in want: + out_exact[str(sub_from_text)] = {"node_ids": node_ids.astype(int), "disp": dmat.copy()} + continue + if isinstance(isub_int, int) and isub_int in want: + out_exact[str(isub_int)] = {"node_ids": node_ids.astype(int), "disp": dmat.copy()} + + try: + folder = os.path.dirname(op2_path) + base = os.path.splitext(os.path.basename(op2_path))[0] + idx_path = os.path.join(folder, f"{base}_op2_displ_index.csv") + pd.DataFrame([{k: v for k, v in r.items() if not k.startswith('_')} for r in debug_rows])\ + .to_csv(idx_path, index=False) + print(f"[DEBUG] Wrote displacement index: {idx_path}", flush=True) + except Exception as e: + print(f"[WARN] Could not write displacement index CSV: {e}", flush=True) + + if all(str(x) in out_exact for x in want): + print("[INFO] Found exact subcases 90, 20, 40 and 60 via labels.", flush=True) + return out_exact + + numerics = [] + for r in debug_rows: + n = r.get("subtitle_int") + if not isinstance(n, int): + n = r.get("isubcase") if isinstance(r.get("isubcase"), int) else None + if isinstance(n, int): + numerics.append((n, r)) + numerics = sorted(numerics, key=lambda t: t[0]) + + if len(numerics) >= 4: + chosen = numerics[:4] + mapping = dict(zip([c[0] for c in chosen], want)) + out = {} + for n, r in chosen: + lbl = str(mapping[n]) + out[lbl] = {"node_ids": r["_node_ids"], "disp": r["_dmat"]} + print("[WARN] Exact 90/20/40/60 not found; remapped smallest numeric subcases " + f"{[c[0] for c in chosen]} -> {want}", flush=True) + return out + + print("\n[DEBUG] Displacement entries found (dump):", flush=True) + if debug_rows: + header = f'{"table_key":<18} {"isubcase":>7} {"subtitle":<40} {"subtitle_int":>12} {"nnodes":>8}' + print(header); print("-"*len(header)) + for r in debug_rows: + print(f'{r["table_key"]:<18} {str(r["isubcase"]):>7} {r["subtitle"]:<40} ' + f'{str(r["subtitle_int"]):>12} {r["nnodes"]:>8}', flush=True) + missing = [x for x in want if str(x) not in out_exact] + raise RuntimeError(f"Required subcases missing: {missing}. " + "Ensure SUBTITLEs (or isubcase IDs) are exactly 90, 20, 40 and 60.") +# -------------------------------------- + + +# ------------- Core logic ------------- +def build_dataframe_for_subcase(label, node_ids, dmat, node_geo): + rows = [] + for nid, vec in zip(node_ids, dmat): + x0, y0, z0 = node_geo.get(int(nid), (np.nan,)*3) + rows.append({ + "Subcase": label, + "NodeLabel": int(nid), + "X0": x0, "Y0": y0, "Z0": z0, + "Disp_Z": float(vec[2]), + "DispUnit": DISP_SRC_UNIT + }) + df = pd.DataFrame(rows) + if df[["X0", "Y0"]].isna().any().any(): + raise RuntimeError("Geometry missing (X0,Y0) for some nodes. Ensure .dat/.bdf matches the OP2.") + return df + +def compute_relative_dz(df_abs_ref, df_abs_ang): + a = df_abs_ref.set_index("NodeLabel") + b = df_abs_ang.set_index("NodeLabel") + common = a.index.intersection(b.index) + + b2 = b.loc[common].copy() + b2["Rel_Disp_Z"] = b2["Disp_Z"] - a.loc[common, "Disp_Z"] + b2.reset_index(inplace=True) + out = df_abs_ang.merge(b2[["NodeLabel", "Rel_Disp_Z"]], on="NodeLabel", how="inner") + out["Rel_WFE_nm"] = 2.0 * out["Rel_Disp_Z"] * NM_PER_UNIT + return out + +def compute_rms_pair_only(X, Y, W_nm): + coeffs, Rmax = compute_zernike_coeffs_chunked(X, Y, W_nm, N_MODES) + Xc = X - np.mean(X); Yc = Y - np.mean(Y) + r = np.hypot(Xc/Rmax, Yc/Rmax); th = np.arctan2(Yc, Xc) + Z = np.column_stack([zernike_noll(j, r, th) for j in range(1, N_MODES+1)]) + W_res_filt = W_nm - Z[:, :FILTER_LOW_ORDERS].dot(coeffs[:FILTER_LOW_ORDERS]) + global_rms = float(np.sqrt((W_nm ** 2).mean())) + filtered_rms = float(np.sqrt((W_res_filt ** 2).mean())) + return global_rms, filtered_rms + +def analyze_manufacturing_metrics(X, Y, W_nm): + """ + Manufacturing-related metrics at any angle. Returns individual mode magnitudes + and filtered RMS with different filter levels. (Numeric only.) + """ + coeffs, Rmax = compute_zernike_coeffs_chunked(X, Y, W_nm, N_MODES) + Xc = X - np.mean(X); Yc = Y - np.mean(Y) + r = np.hypot(Xc/Rmax, Yc/Rmax); th = np.arctan2(Yc, Xc) + Z = np.column_stack([zernike_noll(j, r, th) for j in range(1, N_MODES+1)]) + + W_res_filt3 = W_nm - Z[:, :3].dot(coeffs[:3]) # includes defocus + rms_filt3 = float(np.sqrt((W_res_filt3 ** 2).mean())) + + W_res_filt4 = W_nm - Z[:, :4].dot(coeffs[:4]) # operational metric + rms_filt4 = float(np.sqrt((W_res_filt4 ** 2).mean())) + + astig_rms = float(np.sqrt(coeffs[4]**2 + coeffs[5]**2)) # J5+J6 + coma_rms = float(np.sqrt(coeffs[6]**2 + coeffs[7]**2)) # J7+J8 + trefoil_rms = float(np.sqrt(coeffs[8]**2 + coeffs[9]**2)) # J9+J10 + + return { + "rms_filter_j1to3": rms_filt3, + "rms_filter_j1to4": rms_filt4, + "defocus_nm": float(abs(coeffs[3])), + "astigmatism_rms": astig_rms, + "coma_rms": coma_rms, + "trefoil_rms": trefoil_rms, + "spherical_nm": float(abs(coeffs[10])) + } + + +def zernike_common_name(n: int, m: int) -> str: + names = { + (0, 0): "Piston", + (1, -1): "Tilt X", + (1, 1): "Tilt Y", + (2, 0): "Defocus", + (2, -2): "Astig 45 deg", + (2, 2): "Astig 0 deg", + (3, -1): "Coma X", + (3, 1): "Coma Y", + (3, -3): "Trefoil X", + (3, 3): "Trefoil Y", + (4, 0): "Primary Spherical", + (4, -2): "Secondary Astig X", + (4, 2): "Secondary Astig Y", + (4, -4): "Quadrafoil X", + (4, 4): "Quadrafoil Y", + (5, -1): "Secondary Coma X", + (5, 1): "Secondary Coma Y", + (5, -3): "Secondary Trefoil X", + (5, 3): "Secondary Trefoil Y", + (5, -5): "Pentafoil X", + (5, 5): "Pentafoil Y", + (6, 0): "Secondary Spherical", + } + return names.get((n, m), f"Z(n={n}, m={m})") + +def zernike_label_for_j(j: int) -> str: + n, m = noll_indices(j) + return f"J{j:02d} - {zernike_common_name(n, m)} (n={n}, m={m})" + +def zernike_report(label, X, Y, W_nm, folder, base, is_relative: bool, + abs_pair=None, ref_title="20 deg", mfg_data=None, mfg_data_vs_20=None): + coeffs, Rmax = compute_zernike_coeffs_chunked(X, Y, W_nm, N_MODES) + Xc = X - np.mean(X); Yc = Y - np.mean(Y) + r = np.hypot(Xc/Rmax, Yc/Rmax); th = np.arctan2(Yc, Xc) + Z = np.column_stack([zernike_noll(j, r, th) for j in range(1, N_MODES+1)]) + W_res_filt = W_nm - Z[:, :FILTER_LOW_ORDERS].dot(coeffs[:FILTER_LOW_ORDERS]) + + global_rms = float(np.sqrt((W_nm ** 2).mean())) + filtered_rms = float(np.sqrt((W_res_filt ** 2).mean())) + + # Downsample for display only + n = len(X) + if n > PLOT_DOWNSAMPLE: + rng = np.random.default_rng(42) + sel = rng.choice(n, size=PLOT_DOWNSAMPLE, replace=False) + Xp, Yp, Wp = X[sel], Y[sel], W_res_filt[sel] + else: + Xp, Yp, Wp = X, Y, W_res_filt + + res_amp = AMP * Wp + max_amp = float(np.max(np.abs(res_amp))) if res_amp.size else 1.0 + + # Triangulate the downsampled set + mesh_traces = [] + try: + tri = Triangulation(Xp, Yp) + if tri.triangles is not None and len(tri.triangles) > 0: + i, j, k = tri.triangles.T + mesh_traces.append(go.Mesh3d( + x=Xp, y=Yp, z=res_amp, + i=i, j=j, k=k, + intensity=res_amp, + opacity=0.85, showscale=False + )) + except Exception: + pass + + mesh_traces.append(go.Scatter3d( + x=Xp, y=Yp, z=res_amp, + mode='markers', + marker=dict(size=2), + showlegend=False + )) + + labels = [zernike_label_for_j(j) for j in range(1, N_MODES+1)] + coeff_abs = np.abs(coeffs) + + title_rel = f" (relative to {ref_title})" if is_relative else " (absolute)" + + # Layout + if mfg_data is not None and mfg_data_vs_20 is not None: + if SHOW_ZERNIKE_BAR: + fig = make_subplots( + rows=5, cols=1, + specs=[[{"type":"scene"}], + [{"type":"table"}], + [{"type":"table"}], + [{"type":"table"}], + [{"type":"xy"}]], + row_heights=[0.40, 0.12, 0.12, 0.18, 0.18], + vertical_spacing=0.025, + subplot_titles=[ + f"Surface Residual & RMS of WFE - Subcase {label}{title_rel}", + "RMS Metrics (Absolute 90 deg)", + "Mode Magnitudes at 90 deg", + "Pre-Correction (90 deg - 20 deg)", + "|Zernike Coefficients| (nm)" + ] + ) + else: + fig = make_subplots( + rows=5, cols=1, + specs=[[{"type":"scene"}], + [{"type":"table"}], + [{"type":"table"}], + [{"type":"table"}], + [{"type":"table"}]], + row_heights=[0.40, 0.12, 0.12, 0.18, 0.18], + vertical_spacing=0.025, + subplot_titles=[ + f"Surface Residual & RMS of WFE - Subcase {label}{title_rel}", + "RMS Metrics (Absolute 90 deg)", + "Mode Magnitudes at 90 deg", + "Pre-Correction (90 deg - 20 deg)", + f"Zernike Coefficients ({N_MODES} modes)" + ] + ) + elif SHOW_ZERNIKE_BAR: + fig = make_subplots( + rows=4, cols=1, + specs=[[{"type":"scene"}], + [{"type":"table"}], + [{"type":"table"}], + [{"type":"xy"}]], + row_heights=[0.52, 0.12, 0.24, 0.12], + vertical_spacing=0.035, + subplot_titles=[ + f"Surface Residual & RMS of WFE - Subcase {label}{title_rel}", + "RMS Metrics", + f"Zernike Coefficients ({N_MODES} modes)", + "|Zernike Coefficients| (nm)" + ] + ) + else: + fig = make_subplots( + rows=3, cols=1, + specs=[[{"type":"scene"}], + [{"type":"table"}], + [{"type":"table"}]], + row_heights=[0.60, 0.14, 0.26], + vertical_spacing=0.03, + subplot_titles=[ + f"Surface Residual & RMS of WFE - Subcase {label}{title_rel}", + "RMS Metrics", + f"Zernike Coefficients ({N_MODES} modes)" + ] + ) + + for tr in mesh_traces: + fig.add_trace(tr, row=1, col=1) + + scene = fig.layout.scene + scene.camera = dict(eye=dict(x=0.5, y=0.5, z=0.5)) + scene.zaxis = dict(range=[-max_amp * PANCAKE, max_amp * PANCAKE]) + + # RMS table + if is_relative and abs_pair is not None: + abs_global, abs_filtered = abs_pair + fig.add_trace(go.Table( + header=dict(values=["Metric", "Relative (nm)", "Absolute (nm)"], align="left"), + cells=dict(values=[ + ["Global RMS","Filtered RMS (modes 1-4 removed)"], + [f"{global_rms:.2f}", f"{filtered_rms:.2f}"], + [f"{abs_global:.2f}", f"{abs_filtered:.2f}"], + ], align="left") + ), row=2, col=1) + elif mfg_data is not None and mfg_data_vs_20 is not None: + # 90 deg Absolute RMS (numeric only) + fig.add_trace(go.Table( + header=dict(values=["Metric","Value (nm)"], align="left"), + cells=dict(values=[ + ["Global RMS", "Filtered RMS (J1-J4)"], + [f"{global_rms:.2f}", f"{filtered_rms:.2f}"] + ], align="left") + ), row=2, col=1) + + # Mode magnitudes at 90 deg (absolute) — numeric only + fig.add_trace(go.Table( + header=dict(values=["Mode","Value (nm)"], align="left"), + cells=dict(values=[ + ["Filtered RMS (J1-J3, with defocus)", + "Astigmatism (J5+J6)", + "Coma (J7+J8)", + "Trefoil (J9+J10)", + "Spherical (J11)"], + [f"{mfg_data['rms_filter_j1to3']:.2f}", + f"{mfg_data['astigmatism_rms']:.2f}", + f"{mfg_data['coma_rms']:.2f}", + f"{mfg_data['trefoil_rms']:.2f}", + f"{mfg_data['spherical_nm']:.2f}"] + ], align="left") + ), row=3, col=1) + + # Pre-correction 90 deg - 20 deg — numeric only + fig.add_trace(go.Table( + header=dict(values=["Mode", "Correction (nm)"], align="left"), + cells=dict(values=[ + ["Total RMS (J1-J3 filter)", + "Defocus (J4)", + "Astigmatism (J5+J6)", + "Coma (J7+J8)"], + [f"{mfg_data_vs_20['rms_filter_j1to3']:.2f}", + f"{mfg_data_vs_20['defocus_nm']:.2f}", + f"{mfg_data_vs_20['astigmatism_rms']:.2f}", + f"{mfg_data_vs_20['coma_rms']:.2f}"] + ], align="left") + ), row=4, col=1) + + elif mfg_data is not None: + # Simple manufacturing table (compat) — numeric only + fig.add_trace(go.Table( + header=dict(values=["Metric","Value (nm)"], align="left"), + cells=dict(values=[ + ["Global RMS", + "Filtered RMS (J1-J4)", + "Filtered RMS (J1-J3, with defocus)", + "Defocus (J4)", + "Astigmatism (J5+J6)", + "Coma (J7+J8)", + "Trefoil (J9+J10)", + "Spherical (J11)"], + [f"{global_rms:.2f}", + f"{filtered_rms:.2f}", + f"{mfg_data['rms_filter_j1to3']:.2f}", + f"{mfg_data['defocus_nm']:.2f}", + f"{mfg_data['astigmatism_rms']:.2f}", + f"{mfg_data['coma_rms']:.2f}", + f"{mfg_data['trefoil_rms']:.2f}", + f"{mfg_data['spherical_nm']:.2f}"] + ], align="left") + ), row=2, col=1) + else: + # Standard absolute table + fig.add_trace(go.Table( + header=dict(values=["Metric","Value (nm)"], align="left"), + cells=dict(values=[ + ["Global RMS","Filtered RMS (modes 1-4 removed)"], + [f"{global_rms:.2f}", f"{filtered_rms:.2f}"] + ], align="left") + ), row=2, col=1) + + # Zernike coefficients + if mfg_data is not None and mfg_data_vs_20 is not None: + pass # manufacturing case: bar chart only + elif SHOW_ZERNIKE_BAR: + fig.add_trace(go.Table( + header=dict(values=["Noll j","Label","|Coeff| (nm)"], align="left"), + cells=dict(values=[ + list(range(1, N_MODES+1)), + labels, + list(np.round(coeff_abs, 3)) + ], align="left") + ), row=3, col=1) + else: + fig.add_trace(go.Table( + header=dict(values=["Noll j","Label","|Coeff| (nm)"], align="left"), + cells=dict(values=[ + list(range(1, N_MODES+1)), + labels, + list(np.round(coeff_abs, 3)) + ], align="left") + ), row=3, col=1) + + if SHOW_ZERNIKE_BAR: + bar_row = 5 if (mfg_data is not None and mfg_data_vs_20 is not None) else 4 + fig.add_trace( + go.Bar( + x=coeff_abs.tolist(), + y=labels, + orientation='h', + hovertemplate="%{y}
|Coeff| = %{x:.3f} nm", + showlegend=False + ), + row=bar_row, col=1 + ) + + fig.update_layout( + width=1400, + height=1400 if (mfg_data is not None and mfg_data_vs_20 is not None) + else (1200 if SHOW_ZERNIKE_BAR else 1000), + margin=dict(t=60,b=20) + ) + + # Save + suffix = f"_sub{label}" + html_path = os.path.join(folder, f"{base}{suffix}.html") + fig.write_html(html_path, include_plotlyjs='cdn') + + pd.DataFrame({ + "Noll_j": list(range(1, N_MODES+1)), + "Label": labels, + "Coeff_nm": [float(c) for c in coeffs], + "CoeffAbs_nm": [float(abs(c)) for c in coeffs] + }).to_csv(os.path.join(folder, f"{base}{suffix}_zcoeffs.csv"), index=False) + + if OPEN_HTML: + try: + webbrowser.open('file://' + os.path.abspath(html_path)) + except Exception: + pass + + return { + "global_rms_nm": global_rms, + "filtered_rms_nm": filtered_rms, + "html_path": html_path + } +# -------------------------------------- + + +def write_exp_file(folder, abs_by_angle, rel20_by_angle, rel90_by_angle, mfg_90=None, mfg_90_vs_20=None): + """ + Writes Iteration_results_expression.exp in [MilliMeter]. + For each angle a in {90,20,40,60}, writes: + - RMS_a_Global, RMS_a_Filtered (absolute) + - RMS_a_Global_Rel20, RMS_a_Filtered_Rel20 (relative to 20 deg) + - RMS_a_Global_Rel90, RMS_a_Filtered_Rel90 (relative to 90 deg) + """ + required = ["90", "20", "40", "60"] + out_path = os.path.join(folder, "Iteration_results_expression.exp") + nm_to_mm = lambda x: float(x) * 1e-6 + + def _missing(dct): + return [a for a in required if a not in dct] + + miss_abs = _missing(abs_by_angle) + miss_r20 = _missing(rel20_by_angle) + miss_r90 = _missing(rel90_by_angle) + if miss_abs or miss_r20 or miss_r90: + raise RuntimeError(f"EXP export aborted. Missing sets -> " + f"abs:{miss_abs}, rel20:{miss_r20}, rel90:{miss_r90}.") + + lines = ["// Version: 3"] + for ang in required: + gA, fA = abs_by_angle[ang] + g20, f20 = rel20_by_angle[ang] + g90, f90 = rel90_by_angle[ang] + lines.append(f"[MilliMeter]RMS_{ang}_Global={nm_to_mm(gA):.7f}") + lines.append(f"[MilliMeter]RMS_{ang}_Filtered={nm_to_mm(fA):.7f}") + lines.append(f"[MilliMeter]RMS_{ang}_Global_Rel20={nm_to_mm(g20):.7f}") + lines.append(f"[MilliMeter]RMS_{ang}_Filtered_Rel20={nm_to_mm(f20):.7f}") + lines.append(f"[MilliMeter]RMS_{ang}_Global_Rel90={nm_to_mm(g90):.7f}") + lines.append(f"[MilliMeter]RMS_{ang}_Filtered_Rel90={nm_to_mm(f90):.7f}") + + # Manufacturing metrics for 90 deg (numeric only) + if mfg_90_vs_20 and mfg_90: + lines.append(f"[MilliMeter]RMS_90_Optician_Workload={nm_to_mm(mfg_90_vs_20['rms_filter_j1to3']):.7f}") + lines.append(f"[MilliMeter]RMS_90_Astig_Abs={nm_to_mm(mfg_90['astigmatism_rms']):.7f}") + lines.append(f"[MilliMeter]RMS_90_Coma_Abs={nm_to_mm(mfg_90['coma_rms']):.7f}") + + with open(out_path, "w", encoding="utf-8-sig", newline="\n") as f: + f.write("\n".join(lines) + "\n") + print(f"[OK] Wrote EXP: {out_path}") + + +def main(): + op2_path, dat_path = pick_files_cli() + folder = os.path.dirname(op2_path) + base = os.path.splitext(os.path.basename(op2_path))[0] + + results_dir = os.environ.get("ZERNIKE_RESULTS_DIR") or os.path.join(folder, "Results") + os.makedirs(results_dir, exist_ok=True) + + env_run_id = os.environ.get("ZERNIKE_RUN_ID") + if env_run_id: + run_id = env_run_id + try: + run_dt = datetime.strptime(env_run_id, "%Y%m%d_%H%M%S") + except Exception: + run_dt = datetime.now() + else: + run_dt = datetime.now() + run_id = run_dt.strftime("%Y%m%d_%H%M%S") + + base_ts = f"{base}_{run_id}" + + node_geo = read_geometry(dat_path) + subdisp = read_displacements_by_subcase(op2_path) # dict: '90','20','40','60' + + # ABS for 20 deg (reference for pages) + if REF_LABEL not in subdisp: + raise RuntimeError(f"Reference subcase '{REF_LABEL}' not found after mapping. " + "Check *_op2_displ_index.csv for labels.") + + df_ref_abs = build_dataframe_for_subcase( + REF_LABEL, + subdisp[REF_LABEL]["node_ids"], + subdisp[REF_LABEL]["disp"], + node_geo + ) + df_ref_abs["WFE_nm"] = 2.0 * df_ref_abs["Disp_Z"] * NM_PER_UNIT + Xref = df_ref_abs["X0"].to_numpy() + Yref = df_ref_abs["Y0"].to_numpy() + Wref = df_ref_abs["WFE_nm"].to_numpy() + abs_ref_global, abs_ref_filtered = compute_rms_pair_only(Xref, Yref, Wref) + + # ABS for 90 deg (manufacturing reference; used for REL vs 90) + if POLISH_LABEL not in subdisp: + raise RuntimeError(f"Subcase '{POLISH_LABEL}' not found after mapping.") + df90_abs = build_dataframe_for_subcase( + POLISH_LABEL, + subdisp[POLISH_LABEL]["node_ids"], + subdisp[POLISH_LABEL]["disp"], + node_geo + ) + df90_abs["WFE_nm"] = 2.0 * df90_abs["Disp_Z"] * NM_PER_UNIT + X90 = df90_abs["X0"].to_numpy() + Y90 = df90_abs["Y0"].to_numpy() + W90 = df90_abs["WFE_nm"].to_numpy() + abs90_global, abs90_filtered = compute_rms_pair_only(X90, Y90, W90) + + combined_rows = [] + def emit_rows(df, label, is_relative): + if is_relative: + for row in df.itertuples(index=False): + combined_rows.append({ + "Subcase": label, + "NodeLabel": row.NodeLabel, + "X0": row.X0, "Y0": row.Y0, "Z0": row.Z0, + "Rel_Disp_Z": getattr(row, "Rel_Disp_Z"), + "WFE_nm": getattr(row, "Rel_WFE_nm"), + "DispUnit": DISP_SRC_UNIT, + "RelativeToRef": True, + "RefAngleDeg": REF_LABEL + }) + else: + for row in df.itertuples(index=False): + combined_rows.append({ + "Subcase": label, + "NodeLabel": row.NodeLabel, + "X0": row.X0, "Y0": row.Y0, "Z0": row.Z0, + "Disp_Z": row.Disp_Z, + "WFE_nm": row.WFE_nm, + "DispUnit": DISP_SRC_UNIT, + "RelativeToRef": False, + "RefAngleDeg": "" + }) + + # Emit absolute 20 deg + emit_rows(df_ref_abs, REF_LABEL, is_relative=False) + + metrics_summary = [{ + "Subcase": f"sub{REF_LABEL}", + "Abs_GlobalRMS_nm": abs_ref_global, + "Abs_FilteredRMS_nm": abs_ref_filtered, + "Rel20_GlobalRMS_nm": np.nan, # reference has no relative (vs 20 deg) + "Rel20_FilteredRMS_nm": np.nan + }] + + abs_values_by_angle = {REF_LABEL: (abs_ref_global, abs_ref_filtered), POLISH_LABEL: (abs90_global, abs90_filtered)} + rel20_values_by_angle = {REF_LABEL: (0.0, 0.0)} # 20 vs 20 = 0 + rel90_values_by_angle = {POLISH_LABEL: (0.0, 0.0)} # 90 vs 90 = 0 + + # Process all angles (ABS; REL vs 20 deg for pages) + all_angles = [str(a) for a in REQUIRED_SUBCASES] + for ang in [a for a in all_angles if a != REF_LABEL]: + if ang not in subdisp: + raise RuntimeError(f"Subcase '{ang}' not found after mapping. " + "Check *_op2_displ_index.csv for labels.") + + if ang == POLISH_LABEL: + abs_global, abs_filtered = abs_values_by_angle[ang] + X_abs, Y_abs = X90, Y90 + else: + df_abs = build_dataframe_for_subcase(ang, subdisp[ang]["node_ids"], subdisp[ang]["disp"], node_geo) + W_abs_nm = 2.0 * df_abs["Disp_Z"].to_numpy() * NM_PER_UNIT + X_abs = df_abs["X0"].to_numpy(); Y_abs = df_abs["Y0"].to_numpy() + abs_global, abs_filtered = compute_rms_pair_only(X_abs, Y_abs, W_abs_nm) + abs_values_by_angle[ang] = (abs_global, abs_filtered) + + # REL vs 20 + if ang == POLISH_LABEL: + df_rel20 = compute_relative_dz(df_ref_abs, df90_abs) + else: + df_rel20 = compute_relative_dz(df_ref_abs, df_abs) + emit_rows(df_rel20, ang, is_relative=True) # Only emit CSV rows for 40/60 + + X_rel20 = df_rel20["X0"].to_numpy(); Y_rel20 = df_rel20["Y0"].to_numpy() + W_rel20 = df_rel20["Rel_WFE_nm"].to_numpy() + + # HTML for 40 and 60 only + if ang != POLISH_LABEL: + m_rel20 = zernike_report( + ang, X_rel20, Y_rel20, W_rel20, results_dir, base_ts, + is_relative=True, abs_pair=(abs_global, abs_filtered), ref_title=REF_TITLE + ) + rel20_values_by_angle[ang] = (m_rel20["global_rms_nm"], m_rel20["filtered_rms_nm"]) + + metrics_summary.append({ + "Subcase": f"sub{ang}", + "Abs_GlobalRMS_nm": abs_global, + "Abs_FilteredRMS_nm": abs_filtered, + "Rel20_GlobalRMS_nm": m_rel20["global_rms_nm"], + "Rel20_FilteredRMS_nm": m_rel20["filtered_rms_nm"] + }) + else: + g20, f20 = compute_rms_pair_only(X_rel20, Y_rel20, W_rel20) + rel20_values_by_angle[ang] = (g20, f20) + + # REL vs 90 for all angles (metrics only) + for ang in all_angles: + df_abs = build_dataframe_for_subcase(ang, subdisp[ang]["node_ids"], subdisp[ang]["disp"], node_geo) + df_rel90 = compute_relative_dz(df90_abs, df_abs) + X_rel90 = df_rel90["X0"].to_numpy(); Y_rel90 = df_rel90["Y0"].to_numpy() + W_rel90 = df_rel90["Rel_WFE_nm"].to_numpy() + g90, f90 = compute_rms_pair_only(X_rel90, Y_rel90, W_rel90) + rel90_values_by_angle[ang] = (g90, f90) + + # Manufacturing metrics for 90 + mfg_metrics_90 = None + mfg_metrics_90_vs_20 = None + + if POLISH_LABEL in subdisp: + mfg_metrics_90 = analyze_manufacturing_metrics(X90, Y90, W90) + + df90_rel20 = compute_relative_dz(df_ref_abs, df90_abs) + X90_rel = df90_rel20["X0"].to_numpy() + Y90_rel = df90_rel20["Y0"].to_numpy() + W90_rel = df90_rel20["Rel_WFE_nm"].to_numpy() + + mfg_metrics_90_vs_20 = analyze_manufacturing_metrics(X90_rel, Y90_rel, W90_rel) + + # Special HTML for 90 (numeric only) + _m90 = zernike_report( + POLISH_LABEL, X90, Y90, W90, results_dir, base_ts, + is_relative=False, + mfg_data=mfg_metrics_90, + mfg_data_vs_20=mfg_metrics_90_vs_20 + ) + + # HTML for 20 deg relative to 90 deg + df_20_rel90 = compute_relative_dz(df90_abs, df_ref_abs) + X_20_rel90 = df_20_rel90["X0"].to_numpy() + Y_20_rel90 = df_20_rel90["Y0"].to_numpy() + W_20_rel90 = df_20_rel90["Rel_WFE_nm"].to_numpy() + + _m_ref = zernike_report( + REF_LABEL, X_20_rel90, Y_20_rel90, W_20_rel90, results_dir, base_ts, + is_relative=True, abs_pair=(abs_ref_global, abs_ref_filtered), ref_title=POLISH_TITLE + ) + + # Combined nodal CSV + comb_df = pd.DataFrame(combined_rows) + comb_path = os.path.join(results_dir, f"{base_ts}_all_subcases_nodal.csv") + comb_df.to_csv(comb_path, index=False) + + # Summary CSV (REL20 naming) + summary_path = os.path.join(results_dir, f"{base_ts}_RMS_summary.csv") + pd.DataFrame(metrics_summary, columns=[ + "Subcase", + "Abs_GlobalRMS_nm","Abs_FilteredRMS_nm", + "Rel20_GlobalRMS_nm","Rel20_FilteredRMS_nm" + ]).to_csv(summary_path, index=False) + + # EXP export + write_exp_file(results_dir, abs_values_by_angle, rel20_values_by_angle, rel90_values_by_angle, + mfg_90=mfg_metrics_90, mfg_90_vs_20=mfg_metrics_90_vs_20) + + # Persistent RMS log + log_path = os.path.join(results_dir, "RMS_log.csv") + row = { + "RunID": run_id, + "RunDateTimeLocal": run_dt.strftime("%Y-%m-%d %H:%M:%S"), + "OP2_Base": base, + "DispUnit": DISP_SRC_UNIT, + "ReferenceAngleDeg": REF_LABEL + } + for ang in [str(a) for a in REQUIRED_SUBCASES]: + gA, fA = abs_values_by_angle.get(ang, (np.nan, np.nan)) + gR20, fR20 = rel20_values_by_angle.get(ang, (np.nan, np.nan)) + gR90, fR90 = rel90_values_by_angle.get(ang, (np.nan, np.nan)) + row[f"Abs_GlobalRMS_nm_{ang}"] = gA + row[f"Abs_FilteredRMS_nm_{ang}"] = fA + row[f"Rel_GlobalRMS_nm_{ang}_vs20"] = gR20 + row[f"Rel_FilteredRMS_nm_{ang}_vs20"] = fR20 + row[f"Rel_GlobalRMS_nm_{ang}_vs90"] = gR90 + row[f"Rel_FilteredRMS_nm_{ang}_vs90"] = fR90 + + if mfg_metrics_90 and mfg_metrics_90_vs_20: + row["Mfg_90_OpticianWorkload_nm"] = mfg_metrics_90_vs_20['rms_filter_j1to3'] + row["Mfg_90_Astig_Abs_nm"] = mfg_metrics_90['astigmatism_rms'] + row["Mfg_90_Coma_Abs_nm"] = mfg_metrics_90['coma_rms'] + + pd.DataFrame([row]).to_csv( + log_path, + mode="a", + header=not os.path.exists(log_path), + index=False + ) + + # Console output (numeric only; ASCII-safe) + if mfg_metrics_90 and mfg_metrics_90_vs_20: + print("\n" + "="*60) + print("MANUFACTURING METRICS (90 deg horizontal polishing)") + print("="*60) + + print(f"\n90 deg Absolute (surface shape on test stand):") + print(f" Filtered RMS (J1-J4): {mfg_metrics_90['rms_filter_j1to4']:.1f} nm") + print(f" Astigmatism (J5+J6): {mfg_metrics_90['astigmatism_rms']:.1f} nm") + print(f" Coma (J7+J8): {mfg_metrics_90['coma_rms']:.1f} nm") + print(f" Trefoil (J9+J10): {mfg_metrics_90['trefoil_rms']:.1f} nm") + + print(f"\n90 deg - 20 deg (pre-correction to polish in):") + print(f" Total RMS (J1-J3): {mfg_metrics_90_vs_20['rms_filter_j1to3']:.1f} nm") + print(f" Defocus (J4): {mfg_metrics_90_vs_20['defocus_nm']:.1f} nm") + print(f" Astigmatism (J5+J6): {mfg_metrics_90_vs_20['astigmatism_rms']:.1f} nm") + print(f" Coma (J7+J8): {mfg_metrics_90_vs_20['coma_rms']:.1f} nm") + print("="*60 + "\n") + + print("[OK] Wrote:") + print(" -", comb_path) + print(" -", summary_path) + print(" -", log_path, "(appended)") + + for r in metrics_summary: + tag = r["Subcase"] + if tag == f"sub{REF_LABEL}": + print(f" - {base_ts}_{tag}.html (ABS Global={r['Abs_GlobalRMS_nm']:.2f} nm, " + f"ABS Filtered={r['Abs_FilteredRMS_nm']:.2f} nm)") + else: + print(f" - {base_ts}_{tag}.html (REL(vs20) Global={r['Rel20_GlobalRMS_nm']:.2f} nm, " + f"REL(vs20) Filtered={r['Rel20_FilteredRMS_nm']:.2f} nm | " + f"ABS Global={r['Abs_GlobalRMS_nm']:.2f} nm, " + f"ABS Filtered={r['Abs_FilteredRMS_nm']:.2f} nm)") + +if __name__ == "__main__": + try: + main() + except Exception as e: + msg = f"Fatal error: {e}" + print(msg) + if messagebox: + try: + messagebox.showerror("Fatal error", str(e)) + except Exception: + pass diff --git a/examples/optimization_config_zernike_mirror.json b/examples/optimization_config_zernike_mirror.json new file mode 100644 index 00000000..a2f4ad75 --- /dev/null +++ b/examples/optimization_config_zernike_mirror.json @@ -0,0 +1,124 @@ +{ + "$schema": "Atomizer Optimization Config - Telescope Mirror Zernike Optimization", + "_description": "Example configuration for optimizing telescope mirror support structures using Zernike wavefront error metrics", + + "study_name": "mirror_wfe_optimization", + + "design_variables": [ + { + "name": "support_ring_radius", + "expression_name": "support_radius", + "min": 150.0, + "max": 250.0, + "units": "mm", + "description": "Radial position of support ring" + }, + { + "name": "support_count", + "expression_name": "n_supports", + "min": 3, + "max": 12, + "type": "integer", + "units": "count", + "description": "Number of support points" + }, + { + "name": "support_stiffness", + "expression_name": "k_support", + "min": 1000.0, + "max": 50000.0, + "units": "N/mm", + "description": "Support spring stiffness" + } + ], + + "objectives": [ + { + "name": "filtered_rms_20deg", + "description": "Filtered RMS WFE at 20 deg elevation (operational)", + "direction": "minimize", + "extractor": "zernike", + "extractor_config": { + "subcase": "20", + "metric": "filtered_rms_nm", + "displacement_unit": "mm", + "n_modes": 50, + "filter_orders": 4 + } + }, + { + "name": "mass", + "description": "Total mirror assembly mass", + "direction": "minimize", + "extractor": "mass_from_expression", + "extractor_config": { + "expression_name": "total_mass" + } + } + ], + + "constraints": [ + { + "name": "max_stress", + "description": "Maximum von Mises stress in mirror", + "type": "upper_bound", + "threshold": 5.0, + "units": "MPa", + "extractor": "von_mises_stress", + "extractor_config": { + "subcase": "90" + } + }, + { + "name": "astigmatism_limit", + "description": "Astigmatism RMS at polishing orientation", + "type": "upper_bound", + "threshold": 50.0, + "units": "nm", + "extractor": "zernike", + "extractor_config": { + "subcase": "90", + "metric": "astigmatism_rms_nm" + } + } + ], + + "optimization_settings": { + "n_trials": 200, + "sampler": "NSGA-II", + "protocol": 11, + "n_startup_trials": 30, + "seed": 42 + }, + + "simulation_settings": { + "solver": "NX Nastran", + "solution_name": null, + "required_subcases": [20, 40, 60, 90], + "op2_pattern": "*-solution_1.op2" + }, + + "zernike_settings": { + "_description": "Global Zernike analysis settings", + "n_modes": 50, + "filter_low_orders": 4, + "displacement_unit": "mm", + "reference_subcase": "20", + "polishing_subcase": "90", + "operational_subcases": ["20", "40", "60"], + "metrics_to_log": [ + "global_rms_nm", + "filtered_rms_nm", + "astigmatism_rms_nm", + "coma_rms_nm", + "trefoil_rms_nm", + "spherical_nm" + ] + }, + + "output_settings": { + "save_zernike_coefficients": true, + "generate_html_reports": true, + "export_exp_file": true + } +} diff --git a/nx_journals/user_generated_journals/journal_afem_update_workflow_exemple.py b/nx_journals/user_generated_journals/journal_afem_update_workflow_exemple.py new file mode 100644 index 00000000..60d8727b --- /dev/null +++ b/nx_journals/user_generated_journals/journal_afem_update_workflow_exemple.py @@ -0,0 +1,5510 @@ +# NX 2506 +# Journal created by Antoine on Fri Nov 28 15:20:19 2025 Eastern Standard Time +# +import math +import NXOpen +import NXOpen.Assemblies +import NXOpen.CAE +def main(args) : + + theSession = NXOpen.Session.GetSession() #type: NXOpen.Session + workSimPart = theSession.Parts.BaseWork + displaySimPart = theSession.Parts.BaseDisplay + markId1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Load Part") + + part1, partLoadStatus1 = theSession.Parts.Open("C:\\Users\\Antoine\\CADTOMASTE\\Atomizer\\M1-Gigabit\\Latest\\ASSY_M1.prt") + + partLoadStatus1.Dispose() + markId2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Load Part") + + part2, partLoadStatus2 = theSession.Parts.Open("C:\\Users\\Antoine\\CADTOMASTE\\Atomizer\\M1-Gigabit\\Latest\\M1_Blank_fem1_i.prt") + + partLoadStatus2.Dispose() + markId3 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part") + + part3 = theSession.Parts.FindObject("M1_Blank") + status1, partLoadStatus3 = theSession.Parts.SetActiveDisplay(part3, NXOpen.DisplayPartOption.AllowAdditional, NXOpen.PartDisplayPartWorkPartOption.UseLast) + + workSimPart = NXOpen.BasePart.Null + workPart = theSession.Parts.Work + displaySimPart = NXOpen.BasePart.Null + displayPart = theSession.Parts.Display + partLoadStatus3.Dispose() + theSession.ApplicationSwitchImmediate("UG_APP_MODELING") + + # ---------------------------------------------- + # Menu: Tools->Utilities->Expressions... + # ---------------------------------------------- + markId4 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + + theSession.SetUndoMarkName(markId4, "Expressions Dialog") + + markId5 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Import Expressions") + + expModified1, errorMessages1 = workPart.Expressions.ImportFromFile("C:\\Users\\Antoine\\Atomizer\\nx_journals\\user_generated_journals\\study_variables_expressions_from_journal.exp", NXOpen.ExpressionCollection.ImportMode.Replace) + + markId6 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Edit Expression") + + expression1 = workPart.Expressions.FindObject("whiffle_outer_to_vertical") + unit1 = workPart.UnitCollection.FindObject("MilliMeter") + workPart.Expressions.EditExpressionWithUnits(expression1, unit1, "75.00") + + markId7 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Expressions") + + theSession.DeleteUndoMark(markId7, None) + + markId8 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Expressions") + + markId9 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Make Up to Date") + + markId10 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Update Expression") + + objects1 = [NXOpen.NXObject.Null] * 1 + objects1[0] = expression1 + theSession.UpdateManager.MakeUpToDate(objects1, markId10) + + nErrs1 = theSession.UpdateManager.DoUpdate(markId10) + + objects2 = [NXOpen.NXObject.Null] * 1 + objects2[0] = expression1 + theSession.UpdateManager.MakeUpToDate(objects2, markId9) + + markId11 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update") + + nErrs2 = theSession.UpdateManager.DoUpdate(markId11) + + theSession.DeleteUndoMark(markId11, "NX update") + + theSession.DeleteUndoMark(markId9, None) + + theSession.DeleteUndoMark(markId8, None) + + theSession.SetUndoMarkName(markId4, "Expressions") + + theSession.DeleteUndoMark(markId5, None) + + markId12 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part") + + simPart1 = theSession.Parts.FindObject("ASSY_M1_assyfem1_sim1") + status2, partLoadStatus4 = theSession.Parts.SetActiveDisplay(simPart1, NXOpen.DisplayPartOption.AllowAdditional, NXOpen.PartDisplayPartWorkPartOption.UseLast) + + workPart = NXOpen.Part.Null + workSimPart = theSession.Parts.BaseWork # ASSY_M1_assyfem1_sim1 + displayPart = NXOpen.Part.Null + displaySimPart = theSession.Parts.BaseDisplay # ASSY_M1_assyfem1_sim1 + partLoadStatus4.Dispose() + theSession.ApplicationSwitchImmediate("UG_APP_SFEM") + + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + simPart2 = workSimPart + theSession.Post.UpdateUserGroupsFromSimPart(simPart2) + + markId13 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part") + + component1 = workSimPart.ComponentAssembly.RootComponent.FindObject("COMPONENT ASSY_M1_assyfem1 1") + component2 = component1.FindObject("COMPONENT M1_Blank_fem1 1") + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + partLoadStatus5 = theSession.Parts.SetWorkComponent(component2, NXOpen.PartCollection.RefsetOption.Entire, NXOpen.PartCollection.WorkComponentOption.Visible) + + workFemPart = theSession.Parts.BaseWork + partLoadStatus5.Dispose() + # ---------------------------------------------- + # Menu: Edit->Update + # ---------------------------------------------- + markId14 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Update FE Model") + + fEModel1 = workFemPart.FindObject("FEModel") + fEModel1.UpdateFemodel() + + markId15 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part") + + component3 = component1.FindObject("COMPONENT M1_Vertical_Support_Skeleton_fem1 3") + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + partLoadStatus6 = theSession.Parts.SetWorkComponent(component3, NXOpen.PartCollection.RefsetOption.Entire, NXOpen.PartCollection.WorkComponentOption.Visible) + + workFemPart = theSession.Parts.BaseWork # M1_Vertical_Support_Skeleton_fem1 + partLoadStatus6.Dispose() + markId16 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Load Part") + + part4, partLoadStatus7 = theSession.Parts.Open("C:\\Users\\Antoine\\CADTOMASTE\\Atomizer\\M1-Gigabit\\Latest\\M1_Vertical_Support_Skeleton_fem1_i.prt") + + partLoadStatus7.Dispose() + # ---------------------------------------------- + # Menu: Edit->Update + # ---------------------------------------------- + markId17 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Update FE Model") + + fEModel2 = workFemPart.FindObject("FEModel") + fEModel2.UpdateFemodel() + + markId18 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part") + + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + partLoadStatus8 = theSession.Parts.SetWorkComponent(component1, NXOpen.PartCollection.RefsetOption.Entire, NXOpen.PartCollection.WorkComponentOption.Visible) + + workAssyFemPart = theSession.Parts.BaseWork + partLoadStatus8.Dispose() + # ---------------------------------------------- + # Menu: Analysis->Finite Element Model Check->Duplicate Nodes... + # ---------------------------------------------- + markId19 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + + caePart1 = workAssyFemPart + duplicateNodesCheckBuilder1 = caePart1.ModelCheckMgr.CreateDuplicateNodesCheckBuilder() + + unit2 = duplicateNodesCheckBuilder1.Tolerance.Units + + duplicateNodesCheckBuilder1.Tolerance.Units = unit2 + + duplicateNodesCheckBuilder1.Tolerance.SetFormula("0.01") + + duplicateNodesCheckBuilder1.MergeOccurrenceNodes = True + + theSession.SetUndoMarkName(markId19, "Duplicate Nodes Dialog") + + displaysettings1 = NXOpen.CAE.ModelCheck.DuplicateNodesCheckBuilder.DisplaySettings() + + displaysettings1.ShowDuplicateNodes = True + displaysettings1.ShowMergedNodeLabels = False + displaysettings1.ShowRetainedNodeLabels = False + displaysettings1.KeepNodesColor = displaySimPart.Colors.Find("Blue") + displaysettings1.MergeNodesColor = displaySimPart.Colors.Find("Yellow") + displaysettings1.UnableToMergeNodesColor = displaySimPart.Colors.Find("Red") + duplicateNodesCheckBuilder1.DisplaySettingsData = displaysettings1 + + selectTaggedObjectList1 = duplicateNodesCheckBuilder1.SelectionList + + duplicateNodesCheckBuilder1.CheckScopeOption = NXOpen.CAE.ModelCheck.CheckScope.Displayed + + duplicateNodesCheckBuilder1.IdentifyDuplicateNodes() + + scaleAboutPoint1 = NXOpen.Point3d(-933.20293900317381, 483.96876985524074, 0.0) + viewCenter1 = NXOpen.Point3d(933.20293900318075, -483.96876985525068, 0.0) + displaySimPart.ModelingViews.WorkView.ZoomAboutPoint(0.80000000000000004, scaleAboutPoint1, viewCenter1) + + scaleAboutPoint2 = NXOpen.Point3d(-1166.5036737539685, 604.96096231905221, 0.0) + viewCenter2 = NXOpen.Point3d(1166.5036737539749, -604.96096231906211, 0.0) + displaySimPart.ModelingViews.WorkView.ZoomAboutPoint(0.80000000000000004, scaleAboutPoint2, viewCenter2) + + duplicateNodesCheckBuilder1.MergeDuplicateNodes() + + theSession.SetUndoMarkName(markId19, "Duplicate Nodes") + + duplicateNodesCheckBuilder1.Destroy() + + theSession.DeleteUndoMark(markId19, None) + + markId20 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + + assyFemPart1 = workAssyFemPart + assemblyLabelManagerBuilder1 = assyFemPart1.CreateAssemblyLabelManagerBuilder() + + theSession.SetUndoMarkName(markId20, "Assembly Label Manager Dialog") + + # ---------------------------------------------- + # Dialog Begin Assembly Label Manager + # ---------------------------------------------- + markId21 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Assembly Label Manager") + + theSession.DeleteUndoMark(markId21, None) + + markId22 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Assembly Label Manager") + + fEModelOccurrence1 = workAssyFemPart.FindObject("FEModelOccurrence[3]") + entitytypes1 = [None] * 7 + entitytypes1[0] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Node + entitytypes1[1] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Element + entitytypes1[2] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Csys + entitytypes1[3] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Physical + entitytypes1[4] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Group + entitytypes1[5] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ply + entitytypes1[6] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ssmo + offsetentitytypelabeltonearest1 = [None] * 7 + offsetentitytypelabeltonearest1[0] = 2 + offsetentitytypelabeltonearest1[1] = 2 + offsetentitytypelabeltonearest1[2] = 2 + offsetentitytypelabeltonearest1[3] = 2 + offsetentitytypelabeltonearest1[4] = 2 + offsetentitytypelabeltonearest1[5] = 2 + offsetentitytypelabeltonearest1[6] = 2 + assemblyLabelManagerBuilder1.SetFEModelOccOffsets(fEModelOccurrence1, entitytypes1, offsetentitytypelabeltonearest1) + + fEModelOccurrence2 = workAssyFemPart.FindObject("FEModelOccurrence[4]") + entitytypes2 = [None] * 7 + entitytypes2[0] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Node + entitytypes2[1] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Element + entitytypes2[2] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Csys + entitytypes2[3] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Physical + entitytypes2[4] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Group + entitytypes2[5] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ply + entitytypes2[6] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ssmo + offsetentitytypelabeltonearest2 = [None] * 7 + offsetentitytypelabeltonearest2[0] = 74 + offsetentitytypelabeltonearest2[1] = 74 + offsetentitytypelabeltonearest2[2] = 74 + offsetentitytypelabeltonearest2[3] = 74 + offsetentitytypelabeltonearest2[4] = 74 + offsetentitytypelabeltonearest2[5] = 74 + offsetentitytypelabeltonearest2[6] = 74 + assemblyLabelManagerBuilder1.SetFEModelOccOffsets(fEModelOccurrence2, entitytypes2, offsetentitytypelabeltonearest2) + + fEModelOccurrence3 = workAssyFemPart.FindObject("FEModelOccurrence[5]") + entitytypes3 = [None] * 7 + entitytypes3[0] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Node + entitytypes3[1] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Element + entitytypes3[2] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Csys + entitytypes3[3] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Physical + entitytypes3[4] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Group + entitytypes3[5] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ply + entitytypes3[6] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ssmo + offsetentitytypelabeltonearest3 = [None] * 7 + offsetentitytypelabeltonearest3[0] = 146 + offsetentitytypelabeltonearest3[1] = 146 + offsetentitytypelabeltonearest3[2] = 146 + offsetentitytypelabeltonearest3[3] = 146 + offsetentitytypelabeltonearest3[4] = 146 + offsetentitytypelabeltonearest3[5] = 146 + offsetentitytypelabeltonearest3[6] = 146 + assemblyLabelManagerBuilder1.SetFEModelOccOffsets(fEModelOccurrence3, entitytypes3, offsetentitytypelabeltonearest3) + + fEModelOccurrence4 = workAssyFemPart.FindObject("FEModelOccurrence[7]") + entitytypes4 = [None] * 7 + entitytypes4[0] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Node + entitytypes4[1] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Element + entitytypes4[2] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Csys + entitytypes4[3] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Physical + entitytypes4[4] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Group + entitytypes4[5] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ply + entitytypes4[6] = NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ssmo + offsetentitytypelabeltonearest4 = [None] * 7 + offsetentitytypelabeltonearest4[0] = 218 + offsetentitytypelabeltonearest4[1] = 218 + offsetentitytypelabeltonearest4[2] = 218 + offsetentitytypelabeltonearest4[3] = 218 + offsetentitytypelabeltonearest4[4] = 218 + offsetentitytypelabeltonearest4[5] = 218 + offsetentitytypelabeltonearest4[6] = 218 + assemblyLabelManagerBuilder1.SetFEModelOccOffsets(fEModelOccurrence4, entitytypes4, offsetentitytypelabeltonearest4) + + nXObject1 = assemblyLabelManagerBuilder1.Commit() + + theSession.DeleteUndoMark(markId22, None) + + theSession.SetUndoMarkName(markId20, "Assembly Label Manager") + + assemblyLabelManagerBuilder1.Destroy() + + markId23 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part") + + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + # No object found for the subject of the next call. + # This may be because of previous exceptions + # # .Dispose() + partLoadStatus9 = theSession.Parts.SetWorkComponent(NXOpen.Assemblies.Component.Null, NXOpen.PartCollection.RefsetOption.Entire, NXOpen.PartCollection.WorkComponentOption.Visible) + + workSimPart = theSession.Parts.BaseWork # ASSY_M1_assyfem1_sim1 + partLoadStatus9.Dispose() + # ---------------------------------------------- + # Menu: Analysis->Solve... + # ---------------------------------------------- + markId24 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + + theSession.SetUndoMarkName(markId24, "Solve Dialog") + + markId25 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve") + + theSession.DeleteUndoMark(markId25, None) + + markId26 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve") + + theCAESimSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession) + + psolutions1 = [NXOpen.CAE.SimSolution.Null] * 1 + simSimulation1 = workSimPart.FindObject("Simulation") + simSolution1 = simSimulation1.FindObject("Solution[Solution 1]") + psolutions1[0] = simSolution1 + numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveChainOfSolutions(psolutions1, NXOpen.CAE.SimSolution.SolveOption.Solve, NXOpen.CAE.SimSolution.SetupCheckOption.CompleteCheckAndOutputErrors, NXOpen.CAE.SimSolution.SolveMode.Background) + + theSession.DeleteUndoMark(markId26, None) + + theSession.SetUndoMarkName(markId24, "Solve") + + # ---------------------------------------------- + # Menu: Tools->Automation->Journal->Stop Recording + # ---------------------------------------------- + +if __name__ == '__main__': + main(sys.argv[1:]) \ No newline at end of file diff --git a/optimization_engine/extractors/__init__.py b/optimization_engine/extractors/__init__.py index 6ab63361..d38c7cae 100644 --- a/optimization_engine/extractors/__init__.py +++ b/optimization_engine/extractors/__init__.py @@ -1 +1,25 @@ -"""Core extractor library for Atomizer.""" +"""Core extractor library for Atomizer. + +Available extractors: +- Displacement: extract_displacement +- Stress: extract_solid_stress (von Mises) +- Frequency: extract_frequency +- Mass: extract_mass_from_expression, extract_mass_from_op2 +- Zernike: extract_zernike_from_op2, ZernikeExtractor (telescope mirrors) +""" + +# Zernike extractor for telescope mirror optimization +from optimization_engine.extractors.extract_zernike import ( + ZernikeExtractor, + extract_zernike_from_op2, + extract_zernike_filtered_rms, + extract_zernike_relative_rms, +) + +__all__ = [ + # Zernike (telescope mirrors) + 'ZernikeExtractor', + 'extract_zernike_from_op2', + 'extract_zernike_filtered_rms', + 'extract_zernike_relative_rms', +] diff --git a/optimization_engine/extractors/extract_zernike.py b/optimization_engine/extractors/extract_zernike.py new file mode 100644 index 00000000..834bdf47 --- /dev/null +++ b/optimization_engine/extractors/extract_zernike.py @@ -0,0 +1,860 @@ +""" +Zernike Coefficient Extractor for Telescope Mirror Optimization +================================================================ + +Extracts Zernike polynomial coefficients from OP2 displacement results +for optical surface quality analysis. Designed for telescope mirror +optimization where wavefront error (WFE) metrics are critical. + +Key Features: +- Noll-indexed Zernike polynomials (standard optical convention) +- Multi-subcase support (different gravity orientations: 20, 40, 60, 90 deg) +- Global and filtered RMS wavefront error +- Individual aberration magnitudes (astigmatism, coma, trefoil, spherical) +- Relative metrics between subcases (e.g., operational vs polishing orientation) + +Usage: + from optimization_engine.extractors.extract_zernike import ( + extract_zernike_from_op2, + ZernikeExtractor + ) + + # Simple usage - get filtered RMS for optimization objective + result = extract_zernike_from_op2(op2_file, subcase=20) + rms_filtered = result['filtered_rms_nm'] + + # Full extractor for detailed analysis + extractor = ZernikeExtractor(op2_file, bdf_file) + metrics = extractor.extract_all_subcases() + +Author: Atomizer Framework (adapted from telescope mirror analysis scripts) +""" + +from pathlib import Path +from typing import Dict, Any, Optional, List, Tuple, Union +import numpy as np +from math import factorial +from numpy.linalg import LinAlgError + +try: + from pyNastran.op2.op2 import OP2 + from pyNastran.bdf.bdf import BDF +except ImportError: + raise ImportError("pyNastran is required. Install with: pip install pyNastran") + + +# ============================================================================ +# Configuration +# ============================================================================ + +DEFAULT_N_MODES = 50 # Number of Zernike modes to fit +DEFAULT_FILTER_ORDERS = 4 # Filter first N modes (piston, tip, tilt, defocus) +DEFAULT_CHUNK_SIZE = 100000 # For memory-efficient processing of large meshes + +# Standard telescope orientations (gravity angles in degrees) +STANDARD_SUBCASES = [20, 40, 60, 90] + +# Displacement unit conversions (to nanometers for WFE) +UNIT_TO_NM = { + 'mm': 1e6, # 1 mm = 1e6 nm + 'm': 1e9, # 1 m = 1e9 nm + 'um': 1e3, # 1 um = 1e3 nm + 'nm': 1.0, # already nm +} + + +# ============================================================================ +# Zernike Polynomial Mathematics +# ============================================================================ + +def noll_indices(j: int) -> Tuple[int, int]: + """ + Convert Noll index j to radial order n and azimuthal frequency m. + + The Noll indexing scheme is the standard convention in optics. + j=1: Piston, j=2,3: Tip/Tilt, j=4: Defocus, j=5,6: Astigmatism, etc. + + Args: + j: Noll index (1-based) + + Returns: + (n, m): Radial order and azimuthal frequency + """ + if j < 1: + raise ValueError("Noll index j must be >= 1") + + count = 0 + n = 0 + while True: + if n == 0: + ms = [0] + elif n % 2 == 0: + ms = [0] + [m for k in range(1, n//2 + 1) for m in (-2*k, 2*k)] + else: + ms = [m for k in range(0, (n+1)//2) for m in (-(2*k+1), (2*k+1))] + for m in ms: + count += 1 + if count == j: + return n, m + n += 1 + + +def zernike_radial(n: int, m: int, r: np.ndarray) -> np.ndarray: + """ + Compute the radial component R_n^m(r) of the Zernike polynomial. + + Args: + n: Radial order + m: Azimuthal frequency (absolute value used) + r: Radial coordinates (normalized to unit disk) + + Returns: + Radial polynomial evaluated at r + """ + R = np.zeros_like(r) + m_abs = abs(m) + + for s in range((n - m_abs) // 2 + 1): + coef = ((-1)**s * factorial(n - s) / + (factorial(s) * + factorial((n + m_abs) // 2 - s) * + factorial((n - m_abs) // 2 - s))) + R += coef * r**(n - 2*s) + + return R + + +def zernike_noll(j: int, r: np.ndarray, theta: np.ndarray) -> np.ndarray: + """ + Evaluate Noll-indexed Zernike polynomial Z_j(r, theta). + + Args: + j: Noll index + r: Radial coordinates (normalized to unit disk) + theta: Angular coordinates (radians) + + Returns: + Zernike polynomial values at (r, theta) + """ + n, m = noll_indices(j) + R = zernike_radial(n, m, r) + + if m == 0: + return R + elif m > 0: + return R * np.cos(m * theta) + else: + return R * np.sin(-m * theta) + + +def zernike_name(j: int) -> str: + """ + Get common optical name for Zernike mode. + + Args: + j: Noll index + + Returns: + Human-readable name (e.g., "Defocus", "Astigmatism 0 deg") + """ + n, m = noll_indices(j) + + names = { + (0, 0): "Piston", + (1, -1): "Tilt X", + (1, 1): "Tilt Y", + (2, 0): "Defocus", + (2, -2): "Astigmatism 45 deg", + (2, 2): "Astigmatism 0 deg", + (3, -1): "Coma X", + (3, 1): "Coma Y", + (3, -3): "Trefoil X", + (3, 3): "Trefoil Y", + (4, 0): "Primary Spherical", + (4, -2): "Secondary Astig X", + (4, 2): "Secondary Astig Y", + (4, -4): "Quadrafoil X", + (4, 4): "Quadrafoil Y", + (5, -1): "Secondary Coma X", + (5, 1): "Secondary Coma Y", + (5, -3): "Secondary Trefoil X", + (5, 3): "Secondary Trefoil Y", + (5, -5): "Pentafoil X", + (5, 5): "Pentafoil Y", + (6, 0): "Secondary Spherical", + } + + return names.get((n, m), f"Z(n={n}, m={m})") + + +def zernike_label(j: int) -> str: + """Full label for Zernike mode: J{j} - Name (n=, m=)""" + n, m = noll_indices(j) + return f"J{j:02d} - {zernike_name(j)} (n={n}, m={m})" + + +# ============================================================================ +# Zernike Coefficient Fitting +# ============================================================================ + +def compute_zernike_coefficients( + x: np.ndarray, + y: np.ndarray, + values: np.ndarray, + n_modes: int = DEFAULT_N_MODES, + chunk_size: int = DEFAULT_CHUNK_SIZE +) -> Tuple[np.ndarray, float]: + """ + Fit Zernike coefficients to surface data using least-squares. + + Uses chunked processing for memory efficiency with large meshes. + Points outside the unit disk (after centering/normalization) are excluded. + + Args: + x, y: Node coordinates (will be centered and normalized) + values: Surface values at each node (e.g., WFE in nm) + n_modes: Number of Zernike modes to fit + chunk_size: Chunk size for memory-efficient processing + + Returns: + (coefficients, R_max): Zernike coefficients and normalization radius + """ + # Center coordinates + x_centered = x - np.mean(x) + y_centered = y - np.mean(y) + + # Normalize to unit disk + R_max = float(np.max(np.hypot(x_centered, y_centered))) + r = np.hypot(x_centered / R_max, y_centered / R_max).astype(np.float32) + theta = np.arctan2(y_centered, x_centered).astype(np.float32) + + # Mask: inside unit disk and valid values + mask = (r <= 1.0) & ~np.isnan(values) + if not np.any(mask): + raise RuntimeError("No valid points inside unit disk for Zernike fitting.") + + idx = np.nonzero(mask)[0] + m = int(n_modes) + + # Normal equations: (Z^T Z) c = Z^T v + # Build incrementally for memory efficiency + G = np.zeros((m, m), dtype=np.float64) # Z^T Z + h = np.zeros((m,), dtype=np.float64) # Z^T v + v = values.astype(np.float64) + + for start in range(0, len(idx), chunk_size): + chunk_idx = idx[start:start + chunk_size] + r_chunk = r[chunk_idx] + theta_chunk = theta[chunk_idx] + v_chunk = v[chunk_idx] + + # Build Zernike basis for this chunk + Z_chunk = np.column_stack([ + zernike_noll(j, r_chunk, theta_chunk).astype(np.float32) + for j in range(1, m + 1) + ]) + + # Accumulate normal equations + G += (Z_chunk.T @ Z_chunk).astype(np.float64) + h += (Z_chunk.T @ v_chunk).astype(np.float64) + + # Solve normal equations + try: + coeffs = np.linalg.solve(G, h) + except LinAlgError: + coeffs = np.linalg.lstsq(G, h, rcond=None)[0] + + return coeffs, R_max + + +# ============================================================================ +# RMS Calculations +# ============================================================================ + +def compute_rms_metrics( + x: np.ndarray, + y: np.ndarray, + wfe: np.ndarray, + n_modes: int = DEFAULT_N_MODES, + filter_orders: int = DEFAULT_FILTER_ORDERS +) -> Dict[str, float]: + """ + Compute global and filtered RMS wavefront error. + + Args: + x, y: Node coordinates + wfe: Wavefront error values (nm) + n_modes: Number of Zernike modes to fit + filter_orders: Number of low-order modes to filter (typically 4) + + Returns: + Dict with 'global_rms_nm' and 'filtered_rms_nm' + """ + coeffs, R_max = compute_zernike_coefficients(x, y, wfe, n_modes) + + # Reconstruct filtered WFE (remove low-order modes) + x_c = x - np.mean(x) + y_c = y - np.mean(y) + r = np.hypot(x_c / R_max, y_c / R_max) + theta = np.arctan2(y_c, x_c) + + # Build Zernike basis for low-order modes only + Z_low = np.column_stack([ + zernike_noll(j, r, theta) for j in range(1, filter_orders + 1) + ]) + + # Subtract low-order contribution + wfe_filtered = wfe - Z_low @ coeffs[:filter_orders] + + global_rms = float(np.sqrt(np.mean(wfe**2))) + filtered_rms = float(np.sqrt(np.mean(wfe_filtered**2))) + + return { + 'global_rms_nm': global_rms, + 'filtered_rms_nm': filtered_rms, + 'coefficients': coeffs, + 'R_max': R_max + } + + +def compute_aberration_magnitudes(coeffs: np.ndarray) -> Dict[str, float]: + """ + Compute RMS magnitudes of common optical aberrations. + + Args: + coeffs: Zernike coefficients (at least 11 modes) + + Returns: + Dict with aberration RMS values in nm + """ + if len(coeffs) < 11: + raise ValueError("Need at least 11 Zernike modes for aberration analysis") + + return { + 'defocus_nm': float(abs(coeffs[3])), # J4 + 'astigmatism_rms_nm': float(np.sqrt(coeffs[4]**2 + coeffs[5]**2)), # J5+J6 + 'coma_rms_nm': float(np.sqrt(coeffs[6]**2 + coeffs[7]**2)), # J7+J8 + 'trefoil_rms_nm': float(np.sqrt(coeffs[8]**2 + coeffs[9]**2)), # J9+J10 + 'spherical_nm': float(abs(coeffs[10])), # J11 + } + + +# ============================================================================ +# OP2/BDF Data Extraction +# ============================================================================ + +def read_node_geometry(bdf_path: Path) -> Dict[int, np.ndarray]: + """ + Read node coordinates from BDF/DAT file. + + Args: + bdf_path: Path to .bdf or .dat file + + Returns: + Dict mapping node ID to [x, y, z] coordinates + """ + bdf = BDF() + bdf.read_bdf(str(bdf_path)) + + return { + int(nid): node.get_position() + for nid, node in bdf.nodes.items() + } + + +def find_geometry_file(op2_path: Path) -> Path: + """ + Find matching BDF/DAT file for an OP2. + + Looks for same-basename first, then any .dat/.bdf in same folder. + + Args: + op2_path: Path to OP2 file + + Returns: + Path to geometry file + """ + folder = op2_path.parent + base = op2_path.stem + + # Try same basename + for ext in ['.dat', '.bdf']: + cand = folder / (base + ext) + if cand.exists(): + return cand + + # Try any geometry file + for name in folder.iterdir(): + if name.suffix.lower() in ['.dat', '.bdf']: + return name + + raise FileNotFoundError(f"No .dat or .bdf geometry file found for {op2_path}") + + +def extract_displacements_by_subcase( + op2_path: Path, + required_subcases: Optional[List[int]] = None +) -> Dict[str, Dict[str, np.ndarray]]: + """ + Extract displacement data from OP2 organized by subcase. + + Args: + op2_path: Path to OP2 file + required_subcases: List of required subcases (e.g., [20, 40, 60, 90]) + + Returns: + Dict keyed by subcase label: {'20': {'node_ids': array, 'disp': array}, ...} + """ + op2 = OP2() + op2.read_op2(str(op2_path)) + + if not op2.displacements: + raise RuntimeError("No displacement data found in OP2 file") + + result = {} + + for key, darr in op2.displacements.items(): + data = darr.data + dmat = data[0] if data.ndim == 3 else (data if data.ndim == 2 else None) + if dmat is None: + continue + + ngt = darr.node_gridtype.astype(int) + node_ids = ngt if ngt.ndim == 1 else ngt[:, 0] + + # Try to identify subcase from subtitle or isubcase + subtitle = getattr(darr, 'subtitle', None) + isubcase = getattr(darr, 'isubcase', None) + + # Extract numeric from subtitle + label = None + if isinstance(subtitle, str): + import re + m = re.search(r'-?\d+', subtitle) + if m: + label = m.group(0) + + if label is None and isinstance(isubcase, int): + label = str(isubcase) + + if label: + result[label] = { + 'node_ids': node_ids.astype(int), + 'disp': dmat.copy() + } + + # Validate required subcases if specified + if required_subcases: + missing = [str(s) for s in required_subcases if str(s) not in result] + if missing: + available = list(result.keys()) + raise RuntimeError( + f"Required subcases {missing} not found. Available: {available}" + ) + + return result + + +# ============================================================================ +# Main Extractor Class +# ============================================================================ + +class ZernikeExtractor: + """ + Complete Zernike analysis extractor for telescope mirror optimization. + + This class handles: + - Loading OP2 displacement results + - Matching with BDF geometry + - Computing Zernike coefficients and RMS metrics + - Multi-subcase analysis (different gravity orientations) + - Relative metrics between subcases + + Example usage in optimization: + extractor = ZernikeExtractor(op2_file, bdf_file) + + # For single-objective optimization (minimize filtered RMS at 20 deg) + result = extractor.extract_subcase('20') + objective = result['filtered_rms_nm'] + + # For multi-subcase optimization + all_results = extractor.extract_all_subcases() + """ + + def __init__( + self, + op2_path: Union[str, Path], + bdf_path: Optional[Union[str, Path]] = None, + displacement_unit: str = 'mm', + n_modes: int = DEFAULT_N_MODES, + filter_orders: int = DEFAULT_FILTER_ORDERS + ): + """ + Initialize Zernike extractor. + + Args: + op2_path: Path to OP2 results file + bdf_path: Path to BDF/DAT geometry file (auto-detected if None) + displacement_unit: Unit of displacement in OP2 ('mm', 'm', 'um', 'nm') + n_modes: Number of Zernike modes to fit + filter_orders: Number of low-order modes to filter + """ + self.op2_path = Path(op2_path) + self.bdf_path = Path(bdf_path) if bdf_path else find_geometry_file(self.op2_path) + self.displacement_unit = displacement_unit + self.n_modes = n_modes + self.filter_orders = filter_orders + + # Unit conversion factor (displacement to nm) + self.nm_scale = UNIT_TO_NM.get(displacement_unit.lower(), 1e6) + + # WFE = 2 * surface displacement (optical convention) + self.wfe_factor = 2.0 * self.nm_scale + + # Lazy-loaded data + self._node_geo = None + self._displacements = None + + @property + def node_geometry(self) -> Dict[int, np.ndarray]: + """Lazy-load node geometry from BDF.""" + if self._node_geo is None: + self._node_geo = read_node_geometry(self.bdf_path) + return self._node_geo + + @property + def displacements(self) -> Dict[str, Dict[str, np.ndarray]]: + """Lazy-load displacements from OP2.""" + if self._displacements is None: + self._displacements = extract_displacements_by_subcase(self.op2_path) + return self._displacements + + def _build_dataframe( + self, + subcase_label: str + ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + """ + Build coordinate and WFE arrays for a subcase. + + Returns: + (X, Y, WFE_nm): Arrays of coordinates and wavefront error + """ + if subcase_label not in self.displacements: + available = list(self.displacements.keys()) + raise ValueError(f"Subcase '{subcase_label}' not found. Available: {available}") + + data = self.displacements[subcase_label] + node_ids = data['node_ids'] + disp = data['disp'] + + # Build arrays + X, Y, WFE = [], [], [] + for nid, vec in zip(node_ids, disp): + geo = self.node_geometry.get(int(nid)) + if geo is None: + continue + + X.append(geo[0]) + Y.append(geo[1]) + # Z-displacement to WFE (nm) + wfe = vec[2] * self.wfe_factor + WFE.append(wfe) + + return np.array(X), np.array(Y), np.array(WFE) + + def extract_subcase( + self, + subcase_label: str, + include_coefficients: bool = False + ) -> Dict[str, Any]: + """ + Extract Zernike metrics for a single subcase. + + Args: + subcase_label: Subcase identifier (e.g., '20', '90') + include_coefficients: Whether to include all Zernike coefficients + + Returns: + Dict with RMS metrics, aberrations, and optionally coefficients + """ + X, Y, WFE = self._build_dataframe(subcase_label) + + # Compute RMS metrics + rms_result = compute_rms_metrics( + X, Y, WFE, self.n_modes, self.filter_orders + ) + + # Compute aberration magnitudes + aberrations = compute_aberration_magnitudes(rms_result['coefficients']) + + result = { + 'subcase': subcase_label, + 'global_rms_nm': rms_result['global_rms_nm'], + 'filtered_rms_nm': rms_result['filtered_rms_nm'], + 'n_nodes': len(X), + **aberrations + } + + if include_coefficients: + result['coefficients'] = rms_result['coefficients'].tolist() + result['coefficient_labels'] = [zernike_label(j) for j in range(1, self.n_modes + 1)] + + return result + + def extract_relative( + self, + target_subcase: str, + reference_subcase: str + ) -> Dict[str, Any]: + """ + Extract Zernike metrics relative to a reference subcase. + + Computes: WFE_relative = WFE_target - WFE_reference + + Args: + target_subcase: Subcase to analyze + reference_subcase: Reference subcase to subtract + + Returns: + Dict with relative RMS metrics and aberrations + """ + X_t, Y_t, WFE_t = self._build_dataframe(target_subcase) + X_r, Y_r, WFE_r = self._build_dataframe(reference_subcase) + + # Build node-to-index mapping for reference + target_data = self.displacements[target_subcase] + ref_data = self.displacements[reference_subcase] + + ref_node_to_idx = { + int(nid): i for i, nid in enumerate(ref_data['node_ids']) + } + + # Compute relative WFE for common nodes + X_rel, Y_rel, WFE_rel = [], [], [] + + for i, nid in enumerate(target_data['node_ids']): + nid = int(nid) + if nid not in ref_node_to_idx: + continue + + ref_idx = ref_node_to_idx[nid] + geo = self.node_geometry.get(nid) + if geo is None: + continue + + X_rel.append(geo[0]) + Y_rel.append(geo[1]) + + target_wfe = target_data['disp'][i, 2] * self.wfe_factor + ref_wfe = ref_data['disp'][ref_idx, 2] * self.wfe_factor + WFE_rel.append(target_wfe - ref_wfe) + + X_rel = np.array(X_rel) + Y_rel = np.array(Y_rel) + WFE_rel = np.array(WFE_rel) + + # Compute metrics on relative WFE + rms_result = compute_rms_metrics( + X_rel, Y_rel, WFE_rel, self.n_modes, self.filter_orders + ) + aberrations = compute_aberration_magnitudes(rms_result['coefficients']) + + return { + 'target_subcase': target_subcase, + 'reference_subcase': reference_subcase, + 'relative_global_rms_nm': rms_result['global_rms_nm'], + 'relative_filtered_rms_nm': rms_result['filtered_rms_nm'], + 'n_common_nodes': len(X_rel), + **{f'relative_{k}': v for k, v in aberrations.items()} + } + + def extract_all_subcases( + self, + reference_subcase: Optional[str] = '20' + ) -> Dict[str, Dict[str, Any]]: + """ + Extract metrics for all available subcases. + + Args: + reference_subcase: Reference for relative calculations (None to skip) + + Returns: + Dict mapping subcase label to metrics dict + """ + results = {} + + for label in self.displacements.keys(): + results[label] = self.extract_subcase(label) + + # Add relative metrics if reference specified + if reference_subcase and label != reference_subcase: + try: + rel = self.extract_relative(label, reference_subcase) + results[label].update({ + f'rel_{reference_subcase}_{k}': v + for k, v in rel.items() + if k.startswith('relative_') + }) + except Exception as e: + results[label][f'rel_{reference_subcase}_error'] = str(e) + + return results + + +# ============================================================================ +# Convenience Functions for Optimization +# ============================================================================ + +def extract_zernike_from_op2( + op2_file: Union[str, Path], + bdf_file: Optional[Union[str, Path]] = None, + subcase: Union[int, str] = 1, + displacement_unit: str = 'mm', + n_modes: int = DEFAULT_N_MODES, + filter_orders: int = DEFAULT_FILTER_ORDERS +) -> Dict[str, Any]: + """ + Convenience function to extract Zernike metrics from OP2. + + This is the main entry point for optimization objectives. + + Args: + op2_file: Path to OP2 results file + bdf_file: Path to BDF geometry (auto-detected if None) + subcase: Subcase identifier + displacement_unit: Unit of displacement in OP2 + n_modes: Number of Zernike modes + filter_orders: Low-order modes to filter + + Returns: + Dict with: + - 'global_rms_nm': Global RMS WFE in nanometers + - 'filtered_rms_nm': Filtered RMS (low orders removed) + - 'defocus_nm', 'astigmatism_rms_nm', etc.: Individual aberrations + """ + extractor = ZernikeExtractor( + op2_file, bdf_file, displacement_unit, n_modes, filter_orders + ) + return extractor.extract_subcase(str(subcase)) + + +def extract_zernike_filtered_rms( + op2_file: Union[str, Path], + bdf_file: Optional[Union[str, Path]] = None, + subcase: Union[int, str] = 1, + **kwargs +) -> float: + """ + Extract filtered RMS WFE - the primary metric for mirror optimization. + + Filtered RMS removes piston, tip, tilt, and defocus (modes 1-4), + which can be corrected by alignment and focus adjustment. + + Args: + op2_file: Path to OP2 file + bdf_file: Path to BDF geometry (auto-detected if None) + subcase: Subcase identifier + **kwargs: Additional arguments for ZernikeExtractor + + Returns: + Filtered RMS WFE in nanometers + """ + result = extract_zernike_from_op2(op2_file, bdf_file, subcase, **kwargs) + return result['filtered_rms_nm'] + + +def extract_zernike_relative_rms( + op2_file: Union[str, Path], + target_subcase: Union[int, str], + reference_subcase: Union[int, str], + bdf_file: Optional[Union[str, Path]] = None, + **kwargs +) -> float: + """ + Extract relative filtered RMS between two subcases. + + Useful for analyzing gravity-induced deformation relative to + a reference orientation (e.g., polishing position). + + Args: + op2_file: Path to OP2 file + target_subcase: Subcase to analyze + reference_subcase: Reference subcase + bdf_file: Path to BDF geometry + **kwargs: Additional arguments for ZernikeExtractor + + Returns: + Relative filtered RMS WFE in nanometers + """ + extractor = ZernikeExtractor(op2_file, bdf_file, **kwargs) + result = extractor.extract_relative(str(target_subcase), str(reference_subcase)) + return result['relative_filtered_rms_nm'] + + +# ============================================================================ +# Module Exports +# ============================================================================ + +__all__ = [ + # Main extractor class + 'ZernikeExtractor', + + # Convenience functions for optimization + 'extract_zernike_from_op2', + 'extract_zernike_filtered_rms', + 'extract_zernike_relative_rms', + + # Zernike utilities (for advanced use) + 'compute_zernike_coefficients', + 'compute_rms_metrics', + 'compute_aberration_magnitudes', + 'noll_indices', + 'zernike_noll', + 'zernike_name', + 'zernike_label', +] + + +if __name__ == '__main__': + # Example/test usage + import sys + + if len(sys.argv) > 1: + op2_file = Path(sys.argv[1]) + + print(f"Analyzing: {op2_file}") + + try: + extractor = ZernikeExtractor(op2_file) + + print(f"\nAvailable subcases: {list(extractor.displacements.keys())}") + + results = extractor.extract_all_subcases() + + for label, metrics in results.items(): + print(f"\n=== Subcase {label} ===") + print(f" Global RMS: {metrics['global_rms_nm']:.2f} nm") + print(f" Filtered RMS: {metrics['filtered_rms_nm']:.2f} nm") + print(f" Astigmatism: {metrics['astigmatism_rms_nm']:.2f} nm") + print(f" Coma: {metrics['coma_rms_nm']:.2f} nm") + print(f" Trefoil: {metrics['trefoil_rms_nm']:.2f} nm") + print(f" Spherical: {metrics['spherical_nm']:.2f} nm") + + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + else: + print("Usage: python extract_zernike.py ") + print("\nThis module provides Zernike coefficient extraction for telescope mirror optimization.") + print("\nExample in optimization config:") + print(' "objectives": [') + print(' {') + print(' "name": "filtered_rms",') + print(' "extractor": "zernike",') + print(' "direction": "minimize",') + print(' "extractor_config": {') + print(' "subcase": "20",') + print(' "metric": "filtered_rms_nm"') + print(' }') + print(' }') + print(' ]') diff --git a/optimization_engine/extractors/zernike_helpers.py b/optimization_engine/extractors/zernike_helpers.py new file mode 100644 index 00000000..3995176c --- /dev/null +++ b/optimization_engine/extractors/zernike_helpers.py @@ -0,0 +1,403 @@ +""" +Zernike Helper Functions for Atomizer Optimization +=================================================== + +Convenience wrappers and utilities for using Zernike analysis +in optimization studies. These helpers simplify integration with +the standard Atomizer optimization patterns. + +Usage in run_optimization.py: + from optimization_engine.extractors.zernike_helpers import ( + create_zernike_objective, + ZernikeObjectiveBuilder + ) + + # Simple: create objective function + zernike_obj = create_zernike_objective( + op2_finder=lambda: sim_dir / "model-solution_1.op2", + subcase="20", + metric="filtered_rms_nm" + ) + + # Use in Optuna trial + rms = zernike_obj() +""" + +from pathlib import Path +from typing import Callable, Dict, Any, Optional, Union, List +import logging + +from optimization_engine.extractors.extract_zernike import ( + ZernikeExtractor, + extract_zernike_from_op2, + extract_zernike_filtered_rms, +) + +logger = logging.getLogger(__name__) + + +def create_zernike_objective( + op2_finder: Callable[[], Path], + bdf_finder: Optional[Callable[[], Path]] = None, + subcase: Union[int, str] = "20", + metric: str = "filtered_rms_nm", + displacement_unit: str = "mm", + **kwargs +) -> Callable[[], float]: + """ + Create a Zernike objective function for optimization. + + This factory creates a callable that: + 1. Finds the OP2 file (using op2_finder) + 2. Extracts Zernike metrics + 3. Returns the specified metric value + + Args: + op2_finder: Callable that returns path to current OP2 file + bdf_finder: Callable that returns path to BDF file (auto-detect if None) + subcase: Subcase to analyze (e.g., "20" for 20 deg elevation) + metric: Metric to return (see available_metrics below) + displacement_unit: Unit of displacement in OP2 file + **kwargs: Additional arguments for ZernikeExtractor + + Returns: + Callable that returns the metric value + + Available metrics: + - global_rms_nm: Global RMS wavefront error + - filtered_rms_nm: Filtered RMS (low orders removed) + - defocus_nm: Defocus aberration + - astigmatism_rms_nm: Combined astigmatism + - coma_rms_nm: Combined coma + - trefoil_rms_nm: Combined trefoil + - spherical_nm: Primary spherical aberration + + Example: + op2_finder = lambda: Path("sim_dir") / "model-solution_1.op2" + objective = create_zernike_objective(op2_finder, subcase="20") + + # In optimization loop + rms_value = objective() # Returns filtered RMS in nm + """ + def evaluate() -> float: + op2_path = op2_finder() + bdf_path = bdf_finder() if bdf_finder else None + + result = extract_zernike_from_op2( + op2_path, + bdf_path, + subcase=subcase, + displacement_unit=displacement_unit, + **kwargs + ) + + if metric not in result: + available = [k for k in result.keys() if isinstance(result[k], (int, float))] + raise ValueError(f"Metric '{metric}' not found. Available: {available}") + + return result[metric] + + return evaluate + + +def create_relative_zernike_objective( + op2_finder: Callable[[], Path], + target_subcase: Union[int, str], + reference_subcase: Union[int, str], + bdf_finder: Optional[Callable[[], Path]] = None, + metric: str = "relative_filtered_rms_nm", + **kwargs +) -> Callable[[], float]: + """ + Create objective for relative Zernike metrics between subcases. + + Useful for minimizing gravity-induced deformation relative to + a reference orientation (e.g., polishing position at 90 deg). + + Args: + op2_finder: Callable returning OP2 path + target_subcase: Subcase to analyze + reference_subcase: Reference subcase to subtract + bdf_finder: Optional BDF path finder + metric: Relative metric to return + **kwargs: Additional ZernikeExtractor arguments + + Returns: + Callable that returns relative metric value + """ + def evaluate() -> float: + op2_path = op2_finder() + bdf_path = bdf_finder() if bdf_finder else None + + extractor = ZernikeExtractor(op2_path, bdf_path, **kwargs) + result = extractor.extract_relative( + str(target_subcase), + str(reference_subcase) + ) + + if metric not in result: + available = [k for k in result.keys() if isinstance(result[k], (int, float))] + raise ValueError(f"Metric '{metric}' not found. Available: {available}") + + return result[metric] + + return evaluate + + +class ZernikeObjectiveBuilder: + """ + Builder for complex Zernike objectives with multiple subcases. + + This is useful for multi-subcase optimization where you want + to combine metrics from different gravity orientations. + + Example: + builder = ZernikeObjectiveBuilder( + op2_finder=lambda: sim_dir / "model.op2" + ) + + # Add objectives for different subcases + builder.add_subcase_objective("20", "filtered_rms_nm", weight=1.0) + builder.add_subcase_objective("40", "filtered_rms_nm", weight=0.5) + builder.add_subcase_objective("60", "filtered_rms_nm", weight=0.5) + + # Create combined objective + objective = builder.build_weighted_sum() + combined_rms = objective() # Returns weighted sum + """ + + def __init__( + self, + op2_finder: Callable[[], Path], + bdf_finder: Optional[Callable[[], Path]] = None, + displacement_unit: str = "mm", + **kwargs + ): + self.op2_finder = op2_finder + self.bdf_finder = bdf_finder + self.displacement_unit = displacement_unit + self.kwargs = kwargs + self.objectives: List[Dict[str, Any]] = [] + self._extractor = None + + def add_subcase_objective( + self, + subcase: Union[int, str], + metric: str = "filtered_rms_nm", + weight: float = 1.0, + name: Optional[str] = None + ) -> "ZernikeObjectiveBuilder": + """Add a subcase objective to the builder.""" + self.objectives.append({ + "subcase": str(subcase), + "metric": metric, + "weight": weight, + "name": name or f"{metric}_{subcase}" + }) + return self + + def add_relative_objective( + self, + target_subcase: Union[int, str], + reference_subcase: Union[int, str], + metric: str = "relative_filtered_rms_nm", + weight: float = 1.0, + name: Optional[str] = None + ) -> "ZernikeObjectiveBuilder": + """Add a relative objective between subcases.""" + self.objectives.append({ + "target_subcase": str(target_subcase), + "reference_subcase": str(reference_subcase), + "metric": metric, + "weight": weight, + "name": name or f"rel_{target_subcase}_vs_{reference_subcase}", + "is_relative": True + }) + return self + + def _get_extractor(self) -> ZernikeExtractor: + """Lazy-create extractor (reused for all objectives).""" + if self._extractor is None: + op2_path = self.op2_finder() + bdf_path = self.bdf_finder() if self.bdf_finder else None + self._extractor = ZernikeExtractor( + op2_path, bdf_path, + displacement_unit=self.displacement_unit, + **self.kwargs + ) + return self._extractor + + def _reset_extractor(self): + """Reset extractor (call after OP2 changes).""" + self._extractor = None + + def evaluate_all(self) -> Dict[str, float]: + """ + Evaluate all objectives and return dict of values. + + Returns: + Dict mapping objective name to value + """ + self._reset_extractor() + extractor = self._get_extractor() + results = {} + + for obj in self.objectives: + try: + if obj.get("is_relative"): + rel_result = extractor.extract_relative( + obj["target_subcase"], + obj["reference_subcase"] + ) + results[obj["name"]] = rel_result.get(obj["metric"], 0.0) + else: + sub_result = extractor.extract_subcase(obj["subcase"]) + results[obj["name"]] = sub_result.get(obj["metric"], 0.0) + except Exception as e: + logger.warning(f"Failed to evaluate {obj['name']}: {e}") + results[obj["name"]] = float("inf") + + return results + + def build_weighted_sum(self) -> Callable[[], float]: + """ + Build a callable that returns weighted sum of all objectives. + + Returns: + Callable returning combined objective value + """ + def evaluate() -> float: + values = self.evaluate_all() + total = 0.0 + for obj in self.objectives: + val = values.get(obj["name"], 0.0) + total += obj["weight"] * val + return total + + return evaluate + + def build_max(self) -> Callable[[], float]: + """ + Build a callable that returns maximum of all objectives. + + Useful for worst-case optimization across subcases. + """ + def evaluate() -> float: + values = self.evaluate_all() + weighted = [ + obj["weight"] * values.get(obj["name"], 0.0) + for obj in self.objectives + ] + return max(weighted) if weighted else 0.0 + + return evaluate + + def build_individual(self) -> Callable[[], Dict[str, float]]: + """ + Build a callable that returns dict of individual objective values. + + Useful for multi-objective optimization (NSGA-II). + """ + return self.evaluate_all + + +def extract_zernike_for_trial( + op2_path: Path, + bdf_path: Optional[Path] = None, + subcases: Optional[List[str]] = None, + reference_subcase: str = "20", + metrics: Optional[List[str]] = None, + **kwargs +) -> Dict[str, Any]: + """ + Extract comprehensive Zernike data for a trial. + + This is a high-level function for logging/exporting trial data. + It extracts all metrics for specified subcases and computes + relative metrics vs the reference. + + Args: + op2_path: Path to OP2 file + bdf_path: Path to BDF file (auto-detect if None) + subcases: List of subcases to extract (None = all available) + reference_subcase: Reference for relative calculations + metrics: Specific metrics to extract (None = all) + **kwargs: Additional ZernikeExtractor arguments + + Returns: + Dict with complete trial Zernike data: + { + 'subcases': { + '20': {'global_rms_nm': ..., 'filtered_rms_nm': ..., ...}, + '40': {...}, + ... + }, + 'relative': { + '40_vs_20': {'relative_filtered_rms_nm': ..., ...}, + ... + }, + 'summary': { + 'best_filtered_rms': ..., + 'worst_filtered_rms': ..., + ... + } + } + """ + extractor = ZernikeExtractor(op2_path, bdf_path, **kwargs) + + # Get available subcases + available = list(extractor.displacements.keys()) + if subcases: + subcases = [s for s in subcases if str(s) in available] + else: + subcases = available + + # Extract per-subcase data + subcase_data = {} + for sc in subcases: + try: + subcase_data[sc] = extractor.extract_subcase(str(sc)) + except Exception as e: + logger.warning(f"Failed to extract subcase {sc}: {e}") + + # Extract relative data + relative_data = {} + if reference_subcase in subcases: + for sc in subcases: + if sc != reference_subcase: + try: + key = f"{sc}_vs_{reference_subcase}" + relative_data[key] = extractor.extract_relative( + str(sc), str(reference_subcase) + ) + except Exception as e: + logger.warning(f"Failed to extract relative {key}: {e}") + + # Summary statistics + filtered_rms_values = [ + d.get('filtered_rms_nm', float('inf')) + for d in subcase_data.values() + ] + + summary = { + 'best_filtered_rms': min(filtered_rms_values) if filtered_rms_values else None, + 'worst_filtered_rms': max(filtered_rms_values) if filtered_rms_values else None, + 'mean_filtered_rms': sum(filtered_rms_values) / len(filtered_rms_values) if filtered_rms_values else None, + 'n_subcases': len(subcases), + 'reference_subcase': reference_subcase, + } + + return { + 'subcases': subcase_data, + 'relative': relative_data, + 'summary': summary, + } + + +# Export all helpers +__all__ = [ + 'create_zernike_objective', + 'create_relative_zernike_objective', + 'ZernikeObjectiveBuilder', + 'extract_zernike_for_trial', +] diff --git a/optimization_engine/nx_solver.py b/optimization_engine/nx_solver.py index b1cf1dd7..952ba0be 100644 --- a/optimization_engine/nx_solver.py +++ b/optimization_engine/nx_solver.py @@ -285,14 +285,11 @@ sys.argv = ['', {argv_str}] # Set argv for the main function # Set up environment for Simcenter/NX env = os.environ.copy() - # Set license server (use 29000 for Simcenter) - # Override any incorrect license server settings - env['SPLM_LICENSE_SERVER'] = '29000@AntoineThinkpad' - - # Force desktop licensing instead of enterprise - # User has nx_nas_bn_basic_dsk (desktop) not nx_nas_basic_ent (enterprise) - env['NXNA_LICENSE_FILE'] = '29000@AntoineThinkpad' - env['NXNASTRAN_LICENSE_FILE'] = '29000@AntoineThinkpad' + # Use existing SPLM_LICENSE_SERVER from environment if set + # Only set if not already defined (respects user's license configuration) + if 'SPLM_LICENSE_SERVER' not in env or not env['SPLM_LICENSE_SERVER']: + env['SPLM_LICENSE_SERVER'] = '29000@localhost' + print(f"[NX SOLVER] WARNING: SPLM_LICENSE_SERVER not set, using default: {env['SPLM_LICENSE_SERVER']}") # Add NX/Simcenter paths to environment nx_bin = self.nx_install_dir / "NXBIN" diff --git a/optimization_engine/solve_simulation.py b/optimization_engine/solve_simulation.py index a78e9955..a71451fc 100644 --- a/optimization_engine/solve_simulation.py +++ b/optimization_engine/solve_simulation.py @@ -1,13 +1,53 @@ """ NX Journal Script to Solve Simulation in Batch Mode -This script opens a .sim file, updates the FEM, and solves it through the NX API. -Usage: run_journal.exe solve_simulation.py +This script handles BOTH single-part simulations AND multi-part assembly FEMs. -Based on recorded NX journal pattern for solving simulations. +============================================================================= +MULTI-PART ASSEMBLY FEM WORKFLOW (for .afm-based simulations) +============================================================================= + +Based on recorded NX journal from interactive session (Nov 28, 2025). + +The correct workflow for assembly FEM updates: + +1. LOAD PARTS + - Open ASSY_M1.prt and M1_Blank_fem1_i.prt to have geometry loaded + - Find and switch to M1_Blank part for expression editing + +2. UPDATE EXPRESSIONS + - Switch to modeling application + - Edit expressions with units + - Call MakeUpToDate() on modified expressions + - Call DoUpdate() to rebuild geometry + +3. SWITCH TO SIM AND UPDATE FEM COMPONENTS + - Open the .sim file + - Navigate component hierarchy via RootComponent.FindObject() + - For each component FEM: + - SetWorkComponent() to make it the work part + - FindObject("FEModel").UpdateFemodel() + +4. MERGE DUPLICATE NODES (critical for assembly FEM!) + - Switch to assembly FEM component + - CreateDuplicateNodesCheckBuilder() + - Set MergeOccurrenceNodes = True + - IdentifyDuplicateNodes() then MergeDuplicateNodes() + +5. RESOLVE LABEL CONFLICTS + - CreateAssemblyLabelManagerBuilder() + - SetFEModelOccOffsets() for each occurrence + - Commit() + +6. SOLVE + - SetWorkComponent(Null) to return to sim level + - SolveChainOfSolutions() + +============================================================================= """ import sys +import os import NXOpen import NXOpen.Assemblies import NXOpen.CAE @@ -15,341 +55,510 @@ import NXOpen.CAE def main(args): """ - Open and solve a simulation file with updated expression values. + Main entry point for NX journal. Args: args: Command line arguments args[0]: .sim file path - args[1]: solution_name (optional, e.g., "Solution_Normal_Modes" or None for default) + args[1]: solution_name (optional, or "None" for default) args[2+]: expression updates as "name=value" pairs """ if len(args) < 1: print("ERROR: No .sim file path provided") - print("Usage: run_journal.exe solve_simulation.py [solution_name] [expr1=val1] [expr2=val2] ...") + print("Usage: run_journal.exe solve_simulation.py [solution_name] [expr1=val1] ...") return False sim_file_path = args[0] - - # Parse solution name if provided (args[1]) solution_name = args[1] if len(args) > 1 and args[1] != 'None' else None - # Extract base name from sim file (e.g., "Beam_sim1.sim" -> "Beam") - import os - sim_filename = os.path.basename(sim_file_path) - part_base_name = sim_filename.split('_sim')[0] if '_sim' in sim_filename else sim_filename.split('.sim')[0] - - # Parse expression updates from args[2+] as "name=value" pairs + # Parse expression updates expression_updates = {} for arg in args[2:]: if '=' in arg: name, value = arg.split('=', 1) expression_updates[name] = float(value) - print(f"[JOURNAL] Opening simulation: {sim_file_path}") - print(f"[JOURNAL] Detected part base name: {part_base_name}") - if solution_name: - print(f"[JOURNAL] Will solve specific solution: {solution_name}") - else: - print(f"[JOURNAL] Will solve default solution (Solution 1)") - if expression_updates: - print(f"[JOURNAL] Will update expressions:") - for name, value in expression_updates.items(): - print(f"[JOURNAL] {name} = {value}") + # Get working directory + working_dir = os.path.dirname(os.path.abspath(sim_file_path)) + sim_filename = os.path.basename(sim_file_path) + + print(f"[JOURNAL] " + "="*60) + print(f"[JOURNAL] NX SIMULATION SOLVER (Assembly FEM Workflow)") + print(f"[JOURNAL] " + "="*60) + print(f"[JOURNAL] Simulation: {sim_filename}") + print(f"[JOURNAL] Working directory: {working_dir}") + print(f"[JOURNAL] Solution: {solution_name or 'Solution 1'}") + print(f"[JOURNAL] Expression updates: {len(expression_updates)}") + for name, value in expression_updates.items(): + print(f"[JOURNAL] {name} = {value}") try: theSession = NXOpen.Session.GetSession() - # Set load options to load linked parts from directory - print("[JOURNAL] Setting load options for linked parts...") - import os - working_dir = os.path.dirname(os.path.abspath(sim_file_path)) - - # Complete load options setup (from recorded journal) + # Set load options theSession.Parts.LoadOptions.LoadLatest = False theSession.Parts.LoadOptions.ComponentLoadMethod = NXOpen.LoadOptions.LoadMethod.FromDirectory - - searchDirectories = [working_dir] - searchSubDirs = [True] - theSession.Parts.LoadOptions.SetSearchDirectories(searchDirectories, searchSubDirs) - + theSession.Parts.LoadOptions.SetSearchDirectories([working_dir], [True]) theSession.Parts.LoadOptions.ComponentsToLoad = NXOpen.LoadOptions.LoadComponents.All theSession.Parts.LoadOptions.PartLoadOption = NXOpen.LoadOptions.LoadOption.FullyLoad theSession.Parts.LoadOptions.SetInterpartData(True, NXOpen.LoadOptions.Parent.All) - theSession.Parts.LoadOptions.AllowSubstitution = False - theSession.Parts.LoadOptions.GenerateMissingPartFamilyMembers = True theSession.Parts.LoadOptions.AbortOnFailure = False - referenceSets = ["As Saved", "Use Simplified", "Use Model", "Entire Part", "Empty"] - theSession.Parts.LoadOptions.SetDefaultReferenceSets(referenceSets) - theSession.Parts.LoadOptions.ReferenceSetOverride = False - - print(f"[JOURNAL] Load directory set to: {working_dir}") - - # Close any currently open sim file to force reload from disk - print("[JOURNAL] Checking for open parts...") + # Close any open parts try: - current_work = theSession.Parts.BaseWork - if current_work and hasattr(current_work, 'FullPath'): - current_path = current_work.FullPath - print(f"[JOURNAL] Closing currently open part: {current_path}") - # Close without saving (we want to reload from disk) - partCloseResponses1 = [NXOpen.BasePart.CloseWholeTree] - theSession.Parts.CloseAll(partCloseResponses1) - print("[JOURNAL] Parts closed") - except Exception as e: - print(f"[JOURNAL] No parts to close or error closing: {e}") + theSession.Parts.CloseAll([NXOpen.BasePart.CloseWholeTree]) + except: + pass - # Open the .sim file (now will load fresh from disk with updated .prt files) - print(f"[JOURNAL] Opening simulation fresh from disk...") - basePart1, partLoadStatus1 = theSession.Parts.OpenActiveDisplay( - sim_file_path, - NXOpen.DisplayPartOption.AllowAdditional - ) + # Check for assembly FEM files + afm_files = [f for f in os.listdir(working_dir) if f.endswith('.afm')] + is_assembly = len(afm_files) > 0 - workSimPart = theSession.Parts.BaseWork - displaySimPart = theSession.Parts.BaseDisplay - - print(f"[JOURNAL] Simulation opened successfully") - partLoadStatus1.Dispose() - - # Switch to simulation application - theSession.ApplicationSwitchImmediate("UG_APP_SFEM") - - simPart1 = workSimPart - theSession.Post.UpdateUserGroupsFromSimPart(simPart1) - - # STEP 1: Try to switch to part and update expressions (optional for some models) - print(f"[JOURNAL] STEP 1: Checking for {part_base_name}.prt geometry...") - geometry_updated = False - try: - # Find the main part (may not exist for embedded geometry models) - bracketPart = None - try: - bracketPart = theSession.Parts.FindObject(part_base_name) - except: - pass - - if bracketPart: - print(f"[JOURNAL] Found {part_base_name} part, updating geometry...") - # Make Bracket the active display part - status, partLoadStatus = theSession.Parts.SetActiveDisplay( - bracketPart, - NXOpen.DisplayPartOption.AllowAdditional, - NXOpen.PartDisplayPartWorkPartOption.UseLast - ) - partLoadStatus.Dispose() - - workPart = theSession.Parts.Work - - # CRITICAL: Apply expression changes BEFORE updating geometry - expressions_updated = [] - - # Apply all expression updates dynamically - for expr_name, expr_value in expression_updates.items(): - print(f"[JOURNAL] Applying {expr_name} = {expr_value}") - try: - expr_obj = workPart.Expressions.FindObject(expr_name) - if expr_obj: - # Use millimeters as default unit for geometric parameters - unit_mm = workPart.UnitCollection.FindObject("MilliMeter") - workPart.Expressions.EditExpressionWithUnits(expr_obj, unit_mm, str(expr_value)) - expressions_updated.append(expr_obj) - print(f"[JOURNAL] {expr_name} updated successfully") - else: - print(f"[JOURNAL] WARNING: {expr_name} expression not found!") - except Exception as e: - print(f"[JOURNAL] ERROR updating {expr_name}: {e}") - - # Make expressions up to date - if expressions_updated: - print(f"[JOURNAL] Making {len(expressions_updated)} expression(s) up to date...") - for expr in expressions_updated: - markId_expr = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Make Up to Date") - objects1 = [expr] - theSession.UpdateManager.MakeUpToDate(objects1, markId_expr) - theSession.DeleteUndoMark(markId_expr, None) - - # CRITICAL: Update the geometry model - rebuilds features with new expressions - print(f"[JOURNAL] Rebuilding geometry with new expression values...") - markId_update = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update") - nErrs = theSession.UpdateManager.DoUpdate(markId_update) - theSession.DeleteUndoMark(markId_update, "NX update") - print(f"[JOURNAL] {part_base_name} geometry updated ({nErrs} errors)") - - # Extract mass from expression p173 if it exists and write to temp file - try: - mass_expr = workPart.Expressions.FindObject("p173") - if mass_expr: - mass_kg = mass_expr.Value - mass_output_file = os.path.join(working_dir, "_temp_mass.txt") - with open(mass_output_file, 'w') as f: - f.write(str(mass_kg)) - print(f"[JOURNAL] Mass from p173: {mass_kg:.6f} kg ({mass_kg * 1000:.2f} g)") - print(f"[JOURNAL] Mass written to: {mass_output_file}") - except: - pass # Expression p173 might not exist in all models - - geometry_updated = True - else: - print(f"[JOURNAL] {part_base_name} part not found - may be embedded in sim file") - except Exception as e: - print(f"[JOURNAL] Could not update {part_base_name}.prt: {e}") - print(f"[JOURNAL] Continuing with sim-only solve...") - - # STEP 2: Try to switch to FEM part and update (optional for some models) - fem_part_name = f"{part_base_name}_fem1" - print(f"[JOURNAL] STEP 2: Checking for {fem_part_name}.fem...") - fem_updated = False - try: - # Find the FEM part (may not exist or may have different name) - femPart1 = None - try: - femPart1 = theSession.Parts.FindObject(fem_part_name) - except: - # Try with _i suffix for idealized FEM - try: - femPart1 = theSession.Parts.FindObject(f"{fem_part_name}_i") - except: - pass - - if femPart1: - print(f"[JOURNAL] Found FEM part, updating...") - # Make FEM the active display part - status, partLoadStatus = theSession.Parts.SetActiveDisplay( - femPart1, - NXOpen.DisplayPartOption.AllowAdditional, - NXOpen.PartDisplayPartWorkPartOption.SameAsDisplay - ) - partLoadStatus.Dispose() - - workFemPart = theSession.Parts.BaseWork - - # CRITICAL: Update FE Model - regenerates FEM with new geometry - print("[JOURNAL] Updating FE Model...") - fEModel1 = workFemPart.FindObject("FEModel") - if fEModel1: - fEModel1.UpdateFemodel() - print("[JOURNAL] FE Model updated with new geometry!") - fem_updated = True - else: - print("[JOURNAL] WARNING: Could not find FEModel object") - else: - print(f"[JOURNAL] FEM part not found - may be embedded in sim file") - except Exception as e: - print(f"[JOURNAL] Could not update FEM: {e}") - print(f"[JOURNAL] Continuing with sim-only solve...") - - # STEP 3: Switch back to sim part - print("[JOURNAL] STEP 3: Switching back to sim part...") - try: - status, partLoadStatus = theSession.Parts.SetActiveDisplay( - simPart1, - NXOpen.DisplayPartOption.AllowAdditional, - NXOpen.PartDisplayPartWorkPartOption.UseLast - ) - partLoadStatus.Dispose() - workSimPart = theSession.Parts.BaseWork - print("[JOURNAL] Switched back to sim part") - except Exception as e: - print(f"[JOURNAL] WARNING: Error switching to sim part: {e}") - - # Note: Old output files are deleted by nx_solver.py before calling this journal - # This ensures NX performs a fresh solve - - # Solve the simulation - print("[JOURNAL] Starting solve...") - markId3 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") - theSession.SetUndoMarkName(markId3, "Solve Dialog") - - markId5 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve") - - theCAESimSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession) - - # Get the simulation object - simSimulation1 = workSimPart.FindObject("Simulation") - - # CRITICAL: Disable solution monitor when solving multiple solutions - # This prevents NX from opening multiple monitor windows which superpose and cause usability issues - if not solution_name: - print("[JOURNAL] Disabling solution monitor for all solutions to prevent window pile-up...") - try: - # Get all solutions in the simulation - solutions_disabled = 0 - solution_num = 1 - while True: - try: - solution_obj_name = f"Solution[Solution {solution_num}]" - simSolution = simSimulation1.FindObject(solution_obj_name) - if simSolution: - propertyTable = simSolution.SolverOptionsPropertyTable - propertyTable.SetBooleanPropertyValue("solution monitor", False) - solutions_disabled += 1 - solution_num += 1 - else: - break - except: - break # No more solutions - print(f"[JOURNAL] Solution monitor disabled for {solutions_disabled} solution(s)") - except Exception as e: - print(f"[JOURNAL] WARNING: Could not disable solution monitor: {e}") - print(f"[JOURNAL] Continuing with solve anyway...") - - # Get the solution(s) to solve - either specific or all - if solution_name: - # Solve specific solution in background mode - solution_obj_name = f"Solution[{solution_name}]" - print(f"[JOURNAL] Looking for solution: {solution_obj_name}") - simSolution1 = simSimulation1.FindObject(solution_obj_name) - psolutions1 = [simSolution1] - - numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveChainOfSolutions( - psolutions1, - NXOpen.CAE.SimSolution.SolveOption.Solve, - NXOpen.CAE.SimSolution.SetupCheckOption.CompleteDeepCheckAndOutputErrors, - NXOpen.CAE.SimSolution.SolveMode.Background + if is_assembly and expression_updates: + print(f"[JOURNAL] ") + print(f"[JOURNAL] DETECTED: Multi-part Assembly FEM") + print(f"[JOURNAL] Using ASSEMBLY FEM WORKFLOW") + print(f"[JOURNAL] ") + return solve_assembly_fem_workflow( + theSession, sim_file_path, solution_name, expression_updates, working_dir ) else: - # Solve ALL solutions using SolveAllSolutions API (Foreground mode) - # This ensures all solutions (static + modal, etc.) complete before returning - print(f"[JOURNAL] Solving all solutions using SolveAllSolutions API (Foreground mode)...") - - numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveAllSolutions( - NXOpen.CAE.SimSolution.SolveOption.Solve, - NXOpen.CAE.SimSolution.SetupCheckOption.CompleteCheckAndOutputErrors, - NXOpen.CAE.SimSolution.SolveMode.Foreground, - False + print(f"[JOURNAL] ") + print(f"[JOURNAL] Using SIMPLE WORKFLOW (no expression updates or single-part)") + print(f"[JOURNAL] ") + return solve_simple_workflow( + theSession, sim_file_path, solution_name, expression_updates, working_dir ) - theSession.DeleteUndoMark(markId5, None) - theSession.SetUndoMarkName(markId3, "Solve") - - print(f"[JOURNAL] Solve completed!") - print(f"[JOURNAL] Solutions solved: {numsolutionssolved1}") - print(f"[JOURNAL] Solutions failed: {numsolutionsfailed1}") - print(f"[JOURNAL] Solutions skipped: {numsolutionsskipped1}") - - # NOTE: When solution_name=None, we use Foreground mode to ensure all solutions - # complete before returning. When solution_name is specified, Background mode is used. - - # Save the simulation to write all output files - print("[JOURNAL] Saving simulation to ensure output files are written...") - simPart2 = workSimPart - partSaveStatus1 = simPart2.Save( - NXOpen.BasePart.SaveComponents.TrueValue, - NXOpen.BasePart.CloseAfterSave.FalseValue - ) - partSaveStatus1.Dispose() - print("[JOURNAL] Save complete!") - - return True - except Exception as e: - print(f"[JOURNAL] ERROR: {e}") + print(f"[JOURNAL] FATAL ERROR: {e}") import traceback traceback.print_exc() return False +def solve_assembly_fem_workflow(theSession, sim_file_path, solution_name, expression_updates, working_dir): + """ + Full assembly FEM workflow based on recorded NX journal. + + This is the correct workflow for multi-part assembly FEMs. + """ + sim_filename = os.path.basename(sim_file_path) + + # ========================================================================== + # STEP 1: LOAD REQUIRED PARTS + # ========================================================================== + print(f"[JOURNAL] STEP 1: Loading required parts...") + + # Load ASSY_M1.prt (to have the geometry assembly available) + assy_prt_path = os.path.join(working_dir, "ASSY_M1.prt") + if os.path.exists(assy_prt_path): + print(f"[JOURNAL] Loading ASSY_M1.prt...") + markId1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Load Part") + part1, partLoadStatus1 = theSession.Parts.Open(assy_prt_path) + partLoadStatus1.Dispose() + else: + print(f"[JOURNAL] WARNING: ASSY_M1.prt not found, continuing anyway...") + + # Load M1_Blank_fem1_i.prt (idealized geometry) + idealized_prt_path = os.path.join(working_dir, "M1_Blank_fem1_i.prt") + if os.path.exists(idealized_prt_path): + print(f"[JOURNAL] Loading M1_Blank_fem1_i.prt...") + markId2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Load Part") + part2, partLoadStatus2 = theSession.Parts.Open(idealized_prt_path) + partLoadStatus2.Dispose() + + # ========================================================================== + # STEP 2: UPDATE EXPRESSIONS IN M1_BLANK + # ========================================================================== + print(f"[JOURNAL] STEP 2: Updating expressions in M1_Blank...") + + # Find and switch to M1_Blank part + try: + part3 = theSession.Parts.FindObject("M1_Blank") + markId3 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part") + status1, partLoadStatus3 = theSession.Parts.SetActiveDisplay( + part3, + NXOpen.DisplayPartOption.AllowAdditional, + NXOpen.PartDisplayPartWorkPartOption.UseLast + ) + partLoadStatus3.Dispose() + + # Switch to modeling application for expression editing + theSession.ApplicationSwitchImmediate("UG_APP_MODELING") + + workPart = theSession.Parts.Work + + # Create undo mark for expressions + markId4 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + theSession.SetUndoMarkName(markId4, "Expressions Dialog") + + # Write expressions to a temp file and import (more reliable than editing one by one) + exp_file_path = os.path.join(working_dir, "_temp_expressions.exp") + with open(exp_file_path, 'w') as f: + for expr_name, expr_value in expression_updates.items(): + # Determine unit + if 'angle' in expr_name.lower() or 'vertical' in expr_name.lower(): + unit_str = "Degrees" + else: + unit_str = "MilliMeter" + f.write(f"[{unit_str}]{expr_name}={expr_value}\n") + print(f"[JOURNAL] {expr_name} = {expr_value} ({unit_str})") + + print(f"[JOURNAL] Importing expressions from file...") + markId_import = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Import Expressions") + + try: + expModified, errorMessages = workPart.Expressions.ImportFromFile( + exp_file_path, + NXOpen.ExpressionCollection.ImportMode.Replace + ) + print(f"[JOURNAL] Expressions imported: {expModified} modified") + if errorMessages: + print(f"[JOURNAL] Import errors: {errorMessages}") + + # Update geometry after import + print(f"[JOURNAL] Rebuilding geometry...") + markId_update = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update") + nErrs = theSession.UpdateManager.DoUpdate(markId_update) + theSession.DeleteUndoMark(markId_update, "NX update") + print(f"[JOURNAL] Geometry rebuilt ({nErrs} errors)") + + updated_expressions = list(expression_updates.keys()) + + except Exception as e: + print(f"[JOURNAL] ERROR importing expressions: {e}") + updated_expressions = [] + + # Clean up temp file + try: + os.remove(exp_file_path) + except: + pass + + theSession.SetUndoMarkName(markId4, "Expressions") + + except Exception as e: + print(f"[JOURNAL] ERROR updating expressions: {e}") + + # ========================================================================== + # STEP 3: OPEN SIM AND UPDATE COMPONENT FEMs + # ========================================================================== + print(f"[JOURNAL] STEP 3: Opening sim and updating component FEMs...") + + # Try to find the sim part first (like the recorded journal does) + # This ensures we're working with the same loaded sim part context + sim_part_name = os.path.splitext(sim_filename)[0] # e.g., "ASSY_M1_assyfem1_sim1" + print(f"[JOURNAL] Looking for sim part: {sim_part_name}") + + markId_sim = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part") + + try: + # First try to find it among loaded parts (like recorded journal) + simPart1 = theSession.Parts.FindObject(sim_part_name) + status_sim, partLoadStatus = theSession.Parts.SetActiveDisplay( + simPart1, + NXOpen.DisplayPartOption.AllowAdditional, + NXOpen.PartDisplayPartWorkPartOption.UseLast + ) + partLoadStatus.Dispose() + print(f"[JOURNAL] Found and activated existing sim part") + except: + # Fallback: Open fresh if not found + print(f"[JOURNAL] Sim part not found, opening fresh: {sim_filename}") + basePart, partLoadStatus = theSession.Parts.OpenActiveDisplay( + sim_file_path, + NXOpen.DisplayPartOption.AllowAdditional + ) + partLoadStatus.Dispose() + + workSimPart = theSession.Parts.BaseWork + displaySimPart = theSession.Parts.BaseDisplay + theSession.ApplicationSwitchImmediate("UG_APP_SFEM") + theSession.Post.UpdateUserGroupsFromSimPart(workSimPart) + + # Navigate component hierarchy + try: + rootComponent = workSimPart.ComponentAssembly.RootComponent + component1 = rootComponent.FindObject("COMPONENT ASSY_M1_assyfem1 1") + + # Update M1_Blank_fem1 + print(f"[JOURNAL] Updating M1_Blank_fem1...") + try: + component2 = component1.FindObject("COMPONENT M1_Blank_fem1 1") + markId_fem1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part") + partLoadStatus5 = theSession.Parts.SetWorkComponent( + component2, + NXOpen.PartCollection.RefsetOption.Entire, + NXOpen.PartCollection.WorkComponentOption.Visible + ) + workFemPart = theSession.Parts.BaseWork + partLoadStatus5.Dispose() + + markId_update1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Update FE Model") + fEModel1 = workFemPart.FindObject("FEModel") + fEModel1.UpdateFemodel() + print(f"[JOURNAL] M1_Blank_fem1 updated") + except Exception as e: + print(f"[JOURNAL] WARNING: M1_Blank_fem1: {e}") + + # Update M1_Vertical_Support_Skeleton_fem1 + print(f"[JOURNAL] Updating M1_Vertical_Support_Skeleton_fem1...") + try: + component3 = component1.FindObject("COMPONENT M1_Vertical_Support_Skeleton_fem1 3") + markId_fem2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part") + partLoadStatus6 = theSession.Parts.SetWorkComponent( + component3, + NXOpen.PartCollection.RefsetOption.Entire, + NXOpen.PartCollection.WorkComponentOption.Visible + ) + workFemPart = theSession.Parts.BaseWork + partLoadStatus6.Dispose() + + markId_update2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Update FE Model") + fEModel2 = workFemPart.FindObject("FEModel") + fEModel2.UpdateFemodel() + print(f"[JOURNAL] M1_Vertical_Support_Skeleton_fem1 updated") + except Exception as e: + print(f"[JOURNAL] WARNING: M1_Vertical_Support_Skeleton_fem1: {e}") + + except Exception as e: + print(f"[JOURNAL] ERROR navigating component hierarchy: {e}") + + # ========================================================================== + # STEP 4: MERGE DUPLICATE NODES + # ========================================================================== + print(f"[JOURNAL] STEP 4: Merging duplicate nodes...") + + try: + # Switch to assembly FEM + partLoadStatus8 = theSession.Parts.SetWorkComponent( + component1, + NXOpen.PartCollection.RefsetOption.Entire, + NXOpen.PartCollection.WorkComponentOption.Visible + ) + workAssyFemPart = theSession.Parts.BaseWork + displaySimPart = theSession.Parts.BaseDisplay + partLoadStatus8.Dispose() + print(f"[JOURNAL] Switched to assembly FEM: {workAssyFemPart.Name}") + + markId_merge = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + + caePart1 = workAssyFemPart + duplicateNodesCheckBuilder1 = caePart1.ModelCheckMgr.CreateDuplicateNodesCheckBuilder() + + # Set tolerance + unit_tol = duplicateNodesCheckBuilder1.Tolerance.Units + duplicateNodesCheckBuilder1.Tolerance.Units = unit_tol + duplicateNodesCheckBuilder1.Tolerance.SetFormula("0.01") + print(f"[JOURNAL] Tolerance: 0.01 mm") + + # Enable occurrence node merge - CRITICAL for assembly FEM + duplicateNodesCheckBuilder1.MergeOccurrenceNodes = True + print(f"[JOURNAL] MergeOccurrenceNodes: True") + + theSession.SetUndoMarkName(markId_merge, "Duplicate Nodes Dialog") + + # Configure display settings + displaysettings1 = NXOpen.CAE.ModelCheck.DuplicateNodesCheckBuilder.DisplaySettings() + displaysettings1.ShowDuplicateNodes = True + displaysettings1.ShowMergedNodeLabels = False + displaysettings1.ShowRetainedNodeLabels = False + displaysettings1.KeepNodesColor = displaySimPart.Colors.Find("Blue") + displaysettings1.MergeNodesColor = displaySimPart.Colors.Find("Yellow") + displaysettings1.UnableToMergeNodesColor = displaySimPart.Colors.Find("Red") + duplicateNodesCheckBuilder1.DisplaySettingsData = displaysettings1 + + # Check scope + duplicateNodesCheckBuilder1.CheckScopeOption = NXOpen.CAE.ModelCheck.CheckScope.Displayed + print(f"[JOURNAL] CheckScope: Displayed") + + # Identify duplicates + print(f"[JOURNAL] Identifying duplicate nodes...") + numDuplicates = duplicateNodesCheckBuilder1.IdentifyDuplicateNodes() + print(f"[JOURNAL] Found {numDuplicates} duplicate node sets") + + # Merge duplicates + if numDuplicates > 0: + print(f"[JOURNAL] Merging duplicate nodes...") + numMerged = duplicateNodesCheckBuilder1.MergeDuplicateNodes() + print(f"[JOURNAL] Merged {numMerged} duplicate node sets") + else: + print(f"[JOURNAL] WARNING: No duplicate nodes found to merge!") + print(f"[JOURNAL] This may indicate mesh update didn't work properly") + + theSession.SetUndoMarkName(markId_merge, "Duplicate Nodes") + duplicateNodesCheckBuilder1.Destroy() + theSession.DeleteUndoMark(markId_merge, None) + + except Exception as e: + print(f"[JOURNAL] WARNING: Node merge: {e}") + import traceback + traceback.print_exc() + + # ========================================================================== + # STEP 5: RESOLVE LABEL CONFLICTS + # ========================================================================== + print(f"[JOURNAL] STEP 5: Resolving label conflicts...") + + try: + markId_labels = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + + assyFemPart1 = workAssyFemPart + assemblyLabelManagerBuilder1 = assyFemPart1.CreateAssemblyLabelManagerBuilder() + + theSession.SetUndoMarkName(markId_labels, "Assembly Label Manager Dialog") + + markId_labels2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Assembly Label Manager") + + # Set offsets for each FE model occurrence + # These offsets ensure unique node/element labels across components + entitytypes = [ + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Node, + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Element, + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Csys, + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Physical, + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Group, + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ply, + NXOpen.CAE.AssemblyLabelManagerBuilder.EntityType.Ssmo, + ] + + # Apply offsets to each occurrence (values from recorded journal) + occurrence_offsets = [ + ("FEModelOccurrence[3]", 2), + ("FEModelOccurrence[4]", 74), + ("FEModelOccurrence[5]", 146), + ("FEModelOccurrence[7]", 218), + ] + + for occ_name, offset_val in occurrence_offsets: + try: + fEModelOcc = workAssyFemPart.FindObject(occ_name) + offsets = [offset_val] * 7 + assemblyLabelManagerBuilder1.SetFEModelOccOffsets(fEModelOcc, entitytypes, offsets) + except: + pass # Some occurrences may not exist + + nXObject1 = assemblyLabelManagerBuilder1.Commit() + + theSession.DeleteUndoMark(markId_labels2, None) + theSession.SetUndoMarkName(markId_labels, "Assembly Label Manager") + assemblyLabelManagerBuilder1.Destroy() + + print(f"[JOURNAL] Label conflicts resolved") + + except Exception as e: + print(f"[JOURNAL] WARNING: Label management: {e}") + + # ========================================================================== + # STEP 6: SOLVE + # ========================================================================== + print(f"[JOURNAL] STEP 6: Solving simulation...") + + try: + # Return to sim level by setting null component + partLoadStatus9 = theSession.Parts.SetWorkComponent( + NXOpen.Assemblies.Component.Null, + NXOpen.PartCollection.RefsetOption.Entire, + NXOpen.PartCollection.WorkComponentOption.Visible + ) + workSimPart = theSession.Parts.BaseWork + partLoadStatus9.Dispose() + + # Set up solve + markId_solve = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + theSession.SetUndoMarkName(markId_solve, "Solve Dialog") + + markId_solve2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve") + + theCAESimSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession) + + simSimulation1 = workSimPart.FindObject("Simulation") + sol_name = solution_name if solution_name else "Solution 1" + simSolution1 = simSimulation1.FindObject(f"Solution[{sol_name}]") + + psolutions1 = [simSolution1] + + print(f"[JOURNAL] Solving: {sol_name} (Foreground mode)") + numsolved, numfailed, numskipped = theCAESimSolveManager.SolveChainOfSolutions( + psolutions1, + NXOpen.CAE.SimSolution.SolveOption.Solve, + NXOpen.CAE.SimSolution.SetupCheckOption.CompleteCheckAndOutputErrors, + NXOpen.CAE.SimSolution.SolveMode.Foreground # Use Foreground to ensure OP2 is complete + ) + + theSession.DeleteUndoMark(markId_solve2, None) + theSession.SetUndoMarkName(markId_solve, "Solve") + + print(f"[JOURNAL] Solve completed: {numsolved} solved, {numfailed} failed, {numskipped} skipped") + + return numfailed == 0 + + except Exception as e: + print(f"[JOURNAL] ERROR solving: {e}") + import traceback + traceback.print_exc() + return False + + +def solve_simple_workflow(theSession, sim_file_path, solution_name, expression_updates, working_dir): + """ + Simple workflow for single-part simulations or when no expression updates needed. + """ + print(f"[JOURNAL] Opening simulation: {sim_file_path}") + + # Open the .sim file + basePart1, partLoadStatus1 = theSession.Parts.OpenActiveDisplay( + sim_file_path, + NXOpen.DisplayPartOption.AllowAdditional + ) + partLoadStatus1.Dispose() + + workSimPart = theSession.Parts.BaseWork + theSession.ApplicationSwitchImmediate("UG_APP_SFEM") + theSession.Post.UpdateUserGroupsFromSimPart(workSimPart) + + # Set up solve + markId_solve = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start") + theSession.SetUndoMarkName(markId_solve, "Solve Dialog") + + markId_solve2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve") + + theCAESimSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession) + + simSimulation1 = workSimPart.FindObject("Simulation") + sol_name = solution_name if solution_name else "Solution 1" + simSolution1 = simSimulation1.FindObject(f"Solution[{sol_name}]") + + psolutions1 = [simSolution1] + + print(f"[JOURNAL] Solving: {sol_name}") + numsolved, numfailed, numskipped = theCAESimSolveManager.SolveChainOfSolutions( + psolutions1, + NXOpen.CAE.SimSolution.SolveOption.Solve, + NXOpen.CAE.SimSolution.SetupCheckOption.CompleteCheckAndOutputErrors, + NXOpen.CAE.SimSolution.SolveMode.Background + ) + + theSession.DeleteUndoMark(markId_solve2, None) + theSession.SetUndoMarkName(markId_solve, "Solve") + + print(f"[JOURNAL] Solve completed: {numsolved} solved, {numfailed} failed, {numskipped} skipped") + + # Save + try: + partSaveStatus = workSimPart.Save( + NXOpen.BasePart.SaveComponents.TrueValue, + NXOpen.BasePart.CloseAfterSave.FalseValue + ) + partSaveStatus.Dispose() + print(f"[JOURNAL] Saved!") + except: + pass + + return numfailed == 0 + + if __name__ == '__main__': success = main(sys.argv[1:]) sys.exit(0 if success else 1) diff --git a/studies/m1_mirror_zernike_optimization/1_setup/optimization_config.json b/studies/m1_mirror_zernike_optimization/1_setup/optimization_config.json new file mode 100644 index 00000000..be1108a1 --- /dev/null +++ b/studies/m1_mirror_zernike_optimization/1_setup/optimization_config.json @@ -0,0 +1,236 @@ +{ + "$schema": "Atomizer M1 Mirror Zernike Optimization", + "study_name": "m1_mirror_zernike_optimization", + "description": "Telescope primary mirror support structure optimization using Zernike wavefront error metrics with neural acceleration", + + "design_variables": [ + { + "name": "lateral_inner_angle", + "expression_name": "lateral_inner_angle", + "min": 25.0, + "max": 28.5, + "baseline": 26.79, + "units": "degrees", + "description": "Lateral support inner angle", + "enabled": false + }, + { + "name": "lateral_outer_angle", + "expression_name": "lateral_outer_angle", + "min": 13.0, + "max": 17.0, + "baseline": 14.64, + "units": "degrees", + "description": "Lateral support outer angle", + "enabled": false + }, + { + "name": "lateral_outer_pivot", + "expression_name": "lateral_outer_pivot", + "min": 9.0, + "max": 12.0, + "baseline": 10.40, + "units": "mm", + "description": "Lateral outer pivot position", + "enabled": false + }, + { + "name": "lateral_inner_pivot", + "expression_name": "lateral_inner_pivot", + "min": 9.0, + "max": 12.0, + "baseline": 10.07, + "units": "mm", + "description": "Lateral inner pivot position", + "enabled": false + }, + { + "name": "lateral_middle_pivot", + "expression_name": "lateral_middle_pivot", + "min": 18.0, + "max": 23.0, + "baseline": 20.73, + "units": "mm", + "description": "Lateral middle pivot position", + "enabled": false + }, + { + "name": "lateral_closeness", + "expression_name": "lateral_closeness", + "min": 9.5, + "max": 12.5, + "baseline": 11.02, + "units": "mm", + "description": "Lateral support closeness parameter", + "enabled": false + }, + { + "name": "whiffle_min", + "expression_name": "whiffle_min", + "min": 35.0, + "max": 55.0, + "baseline": 40.55, + "units": "mm", + "description": "Whiffle tree minimum parameter", + "enabled": true + }, + { + "name": "whiffle_outer_to_vertical", + "expression_name": "whiffle_outer_to_vertical", + "min": 68.0, + "max": 80.0, + "baseline": 75.67, + "units": "degrees", + "description": "Whiffle tree outer to vertical angle", + "enabled": true + }, + { + "name": "whiffle_triangle_closeness", + "expression_name": "whiffle_triangle_closeness", + "min": 50.0, + "max": 65.0, + "baseline": 60.00, + "units": "mm", + "description": "Whiffle tree triangle closeness", + "enabled": false + }, + { + "name": "blank_backface_angle", + "expression_name": "blank_backface_angle", + "min": 3.5, + "max": 5.0, + "baseline": 4.23, + "units": "degrees", + "description": "Mirror blank backface angle", + "enabled": false + }, + { + "name": "inner_circular_rib_dia", + "expression_name": "inner_circular_rib_dia", + "min": 480.0, + "max": 620.0, + "baseline": 534.00, + "units": "mm", + "description": "Inner circular rib diameter", + "enabled": true + } + ], + + "objectives": [ + { + "name": "rel_filtered_rms_40_vs_20", + "description": "Filtered RMS WFE at 40 deg relative to 20 deg reference", + "direction": "minimize", + "weight": 5.0, + "target": 4.0, + "units": "nm", + "extractor": "zernike_relative", + "extractor_config": { + "target_subcase": "40", + "reference_subcase": "20", + "metric": "relative_filtered_rms_nm" + } + }, + { + "name": "rel_filtered_rms_60_vs_20", + "description": "Filtered RMS WFE at 60 deg relative to 20 deg reference", + "direction": "minimize", + "weight": 5.0, + "target": 10.0, + "units": "nm", + "extractor": "zernike_relative", + "extractor_config": { + "target_subcase": "60", + "reference_subcase": "20", + "metric": "relative_filtered_rms_nm" + } + }, + { + "name": "mfg_90_optician_workload", + "description": "Optician workload at 90 deg polishing orientation (filtered RMS with J1-J3)", + "direction": "minimize", + "weight": 1.0, + "target": 20.0, + "units": "nm", + "extractor": "zernike", + "extractor_config": { + "subcase": "90", + "metric": "rms_filter_j1to3", + "reference_subcase": "20" + } + } + ], + + "constraints": [ + { + "name": "max_stress", + "description": "Maximum von Mises stress in mirror assembly", + "type": "upper_bound", + "threshold": 10.0, + "units": "MPa", + "enabled": false + } + ], + + "zernike_settings": { + "n_modes": 50, + "filter_low_orders": 4, + "displacement_unit": "mm", + "subcases": ["1", "2", "3", "4"], + "subcase_labels": {"1": "90deg", "2": "20deg", "3": "40deg", "4": "60deg"}, + "reference_subcase": "2", + "polishing_subcase": "1", + "output_full_coefficients": true, + "_note": "Subcase mapping matches NX: 1=90deg, 2=20deg(ref), 3=40deg, 4=60deg" + }, + + "optimization_settings": { + "n_trials": 100, + "n_fea_trials": 40, + "n_neural_trials": 500, + "sampler": "TPE", + "seed": 42, + "n_startup_trials": 15, + "tpe_n_ei_candidates": 150, + "tpe_multivariate": true, + "objective_strategy": "weighted_sum", + "objective_direction": "minimize" + }, + + "surrogate_settings": { + "enabled": true, + "model_type": "ParametricZernikePredictor", + "training_config": { + "hidden_channels": 128, + "num_layers": 4, + "learning_rate": 0.001, + "epochs": 200, + "batch_size": 8, + "train_split": 0.8 + }, + "outputs": { + "description": "50 Zernike coefficients x 4 subcases = 200 outputs", + "coefficients_per_subcase": 50, + "subcases": ["20", "40", "60", "90"] + } + }, + + "nx_settings": { + "nx_install_path": "C:\\Program Files\\Siemens\\NX2506", + "model_dir": "C:\\Users\\Antoine\\CADTOMASTE\\Atomizer\\M1-Gigabit\\Latest", + "sim_file": "ASSY_M1_assyfem1_sim1.sim", + "solution_name": "Solution 1", + "solve_all_subcases": true, + "op2_pattern": "*-solution_1.op2", + "op2_timeout_s": 1800, + "op2_stable_s": 5, + "post_solve_delay_s": 5 + }, + + "output_settings": { + "save_zernike_coefficients": true, + "save_field_data": true, + "generate_reports": true, + "archive_results": true + } +} diff --git a/studies/m1_mirror_zernike_optimization/2_results/study.db b/studies/m1_mirror_zernike_optimization/2_results/study.db new file mode 100644 index 0000000000000000000000000000000000000000..b333a5fe0d82127e25cd5a4ae3245307064f9c29 GIT binary patch literal 258048 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 d&))v!Uw-uMzy0$c|NDwi|7R9-v%F8O=ikc