#!/usr/bin/env python3 import os import shutil import subprocess import sys def check_command_installed(cmd): """Checks if a command-line tool is installed in the current environment.""" return shutil.which(cmd) is not None def run_process(args, description): """Runs a subprocess with clean console updates.""" print(f"\n[Running] {description}...") try: res = subprocess.run(args, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) print(f"[Success] {description} completed successfully!") if res.stdout.strip(): print(res.stdout.strip()) return True except subprocess.CalledProcessError as e: print(f"[Error] {description} FAILED with exit code {e.returncode}!") if e.stdout: print("--- STDOUT ---") print(e.stdout.strip()) if e.stderr: print("--- STDERR ---") print(e.stderr.strip()) return False except Exception as e: print(f"[Error] Failed to execute process: {e}") return False def build_orchestrator(): workspace_dir = os.path.dirname(os.path.abspath(__file__)) docs_dir = os.path.join(workspace_dir, "docs") site_dir = os.path.join(workspace_dir, "site") # Clean old docs and site if os.path.exists(docs_dir): shutil.rmtree(docs_dir) os.makedirs(docs_dir, exist_ok=True) os.makedirs(os.path.join(docs_dir, "python"), exist_ok=True) if os.path.exists(site_dir): shutil.rmtree(site_dir) # 1. Create landing index.md index_path = os.path.join(docs_dir, "index.md") print(f"Creating landing index: {index_path}") with open(index_path, 'w', encoding='utf-8') as f: f.write("""# HPC DNS Post-Processing Reference Manual Welcome to the automated documentation suite for the high-order compact difference post-processing project! This manual includes reference documentation for both the compiled Fortran core and the Python binds/algebraic generators. --- ## Architecture Overview This post-processor compiles algebraic equations defined in a custom DSL (Domain Specific Language), computes high-order derivatives (1st & 2nd spatial derivatives) using tridiagonal finite differences, and leverages parallel MPI-IO blocks. Below is the primary compiler pipeline: ```mermaid graph TD A[code_gen.py / DSL Input] -->|AST Compiler Pipeline| B[post.py / Code Gen] B -->|Generates budget kernels| C[m_calculate.f90] C -->|LU Tridiagonal Solvers| D[Compact.f90] C -->|Array allocations| E[m_arrays.f90] F[post.f90 / Main Driver] -->|Orchestrates calculations| C F -->|Parallelizes domain| G[m_openmpi.f90] F -->|Loads run configuration| H[m_parameters.f90] I[pycompact.py / Legacy Bindings - Archived] -.->|Transitional| C ``` --- ## Navigation Guide - **[Python API Reference](python/code_gen.md)**: Automatically parsed API and signatures for the DSL parsers, topological compiler stages, and code generation routines. - **[Fortran Core Reference](fortran.md)**: Standard documentation compiled by `FORD` covering tridiagonal compact solvers, math kernels, and MPI bindings. - **[Archived Python Bindings](archive/pycompact.md)**: Legacy bindings wrapping calculation kernels. *Generated automatically using industry-standard tools: FORD, MkDocs, and Material theme.* """) # 2. Create fortran.md entry page fortran_entry_path = os.path.join(docs_dir, "fortran.md") print(f"Creating Fortran entry: {fortran_entry_path}") with open(fortran_entry_path, 'w', encoding='utf-8') as f: f.write("""# Fortran Core API Reference The Fortran core elements have been fully documented and compiled using **FORD** (FORtran Documenter), which extracts comments (`!>` and `!!`) and draws rich module dependency graphs. --- ## View Interactive Documentation [👉 Click Here to Open Interactive Fortran API Reference](fortran_api/index.html){: .md-button .md-button--primary .md-button--large } --- ### Core Modules Cataloged - **[[Compact]]**: Solves compact finite difference scheme equations using high-order tridiagonal/pentadiagonal schemes. - **[[m_calculate]]**: Runs budget calculations, spatial derivatives, and pieces Arrhenius chemical kinetics. - **[[m_arrays]]**: Handles array allocations and instant budget memory blocks. - **[[m_openmpi]]**: Wraps domain sizing, grid splitting, and MPI communicators. - **[[m_parameters]]**: Loads physical settings, boundary configurations, and grid sizes from file inputs. - **[[post]]**: Entry driver performing budget calculation run tasks. """) # 3. Create Python stubs for mkdocstrings python_modules = [ ("code_gen", "Code Generator (`code_gen.py`)"), ("post", "Compiler Stages (`post.py`)") ] for mod_name, title in python_modules: stub_path = os.path.join(docs_dir, "python", f"{mod_name}.md") print(f"Creating Python stub: {stub_path}") with open(stub_path, 'w', encoding='utf-8') as f: f.write(f"""# {title} ::: {mod_name} """) # Create Archived Python Bindings stub os.makedirs(os.path.join(docs_dir, "archive"), exist_ok=True) archive_stub_path = os.path.join(docs_dir, "archive", "pycompact.md") print(f"Creating Archived Python Bindings stub: {archive_stub_path}") with open(archive_stub_path, 'w', encoding='utf-8') as f: f.write("""# Archived Python Bindings (`pycompact.py`) !!! warning "Archived/Legacy Component" **Historical Note**: Originally, researchers manually wrote post-processing Fortran codes. To reduce this maintenance overhead, python bindings (`pycompact.py`) were introduced as a transitional wrapper. However, since the bindings did not significantly improve ease of use, we subsequently defined a custom DSL (Domain Specific Language) for describing post-processing operations and developed a compiler (`code_gen.py` & `post.py`) to generate highly optimized Fortran codes. As a result, the python bindings are now archived. ::: pycompact """) # 4. Environment validation missing_tools = [] if not check_command_installed("ford"): missing_tools.append("ford") if not check_command_installed("mkdocs"): missing_tools.append("mkdocs") if missing_tools: print("\n" + "="*80) print(" [Warning] Missing Required Documentation Tools!") print("="*80) print("To compile the complete interactive manual, please install the requirements:") print(" pip install -r requirements.txt") print("Note: If running in a virtualenv, make sure it is activated.") print("="*80 + "\n") return False # 5. Compile Fortran API docs using FORD ford_success = run_process(["ford", "ford.md"], "FORD Fortran Compiler") if not ford_success: print("[Warning] FORD documentation compilation failed. Continuing to MkDocs compilation...") # 6. Compile Python API and build MkDocs site mkdocs_success = run_process(["mkdocs", "build"], "MkDocs Website Compiler") if mkdocs_success: print("\n" + "="*80) print(" 🎉 Documentation manual built successfully!") print("="*80) print(f"Output directory: {site_dir}") print("To view or serve the website locally, run:") print(" mkdocs serve") print("="*80) else: print("\n[Error] MkDocs website compilation FAILED!") return False return True if __name__ == "__main__": success = build_orchestrator() sys.exit(0 if success else 1)