diff --git a/README.md b/README.md index c20f12c8..c78c3633 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,9 @@ decisions the solver makes, such as `dispatch`; and the rules those decisions obey, such as `sum(dispatch, over=generator) == load`. The file [below](#example) is a complete model. -math-spec reads that file, checks everything that can be checked without data, -and hands the result on: to an engine that builds and solves the model, or to the -typesetter that prints it as LaTeX, Typst or Markdown. It builds nothing and -solves nothing itself. Every tool reads the file through the same checked syntax -tree, so an engine and a renderer cannot disagree about what the file means -([what counts as language](docs/about/what-counts-as-language.md)). - +math-spec builds nothing and solves nothing itself. [specsolve](https://github.com/fluxopt/specsolve) and [linopy](https://github.com/PyPSA/linopy) build and solve a math-spec model. Support in both is work in progress. @@ -50,33 +44,12 @@ Support in both is work in progress. - **One model, many tools.** Engines, renderers and analysers read the model through one public API, so no two of them can read the file differently. [Program API →](https://math-spec.readthedocs.io/en/latest/reference/program/) -- **Review a model as a diff.** The model is plain YAML, and no Python - state changes what it means. - **Write full-size models.** PyPSA's `n.optimize()` model is one file, with stochastic, multi-period and quadratic variants. [PyPSA in one file →](https://math-spec.readthedocs.io/en/latest/examples/pypsa/) -```mermaid -flowchart LR - Y["model.yaml"] --> S["schema
closed at every level"] - S --> AST["syntax tree
two grammars"] - AST --> Q{"inside the
language?"} - Q -->|"no"| ERR["load error
naming the construct + rewrite"] - Q -->|"yes"| M["Spec
what the file says"] - M -->|".program"| P["Program
names, dimensions and operators resolved"] - P --> ENG["an engine that builds → solver"] - M --> T["to_latex / to_typst / to_markdown"] - - classDef spec fill:#f0f7f0,stroke:#3a7d44,stroke-width:2px,color:#111 - classDef consumer fill:#eef1fb,stroke:#4a5fc1,stroke-width:2px,color:#111 - classDef err fill:#fdf3e7,stroke:#b7791f,color:#111 - class S,AST,M,P spec - class ENG,T consumer - class ERR err -``` - ## Example @@ -191,80 +164,6 @@ $`0 \le \mathit{dispatch}_{s,g} \le \bar p_{g} \qquad \forall\, s \in \mathcal{S -
-The same document as LaTeX - -```latex -\noindent Least-cost dispatch of a generator fleet against an hourly load. - -\paragraph{Sets} -\begin{description} -\item[{$\mathcal{S}$}] index $s$ --- \texttt{snapshot} --- dispatch periods -\item[{$\mathcal{G}$}] index $g$ --- \texttt{generator} --- generating units -\end{description} - -\paragraph{Parameters} -\begin{description} -\item[{$\bar p$}] \texttt{capacity} over $\mathcal{G}$ --- installed capacity -\item[{$\ell$}] \texttt{load} over $\mathcal{S}$ --- demand to be met -\item[{$c$}] \texttt{cost} over $\mathcal{G}$ --- marginal cost -\end{description} - -\paragraph{Variables} -\begin{description} -\item[{$\mathit{dispatch}$}] \texttt{dispatch} over $\mathcal{S} \times \mathcal{G}$ --- output of a generator in a snapshot -\end{description} - -\paragraph{Objective} -\begin{align*} - && \min & \sum_{s \in \mathcal{S},\ g \in \mathcal{G}} \mathit{dispatch}_{s,g} \cdot c_{g} -\end{align*} - -\paragraph{Subject to} -\begin{align*} -\text{power\_balance} && \sum_{g \in \mathcal{G}} \mathit{dispatch}_{s,g} & = \ell_{s} && \forall\, s \in \mathcal{S} -\end{align*} - -\paragraph{Variable domains} -\begin{align*} -\text{dispatch} && 0 \le \mathit{dispatch}_{s,g} & \le \bar p_{g} && \forall\, s \in \mathcal{S},\ g \in \mathcal{G} \,:\, \bar p_{g} > 0 -\end{align*} -``` - -
- -
-The same document as Typst, printed with no symbol table - -```typst -Least-cost dispatch of a generator fleet against an hourly load. - -== Sets -/ $cal(T)$: index $t$ --- `snapshot` --- dispatch periods -/ $cal(G)$: index $g$ --- `generator` --- generating units - -== Parameters -/ $upright("capacity")$: `capacity` over $cal(G)$ --- installed capacity -/ $upright("load")$: `load` over $cal(T)$ --- demand to be met -/ $upright("cost")$: `cost` over $cal(G)$ --- marginal cost - -== Variables -/ $italic("dispatch")$: `dispatch` over $cal(T) times cal(G)$ --- output of a generator in a snapshot - -Upright is what the model is given --- a parameter such as $upright("capacity")$, a coordinate map, a label --- and italic is what the solver chooses, such as $italic("dispatch")$. An index is italic too, being what a quantifier chooses, and a set is script. - -== Objective -$ & min & sum_(t in cal(T), g in cal(G)) italic("dispatch")_(t,g) dot upright("cost")_(g) $ - -== Subject to -$ upright("power_balance") & sum_(g in cal(G)) italic("dispatch")_(t,g) & = upright("load")_(t) & forall t in cal(T) $ - -== Variable domains -$ upright("dispatch") & 0 <= italic("dispatch")_(t,g) & <= upright("capacity")_(g) & forall t in cal(T), g in cal(G) colon upright("capacity")_(g) > 0 $ -``` - -
- @@ -275,16 +174,14 @@ import math_spec as ms spec = ms.to_spec('dispatch.yaml') -ms.to_markdown(spec) # renders as-is on GitHub, as above -ms.to_latex(spec) # amsmath align -ms.to_typst(spec) # compiles without a TeX toolchain +ms.to_markdown(spec) +ms.to_latex(spec) +ms.to_typst(spec) ``` A [symbol table](docs/reference/typeset.md#symbol-tables) gives the names their -conventional spelling, as in the first folded block. +conventional spelling, as in the folded block. [Print a model as math](docs/howto/print.md) does the same from a shell. -`to_spec` returns a `Spec`, and `spec.program` its `Program` -([reading a spec and its program](docs/reference/reading.md#spec-and-program)). ## Documentation @@ -292,10 +189,8 @@ The documentation is at . ## Installation -Nothing is published yet. -[Installation](docs/howto/installation.md) gives the command that installs from -git, and [contributing](docs/contributing.md#setting-up-a-development-environment) -sets up a development clone. +See [installation](docs/howto/installation.md). To work on math-spec, see +[contributing](docs/contributing.md#setting-up-a-development-environment). ## Prior art @@ -304,8 +199,7 @@ and extracted here. The keys themselves, which are YAML math, a block per component, `dims:` and a `where:` string, come from [Calliope](https://github.com/calliope-project/calliope). [linopy](https://github.com/PyPSA/linopy) supplies the vocabulary that -`sum(over=)` and the dimension rules are named against. Issue numbers in these -pages point at specsolve, where the arguments happened. +`sum(over=)` and the dimension rules are named against. ## Status @@ -324,9 +218,5 @@ not yet frozen. ## Licence -The code is [MIT](LICENSE): everything under `src/`, `tests/`, `tools/`, the -examples, and the generated schema. - -The prose is [CC-BY-4.0](LICENSES/CC-BY-4.0.txt): everything under `docs/`, this -README, `CHANGELOG.md`, and the logos in `resources/`. Reuse it freely, with -attribution. +The code is [MIT](LICENSE), and the prose is +[CC-BY-4.0](LICENSES/CC-BY-4.0.txt). diff --git a/docs/index.md b/docs/index.md index becfc59c..f7cc2fd3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -153,11 +153,11 @@ call. }, } - spec = ms.to_spec('dispatch.yaml') # read and checked once, then printed three ways + spec = ms.to_spec('dispatch.yaml') - ms.to_latex(spec, symbols=symbols) # amsmath align - ms.to_typst(spec) # compiles without a TeX toolchain - ms.to_markdown(spec) # renders as-is on GitHub + ms.to_latex(spec, symbols=symbols) + ms.to_typst(spec) + ms.to_markdown(spec) ``` `symbols` gives every name its conventional spelling. Pass a dict, a YAML path @@ -193,8 +193,7 @@ call. ## Install it -Nothing is published yet. [Installation](howto/installation.md) gives the -command that installs from git. +See [installation](howto/installation.md). !!! warning "Alpha, pre-1.0" diff --git a/docs/static/extras.css b/docs/static/extras.css index 6d0d8530..469163d2 100644 --- a/docs/static/extras.css +++ b/docs/static/extras.css @@ -310,6 +310,10 @@ /* Benefit cards: the README bullet's bold lead-in is the card's title, and its * one link covers the whole card, since the README cannot carry card markup. */ +.md-typeset .landing .grid.cards { + grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); +} + .md-typeset .landing .grid.cards > ul > li { position: relative; color: var(--md-default-fg-color--light); diff --git a/tests/test_docs.py b/tests/test_docs.py index a22066ff..85cbfdc5 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -67,7 +67,7 @@ def test_no_fold_in_the_readme_prints_its_math_as_a_code_block(): `tools._page.inlined` is what rewrites a fold into it. """ folds = re.findall(r'
.*?
', home_math.README.read_text(), re.DOTALL) - assert len(folds) == 3, 'the README folds the whole document, then the same document in the other two notations' + assert len(folds) == 1, 'the README folds one block, the whole document with its symbol table' hiding = [fold[:60] for fold in folds if '```math' in fold] assert not hiding, f'a `math` fence inside a fold prints its TeX rather than its math: {hiding}' diff --git a/tools/home_math.py b/tools/home_math.py index a94d54ec..a9b3aaab 100644 --- a/tools/home_math.py +++ b/tools/home_math.py @@ -9,7 +9,7 @@ ``examples/dispatch.yaml`` is printed once and spliced into two pages that need different shapes for it. GitHub renders the Markdown math but not a tabbed -block, so ``README.md`` takes the equations with the other two formats folded +block, so ``README.md`` takes the equations with the whole document folded under them, and ``docs/index.md`` takes tabs, plus a third tab holding the call that produced the other two. """ @@ -19,7 +19,7 @@ import textwrap from math_spec import to_spec -from math_spec.typesetting import to_latex, to_markdown, to_typst +from math_spec.typesetting import to_latex, to_markdown from tools._page import ROOT, inlined, sidecar_for, splice, without_header from tools._page import main as page_main @@ -52,11 +52,11 @@ }, } -spec = ms.to_spec('dispatch.yaml') # read and checked once, then printed three ways +spec = ms.to_spec('dispatch.yaml') -ms.to_latex(spec, symbols=symbols) # amsmath align -ms.to_typst(spec) # compiles without a TeX toolchain -ms.to_markdown(spec) # renders as-is on GitHub +ms.to_latex(spec, symbols=symbols) +ms.to_typst(spec) +ms.to_markdown(spec) ``` `symbols` gives every name its conventional spelling. Pass a dict, a YAML path @@ -107,10 +107,7 @@ def readme_block() -> str: The visible block carries no legend and no symbol table, because a README is read before anything else: three legend tables are half its length, and a derived symbol is the file's own name, which needs no table to be read. - The first fold is what the legend and a table add. - - Typst is printed with no table for a second reason: the sidecar is written - in LaTeX, and a render in the other notation is refused. + The fold is what the legend and a table add. """ spec = to_spec(MODEL) symbols = sidecar_for(MODEL) @@ -121,14 +118,6 @@ def readme_block() -> str: 'The whole document: a symbol table, and the legend it prints', inlined(to_markdown(spec, symbols=symbols, numbered=False).strip()), ), - details( - 'The same document as LaTeX', - f'```latex\n{to_latex(spec, symbols=symbols, numbered=False).strip()}\n```', - ), - details( - 'The same document as Typst, printed with no symbol table', - f'```typst\n{to_typst(spec, numbered=False).strip()}\n```', - ), ) )