diff --git a/docs/about/limits.md b/docs/about/limits.md index 39f7f6f1..90e02435 100644 --- a/docs/about/limits.md +++ b/docs/about/limits.md @@ -32,8 +32,8 @@ costs to add. `variables:` or `given_variables:`. One enters where it says something no section already says, where a file decides it without data, and where the typesetter prints it. `given_variables:` entered on all three: nothing else - states that a column belongs to another file, which is what lets a template - load and print on its own. + states that a column belongs to another file, which is what lets a component + file load and print on its own. A request that is none of the three is refused, and the [table of refusals](#deliberate-non-primitives) records it with what to write @@ -174,10 +174,10 @@ That another tool has a feature is not by itself a reason to add it. ## Composition (component libraries) -A component library is a set of templates, such as a boiler, a battery and a -line, that agree on how ports and flows are named. You merge the templates you -need into one file, wire the components together with a connectivity table in -the data, and close the system with one `sum(by=)` balance. +A component library is a set of files, one per component type — a boiler, a +battery, a line — that agree on how ports and flows are named. You merge the +files you need into one, wire the components together with a connectivity table +in the data, and close the system with one `sum(by=)` balance. The topology is data. Adding a second battery is a row in a table, not a second block of YAML, so the file grows with the number of component _types_ and not @@ -189,16 +189,16 @@ a path, so a model assembled in Python is checked exactly as a file is, and file may hold. There is no Python API that builds models any other way. Two verbs do this, and they answer different questions. `merge` composes -peers, so a name two templates declare is a collision and the order they are +peers, so a name two fragments declare is a collision and the order they are given in means nothing. `override` lays patches over a base, which is what a framework ships and a project extends, a field at a time. [Compose a model from several files](../howto/compose.md) is the recipe for both. -A template reads the coupling surface it is written against, and declares that -column under `given_variables:`. So a template loads on its own, and prints as -math on its own, which is what it could not do while a fragment was a file the -loader had to refuse. `merge` folds each given declaration into the one that +A component file reads the coupling surface it is written against, and declares +that column under `given_variables:`. So it loads on its own, and prints as math +on its own, which is what it could not do while a fragment was a file the loader +had to refuse. `merge` folds each given declaration into the one that introduces it, so a composed library carries none. A layer over a model this language never sees — one built through linopy, say — @@ -219,8 +219,8 @@ exactly, never changed, because the expressions written over an axis are already in the base. Two requests were closed against this design. A built-in merge (#30) and -namespaces so that two templates can each declare a `p` (#29) are both things a +namespaces so that two fragments can each declare a `p` (#29) are both things a library does before it hands over a `dict`. Arithmetic in `bounds:`, which signed and bidirectional flows need, is still open as #31. A component whose number of ports is only known at run time belongs in the library, which emits more rows or -more templates, and never one block of YAML per component. +more files, and never one block of YAML per component. diff --git a/docs/examples/index.md b/docs/examples/index.md index d6c5e3a9..178bcfde 100644 --- a/docs/examples/index.md +++ b/docs/examples/index.md @@ -23,8 +23,8 @@ or starts printing different math fails CI. declaration at a time. PyPSA's name for each row sits beside the YAML and the equation. - [A component library](library/index.md) is several files that compose into - one model. Each template reads the coupling surface and prints on its own, - and the composed page shows what `merge` returns. + one model. Each file reads the coupling surface and prints on its own, and the + composed page shows what `merge` returns. The math on these pages is printed by the typesetter from the file above it. See [Typeset the math](../reference/typeset.md) to print your own. diff --git a/docs/howto/compose.md b/docs/howto/compose.md index 3eb83eb9..ad80df4b 100644 --- a/docs/howto/compose.md +++ b/docs/howto/compose.md @@ -6,13 +6,13 @@ SPDX-License-Identifier: CC-BY-4.0 # Compose a model from several files Build one model out of files that each say part of it. `merge` composes -**peers** — the templates of a component library, where a name two of them -declare is a collision. `override` lays **patches** over a base — what a +**peers** — the files of a component library, where a name two of them declare +is a collision. `override` lays **patches** over a base — what a framework ships and a project extends. Both hand back one mapping, which [`to_spec`](../reference/language/reading.md) loads like any file, and they compose: `override(merge({…}), {…})`. -## A library of templates +## A library of components 1. **Write the coupling surface as a model.** One flow per port, one balance per bus. Nothing in it knows which components exist. @@ -131,12 +131,12 @@ compose: `override(merge({…}), {…})`. Fragments own their math, so a name two of them declare is refused, both named. Here two files each say what a generator fleet is: ```text -fragments 'gas' and 'coal' both declare the parameter 'Generator_p_nom'. Two of the same kind of thing are two rows of a dimension rather than two fragments: merge the template once, and let the data carry both. Different math under one spelling is a rename — call one of them something else. +fragments 'gas' and 'coal' both declare the parameter 'Generator_p_nom'. Two of the same kind of thing are two rows of a dimension rather than two fragments: merge the fragment once, and let the data carry both. Different math under one spelling is a rename — call one of them something else. ``` ## A column read one way and introduced another -What a template states about a column it reads has to agree with the file that +What a fragment states about a column it reads has to agree with the file that owns it: ```text diff --git a/docs/reference/language/declarations.md b/docs/reference/language/declarations.md index 9d87d250..65da623a 100644 --- a/docs/reference/language/declarations.md +++ b/docs/reference/language/declarations.md @@ -117,7 +117,7 @@ stand in another variable's `bounds`. ## `given_variables` A given variable is a column this file reads and another file introduces. It is -what lets a template stand on its own: the file loads, and it prints as math, +what lets a fragment stand on its own: the file loads, and it prints as math, without the file that owns the column. ```yaml diff --git a/src/math_spec/advice.py b/src/math_spec/advice.py index 7f119e68..400aec54 100644 --- a/src/math_spec/advice.py +++ b/src/math_spec/advice.py @@ -45,7 +45,7 @@ def _given(program: Program) -> list[Advice]: """One note per declaration the program reads and does not build. A note rather than a refusal, because both readings are a model somebody - meant: a template is composed with the file that introduces the column, and + meant: a fragment is composed with the file that introduces the column, and a layer is bound to the model it is laid onto. What neither is, is a model a consumer can build alone, and the consumer is the one that can tell which it is holding. @@ -55,7 +55,7 @@ def _given(program: Program) -> list[Advice]: 'given', name, f"{kind} '{name}' is read here and built elsewhere: a consumer binds it to the model this " - f'one is layered onto, and refuses where it cannot. A template is composed instead, and ' + f'one is layered onto, and refuses where it cannot. A fragment is composed instead, and ' f'merge() folds it into the file that introduces it.', ) for kind, group in (('variable', program.given_variables), ('row family', program.given_constraints)) diff --git a/src/math_spec/composition.py b/src/math_spec/composition.py index d428c427..3268fef8 100644 --- a/src/math_spec/composition.py +++ b/src/math_spec/composition.py @@ -5,7 +5,7 @@ """Several files into one model, before any of them is validated. Two verbs, and they answer different questions. :func:`merge` composes -**peers**: templates that each own part of the math, where a name two of them +**peers**: fragments that each own part of the math, where a name two of them declare is a collision and the order they are given in means nothing. :func:`override` layers a **base and its patches**: what a framework ships and a project extends, where a name the patch declares is the point. Neither is a @@ -95,14 +95,14 @@ def merge( ) -> dict[str, Any]: """*fragments* composed as peers, each owning the math it declares. - A component library is a set of templates that agree on a coupling - surface — one flow per port, one balance per bus — and wiring a system is - rows in a table rather than generated YAML. This is what takes the - templates and hands back one model. + A component library is a set of files that agree on a coupling surface — + one flow per port, one balance per bus — and wiring a system is rows in a + table rather than generated YAML. This is what takes those files and hands + back one model. Args: fragments: What each fragment is called, to the fragment. The name is - what an error calls it, so it is the template's name rather than a + what an error calls it, so it is the fragment's name rather than a path. The order they are given in does not reach the result. description: What the *composed* model is. A fragment's own ``description`` is about the fragment, so it is neither carried nor @@ -142,7 +142,7 @@ def _one_version(read: Mapping[str, dict[str, Any]]) -> int: """The language version every fragment is written against. A fragment saying nothing is version 0 like any file, so a library pinning - one and a template pinning none is the disagreement it looks like rather + one and a fragment pinning none is the disagreement it looks like rather than a default quietly winning. """ declared = {name: sections.get('version', 0) for name, sections in read.items()} @@ -198,7 +198,7 @@ def _claimed(read: Mapping[str, dict[str, Any]], section: str) -> dict[str, Any] raise LanguageError( f"fragments '{author[key]}' and '{name}' both declare the {_singular(section)} " f'{key!r}. Two of the same kind of thing are two rows of a dimension rather than ' - f'two fragments: merge the template once, and let the data carry both. Different ' + f'two fragments: merge the fragment once, and let the data carry both. Different ' f'math under one spelling is a rename — call one of them something else.' ) merged[key] = block @@ -239,7 +239,7 @@ def _author_of(read: Mapping[str, dict[str, Any]], section: str, key: str) -> st def _summed_objective(read: Mapping[str, dict[str, Any]]) -> dict[str, Any] | None: """Every fragment's objective, summed, or ``None`` where none declares one. - Summing is what composing costs: each template prices what it owns, and the + Summing is what composing costs: each fragment prices what it owns, and the system pays for all of it. The senses must agree, because a sum of two objectives has one sense and nothing in the files says which — negating the minority would be this function deciding what a model means. diff --git a/tests/test_given.py b/tests/test_given.py index 7a30f910..addb3b5f 100644 --- a/tests/test_given.py +++ b/tests/test_given.py @@ -4,7 +4,7 @@ """What a file reads and does not build: a column, and a row family. -Two readings, and each is a model somebody meant. A **template** reads a column +Two readings, and each is a model somebody meant. A **fragment** reads a column the file beside it introduces, and `merge` folds the two together, so the composed model carries neither the declaration nor any trace of it. A **layer** reads a column, or the dual of a row family, that a model outside the language @@ -21,7 +21,7 @@ from math_spec import FORMATS, LanguageError, advice, merge, to_markdown, to_program, to_spec, typeset -#: One component template: it pins the flow at its own port, and the column it +#: One component file: it pins the flow at its own port, and the column it #: pins belongs to the surface fragment below. SUPPLY = { 'description': 'A fleet of generators, each on one port.', diff --git a/tests/test_library_example.py b/tests/test_library_example.py index 8a6738f2..464d68b2 100644 --- a/tests/test_library_example.py +++ b/tests/test_library_example.py @@ -32,7 +32,7 @@ def test_every_fragment_loads_and_prints_on_its_own(name): @pytest.mark.parametrize('name', ['generator', 'load']) -def test_a_component_template_reads_the_surface_and_introduces_no_flow(name): +def test_a_component_file_reads_the_surface_and_introduces_no_flow(name): spec = to_spec(FRAGMENTS[name]) assert sorted(spec.given_variables) == ['Port_p'] assert 'Port_p' not in spec.variables, 'the surface introduces the column, and a component file only writes into it' @@ -48,7 +48,7 @@ def test_the_library_composes_into_one_model(): ) -def test_the_balance_is_written_once_however_many_templates_are_merged(): +def test_the_balance_is_written_once_however_many_fragments_are_merged(): one = to_spec(merge({'surface': FRAGMENTS['surface'], 'load': FRAGMENTS['load']})) both = to_spec(merge(FRAGMENTS)) assert one.constraints['Bus_nodal_balance'].expression == both.constraints['Bus_nodal_balance'].expression