mirror of
https://github.com/cp2k/cp2k.git
synced 2026-07-29 06:35:28 -04:00
Manual: Add special treatment for XC_FUNCTIONAL sections
This commit is contained in:
parent
73d5172bf9
commit
c03a135806
1 changed files with 110 additions and 34 deletions
|
|
@ -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"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue