Files
Atomizer/docs/generated/EXTRACTORS.md
Antoine 0e04457539 feat: Implement Agentic Architecture for robust session workflows
Phase 1 - Session Bootstrap:
- Add .claude/ATOMIZER_CONTEXT.md as single entry point for new sessions
- Add study state detection and task routing

Phase 2 - Code Deduplication:
- Add optimization_engine/base_runner.py (ConfigDrivenRunner)
- Add optimization_engine/generic_surrogate.py (ConfigDrivenSurrogate)
- Add optimization_engine/study_state.py for study detection
- Add optimization_engine/templates/ with registry and templates
- Studies now require ~50 lines instead of ~300

Phase 3 - Skill Consolidation:
- Add YAML frontmatter metadata to all skills (versioning, dependencies)
- Consolidate create-study.md into core/study-creation-core.md
- Update 00_BOOTSTRAP.md, 01_CHEATSHEET.md, 02_CONTEXT_LOADER.md

Phase 4 - Self-Expanding Knowledge:
- Add optimization_engine/auto_doc.py for auto-generating documentation
- Generate docs/generated/EXTRACTORS.md (27 extractors documented)
- Generate docs/generated/TEMPLATES.md (6 templates)
- Generate docs/generated/EXTRACTOR_CHEATSHEET.md

Phase 5 - Subagent Implementation:
- Add .claude/commands/study-builder.md (create studies)
- Add .claude/commands/nx-expert.md (NX Open API)
- Add .claude/commands/protocol-auditor.md (config validation)
- Add .claude/commands/results-analyzer.md (results analysis)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 14:52:25 -05:00

25 KiB

Atomizer Extractor Library

Auto-generated: 2025-12-07 12:53

This document is automatically generated from the extractor source code.


Quick Reference

Extractor Category Phase Description
check_force_equilibrium forces Phase 2 Check if reaction forces balance applied loads (equilibrium
extract_reaction_component forces Phase 2 Extract maximum absolute value of a specific reaction compon
extract_spc_forces forces Phase 2 Extract SPC (reaction) forces from boundary conditions.
extract_total_reaction_force forces Phase 2 Convenience function to extract total reaction force magnitu
extract_frequencies general Phase 3 Extract natural frequencies from modal analysis F06 file.
extract_part_material general Phase 1 Convenience function to extract just material info.
PartMassExtractor mass Phase 1 Class-based extractor for part mass and material with cachin
extract_part_mass mass Phase 1 Convenience function to extract just the mass in kg.
extract_part_mass_material mass Phase 1 Extract mass and material properties from NX part file.
extract_modal_mass modal Phase 3 Extract modal effective mass from F06 file.
get_first_frequency modal Phase 3 Get first natural frequency from F06 file.
get_modal_mass_ratio modal Phase 3 Get cumulative modal mass ratio for first n modes.
ZernikeExtractor optical Phase 1 Complete Zernike analysis extractor for telescope mirror opt
extract_zernike_filtered_rms optical Phase 1 Extract filtered RMS WFE - the primary metric for mirror opt
extract_zernike_from_op2 optical Phase 1 Convenience function to extract Zernike metrics from OP2.
extract_zernike_relative_rms optical Phase 1 Extract relative filtered RMS between two subcases.
extract_strain_energy strain Phase 2 Extract strain energy from structural elements.
extract_strain_energy_density strain Phase 2 Extract strain energy density (energy per volume).
extract_total_strain_energy strain Phase 2 Convenience function to extract total strain energy.
extract_max_principal_stress stress Phase 2 Convenience function to extract maximum principal stress.
extract_min_principal_stress stress Phase 2 Convenience function to extract minimum principal stress.
extract_principal_stress stress Phase 2 Extract principal stresses from solid or shell elements.
extract_solid_stress stress Phase 1 Extract stress from solid elements.
extract_heat_flux thermal Phase 3 Extract element heat flux from thermal analysis OP2 file.
extract_temperature thermal Phase 3 Extract nodal temperatures from thermal analysis OP2 file.
extract_temperature_gradient thermal Phase 3 Extract temperature gradients from thermal analysis.
get_max_temperature thermal Phase 3 Get maximum temperature from OP2 file.

Forces Extractors

check_force_equilibrium

Module: optimization_engine.extractors.extract_spc_forces Phase: Phase 2

Parameters:

  • op2_file
  • applied_load = None
  • tolerance = 1.0

Description:

Check if reaction forces balance applied loads (equilibrium check).

In a valid static analysis, sum of reactions should equal applied loads.

Args:
    op2_file: Path to .op2 file
    applied_load: Optional dict of applied loads {'fx': N, 'fy': N, 'fz': N}
    tolerance: Tolerance for equilibrium check (default 1.0 N)

Returns:
    dict: {
        'in_equilibrium': Boolean,
        'reaction_sum': [fx, fy, fz],
        'imbalance': [dx, dy, dz] (if applied_load provided),
        'max_imbalance': Maximum component imbalance
    }

extract_reaction_component

Module: optimization_engine.extractors.extract_spc_forces Phase: Phase 2

Parameters:

  • op2_file
  • component = fz
  • subcase = 1

Description:

Extract maximum absolute value of a specific reaction component.

Args:
    op2_file: Path to .op2 file
    component: 'fx', 'fy', 'fz', 'mx', 'my', 'mz'
    subcase: Subcase ID

Returns:
    Maximum absolute value of the specified component

extract_spc_forces

Module: optimization_engine.extractors.extract_spc_forces Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1
  • component = total

Description:

Extract SPC (reaction) forces from boundary conditions.

SPC forces are the reaction forces at constrained nodes. They balance
the applied loads and indicate load path through the structure.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID (default 1)
    component: Which component(s) to return:
        - 'total': Resultant force magnitude (sqrt(fx^2+fy^2+fz^2))
        - 'fx', 'fy', 'fz': Individual force components
        - 'mx', 'my', 'mz': Individual moment components
        - 'force': Vector sum of forces only
        - 'moment': Vector sum of moments only

Returns:
    dict: {
        'total_reaction': Total reaction force magnitude,
        'max_reaction': Maximum nodal reaction,
        'max_reaction_node': Node ID with max reaction,
        'sum_fx': Sum of Fx at all nodes,
        'sum_fy': Sum of Fy at all nodes,
        'sum_fz': Sum of Fz at all nodes,
        'sum_mx': Sum of Mx at all nodes,
        'sum_my': Sum of My at all nodes,
        'sum_mz': Sum of Mz at all nodes,
        'node_reactions': Dict of {node_id: [fx,fy,fz,mx,my,mz]},
        'num_constrained_nodes': Number of nodes with SPCs,
        'subcase': Subcase ID,
        'units': 'N, N-mm (model units)'
    }

Example:
    >>> result = extract_spc_forces('model.op2')
    >>> print(f"Total reaction: {result['total_reaction']:.2f} N")
    >>> print(f"Sum Fz: {result['sum_fz']:.2f} N")

extract_total_reaction_force

Module: optimization_engine.extractors.extract_spc_forces Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1

Description:

Convenience function to extract total reaction force magnitude.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID

Returns:
    Total reaction force magnitude (N)

General Extractors

extract_frequencies

Module: optimization_engine.extractors.extract_modal_mass Phase: Phase 3

Parameters:

  • f06_file
  • n_modes = None

Description:

Extract natural frequencies from modal analysis F06 file.

Simpler version of extract_modal_mass that just gets frequencies.

Args:
    f06_file: Path to F06 file
    n_modes: Number of modes to extract (default: all)

Returns:
    dict: {
        'success': bool,
        'frequencies': list of frequencies in Hz,
        'mode_count': int,
        'first_frequency': float,
        'error': str or None
    }

extract_part_material

Module: optimization_engine.extractors.extract_part_mass_material Phase: Phase 1

Parameters:

  • prt_file
  • properties_file = None

Description:

Convenience function to extract just material info.

Args:
    prt_file: Path to .prt file
    properties_file: Optional explicit path to temp file

Returns:
    Dictionary with 'name', 'density', 'density_unit'

Example:
    >>> mat = extract_part_material('model.prt')
    >>> print(f"Material: {mat['name']}, Density: {mat['density']}")
    Material: Steel_304, Density: 7.93e-06

Mass Extractors

PartMassExtractor

Module: optimization_engine.extractors.extract_part_mass_material Phase: Phase 1

Parameters:

  • prt_file
  • properties_file = None

Description:

Class-based extractor for part mass and material with caching.

Use this when you need to extract properties from multiple parts
or want to cache results.

Example:
    >>> extractor = PartMassExtractor('model.prt')
    >>> result = extractor.extract()
    >>> print(result['mass_kg'])
    1.234

extract_part_mass

Module: optimization_engine.extractors.extract_part_mass_material Phase: Phase 1

Parameters:

  • prt_file
  • properties_file = None

Description:

Convenience function to extract just the mass in kg.

Args:
    prt_file: Path to .prt file
    properties_file: Optional explicit path to temp file

Returns:
    Mass in kilograms (float)

Example:
    >>> mass = extract_part_mass('model.prt')
    >>> print(f"Mass: {mass:.3f} kg")
    Mass: 1.234 kg

extract_part_mass_material

Module: optimization_engine.extractors.extract_part_mass_material Phase: Phase 1

Parameters:

  • prt_file
  • properties_file = None

Description:

Extract mass and material properties from NX part file.

This function reads from a temp JSON file that must be created by
running the NX journal: nx_journals/extract_part_mass_material.py

Args:
    prt_file: Path to .prt file (used to locate temp file)
    properties_file: Optional explicit path to _temp_part_properties.json
                    If not provided, looks in same directory as prt_file

Returns:
    Dictionary containing:
    - 'mass_kg': Mass in kilograms (float)
    - 'mass_g': Mass in grams (float)
    - 'volume_mm3': Volume in mm^3 (float)
    - 'surface_area_mm2': Surface area in mm^2 (float)
    - 'center_of_gravity_mm': [x, y, z] in mm (list)
    - 'moments_of_inertia': {'Ixx', 'Iyy', 'Izz', 'unit'} or None
    - 'material': {'name', 'density', 'density_unit'} (dict)
    - 'num_bodies': Number of solid bodies (int)

Raises:
    FileNotFoundError: If prt file or temp properties file not found
    ValueError: If temp file has invalid format or extraction failed

Example:
    >>> result = extract_part_mass_material('model.prt')
    >>> print(f"Mass: {result['mass_kg']:.3f} kg")
    Mass: 1.234 kg
    >>> print(f"Material: {result['material']['name']}")
    Material: Aluminum_6061

Note:
    Before calling this function, you must run the NX journal to
    create the temp file:
    ```
    run_journal.exe extract_part_mass_material.py model.prt
    ```

Modal Extractors

extract_modal_mass

Module: optimization_engine.extractors.extract_modal_mass Phase: Phase 3

Parameters:

  • f06_file
  • mode = None
  • direction = all

Description:

Extract modal effective mass from F06 file.

Modal effective mass indicates how much of the total mass participates
in each mode for each direction. Important for:
- Base excitation problems
- Seismic analysis
- Random vibration

Args:
    f06_file: Path to the F06 results file
    mode: Mode number to extract (1-indexed). If None, returns all modes.
    direction: Direction(s) to extract:
               'x', 'y', 'z' - single direction
               'all' - all directions (default)
               'total' - sum of all directions

Returns:
    dict: {
        'success': bool,
        'modes': list of mode data (if mode=None),
        'modal_mass_x': float (kg) - effective mass in X,
        'modal_mass_y': float (kg) - effective mass in Y,
        'modal_mass_z': float (kg) - effective mass in Z,
        'modal_mass_rx': float (kg·m²) - rotational mass about X,
        'modal_mass_ry': float (kg·m²) - rotational mass about Y,
        'modal_mass_rz': float (kg·m²) - rotational mass about Z,
        'participation_x': float (0-1) - participation factor X,
        'participation_y': float (0-1) - participation factor Y,
        'participation_z': float (0-1) - participation factor Z,
        'frequency': float (Hz) - natural frequency,
        'cumulative_mass_x': float - cumulative mass fraction X,
        'cumulative_mass_y': float - cumulative mass fraction Y,
        'cumulative_mass_z': float - cumulative mass fraction Z,
        'total_mass': float (kg) - total model mass,
        'error': str or None
    }

Example:
    >>> result = extract_modal_mass("modal_analysis.f06", mode=1)
    >>> if result['success']:
    ...     print(f"Mode 1 frequency: {result['frequency']:.2f} Hz")
    ...     print(f"X participation: {result['participation_x']*100:.1f}%")

get_first_frequency

Module: optimization_engine.extractors.extract_modal_mass Phase: Phase 3

Parameters:

  • f06_file

Description:

Get first natural frequency from F06 file.

Convenience function for optimization constraints.
Returns 0 if extraction fails.

Args:
    f06_file: Path to F06 file

Returns:
    float: First natural frequency in Hz, or 0 on failure

get_modal_mass_ratio

Module: optimization_engine.extractors.extract_modal_mass Phase: Phase 3

Parameters:

  • f06_file
  • direction = z
  • n_modes = 10

Description:

Get cumulative modal mass ratio for first n modes.

This indicates what fraction of total mass participates in the
first n modes. Important for determining if enough modes are included.

Args:
    f06_file: Path to F06 file
    direction: Direction ('x', 'y', or 'z')
    n_modes: Number of modes to include

Returns:
    float: Cumulative mass ratio (0-1), or 0 on failure

Optical Extractors

ZernikeExtractor

Module: optimization_engine.extractors.extract_zernike Phase: Phase 1

Parameters:

  • op2_path
  • bdf_path = None
  • displacement_unit = mm
  • n_modes = 50
  • filter_orders = 4

Description:

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

extract_zernike_filtered_rms

Module: optimization_engine.extractors.extract_zernike Phase: Phase 1

Parameters:

  • op2_file
  • bdf_file = None
  • subcase = 1
  • kwargs

Description:

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

extract_zernike_from_op2

Module: optimization_engine.extractors.extract_zernike Phase: Phase 1

Parameters:

  • op2_file
  • bdf_file = None
  • subcase = 1
  • displacement_unit = mm
  • n_modes = 50
  • filter_orders = 4

Description:

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

extract_zernike_relative_rms

Module: optimization_engine.extractors.extract_zernike Phase: Phase 1

Parameters:

  • op2_file
  • target_subcase
  • reference_subcase
  • bdf_file = None
  • kwargs

Description:

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

Strain Extractors

extract_strain_energy

Module: optimization_engine.extractors.extract_strain_energy Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1
  • element_type = None
  • top_n = 10

Description:

Extract strain energy from structural elements.

Strain energy (U) is a measure of the work done to deform the structure:
U = 0.5 * integral(sigma * epsilon) dV

High strain energy density indicates highly stressed regions.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID (default 1)
    element_type: Filter by element type (e.g., 'ctetra', 'chexa', 'cquad4')
                 If None, returns total from all elements
    top_n: Number of top elements to return by strain energy

Returns:
    dict: {
        'total_strain_energy': Total strain energy (all elements),
        'mean_strain_energy': Mean strain energy per element,
        'max_strain_energy': Maximum element strain energy,
        'max_energy_element': Element ID with max strain energy,
        'top_elements': List of (element_id, energy) tuples,
        'energy_by_type': Dict of {element_type: total_energy},
        'num_elements': Total element count,
        'subcase': Subcase ID,
        'units': 'N-mm (model units)'
    }

Example:
    >>> result = extract_strain_energy('model.op2')
    >>> print(f"Total strain energy: {result['total_strain_energy']:.2f} N-mm")
    >>> print(f"Highest energy element: {result['max_energy_element']}")

extract_strain_energy_density

Module: optimization_engine.extractors.extract_strain_energy Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1
  • element_type = ctetra

Description:

Extract strain energy density (energy per volume).

Strain energy density is useful for identifying critical regions
and for material utilization optimization.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID
    element_type: Element type to analyze

Returns:
    dict: {
        'max_density': Maximum strain energy density,
        'mean_density': Mean strain energy density,
        'total_energy': Total strain energy,
        'units': 'N/mm^2 (MPa equivalent)'
    }

Note:
    This requires element volume data which may not always be available.
    Falls back to energy-only metrics if volume is unavailable.

extract_total_strain_energy

Module: optimization_engine.extractors.extract_strain_energy Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1

Description:

Convenience function to extract total strain energy.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID

Returns:
    Total strain energy (N-mm)

Stress Extractors

extract_max_principal_stress

Module: optimization_engine.extractors.extract_principal_stress Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1
  • element_type = ctetra

Description:

Convenience function to extract maximum principal stress.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID
    element_type: Element type

Returns:
    Maximum principal stress value (tension positive)

extract_min_principal_stress

Module: optimization_engine.extractors.extract_principal_stress Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1
  • element_type = ctetra

Description:

Convenience function to extract minimum principal stress.

For solid elements, returns sigma3 (most compressive).
For shell elements, returns sigma2.

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID
    element_type: Element type

Returns:
    Minimum principal stress value (compression negative)

extract_principal_stress

Module: optimization_engine.extractors.extract_principal_stress Phase: Phase 2

Parameters:

  • op2_file
  • subcase = 1
  • element_type = ctetra
  • principal = max

Description:

Extract principal stresses from solid or shell elements.

Principal stresses are the eigenvalues of the stress tensor,
ordered as: sigma1 >= sigma2 >= sigma3 (o1 >= o2 >= o3)

Args:
    op2_file: Path to .op2 file
    subcase: Subcase ID (default 1)
    element_type: Element type ('ctetra', 'chexa', 'cquad4', 'ctria3')
    principal: Which principal stress to return:
        - 'max': Maximum principal (sigma1, tension positive)
        - 'mid': Middle principal (sigma2)
        - 'min': Minimum principal (sigma3, compression negative)
        - 'all': Return all three principals

Returns:
    dict: {
        'max_principal': Maximum principal stress value,
        'min_principal': Minimum principal stress value,
        'mid_principal': Middle principal stress (if applicable),
        'max_element': Element ID with maximum principal,
        'min_element': Element ID with minimum principal,
        'von_mises_max': Max von Mises for comparison,
        'element_type': Element type used,
        'subcase': Subcase ID,
        'units': 'MPa (model units)'
    }

Example:
    >>> result = extract_principal_stress('model.op2', element_type='ctetra')
    >>> print(f"Max tension: {result['max_principal']:.2f} MPa")
    >>> print(f"Max compression: {result['min_principal']:.2f} MPa")

extract_solid_stress

Module: optimization_engine.extractors.extract_von_mises_stress Phase: Phase 1

Parameters:

  • op2_file
  • subcase = 1
  • element_type = ctetra

Description:

Extract stress from solid elements.

Thermal Extractors

extract_heat_flux

Module: optimization_engine.extractors.extract_temperature Phase: Phase 3

Parameters:

  • op2_file
  • subcase = 1
  • element_type = all

Description:

Extract element heat flux from thermal analysis OP2 file.

Args:
    op2_file: Path to the OP2 results file
    subcase: Subcase number
    element_type: Element type to extract ('all', 'ctetra', 'chexa', etc.)

Returns:
    dict: {
        'success': bool,
        'max_heat_flux': float (W/mm² or model units),
        'min_heat_flux': float,
        'avg_heat_flux': float,
        'max_element_id': int,
        'element_count': int,
        'unit': str,
        'error': str or None
    }

extract_temperature

Module: optimization_engine.extractors.extract_temperature Phase: Phase 3

Parameters:

  • op2_file
  • subcase = 1
  • nodes = None
  • return_field = False

Description:

Extract nodal temperatures from thermal analysis OP2 file.

Args:
    op2_file: Path to the OP2 results file
    subcase: Subcase number to extract (default: 1)
    nodes: Optional list of specific node IDs to extract.
           If None, extracts all nodes.
    return_field: If True, include full temperature field in result

Returns:
    dict: {
        'success': bool,
        'max_temperature': float (K or °C depending on model units),
        'min_temperature': float,
        'avg_temperature': float,
        'max_node_id': int (node with max temperature),
        'min_node_id': int (node with min temperature),
        'node_count': int,
        'temperatures': dict (node_id: temp) - only if return_field=True,
        'unit': str ('K' or 'C'),
        'subcase': int,
        'error': str or None
    }

Example:
    >>> result = extract_temperature("thermal_analysis.op2", subcase=1)
    >>> if result['success']:
    ...     print(f"Max temp: {result['max_temperature']:.1f} K at node {result['max_node_id']}")
    ...     print(f"Temperature range: {result['min_temperature']:.1f} - {result['max_temperature']:.1f} K")

extract_temperature_gradient

Module: optimization_engine.extractors.extract_temperature Phase: Phase 3

Parameters:

  • op2_file
  • subcase = 1
  • method = nodal_difference

Description:

Extract temperature gradients from thermal analysis.

Computes temperature gradients based on nodal temperature differences.
This is useful for identifying thermal stress hot spots.

Args:
    op2_file: Path to the OP2 results file
    subcase: Subcase number
    method: Gradient calculation method:
            - 'nodal_difference': Max temperature difference between adjacent nodes
            - 'element_based': Gradient within elements (requires mesh connectivity)

Returns:
    dict: {
        'success': bool,
        'max_gradient': float (K/mm or temperature units/length),
        'avg_gradient': float,
        'temperature_range': float (max - min temperature),
        'gradient_location': tuple (node_id_hot, node_id_cold),
        'error': str or None
    }

Note:
    For accurate gradients, element-based calculation requires mesh connectivity
    which may not be available in all OP2 files. The nodal_difference method
    provides an approximation based on temperature range.

get_max_temperature

Module: optimization_engine.extractors.extract_temperature Phase: Phase 3

Parameters:

  • op2_file
  • subcase = 1

Description:

Get maximum temperature from OP2 file.

Convenience function for use in optimization constraints.
Returns inf if extraction fails.

Args:
    op2_file: Path to OP2 file
    subcase: Subcase number

Returns:
    float: Maximum temperature or inf on failure