Manual: Add special treatment for XC_FUNCTIONAL sections

This commit is contained in:
Ole Schütt 2023-10-29 16:22:17 +01:00 committed by Ole Schütt
parent 73d5172bf9
commit c03a135806

View file

@ -2,7 +2,7 @@
# author: Ole Schuett
from typing import Tuple, List, Optional
from typing import Dict, Tuple, List, Optional
import lxml.etree as ET
import lxml
from pathlib import Path
@ -105,34 +105,13 @@ def process_section(
has_name_collision: bool,
output_dir: Path,
) -> int:
# Find more section fields.
repeats = "repeats" in section.attrib and section.attrib["repeats"] == "yes"
description = get_text(section.find("DESCRIPTION"))
location = get_text(section.find("LOCATION"))
section_name = section_path[-1] # section.find("NAME") doesn't work for root
section_xref = ".".join(section_path) # used for cross-referencing
# Find section references.
references = [get_name(ref) for ref in section.findall("REFERENCE")]
output = []
output += ["%", "% This file was created by generate_input_reference.py", "%"]
# There are a few collisions between cross references for sections and keywords,
# for example CP2K_INPUT.FORCE_EVAL.SUBSYS.KIND.POTENTIAL
collision_resolution_suffix = "_SECTION" if has_name_collision else ""
output += [f"({section_xref}{collision_resolution_suffix})="]
output += [f"# {section_name}", ""]
if repeats:
output += ["**Section can be repeated.**", ""]
if references:
citations = ", ".join([f"{{ref}}`{r}`" for r in references])
output += [f"**References:** {citations}", ""]
output += [escape_markdown(description), github_link(location), ""]
# Collect and sort subsections.
subsections = sorted(section.findall("SECTION"), key=get_name)
# Render section header.
output, section_name, section_xref = render_section_header(
section, section_path, has_name_collision
)
# Render TOC for subsections
subsections = sorted(section.findall("SECTION"), key=get_name)
if subsections:
output += ["```{toctree}"]
output += [":maxdepth: 1"]
@ -175,16 +154,108 @@ def process_section(
# Process subsections
keyword_names = {get_name(keyword) for keyword in keywords}
for subsection in subsections:
subsection_path = (*section_path, get_name(subsection))
has_name_collision = get_name(subsection) in keyword_names
n = process_section(subsection, subsection_path, has_name_collision, output_dir)
subsection_name = get_name(subsection)
subsection_path = (*section_path, subsection_name)
has_collision = subsection_name in keyword_names
if subsection_name == "XC_FUNCTIONAL":
n = process_xc_functional_section(subsection, subsection_path, output_dir)
else:
n = process_section(subsection, subsection_path, has_collision, output_dir)
num_files_written += n
return num_files_written
# ======================================================================================
def render_keyword(keyword: lxml.etree._Element, section_xref: str) -> List[str]:
def process_xc_functional_section(
section: lxml.etree._Element,
section_path: SectionPath,
output_dir: Path,
) -> int:
# Render section header and keywords.
assert section_path[-1] == "XC_FUNCTIONAL"
output, section_name, section_xref = render_section_header(section, section_path)
for keyword in section.findall("SECTION_PARAMETERS") + section.findall("KEYWORD"):
output += render_keyword(keyword, section_xref)
# Render note.
output += ["```{note}"]
output += ["Thanks to [Libxc](https://www.tddft.org/programs/Libxc) there are 600+"]
output += ["functionals available. For ease of browsing their documentation has"]
output += ["been inlined into this page. Each of the functionals has a"]
output += ["corresponding subsection that, if present, enables the functional."]
output += ["```", ""]
# Index functionals by prefix.
subsections = sorted(section.findall("SECTION"), key=get_name)
prefixes = ["", "LDA_X_", "LDA_C_", "LDA_XC_", "LDA_K_", "HYB_LDA_XC_", "GGA_X_"]
prefixes += ["GGA_C_", "GGA_XC_", "GGA_K_", "HYB_GGA_X_", "HYB_GGA_XC_", "MGGA_X_"]
prefixes += ["MGGA_C_", "MGGA_XC_", "MGGA_K_", "HYB_MGGA_X_", "HYB_MGGA_XC_"]
subsections_by_prefix: Dict[str, List[str]] = {prefix: [] for prefix in prefixes}
for subsection in subsections:
subsection_name = get_name(subsection)
for prefix in reversed(prefixes): # reverse order because "" always matches
if subsection_name.startswith(prefix):
subsections_by_prefix[prefix].append(subsection_name)
break
# Render list of functionals for each prefix.
for prefix, section_names in subsections_by_prefix.items():
category = f"Libxc {prefix[:-1]}" if prefix else "Built-in"
items = [f"[{s[len(prefix):]}](#{section_xref}.{s})" for s in section_names]
output += [f"## {category} Functionals", "", ",\n".join(items), ""]
# Render inline subsections
for subsection in subsections:
subsection_name = get_name(subsection)
output += [f"({section_xref}.{subsection_name})=", f"## {subsection_name}", ""]
output += [escape_markdown(get_text(subsection.find("DESCRIPTION"))), ""]
for keyword in sorted(subsection.findall("KEYWORD"), key=get_name):
output += render_keyword(keyword, section_xref=None, github=False)
# Write output
section_dir = output_dir / "/".join(section_path[:-1])
write_file(section_dir / "XC_FUNCTIONAL.md", "\n".join(output))
return 1
# ======================================================================================
def render_section_header(
section: lxml.etree._Element,
section_path: SectionPath,
has_name_collision: bool = False,
) -> Tuple[List[str], str, str]:
# Collect information from section fields.
repeats = "repeats" in section.attrib and section.attrib["repeats"] == "yes"
description = get_text(section.find("DESCRIPTION"))
location = get_text(section.find("LOCATION"))
section_name = section_path[-1] # section.find("NAME") doesn't work for root
section_xref = ".".join(section_path) # used for cross-referencing
references = [get_name(ref) for ref in section.findall("REFERENCE")]
# Render header.
output = []
output += ["%", "% This file was created by generate_input_reference.py", "%"]
# There are a few collisions between cross references for sections and keywords,
# for example CP2K_INPUT.FORCE_EVAL.SUBSYS.KIND.POTENTIAL
collision_resolution_suffix = "_SECTION" if has_name_collision else ""
output += [f"({section_xref}{collision_resolution_suffix})="]
output += [f"# {section_name}", ""]
if repeats:
output += ["**Section can be repeated.**", ""]
if references:
citations = ", ".join([f"{{ref}}`{r}`" for r in references])
output += [f"**References:** {citations}", ""]
output += [escape_markdown(description), github_link(location), ""]
return output, section_name, section_xref
# ======================================================================================
def render_keyword(
keyword: lxml.etree._Element,
section_xref: Optional[str],
github: bool = True,
) -> List[str]:
# Find keyword names.
keyword_names: List[str]
if keyword.tag == "SECTION_PARAMETERS":
@ -230,7 +301,10 @@ def render_keyword(keyword: lxml.etree._Element, section_xref: str) -> List[str]
# Use Sphinx's py:data directive to document keywords.
output += [f"```{{py:data}} {canonical_name}"]
n_var_brackets = f"[{n_var}]" if n_var > 1 else ""
output += [f":module: {section_xref}"]
if section_xref:
output += [f":module: {section_xref}"]
else:
output += [":noindex:"]
output += [f":type: '{data_type}{n_var_brackets}'"]
if default_value:
output += [f":value: '{default_value}'"]
@ -254,8 +328,10 @@ def render_keyword(keyword: lxml.etree._Element, section_xref: str) -> List[str]
if references:
citations = ", ".join([f"{{ref}}`{r}`" for r in references])
output += [f"**References:** {citations}", ""]
output += [escape_markdown(description), github_link(location), ""]
output += ["```", ""] # Close py:data directive.
output += [escape_markdown(description)]
if github:
output += [github_link(location)]
output += ["", "```", ""] # Close py:data directive.
if deprecation_notice:
output += ["```{warning}", "The keyword"]