From b36e689be37ec2ac8c500c7c899de2bbe3a8468b Mon Sep 17 00:00:00 2001 From: lukelowry Date: Sun, 7 Jun 2026 23:03:55 -0500 Subject: [PATCH 1/6] Initial RTD interfacing setup --- .gitignore | 6 + .readthedocs.yaml | 20 + docs/Doxyfile | 14 + docs/README.md | 57 +- docs/api.md | 5 + docs/conf.py | 33 + docs/generate_model_docs.py | 574 ++++++++++++++++++ docs/index.md | 21 + docs/models/index.md | 21 + docs/project/index.md | 23 + docs/requirements.txt | 4 + .../Tiny/TwoBus/Gensal/README.md | 4 +- 12 files changed, 755 insertions(+), 27 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 docs/Doxyfile create mode 100644 docs/api.md create mode 100644 docs/conf.py create mode 100644 docs/generate_model_docs.py create mode 100644 docs/index.md create mode 100644 docs/models/index.md create mode 100644 docs/project/index.md create mode 100644 docs/requirements.txt diff --git a/.gitignore b/.gitignore index fc8ef955b..3860c865d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,10 @@ build/ *.swp doxygen-docs/ +docs/_build/ +docs/api/generated/ +docs/examples/generated/ +docs/generated/ +docs/models/generated/ +docs/xml/ __pycache__ diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..506264ffd --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +version: 2 + +build: + os: ubuntu-24.04 + tools: + python: "3.12" + apt_packages: + - doxygen + - graphviz + jobs: + pre_build: + - python docs/generate_model_docs.py + - cd docs && doxygen Doxyfile + +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: docs/requirements.txt diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 000000000..98f1a96ec --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,14 @@ +PROJECT_NAME = "GridKit" +OUTPUT_DIRECTORY = . + +INPUT = ../GridKit ../README.md +RECURSIVE = YES +USE_MDFILE_AS_MAINPAGE = ../README.md + +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_OUTPUT = xml + +EXTRACT_ALL = YES +QUIET = YES diff --git a/docs/README.md b/docs/README.md index 2242f220a..923ca5d1a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,32 +1,39 @@ -# GridKit Documentation Target +# GridKit Documentation -We use CMake's built-in `FindDoxygen` module to generate a target that will -use Doxygen to build the project documentation. +GridKit documentation can be built two ways: -## Building +1. Read the Docs builds the Sphinx site from `docs/conf.py`. +2. CMake can build the existing Doxygen HTML target. + +## Read the Docs Build + +The Read the Docs proof of concept is configured by `.readthedocs.yaml`. +Before Sphinx runs, the build generates Markdown wrapper pages and Doxygen XML: -The documentation target is excluded from the default set of targets built via -`make` (or `cmake --build .`). It must be designated explicitly with ```sh -make GridKitDocs +python docs/generate_model_docs.py +cd docs && doxygen Doxyfile ``` -or + +To test the same flow locally: + ```sh -cmake --build . -t GridKitDocs +python -m pip install -r docs/requirements.txt +python docs/generate_model_docs.py +cd docs && doxygen Doxyfile +cd .. +sphinx-build -T -b html docs docs/_build/html ``` -The generated files can be found in the build directory under `docs/html/` and -the main page is `docs/html/index.html`. - -If you wish to inspect the Doxyfile itself, it is generated as -`docs/Doxyfile.GridKitDocs`. - -## Notes -The reasoning behind taking this approach is as follows: - -1. It is easier to maintain just the few options we need to customize rather - than a whole Doxyfile (leave what can be generated out of the repo); if - another option is needed, it's easy to look it up. -2. Since the documentation can be seen as a "product" or "artifact" of the code, - it makes sense for it to be a buildable "target" -3. Generated files should not be placed in the source directory. The binary - directory makes sense for this, and the CMake target makes this easy. + +The generated Sphinx files, Doxygen XML, and HTML output are build artifacts and +should not be committed. + +## CMake Doxygen Target + +The existing CMake target is still available for standalone Doxygen HTML output: + +```sh +cmake --build build -t GridKitDocs +``` + +The generated files are written under the build directory. diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 000000000..5218706cd --- /dev/null +++ b/docs/api.md @@ -0,0 +1,5 @@ +# API Reference + +```{doxygenindex} +:project: GridKit +``` diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..fcd5342e8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,33 @@ +from pathlib import Path + +project = "GridKit" +author = "GridKit Developers" + +docs_dir = Path(__file__).parent.resolve() + +extensions = ["breathe", "myst_parser", "sphinx.ext.graphviz"] + +breathe_projects = {"GridKit": str(docs_dir / "xml")} +breathe_default_project = "GridKit" + +html_theme = "sphinx_rtd_theme" +html_extra_path = ["Figures"] +html_theme_options = { + "collapse_navigation": False, + "navigation_depth": 6, + "titles_only": True, +} + +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} + +myst_enable_extensions = [ + "amsmath", + "dollarmath", + "html_image", +] +myst_heading_anchors = 5 + +exclude_patterns = ["_build", "xml", "README.md", "superpowers/**"] diff --git a/docs/generate_model_docs.py b/docs/generate_model_docs.py new file mode 100644 index 000000000..b3e34c9c4 --- /dev/null +++ b/docs/generate_model_docs.py @@ -0,0 +1,574 @@ +#!/usr/bin/env python3 +"""Generate Sphinx pages from GridKit Markdown documentation.""" + +from __future__ import annotations + +import os +import re +import shutil +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(frozen=True) +class MarkdownPage: + source: Path + output: Path + title: str + + +REPO_ROOT = Path(__file__).resolve().parents[1] +DOCS_DIR = REPO_ROOT / "docs" +MODEL_DIR = REPO_ROOT / "GridKit" / "Model" +EXAMPLES_DIR = REPO_ROOT / "examples" +GITHUB_TREE_URL = "https://github.com/ORNL/GridKit/tree/develop" + +GENERAL_OUT_DIR = DOCS_DIR / "generated" +MODEL_OUT_DIR = DOCS_DIR / "models" / "generated" +EXAMPLE_OUT_DIR = DOCS_DIR / "examples" / "generated" + +COMMON_MATH = REPO_ROOT / "GridKit" / "CommonMath.md" +PHASOR_INPUT_FORMAT = MODEL_DIR / "PhasorDynamics" / "INPUT_FORMAT.md" + +PROJECT_DOCS = ( + MarkdownPage(REPO_ROOT / "README.md", GENERAL_OUT_DIR / "root-readme.md", "GridKit"), + MarkdownPage(REPO_ROOT / "INSTALL.md", GENERAL_OUT_DIR / "install.md", "Installation"), + MarkdownPage( + REPO_ROOT / "CONTRIBUTING.md", + GENERAL_OUT_DIR / "contributing.md", + "Contributing", + ), + MarkdownPage( + REPO_ROOT / "CHANGELOG.md", + GENERAL_OUT_DIR / "changelog.md", + "Changelog", + ), + MarkdownPage( + DOCS_DIR / "README.md", + GENERAL_OUT_DIR / "documentation-build.md", + "Documentation Build", + ), + MarkdownPage( + REPO_ROOT / "buildsystem" / "README.md", + GENERAL_OUT_DIR / "buildsystem.md", + "Buildsystem", + ), + MarkdownPage( + REPO_ROOT / "application" / "PhasorDynamics" / "README.md", + GENERAL_OUT_DIR / "phasor-dynamics-application.md", + "Phasor Dynamics Application", + ), +) + +PROJECT_DOC_BY_SOURCE = {page.source: page for page in PROJECT_DOCS} +EXAMPLE_READMES = frozenset(EXAMPLES_DIR.glob("**/README.md")) + + +def example_doc_dirs() -> set[Path]: + dirs = {EXAMPLES_DIR} + for readme in EXAMPLE_READMES: + directory = readme.parent + while directory != EXAMPLES_DIR.parent: + dirs.add(directory) + if directory == EXAMPLES_DIR: + break + directory = directory.parent + return dirs + + +EXAMPLE_DOC_DIRS = example_doc_dirs() + + +def slugify(value: str) -> str: + value = value.replace("README.md", "").strip("/") + value = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", value) + value = value.replace("/", "-").replace("_", "-") + value = re.sub(r"[^A-Za-z0-9.-]+", "-", value) + value = re.sub(r"-+", "-", value) + return value.strip("-").lower() + + +def title_for_dir(directory: Path, root: Path, root_title: str) -> str: + if directory == root: + return root_title + return directory.name + + +def title_for(path: Path) -> str: + if path in PROJECT_DOC_BY_SOURCE: + return PROJECT_DOC_BY_SOURCE[path].title + if path == COMMON_MATH: + return "CommonMath" + if path == PHASOR_INPUT_FORMAT: + return "Input Format" + if is_example_readme(path): + return title_for_dir(path.parent, EXAMPLES_DIR, "Examples") + return path.parent.name + + +def is_readme_under(path: Path, root: Path) -> bool: + return path.name == "README.md" and root in path.parents + + +def model_parts_for_dir(model_dir: Path) -> list[str]: + return [slugify(part) for part in model_dir.relative_to(MODEL_DIR).parts] + + +def example_parts_for_dir(example_dir: Path) -> list[str]: + return [slugify(part) for part in example_dir.relative_to(EXAMPLES_DIR).parts] + + +def child_readme_dirs(model_dir: Path) -> list[Path]: + return sorted( + child + for child in model_dir.iterdir() + if child.is_dir() and (child / "README.md").exists() + ) + + +def example_child_dirs(directory: Path) -> list[Path]: + return sorted( + child + for child in EXAMPLE_DOC_DIRS + if child.parent == directory and child != directory + ) + + +def is_model_readme(path: Path) -> bool: + return is_readme_under(path, MODEL_DIR) + + +def is_example_readme(path: Path) -> bool: + return path in EXAMPLE_READMES + + +def is_model_container(path: Path) -> bool: + return is_model_readme(path) and bool(child_readme_dirs(path.parent)) + + +def is_example_container(path: Path) -> bool: + return is_example_readme(path) and bool(example_child_dirs(path.parent)) + + +def example_index_path_for_dir(directory: Path) -> Path: + return EXAMPLE_OUT_DIR.joinpath(*example_parts_for_dir(directory), "index.md") + + +def example_readme_output_path(path: Path) -> Path: + if is_example_container(path): + return EXAMPLE_OUT_DIR.joinpath(*example_parts_for_dir(path.parent), "overview.md") + return example_index_path_for_dir(path.parent) + + +def output_path_for(path: Path) -> Path: + if path in PROJECT_DOC_BY_SOURCE: + return PROJECT_DOC_BY_SOURCE[path].output + if path == COMMON_MATH: + return MODEL_OUT_DIR / "common-math.md" + if path == PHASOR_INPUT_FORMAT: + return MODEL_OUT_DIR / "phasor-dynamics" / "input-format.md" + if is_model_container(path): + return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "overview.md") + if is_model_readme(path): + return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "index.md") + if is_example_readme(path): + return example_readme_output_path(path) + raise ValueError(f"Unhandled documentation source: {path}") + + +def index_path_for(path: Path) -> Path: + if is_model_readme(path): + return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "index.md") + if is_example_readme(path): + return example_index_path_for_dir(path.parent) + return output_path_for(path) + + +def is_url(target: str) -> bool: + return bool(re.match(r"^[A-Za-z][A-Za-z0-9+.-]*:", target)) + + +def split_anchor(target: str) -> tuple[str, str]: + path, sep, anchor = target.partition("#") + return path, f"{sep}{anchor}" if sep else "" + + +def resolve_local(source_dir: Path, target: str) -> Path: + return (source_dir / target).resolve() + + +def relpath_from_output(target: Path, current_output: Path) -> str: + return Path(os.path.relpath(target, current_output.parent)).as_posix() + + +def repo_source_url(path: Path) -> str: + relpath = path.relative_to(REPO_ROOT).as_posix() + return f"{GITHUB_TREE_URL}/{relpath}" + + +def fallback_repo_path(source_dir: Path, path_part: str) -> Path | None: + candidates: list[Path] = [] + if path_part.endswith("src/Model/PowerFlow/Gen/README.md"): + candidates.append(MODEL_DIR / "PowerFlow" / "README.md") + if path_part.startswith("src/Model/"): + candidates.append(REPO_ROOT / "GridKit" / path_part.removeprefix("src/")) + if path_part.startswith("Model/"): + candidates.append(REPO_ROOT / "GridKit" / path_part) + if "Model/" in path_part: + candidates.append( + REPO_ROOT / "GridKit" / "Model" / path_part.split("Model/", 1)[1] + ) + + source_parts = source_dir.relative_to(REPO_ROOT).parts + if len(source_parts) >= 2 and source_parts[0] == "application": + candidates.append(REPO_ROOT / "GridKit" / path_part.removeprefix("../../")) + + return next((candidate.resolve() for candidate in candidates if candidate.exists()), None) + + +def page_link(path: Path, anchor: str, current_output: Path) -> str | None: + if path == COMMON_MATH and anchor == "#anti-windup-indicator": + anchor = "#derived-functions" + + if path in PROJECT_DOC_BY_SOURCE or path == COMMON_MATH or path == PHASOR_INPUT_FORMAT: + return f"{relpath_from_output(output_path_for(path), current_output)}{anchor}" + if (is_model_readme(path) or is_example_readme(path)) and path.exists(): + return f"{relpath_from_output(output_path_for(path), current_output)}{anchor}" + return None + + +def rewrite_target(source_dir: Path, target: str, current_output: Path) -> str: + if not target or target.startswith("#") or is_url(target): + return target + + path_part, anchor = split_anchor(target) + resolved = resolve_local(source_dir, path_part) + fallback_path = None + if not resolved.exists(): + fallback_path = fallback_repo_path(source_dir, path_part) + if fallback_path is not None: + resolved = fallback_path + + generated_link = page_link(resolved, anchor, current_output) + if generated_link is not None: + return generated_link + + if resolved.exists() and not resolved.is_dir(): + return f"{relpath_from_output(resolved, current_output)}{anchor}" + if resolved.exists() and resolved.is_dir(): + readme = resolved / "README.md" + if readme.exists(): + generated_dir_link = page_link(readme, anchor, current_output) + if generated_dir_link is not None: + return generated_dir_link + return f"{repo_source_url(resolved)}{anchor}" + + try: + resolved.relative_to(REPO_ROOT) + except ValueError: + return target + + return f"{relpath_from_output(resolved, current_output)}{anchor}" + + +def rewrite_asset_target(source_dir: Path, target: str, current_output: Path) -> str: + if not target or target.startswith("#") or is_url(target): + return target + + path_part, anchor = split_anchor(target) + resolved = resolve_local(source_dir, path_part) + try: + path = relpath_from_output(resolved, current_output) + except ValueError: + return target + return f"{path}{anchor}" + + +def rewrite_markdown_links(text: str, source_dir: Path, current_output: Path) -> str: + def image_repl(match: re.Match[str]) -> str: + label, target = match.groups() + return f"![{label}]({rewrite_asset_target(source_dir, target, current_output)})" + + def link_repl(match: re.Match[str]) -> str: + label, target = match.groups() + return f"[{label}]({rewrite_target(source_dir, target, current_output)})" + + text = re.sub(r"!\[([^\]]*)\]\(([^)\s]+)\)", image_repl, text) + text = re.sub(r"(? str: + def repl(match: re.Match[str]) -> str: + prefix, target, suffix = match.groups() + return f"{prefix}{rewrite_asset_target(source_dir, target, current_output)}{suffix}" + + return re.sub(r"(]*\bsrc=[\"'])([^\"']+)([\"'])", repl, text) + + +def normalize_heading_levels(text: str) -> str: + levels = sorted( + {len(match.group(1)) for match in re.finditer(r"(?m)^(#{1,6})(\s+)", text)} + ) + mapping = {level: index + 2 for index, level in enumerate(levels)} + + def repl(match: re.Match[str]) -> str: + level = len(match.group(1)) + return f"{'#' * mapping.get(level, level)}{match.group(2)}" + + return re.sub(r"(?m)^(#{1,6})(\s+)", repl, text) + + +def comparable_title(value: str) -> str: + value = value.replace("™", "") + return re.sub(r"[^a-z0-9]+", "", value.lower()) + + +def strip_duplicate_top_heading(text: str, title: str) -> str: + match = re.match(r"\s*#\s+(.+?)\s*(?:\n+|$)", text) + if match and comparable_title(match.group(1)) == comparable_title(title): + return text[match.end() :] + return text + + +def normalize_comment_fences(text: str, source: Path) -> str: + if source != REPO_ROOT / "CONTRIBUTING.md": + return text + + def repl(match: re.Match[str]) -> str: + body = match.group(1) + lines = [line.strip() for line in body.splitlines() if line.strip()] + if lines and all(line.startswith(("/", "*")) for line in lines): + return f"```text\n{body}```" + return match.group(0) + + return re.sub(r"```c\+\+\n(.*?)```", repl, text, flags=re.DOTALL) + + +def normalize_markdown(text: str, source: Path, title: str) -> str: + text = strip_duplicate_top_heading(text, title) + text = normalize_comment_fences(text, source) + text = re.sub(r"(?m)^```\s*math\s*$", "```{math}", text) + text = re.sub(r"\$`([^`]+)`\$", r"$\1$", text) + text = normalize_heading_levels(text) + if source == COMMON_MATH: + text = re.sub( + r"(?m)^(#{2,6}\s+Derived Functions)", + r"(anti-windup-indicator)=\n\1", + text, + count=1, + ) + return text + + +def toctree_block(entries: list[str], *, hidden: bool = False, maxdepth: int = 2) -> str: + options = [f":maxdepth: {maxdepth}", ":titlesonly:"] + if hidden: + options.append(":hidden:") + + return ( + "```{toctree}\n" + + "\n".join(options) + + "\n\n" + + "\n".join(entries) + + "\n```\n" + ) + + +def markdown_link(title: str, path: Path, current_output: Path) -> str: + return f"[{title}]({relpath_from_output(path, current_output)})" + + +def tree_index_entries(source: Path, current_output: Path) -> list[str]: + entries = [relpath_from_output(output_path_for(source).with_suffix(""), current_output)] + if source == MODEL_DIR / "PhasorDynamics" / "README.md": + entries.append( + relpath_from_output( + output_path_for(PHASOR_INPUT_FORMAT).with_suffix(""), + current_output, + ) + ) + + entries.extend( + relpath_from_output( + index_path_for(child_dir / "README.md").with_suffix(""), + current_output, + ) + for child_dir in child_readme_dirs(source.parent) + ) + return entries + + +def overview_entries_for(source: Path, current_output: Path) -> list[str]: + entries = [ + markdown_link("Overview", output_path_for(source), current_output), + ] + if source == MODEL_DIR / "PhasorDynamics" / "README.md": + entries.append( + markdown_link("Input Format", output_path_for(PHASOR_INPUT_FORMAT), current_output) + ) + return entries + + +def model_contents_block(source: Path, current_output: Path) -> str: + child_dirs = child_readme_dirs(source.parent) + lines = ["## Pages", ""] + lines.extend(f"- {entry}" for entry in overview_entries_for(source, current_output)) + + if child_dirs: + lines.extend(["", "## Sections", ""]) + for child_dir in child_dirs: + child_readme = child_dir / "README.md" + child_link = markdown_link( + title_for(child_readme), + index_path_for(child_readme), + current_output, + ) + grandchild_links = [ + markdown_link( + title_for(grandchild / "README.md"), + index_path_for(grandchild / "README.md"), + current_output, + ) + for grandchild in child_readme_dirs(child_dir) + ] + if grandchild_links: + lines.append(f"- {child_link}: {', '.join(grandchild_links)}") + else: + lines.append(f"- {child_link}") + + return "\n".join(lines) + "\n" + + +def example_index_entries(directory: Path, current_output: Path) -> list[str]: + entries: list[str] = [] + readme = directory / "README.md" + if readme in EXAMPLE_READMES and example_child_dirs(directory): + entries.append(relpath_from_output(output_path_for(readme).with_suffix(""), current_output)) + + entries.extend( + relpath_from_output(example_index_path_for_dir(child).with_suffix(""), current_output) + for child in example_child_dirs(directory) + ) + return entries + + +def example_contents_block(directory: Path, current_output: Path) -> str: + readme = directory / "README.md" + child_dirs = example_child_dirs(directory) + lines: list[str] = [] + + if readme in EXAMPLE_READMES and child_dirs: + lines.extend( + [ + "## Pages", + "", + f"- {markdown_link('Overview', output_path_for(readme), current_output)}", + ] + ) + + if child_dirs: + if lines: + lines.append("") + lines.extend(["## Sections", ""]) + for child in child_dirs: + child_link = markdown_link( + title_for_dir(child, EXAMPLES_DIR, "Examples"), + example_index_path_for_dir(child), + current_output, + ) + grandchild_links = [ + markdown_link( + title_for_dir(grandchild, EXAMPLES_DIR, "Examples"), + example_index_path_for_dir(grandchild), + current_output, + ) + for grandchild in example_child_dirs(child) + ] + if grandchild_links: + lines.append(f"- {child_link}: {', '.join(grandchild_links)}") + else: + lines.append(f"- {child_link}") + + return "\n".join(lines) + "\n" + + +def generated_page_title(source: Path) -> str: + title = title_for(source) + if is_model_container(source) or is_example_container(source): + return "Overview" + return title + + +def generate_page(source: Path) -> None: + out = output_path_for(source) + source_dir = source.parent + title = generated_page_title(source) + body = source.read_text(encoding="utf-8") + body = normalize_markdown(body, source, title) + body = rewrite_markdown_links(body, source_dir, out) + body = rewrite_html_paths(body, source_dir, out) + + out.parent.mkdir(parents=True, exist_ok=True) + rel_source = source.relative_to(REPO_ROOT).as_posix() + out.write_text( + f"# {title}\n\n" + f"_Source: `{rel_source}`_\n\n" + f"{body.rstrip()}\n", + encoding="utf-8", + ) + + +def generate_model_index_page(source: Path) -> None: + if not is_model_container(source): + return + + out = index_path_for(source) + out.parent.mkdir(parents=True, exist_ok=True) + entries = tree_index_entries(source, out) + out.write_text( + f"# {title_for(source)}\n\n" + f"{toctree_block(entries, hidden=True)}\n" + f"{model_contents_block(source, out)}", + encoding="utf-8", + ) + + +def generate_example_index_page(directory: Path) -> None: + child_dirs = example_child_dirs(directory) + readme = directory / "README.md" + if not child_dirs and readme in EXAMPLE_READMES: + return + + out = example_index_path_for_dir(directory) + out.parent.mkdir(parents=True, exist_ok=True) + entries = example_index_entries(directory, out) + out.write_text( + f"# {title_for_dir(directory, EXAMPLES_DIR, 'Examples')}\n\n" + f"{toctree_block(entries, hidden=True)}\n" + f"{example_contents_block(directory, out)}", + encoding="utf-8", + ) + + +def main() -> None: + for generated_dir in (GENERAL_OUT_DIR, MODEL_OUT_DIR, EXAMPLE_OUT_DIR): + if generated_dir.exists(): + shutil.rmtree(generated_dir) + generated_dir.mkdir(parents=True, exist_ok=True) + + project_sources = [page.source for page in PROJECT_DOCS] + model_sources = [COMMON_MATH, PHASOR_INPUT_FORMAT] + model_sources.extend(sorted(MODEL_DIR.glob("**/README.md"))) + example_sources = sorted(EXAMPLE_READMES) + + for source in project_sources + model_sources + example_sources: + generate_page(source) + for source in model_sources: + generate_model_index_page(source) + for directory in sorted(EXAMPLE_DOC_DIRS): + generate_example_index_page(directory) + + +if __name__ == "__main__": + main() diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..ab487c268 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,21 @@ +# GridKit Documentation + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +generated/root-readme +project/index +examples/generated/index +models/index +api +``` + +## Sections + +- [GridKit Overview](generated/root-readme.md) +- [Project Documentation](project/index.md) +- [Examples](examples/generated/index.md) +- [Model Documentation](models/index.md) +- [API Reference](api.md) diff --git a/docs/models/index.md b/docs/models/index.md new file mode 100644 index 000000000..d2adaed7a --- /dev/null +++ b/docs/models/index.md @@ -0,0 +1,21 @@ +# Model Documentation + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +generated/common-math +generated/emt/index +generated/phasor-dynamics/index +generated/power-electronics/index +generated/power-flow/index +``` + +## Sections + +- [CommonMath](generated/common-math.md) +- [EMT](generated/emt/index.md) +- [PhasorDynamics](generated/phasor-dynamics/index.md) +- [PowerElectronics](generated/power-electronics/index.md) +- [PowerFlow](generated/power-flow/index.md) diff --git a/docs/project/index.md b/docs/project/index.md new file mode 100644 index 000000000..85263d229 --- /dev/null +++ b/docs/project/index.md @@ -0,0 +1,23 @@ +# Project Documentation + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +../generated/install +../generated/contributing +../generated/changelog +../generated/documentation-build +../generated/buildsystem +../generated/phasor-dynamics-application +``` + +## Sections + +- [Installation](../generated/install.md) +- [Contributing](../generated/contributing.md) +- [Changelog](../generated/changelog.md) +- [Documentation Build](../generated/documentation-build.md) +- [Buildsystem](../generated/buildsystem.md) +- [Phasor Dynamics Application](../generated/phasor-dynamics-application.md) diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..0f633b43e --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +sphinx +breathe +myst-parser +sphinx-rtd-theme diff --git a/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md b/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md index 475c30031..0d7cf94e2 100644 --- a/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md +++ b/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md @@ -9,8 +9,8 @@ monitored machine states are compared against `GENSAL.ref.csv`. ## Trajectory Comparison -![GENSAL validation trajectory](GENSAL.png) +![GENSAL validation trajectory](Gensal_validation.png) ## Error -![GENSAL validation error](GENSAL_ERROR.png) +![GENSAL validation error](Gensal_validation_error.png) From 19ed60ca93df2085f81e5470484fa75604744d4e Mon Sep 17 00:00:00 2001 From: lukelowry Date: Mon, 8 Jun 2026 00:25:17 -0500 Subject: [PATCH 2/6] Reorganize RTD structure --- .gitignore | 1 + CHANGELOG.md | 1 + docs/Doxyfile | 7 +- docs/api.md | 34 +++- docs/applications/index.md | 16 ++ .../phasor-dynamics/contingency-analysis.md | 14 ++ .../phasor-dynamics/dynamic-simulation.md | 12 ++ docs/applications/phasor-dynamics/index.md | 20 +++ docs/conf.py | 43 ++++- docs/{project => development}/index.md | 14 +- docs/generate_model_docs.py | 155 +++++++++--------- docs/index.md | 20 +-- docs/models/index.md | 2 +- docs/requirements.txt | 1 + 14 files changed, 234 insertions(+), 106 deletions(-) create mode 100644 docs/applications/index.md create mode 100644 docs/applications/phasor-dynamics/contingency-analysis.md create mode 100644 docs/applications/phasor-dynamics/dynamic-simulation.md create mode 100644 docs/applications/phasor-dynamics/index.md rename docs/{project => development}/index.md (64%) diff --git a/.gitignore b/.gitignore index 3860c865d..5689b87f9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ build/ doxygen-docs/ docs/_build/ docs/api/generated/ +docs/api/reference/ docs/examples/generated/ docs/generated/ docs/models/generated/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 066786e09..612211975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Added 3, 10, 37, and 39 bus test cases. - Updated documentation. +- Added support for Read the Docs - Added JSON parsing. - Automatic differentiation with enzyme (w.r.t. internal and external variables). - Added PR and issue templates. diff --git a/docs/Doxyfile b/docs/Doxyfile index 98f1a96ec..8564e075e 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,14 +1,17 @@ PROJECT_NAME = "GridKit" OUTPUT_DIRECTORY = . -INPUT = ../GridKit ../README.md +INPUT = ../GridKit RECURSIVE = YES -USE_MDFILE_AS_MAINPAGE = ../README.md +EXCLUDE_PATTERNS = *.md +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = .. GENERATE_HTML = NO GENERATE_LATEX = NO GENERATE_XML = YES XML_OUTPUT = xml +XML_PROGRAMLISTING = NO EXTRACT_ALL = YES QUIET = YES diff --git a/docs/api.md b/docs/api.md index 5218706cd..39b30249b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,5 +1,35 @@ # API Reference -```{doxygenindex} -:project: GridKit +```{toctree} +:maxdepth: 1 +:titlesonly: +:hidden: + +GridKit Core +Solvers +Phasor Dynamics +Power Electronics +Power Flow Data +Linear Algebra +Math Utilities +Model Utilities +Utilities +Testing ``` + +The API reference is organized by the primary GridKit namespaces. Lower-level +class, struct, enum, function, and file pages are generated by Exhale and linked +from these namespace pages. + +## Main Areas + +- [GridKit core](api/reference/namespace_GridKit.rst) +- [Solvers](api/reference/namespace_AnalysisManager.rst) +- [GridKit::PhasorDynamics](api/reference/namespace_GridKit__PhasorDynamics.rst) +- [GridKit::PowerElectronics](api/reference/namespace_GridKit__PowerElectronics.rst) +- [GridKit::PowerFlowData](api/reference/namespace_GridKit__PowerFlowData.rst) +- [GridKit::LinearAlgebra](api/reference/namespace_GridKit__LinearAlgebra.rst) +- [GridKit::Model](api/reference/namespace_GridKit__Model.rst) +- [GridKit::Math](api/reference/namespace_GridKit__Math.rst) +- [GridKit::Utilities](api/reference/namespace_GridKit__Utilities.rst) +- [GridKit::Testing](api/reference/namespace_GridKit__Testing.rst) diff --git a/docs/applications/index.md b/docs/applications/index.md new file mode 100644 index 000000000..a2344733b --- /dev/null +++ b/docs/applications/index.md @@ -0,0 +1,16 @@ +# Applications + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +phasor-dynamics/index +``` + +## Sections + +- [PhasorDynamics](phasor-dynamics/index.md) +- PowerElectronics: none +- PowerFlow: none +- EMT: none diff --git a/docs/applications/phasor-dynamics/contingency-analysis.md b/docs/applications/phasor-dynamics/contingency-analysis.md new file mode 100644 index 000000000..ea07ba64a --- /dev/null +++ b/docs/applications/phasor-dynamics/contingency-analysis.md @@ -0,0 +1,14 @@ +# ContingencyAnalysis + +`ContingencyAnalysis` runs a PhasorDynamics study for each configured bus fault. +When OpenMP is available, the fault studies run through the OpenMP path; otherwise +the application uses asynchronous tasks. + +## Inputs + +- [Application Input Format](../../generated/application-input-format.md) +- [System Model Input Format](../../models/generated/phasor-dynamics/input-format.md) + +## Source + +- `application/PhasorDynamics/ContingencyAnalysis.cpp` diff --git a/docs/applications/phasor-dynamics/dynamic-simulation.md b/docs/applications/phasor-dynamics/dynamic-simulation.md new file mode 100644 index 000000000..c7d7491e5 --- /dev/null +++ b/docs/applications/phasor-dynamics/dynamic-simulation.md @@ -0,0 +1,12 @@ +# DynamicSimulation + +`DynamicSimulation` runs one PhasorDynamics study from a solver JSON file. + +## Inputs + +- [Application Input Format](../../generated/application-input-format.md) +- [System Model Input Format](../../models/generated/phasor-dynamics/input-format.md) + +## Source + +- `application/PhasorDynamics/DynamicSimulation.cpp` diff --git a/docs/applications/phasor-dynamics/index.md b/docs/applications/phasor-dynamics/index.md new file mode 100644 index 000000000..ee28f46ef --- /dev/null +++ b/docs/applications/phasor-dynamics/index.md @@ -0,0 +1,20 @@ +# PhasorDynamics + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +dynamic-simulation +contingency-analysis +../../generated/application-input-format +``` + +## Applications + +- [DynamicSimulation](dynamic-simulation.md) +- [ContingencyAnalysis](contingency-analysis.md) + +## Shared Reference + +- [Application Input Format](../../generated/application-input-format.md) diff --git a/docs/conf.py b/docs/conf.py index fcd5342e8..e40f307a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,15 +1,39 @@ from pathlib import Path +from exhale import utils as exhale_utils + project = "GridKit" author = "GridKit Developers" docs_dir = Path(__file__).parent.resolve() -extensions = ["breathe", "myst_parser", "sphinx.ext.graphviz"] +extensions = ["breathe", "exhale", "myst_parser", "sphinx.ext.graphviz"] breathe_projects = {"GridKit": str(docs_dir / "xml")} breathe_default_project = "GridKit" + +def exhale_specs(kind): + if kind in {"class", "struct"}: + return [":members:"] + return [] + + +exhale_args = { + "containmentFolder": "./api/reference", + "rootFileName": "EXCLUDE", + "doxygenStripFromPath": str(docs_dir.parent), + "createTreeView": False, + "customSpecificationsMapping": exhale_utils.makeCustomSpecificationsMapping( + exhale_specs + ), + "contentsDirectives": False, + "fullToctreeMaxDepth": 1, + "pageLevelConfigMeta": ":orphan:", +} + +primary_domain = "cpp" + html_theme = "sphinx_rtd_theme" html_extra_path = ["Figures"] html_theme_options = { @@ -30,4 +54,19 @@ ] myst_heading_anchors = 5 -exclude_patterns = ["_build", "xml", "README.md", "superpowers/**"] +exclude_patterns = [ + "_build", + "api/generated/**", + "xml", + "README.md", + "superpowers/**", +] + + +def rewrite_included_root_readme_links(app, relative_path, parent_docname, source): + if Path(str(relative_path)).name == "README.md": + source[0] = source[0].replace("](INSTALL.md", "](generated/install.md") + + +def setup(app): + app.connect("include-read", rewrite_included_root_readme_links) diff --git a/docs/project/index.md b/docs/development/index.md similarity index 64% rename from docs/project/index.md rename to docs/development/index.md index 85263d229..12d13f933 100644 --- a/docs/project/index.md +++ b/docs/development/index.md @@ -1,23 +1,19 @@ -# Project Documentation +# Development ```{toctree} :maxdepth: 2 :titlesonly: :hidden: -../generated/install ../generated/contributing -../generated/changelog -../generated/documentation-build ../generated/buildsystem -../generated/phasor-dynamics-application +../generated/documentation-build +../generated/changelog ``` ## Sections -- [Installation](../generated/install.md) - [Contributing](../generated/contributing.md) -- [Changelog](../generated/changelog.md) -- [Documentation Build](../generated/documentation-build.md) - [Buildsystem](../generated/buildsystem.md) -- [Phasor Dynamics Application](../generated/phasor-dynamics-application.md) +- [Documentation Build](../generated/documentation-build.md) +- [Changelog](../generated/changelog.md) diff --git a/docs/generate_model_docs.py b/docs/generate_model_docs.py index b3e34c9c4..e81462b52 100644 --- a/docs/generate_model_docs.py +++ b/docs/generate_model_docs.py @@ -26,12 +26,21 @@ class MarkdownPage: GENERAL_OUT_DIR = DOCS_DIR / "generated" MODEL_OUT_DIR = DOCS_DIR / "models" / "generated" EXAMPLE_OUT_DIR = DOCS_DIR / "examples" / "generated" +ROOT_INDEX = DOCS_DIR / "index.md" +ROOT_TOCTREE_ENTRIES = ( + "generated/install", + "applications/index", + "models/index", + "examples/generated/index", + "api", + "development/index", +) COMMON_MATH = REPO_ROOT / "GridKit" / "CommonMath.md" PHASOR_INPUT_FORMAT = MODEL_DIR / "PhasorDynamics" / "INPUT_FORMAT.md" PROJECT_DOCS = ( - MarkdownPage(REPO_ROOT / "README.md", GENERAL_OUT_DIR / "root-readme.md", "GridKit"), + MarkdownPage(REPO_ROOT / "README.md", ROOT_INDEX, "GridKit"), MarkdownPage(REPO_ROOT / "INSTALL.md", GENERAL_OUT_DIR / "install.md", "Installation"), MarkdownPage( REPO_ROOT / "CONTRIBUTING.md", @@ -55,8 +64,8 @@ class MarkdownPage: ), MarkdownPage( REPO_ROOT / "application" / "PhasorDynamics" / "README.md", - GENERAL_OUT_DIR / "phasor-dynamics-application.md", - "Phasor Dynamics Application", + GENERAL_OUT_DIR / "application-input-format.md", + "Application Input Format", ), ) @@ -155,8 +164,6 @@ def example_index_path_for_dir(directory: Path) -> Path: def example_readme_output_path(path: Path) -> Path: - if is_example_container(path): - return EXAMPLE_OUT_DIR.joinpath(*example_parts_for_dir(path.parent), "overview.md") return example_index_path_for_dir(path.parent) @@ -167,8 +174,6 @@ def output_path_for(path: Path) -> Path: return MODEL_OUT_DIR / "common-math.md" if path == PHASOR_INPUT_FORMAT: return MODEL_OUT_DIR / "phasor-dynamics" / "input-format.md" - if is_model_container(path): - return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "overview.md") if is_model_readme(path): return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "index.md") if is_example_readme(path): @@ -299,11 +304,43 @@ def link_repl(match: re.Match[str]) -> str: def rewrite_html_paths(text: str, source_dir: Path, current_output: Path) -> str: - def repl(match: re.Match[str]) -> str: - prefix, target, suffix = match.groups() - return f"{prefix}{rewrite_asset_target(source_dir, target, current_output)}{suffix}" + text = re.sub( + r"(?im)^\s*]*\balign=[\"']center[\"'][^>]*>\s*$", + "", + text, + ) + text = re.sub(r"(?im)^\s*\s*$", "", text) - return re.sub(r"(]*\bsrc=[\"'])([^\"']+)([\"'])", repl, text) + def html_attr(attrs: str, name: str) -> str | None: + match = re.search(rf"\b{name}\s*=\s*([\"'])(.*?)\1", attrs, re.IGNORECASE) + return match.group(2) if match else None + + def repl(match: re.Match[str]) -> str: + before, target, after = match.groups() + attrs = f"{before} {after}" + target = rewrite_asset_target(source_dir, target, current_output) + options = [] + + alt = html_attr(attrs, "alt") + if alt: + options.append(f":alt: {alt}") + + align = html_attr(attrs, "align") + if align in {"left", "center", "right"}: + options.append(f":align: {align}") + + option_block = "\n".join(options) + if option_block: + option_block += "\n" + + return f"\n```{{image}} {target}\n{option_block}```\n" + + return re.sub( + r"]*)\bsrc=[\"']([^\"']+)[\"']([^>]*)>", + repl, + text, + flags=re.IGNORECASE, + ) def normalize_heading_levels(text: str) -> str: @@ -380,7 +417,7 @@ def markdown_link(title: str, path: Path, current_output: Path) -> str: def tree_index_entries(source: Path, current_output: Path) -> list[str]: - entries = [relpath_from_output(output_path_for(source).with_suffix(""), current_output)] + entries = [] if source == MODEL_DIR / "PhasorDynamics" / "README.md": entries.append( relpath_from_output( @@ -399,52 +436,8 @@ def tree_index_entries(source: Path, current_output: Path) -> list[str]: return entries -def overview_entries_for(source: Path, current_output: Path) -> list[str]: - entries = [ - markdown_link("Overview", output_path_for(source), current_output), - ] - if source == MODEL_DIR / "PhasorDynamics" / "README.md": - entries.append( - markdown_link("Input Format", output_path_for(PHASOR_INPUT_FORMAT), current_output) - ) - return entries - - -def model_contents_block(source: Path, current_output: Path) -> str: - child_dirs = child_readme_dirs(source.parent) - lines = ["## Pages", ""] - lines.extend(f"- {entry}" for entry in overview_entries_for(source, current_output)) - - if child_dirs: - lines.extend(["", "## Sections", ""]) - for child_dir in child_dirs: - child_readme = child_dir / "README.md" - child_link = markdown_link( - title_for(child_readme), - index_path_for(child_readme), - current_output, - ) - grandchild_links = [ - markdown_link( - title_for(grandchild / "README.md"), - index_path_for(grandchild / "README.md"), - current_output, - ) - for grandchild in child_readme_dirs(child_dir) - ] - if grandchild_links: - lines.append(f"- {child_link}: {', '.join(grandchild_links)}") - else: - lines.append(f"- {child_link}") - - return "\n".join(lines) + "\n" - - def example_index_entries(directory: Path, current_output: Path) -> list[str]: entries: list[str] = [] - readme = directory / "README.md" - if readme in EXAMPLE_READMES and example_child_dirs(directory): - entries.append(relpath_from_output(output_path_for(readme).with_suffix(""), current_output)) entries.extend( relpath_from_output(example_index_path_for_dir(child).with_suffix(""), current_output) @@ -494,14 +487,34 @@ def example_contents_block(directory: Path, current_output: Path) -> str: def generated_page_title(source: Path) -> str: - title = title_for(source) - if is_model_container(source) or is_example_container(source): - return "Overview" - return title + return title_for(source) + + +def generated_page_toctree(source: Path, current_output: Path) -> str: + if source == REPO_ROOT / "README.md": + return toctree_block(list(ROOT_TOCTREE_ENTRIES), hidden=True, maxdepth=4) + if is_model_container(source): + entries = tree_index_entries(source, current_output) + return f"{toctree_block(entries, hidden=True)}\n" if entries else "" + if is_example_container(source): + entries = example_index_entries(source.parent, current_output) + return f"{toctree_block(entries, hidden=True)}\n" if entries else "" + return "" def generate_page(source: Path) -> None: out = output_path_for(source) + if source == REPO_ROOT / "README.md": + out.write_text( + "# GridKit\n\n" + f"{generated_page_toctree(source, out)}" + "```{include} ../README.md\n" + ":relative-images:\n" + "```\n", + encoding="utf-8", + ) + return + source_dir = source.parent title = generated_page_title(source) body = source.read_text(encoding="utf-8") @@ -513,31 +526,19 @@ def generate_page(source: Path) -> None: rel_source = source.relative_to(REPO_ROOT).as_posix() out.write_text( f"# {title}\n\n" + f"{generated_page_toctree(source, out)}" f"_Source: `{rel_source}`_\n\n" f"{body.rstrip()}\n", encoding="utf-8", ) -def generate_model_index_page(source: Path) -> None: - if not is_model_container(source): - return - - out = index_path_for(source) - out.parent.mkdir(parents=True, exist_ok=True) - entries = tree_index_entries(source, out) - out.write_text( - f"# {title_for(source)}\n\n" - f"{toctree_block(entries, hidden=True)}\n" - f"{model_contents_block(source, out)}", - encoding="utf-8", - ) - - def generate_example_index_page(directory: Path) -> None: child_dirs = example_child_dirs(directory) readme = directory / "README.md" - if not child_dirs and readme in EXAMPLE_READMES: + if readme in EXAMPLE_READMES: + return + if not child_dirs: return out = example_index_path_for_dir(directory) @@ -564,8 +565,6 @@ def main() -> None: for source in project_sources + model_sources + example_sources: generate_page(source) - for source in model_sources: - generate_model_index_page(source) for directory in sorted(EXAMPLE_DOC_DIRS): generate_example_index_page(directory) diff --git a/docs/index.md b/docs/index.md index ab487c268..bc4d0d2a8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,21 +1,17 @@ -# GridKit Documentation +# GridKit ```{toctree} :maxdepth: 4 :titlesonly: :hidden: -generated/root-readme -project/index -examples/generated/index +generated/install +applications/index models/index +examples/generated/index api +development/index +``` +```{include} ../README.md +:relative-images: ``` - -## Sections - -- [GridKit Overview](generated/root-readme.md) -- [Project Documentation](project/index.md) -- [Examples](examples/generated/index.md) -- [Model Documentation](models/index.md) -- [API Reference](api.md) diff --git a/docs/models/index.md b/docs/models/index.md index d2adaed7a..8ac0a888d 100644 --- a/docs/models/index.md +++ b/docs/models/index.md @@ -1,4 +1,4 @@ -# Model Documentation +# Models ```{toctree} :maxdepth: 4 diff --git a/docs/requirements.txt b/docs/requirements.txt index 0f633b43e..7f0c2b7ed 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ sphinx breathe +exhale myst-parser sphinx-rtd-theme From 79f578c13aef896878b2906b1f53f712175f4cc8 Mon Sep 17 00:00:00 2001 From: lukelowry Date: Mon, 8 Jun 2026 01:15:06 -0500 Subject: [PATCH 3/6] Trim uneeded docs --- docs/applications/index.md | 13 +- .../phasor-dynamics/contingency-analysis.md | 14 - .../phasor-dynamics/dynamic-simulation.md | 12 - docs/applications/phasor-dynamics/index.md | 20 - docs/conf.py | 9 - docs/generate_model_docs.py | 693 +++++++----------- docs/index.md | 4 +- 7 files changed, 261 insertions(+), 504 deletions(-) delete mode 100644 docs/applications/phasor-dynamics/contingency-analysis.md delete mode 100644 docs/applications/phasor-dynamics/dynamic-simulation.md delete mode 100644 docs/applications/phasor-dynamics/index.md diff --git a/docs/applications/index.md b/docs/applications/index.md index a2344733b..14211ecb9 100644 --- a/docs/applications/index.md +++ b/docs/applications/index.md @@ -1,16 +1,15 @@ # Applications ```{toctree} -:maxdepth: 4 +:maxdepth: 2 :titlesonly: :hidden: -phasor-dynamics/index +../generated/application-input-format ``` -## Sections +## PhasorDynamics -- [PhasorDynamics](phasor-dynamics/index.md) -- PowerElectronics: none -- PowerFlow: none -- EMT: none +- [Application Input Format](../generated/application-input-format.md) +- `application/PhasorDynamics/DynamicSimulation.cpp` +- `application/PhasorDynamics/ContingencyAnalysis.cpp` diff --git a/docs/applications/phasor-dynamics/contingency-analysis.md b/docs/applications/phasor-dynamics/contingency-analysis.md deleted file mode 100644 index ea07ba64a..000000000 --- a/docs/applications/phasor-dynamics/contingency-analysis.md +++ /dev/null @@ -1,14 +0,0 @@ -# ContingencyAnalysis - -`ContingencyAnalysis` runs a PhasorDynamics study for each configured bus fault. -When OpenMP is available, the fault studies run through the OpenMP path; otherwise -the application uses asynchronous tasks. - -## Inputs - -- [Application Input Format](../../generated/application-input-format.md) -- [System Model Input Format](../../models/generated/phasor-dynamics/input-format.md) - -## Source - -- `application/PhasorDynamics/ContingencyAnalysis.cpp` diff --git a/docs/applications/phasor-dynamics/dynamic-simulation.md b/docs/applications/phasor-dynamics/dynamic-simulation.md deleted file mode 100644 index c7d7491e5..000000000 --- a/docs/applications/phasor-dynamics/dynamic-simulation.md +++ /dev/null @@ -1,12 +0,0 @@ -# DynamicSimulation - -`DynamicSimulation` runs one PhasorDynamics study from a solver JSON file. - -## Inputs - -- [Application Input Format](../../generated/application-input-format.md) -- [System Model Input Format](../../models/generated/phasor-dynamics/input-format.md) - -## Source - -- `application/PhasorDynamics/DynamicSimulation.cpp` diff --git a/docs/applications/phasor-dynamics/index.md b/docs/applications/phasor-dynamics/index.md deleted file mode 100644 index ee28f46ef..000000000 --- a/docs/applications/phasor-dynamics/index.md +++ /dev/null @@ -1,20 +0,0 @@ -# PhasorDynamics - -```{toctree} -:maxdepth: 2 -:titlesonly: -:hidden: - -dynamic-simulation -contingency-analysis -../../generated/application-input-format -``` - -## Applications - -- [DynamicSimulation](dynamic-simulation.md) -- [ContingencyAnalysis](contingency-analysis.md) - -## Shared Reference - -- [Application Input Format](../../generated/application-input-format.md) diff --git a/docs/conf.py b/docs/conf.py index e40f307a7..3bf0b78d5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -61,12 +61,3 @@ def exhale_specs(kind): "README.md", "superpowers/**", ] - - -def rewrite_included_root_readme_links(app, relative_path, parent_docname, source): - if Path(str(relative_path)).name == "README.md": - source[0] = source[0].replace("](INSTALL.md", "](generated/install.md") - - -def setup(app): - app.connect("include-read", rewrite_included_root_readme_links) diff --git a/docs/generate_model_docs.py b/docs/generate_model_docs.py index e81462b52..555155b92 100644 --- a/docs/generate_model_docs.py +++ b/docs/generate_model_docs.py @@ -1,196 +1,161 @@ #!/usr/bin/env python3 -"""Generate Sphinx pages from GridKit Markdown documentation.""" +"""Generate Sphinx pages from selected GridKit Markdown files.""" from __future__ import annotations import os import re import shutil -from dataclasses import dataclass +import subprocess from pathlib import Path -@dataclass(frozen=True) -class MarkdownPage: - source: Path - output: Path - title: str - - -REPO_ROOT = Path(__file__).resolve().parents[1] -DOCS_DIR = REPO_ROOT / "docs" -MODEL_DIR = REPO_ROOT / "GridKit" / "Model" -EXAMPLES_DIR = REPO_ROOT / "examples" -GITHUB_TREE_URL = "https://github.com/ORNL/GridKit/tree/develop" - -GENERAL_OUT_DIR = DOCS_DIR / "generated" -MODEL_OUT_DIR = DOCS_DIR / "models" / "generated" -EXAMPLE_OUT_DIR = DOCS_DIR / "examples" / "generated" -ROOT_INDEX = DOCS_DIR / "index.md" -ROOT_TOCTREE_ENTRIES = ( - "generated/install", - "applications/index", - "models/index", - "examples/generated/index", - "api", - "development/index", -) - -COMMON_MATH = REPO_ROOT / "GridKit" / "CommonMath.md" -PHASOR_INPUT_FORMAT = MODEL_DIR / "PhasorDynamics" / "INPUT_FORMAT.md" - -PROJECT_DOCS = ( - MarkdownPage(REPO_ROOT / "README.md", ROOT_INDEX, "GridKit"), - MarkdownPage(REPO_ROOT / "INSTALL.md", GENERAL_OUT_DIR / "install.md", "Installation"), - MarkdownPage( - REPO_ROOT / "CONTRIBUTING.md", - GENERAL_OUT_DIR / "contributing.md", - "Contributing", - ), - MarkdownPage( - REPO_ROOT / "CHANGELOG.md", - GENERAL_OUT_DIR / "changelog.md", - "Changelog", - ), - MarkdownPage( - DOCS_DIR / "README.md", - GENERAL_OUT_DIR / "documentation-build.md", - "Documentation Build", - ), - MarkdownPage( - REPO_ROOT / "buildsystem" / "README.md", - GENERAL_OUT_DIR / "buildsystem.md", - "Buildsystem", - ), - MarkdownPage( - REPO_ROOT / "application" / "PhasorDynamics" / "README.md", - GENERAL_OUT_DIR / "application-input-format.md", +ROOT = Path(__file__).resolve().parents[1] +DOCS = ROOT / "docs" +MODEL = ROOT / "GridKit" / "Model" +EXAMPLES = ROOT / "examples" +GITHUB_REPO = "https://github.com/ORNL/GridKit" + +GENERATED = DOCS / "generated" +GENERATED_MODELS = DOCS / "models" / "generated" +GENERATED_EXAMPLES = DOCS / "examples" / "generated" + +COMMON_MATH = ROOT / "GridKit" / "CommonMath.md" +PHASOR_INPUT_FORMAT = MODEL / "PhasorDynamics" / "INPUT_FORMAT.md" + +PROJECT_PAGES = { + ROOT / "README.md": (GENERATED / "readme.md", "GridKit"), + ROOT / "INSTALL.md": (GENERATED / "install.md", "Installation"), + ROOT / "CONTRIBUTING.md": (GENERATED / "contributing.md", "Contributing"), + ROOT / "CHANGELOG.md": (GENERATED / "changelog.md", "Changelog"), + DOCS / "README.md": (GENERATED / "documentation-build.md", "Documentation Build"), + ROOT / "buildsystem" / "README.md": (GENERATED / "buildsystem.md", "Buildsystem"), + ROOT / "application" / "PhasorDynamics" / "README.md": ( + GENERATED / "application-input-format.md", "Application Input Format", ), -) - -PROJECT_DOC_BY_SOURCE = {page.source: page for page in PROJECT_DOCS} -EXAMPLE_READMES = frozenset(EXAMPLES_DIR.glob("**/README.md")) +} +MODEL_READMES = tuple(path.resolve() for path in sorted(MODEL.glob("**/README.md"))) +EXAMPLE_READMES = tuple(path.resolve() for path in sorted(EXAMPLES.glob("**/README.md"))) -def example_doc_dirs() -> set[Path]: - dirs = {EXAMPLES_DIR} - for readme in EXAMPLE_READMES: - directory = readme.parent - while directory != EXAMPLES_DIR.parent: - dirs.add(directory) - if directory == EXAMPLES_DIR: - break - directory = directory.parent - return dirs - -EXAMPLE_DOC_DIRS = example_doc_dirs() - - -def slugify(value: str) -> str: - value = value.replace("README.md", "").strip("/") - value = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", value) - value = value.replace("/", "-").replace("_", "-") - value = re.sub(r"[^A-Za-z0-9.-]+", "-", value) - value = re.sub(r"-+", "-", value) - return value.strip("-").lower() - - -def title_for_dir(directory: Path, root: Path, root_title: str) -> str: - if directory == root: - return root_title - return directory.name - - -def title_for(path: Path) -> str: - if path in PROJECT_DOC_BY_SOURCE: - return PROJECT_DOC_BY_SOURCE[path].title - if path == COMMON_MATH: - return "CommonMath" - if path == PHASOR_INPUT_FORMAT: - return "Input Format" - if is_example_readme(path): - return title_for_dir(path.parent, EXAMPLES_DIR, "Examples") - return path.parent.name +def git_output(*args: str) -> str: + try: + return subprocess.check_output( + ["git", *args], + cwd=ROOT, + stderr=subprocess.DEVNULL, + text=True, + ).strip() + except (OSError, subprocess.CalledProcessError): + return "" -def is_readme_under(path: Path, root: Path) -> bool: - return path.name == "README.md" and root in path.parents +def source_ref() -> str: + if os.environ.get("READTHEDOCS_VERSION_TYPE") == "external": + if ref := os.environ.get("READTHEDOCS_GIT_COMMIT_HASH"): + return ref + if ref := os.environ.get("READTHEDOCS_GIT_IDENTIFIER"): + return ref + if ref := git_output("rev-parse", "--abbrev-ref", "HEAD"): + if ref != "HEAD": + return ref + return git_output("rev-parse", "HEAD") or "HEAD" -def model_parts_for_dir(model_dir: Path) -> list[str]: - return [slugify(part) for part in model_dir.relative_to(MODEL_DIR).parts] +SOURCE_REF = source_ref() -def example_parts_for_dir(example_dir: Path) -> list[str]: - return [slugify(part) for part in example_dir.relative_to(EXAMPLES_DIR).parts] +def slug(text: str) -> str: + text = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", text) + text = re.sub(r"[^A-Za-z0-9.-]+", "-", text.replace("_", "-").replace("/", "-")) + return re.sub(r"-+", "-", text).strip("-").lower() -def child_readme_dirs(model_dir: Path) -> list[Path]: - return sorted( - child - for child in model_dir.iterdir() - if child.is_dir() and (child / "README.md").exists() - ) +def relative(target: Path, page: Path) -> str: + return Path(os.path.relpath(target, page.parent)).as_posix() -def example_child_dirs(directory: Path) -> list[Path]: - return sorted( - child - for child in EXAMPLE_DOC_DIRS - if child.parent == directory and child != directory - ) +def model_page(readme: Path) -> Path: + parts = [slug(part) for part in readme.parent.relative_to(MODEL).parts] + return GENERATED_MODELS.joinpath(*parts, "index.md") -def is_model_readme(path: Path) -> bool: - return is_readme_under(path, MODEL_DIR) +def example_page(directory: Path) -> Path: + parts = [slug(part) for part in directory.relative_to(EXAMPLES).parts] + return GENERATED_EXAMPLES.joinpath(*parts, "index.md") -def is_example_readme(path: Path) -> bool: - return path in EXAMPLE_READMES +SOURCE_TO_PAGE = {source.resolve(): output for source, (output, _) in PROJECT_PAGES.items()} +SOURCE_TO_PAGE[COMMON_MATH.resolve()] = GENERATED_MODELS / "common-math.md" +SOURCE_TO_PAGE[PHASOR_INPUT_FORMAT.resolve()] = ( + GENERATED_MODELS / "phasor-dynamics" / "input-format.md" +) +SOURCE_TO_PAGE.update({readme: model_page(readme) for readme in MODEL_READMES}) +SOURCE_TO_PAGE.update({readme: example_page(readme.parent) for readme in EXAMPLE_READMES}) -def is_model_container(path: Path) -> bool: - return is_model_readme(path) and bool(child_readme_dirs(path.parent)) +def example_dirs() -> set[Path]: + dirs = {EXAMPLES.resolve()} + for readme in EXAMPLE_READMES: + directory = readme.parent + while directory != EXAMPLES.parent: + dirs.add(directory.resolve()) + if directory == EXAMPLES: + break + directory = directory.parent + return dirs -def is_example_container(path: Path) -> bool: - return is_example_readme(path) and bool(example_child_dirs(path.parent)) +EXAMPLE_DOC_DIRS = example_dirs() -def example_index_path_for_dir(directory: Path) -> Path: - return EXAMPLE_OUT_DIR.joinpath(*example_parts_for_dir(directory), "index.md") +def title_for(source: Path) -> str: + if source in PROJECT_PAGES: + return PROJECT_PAGES[source][1] + if source == COMMON_MATH: + return "CommonMath" + if source == PHASOR_INPUT_FORMAT: + return "Input Format" + if source in EXAMPLE_READMES and source.parent == EXAMPLES: + return "Examples" + return source.parent.name -def example_readme_output_path(path: Path) -> Path: - return example_index_path_for_dir(path.parent) +def toctree(entries: list[str], maxdepth: int = 2) -> str: + return ( + "```{toctree}\n" + f":maxdepth: {maxdepth}\n" + ":titlesonly:\n:hidden:\n\n" + + "\n".join(entries) + + "\n```\n" + ) -def output_path_for(path: Path) -> Path: - if path in PROJECT_DOC_BY_SOURCE: - return PROJECT_DOC_BY_SOURCE[path].output - if path == COMMON_MATH: - return MODEL_OUT_DIR / "common-math.md" - if path == PHASOR_INPUT_FORMAT: - return MODEL_OUT_DIR / "phasor-dynamics" / "input-format.md" - if is_model_readme(path): - return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "index.md") - if is_example_readme(path): - return example_readme_output_path(path) - raise ValueError(f"Unhandled documentation source: {path}") +def children_of(directory: Path, readmes: tuple[Path, ...]) -> list[Path]: + return sorted(readme.parent for readme in readmes if readme.parent.parent == directory) -def index_path_for(path: Path) -> Path: - if is_model_readme(path): - return MODEL_OUT_DIR.joinpath(*model_parts_for_dir(path.parent), "index.md") - if is_example_readme(path): - return example_index_path_for_dir(path.parent) - return output_path_for(path) +def example_children(directory: Path) -> list[Path]: + return sorted(path for path in EXAMPLE_DOC_DIRS if path.parent == directory and path != directory) -def is_url(target: str) -> bool: - return bool(re.match(r"^[A-Za-z][A-Za-z0-9+.-]*:", target)) +def generated_toctree(source: Path, page: Path) -> str: + entries = [] + if source in MODEL_READMES: + if source == MODEL / "PhasorDynamics" / "README.md": + entries.append(relative(SOURCE_TO_PAGE[PHASOR_INPUT_FORMAT.resolve()].with_suffix(""), page)) + entries += [ + relative(model_page(child / "README.md").with_suffix(""), page) + for child in children_of(source.parent, MODEL_READMES) + ] + elif source in EXAMPLE_READMES: + entries = [ + relative(example_page(child).with_suffix(""), page) + for child in example_children(source.parent) + ] + return f"{toctree(entries)}\n" if entries else "" def split_anchor(target: str) -> tuple[str, str]: @@ -198,196 +163,143 @@ def split_anchor(target: str) -> tuple[str, str]: return path, f"{sep}{anchor}" if sep else "" -def resolve_local(source_dir: Path, target: str) -> Path: - return (source_dir / target).resolve() - - -def relpath_from_output(target: Path, current_output: Path) -> str: - return Path(os.path.relpath(target, current_output.parent)).as_posix() - - -def repo_source_url(path: Path) -> str: - relpath = path.relative_to(REPO_ROOT).as_posix() - return f"{GITHUB_TREE_URL}/{relpath}" - - -def fallback_repo_path(source_dir: Path, path_part: str) -> Path | None: - candidates: list[Path] = [] - if path_part.endswith("src/Model/PowerFlow/Gen/README.md"): - candidates.append(MODEL_DIR / "PowerFlow" / "README.md") - if path_part.startswith("src/Model/"): - candidates.append(REPO_ROOT / "GridKit" / path_part.removeprefix("src/")) - if path_part.startswith("Model/"): - candidates.append(REPO_ROOT / "GridKit" / path_part) - if "Model/" in path_part: - candidates.append( - REPO_ROOT / "GridKit" / "Model" / path_part.split("Model/", 1)[1] - ) - - source_parts = source_dir.relative_to(REPO_ROOT).parts - if len(source_parts) >= 2 and source_parts[0] == "application": - candidates.append(REPO_ROOT / "GridKit" / path_part.removeprefix("../../")) - - return next((candidate.resolve() for candidate in candidates if candidate.exists()), None) +def is_external(target: str) -> bool: + return bool(re.match(r"^[A-Za-z][A-Za-z0-9+.-]*:", target)) -def page_link(path: Path, anchor: str, current_output: Path) -> str | None: - if path == COMMON_MATH and anchor == "#anti-windup-indicator": +def fallback(source_dir: Path, target: str) -> Path | None: + candidates = [] + if target.endswith("src/Model/PowerFlow/Gen/README.md"): + candidates.append(MODEL / "PowerFlow" / "README.md") + if target.startswith("src/Model/"): + candidates.append(ROOT / "GridKit" / target.removeprefix("src/")) + if target.startswith("Model/"): + candidates.append(ROOT / "GridKit" / target) + if "Model/" in target: + candidates.append(MODEL / target.split("Model/", 1)[1]) + if source_dir.relative_to(ROOT).parts[:1] == ("application",): + candidates.append(ROOT / "GridKit" / target.removeprefix("../../")) + return next((path.resolve() for path in candidates if path.exists()), None) + + +def page_link(path: Path, anchor: str, current_page: Path) -> str | None: + path = path.resolve() + if path == COMMON_MATH.resolve() and anchor == "#anti-windup-indicator": anchor = "#derived-functions" - - if path in PROJECT_DOC_BY_SOURCE or path == COMMON_MATH or path == PHASOR_INPUT_FORMAT: - return f"{relpath_from_output(output_path_for(path), current_output)}{anchor}" - if (is_model_readme(path) or is_example_readme(path)) and path.exists(): - return f"{relpath_from_output(output_path_for(path), current_output)}{anchor}" + if path in SOURCE_TO_PAGE: + return f"{relative(SOURCE_TO_PAGE[path], current_page)}{anchor}" + readme = (path / "README.md").resolve() if path.is_dir() else None + if readme in SOURCE_TO_PAGE: + return f"{relative(SOURCE_TO_PAGE[readme], current_page)}{anchor}" return None -def rewrite_target(source_dir: Path, target: str, current_output: Path) -> str: - if not target or target.startswith("#") or is_url(target): +def rewrite_link(source_dir: Path, target: str, current_page: Path) -> str: + if not target or target.startswith("#") or is_external(target): return target - - path_part, anchor = split_anchor(target) - resolved = resolve_local(source_dir, path_part) - fallback_path = None - if not resolved.exists(): - fallback_path = fallback_repo_path(source_dir, path_part) - if fallback_path is not None: - resolved = fallback_path - - generated_link = page_link(resolved, anchor, current_output) - if generated_link is not None: - return generated_link - - if resolved.exists() and not resolved.is_dir(): - return f"{relpath_from_output(resolved, current_output)}{anchor}" - if resolved.exists() and resolved.is_dir(): - readme = resolved / "README.md" - if readme.exists(): - generated_dir_link = page_link(readme, anchor, current_output) - if generated_dir_link is not None: - return generated_dir_link - return f"{repo_source_url(resolved)}{anchor}" - - try: - resolved.relative_to(REPO_ROOT) - except ValueError: - return target - - return f"{relpath_from_output(resolved, current_output)}{anchor}" - - -def rewrite_asset_target(source_dir: Path, target: str, current_output: Path) -> str: - if not target or target.startswith("#") or is_url(target): - return target - - path_part, anchor = split_anchor(target) - resolved = resolve_local(source_dir, path_part) - try: - path = relpath_from_output(resolved, current_output) - except ValueError: + path_text, anchor = split_anchor(target) + path = (source_dir / path_text).resolve() + if not path.exists(): + path = fallback(source_dir, path_text) or path + link = page_link(path, anchor, current_page) + if link: + return link + if path.exists() and path.is_dir(): + return f"{GITHUB_REPO}/tree/{SOURCE_REF}/{path.relative_to(ROOT).as_posix()}{anchor}" + if path.exists() or ROOT in path.parents: + return f"{relative(path, current_page)}{anchor}" + return target + + +def rewrite_asset(source_dir: Path, target: str, current_page: Path) -> str: + if not target or target.startswith("#") or is_external(target): return target - return f"{path}{anchor}" - - -def rewrite_markdown_links(text: str, source_dir: Path, current_output: Path) -> str: - def image_repl(match: re.Match[str]) -> str: - label, target = match.groups() - return f"![{label}]({rewrite_asset_target(source_dir, target, current_output)})" - - def link_repl(match: re.Match[str]) -> str: - label, target = match.groups() - return f"[{label}]({rewrite_target(source_dir, target, current_output)})" - - text = re.sub(r"!\[([^\]]*)\]\(([^)\s]+)\)", image_repl, text) - text = re.sub(r"(? str: +def rewrite_markdown_links(text: str, source_dir: Path, current_page: Path) -> str: text = re.sub( - r"(?im)^\s*]*\balign=[\"']center[\"'][^>]*>\s*$", - "", + r"!\[([^\]]*)\]\(([^)\s]+)\)", + lambda m: f"![{m.group(1)}]({rewrite_asset(source_dir, m.group(2), current_page)})", text, ) + return re.sub( + r"(? str: + text = re.sub(r"(?im)^\s*]*\balign=[\"']center[\"'][^>]*>\s*$", "", text) text = re.sub(r"(?im)^\s*\s*$", "", text) - def html_attr(attrs: str, name: str) -> str | None: + def attr(attrs: str, name: str) -> str | None: match = re.search(rf"\b{name}\s*=\s*([\"'])(.*?)\1", attrs, re.IGNORECASE) return match.group(2) if match else None - def repl(match: re.Match[str]) -> str: + def image(match: re.Match[str]) -> str: before, target, after = match.groups() attrs = f"{before} {after}" - target = rewrite_asset_target(source_dir, target, current_output) options = [] - - alt = html_attr(attrs, "alt") - if alt: + if alt := attr(attrs, "alt"): options.append(f":alt: {alt}") - - align = html_attr(attrs, "align") - if align in {"left", "center", "right"}: + if (align := attr(attrs, "align")) in {"left", "center", "right"}: options.append(f":align: {align}") - - option_block = "\n".join(options) - if option_block: - option_block += "\n" - - return f"\n```{{image}} {target}\n{option_block}```\n" + options_text = "\n".join(options) + if options_text: + options_text += "\n" + target = rewrite_asset(source_dir, target, current_page) + return f"\n```{{image}} {target}\n{options_text}```\n" return re.sub( r"]*)\bsrc=[\"']([^\"']+)[\"']([^>]*)>", - repl, + image, text, flags=re.IGNORECASE, ) -def normalize_heading_levels(text: str) -> str: - levels = sorted( - {len(match.group(1)) for match in re.finditer(r"(?m)^(#{1,6})(\s+)", text)} +def normalize_headings(text: str) -> str: + levels = sorted({len(m.group(1)) for m in re.finditer(r"(?m)^(#{1,6})(\s+)", text)}) + levels = {level: index + 2 for index, level in enumerate(levels)} + return re.sub( + r"(?m)^(#{1,6})(\s+)", + lambda m: f"{'#' * levels.get(len(m.group(1)), len(m.group(1)))}{m.group(2)}", + text, ) - mapping = {level: index + 2 for index, level in enumerate(levels)} - - def repl(match: re.Match[str]) -> str: - level = len(match.group(1)) - return f"{'#' * mapping.get(level, level)}{match.group(2)}" - - return re.sub(r"(?m)^(#{1,6})(\s+)", repl, text) -def comparable_title(value: str) -> str: - value = value.replace("™", "") - return re.sub(r"[^a-z0-9]+", "", value.lower()) +def strip_title(text: str, title: str) -> str: + def comparable(value: str) -> str: + return re.sub(r"[^a-z0-9]+", "", value.replace("™", "").lower()) - -def strip_duplicate_top_heading(text: str, title: str) -> str: match = re.match(r"\s*#\s+(.+?)\s*(?:\n+|$)", text) - if match and comparable_title(match.group(1)) == comparable_title(title): + if match and comparable(match.group(1)) == comparable(title): return text[match.end() :] return text def normalize_comment_fences(text: str, source: Path) -> str: - if source != REPO_ROOT / "CONTRIBUTING.md": + if source != ROOT / "CONTRIBUTING.md": return text - def repl(match: re.Match[str]) -> str: - body = match.group(1) - lines = [line.strip() for line in body.splitlines() if line.strip()] + def fence(match: re.Match[str]) -> str: + lines = [line.strip() for line in match.group(1).splitlines() if line.strip()] if lines and all(line.startswith(("/", "*")) for line in lines): - return f"```text\n{body}```" + return f"```text\n{match.group(1)}```" return match.group(0) - return re.sub(r"```c\+\+\n(.*?)```", repl, text, flags=re.DOTALL) + return re.sub(r"```c\+\+\n(.*?)```", fence, text, flags=re.DOTALL) def normalize_markdown(text: str, source: Path, title: str) -> str: - text = strip_duplicate_top_heading(text, title) + text = strip_title(text, title) text = normalize_comment_fences(text, source) text = re.sub(r"(?m)^```\s*math\s*$", "```{math}", text) text = re.sub(r"\$`([^`]+)`\$", r"$\1$", text) - text = normalize_heading_levels(text) + text = normalize_headings(text) if source == COMMON_MATH: text = re.sub( r"(?m)^(#{2,6}\s+Derived Functions)", @@ -398,175 +310,76 @@ def normalize_markdown(text: str, source: Path, title: str) -> str: return text -def toctree_block(entries: list[str], *, hidden: bool = False, maxdepth: int = 2) -> str: - options = [f":maxdepth: {maxdepth}", ":titlesonly:"] - if hidden: - options.append(":hidden:") - - return ( - "```{toctree}\n" - + "\n".join(options) - + "\n\n" - + "\n".join(entries) - + "\n```\n" - ) - - -def markdown_link(title: str, path: Path, current_output: Path) -> str: - return f"[{title}]({relpath_from_output(path, current_output)})" - - -def tree_index_entries(source: Path, current_output: Path) -> list[str]: - entries = [] - if source == MODEL_DIR / "PhasorDynamics" / "README.md": - entries.append( - relpath_from_output( - output_path_for(PHASOR_INPUT_FORMAT).with_suffix(""), - current_output, - ) - ) - - entries.extend( - relpath_from_output( - index_path_for(child_dir / "README.md").with_suffix(""), - current_output, - ) - for child_dir in child_readme_dirs(source.parent) - ) - return entries - - -def example_index_entries(directory: Path, current_output: Path) -> list[str]: - entries: list[str] = [] - - entries.extend( - relpath_from_output(example_index_path_for_dir(child).with_suffix(""), current_output) - for child in example_child_dirs(directory) - ) - return entries +def write_page(source: Path) -> None: + source = source.resolve() + page = SOURCE_TO_PAGE[source] + page.parent.mkdir(parents=True, exist_ok=True) - -def example_contents_block(directory: Path, current_output: Path) -> str: - readme = directory / "README.md" - child_dirs = example_child_dirs(directory) - lines: list[str] = [] - - if readme in EXAMPLE_READMES and child_dirs: - lines.extend( - [ - "## Pages", - "", - f"- {markdown_link('Overview', output_path_for(readme), current_output)}", - ] - ) - - if child_dirs: - if lines: - lines.append("") - lines.extend(["## Sections", ""]) - for child in child_dirs: - child_link = markdown_link( - title_for_dir(child, EXAMPLES_DIR, "Examples"), - example_index_path_for_dir(child), - current_output, - ) - grandchild_links = [ - markdown_link( - title_for_dir(grandchild, EXAMPLES_DIR, "Examples"), - example_index_path_for_dir(grandchild), - current_output, - ) - for grandchild in example_child_dirs(child) - ] - if grandchild_links: - lines.append(f"- {child_link}: {', '.join(grandchild_links)}") - else: - lines.append(f"- {child_link}") - - return "\n".join(lines) + "\n" - - -def generated_page_title(source: Path) -> str: - return title_for(source) - - -def generated_page_toctree(source: Path, current_output: Path) -> str: - if source == REPO_ROOT / "README.md": - return toctree_block(list(ROOT_TOCTREE_ENTRIES), hidden=True, maxdepth=4) - if is_model_container(source): - entries = tree_index_entries(source, current_output) - return f"{toctree_block(entries, hidden=True)}\n" if entries else "" - if is_example_container(source): - entries = example_index_entries(source.parent, current_output) - return f"{toctree_block(entries, hidden=True)}\n" if entries else "" - return "" - - -def generate_page(source: Path) -> None: - out = output_path_for(source) - if source == REPO_ROOT / "README.md": - out.write_text( - "# GridKit\n\n" - f"{generated_page_toctree(source, out)}" - "```{include} ../README.md\n" - ":relative-images:\n" - "```\n", - encoding="utf-8", - ) + text = source.read_text(encoding="utf-8") + if source == (ROOT / "README.md").resolve(): + text = rewrite_markdown_links(text, source.parent, page) + page.write_text(text.rstrip() + "\n", encoding="utf-8") return - source_dir = source.parent - title = generated_page_title(source) - body = source.read_text(encoding="utf-8") - body = normalize_markdown(body, source, title) - body = rewrite_markdown_links(body, source_dir, out) - body = rewrite_html_paths(body, source_dir, out) - - out.parent.mkdir(parents=True, exist_ok=True) - rel_source = source.relative_to(REPO_ROOT).as_posix() - out.write_text( + title = title_for(source) + text = normalize_markdown(text, source, title) + text = rewrite_markdown_links(text, source.parent, page) + text = rewrite_html_images(text, source.parent, page) + source_name = source.relative_to(ROOT).as_posix() + page.write_text( f"# {title}\n\n" - f"{generated_page_toctree(source, out)}" - f"_Source: `{rel_source}`_\n\n" - f"{body.rstrip()}\n", + f"{generated_toctree(source, page)}" + f"_Source: `{source_name}`_\n\n" + f"{text.rstrip()}\n", encoding="utf-8", ) -def generate_example_index_page(directory: Path) -> None: - child_dirs = example_child_dirs(directory) - readme = directory / "README.md" - if readme in EXAMPLE_READMES: +def write_example_index(directory: Path) -> None: + if (directory / "README.md").resolve() in EXAMPLE_READMES: return - if not child_dirs: + children = example_children(directory) + if not children: return - out = example_index_path_for_dir(directory) - out.parent.mkdir(parents=True, exist_ok=True) - entries = example_index_entries(directory, out) - out.write_text( - f"# {title_for_dir(directory, EXAMPLES_DIR, 'Examples')}\n\n" - f"{toctree_block(entries, hidden=True)}\n" - f"{example_contents_block(directory, out)}", + page = example_page(directory) + page.parent.mkdir(parents=True, exist_ok=True) + entries = [relative(example_page(child).with_suffix(""), page) for child in children] + lines = ["## Sections", ""] + for child in children: + child_link = f"[{child.name}]({relative(example_page(child), page)})" + grandchildren = [ + f"[{grandchild.name}]({relative(example_page(grandchild), page)})" + for grandchild in example_children(child) + ] + lines.append(f"- {child_link}: {', '.join(grandchildren)}" if grandchildren else f"- {child_link}") + + page.write_text( + f"# {'Examples' if directory == EXAMPLES else directory.name}\n\n" + f"{toctree(entries)}\n" + + "\n".join(lines) + + "\n", encoding="utf-8", ) def main() -> None: - for generated_dir in (GENERAL_OUT_DIR, MODEL_OUT_DIR, EXAMPLE_OUT_DIR): - if generated_dir.exists(): - shutil.rmtree(generated_dir) - generated_dir.mkdir(parents=True, exist_ok=True) - - project_sources = [page.source for page in PROJECT_DOCS] - model_sources = [COMMON_MATH, PHASOR_INPUT_FORMAT] - model_sources.extend(sorted(MODEL_DIR.glob("**/README.md"))) - example_sources = sorted(EXAMPLE_READMES) - - for source in project_sources + model_sources + example_sources: - generate_page(source) + for directory in (GENERATED, GENERATED_MODELS, GENERATED_EXAMPLES): + if directory.exists(): + shutil.rmtree(directory) + directory.mkdir(parents=True, exist_ok=True) + + sources = [ + *PROJECT_PAGES, + COMMON_MATH, + PHASOR_INPUT_FORMAT, + *MODEL_READMES, + *EXAMPLE_READMES, + ] + for source in sources: + write_page(source) for directory in sorted(EXAMPLE_DOC_DIRS): - generate_example_index_page(directory) + write_example_index(directory) if __name__ == "__main__": diff --git a/docs/index.md b/docs/index.md index bc4d0d2a8..73347a2b5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,6 +12,6 @@ examples/generated/index api development/index ``` -```{include} ../README.md -:relative-images: +```{include} generated/readme.md +:relative-docs: generated/ ``` From 3bafe2e29900a84e30642892c36f7f0e43e1afd7 Mon Sep 17 00:00:00 2001 From: lukelowry Date: Tue, 16 Jun 2026 14:37:17 -0500 Subject: [PATCH 4/6] Remove script do mirror structure for ease --- .gitignore | 5 - .readthedocs.yaml | 4 +- CONTRIBUTING.md | 4 +- GridKit/CommonMath.md | 30 +- .../Branch/BranchLumpedConstant/README.md | 6 +- GridKit/Model/PhasorDynamics/Branch/README.md | 6 +- GridKit/Model/PhasorDynamics/Bus/README.md | 11 +- .../BusToSignalAdapter/README.md | 6 +- .../PhasorDynamics/Converter/REECA/README.md | 8 +- .../PhasorDynamics/Converter/REGCA/README.md | 6 +- .../PhasorDynamics/Converter/REGCB/README.md | 6 +- .../PhasorDynamics/Exciter/ESAC6A/README.md | 6 +- .../PhasorDynamics/Exciter/ESDC2A/README.md | 8 +- .../PhasorDynamics/Exciter/ESST4B/README.md | 8 +- .../PhasorDynamics/Exciter/EXAC1/README.md | 8 +- .../PhasorDynamics/Exciter/EXAC2/README.md | 8 +- .../PhasorDynamics/Exciter/EXDC1/README.md | 8 +- .../PhasorDynamics/Exciter/EXPIC1/README.md | 8 +- .../PhasorDynamics/Exciter/IEEET1/README.md | 9 +- .../PhasorDynamics/Exciter/SCRX/README.md | 8 +- .../PhasorDynamics/Exciter/SEXS-PTI/README.md | 8 +- .../PhasorDynamics/Governor/GGOV1/README.md | 8 +- .../PhasorDynamics/Governor/IEEEG1/README.md | 8 +- .../PhasorDynamics/Governor/Tgov1/README.md | 10 +- .../Model/PhasorDynamics/SignalNode/README.md | 15 + .../Stabilizer/IEEEST/README.md | 6 +- .../PhasorDynamics/Stabilizer/PSS1A/README.md | 9 +- .../SynchronousMachine/GENROUwS/README.md | 10 +- .../SynchronousMachine/GENSALwS/README.md | 8 +- .../SynchronousMachine/README.md | 8 +- GridKit/Model/PowerFlow/Branch/README.md | 17 +- GridKit/Model/PowerFlow/Bus/README.md | 9 +- application/PhasorDynamics/README.md | 2 +- docs/Doxyfile | 2 +- docs/GridKit/CommonMath.md | 6 + docs/GridKit/Model/EMT/Bus/README.md | 6 + .../Branch/BranchLumpedConstant/README.md | 6 + .../Model/EMT/Component/Branch/README.md | 14 + .../Model/EMT/Component/LoadRL/README.md | 6 + docs/GridKit/Model/EMT/Component/README.md | 16 + .../EMT/Component/VoltageSource/README.md | 6 + docs/GridKit/Model/EMT/README.md | 15 + .../Model/PhasorDynamics/Branch/README.md | 6 + .../Model/PhasorDynamics/Bus/README.md | 6 + .../Model/PhasorDynamics/BusFault/README.md | 6 + .../BusToSignalAdapter/README.md | 6 + .../Model/PhasorDynamics/Converter/README.md | 16 + .../PhasorDynamics/Converter/REECA/README.md | 6 + .../PhasorDynamics/Converter/REGCA/README.md | 6 + .../PhasorDynamics/Converter/REGCB/README.md | 6 + .../PhasorDynamics/Exciter/ESAC6A/README.md | 6 + .../PhasorDynamics/Exciter/ESDC2A/README.md | 6 + .../PhasorDynamics/Exciter/ESST4B/README.md | 6 + .../PhasorDynamics/Exciter/EXAC1/README.md | 6 + .../PhasorDynamics/Exciter/EXAC2/README.md | 6 + .../PhasorDynamics/Exciter/EXDC1/README.md | 6 + .../PhasorDynamics/Exciter/EXPIC1/README.md | 6 + .../PhasorDynamics/Exciter/IEEET1/README.md | 6 + .../Model/PhasorDynamics/Exciter/README.md | 23 ++ .../PhasorDynamics/Exciter/SCRX/README.md | 6 + .../PhasorDynamics/Exciter/SEXS-PTI/README.md | 6 + .../PhasorDynamics/Governor/GGOV1/README.md | 6 + .../PhasorDynamics/Governor/IEEEG1/README.md | 6 + .../Model/PhasorDynamics/Governor/README.md | 16 + .../PhasorDynamics/Governor/Tgov1/README.md | 6 + .../Model/PhasorDynamics/INPUT_FORMAT.md | 6 + .../Model/PhasorDynamics/Load/README.md | 6 + .../Model/PhasorDynamics/LoadZIP/README.md | 6 + docs/GridKit/Model/PhasorDynamics/README.md | 26 ++ .../Model/PhasorDynamics/SignalNode/README.md | 6 + .../Stabilizer/IEEEST/README.md | 6 + .../PhasorDynamics/Stabilizer/PSS1A/README.md | 6 + .../Model/PhasorDynamics/Stabilizer/README.md | 15 + .../SynchronousMachine/GENROUwS/README.md | 6 + .../SynchronousMachine/GENSALwS/README.md | 6 + .../SynchronousMachine/GenClassical/README.md | 6 + .../SynchronousMachine/README.md | 16 + .../DistributedGenerator/README.md | 5 + .../PowerElectronics/MicrogridBusDQ/README.md | 5 + .../PowerElectronics/MicrogridLine/README.md | 5 + .../PowerElectronics/MicrogridLoad/README.md | 5 + docs/GridKit/Model/PowerElectronics/README.md | 16 + docs/GridKit/Model/PowerFlow/Branch/README.md | 6 + docs/GridKit/Model/PowerFlow/Bus/README.md | 6 + docs/GridKit/Model/PowerFlow/Load/README.md | 6 + docs/GridKit/Model/PowerFlow/README.md | 15 + docs/GridKit/Model/README.md | 13 + docs/INSTALL.md | 6 + docs/README.md | 11 +- docs/api.md | 17 - .../PhasorDynamics/ContingencyAnalysis.md | 5 + .../PhasorDynamics/DynamicSimulation.md | 5 + docs/application/PhasorDynamics/README.md | 15 + docs/application/README.md | 9 + docs/applications/index.md | 15 - docs/conf.py | 24 +- docs/development/CHANGELOG.md | 6 + docs/development/CONTRIBUTING.md | 6 + docs/development/README.md | 12 + docs/development/buildsystem/README.md | 5 + docs/development/docs/README.md | 6 + docs/development/index.md | 19 - .../PhasorDynamics/Large/Illinois/README.md | 6 + docs/examples/PhasorDynamics/Large/README.md | 11 + .../PhasorDynamics/Large/Texas/README.md | 6 + .../PhasorDynamics/Large/WECC/README.md | 6 + .../PhasorDynamics/Medium/Hawaii/README.md | 6 + .../Medium/NewEngland/README.md | 6 + docs/examples/PhasorDynamics/Medium/README.md | 10 + docs/examples/PhasorDynamics/README.md | 12 + docs/examples/PhasorDynamics/Small/README.md | 9 + .../PhasorDynamics/Small/TwoArea/README.md | 6 + docs/examples/PhasorDynamics/Tiny/README.md | 9 + .../Tiny/TwoBus/Gensal/README.md | 6 + .../PhasorDynamics/Tiny/TwoBus/README.md | 9 + .../PowerElectronics/Microgrid/README.md | 5 + docs/examples/PowerElectronics/README.md | 10 + .../PowerElectronics/ScaleMicrogrid/README.md | 5 + docs/examples/PowerFlow/Grid3Bus/README.md | 6 + docs/examples/PowerFlow/README.md | 9 + docs/examples/README.md | 16 + docs/generate_model_docs.py | 386 ------------------ docs/index.md | 21 +- docs/models/index.md | 21 - .../PhasorDynamics/Large/Illinois/README.md | 8 +- examples/PhasorDynamics/Large/WECC/README.md | 8 +- .../PhasorDynamics/Medium/Hawaii/README.md | 8 +- .../Medium/NewEngland/README.md | 8 +- .../PhasorDynamics/Small/TwoArea/README.md | 8 +- examples/PowerFlow/Grid3Bus/README.md | 11 +- 130 files changed, 826 insertions(+), 693 deletions(-) create mode 100644 GridKit/Model/PhasorDynamics/SignalNode/README.md create mode 100644 docs/GridKit/CommonMath.md create mode 100644 docs/GridKit/Model/EMT/Bus/README.md create mode 100644 docs/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md create mode 100644 docs/GridKit/Model/EMT/Component/Branch/README.md create mode 100644 docs/GridKit/Model/EMT/Component/LoadRL/README.md create mode 100644 docs/GridKit/Model/EMT/Component/README.md create mode 100644 docs/GridKit/Model/EMT/Component/VoltageSource/README.md create mode 100644 docs/GridKit/Model/EMT/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Branch/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Bus/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/BusFault/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Converter/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Converter/REECA/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Governor/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Load/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/LoadZIP/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/SignalNode/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/Stabilizer/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/README.md create mode 100644 docs/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md create mode 100644 docs/GridKit/Model/PowerElectronics/DistributedGenerator/README.md create mode 100644 docs/GridKit/Model/PowerElectronics/MicrogridBusDQ/README.md create mode 100644 docs/GridKit/Model/PowerElectronics/MicrogridLine/README.md create mode 100644 docs/GridKit/Model/PowerElectronics/MicrogridLoad/README.md create mode 100644 docs/GridKit/Model/PowerElectronics/README.md create mode 100644 docs/GridKit/Model/PowerFlow/Branch/README.md create mode 100644 docs/GridKit/Model/PowerFlow/Bus/README.md create mode 100644 docs/GridKit/Model/PowerFlow/Load/README.md create mode 100644 docs/GridKit/Model/PowerFlow/README.md create mode 100644 docs/GridKit/Model/README.md create mode 100644 docs/INSTALL.md create mode 100644 docs/application/PhasorDynamics/ContingencyAnalysis.md create mode 100644 docs/application/PhasorDynamics/DynamicSimulation.md create mode 100644 docs/application/PhasorDynamics/README.md create mode 100644 docs/application/README.md delete mode 100644 docs/applications/index.md create mode 100644 docs/development/CHANGELOG.md create mode 100644 docs/development/CONTRIBUTING.md create mode 100644 docs/development/README.md create mode 100644 docs/development/buildsystem/README.md create mode 100644 docs/development/docs/README.md delete mode 100644 docs/development/index.md create mode 100644 docs/examples/PhasorDynamics/Large/Illinois/README.md create mode 100644 docs/examples/PhasorDynamics/Large/README.md create mode 100644 docs/examples/PhasorDynamics/Large/Texas/README.md create mode 100644 docs/examples/PhasorDynamics/Large/WECC/README.md create mode 100644 docs/examples/PhasorDynamics/Medium/Hawaii/README.md create mode 100644 docs/examples/PhasorDynamics/Medium/NewEngland/README.md create mode 100644 docs/examples/PhasorDynamics/Medium/README.md create mode 100644 docs/examples/PhasorDynamics/README.md create mode 100644 docs/examples/PhasorDynamics/Small/README.md create mode 100644 docs/examples/PhasorDynamics/Small/TwoArea/README.md create mode 100644 docs/examples/PhasorDynamics/Tiny/README.md create mode 100644 docs/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md create mode 100644 docs/examples/PhasorDynamics/Tiny/TwoBus/README.md create mode 100644 docs/examples/PowerElectronics/Microgrid/README.md create mode 100644 docs/examples/PowerElectronics/README.md create mode 100644 docs/examples/PowerElectronics/ScaleMicrogrid/README.md create mode 100644 docs/examples/PowerFlow/Grid3Bus/README.md create mode 100644 docs/examples/PowerFlow/README.md create mode 100644 docs/examples/README.md delete mode 100644 docs/generate_model_docs.py delete mode 100644 docs/models/index.md diff --git a/.gitignore b/.gitignore index 5689b87f9..0a6e13c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,5 @@ build/ *.swp doxygen-docs/ docs/_build/ -docs/api/generated/ docs/api/reference/ -docs/examples/generated/ -docs/generated/ -docs/models/generated/ -docs/xml/ __pycache__ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 506264ffd..f8f34ddc2 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,11 +6,9 @@ build: python: "3.12" apt_packages: - doxygen - - graphviz jobs: pre_build: - - python docs/generate_model_docs.py - - cd docs && doxygen Doxyfile + - rm -rf build/docs/doxygen && cd docs && doxygen Doxyfile sphinx: configuration: docs/conf.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4985b656a..1a446018e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,11 +107,11 @@ required. Doxygen supports Markdown markup and it should be used to make documentation more clear. For example, -```c++ +```text * @return The size of `a` ``` is clearer than -```c++ +```text * @return The size of a ``` when read in plain text and in formatted documentation. diff --git a/GridKit/CommonMath.md b/GridKit/CommonMath.md index 9f9f9bdcc..9c1995e51 100644 --- a/GridKit/CommonMath.md +++ b/GridKit/CommonMath.md @@ -1,6 +1,6 @@ # CommonMath -Smooth, autodiff-friendly replacements for piecewise functions used across GridKit component models. See [CommonMath.hpp](CommonMath.hpp) for implementation details. +Smooth, autodiff-friendly replacements for piecewise functions used across GridKit component models. See `CommonMath.hpp` for implementation details. ## Primitives @@ -28,7 +28,7 @@ The sigmoid is also known as the logistic function. The equivalent `tanh` form i \end{aligned} ``` -
+![](../docs/Figures/CommonMath/sigmoid.svg) ### $\rho$ - `ramp` @@ -42,7 +42,7 @@ The sigmoid is also known as the logistic function. The equivalent `tanh` form i \end{aligned} ``` -
+![](../docs/Figures/CommonMath/ramp.svg) ### $q$ - `qramp` @@ -52,7 +52,7 @@ The sigmoid is also known as the logistic function. The equivalent `tanh` form i q(x)=x^2\,\sigma(x) ``` -
+![](../docs/Figures/CommonMath/qramp.svg) ## Derived Functions @@ -85,7 +85,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/max.svg) ### `min` @@ -101,7 +101,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/min.svg) ### `clamp` @@ -118,7 +118,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/clamp.svg) ### `deadband1` @@ -135,7 +135,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/deadband1.svg) ### `deadband2` @@ -152,7 +152,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/deadband2.svg) ### `slew` @@ -169,7 +169,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/slew.svg) ### `linseg` @@ -186,7 +186,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/linseg.svg) ### `above` @@ -202,7 +202,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/above.svg) ### `below` @@ -218,7 +218,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/below.svg) ### `inside` @@ -234,7 +234,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/inside.svg) ### `outside` @@ -250,7 +250,7 @@ q(x)=x^2\,\sigma(x) \end{aligned} ``` -
+![](../docs/Figures/CommonMath/outside.svg) ### `antiwindup` diff --git a/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md b/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md index 17a7374ea..0ec665ed2 100644 --- a/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md +++ b/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md @@ -7,11 +7,9 @@ Series current $\mathbf{i}$ is directed from bus 1 to bus 2. Bus residual current injections are positive into buses. All electrical parameter matrices are $3 \times 3$ and capture self and mutual coupling between phases. -
- +![](../../../../../../docs/Figures/EMT/lumped_constant_diagram.svg) - Figure 1: Lumped constant EMT branch model -
+Figure 1: Lumped constant EMT branch model ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/Branch/README.md b/GridKit/Model/PhasorDynamics/Branch/README.md index e96c4e3f6..fcdea7692 100644 --- a/GridKit/Model/PhasorDynamics/Branch/README.md +++ b/GridKit/Model/PhasorDynamics/Branch/README.md @@ -17,11 +17,9 @@ An ideal complex tap is placed on the bus-1 side of the branch equivalent. The ordinary transmission-line $\pi$ model is recovered with $\tau = 1$ and $\theta = 0$. -
- +![](../../../../docs/Figures/transformer-branch.png) - Figure 1: Branch equivalent circuit -
+Figure 1: Branch equivalent circuit ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/Bus/README.md b/GridKit/Model/PhasorDynamics/Bus/README.md index 2d61eed2f..327a9e53b 100644 --- a/GridKit/Model/PhasorDynamics/Bus/README.md +++ b/GridKit/Model/PhasorDynamics/Bus/README.md @@ -15,16 +15,11 @@ numerical integrator requests residual evaluation. Current entering the bus has positive and current exiting the bus negative sign. -
- - - Figure 1: Needs to be changed to represent current balance instead of power - balance. -
- - +![](../../../../docs/Figures/bus_variables.jpg) +Figure 1: Needs to be changed to represent current balance instead of power +balance. **Other Parameters** Buses are uniquely defined by their ID (number or name). Besides, each bus should have associated Nominal Voltage value. diff --git a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md index b418fad98..d618c6323 100644 --- a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md +++ b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md @@ -3,17 +3,17 @@ This component enables signals to send and receive bus variables. It has five ports: -#### Bus port +## Bus Port - `bus` for the bus whose variables are managed by the adapter -#### Input ports +## Input Ports - `ir` ($I_r$) - `ii` ($I_i$) External current injections are read from input signal nodes added to currents on the bus. -#### Output ports +## Output Ports - `vr` ($V_r$) - `vi` ($V_i$) diff --git a/GridKit/Model/PhasorDynamics/Converter/REECA/README.md b/GridKit/Model/PhasorDynamics/Converter/REECA/README.md index 6e0d0b83e..68c8ab96f 100644 --- a/GridKit/Model/PhasorDynamics/Converter/REECA/README.md +++ b/GridKit/Model/PhasorDynamics/Converter/REECA/README.md @@ -11,11 +11,9 @@ Notes: Standard REECA block diagram. -
- +![](../../../../../docs/Figures/PhasorDynamics_REECA_Diagram.png) - Figure 1: REECA block diagram. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: REECA block diagram. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -251,7 +249,7 @@ The state-equation residuals use compact limiter notation where applicable. The \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) target and smooth approximation. +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md b/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md index ea352c520..45b63f3a7 100644 --- a/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md +++ b/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md @@ -13,11 +13,9 @@ Notes: Standard REGCA converter-interface model. -
- +![](../../../../../docs/Figures/PhasorDynamics_REGCA_Diagram.png) - Figure 1: Generator/Converter REGCA model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Generator/Converter REGCA model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md b/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md index f10fe2218..32f86f38d 100644 --- a/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md +++ b/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md @@ -9,11 +9,9 @@ the REGCB source standard before implementation. Standard model diagram for the REGCB converter interface. -
- +![](../../../../../docs/Figures/PhasorDynamics_REGCB_Diagram.png) - Figure 1: Generator/Converter REGCB model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Generator/Converter REGCB model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) Detailed REGCB parameters, variables, equations, initialization details, and outputs will be added after validation against the REGCB source standard. diff --git a/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md b/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md index c2b556cd7..07a3ccfda 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md @@ -16,11 +16,9 @@ Notes: Standard model of the ESAC6A Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/ESAC6A_diagram.png) - Figure 1: Exciter ESAC6A model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter ESAC6A model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md b/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md index 29c3028ff..da01f2157 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md @@ -17,11 +17,9 @@ Notes: Standard model of the ESDC2A Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/ESDC2A_diagram.png) - Figure 1: Exciter ESDC2A model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter ESDC2A model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -167,7 +165,7 @@ $\omega$ | [p.u.] | Machine speed deviation \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md b/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md index fa77d9030..27ae8e018 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md @@ -18,11 +18,9 @@ Notes: Standard model of the ESST4B Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/ESST4B_diagram.png) - Figure 1: Exciter ESST4B model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter ESST4B model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -151,7 +149,7 @@ $I_{\mathrm{fd}}$ | [p.u.] | Machine field current \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md b/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md index bbf7a68b3..31f04139a 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md @@ -16,11 +16,9 @@ Notes: Standard model of the EXAC1 Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/EXAC1_diagram.png) - Figure 1: Exciter EXAC1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter EXAC1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -153,7 +151,7 @@ $\omega$ | [p.u.] | Machine speed deviation \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md b/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md index 3ec486d00..f957d31ff 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md @@ -16,11 +16,9 @@ Notes: Standard model of the EXAC2 Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/EXAC2_diagram.png) - Figure 1: Exciter EXAC2 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter EXAC2 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -150,7 +148,7 @@ $\omega$ | [p.u.] | Machine speed deviation \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md b/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md index 3b6c75108..db6f96886 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md @@ -4,11 +4,9 @@ > This documentation is not in the standard format and EXDC1 is not scheduled to be developed as of 06/26/2025. -
- - - Figure 1: Exciter EXDC1 model. Figure courtesy of [PoweWorld](https://www.powerworld.com/WebHelp/). -
+![](../../../../../docs/Figures/EXDC1.JPG) + +Figure 1: Exciter EXDC1 model. Figure courtesy of [PoweWorld](https://www.powerworld.com/WebHelp/). ## Nomenclature diff --git a/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md b/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md index 1073c54ac..fe51dbaca 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md @@ -17,11 +17,9 @@ Notes: Standard model of the EXPIC1 Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/EXPIC1_diagram.png) - Figure 1: Exciter EXPIC1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter EXPIC1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -164,7 +162,7 @@ $I_{\mathrm{fd}}$ | [p.u.] | Machine field current \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md index 9bb2ef5fb..d6b252825 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md @@ -9,12 +9,9 @@ Notes: - The current implementation uses its `Bus` reference as a proxy for $E_C$. - This direct coupling affects numerical conditioning; production models typically use a decoupling reactance for the exciter-current path that forms $E_C$. -
- +![](../../../../../docs/Figures/PhasorDynamics_IEEET1_Diagram.png) - - Figure 1: Exciter IEEET1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter IEEET1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -133,7 +130,7 @@ so that $\dot V_R$ is the anti-windup limited derivative. \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) target and smooth approximation. +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md b/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md index 8d53d83b2..e5eca2ed8 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md @@ -18,11 +18,9 @@ Notes: Standard model of the SCRX Exciter. -
- +![](../../../../../docs/Figures/PhasorDynamics/SCRX_diagram.png) - Figure 1: Exciter SCRX model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter SCRX model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -118,7 +116,7 @@ $V_{\mathrm{oel}}$ | [p.u.] | Over-excitation limiter input \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md index 5ee75a980..ba79674d6 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md +++ b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md @@ -4,11 +4,9 @@ Simplified excitation system model. -
- +![](../../../../../docs/Figures/SEXS_PTI_DIAGRAM.png) - Figure 1: Exciter SEXS-PTI model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Exciter SEXS-PTI model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -84,7 +82,7 @@ so that $\dot E_{fd}$ can be written in piecewise form compactly. \end{aligned} ``` -In simulation the piecewise form above is replaced with a smooth approximation where $\phi$ is GridKit's smooth anti-windup indicator. See [CommonMath: Anti-Windup Indicator](../../../../CommonMath.md#anti-windup-indicator) for its definition, behavior, and design rationale. +In simulation the piecewise form above is replaced with a smooth approximation where $\phi$ is GridKit's smooth anti-windup indicator. See [CommonMath: Anti-Windup Indicator](../../../../CommonMath.md#antiwindup) for its definition, behavior, and design rationale. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md b/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md index 2cf037d9e..4b14a4662 100644 --- a/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md +++ b/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md @@ -19,11 +19,9 @@ Notes: Standard model of the GGOV1 Governor. -
- +![](../../../../../docs/Figures/PhasorDynamics/GGOV1_diagram.png) - Figure 1: Governor GGOV1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Governor GGOV1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -176,7 +174,7 @@ $\omega$ | [p.u.] | Machine speed deviation \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md b/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md index c6e775e37..e2bfc8a6e 100644 --- a/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md +++ b/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md @@ -20,11 +20,9 @@ Notes: Standard model of the IEEEG1 Governor. -
- +![](../../../../../docs/Figures/PhasorDynamics/IEEEG1_diagram.png) - Figure 1: Governor IEEEG1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Governor IEEEG1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -170,7 +168,7 @@ $P_{\mathrm{aux}}$ | [p.u.] | Auxiliary power input | Sour \end{aligned} ``` -CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#antiwindup) target and smooth approximation. ### Algebraic Equations diff --git a/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md b/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md index c51dc72b1..8e57404da 100644 --- a/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md +++ b/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md @@ -4,11 +4,9 @@ Standard model of the stream turbine -
- - - Figure 1: Governor TGOV1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+![](../../../../../docs/Figures/TGOV1.JPG) + +Figure 1: Governor TGOV1 model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters @@ -83,7 +81,7 @@ The algebraic equation dictating the mechnical power output. \end{aligned} ``` -In simulation the piecewise form above is replaced with a smooth approximation where $\phi$ is GridKit's smooth anti-windup indicator. See [CommonMath: Anti-Windup Indicator](../../../../CommonMath.md#anti-windup-indicator) for its definition, behavior, and design rationale. +In simulation the piecewise form above is replaced with a smooth approximation where $\phi$ is GridKit's smooth anti-windup indicator. See [CommonMath: Anti-Windup Indicator](../../../../CommonMath.md#antiwindup) for its definition, behavior, and design rationale. ## Initialization At steady state we assume that $P_v$ is at or within its limits. This implies the initial conditions are a function of $P_m$ which is equal to the electric torque. diff --git a/GridKit/Model/PhasorDynamics/SignalNode/README.md b/GridKit/Model/PhasorDynamics/SignalNode/README.md new file mode 100644 index 000000000..c05d85a0d --- /dev/null +++ b/GridKit/Model/PhasorDynamics/SignalNode/README.md @@ -0,0 +1,15 @@ +# Signal Node + +Signal nodes provide scalar connection points between phasor-dynamics +components. Components attach external inputs to signal nodes and assign +internal outputs to signal nodes through `ComponentSignals`. + +A linked signal node stores a pointer to the component variable that owns the +signal value, so other connected components can read or initialize that value +without owning the producing model. + +## Model Parameters + +Symbol | Description +-------|------------ +`signal_id` | Unique identifier for the signal node diff --git a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md index d01c45be3..e5765ba1a 100644 --- a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md +++ b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md @@ -5,11 +5,9 @@ blocks, washout, and output limiter. ## Block Diagram -
- +![](../../../../../docs/Figures/stabilizer_ieeest_diagram.png) - Figure 1: Stabilizer IEEEST model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Stabilizer IEEEST model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md b/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md index f380cc21c..6f3ba0cc1 100644 --- a/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md +++ b/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md @@ -8,14 +8,9 @@ -
- - - - Figure 1: Power system stabilizer PSS1A model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
- +![](../../../../../docs/Figures/PSS1A.JPG) +Figure 1: Power system stabilizer PSS1A model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md index ea8607359..96679af3f 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md @@ -9,12 +9,10 @@ Notes: - Same relative amount of saturation occurs on both $d$ and $q$ axis ## Block Diagram -
- - - Figure 2: GENROU. Figure courtesy of - [PowerWorld](https://www.powerworld.com/WebHelp/) -
+![](../../../../../docs/Figures/GENROU.JPG) + +Figure 2: GENROU. Figure courtesy of +[PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md index bcb0136a7..8bbf0a206 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md @@ -13,12 +13,10 @@ Notes: - Only d-axis affected by saturation ## Block Diagram -
- +![](../../../../../docs/Figures/GENSAL.JPG) - Figure 2: GENSAL. Figure courtesy of - [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 2: GENSAL. Figure courtesy of +[PowerWorld](https://www.powerworld.com/WebHelp/) ## Model Parameters diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md b/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md index e7fd5fbc9..39c86cc2d 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md @@ -2,12 +2,10 @@ ## Convention -
- +![](../../../../docs/Figures/SM1.JPG) - Figure 1: Synchronous Machine. Figure courtesy of - [PowerWorld](https://www.powerworld.com/files/Synchronous-Machines.pdf/) -
+Figure 1: Synchronous Machine. Figure courtesy of +[PowerWorld](https://www.powerworld.com/files/Synchronous-Machines.pdf/) The following conventions are used for the d-q reference frame. - The q-axis leads the d-axis diff --git a/GridKit/Model/PowerFlow/Branch/README.md b/GridKit/Model/PowerFlow/Branch/README.md index 3440da3eb..c9c8eb06a 100644 --- a/GridKit/Model/PowerFlow/Branch/README.md +++ b/GridKit/Model/PowerFlow/Branch/README.md @@ -6,12 +6,9 @@ Transmission lines and different types of transformers (traditional, Load Tap-Ch The most common circuit that is used to represent the transmission line model is $`\pi`$ circuit as shown in Figure 1. The nominal flow direction is from sending bus _s_ to receiving bus _r_. -
- - - - Figure 1: Transmission line $`\pi`$ equivalent circuit -
+![](../../../../docs/Figures/TL.jpg) + +Figure 1: Transmission line $`\pi`$ equivalent circuit Here ``` math @@ -107,13 +104,9 @@ These quantities are treated as _loads_ and are substracted from $`P`$ and $`Q`$ The branch model can be created by adding the ideal transformer in series with the $`\pi`$ circuit as shown in Figure 2 where $`\tau`$ is a tap ratio magnitude and $`\theta_{shift}`$is the phase shift angle. -
- - - - Figure 2: Branch equivalent circuit -
+![](../../../../docs/Figures/branch.jpg) +Figure 2: Branch equivalent circuit The branch admitance matrix is then: diff --git a/GridKit/Model/PowerFlow/Bus/README.md b/GridKit/Model/PowerFlow/Bus/README.md index c67f718cc..d7f2288cb 100644 --- a/GridKit/Model/PowerFlow/Bus/README.md +++ b/GridKit/Model/PowerFlow/Bus/README.md @@ -27,14 +27,9 @@ There exist two: - *Generator convention*: current **leaves** positive terminal of the circuit element, and if P(Q) is positive that means power is **delivered**, or if negative then it is **absorbed**. -
- - - - Figure 1: Sign convention for the power flow at the bus $`i`$ -
- +![](../../../../docs/Figures/bus_variables.jpg) +Figure 1: Sign convention for the power flow at the bus $`i`$ Using the previously defined sign convention, real and reactive power **delivered** to bus $`i`$ are then defined as follows: diff --git a/application/PhasorDynamics/README.md b/application/PhasorDynamics/README.md index c3da80264..7f7d0f902 100644 --- a/application/PhasorDynamics/README.md +++ b/application/PhasorDynamics/README.md @@ -12,7 +12,7 @@ `reference_file` | A string containing the name of the case `error_tolerance` | A string containing the name of the case -[^1]: See system model [case format](../../Model/PhasorDynamics/INPUT_FORMAT.md) +[^1]: See system model [case format](../../GridKit/Model/PhasorDynamics/INPUT_FORMAT.md) ## Events diff --git a/docs/Doxyfile b/docs/Doxyfile index 8564e075e..15c1cb77b 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,5 +1,5 @@ PROJECT_NAME = "GridKit" -OUTPUT_DIRECTORY = . +OUTPUT_DIRECTORY = ../build/docs/doxygen INPUT = ../GridKit RECURSIVE = YES diff --git a/docs/GridKit/CommonMath.md b/docs/GridKit/CommonMath.md new file mode 100644 index 000000000..5c5b8a5fa --- /dev/null +++ b/docs/GridKit/CommonMath.md @@ -0,0 +1,6 @@ +# CommonMath + +```{include} ../../GridKit/CommonMath.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/Bus/README.md b/docs/GridKit/Model/EMT/Bus/README.md new file mode 100644 index 000000000..251bb5167 --- /dev/null +++ b/docs/GridKit/Model/EMT/Bus/README.md @@ -0,0 +1,6 @@ +# Bus + +```{include} ../../../../../GridKit/Model/EMT/Bus/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md b/docs/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md new file mode 100644 index 000000000..9f3caaec5 --- /dev/null +++ b/docs/GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md @@ -0,0 +1,6 @@ +# BranchLumpedConstant + +```{include} ../../../../../../../GridKit/Model/EMT/Component/Branch/BranchLumpedConstant/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/Component/Branch/README.md b/docs/GridKit/Model/EMT/Component/Branch/README.md new file mode 100644 index 000000000..27dca5bee --- /dev/null +++ b/docs/GridKit/Model/EMT/Component/Branch/README.md @@ -0,0 +1,14 @@ +# Branch + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +BranchLumpedConstant +``` + +```{include} ../../../../../../GridKit/Model/EMT/Component/Branch/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/Component/LoadRL/README.md b/docs/GridKit/Model/EMT/Component/LoadRL/README.md new file mode 100644 index 000000000..ce5aa7f65 --- /dev/null +++ b/docs/GridKit/Model/EMT/Component/LoadRL/README.md @@ -0,0 +1,6 @@ +# LoadRL + +```{include} ../../../../../../GridKit/Model/EMT/Component/LoadRL/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/Component/README.md b/docs/GridKit/Model/EMT/Component/README.md new file mode 100644 index 000000000..587ecb957 --- /dev/null +++ b/docs/GridKit/Model/EMT/Component/README.md @@ -0,0 +1,16 @@ +# Component + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Branch +LoadRL +VoltageSource +``` + +```{include} ../../../../../GridKit/Model/EMT/Component/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/Component/VoltageSource/README.md b/docs/GridKit/Model/EMT/Component/VoltageSource/README.md new file mode 100644 index 000000000..fd8ce8d32 --- /dev/null +++ b/docs/GridKit/Model/EMT/Component/VoltageSource/README.md @@ -0,0 +1,6 @@ +# VoltageSource + +```{include} ../../../../../../GridKit/Model/EMT/Component/VoltageSource/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/EMT/README.md b/docs/GridKit/Model/EMT/README.md new file mode 100644 index 000000000..7a6e5fe76 --- /dev/null +++ b/docs/GridKit/Model/EMT/README.md @@ -0,0 +1,15 @@ +# EMT + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Bus +Component +``` + +```{include} ../../../../GridKit/Model/EMT/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Branch/README.md b/docs/GridKit/Model/PhasorDynamics/Branch/README.md new file mode 100644 index 000000000..f1adc4f9d --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Branch/README.md @@ -0,0 +1,6 @@ +# Branch + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Branch/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Bus/README.md b/docs/GridKit/Model/PhasorDynamics/Bus/README.md new file mode 100644 index 000000000..7ef7e2a7c --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Bus/README.md @@ -0,0 +1,6 @@ +# Bus + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Bus/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/BusFault/README.md b/docs/GridKit/Model/PhasorDynamics/BusFault/README.md new file mode 100644 index 000000000..5e8126a10 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/BusFault/README.md @@ -0,0 +1,6 @@ +# BusFault + +```{include} ../../../../../GridKit/Model/PhasorDynamics/BusFault/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md b/docs/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md new file mode 100644 index 000000000..7189c86bd --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md @@ -0,0 +1,6 @@ +# BusToSignalAdapter + +```{include} ../../../../../GridKit/Model/PhasorDynamics/BusToSignalAdapter/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Converter/README.md b/docs/GridKit/Model/PhasorDynamics/Converter/README.md new file mode 100644 index 000000000..fa56e8dd8 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Converter/README.md @@ -0,0 +1,16 @@ +# Converter + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +REGCA +REGCB +REECA +``` + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Converter/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Converter/REECA/README.md b/docs/GridKit/Model/PhasorDynamics/Converter/REECA/README.md new file mode 100644 index 000000000..e84371269 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Converter/REECA/README.md @@ -0,0 +1,6 @@ +# REECA + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Converter/REECA/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md b/docs/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md new file mode 100644 index 000000000..499bb0c57 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Converter/REGCA/README.md @@ -0,0 +1,6 @@ +# REGCA + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Converter/REGCA/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md b/docs/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md new file mode 100644 index 000000000..d19a58851 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Converter/REGCB/README.md @@ -0,0 +1,6 @@ +# REGCB + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Converter/REGCB/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md new file mode 100644 index 000000000..435254723 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md @@ -0,0 +1,6 @@ +# ESAC6A + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/ESAC6A/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md new file mode 100644 index 000000000..eaf45c4b8 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md @@ -0,0 +1,6 @@ +# ESDC2A + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/ESDC2A/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md new file mode 100644 index 000000000..55d73db77 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md @@ -0,0 +1,6 @@ +# ESST4B + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/ESST4B/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md new file mode 100644 index 000000000..5a46913bb --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md @@ -0,0 +1,6 @@ +# EXAC1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/EXAC1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md new file mode 100644 index 000000000..2ca7536f6 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md @@ -0,0 +1,6 @@ +# EXAC2 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/EXAC2/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md new file mode 100644 index 000000000..fc7417fe8 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md @@ -0,0 +1,6 @@ +# EXDC1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/EXDC1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md new file mode 100644 index 000000000..d04c95c7f --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md @@ -0,0 +1,6 @@ +# EXPIC1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/EXPIC1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md new file mode 100644 index 000000000..9064ca815 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md @@ -0,0 +1,6 @@ +# IEEET1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/README.md new file mode 100644 index 000000000..2980fd53c --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/README.md @@ -0,0 +1,23 @@ +# Exciter + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +ESAC6A +IEEET1 +EXDC1 +ESDC2A +EXAC1 +ESST4B +SCRX +EXAC2 +EXPIC1 +SEXS-PTI +``` + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Exciter/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md new file mode 100644 index 000000000..ada503dab --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md @@ -0,0 +1,6 @@ +# SCRX + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/SCRX/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md b/docs/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md new file mode 100644 index 000000000..05c770732 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md @@ -0,0 +1,6 @@ +# SEXS-PTI + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md b/docs/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md new file mode 100644 index 000000000..16bd080c5 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md @@ -0,0 +1,6 @@ +# GGOV1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Governor/GGOV1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md b/docs/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md new file mode 100644 index 000000000..e0c238862 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md @@ -0,0 +1,6 @@ +# IEEEG1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Governor/IEEEG1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Governor/README.md b/docs/GridKit/Model/PhasorDynamics/Governor/README.md new file mode 100644 index 000000000..fe3d5b5b7 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Governor/README.md @@ -0,0 +1,16 @@ +# Governor + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +TGOV1 +IEEEG1 +GGOV1 +``` + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Governor/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md b/docs/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md new file mode 100644 index 000000000..fe595d696 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md @@ -0,0 +1,6 @@ +# TGOV1 + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md b/docs/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md new file mode 100644 index 000000000..5b290cfae --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md @@ -0,0 +1,6 @@ +# Input Format + +```{include} ../../../../GridKit/Model/PhasorDynamics/INPUT_FORMAT.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Load/README.md b/docs/GridKit/Model/PhasorDynamics/Load/README.md new file mode 100644 index 000000000..d595e2985 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Load/README.md @@ -0,0 +1,6 @@ +# Load + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Load/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/LoadZIP/README.md b/docs/GridKit/Model/PhasorDynamics/LoadZIP/README.md new file mode 100644 index 000000000..632aa337d --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/LoadZIP/README.md @@ -0,0 +1,6 @@ +# LoadZIP + +```{include} ../../../../../GridKit/Model/PhasorDynamics/LoadZIP/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/README.md b/docs/GridKit/Model/PhasorDynamics/README.md new file mode 100644 index 000000000..cc93a9d23 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/README.md @@ -0,0 +1,26 @@ +# PhasorDynamics + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Input Format +Branch +Bus +BusFault +BusToSignalAdapter +Converter +Exciter +Governor +Load +LoadZIP +SignalNode +Stabilizer +SynchronousMachine +``` + +```{include} ../../../../GridKit/Model/PhasorDynamics/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/SignalNode/README.md b/docs/GridKit/Model/PhasorDynamics/SignalNode/README.md new file mode 100644 index 000000000..41e8eea1a --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/SignalNode/README.md @@ -0,0 +1,6 @@ +# SignalNode + +```{include} ../../../../../GridKit/Model/PhasorDynamics/SignalNode/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md b/docs/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md new file mode 100644 index 000000000..2b75014c8 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md @@ -0,0 +1,6 @@ +# IEEEST + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md b/docs/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md new file mode 100644 index 000000000..1c1c33632 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md @@ -0,0 +1,6 @@ +# PSS1A + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/Stabilizer/PSS1A/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/Stabilizer/README.md b/docs/GridKit/Model/PhasorDynamics/Stabilizer/README.md new file mode 100644 index 000000000..d67f66663 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/Stabilizer/README.md @@ -0,0 +1,15 @@ +# Stabilizer + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +IEEEST +PSS1A +``` + +```{include} ../../../../../GridKit/Model/PhasorDynamics/Stabilizer/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md new file mode 100644 index 000000000..adaa79d23 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md @@ -0,0 +1,6 @@ +# GENROU + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md new file mode 100644 index 000000000..402e58221 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md @@ -0,0 +1,6 @@ +# GENSAL + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/SynchronousMachine/GENSALwS/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/README.md b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/README.md new file mode 100644 index 000000000..22f3fe8e4 --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/README.md @@ -0,0 +1,6 @@ +# GenClassical + +```{include} ../../../../../../GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md new file mode 100644 index 000000000..eedbbe0de --- /dev/null +++ b/docs/GridKit/Model/PhasorDynamics/SynchronousMachine/README.md @@ -0,0 +1,16 @@ +# SynchronousMachine + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +GenClassical +GENROU +GENSAL +``` + +```{include} ../../../../../GridKit/Model/PhasorDynamics/SynchronousMachine/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerElectronics/DistributedGenerator/README.md b/docs/GridKit/Model/PowerElectronics/DistributedGenerator/README.md new file mode 100644 index 000000000..47c358d81 --- /dev/null +++ b/docs/GridKit/Model/PowerElectronics/DistributedGenerator/README.md @@ -0,0 +1,5 @@ +# DistributedGenerator + +```{include} ../../../../../GridKit/Model/PowerElectronics/DistributedGenerator/README.md +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerElectronics/MicrogridBusDQ/README.md b/docs/GridKit/Model/PowerElectronics/MicrogridBusDQ/README.md new file mode 100644 index 000000000..8f6a8e8e4 --- /dev/null +++ b/docs/GridKit/Model/PowerElectronics/MicrogridBusDQ/README.md @@ -0,0 +1,5 @@ +# MicrogridBusDQ + +```{include} ../../../../../GridKit/Model/PowerElectronics/MicrogridBusDQ/README.md +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerElectronics/MicrogridLine/README.md b/docs/GridKit/Model/PowerElectronics/MicrogridLine/README.md new file mode 100644 index 000000000..9da5568b0 --- /dev/null +++ b/docs/GridKit/Model/PowerElectronics/MicrogridLine/README.md @@ -0,0 +1,5 @@ +# MicrogridLine + +```{include} ../../../../../GridKit/Model/PowerElectronics/MicrogridLine/README.md +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerElectronics/MicrogridLoad/README.md b/docs/GridKit/Model/PowerElectronics/MicrogridLoad/README.md new file mode 100644 index 000000000..8637cabc0 --- /dev/null +++ b/docs/GridKit/Model/PowerElectronics/MicrogridLoad/README.md @@ -0,0 +1,5 @@ +# MicrogridLoad + +```{include} ../../../../../GridKit/Model/PowerElectronics/MicrogridLoad/README.md +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerElectronics/README.md b/docs/GridKit/Model/PowerElectronics/README.md new file mode 100644 index 000000000..e6ab491a7 --- /dev/null +++ b/docs/GridKit/Model/PowerElectronics/README.md @@ -0,0 +1,16 @@ +# PowerElectronics + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +DistributedGenerator +MicrogridBusDQ +MicrogridLine +MicrogridLoad +``` + +```{include} ../../../../GridKit/Model/PowerElectronics/README.md +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerFlow/Branch/README.md b/docs/GridKit/Model/PowerFlow/Branch/README.md new file mode 100644 index 000000000..72bfa3d2a --- /dev/null +++ b/docs/GridKit/Model/PowerFlow/Branch/README.md @@ -0,0 +1,6 @@ +# Branch + +```{include} ../../../../../GridKit/Model/PowerFlow/Branch/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerFlow/Bus/README.md b/docs/GridKit/Model/PowerFlow/Bus/README.md new file mode 100644 index 000000000..3c817be13 --- /dev/null +++ b/docs/GridKit/Model/PowerFlow/Bus/README.md @@ -0,0 +1,6 @@ +# Bus + +```{include} ../../../../../GridKit/Model/PowerFlow/Bus/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerFlow/Load/README.md b/docs/GridKit/Model/PowerFlow/Load/README.md new file mode 100644 index 000000000..2e491131b --- /dev/null +++ b/docs/GridKit/Model/PowerFlow/Load/README.md @@ -0,0 +1,6 @@ +# Load + +```{include} ../../../../../GridKit/Model/PowerFlow/Load/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/GridKit/Model/PowerFlow/README.md b/docs/GridKit/Model/PowerFlow/README.md new file mode 100644 index 000000000..013ad8b5d --- /dev/null +++ b/docs/GridKit/Model/PowerFlow/README.md @@ -0,0 +1,15 @@ +# PowerFlow + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Branch +Bus +Load +``` + +```{include} ../../../../GridKit/Model/PowerFlow/README.md +:relative-images: +``` diff --git a/docs/GridKit/Model/README.md b/docs/GridKit/Model/README.md new file mode 100644 index 000000000..d725e4a3a --- /dev/null +++ b/docs/GridKit/Model/README.md @@ -0,0 +1,13 @@ +# Models + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +CommonMath <../CommonMath> +EMT +PhasorDynamics +PowerElectronics +PowerFlow +``` diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 000000000..b30e0e212 --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,6 @@ +# Installation + +```{include} ../INSTALL.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/README.md b/docs/README.md index 923ca5d1a..79238fe4a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,10 +8,11 @@ GridKit documentation can be built two ways: ## Read the Docs Build The Read the Docs proof of concept is configured by `.readthedocs.yaml`. -Before Sphinx runs, the build generates Markdown wrapper pages and Doxygen XML: +Static MyST wrapper pages under `docs/` include the repository Markdown +files directly. Before Sphinx runs, the build generates Doxygen XML: ```sh -python docs/generate_model_docs.py +rm -rf build/docs/doxygen cd docs && doxygen Doxyfile ``` @@ -19,14 +20,14 @@ To test the same flow locally: ```sh python -m pip install -r docs/requirements.txt -python docs/generate_model_docs.py +rm -rf build/docs/doxygen cd docs && doxygen Doxyfile cd .. sphinx-build -T -b html docs docs/_build/html ``` -The generated Sphinx files, Doxygen XML, and HTML output are build artifacts and -should not be committed. +Doxygen XML under `build/docs/doxygen`, generated API reference files, and HTML +output are build artifacts and should not be committed. ## CMake Doxygen Target diff --git a/docs/api.md b/docs/api.md index 39b30249b..8c60ce7a7 100644 --- a/docs/api.md +++ b/docs/api.md @@ -16,20 +16,3 @@ Model Utilities Utilities Testing ``` - -The API reference is organized by the primary GridKit namespaces. Lower-level -class, struct, enum, function, and file pages are generated by Exhale and linked -from these namespace pages. - -## Main Areas - -- [GridKit core](api/reference/namespace_GridKit.rst) -- [Solvers](api/reference/namespace_AnalysisManager.rst) -- [GridKit::PhasorDynamics](api/reference/namespace_GridKit__PhasorDynamics.rst) -- [GridKit::PowerElectronics](api/reference/namespace_GridKit__PowerElectronics.rst) -- [GridKit::PowerFlowData](api/reference/namespace_GridKit__PowerFlowData.rst) -- [GridKit::LinearAlgebra](api/reference/namespace_GridKit__LinearAlgebra.rst) -- [GridKit::Model](api/reference/namespace_GridKit__Model.rst) -- [GridKit::Math](api/reference/namespace_GridKit__Math.rst) -- [GridKit::Utilities](api/reference/namespace_GridKit__Utilities.rst) -- [GridKit::Testing](api/reference/namespace_GridKit__Testing.rst) diff --git a/docs/application/PhasorDynamics/ContingencyAnalysis.md b/docs/application/PhasorDynamics/ContingencyAnalysis.md new file mode 100644 index 000000000..40ee2c3f9 --- /dev/null +++ b/docs/application/PhasorDynamics/ContingencyAnalysis.md @@ -0,0 +1,5 @@ +# ContingencyAnalysis + +Source: `application/PhasorDynamics/ContingencyAnalysis.cpp` + +Input format: [Phasor Dynamics](README.md) diff --git a/docs/application/PhasorDynamics/DynamicSimulation.md b/docs/application/PhasorDynamics/DynamicSimulation.md new file mode 100644 index 000000000..1f7322ec7 --- /dev/null +++ b/docs/application/PhasorDynamics/DynamicSimulation.md @@ -0,0 +1,5 @@ +# DynamicSimulation + +Source: `application/PhasorDynamics/DynamicSimulation.cpp` + +Input format: [Phasor Dynamics](README.md) diff --git a/docs/application/PhasorDynamics/README.md b/docs/application/PhasorDynamics/README.md new file mode 100644 index 000000000..10aaadf18 --- /dev/null +++ b/docs/application/PhasorDynamics/README.md @@ -0,0 +1,15 @@ +# Phasor Dynamics + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +DynamicSimulation +ContingencyAnalysis +``` + +```{include} ../../../application/PhasorDynamics/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/application/README.md b/docs/application/README.md new file mode 100644 index 000000000..b7faa35c0 --- /dev/null +++ b/docs/application/README.md @@ -0,0 +1,9 @@ +# Applications + +```{toctree} +:maxdepth: 3 +:titlesonly: +:hidden: + +Phasor Dynamics +``` diff --git a/docs/applications/index.md b/docs/applications/index.md deleted file mode 100644 index 14211ecb9..000000000 --- a/docs/applications/index.md +++ /dev/null @@ -1,15 +0,0 @@ -# Applications - -```{toctree} -:maxdepth: 2 -:titlesonly: -:hidden: - -../generated/application-input-format -``` - -## PhasorDynamics - -- [Application Input Format](../generated/application-input-format.md) -- `application/PhasorDynamics/DynamicSimulation.cpp` -- `application/PhasorDynamics/ContingencyAnalysis.cpp` diff --git a/docs/conf.py b/docs/conf.py index 3bf0b78d5..5a33160c9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,13 +7,14 @@ docs_dir = Path(__file__).parent.resolve() -extensions = ["breathe", "exhale", "myst_parser", "sphinx.ext.graphviz"] +extensions = ["breathe", "exhale", "myst_parser"] -breathe_projects = {"GridKit": str(docs_dir / "xml")} +breathe_projects = {"GridKit": str(docs_dir.parent / "build/docs/doxygen/xml")} breathe_default_project = "GridKit" -def exhale_specs(kind): +# Exhale's default class/struct pages also include protected and undocumented members. +def public_member_specs(kind): if kind in {"class", "struct"}: return [":members:"] return [] @@ -23,41 +24,32 @@ def exhale_specs(kind): "containmentFolder": "./api/reference", "rootFileName": "EXCLUDE", "doxygenStripFromPath": str(docs_dir.parent), - "createTreeView": False, "customSpecificationsMapping": exhale_utils.makeCustomSpecificationsMapping( - exhale_specs + public_member_specs ), "contentsDirectives": False, - "fullToctreeMaxDepth": 1, "pageLevelConfigMeta": ":orphan:", } primary_domain = "cpp" html_theme = "sphinx_rtd_theme" -html_extra_path = ["Figures"] html_theme_options = { - "collapse_navigation": False, + "collapse_navigation": True, "navigation_depth": 6, "titles_only": True, } -source_suffix = { - ".rst": "restructuredtext", - ".md": "markdown", -} - myst_enable_extensions = [ "amsmath", "dollarmath", "html_image", ] +myst_fence_as_directive = ["math"] myst_heading_anchors = 5 + exclude_patterns = [ "_build", - "api/generated/**", - "xml", "README.md", - "superpowers/**", ] diff --git a/docs/development/CHANGELOG.md b/docs/development/CHANGELOG.md new file mode 100644 index 000000000..004c4928a --- /dev/null +++ b/docs/development/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +```{include} ../../CHANGELOG.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/development/CONTRIBUTING.md b/docs/development/CONTRIBUTING.md new file mode 100644 index 000000000..a3db186d1 --- /dev/null +++ b/docs/development/CONTRIBUTING.md @@ -0,0 +1,6 @@ +# Contributing + +```{include} ../../CONTRIBUTING.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/development/README.md b/docs/development/README.md new file mode 100644 index 000000000..e6c32e266 --- /dev/null +++ b/docs/development/README.md @@ -0,0 +1,12 @@ +# Development + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +Contributing +Buildsystem +Documentation Build +Changelog +``` diff --git a/docs/development/buildsystem/README.md b/docs/development/buildsystem/README.md new file mode 100644 index 000000000..3d3819565 --- /dev/null +++ b/docs/development/buildsystem/README.md @@ -0,0 +1,5 @@ +# Buildsystem + +```{include} ../../../buildsystem/README.md +:relative-images: +``` diff --git a/docs/development/docs/README.md b/docs/development/docs/README.md new file mode 100644 index 000000000..06d53cb30 --- /dev/null +++ b/docs/development/docs/README.md @@ -0,0 +1,6 @@ +# Documentation Build + +```{include} ../../README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/development/index.md b/docs/development/index.md deleted file mode 100644 index 12d13f933..000000000 --- a/docs/development/index.md +++ /dev/null @@ -1,19 +0,0 @@ -# Development - -```{toctree} -:maxdepth: 2 -:titlesonly: -:hidden: - -../generated/contributing -../generated/buildsystem -../generated/documentation-build -../generated/changelog -``` - -## Sections - -- [Contributing](../generated/contributing.md) -- [Buildsystem](../generated/buildsystem.md) -- [Documentation Build](../generated/documentation-build.md) -- [Changelog](../generated/changelog.md) diff --git a/docs/examples/PhasorDynamics/Large/Illinois/README.md b/docs/examples/PhasorDynamics/Large/Illinois/README.md new file mode 100644 index 000000000..ef619f11a --- /dev/null +++ b/docs/examples/PhasorDynamics/Large/Illinois/README.md @@ -0,0 +1,6 @@ +# Illinois + +```{include} ../../../../../examples/PhasorDynamics/Large/Illinois/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Large/README.md b/docs/examples/PhasorDynamics/Large/README.md new file mode 100644 index 000000000..3c272648e --- /dev/null +++ b/docs/examples/PhasorDynamics/Large/README.md @@ -0,0 +1,11 @@ +# Large + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Illinois +Texas +WECC +``` diff --git a/docs/examples/PhasorDynamics/Large/Texas/README.md b/docs/examples/PhasorDynamics/Large/Texas/README.md new file mode 100644 index 000000000..b6f0a2708 --- /dev/null +++ b/docs/examples/PhasorDynamics/Large/Texas/README.md @@ -0,0 +1,6 @@ +# Texas + +```{include} ../../../../../examples/PhasorDynamics/Large/Texas/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Large/WECC/README.md b/docs/examples/PhasorDynamics/Large/WECC/README.md new file mode 100644 index 000000000..fe16c3a72 --- /dev/null +++ b/docs/examples/PhasorDynamics/Large/WECC/README.md @@ -0,0 +1,6 @@ +# WECC + +```{include} ../../../../../examples/PhasorDynamics/Large/WECC/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Medium/Hawaii/README.md b/docs/examples/PhasorDynamics/Medium/Hawaii/README.md new file mode 100644 index 000000000..b31917834 --- /dev/null +++ b/docs/examples/PhasorDynamics/Medium/Hawaii/README.md @@ -0,0 +1,6 @@ +# Hawaii + +```{include} ../../../../../examples/PhasorDynamics/Medium/Hawaii/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Medium/NewEngland/README.md b/docs/examples/PhasorDynamics/Medium/NewEngland/README.md new file mode 100644 index 000000000..4d2e32cb5 --- /dev/null +++ b/docs/examples/PhasorDynamics/Medium/NewEngland/README.md @@ -0,0 +1,6 @@ +# NewEngland + +```{include} ../../../../../examples/PhasorDynamics/Medium/NewEngland/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Medium/README.md b/docs/examples/PhasorDynamics/Medium/README.md new file mode 100644 index 000000000..6bb0cbca1 --- /dev/null +++ b/docs/examples/PhasorDynamics/Medium/README.md @@ -0,0 +1,10 @@ +# Medium + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Hawaii +NewEngland +``` diff --git a/docs/examples/PhasorDynamics/README.md b/docs/examples/PhasorDynamics/README.md new file mode 100644 index 000000000..c7d2bbf1c --- /dev/null +++ b/docs/examples/PhasorDynamics/README.md @@ -0,0 +1,12 @@ +# PhasorDynamics + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Large +Medium +Small +Tiny +``` diff --git a/docs/examples/PhasorDynamics/Small/README.md b/docs/examples/PhasorDynamics/Small/README.md new file mode 100644 index 000000000..dacd336f8 --- /dev/null +++ b/docs/examples/PhasorDynamics/Small/README.md @@ -0,0 +1,9 @@ +# Small + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +TwoArea +``` diff --git a/docs/examples/PhasorDynamics/Small/TwoArea/README.md b/docs/examples/PhasorDynamics/Small/TwoArea/README.md new file mode 100644 index 000000000..ae355b039 --- /dev/null +++ b/docs/examples/PhasorDynamics/Small/TwoArea/README.md @@ -0,0 +1,6 @@ +# TwoArea + +```{include} ../../../../../examples/PhasorDynamics/Small/TwoArea/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Tiny/README.md b/docs/examples/PhasorDynamics/Tiny/README.md new file mode 100644 index 000000000..f624a9299 --- /dev/null +++ b/docs/examples/PhasorDynamics/Tiny/README.md @@ -0,0 +1,9 @@ +# Tiny + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +TwoBus +``` diff --git a/docs/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md b/docs/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md new file mode 100644 index 000000000..ff711e698 --- /dev/null +++ b/docs/examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md @@ -0,0 +1,6 @@ +# GENSAL + +```{include} ../../../../../../examples/PhasorDynamics/Tiny/TwoBus/Gensal/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PhasorDynamics/Tiny/TwoBus/README.md b/docs/examples/PhasorDynamics/Tiny/TwoBus/README.md new file mode 100644 index 000000000..812eb7908 --- /dev/null +++ b/docs/examples/PhasorDynamics/Tiny/TwoBus/README.md @@ -0,0 +1,9 @@ +# TwoBus + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +GENSAL +``` diff --git a/docs/examples/PowerElectronics/Microgrid/README.md b/docs/examples/PowerElectronics/Microgrid/README.md new file mode 100644 index 000000000..a12d20b72 --- /dev/null +++ b/docs/examples/PowerElectronics/Microgrid/README.md @@ -0,0 +1,5 @@ +# Microgrid + +```{include} ../../../../examples/PowerElectronics/Microgrid/README.md +:relative-images: +``` diff --git a/docs/examples/PowerElectronics/README.md b/docs/examples/PowerElectronics/README.md new file mode 100644 index 000000000..2658685a9 --- /dev/null +++ b/docs/examples/PowerElectronics/README.md @@ -0,0 +1,10 @@ +# PowerElectronics + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Microgrid +ScaleMicrogrid +``` diff --git a/docs/examples/PowerElectronics/ScaleMicrogrid/README.md b/docs/examples/PowerElectronics/ScaleMicrogrid/README.md new file mode 100644 index 000000000..c8d36c436 --- /dev/null +++ b/docs/examples/PowerElectronics/ScaleMicrogrid/README.md @@ -0,0 +1,5 @@ +# ScaleMicrogrid + +```{include} ../../../../examples/PowerElectronics/ScaleMicrogrid/README.md +:relative-images: +``` diff --git a/docs/examples/PowerFlow/Grid3Bus/README.md b/docs/examples/PowerFlow/Grid3Bus/README.md new file mode 100644 index 000000000..13eceaf03 --- /dev/null +++ b/docs/examples/PowerFlow/Grid3Bus/README.md @@ -0,0 +1,6 @@ +# Grid3Bus + +```{include} ../../../../examples/PowerFlow/Grid3Bus/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/examples/PowerFlow/README.md b/docs/examples/PowerFlow/README.md new file mode 100644 index 000000000..a09011b8f --- /dev/null +++ b/docs/examples/PowerFlow/README.md @@ -0,0 +1,9 @@ +# PowerFlow + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +Grid3Bus +``` diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 000000000..d06c6f640 --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,16 @@ +# Examples + +```{toctree} +:maxdepth: 4 +:titlesonly: +:hidden: + +PhasorDynamics +PowerElectronics +PowerFlow +``` + +```{include} ../../examples/README.md +:start-line: 1 +:relative-images: +``` diff --git a/docs/generate_model_docs.py b/docs/generate_model_docs.py deleted file mode 100644 index 555155b92..000000000 --- a/docs/generate_model_docs.py +++ /dev/null @@ -1,386 +0,0 @@ -#!/usr/bin/env python3 -"""Generate Sphinx pages from selected GridKit Markdown files.""" - -from __future__ import annotations - -import os -import re -import shutil -import subprocess -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[1] -DOCS = ROOT / "docs" -MODEL = ROOT / "GridKit" / "Model" -EXAMPLES = ROOT / "examples" -GITHUB_REPO = "https://github.com/ORNL/GridKit" - -GENERATED = DOCS / "generated" -GENERATED_MODELS = DOCS / "models" / "generated" -GENERATED_EXAMPLES = DOCS / "examples" / "generated" - -COMMON_MATH = ROOT / "GridKit" / "CommonMath.md" -PHASOR_INPUT_FORMAT = MODEL / "PhasorDynamics" / "INPUT_FORMAT.md" - -PROJECT_PAGES = { - ROOT / "README.md": (GENERATED / "readme.md", "GridKit"), - ROOT / "INSTALL.md": (GENERATED / "install.md", "Installation"), - ROOT / "CONTRIBUTING.md": (GENERATED / "contributing.md", "Contributing"), - ROOT / "CHANGELOG.md": (GENERATED / "changelog.md", "Changelog"), - DOCS / "README.md": (GENERATED / "documentation-build.md", "Documentation Build"), - ROOT / "buildsystem" / "README.md": (GENERATED / "buildsystem.md", "Buildsystem"), - ROOT / "application" / "PhasorDynamics" / "README.md": ( - GENERATED / "application-input-format.md", - "Application Input Format", - ), -} - -MODEL_READMES = tuple(path.resolve() for path in sorted(MODEL.glob("**/README.md"))) -EXAMPLE_READMES = tuple(path.resolve() for path in sorted(EXAMPLES.glob("**/README.md"))) - - -def git_output(*args: str) -> str: - try: - return subprocess.check_output( - ["git", *args], - cwd=ROOT, - stderr=subprocess.DEVNULL, - text=True, - ).strip() - except (OSError, subprocess.CalledProcessError): - return "" - - -def source_ref() -> str: - if os.environ.get("READTHEDOCS_VERSION_TYPE") == "external": - if ref := os.environ.get("READTHEDOCS_GIT_COMMIT_HASH"): - return ref - if ref := os.environ.get("READTHEDOCS_GIT_IDENTIFIER"): - return ref - if ref := git_output("rev-parse", "--abbrev-ref", "HEAD"): - if ref != "HEAD": - return ref - return git_output("rev-parse", "HEAD") or "HEAD" - - -SOURCE_REF = source_ref() - - -def slug(text: str) -> str: - text = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", text) - text = re.sub(r"[^A-Za-z0-9.-]+", "-", text.replace("_", "-").replace("/", "-")) - return re.sub(r"-+", "-", text).strip("-").lower() - - -def relative(target: Path, page: Path) -> str: - return Path(os.path.relpath(target, page.parent)).as_posix() - - -def model_page(readme: Path) -> Path: - parts = [slug(part) for part in readme.parent.relative_to(MODEL).parts] - return GENERATED_MODELS.joinpath(*parts, "index.md") - - -def example_page(directory: Path) -> Path: - parts = [slug(part) for part in directory.relative_to(EXAMPLES).parts] - return GENERATED_EXAMPLES.joinpath(*parts, "index.md") - - -SOURCE_TO_PAGE = {source.resolve(): output for source, (output, _) in PROJECT_PAGES.items()} -SOURCE_TO_PAGE[COMMON_MATH.resolve()] = GENERATED_MODELS / "common-math.md" -SOURCE_TO_PAGE[PHASOR_INPUT_FORMAT.resolve()] = ( - GENERATED_MODELS / "phasor-dynamics" / "input-format.md" -) -SOURCE_TO_PAGE.update({readme: model_page(readme) for readme in MODEL_READMES}) -SOURCE_TO_PAGE.update({readme: example_page(readme.parent) for readme in EXAMPLE_READMES}) - - -def example_dirs() -> set[Path]: - dirs = {EXAMPLES.resolve()} - for readme in EXAMPLE_READMES: - directory = readme.parent - while directory != EXAMPLES.parent: - dirs.add(directory.resolve()) - if directory == EXAMPLES: - break - directory = directory.parent - return dirs - - -EXAMPLE_DOC_DIRS = example_dirs() - - -def title_for(source: Path) -> str: - if source in PROJECT_PAGES: - return PROJECT_PAGES[source][1] - if source == COMMON_MATH: - return "CommonMath" - if source == PHASOR_INPUT_FORMAT: - return "Input Format" - if source in EXAMPLE_READMES and source.parent == EXAMPLES: - return "Examples" - return source.parent.name - - -def toctree(entries: list[str], maxdepth: int = 2) -> str: - return ( - "```{toctree}\n" - f":maxdepth: {maxdepth}\n" - ":titlesonly:\n:hidden:\n\n" - + "\n".join(entries) - + "\n```\n" - ) - - -def children_of(directory: Path, readmes: tuple[Path, ...]) -> list[Path]: - return sorted(readme.parent for readme in readmes if readme.parent.parent == directory) - - -def example_children(directory: Path) -> list[Path]: - return sorted(path for path in EXAMPLE_DOC_DIRS if path.parent == directory and path != directory) - - -def generated_toctree(source: Path, page: Path) -> str: - entries = [] - if source in MODEL_READMES: - if source == MODEL / "PhasorDynamics" / "README.md": - entries.append(relative(SOURCE_TO_PAGE[PHASOR_INPUT_FORMAT.resolve()].with_suffix(""), page)) - entries += [ - relative(model_page(child / "README.md").with_suffix(""), page) - for child in children_of(source.parent, MODEL_READMES) - ] - elif source in EXAMPLE_READMES: - entries = [ - relative(example_page(child).with_suffix(""), page) - for child in example_children(source.parent) - ] - return f"{toctree(entries)}\n" if entries else "" - - -def split_anchor(target: str) -> tuple[str, str]: - path, sep, anchor = target.partition("#") - return path, f"{sep}{anchor}" if sep else "" - - -def is_external(target: str) -> bool: - return bool(re.match(r"^[A-Za-z][A-Za-z0-9+.-]*:", target)) - - -def fallback(source_dir: Path, target: str) -> Path | None: - candidates = [] - if target.endswith("src/Model/PowerFlow/Gen/README.md"): - candidates.append(MODEL / "PowerFlow" / "README.md") - if target.startswith("src/Model/"): - candidates.append(ROOT / "GridKit" / target.removeprefix("src/")) - if target.startswith("Model/"): - candidates.append(ROOT / "GridKit" / target) - if "Model/" in target: - candidates.append(MODEL / target.split("Model/", 1)[1]) - if source_dir.relative_to(ROOT).parts[:1] == ("application",): - candidates.append(ROOT / "GridKit" / target.removeprefix("../../")) - return next((path.resolve() for path in candidates if path.exists()), None) - - -def page_link(path: Path, anchor: str, current_page: Path) -> str | None: - path = path.resolve() - if path == COMMON_MATH.resolve() and anchor == "#anti-windup-indicator": - anchor = "#derived-functions" - if path in SOURCE_TO_PAGE: - return f"{relative(SOURCE_TO_PAGE[path], current_page)}{anchor}" - readme = (path / "README.md").resolve() if path.is_dir() else None - if readme in SOURCE_TO_PAGE: - return f"{relative(SOURCE_TO_PAGE[readme], current_page)}{anchor}" - return None - - -def rewrite_link(source_dir: Path, target: str, current_page: Path) -> str: - if not target or target.startswith("#") or is_external(target): - return target - path_text, anchor = split_anchor(target) - path = (source_dir / path_text).resolve() - if not path.exists(): - path = fallback(source_dir, path_text) or path - link = page_link(path, anchor, current_page) - if link: - return link - if path.exists() and path.is_dir(): - return f"{GITHUB_REPO}/tree/{SOURCE_REF}/{path.relative_to(ROOT).as_posix()}{anchor}" - if path.exists() or ROOT in path.parents: - return f"{relative(path, current_page)}{anchor}" - return target - - -def rewrite_asset(source_dir: Path, target: str, current_page: Path) -> str: - if not target or target.startswith("#") or is_external(target): - return target - path_text, anchor = split_anchor(target) - return f"{relative((source_dir / path_text).resolve(), current_page)}{anchor}" - - -def rewrite_markdown_links(text: str, source_dir: Path, current_page: Path) -> str: - text = re.sub( - r"!\[([^\]]*)\]\(([^)\s]+)\)", - lambda m: f"![{m.group(1)}]({rewrite_asset(source_dir, m.group(2), current_page)})", - text, - ) - return re.sub( - r"(? str: - text = re.sub(r"(?im)^\s*]*\balign=[\"']center[\"'][^>]*>\s*$", "", text) - text = re.sub(r"(?im)^\s*\s*$", "", text) - - def attr(attrs: str, name: str) -> str | None: - match = re.search(rf"\b{name}\s*=\s*([\"'])(.*?)\1", attrs, re.IGNORECASE) - return match.group(2) if match else None - - def image(match: re.Match[str]) -> str: - before, target, after = match.groups() - attrs = f"{before} {after}" - options = [] - if alt := attr(attrs, "alt"): - options.append(f":alt: {alt}") - if (align := attr(attrs, "align")) in {"left", "center", "right"}: - options.append(f":align: {align}") - options_text = "\n".join(options) - if options_text: - options_text += "\n" - target = rewrite_asset(source_dir, target, current_page) - return f"\n```{{image}} {target}\n{options_text}```\n" - - return re.sub( - r"]*)\bsrc=[\"']([^\"']+)[\"']([^>]*)>", - image, - text, - flags=re.IGNORECASE, - ) - - -def normalize_headings(text: str) -> str: - levels = sorted({len(m.group(1)) for m in re.finditer(r"(?m)^(#{1,6})(\s+)", text)}) - levels = {level: index + 2 for index, level in enumerate(levels)} - return re.sub( - r"(?m)^(#{1,6})(\s+)", - lambda m: f"{'#' * levels.get(len(m.group(1)), len(m.group(1)))}{m.group(2)}", - text, - ) - - -def strip_title(text: str, title: str) -> str: - def comparable(value: str) -> str: - return re.sub(r"[^a-z0-9]+", "", value.replace("™", "").lower()) - - match = re.match(r"\s*#\s+(.+?)\s*(?:\n+|$)", text) - if match and comparable(match.group(1)) == comparable(title): - return text[match.end() :] - return text - - -def normalize_comment_fences(text: str, source: Path) -> str: - if source != ROOT / "CONTRIBUTING.md": - return text - - def fence(match: re.Match[str]) -> str: - lines = [line.strip() for line in match.group(1).splitlines() if line.strip()] - if lines and all(line.startswith(("/", "*")) for line in lines): - return f"```text\n{match.group(1)}```" - return match.group(0) - - return re.sub(r"```c\+\+\n(.*?)```", fence, text, flags=re.DOTALL) - - -def normalize_markdown(text: str, source: Path, title: str) -> str: - text = strip_title(text, title) - text = normalize_comment_fences(text, source) - text = re.sub(r"(?m)^```\s*math\s*$", "```{math}", text) - text = re.sub(r"\$`([^`]+)`\$", r"$\1$", text) - text = normalize_headings(text) - if source == COMMON_MATH: - text = re.sub( - r"(?m)^(#{2,6}\s+Derived Functions)", - r"(anti-windup-indicator)=\n\1", - text, - count=1, - ) - return text - - -def write_page(source: Path) -> None: - source = source.resolve() - page = SOURCE_TO_PAGE[source] - page.parent.mkdir(parents=True, exist_ok=True) - - text = source.read_text(encoding="utf-8") - if source == (ROOT / "README.md").resolve(): - text = rewrite_markdown_links(text, source.parent, page) - page.write_text(text.rstrip() + "\n", encoding="utf-8") - return - - title = title_for(source) - text = normalize_markdown(text, source, title) - text = rewrite_markdown_links(text, source.parent, page) - text = rewrite_html_images(text, source.parent, page) - source_name = source.relative_to(ROOT).as_posix() - page.write_text( - f"# {title}\n\n" - f"{generated_toctree(source, page)}" - f"_Source: `{source_name}`_\n\n" - f"{text.rstrip()}\n", - encoding="utf-8", - ) - - -def write_example_index(directory: Path) -> None: - if (directory / "README.md").resolve() in EXAMPLE_READMES: - return - children = example_children(directory) - if not children: - return - - page = example_page(directory) - page.parent.mkdir(parents=True, exist_ok=True) - entries = [relative(example_page(child).with_suffix(""), page) for child in children] - lines = ["## Sections", ""] - for child in children: - child_link = f"[{child.name}]({relative(example_page(child), page)})" - grandchildren = [ - f"[{grandchild.name}]({relative(example_page(grandchild), page)})" - for grandchild in example_children(child) - ] - lines.append(f"- {child_link}: {', '.join(grandchildren)}" if grandchildren else f"- {child_link}") - - page.write_text( - f"# {'Examples' if directory == EXAMPLES else directory.name}\n\n" - f"{toctree(entries)}\n" - + "\n".join(lines) - + "\n", - encoding="utf-8", - ) - - -def main() -> None: - for directory in (GENERATED, GENERATED_MODELS, GENERATED_EXAMPLES): - if directory.exists(): - shutil.rmtree(directory) - directory.mkdir(parents=True, exist_ok=True) - - sources = [ - *PROJECT_PAGES, - COMMON_MATH, - PHASOR_INPUT_FORMAT, - *MODEL_READMES, - *EXAMPLE_READMES, - ] - for source in sources: - write_page(source) - for directory in sorted(EXAMPLE_DOC_DIRS): - write_example_index(directory) - - -if __name__ == "__main__": - main() diff --git a/docs/index.md b/docs/index.md index 73347a2b5..54fc5258d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,16 @@ -# GridKit +```{include} ../README.md +:relative-images: +``` ```{toctree} -:maxdepth: 4 +:maxdepth: 5 :titlesonly: :hidden: -generated/install -applications/index -models/index -examples/generated/index -api -development/index -``` -```{include} generated/readme.md -:relative-docs: generated/ +Installation +Applications +Models +Examples +API Reference +Development ``` diff --git a/docs/models/index.md b/docs/models/index.md deleted file mode 100644 index 8ac0a888d..000000000 --- a/docs/models/index.md +++ /dev/null @@ -1,21 +0,0 @@ -# Models - -```{toctree} -:maxdepth: 4 -:titlesonly: -:hidden: - -generated/common-math -generated/emt/index -generated/phasor-dynamics/index -generated/power-electronics/index -generated/power-flow/index -``` - -## Sections - -- [CommonMath](generated/common-math.md) -- [EMT](generated/emt/index.md) -- [PhasorDynamics](generated/phasor-dynamics/index.md) -- [PowerElectronics](generated/power-electronics/index.md) -- [PowerFlow](generated/power-flow/index.md) diff --git a/examples/PhasorDynamics/Large/Illinois/README.md b/examples/PhasorDynamics/Large/Illinois/README.md index a429de7f8..e4ab9ad8c 100644 --- a/examples/PhasorDynamics/Large/Illinois/README.md +++ b/examples/PhasorDynamics/Large/Illinois/README.md @@ -2,11 +2,9 @@ ## One-Line Diagram -
- +![](illinois.png) - Figure 1: Oneline of the ACTIVSg200 Case, from Texas A&M University [Grid Repository](https://electricgrids.engr.tamu.edu/electric-grid-test-cases/activsg200/) (Updated Oneline WIP) -
+Figure 1: Oneline of the ACTIVSg200 Case, from Texas A&M University [Grid Repository](https://electricgrids.engr.tamu.edu/electric-grid-test-cases/activsg200/) (Updated Oneline WIP) ## Case Description @@ -20,7 +18,7 @@ [TGOV1](../../../../GridKit/Model/PhasorDynamics/Governor/Tgov1/README.md) | 40 [SEXS-PTI](../../../../GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md) | 40 [LoadZIP](../../../../GridKit/Model/PhasorDynamics/LoadZIP/README.md) | 164 - [SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/) | 120 + [SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/README.md) | 120 ## Data Notes diff --git a/examples/PhasorDynamics/Large/WECC/README.md b/examples/PhasorDynamics/Large/WECC/README.md index 373ed3302..efa0e1742 100644 --- a/examples/PhasorDynamics/Large/WECC/README.md +++ b/examples/PhasorDynamics/Large/WECC/README.md @@ -2,11 +2,9 @@ ## One-Line Diagram -
- +![](wecc.jpg) - Figure 1: Oneline of the WECC case of [National Laboratory of the Rockies](https://www.nlr.gov/grid/test-case-repository) -
+Figure 1: Oneline of the WECC case of [National Laboratory of the Rockies](https://www.nlr.gov/grid/test-case-repository) ## Case Description @@ -23,7 +21,7 @@ Model | Count [SEXS-PTI](../../../../GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/README.md) | 103 [IEEEST](../../../../GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md) | 10 [LoadZIP](../../../../GridKit/Model/PhasorDynamics/LoadZIP/README.md) | 146 -[SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/) | 319 +[SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/README.md) | 319 ## Data Notes diff --git a/examples/PhasorDynamics/Medium/Hawaii/README.md b/examples/PhasorDynamics/Medium/Hawaii/README.md index 9923641ca..3c780c3bb 100644 --- a/examples/PhasorDynamics/Medium/Hawaii/README.md +++ b/examples/PhasorDynamics/Medium/Hawaii/README.md @@ -2,11 +2,9 @@ ## One-Line Diagram -
- +![](hawaii.png) - Figure 1: Oneline of the synthetic Hawaii case, courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) (WIP Updated Oneline) -
+Figure 1: Oneline of the synthetic Hawaii case, courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) (WIP Updated Oneline) ## Case Description @@ -21,7 +19,7 @@ Model | Count [IEEET1](../../../../GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md) | 39 [IEEEST](../../../../GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md) | 14 [LoadZIP](../../../../GridKit/Model/PhasorDynamics/LoadZIP/README.md) | 28 -[SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/) | 131 +[SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/README.md) | 131 ## Data Notes diff --git a/examples/PhasorDynamics/Medium/NewEngland/README.md b/examples/PhasorDynamics/Medium/NewEngland/README.md index 2d9e6a449..e34535891 100644 --- a/examples/PhasorDynamics/Medium/NewEngland/README.md +++ b/examples/PhasorDynamics/Medium/NewEngland/README.md @@ -2,11 +2,9 @@ ## One-Line Diagram -
- +![](newengland.png) - Figure 1: Oneline of the New England IEEE 39-bus case, courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+Figure 1: Oneline of the New England IEEE 39-bus case, courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Case Description @@ -21,7 +19,7 @@ Model | Count [IEEET1](../../../../GridKit/Model/PhasorDynamics/Exciter/IEEET1/README.md) | 10 [IEEEST](../../../../GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/README.md) | 10 [LoadZIP](../../../../GridKit/Model/PhasorDynamics/LoadZIP/README.md) | 19 -[SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/) | 40 +[SignalNode](../../../../GridKit/Model/PhasorDynamics/SignalNode/README.md) | 40 ## Events diff --git a/examples/PhasorDynamics/Small/TwoArea/README.md b/examples/PhasorDynamics/Small/TwoArea/README.md index 480b4c9ac..55f38f395 100644 --- a/examples/PhasorDynamics/Small/TwoArea/README.md +++ b/examples/PhasorDynamics/Small/TwoArea/README.md @@ -2,11 +2,9 @@ ## One-Line Diagram -
- - - Figure 1: Oneline of the two-area Case, courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) -
+![](twoarea_oneline.png) + +Figure 1: Oneline of the two-area Case, courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) ## Case Description diff --git a/examples/PowerFlow/Grid3Bus/README.md b/examples/PowerFlow/Grid3Bus/README.md index 943b6f044..ae76d93f1 100644 --- a/examples/PowerFlow/Grid3Bus/README.md +++ b/examples/PowerFlow/Grid3Bus/README.md @@ -9,12 +9,9 @@ The mathematical model of the power flow problem is formulated as a set of nonli The model and its parameters are described in Figure 1: -
- - - - Figure 1: A simple 3-bus grid example. -
+![](../../../docs/Figures/example1.jpg) + +Figure 1: A simple 3-bus grid example. Problem variables are voltage magnitudes and phases; they are stored in bus objects. Branch and load models do not have any internal variables. Contributions to residual vector for the model are computed in individual component model objects. Residual values are sumed up and stored in buses. @@ -34,7 +31,7 @@ Q_2 & = & -Q_{L1} &~~~\mathrm{(load ~2)} \\ \end{array} ``` -**Bus 3**: PV bus, stores variable $`\theta_3`$ and residual $`P_3`$. Voltage is set to $`|V_3| \equiv 1.1`$p.u.. Generator $`P_{G3} = 2`$p.u. is attached to it. From the equations for [branch](../../../src/Model/PowerFlow/Branch/README.md) and [generator](../../../src/Model/PowerFlow/Gen/README.md) components, we assemble Bus 3 residual as: +**Bus 3**: PV bus, stores variable $`\theta_3`$ and residual $`P_3`$. Voltage is set to $`|V_3| \equiv 1.1`$p.u.. Generator $`P_{G3} = 2`$p.u. is attached to it. From the equations for [branch](../../../GridKit/Model/PowerFlow/Branch/README.md) and [generator](../../../GridKit/Model/PowerFlow/README.md) components, we assemble Bus 3 residual as: ```math \begin{array}{rcll} P_3 & = &P_{G3} &~~~\mathrm{(generator ~3)} \\ From 50fbebac9dc1f310b32f9aa7a92942752c9425bb Mon Sep 17 00:00:00 2001 From: lukelowry Date: Tue, 16 Jun 2026 14:45:00 -0500 Subject: [PATCH 5/6] Correct doxygen build path --- .gitignore | 1 + .readthedocs.yaml | 2 +- docs/Doxyfile | 2 +- docs/README.md | 6 +++--- docs/conf.py | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0a6e13c9a..9ee0b2652 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ build/ doxygen-docs/ docs/_build/ docs/api/reference/ +docs/xml/ __pycache__ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f8f34ddc2..4ddc09f6b 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,7 +8,7 @@ build: - doxygen jobs: pre_build: - - rm -rf build/docs/doxygen && cd docs && doxygen Doxyfile + - rm -rf docs/xml && cd docs && doxygen Doxyfile sphinx: configuration: docs/conf.py diff --git a/docs/Doxyfile b/docs/Doxyfile index 15c1cb77b..8564e075e 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,5 +1,5 @@ PROJECT_NAME = "GridKit" -OUTPUT_DIRECTORY = ../build/docs/doxygen +OUTPUT_DIRECTORY = . INPUT = ../GridKit RECURSIVE = YES diff --git a/docs/README.md b/docs/README.md index 79238fe4a..357d7a8e1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,7 @@ Static MyST wrapper pages under `docs/` include the repository Markdown files directly. Before Sphinx runs, the build generates Doxygen XML: ```sh -rm -rf build/docs/doxygen +rm -rf docs/xml cd docs && doxygen Doxyfile ``` @@ -20,13 +20,13 @@ To test the same flow locally: ```sh python -m pip install -r docs/requirements.txt -rm -rf build/docs/doxygen +rm -rf docs/xml cd docs && doxygen Doxyfile cd .. sphinx-build -T -b html docs docs/_build/html ``` -Doxygen XML under `build/docs/doxygen`, generated API reference files, and HTML +Doxygen XML under `docs/xml`, generated API reference files, and HTML output are build artifacts and should not be committed. ## CMake Doxygen Target diff --git a/docs/conf.py b/docs/conf.py index 5a33160c9..c66dda99f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,7 @@ extensions = ["breathe", "exhale", "myst_parser"] -breathe_projects = {"GridKit": str(docs_dir.parent / "build/docs/doxygen/xml")} +breathe_projects = {"GridKit": str(docs_dir / "xml")} breathe_default_project = "GridKit" From 09d1d449b1270fa1f114824ec1454712b0b2c1d2 Mon Sep 17 00:00:00 2001 From: lukelowry Date: Mon, 22 Jun 2026 10:22:59 -0500 Subject: [PATCH 6/6] Add links to main page --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 58a252f7b..379856292 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ framework could be used in other areas without major modifications. GridKit™ supports adding multiple families of models to provide different representations of power grids and possibly other complex engineered systems. +- Documentation is hosted on [ReadTheDocs](https://gridkit.readthedocs.io/en/latest/) +- Source code is hosted on [GitHub](https://github.com/ORNL/GridKit) + ## Installation Guide GridKit™ has been built and tested on Linux and Mac platforms. It should