Skip to content

Repository files navigation

Fortran Analyzer

A powerful Python package for analyzing Fortran codebases. Perfect for understanding legacy code, preparing for modernization, analyzing dependencies, and planning translations to other languages.

Key Use Case: Analyze multiple Fortran projects with a single, flexible tool.

Why Fortran Analyzer?

  • Universal: Works with any Fortran codebase (F77, F90, F95, F2003, F2008)
  • Zero Configuration: Auto-detects project type and analyzes with sensible defaults
  • Batch Processing: Analyze multiple codebases efficiently with templates
  • Rich Insights: Dependency graphs, translation recommendations, code metrics
  • Multiple Outputs: JSON, YAML, GraphML, and interactive HTML visualizations
  • Python & CLI: Use as a Python library or command-line tool

Features

  • Dependency Analysis: Build module dependency graphs, identify circular dependencies
  • Translation Planning: Break large procedures into manageable translation units
  • Call Graph Generation: Visualize relationships between modules and procedures
  • Project Templates: Pre-configured for CTSM, climate models, numerical libraries, and more
  • Extensible: Easy to customize for specific project needs

Installation

# Recommended: Install with advanced parsing support
pip install fortran-analyzer[fparser]

# Or just the basics
pip install fortran-analyzer

# Development installation
git clone https://github.com/yourusername/fortran-analyzer.git
cd fortran-analyzer
pip install -e .[dev]

Optional Features:

  • [fparser] - Advanced Fortran parsing (recommended)
  • [interactive] - Interactive visualizations with Plotly
  • [full] - All features included

📖 New to Fortran Analyzer? Check out the Quick Start Guide for a 5-minute tutorial!

Quick Start

5-Minute Tutorial

Analyze a single Fortran project:

from fortran_analyzer import quick_analyze

# Auto-detect and analyze - that's it!
results = quick_analyze('/path/to/fortran/project')

# Access results
print(f"Files: {results['parsing']['statistics']['total_files']}")
print(f"Modules: {len(results['parsing']['modules'])}")
print(f"Translation units: {results['translation']['units']}")

Analyze multiple codebases:

from fortran_analyzer import create_analyzer_for_project

# Analyze multiple projects in a loop
projects = {
    'climate_model_a': '/path/to/model_a',
    'climate_model_b': '/path/to/model_b',
    'library_c': '/path/to/library_c'
}

for name, path in projects.items():
    print(f"\nAnalyzing {name}...")

    # Auto-detect project type and analyze
    analyzer = create_analyzer_for_project(
        path,
        template='auto',
        output_dir=f'analysis_results/{name}'
    )

    results = analyzer.analyze()
    summary = analyzer.get_summary_statistics()

    print(f"  Files: {summary['files']}")
    print(f"  Modules: {summary['modules']}")
    print(f"  Lines: {summary['lines']:,}")

Use from command line:

# Auto-detect and analyze
fortran-analyzer analyze /path/to/project --template auto

# Analyze with specific template
fortran-analyzer analyze /path/to/ctsm --template ctsm

# Batch process multiple projects
for project in /path/to/projects/*; do
    fortran-analyzer analyze "$project" --template auto \
        --output-dir "results/$(basename $project)"
done

Common Use Cases

1. Understanding a New Codebase

from fortran_analyzer import quick_analyze

# Quick overview of a new project
results = quick_analyze('/path/to/unknown/project', output_dir='./analysis')

# Check the generated files:
# - analysis_summary.txt: Human-readable overview
# - analysis_results.json: Full structured data
# - graphs/: Dependency visualizations

2. Planning a Translation Project

from fortran_analyzer import create_analyzer_for_project

# Analyze for translation planning
analyzer = create_analyzer_for_project(
    '/path/to/legacy/code',
    template='auto',
    max_translation_unit_lines=150  # Customize chunk size
)

results = analyzer.analyze()

# Get translation recommendations
recommendations = results['recommendations']
print("Translation Strategy:", recommendations['translation_strategy'])
print("Dependency Issues:", recommendations['dependency_issues'])

# Get suggested order
translation_order = analyzer.get_translation_order()
print(f"Start with: {translation_order[:5]}")

3. Comparing Multiple Codebases

from fortran_analyzer import create_analyzer_for_project
import json

# Compare multiple similar projects
codebases = ['codebase_v1', 'codebase_v2', 'codebase_v3']
comparison = {}

for codebase in codebases:
    analyzer = create_analyzer_for_project(f'/path/to/{codebase}')
    results = analyzer.analyze()

    comparison[codebase] = {
        'modules': len(results['parsing']['modules']),
        'lines': results['parsing']['statistics']['total_lines'],
        'dependencies': results['dependencies']['module_graph_summary']['edges'],
        'circular_deps': len(results['dependencies']['analysis']['circular_dependencies'])
    }

# Save comparison
with open('codebase_comparison.json', 'w') as f:
    json.dump(comparison, f, indent=2)

# Print comparison table
for name, stats in comparison.items():
    print(f"{name:20} | Modules: {stats['modules']:4} | Lines: {stats['lines']:8,} | Deps: {stats['dependencies']:4}")

4. Custom Configuration for Specific Needs

from fortran_analyzer.config.project_config import FortranProjectConfig
from fortran_analyzer import FortranAnalyzer

# Full control with custom configuration
config = FortranProjectConfig(
    project_name="My Legacy System",
    project_root="/path/to/project",
    source_dirs=["src", "lib", "modules"],
    fortran_extensions=[".f90", ".F90", ".f", ".F"],
    max_translation_unit_lines=80,
    external_libraries=["netcdf", "hdf5", "mpi"],
    system_modules=["iso_fortran_env", "iso_c_binding"],
    exclude_patterns=["**/test_*", "**/deprecated/**"],
    generate_graphs=True
)

analyzer = FortranAnalyzer(config)
results = analyzer.analyze()

Documentation

📚 Comprehensive Guides:

Configuration

Configuration Templates

The analyzer comes with predefined templates for common project types:

  • ctsm: Community Terrestrial Systems Model
  • scientific_computing: General scientific computing projects
  • numerical_library: Numerical libraries and mathematical software
  • climate_model: Climate and atmospheric models
  • generic: Generic Fortran projects

Configuration Options

Key configuration parameters:

# Project identification
project_name: "My Fortran Project"
project_root: "/path/to/project"

# Source code locations
source_dirs:
  - "src"
  - "lib"

# File patterns
include_patterns:
  - "**/*.f90"
  - "**/*.F90"
exclude_patterns:
  - "**/test_*"

# Fortran settings
fortran_extensions: [".f90", ".F90"]
fortran_standard: "f2003"

# Translation unit settings
max_translation_unit_lines: 150
min_chunk_lines: 50
preserve_interfaces: true

# Dependencies
system_modules:
  - "iso_fortran_env"
external_libraries:
  - "netcdf"
  - "mpi"

# Output settings
output_dir: "analysis_output"
generate_graphs: true
generate_metrics: true

Output

Generated Files

The analyzer generates several output files:

  • analysis_results.json: Complete analysis results in JSON format
  • analysis_summary.txt: Human-readable summary report
  • translation_units.json: Translation unit decomposition
  • graphs/: Directory containing dependency graphs in various formats
  • visualizations/: Directory containing visualization images and HTML files

Visualizations

Generated visualizations include:

  1. Module Dependency Graph: Shows relationships between modules
  2. Translation Units Analysis: Charts showing unit distribution and complexity
  3. Project Overview: Summary statistics and metrics
  4. Translation Priority Chart: Recommended translation order
  5. Interactive Dependency Graph: Web-based interactive visualization

Advanced Usage

Extending the Framework

Custom Parser

from fortran_analyzer.parser.fortran_parser import FortranParser

class CustomFortranParser(FortranParser):
    def parse_custom_construct(self, content):
        # Custom parsing logic
        pass

Custom Analysis

from fortran_analyzer.analysis.call_graph_builder import CallGraphBuilder

class CustomCallGraphBuilder(CallGraphBuilder):
    def analyze_custom_relationships(self, modules):
        # Custom analysis logic
        pass

Integration with Other Tools

Export for Other Tools

# Export graphs for Gephi
analyzer.call_graph_builder.export_graphs(
    output_dir, formats=['gexf']
)

# Export for NetworkX processing
module_graph = analyzer.call_graph_builder.get_module_graph()
# Use NetworkX algorithms...

Continuous Integration

# Use in CI/CD pipeline
fortran-analyzer analyze $PROJECT_ROOT --template auto --output-dir ./reports
# Upload reports as artifacts

Real-World Examples

Example 1: Batch Analysis of Multiple Research Codebases

"""
Analyze multiple Fortran codebases from a research organization.
"""
from fortran_analyzer import create_analyzer_for_project
from pathlib import Path
import json

# Define your codebases
codebases = {
    'weather_model': {
        'path': '/research/weather/model',
        'template': 'climate_model'
    },
    'ocean_dynamics': {
        'path': '/research/ocean/dynamics',
        'template': 'scientific_computing'
    },
    'linear_algebra_lib': {
        'path': '/research/libs/linalg',
        'template': 'numerical_library'
    }
}

# Analyze all codebases
results_summary = {}

for name, config in codebases.items():
    print(f"\n{'='*60}")
    print(f"Analyzing: {name}")
    print(f"{'='*60}")

    analyzer = create_analyzer_for_project(
        config['path'],
        template=config['template'],
        output_dir=f'batch_analysis/{name}'
    )

    results = analyzer.analyze()
    summary = analyzer.get_summary_statistics()

    results_summary[name] = summary

    print(f"✓ Completed: {name}")
    print(f"  Files: {summary['files']}")
    print(f"  Modules: {summary['modules']}")
    print(f"  Translation Units: {summary['translation_units']}")

# Save consolidated summary
with open('batch_analysis/summary.json', 'w') as f:
    json.dump(results_summary, f, indent=2)

print("\n" + "="*60)
print("Batch analysis complete! Results saved to batch_analysis/")

Example 2: Automated Codebase Health Check

"""
Regular health check for Fortran codebases - detect issues early.
"""
from fortran_analyzer import create_analyzer_for_project

def health_check(project_path, project_name):
    """Run health check on a Fortran codebase."""

    print(f"\nHealth Check: {project_name}")
    print("-" * 40)

    analyzer = create_analyzer_for_project(project_path, template='auto')
    results = analyzer.analyze()

    # Check for issues
    deps = results['dependencies']['analysis']
    circular_deps = deps.get('circular_dependencies', [])
    orphaned = deps.get('orphaned_modules', [])

    issues = []

    if circular_deps:
        issues.append(f"⚠️  {len(circular_deps)} circular dependencies found")

    if orphaned:
        issues.append(f"ℹ️  {len(orphaned)} orphaned modules")

    external_deps = deps.get('external_dependencies', [])
    if len(external_deps) > 10:
        issues.append(f"⚠️  High external dependencies: {len(external_deps)}")

    if not issues:
        print("✓ No issues found - codebase is healthy!")
    else:
        print("Issues detected:")
        for issue in issues:
            print(f"  {issue}")

    return len(issues) == 0

# Run health checks on multiple projects
projects = {
    'MainModel': '/path/to/main/model',
    'SupportLib': '/path/to/support/lib',
}

all_healthy = all(health_check(path, name) for name, path in projects.items())

if all_healthy:
    print("\n✓ All projects are healthy!")
else:
    print("\n⚠️  Some projects have issues - review the reports")

Example 3: Migration Planning Report

"""
Generate a comprehensive migration/translation planning report.
"""
from fortran_analyzer import create_analyzer_for_project
import json

def create_migration_plan(project_path, target_language='Python'):
    """Create a detailed migration plan."""

    print(f"Creating migration plan for: {project_path}")
    print(f"Target language: {target_language}\n")

    analyzer = create_analyzer_for_project(
        project_path,
        template='auto',
        max_translation_unit_lines=100,  # Smaller units for easier translation
        output_dir='migration_plan'
    )

    results = analyzer.analyze()

    # Generate report
    report = {
        'project_overview': {
            'total_files': results['parsing']['statistics']['total_files'],
            'total_lines': results['parsing']['statistics']['total_lines'],
            'total_modules': len(results['parsing']['modules']),
        },
        'translation_plan': {
            'total_units': results['translation']['units'],
            'recommended_order': analyzer.get_translation_order()[:10],  # First 10
            'effort_estimate': results['translation']['statistics'].get('units_by_effort', {}),
        },
        'dependencies': {
            'external_libraries': results['dependencies']['analysis'].get('external_dependencies', []),
            'circular_dependencies': results['dependencies']['analysis'].get('circular_dependencies', []),
        },
        'recommendations': results['recommendations'],
    }

    # Save detailed report
    with open('migration_plan/detailed_plan.json', 'w') as f:
        json.dump(report, f, indent=2)

    # Print summary
    print("Migration Plan Summary:")
    print(f"  Total modules to translate: {report['project_overview']['total_modules']}")
    print(f"  Translation units: {report['translation_plan']['total_units']}")
    print(f"  Recommended start: {report['translation_plan']['recommended_order'][0]}")

    effort = report['translation_plan']['effort_estimate']
    print(f"\nEffort Estimate:")
    print(f"  Low effort units: {effort.get('low', 0)}")
    print(f"  Medium effort units: {effort.get('medium', 0)}")
    print(f"  High effort units: {effort.get('high', 0)}")

    return report

# Run migration planning
plan = create_migration_plan('/path/to/legacy/fortran', target_language='Python')

Example 4: Continuous Integration Check

"""
CI/CD script to ensure code quality standards.
"""
import sys
from fortran_analyzer import create_analyzer_for_project

def ci_quality_check(project_path, max_circular_deps=0, max_orphaned=5):
    """Quality check for CI/CD pipeline."""

    analyzer = create_analyzer_for_project(project_path, template='auto')
    results = analyzer.analyze()

    deps_analysis = results['dependencies']['analysis']

    # Check quality metrics
    circular_deps = len(deps_analysis.get('circular_dependencies', []))
    orphaned = len(deps_analysis.get('orphaned_modules', []))

    passed = True

    if circular_deps > max_circular_deps:
        print(f"❌ FAIL: {circular_deps} circular dependencies (max: {max_circular_deps})")
        passed = False

    if orphaned > max_orphaned:
        print(f"⚠️  WARNING: {orphaned} orphaned modules (max: {max_orphaned})")

    if passed:
        print("✓ Quality check passed!")
        return 0
    else:
        return 1

# Use in CI pipeline
if __name__ == '__main__':
    project_path = sys.argv[1] if len(sys.argv) > 1 else '.'
    exit_code = ci_quality_check(project_path)
    sys.exit(exit_code)

Example 5: CTSM-Specific Analysis

from fortran_analyzer import create_analyzer_for_project

# Analyze CTSM biogeophysics component
analyzer = create_analyzer_for_project(
    '/path/to/ctsm',
    template='ctsm',
    source_dirs=['src/biogeophys'],  # Focus on specific component
    output_dir='ctsm_biogeophys_analysis'
)

results = analyzer.analyze()

# Get specific recommendations
recommendations = results['recommendations']
for category, items in recommendations.items():
    if items:
        print(f"\n{category.upper()}:")
        for item in items:
            print(f"  - {item}")

Troubleshooting

Common Issues

  1. Parser Errors: If fparser2 is not available, the analyzer falls back to regex-based parsing
  2. Large Projects: For very large projects, consider analyzing subdirectories separately
  3. Memory Usage: Large dependency graphs may require substantial memory

Performance Tips

  • Use exclude_patterns to skip unnecessary files
  • Focus analysis on specific source directories
  • Disable graph generation for faster analysis: generate_graphs: false

Getting Help

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Development Setup

git clone https://github.com/yourusername/fortran-analyzer.git
cd fortran-analyzer
pip install -e .[dev]
pytest  # Run tests

License

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

Citation

If you use this tool in research, please cite:

@software{fortran_analyzer,
  title={Fortran Analyzer: A Generic Framework for Fortran Code Analysis},
  author={Fortran Analyzer Team},
  year={2024},
  url={https://github.com/yourusername/fortran-analyzer}
}

Acknowledgments

  • Built with NetworkX for graph analysis
  • Optional integration with fparser2 for advanced parsing
  • Visualizations powered by Matplotlib and Plotly
  • Inspired by the need to modernize legacy Fortran codebases

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages