incomp-flame-post/build_docs.py
ignis 2b3362a927
All checks were successful
CI Test Suite / run-tests (push) Successful in 1m11s
Deploy Documentation / build-and-deploy (push) Successful in 53s
Archive legacy pycompact.py python bindings and update documentation with historical context
2026-06-03 12:00:43 +00:00

177 lines
7.4 KiB
Python

#!/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)