docs(docs): built docs

Note for now much of the docs is LLM written. This was done to save time and focus on code development. The docs have been LOOSLEY reviewed. However, the code itself has been either fully written by hand or fully reviewed. Therefore, the docs should at the moment be seen as a loose resource rather than authoratiative (and this is on top of the general ability for docs to drift)
This commit is contained in:
2026-11-09 14:09:44 -05:00
parent d9c4919477
commit 6b57738330
32596 changed files with 4155414 additions and 0 deletions

310
docs/static/dependencies.md vendored Normal file
View File

@@ -0,0 +1,310 @@
# Computational graphs
`serif::utils::dependencies` is a computational graph library: computations are
nodes, and their input keys declare incoming edges from sources or other nodes.
The existing namespace and API names are retained. The library is independent of
MFEM, equations, surfaces, and deformation. A context supplies external inputs;
a graph owns the computations and their execution order; a cache owns that graph,
source snapshots, and accepted results.
## A complete calculation
```cpp
#include <array>
#include "serif/utils/dependencies/dependencies.hpp"
/// Compute b = lambda * a, then reuse it while both inputs remain unchanged.
int main() {
namespace dependencies = serif::utils::dependencies;
namespace keys = dependencies::keys;
using Vector = std::array<double, 3>;
Vector a{1.0, 2.0, 3.0};
double lambda = 2.0;
const auto context = dependencies::make_dependency_context(
dependencies::borrow<keys::Input>(a),
dependencies::borrow<keys::Scale>(lambda)
);
const auto scale_vector = dependencies::make_computation<
keys::Output, dependencies::InputList<keys::Input, keys::Scale>
>(
[](const Vector& input, double scale) -> Vector {
return {scale * input[0], scale * input[1], scale * input[2]};
}
);
auto cache = dependencies::make_computation_cache(context, scale_vector);
auto report = cache.try_prepare(context);
if (!report) {
return 1;
}
const Vector b = cache.get<keys::Output>(); // <2, 4, 6>
report = cache.try_prepare(context);
if (!report) {
return 1;
}
return report->status<keys::Output>() == dependencies::PreparationStatus::reused ? 0 : 1;
}
```
Creating the cache does not run the calculation or retain the context. The first
preparation computes the result. Further preparations compare input snapshots and
reuse the result while both `a` and `lambda` remain unchanged. Changing either one
requires recomputation. Keep the cache between calls so it can retain this state.
The [dependency sandbox](../../tests/sandbox/dependencies_sandbox.cpp) prints the values, per-node computation/reuse status, and elapsed time through those cases.
## Keys and ordered input lists
A key is a type identifying a role, independently of the type of value stored for
that role. `keys.hpp` offers `keys::Input`, `keys::Output`, `keys::Scale`,
`keys::Parameters`, and `keys::Coordinates` for small computations. These keys are
optional; custom types and existing discretization term descriptors work without
registration. A key need not be empty or constructible, because no key object is
created. Use distinct application keys when a graph has several different inputs
or outputs; giving two results the same convenience key is still a duplicate.
`InputList<A, B>` passes the values for `A` and `B` to a callable in that order.
`RequestedResults<X, Y>` selects which outputs to prepare. Both are aliases of the
same `KeyList` implementation, which also supports context selection and key
combination. `CombinedKeyList` removes duplicate roles in first-appearance order.
## Multiple computations and explicit graphs
The ordinary factory accepts any number of named computations:
```cpp
auto cache = dependencies::make_computation_cache(context, first, second);
```
Their supplied order is unrestricted. Dependency relationships establish the
evaluation order at compile time; each required computation runs at most once per
preparation. For a named or reusable collection, build an explicit graph:
```cpp
const auto graph = dependencies::make_computation_graph(first, second);
auto cache = dependencies::make_computation_cache(graph, context);
auto report = cache.try_prepare(context, dependencies::RequestedResults<FinalResult>{});
```
Named copyable computations and graphs are copied into their owner, so ordinary
calls need no `std::move`. Temporaries transfer ownership. A named computation or
graph owning noncopyable state requires an explicit transfer with `std::move`.
The factories reject unavailable copies at compile time.
## Observing execution and input consumption
Observers belong to the cache that executes a graph. Node observers run before and
after a calculation; edge observers inspect the inputs that calculation consumes.
The graph and its numerical computations do not need to know about observation.
Existing factory calls remain valid; an explicit graph accepts an optional observer
bundle as the third argument to `make_computation_cache`.
Continuing the vector-scaling example, include `<chrono>` and `<cstddef>`, and attach
a timer to the output node and a callback to its scale input:
```cpp
using Clock = std::chrono::steady_clock;
std::chrono::nanoseconds elapsed{};
std::size_t executions = 0;
double last_scale = 0.0;
dependencies::NodeOutcome last_outcome = dependencies::NodeOutcome::completed;
const auto observers = dependencies::make_observers(
dependencies::observe_node<keys::Output>(
[]() { return Clock::now(); },
[&](Clock::time_point& started, dependencies::NodeOutcome outcome) noexcept {
elapsed += std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now() - started);
++executions;
last_outcome = outcome;
}
),
dependencies::observe_edge<keys::Scale, keys::Output>(
[&](const double& scale) { last_scale = scale; }
)
);
const auto graph = dependencies::make_computation_graph(scale_vector);
auto observed_cache = dependencies::make_computation_cache(graph, context, observers);
const auto observed_report = observed_cache.try_prepare(context);
```
The cache owns its callbacks. Each before callback returns a movable object that
belongs to that one execution. The matching after callback receives this same
object by reference, which gives a timer or other paired operation local storage
without adding state to the numerical computation. Accumulated measurements belong
to the application, as the captures above illustrate. Captured references must
outlive every use of the cache; an owning capture may be used instead. Named,
noncopyable observer bundles must be transferred with `std::move`.
A node execution has the following order:
1. Matching before callbacks run in observer registration order.
2. Edge callbacks run in the node's declared `InputList` order, with callbacks for
the same edge running in registration order.
3. The numerical callable runs.
4. Matching after callbacks run in reverse registration order.
An edge is identified by **input key, then consuming node key**. The input can be
an external source or another node's result. Each callback receives the underlying
value by const reference, including for owning computations whose numerical
callable receives shared handles. An unchanged prerequisite can therefore have an
edge callback when its consumer recomputes. Callbacks must not modify source or
result state, including objects reachable through pointers. Callbacks must not
prepare, invalidate, or move the cache whose execution they are observing. Numerical
transformations belong in declared computations.
No callbacks run for reused or unrequested nodes. Node keys, actual graph edges,
and callback signatures are checked at compile time. Multiple observers may attach
to the same node or edge. A node observer needs both before and after callbacks;
return a small empty object from before when no per-execution data is needed.
`NodeOutcome::completed` means the callable returned a value, `rejected` means it
returned an unsuccessful `std::expected`, and `exception` means execution exited
through an exception, including from an edge callback. These describe individual
execution, **not publication of the request**: a later node can still fail and
cause the whole preparation to be rejected. Inspect the final preparation report
when recording accepted requests.
After callbacks must return `void` and be `noexcept`, so they can safely finish
during exception propagation. Before and edge callbacks may propagate exceptions;
the cache retains the same transactional protection as numerical-callable failures.
If a before callback fails, its after callback does not run; already-started node
observers finish with `exception`. Observation side effects, such as recorded
measurements, are not rolled back. The sandbox records measurements in callbacks
and prints them afterward, keeping potentially throwing output out of after hooks.
## Operator caches
The MFEM operator backend uses these ordinary caches directly. Its volume
factories declare domain-specific calculations and return `ComputationCache` objects;
there is no second lifecycle owner around them. Input-validation helpers construct
current contexts before invoking the cache. Residual and Jacobian functions consume
the accepted data, and the MFEM adapter borrows the cache. The
[operator guide](operators.md#model-ownership-and-startup) shows a complete volume
example.
## Where things belong
| Component | Responsibility |
| --- | --- |
| `keys.hpp` | Optional general role names; applications may supply their own. |
| `binding.hpp`, `key_list.hpp` | Associate role keys with target types and express ordered inputs or requested results. |
| `key_list_operations.hpp` | Combine key lists without duplicating roles. |
| `resource.hpp`, `dependency_context.hpp` | Supply owned or borrowed objects and select views of their roles. |
| `snapshot.hpp` | Specify which source state determines whether cached work remains valid. |
| `computation.hpp` | Attach result and input keys to a callable. |
| `computation_graph.hpp` | Store calculations and establish their dependency order. |
| `computation_cache.hpp`, `preparation.hpp` | Prepare requested results, retain accepted work, and report status or rejection. |
| `observers.hpp` | Attach execution callbacks to node and edge keys, with per-execution node state. |
| `detail/` | Graph traversal, type resolution, and cache storage used by these interfaces. |
## Computation and lifetime contracts
- A callable can be a lambda, function, or function object. It must be callable
through a const reference, using its inputs as const references or copies. Each
result must be a movable object, returned by value.
- Every changing input that affects a result belongs in its `InputList`. Captures
may hold fixed configuration. Changing state hidden behind captures, globals,
or pointers cannot be discovered automatically.
- `own<Key>(value)` copies an lvalue or moves an rvalue. `borrow<Key>(object)`
borrows an existing object; temporaries are rejected. The object must outlive
the context's uses. Owning a view does not own its referenced data.
`context.select(KeyList<...>{})` borrows its parent context and preserves the
selected source identities.
- Results and captured snapshots must retain their relevant state independently
of subsequent source edits. An owning container works; a view into mutable
source storage does not freeze that storage. Treat cached results and their
referenced objects as read-only.
- Preparation is serial. Sources and callable configuration must remain stable
during each call. References obtained from `get` are valid until the next
preparation, invalidation, or destruction of their owner.
`cache.share<Key>()` retains an immutable result across later preparations,
rejection, invalidation, and cache destruction. It owns the result itself; anything
borrowed inside that result must still outlive its use. Both `get` and `share`
require the result to belong to the latest successful request.
Use `make_owning_computation<Key, InputList<...>>(callable)` when a result needs
to retain its computed prerequisites. Its inputs are immutable shared handles,
so retained results keep the exact prerequisite generation that produced them.
Every input to an owning computation must be produced by the same graph; external
context sources cannot be retained through this interface.
## Tracking changes and reusing work
The default `ValueSnapshot` owns a copy and compares it using equality. It works
for numbers and value-owning standard containers. Objects with indirect state,
expensive copies, or no copy operation need an appropriate policy. A revision
counter can be used if its owner updates it for every relevant edit:
```cpp
auto source = dependencies::borrow<Samples>(samples,
dependencies::track_by([](const auto& value) { return value.revision(); }));
```
This example assumes a source type providing `revision()`. For more control,
provide a policy with `capture(const Source&)` returning owned state and
`matches(const Captured&, const Source&)` returning a boolean. Snapshots also track
source identity: replacing a borrowed object forces recomputation even if its
tracked value is equal. A policy can override `identity(const Source&)` to return
`const void*` identifying the underlying object of a temporary view. Returning
`nullptr` deliberately opts into content-only comparison; the snapshot must then
include any identity relevant to correctness. Unused context objects need no
snapshot policy.
`try_prepare(context)` defaults to `PreparationMode::reuse_unchanged` and requests every result. `RequestedResults<...>{}` restricts
the request to those results and their prerequisites. Changed inputs invalidate
their dependents, including those requested in a later call. Other branches
retain their caches but are unavailable through `get` until requested again.
Accepted results stay frozen between preparations. When an input changes, the
computation and its required dependents run even if recomputation happens to produce
the same numerical output; result equality is not used to infer reuse.
The report's `status<Key>()` returns `PreparationStatus::not_required`, `reused`,
or `computed`. `computed` includes first evaluation and reevaluation; `not_required`
means outside the requested results and their prerequisites. The report also
provides `source_changed<Key>()`, `computed_count()`, and `reused_count()`.
An uninspected source is reported unchanged.
Pass `PreparationMode::rebuild` as the third argument to refresh the requested
results and their prerequisites, or call `invalidate()` to discard the whole
cache. Forced refreshes also mark inspected sources changed. `contains<Key>()`
checks result availability; `ready()` reports a successful current preparation.
`preparation_count()` counts successful requests, including requests that reused every
result and requests for only part of a graph. Rejected or throwing attempts do not
increment it. Invalidation retains the count. Moving the cache transfers the count
and accepted state to the destination and resets the source to an unprepared state
with count zero. This counter describes accepted requests, not numerical work;
use the report to distinguish computation from reuse.
## Rejection and compile-time checks
A computation can return `std::expected<Value, Error>` when it may reject an input.
Ordinary value-returning and fallible computations can share a graph. The default
error type is `std::string`; use `make_computation_cache<Error>(context, computations...)`
or `make_computation_cache<Error>(graph, context)` for another type. Each fallible
computation must return that exact error type, or an exact, unambiguous alternative
when the cache error is a `std::variant`. Implicit conversions between unrelated
error types are rejected. A failure contains the error
and the computation's index in the supplied graph; `ComputationGraph::index<Key>()`
gives the corresponding index for a known result key.
Preparation stages snapshots and results together. Success publishes all requested
results and their prerequisites. Rejection publishes none and makes results
unavailable until the next successful preparation. The previous accepted cache is
retained for possible reuse. Exceptions from callables or policies propagate with
the same cache protection. External side effects are not rolled back. A backend may
choose a stricter validation boundary: the MFEM `try_prepare_*` helpers invalidate
storage on rejection so that a later retry starts fresh. That policy is explicit
in the backend and does not change the generic cache's behavior.
Duplicate context roles, duplicate producers, and cycles fail at construction.
`ComputationGraphFor<Graph, Context>` checks input availability, source/result key
collisions, callable signatures, and result types. `CanPrepare<Graph, Context, Error>`
also checks snapshot policies and error compatibility. Both cache factories apply
these checks. Requests are constrained to result keys. Reading an unavailable
result throws `std::logic_error` because it indicates a caller sequencing error.
The general dependency module owns snapshots, accepted results, and rejection
handling; it contains no MFEM types.

View File

@@ -0,0 +1,22 @@
name: publish
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: set version
run: echo "PROJECT_NUMBER = `git describe --tags`" >> Doxyfile
- name: Generate Documentation
uses: mattnotmitt/doxygen-action@edge
- name: Publish generated content to GitHub Pages
uses: tsunematsu21/actions-publish-gh-pages@v1.0.2
with:
dir: docs/html
branch: gh-pages
token: ${{ secrets.ACCESS_TOKEN }}

View File

@@ -0,0 +1,6 @@
docs/html
.DS_Store
.idea
node_modules
*.tgz

View File

@@ -0,0 +1,3 @@
*
!doxygen-awesome*

2932
docs/static/doxygen-awesome-css/Doxyfile vendored Normal file

File diff suppressed because it is too large Load Diff

21
docs/static/doxygen-awesome-css/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: MIT
.POSIX:
PROJECT = doxygen-awesome-css
# Paths
PREFIX = /usr/local
DATADIR = share
INSTALLDIR = $(DESTDIR)$(PREFIX)/$(DATADIR)/$(PROJECT)
# Utilities
INSTALL = install -m 644
MKDIR = mkdir -p
RM = rm -f
# Files to be installed
FILES = doxygen-awesome-darkmode-toggle.js \
doxygen-awesome-fragment-copy-button.js \
doxygen-awesome-interactive-toc.js \
doxygen-awesome-paragraph-link.js \
doxygen-awesome-sidebar-only-darkmode-toggle.css \
doxygen-awesome-sidebar-only.css \
doxygen-awesome-tabs.js \
doxygen-awesome.css
# Empty targets so that `make` and `make clean` do not cause errors
all:
clean:
install:
$(MKDIR) $(INSTALLDIR)
$(INSTALL) $(FILES) $(INSTALLDIR)/
uninstall:
$(RM) -r $(INSTALLDIR)/
.PHONY: all clean install uninstall

View File

@@ -0,0 +1,173 @@
# Doxygen Awesome
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/releases/latest)
[![GitHub](https://img.shields.io/github/license/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/blob/main/LICENSE)
![GitHub Repo stars](https://img.shields.io/github/stars/jothepro/doxygen-awesome-css)
<div class="title_screenshot">
![Screenshot of Doxygen Awesome CSS](img/screenshot.png)
</div>
**Doxygen Awesome** is a custom CSS theme for Doxygen HTML documentation with lots of customization parameters.
## Motivation
I really like how the Doxygen HTML documentation is structured! But IMHO it looks a bit outdated.
This theme is an attempt to update the visuals of Doxygen without changing its overall layout too much.
## Features
- 🌈 Clean, modern design
- 🚀 Heavily customizable by adjusting CSS variables
- 🧩 No changes to the HTML structure of Doxygen are required
- 📱 Improved mobile usability
- 🌘 Dark mode support!
- 🥇 Works best with **doxygen 1.9.1** - **1.9.4** and **1.9.6** - **1.12.0**
## Examples
Some websites using this theme:
- [Documentation of this repository](https://jothepro.github.io/doxygen-awesome-css/)
- [wxWidgets](https://docs.wxwidgets.org/3.2/)
- [OpenCV 5.x](https://docs.opencv.org/5.x/)
- [Zephyr](https://docs.zephyrproject.org/latest/doxygen/html/index.html)
- [FELTOR](https://mwiesenberger.github.io/feltor/dg/html/modules.html)
- [Spatial Audio Framework (SAF)](https://leomccormack.github.io/Spatial_Audio_Framework/index.html)
- [Randolf Richardson's C++ classes](https://www.randolf.ca/c++/docs/)
- [libCloudSync](https://jothepro.github.io/libCloudSync/)
- [libsl3](https://a4z.github.io/libsl3/)
## Installation
To use the theme when generating your documentation, bring the required CSS and JS files from this repository into your project.
This can be done in several ways:
- manually copying the files
- adding the project as a Git submodule
- adding the project as a npm/xpm dependency
- installing the theme system-wide
All theme files are located in the root of this repository and start with the prefix `doxygen-awesome-`. You may not need all of them. Follow the install instructions to figure out what files are required for your setup.
### Git submodule
For projects that use git, add the repository as a submodule and check out the desired release:
```sh
git submodule add https://github.com/jothepro/doxygen-awesome-css.git
cd doxygen-awesome-css
git checkout v2.3.4
```
### npm/xpm dependency
In the npm ecosystem, this project can be added as a development dependency
to your project:
```sh
cd your-project
npm install https://github.com/jothepro/doxygen-awesome-css#v2.3.4 --save-dev
ls -l node_module/@jothepro/doxygen-awesome-css
```
Similarly, in the [xPack](https://xpack.github.io) ecosystem, this project can be added
as a development dependency to an [`xpm`](https://xpack.github.io/xpm/)
managed project.
### System-wide
You can even install the theme system-wide by running `make install`.
The files will be installed to `/usr/local/share/` by default,
but you can customize the install location with `make PREFIX=/my/custom/path install`.
### Choosing a layout
There are two layout options. Choose one of them and configure Doxygen accordingly:
<div class="tabbed">
- <b class="tab-title">Base Theme</b><div class="darkmode_inverted_image">
![](img/theme-variants-base.drawio.svg)
</div>
Comes with the typical Doxygen titlebar. Optionally the treeview in the sidebar can be enabled.
Required files: `doxygen-awesome.css`
Required `Doxyfile` configuration:
```
GENERATE_TREEVIEW = YES # optional. Also works without treeview
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
```
- <b class="tab-title">Sidebar-Only Theme</b><div class="darkmode_inverted_image">
![](img/theme-variants-sidebar-only.drawio.svg)
</div>
Hides the top titlebar to give more space to the content. The treeview must be enabled in order for this theme to work.
Required files: `doxygen-awesome.css`, `doxygen-awesome-sidebar-only.css`
Required `Doxyfile` configuration:
```
GENERATE_TREEVIEW = YES # required!
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css \
doxygen-awesome-css/doxygen-awesome-sidebar-only.css
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
```
</div>
<br>
@warning
- This theme is not compatible with the `FULL_SIDEBAR = YES` option provided by Doxygen!
- `HTML_COLORSTYLE` must be set to `LIGHT` since Doxygen 1.9.5!
### Further installation instructions
- [Installing extensions](docs/extensions.md)
- [Customizing the theme (colors, spacing, border-radius, ...)](docs/customization.md)
- [Tips and Tricks for further configuration](docs/tricks.md)
## Browser support
Tested with
- Chrome 119, Chrome 119 for Android, Chrome 119 for iOS
- Safari 17, Safari for iOS 16
- Firefox 118, Firefox 120 for Android, Firefox 119 for iOS
- Edge 119
- Opera 108
The theme does not strive to be backward compatible with (significantly) older browser versions.
## Credits
Thanks for all the bug reports and inspiring feedback on GitHub!
Special thanks to all the contributors:
<br><br>
<a href="https://github.com/jothepro/doxygen-awesome-css/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jothepro/doxygen-awesome-css" />
</a>
<div class="section_buttons">
| Read Next |
|---------------------------------:|
| [Extensions](docs/extensions.md) |
</div>

View File

@@ -0,0 +1,121 @@
# Customization
[TOC]
## CSS-Variables
This theme is highly customizable because a lot of things are parameterized with CSS variables.
Just to give you an idea of how flexible the styling is, click this button:
<div class="alter-theme-button" onclick="toggle_alternative_theme()" onkeypress="if (event.keyCode == 13) toggle_alternative_theme()" tabindex=0>Alter theme</div>
<br><hr>
### Setup
It is recommended to add your own `custom.css` and overwrite the variables there:
```
HTML_EXTRA_STYLESHEET = doxygen-awesome.css custom.css
```
Make sure to override the variables in the correct spot. All variables should be customized where they have been defined, in the `html` tag selector:
```css
html {
/* override light-mode variables here */
}
```
For dark-mode overrides, you have to choose where to put them, depending on whether the dark-mode toggle extension is installed or not:
<div class="tabbed">
- <b class="tab-title">dark-mode toggle is installed</b>
```css
html.dark-mode {
/* define dark-mode variable overrides here if you DO use doxygen-awesome-darkmode-toggle.js */
}
```
- <b class="tab-title">dark-mode toggle is **NOT** installed</b>
The dark-mode is enabled automatically depending on the system preference:
```css
@media (prefers-color-scheme: dark) {
html:not(.light-mode) {
/* define dark-mode variable overrides here if you DON'T use doxygen-awesome-darkmode-toggle.js */
}
}
```
</div>
### Available variables
The following list gives an overview of the variables defined in [`doxygen-awesome.css`](https://github.com/jothepro/doxygen-awesome-css/blob/main/doxygen-awesome.css).
The list is not complete. To explore all available variables, have a look at the CSS starting from [here](https://github.com/jothepro/doxygen-awesome-css/blob/main/doxygen-awesome.css#L30).
All variables are defined at the beginning of the stylesheet.
| Parameter | Default (Light) | Default (Dark) |
| :---------------------------------- | :---------------------------------------------------------- | :---------------------------------------------------------- |
| **Color Scheme**:<br>primary theme colors. This will affect the entire websites color scheme: links, arrows, labels, ... |||
| `--primary-color` | <code style="background:#1779c4;color:white">#1779c4</code> | <code style="background:#1982d2;color:white">#1982d2</code> |
| `--primary-dark-color` | <code style="background:#335c80;color:white">#335c80</code> | <code style="background:#5ca8e2;color:black">#5ca8e2</code> |
| `--primary-light-color` | <code style="background:#70b1e9;color:black">#70b1e9</code> | <code style="background:#4779ac;color:white">#4779ac</code> |
| **Page Colors**:<br>background and foreground (text-color) of the documentation. |||
| `--page-background-color` | <code style="background:#ffffff;color:black">#ffffff</code> | <code style="background:#1C1D1F;color:white">#1C1D1F</code> |
| `--page-foreground-color` | <code style="background:#2f4153;color:white">#2f4153</code> | <code style="background:#d2dbde;color:black">#d2dbde</code> |
| `--page-secondary-foreground-color` | <code style="background:#6f7e8e;color:white">#6f7e8e</code> | <code style="background:#859399;color:white">#859399</code> |
| **Spacing:**<br>default spacings. Most ui components reference these values for spacing, to provide uniform spacing on the page. |||
| `--spacing-small` | `5px` | |
| `--spacing-medium` | `10px` | |
| `--spacing-large` | `16px` | |
| **Border Radius**:<br>border radius for all rounded ui components. Will affect many components, like dropdowns, memitems, codeblocks, ... |||
| `--border-radius-small` | `4px` | |
| `--border-radius-medium` | `6px` | |
| `--border-radius-large` | `8px` | |
| **Content Width**:<br>The content is centered and constrained in its width. To make the content fill the whole page, set the following variable to `auto`. |||
| `--content-maxwidth` | `1000px` | |
| **Code Fragment Colors**:<br>Color-Scheme of multiline codeblocks |||
| `--fragment-background` | <code style="background:#F8F9FA;color:black">#F8F9FA</code> | <code style="background:#282c34;color:white">#282c34</code> |
| `--fragment-foreground` | <code style="background:#37474F;color:white">#37474F</code> | <code style="background:#dbe4eb;color:black">#dbe4eb</code> |
| **Arrow Opacity**:<br>By default the arrows in the sidebar are only visible on hover. You can override this behavior so they are visible all the time. |||
| `--side-nav-arrow-opacity` | `0` | |
| `--side-nav-arrow-hover-opacity` | `0.9` | |
| ...and many more |||
If you miss a configuration option or find a bug, please consider [opening an issue](https://github.com/jothepro/doxygen-awesome-css/issues)!
## Doxygen generator
The theme overrides most colors with the `--primary-color-*` variables.
But there are a few small images and graphics that the theme cannot adjust or replace. To make these blend in better with
the rest, it is recommended to adjust the [doxygen color settings](https://www.doxygen.nl/manual/customize.html#minor_tweaks_colors)
to something that matches the chosen color scheme.
For the default color scheme, these values work out quite well:
```
# Doxyfile
HTML_COLORSTYLE_HUE = 209
HTML_COLORSTYLE_SAT = 255
HTML_COLORSTYLE_GAMMA = 113
```
## Share your customizations
If you have customized the theme with custom colors, spacings, font-sizes, etc. and you want to share your creation with others, you can do this [here](https://github.com/jothepro/doxygen-awesome-css/discussions/13).
I am always curious to learn about how you made the theme look even better!
<div class="section_buttons">
| Previous | Next |
|:----------------------------|---------------------------:|
| [Extensions](extensions.md) | [Tips & Tricks](tricks.md) |
</div>

View File

@@ -0,0 +1,284 @@
# Extensions
[TOC]
On top of the base theme provided by `doxygen-awesome.css`, this repository comes with Javascript extensions that require additional setup steps to get them running.
The extensions require customizations in the header HTML template.
This is how you can create the default template with Doxygen:
1. Create default header template:
```sh
doxygen -w html header.html delete_me.html delete_me.css
```
2. Reference the template in your `Doxyfile`:
```
HTML_HEADER = header.html
```
[More details on header customization](https://www.doxygen.nl/manual/customize.html#minor_tweaks_header_css)
## Dark Mode Toggle {#extension-dark-mode-toggle}
Adds a button next to the search bar to enable and disable the dark theme variant manually:
<div class="darkmode_inverted_image bordered_image">
![](img/darkmode_toggle.png){width=250px}
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-darkmode-toggle.js`
- **HTML_EXTRA_STYLESHEET:** `doxygen-awesome-sidebar-only-darkmode-toggle.css`
<em>(ONLY required for the sidebar-only theme variant!)</em>
2. In the `header.html` template, include `doxygen-awesome-darkmode-toggle.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript">
DoxygenAwesomeDarkModeToggle.init()
</script>
</head>
<body>
```
### Customizing
Changing the tooltip of the button:
```js
DoxygenAwesomeDarkModeToggle.title = "Zwischen hellem/dunklem Modus wechseln"
```
Changing Icons. Both Emoji or SVG icons are supported:
```js
DoxygenAwesomeDarkModeToggle.lightModeIcon = '🌞'
// icon from https://fonts.google.com/icons
DoxygenAwesomeDarkModeToggle.darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#009793"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M8.1,14.15C9.77,14.63,11,16.17,11,18c0,0.68-0.19,1.31-0.48,1.87c0.48,0.09,0.97,0.14,1.48,0.14 c1.48,0,2.9-0.41,4.13-1.15c-2.62-0.92-5.23-2.82-6.8-5.86C7.74,9.94,7.78,7.09,8.29,4.9c-2.57,1.33-4.3,4.01-4.3,7.1c0,0,0,0,0,0 c0.01,0,0.01,0,0.02,0C5.66,12,7.18,12.83,8.1,14.15z" opacity=".3"/><path d="M19.78,17.51c-2.47,0-6.57-1.33-8.68-5.43C8.77,7.57,10.6,3.6,11.63,2.01C6.27,2.2,1.98,6.59,1.98,12 c0,0.14,0.02,0.28,0.02,0.42C2.61,12.16,3.28,12,3.98,12c0,0,0,0,0,0c0-3.09,1.73-5.77,4.3-7.1C7.78,7.09,7.74,9.94,9.32,13 c1.57,3.04,4.18,4.95,6.8,5.86c-1.23,0.74-2.65,1.15-4.13,1.15c-0.5,0-1-0.05-1.48-0.14c-0.37,0.7-0.94,1.27-1.64,1.64 c0.98,0.32,2.03,0.5,3.11,0.5c3.5,0,6.58-1.8,8.37-4.52C20.18,17.5,19.98,17.51,19.78,17.51z"/><path d="M7,16l-0.18,0C6.4,14.84,5.3,14,4,14c-1.66,0-3,1.34-3,3s1.34,3,3,3c0.62,0,2.49,0,3,0c1.1,0,2-0.9,2-2 C9,16.9,8.1,16,7,16z"/></g></g></svg>`
```
All customizations must be applied before calling `DoxygenAwesomeDarkModeToggle.init()`!
## Fragment Copy Button {#extension-copy-button}
Shows a copy button when the user hovers over a code fragment:
<div class="darkmode_inverted_image bordered_image">
![](img/fragment_copy_button.png){width=490}
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-fragment-copy-button.js`
- **HTML_COPY_CLIPBOARD:** `NO` required with Doxygen >= 1.10.0
2. In the `header.html` template, include `doxygen-awesome-fragment-copy-button.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
</script>
</head>
<body>
```
### Customizing
The tooltip of the button can be changed:
```js
DoxygenAwesomeFragmentCopyButton.title = "In die Zwischenablage kopieren"
```
The icon can be changed. It must be an SVG:
```js
DoxygenAwesomeFragmentCopyButton.copyIcon = `<svg ...>`
DoxygenAwesomeFragmentCopyButton.successIcon = `<svg ...>`
```
All customizations must be applied before calling `DoxygenAwesomeDarkModeToggle.init()`!
## Paragraph Linking {#extension-para}
Provides a button on hover behind every headline to allow easy creation of a permanent link to the headline:
<div class="darkmode_inverted_image bordered_image">
![](img/paragraph_link.png){width=220}
</div>
Works for all headlines and for many documentation section titles.
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-paragraph-link.js`
2. In the `header.html` template, include `doxygen-awesome-paragraph-link.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript">
DoxygenAwesomeParagraphLink.init()
</script>
</head>
<body>
```
### Customizing
The button tooltip can be changed:
```js
DoxygenAwesomeParagraphLink.title = "Abschnitt verknüpfen"
```
The icon of the button can be changed. Both plain characters or SVG icons are supported:
```js
DoxygenAwesomeParagraphLink.icon = "¶"
```
All customizations must be applied before calling `DoxygenAwesomeParagraphLink.init()`!
## Interactive TOC {#extension-toc}
On large screens, the Table of Contents (TOC) is anchored on the top right of the page. This extension visualizes the reading progress by dynamically highlighting the currently active section.
On small screens, the extension hides the TOC by default. The user can open it manually when needed:
<div class="darkmode_inverted_image bordered_image">
![](img/interactive_toc_mobile.png){width=380}
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-interactive-toc.js`
2. In the `header.html` template, include `doxygen-awesome-interactive-toc.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript">
DoxygenAwesomeInteractiveToc.init()
</script>
</head>
<body>
```
### Customizing
The offset for when a headline is considered active can be changed. A smaller value means that the headline of the section must be closer to the top of the viewport before it is highlighted in the TOC:
```js
DoxygenAwesomeInteractiveToc.topOffset = 45
```
Hiding the TOC on small screens can be disabled. It is still interactive and can be hidden by the user but will now be open by default:
```js
DoxygenAwesomeInteractiveToc.hideMobileMenu = false
```
## Tabs {#extension-tabs}
@warning Experimental feature! Please report bugs [here](https://github.com/jothepro/doxygen-awesome-css/issues).
This extension allows to arrange list content in tabs:
<div class="tabbed">
- <b class="tab-title">Tab 1</b>
This is the content of tab 1
- <b class="tab-title">Tab 2</b>
This is the content of tab 2
1. it has a list
2. with multiple items
</div>
### Installation
1. Add the required resources in your `Doxyfile`:
- **HTML_EXTRA_FILES:** `doxygen-awesome-tabs.js`
2. In the `header.html` template, include `doxygen-awesome-tabs.js` at the end of the `<head>` and then initialize it:
```html
<html>
<head>
<!-- ... other metadata & script includes ... -->
<script type="text/javascript" src="$relpath^doxygen-awesome-tabs.js"></script>
<script type="text/javascript">
DoxygenAwesomeTabs.init()
</script>
</head>
<body>
```
### Usage
Each list that is supposed to be displayed as tabs has to be wrapped with the `tabbed` CSS class.
Each item in the list must start with an element that has the class `tab-title`. It will then be used as tab title.
```md
<div class="tabbed">
- <b class="tab-title">Tab 1</b> This is the content of tab 1
- <b class="tab-title">Tab 2</b> This is the content of tab 2
</div>
```
## Page Navigation {#extension-page-navigation}
@warning Experimental feature! Please report bugs [here](https://github.com/jothepro/doxygen-awesome-css/issues).
To allow the user to easily navigate from one document to another, "Next" and "Previous" buttons can be added at the end of a Markdown document.
### Installation
The feature is shipped inside the default `doxygen-awesome.css`. No additional stylesheets or scripts need to be added.
### Usage
The following conditions must be met for the feature to work properly:
- The navigation must be inside a Markdown table with 1-2 columns.
- The alignment of the column defines the alignment of the arrow on the navigation button.
- the table must be wrapped inside a `<div>` with the class `section_buttons`.
<div class="tabbed">
- <span class="tab-title">Code</span>
```md
<div class="section_buttons">
| Previous | Next |
|:------------------|----------------------------------:|
| [Home](README.md) | [Customization](customization.md) |
</div>
```
- <span class="tab-title">Result</span>
<div class="section_buttons">
| Previous | Next |
|:------------------|----------------------------------:|
| [Home](README.md) | [Customization](customization.md) |
</div>
</div>
<div class="section_buttons">
| Previous | Next |
|:------------------|----------------------------------:|
| [Home](README.md) | [Customization](customization.md) |
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,127 @@
# Tips & Tricks
[TOC]
## Diagrams with Graphviz {#tricks-graphviz}
To get the best-looking class diagrams for your documentation, generate them with Graphviz as vector graphics with transparent background:
```
# Doxyfile
HAVE_DOT = YES
DOT_IMAGE_FORMAT = svg
DOT_TRANSPARENT = YES
```
In case `INTERACTIVE_SVG = YES` is set in the Doxyfile, all user-defined dotgraphs must be wrapped with the `interactive_dotgraph` CSS class for them to be rendered correctly:
```md
<div class="interactive_dotgraph">
\dotfile graph.dot
</div>
```
@note Both the default overflow scrolling behavior in this theme and the interactive editor enabled by `INTERACTIVE_SVG` are unsatisfying workarounds IMHO. Consider designing your graphs to be narrow enough to fit the page to avoid scrolling.
## Disable Dark Mode {#tricks-darkmode}
If you don't want the theme to automatically switch to dark mode depending on the browser preference,
you can disable dark mode by adding the `light-mode` class to the HTML tag in the header template:
```html
<html xmlns="http://www.w3.org/1999/xhtml" class="light-mode">
```
The same can be done to always enable dark mode:
```html
<html xmlns="http://www.w3.org/1999/xhtml" class="dark-mode">
```
@warning This only works if you don't use the dark-mode toggle extension.
## Choosing Sidebar Width {#tricks-sidebar}
If you have enabled the sidebar-only theme variant, make sure to carefully choose a proper width for your sidebar.
It should be wide enough to hold the icon, project title and version number. If the content is too wide, it will be
cut off.
```css
html {
/* Make sure sidebar is wide enough to contain the page title (logo + title + version) */
--side-nav-fixed-width: 335px;
}
```
The chosen width should also be set in the Doxyfile:
```
# Doxyfile
TREEVIEW_WIDTH = 335
```
## Formatting Tables {#tricks-tables}
By default tables in this theme are left-aligned and as wide as required to fit their content.
Those properties can be changed for individual tables.
### Centering
Tables can be centered by wrapping them in the `<center>` HTML tag.
<div class="tabbed">
- <span class="tab-title">Code</span>
```md
<center>
| This table | is centered |
|------------|----------------------|
| test 1 | test 2 |
</center>
```
- <span class="tab-title">Result</span>
<center>
| This table | is centered |
|------------|----------------------|
| test 1 | test 2 |
</center>
</div>
### Full Width
To make tables span the full width of the page, no matter how wide the content is, wrap the table in the `full_width_table` CSS class.
@warning Apply with caution! This breaks the overflow scrolling of the table. Content might be cut off on small screens!
<div class="tabbed">
- <span class="tab-title">Code</span>
```md
<div class="full_width_table">
| This table | spans the full width |
|------------|----------------------|
| test 1 | test 2 |
</div>
```
- <span class="tab-title">Result</span>
<div class="full_width_table">
| This table | spans the full width |
|------------|----------------------|
| test 1 | test 2 |
</div>
</div>
<div class="section_buttons">
| Previous | Next |
|:----------------------------------|---------------------------------------:|
| [Customization](customization.md) | [Example](https://jothepro.github.io/doxygen-awesome-css/class_my_library_1_1_example.html) |
</div>

View File

@@ -0,0 +1,157 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
// SVG icons from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
static title = "Toggle Light/Dark Mode"
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
static _staticConstructor = function() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
// Update the color scheme when the browsers preference changes
// without user interaction on the website.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
}
});
}()
static init() {
$(function() {
$(document).ready(function() {
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
toggleButton.updateIcon()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
toggleButton.updateIcon()
})
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
toggleButton.updateIcon()
}
});
$(document).ready(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
$(window).resize(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
})
})
}
constructor() {
super();
this.onclick=this.toggleDarkMode
}
/**
* @returns `true` for dark-mode, `false` for light-mode system preference
*/
static get systemPreference() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
/**
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
static set userPreference(userPreference) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
if(!userPreference) {
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
}
} else {
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
}
}
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
}
static enableDarkMode(enable) {
if(enable) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
document.documentElement.classList.add("dark-mode")
document.documentElement.classList.remove("light-mode")
} else {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
document.documentElement.classList.remove("dark-mode")
document.documentElement.classList.add("light-mode")
}
}
static onSystemPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
static onUserPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
toggleDarkMode() {
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
this.updateIcon()
}
updateIcon() {
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
} else {
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
}
}
}
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);

View File

@@ -0,0 +1,85 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)

View File

@@ -0,0 +1,91 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeInteractiveToc {
static topOffset = 38
static hideMobileMenu = true
static headers = []
static init() {
window.addEventListener("load", () => {
let toc = document.querySelector(".contents > .toc")
if(toc) {
toc.classList.add("interactive")
if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) {
toc.classList.add("open")
}
document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => {
if(toc.classList.contains("open")) {
toc.classList.remove("open")
} else {
toc.classList.add("open")
}
})
document.querySelectorAll(".contents > .toc > ul a").forEach((node) => {
let id = node.getAttribute("href").substring(1)
DoxygenAwesomeInteractiveToc.headers.push({
node: node,
headerNode: document.getElementById(id)
})
document.getElementById("doc-content")?.addEventListener("scroll",this.throttle(DoxygenAwesomeInteractiveToc.update, 100))
})
DoxygenAwesomeInteractiveToc.update()
}
})
}
static update() {
let active = DoxygenAwesomeInteractiveToc.headers[0]?.node
DoxygenAwesomeInteractiveToc.headers.forEach((header) => {
let position = header.headerNode.getBoundingClientRect().top
header.node.classList.remove("active")
header.node.classList.remove("aboveActive")
if(position < DoxygenAwesomeInteractiveToc.topOffset) {
active = header.node
active?.classList.add("aboveActive")
}
})
active?.classList.add("active")
active?.classList.remove("aboveActive")
}
static throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCall < delay) {
return;
}
lastCall = now;
return setTimeout(() => {func(...args)}, delay);
};
}
}

View File

@@ -0,0 +1,51 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeParagraphLink {
// Icon from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static icon = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
static title = "Permanent Link"
static init() {
$(function() {
$(document).ready(function() {
document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => {
let anchorlink = document.createElement("a")
anchorlink.setAttribute("href", `#${node.getAttribute("id")}`)
anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title)
anchorlink.classList.add("anchorlink")
node.classList.add("anchor")
anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon
node.parentElement.appendChild(anchorlink)
})
})
})
}
}

View File

@@ -0,0 +1,40 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@media screen and (min-width: 768px) {
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
}
}

View File

@@ -0,0 +1,116 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
html {
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
* Make sure it is wide enough to contain the page title (logo + title + version)
*/
--side-nav-fixed-width: 335px;
--menu-display: none;
--top-height: 120px;
--toc-sticky-top: -25px;
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
}
#projectname {
white-space: nowrap;
}
@media screen and (min-width: 768px) {
html {
--searchbar-background: var(--page-background-color);
}
#side-nav {
min-width: var(--side-nav-fixed-width);
max-width: var(--side-nav-fixed-width);
top: var(--top-height);
overflow: visible;
}
#nav-tree, #side-nav {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
height: var(--top-height);
margin-bottom: calc(0px - var(--top-height));
max-width: var(--side-nav-fixed-width);
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
cursor: default;
width: 1px !important;
background: var(--separator-color);
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
#doc-content {
height: calc(100vh - 31px) !important;
padding-bottom: calc(3 * var(--spacing-large));
padding-top: calc(var(--top-height) - 80px);
box-sizing: border-box;
margin-left: var(--side-nav-fixed-width) !important;
}
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
}
#MSearchResultsWindow {
left: var(--spacing-medium) !important;
right: auto;
}
}

View File

@@ -0,0 +1,90 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2023 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeTabs {
static init() {
window.addEventListener("load", () => {
document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => {
let tabLinkList = []
tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => {
tab.id = "tab_" + tabbedIndex + "_" + tabIndex
let header = tab.querySelector(".tab-title")
let tabLink = document.createElement("button")
tabLink.classList.add("tab-button")
tabLink.appendChild(header)
header.title = header.textContent
tabLink.addEventListener("click", () => {
tabbed.querySelectorAll(":scope > ul > li").forEach((tab) => {
tab.classList.remove("selected")
})
tabLinkList.forEach((tabLink) => {
tabLink.classList.remove("active")
})
tab.classList.add("selected")
tabLink.classList.add("active")
})
tabLinkList.push(tabLink)
if(tabIndex == 0) {
tab.classList.add("selected")
tabLink.classList.add("active")
}
})
let tabsOverview = document.createElement("div")
tabsOverview.classList.add("tabs-overview")
let tabsOverviewContainer = document.createElement("div")
tabsOverviewContainer.classList.add("tabs-overview-container")
tabLinkList.forEach((tabLink) => {
tabsOverview.appendChild(tabLink)
})
tabsOverviewContainer.appendChild(tabsOverview)
tabbed.before(tabsOverviewContainer)
function resize() {
let maxTabHeight = 0
tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => {
let visibility = tab.style.display
tab.style.display = "block"
maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight)
tab.style.display = visibility
})
tabbed.style.height = `${maxTabHeight + 10}px`
}
resize()
new ResizeObserver(resize).observe(tabbed)
})
})
}
static resize(tabbed) {
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
html.alternative {
/* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #7aabd6;
--primary-lighter-color: #cae1f1;
--primary-lightest-color: #e9f1f8;
/* page base colors */
--page-background-color: white;
--page-foreground-color: #2c3e50;
--page-secondary-foreground-color: #67727e;
--border-radius-large: 22px;
--border-radius-small: 9px;
--border-radius-medium: 14px;
--spacing-small: 8px;
--spacing-medium: 14px;
--spacing-large: 19px;
--top-height: 125px;
--side-nav-background: #324067;
--side-nav-foreground: #F1FDFF;
--header-foreground: var(--side-nav-foreground);
--searchbar-background: var(--side-nav-foreground);
--searchbar-border-radius: var(--border-radius-medium);
--header-background: var(--side-nav-background);
--header-foreground: var(--side-nav-foreground);
--toc-background: rgb(243, 240, 252);
--toc-foreground: var(--page-foreground-color);
}
html.alternative.dark-mode {
color-scheme: dark;
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #4779ac;
--primary-lighter-color: #191e21;
--primary-lightest-color: #191a1c;
--page-background-color: #1C1D1F;
--page-foreground-color: #d2dbde;
--page-secondary-foreground-color: #859399;
--separator-color: #3a3246;
--side-nav-background: #171D32;
--side-nav-foreground: #F1FDFF;
--toc-background: #20142C;
--searchbar-background: var(--page-background-color);
}

View File

@@ -0,0 +1,57 @@
.github-corner svg {
fill: var(--primary-light-color);
color: var(--page-background-color);
width: 72px;
height: 72px;
}
@media screen and (max-width: 767px) {
.github-corner svg {
width: 50px;
height: 50px;
}
#projectnumber {
margin-right: 22px;
}
}
.alter-theme-button {
display: inline-block;
cursor: pointer;
background: var(--primary-color);
color: var(--page-background-color) !important;
border-radius: var(--border-radius-medium);
padding: var(--spacing-small) var(--spacing-medium);
text-decoration: none;
}
.alter-theme-button:hover {
background: var(--primary-dark-color);
}
html.dark-mode .darkmode_inverted_image img, /* < doxygen 1.9.3 */
html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
filter: brightness(89%) hue-rotate(180deg) invert();
}
.bordered_image {
border-radius: var(--border-radius-small);
border: 1px solid var(--separator-color);
display: inline-block;
overflow: hidden;
}
html.dark-mode .bordered_image img, /* < doxygen 1.9.3 */
html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
border-radius: var(--border-radius-small);
}
.title_screenshot {
filter: drop-shadow(0px 3px 10px rgba(0,0,0,0.22));
max-width: 500px;
margin: var(--spacing-large) 0;
}
.title_screenshot .caption {
display: none;
}

View File

@@ -0,0 +1,90 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!-- BEGIN opengraph metadata -->
<meta property="og:title" content="Doxygen Awesome" />
<meta property="og:image" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta property="og:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
<!-- END opengraph metadata -->
<!-- BEGIN twitter metadata -->
<meta name="twitter:image:src" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta name="twitter:title" content="Doxygen Awesome" />
<meta name="twitter:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<!-- END twitter metadata -->
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<link rel="icon" type="image/svg+xml" href="logo.drawio.svg"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-tabs.js"></script>
<script type="text/javascript" src="$relpath^toggle-alternative-theme.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
DoxygenAwesomeDarkModeToggle.init()
DoxygenAwesomeParagraphLink.init()
DoxygenAwesomeInteractiveToc.init()
DoxygenAwesomeTabs.init()
</script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<!-- https://tholman.com/github-corners/ -->
<a href="https://github.com/jothepro/doxygen-awesome-css" class="github-corner" title="View source on GitHub" target="_blank" rel="noopener noreferrer">
<svg viewBox="0 0 250 250" width="40" height="40" style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@@ -0,0 +1,12 @@
let original_theme_active = true;
function toggle_alternative_theme() {
if(original_theme_active) {
document.documentElement.classList.add("alternative")
original_theme_active = false;
} else {
document.documentElement.classList.remove("alternative")
original_theme_active = true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,117 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="491px" height="261px" viewBox="-0.5 -0.5 491 261" content="&lt;mxfile&gt;&lt;diagram id=&quot;6E4AiNPWWr3a8GvC3Ypl&quot; name=&quot;Page-1&quot;&gt;xZfLrpswEIafBqndIIzBwLK5tZt2k0pd+wQHrBqcOs6tT98xmABxmrYiUUEK+Pd1vhkPjofn1fmjorvys8yZ8MIgP3t44YVhihL4NcKlFZIkbYVC8byVUC+s+U9mxcCqB56z/aihllJovhuLG1nXbKNHGlVKnsbNtlKMZ93RgjnCekOFq37juS6tWXHQ658YL8puZhTYmop2ja2wL2kuTwMJLz08V1Lq9q06z5kw7Doubb/Vb2qvC1Os1n/ToVvHkYqDNc4uTF86awslDzsPz+BR58x0DKBE1cY6JoWSO69dypEpzc73vELfugl6wyFgmKyYVhdod+rRRpldZznAGhIrUuvO4tq3txherNH3AZA/2z+2+1RyzdY7ujG1Jwhv0EpdwfgLBK97reR3NpdCqqY3JktzQ00h6H5vBwEEmvKaKVt28V0dc8vvP3GKHE5zsMEs97m4ls09FUlXm6V+jNteNtVEkZ/a7DPgBi19Qlx0KI19hKfTCx16X7kW7I0qUN99oUdeUM1l7RkbwfRgzWB/le+nwd1yIQZoF8vlYrV6GKMvjcMGPUqyqPudzhU7XNfwZXCwPpfjKjH36zg+jFQUEj/Br4vU2CG6go8Ra4DOFKP5Rh2q3X4i0deSg4wHu3lwZQ7GuwGKsY9HF5nOM3EjtNnaD/ihKfy2kJUHekjM/aR0ihPkJ2GEcJJhFOAoHmVWdLu9Qzd4A+R3naM0TOLsTiDf+I6k052Q3vnIE6EtruZI0hEjPw7m7DXr2Q0kUphnk7q7AWDqdoy2znEroNWPfFfLmt2kGCtRwYsaioJtzQjGTRyOoB+sXPE8N5PcDZXxZjQLtGe1cPJp43x1U5qROEZdQIT/Ggyw24ahkJL4JcEAxf443dQN/pPg5S8=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="490" height="260" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<rect x="198.53" y="44.87" width="219.66" height="185.13" fill="rgb(255, 255, 255)" stroke="#e3e3e3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 137px; margin-left: 200px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Content
</div>
</div>
</div>
</foreignObject>
<text x="308" y="141" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Content
</text>
</switch>
</g>
<rect x="0" y="0" width="490" height="44.87" fill="#deedff" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 22px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Titlebar (Navigation + Search)
</div>
</div>
</div>
</foreignObject>
<text x="245" y="26" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Titlebar (Navigation + Search)
</text>
</switch>
</g>
<rect x="0" y="44.87" width="126.73" height="185.13" fill="#f7f7f7" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 137px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Sidebar (Navigation)
</div>
</div>
</div>
</foreignObject>
<text x="63" y="141" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Sidebar (Navigation)
</text>
</switch>
</g>
<rect x="0" y="226.67" width="490" height="33.33" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 243px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Footer (Breadcrumbs)
</div>
</div>
</div>
</foreignObject>
<text x="245" y="247" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Footer (Breadcrumbs)
</text>
</switch>
</g>
<rect x="371.72" y="14.87" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 23px; margin-left: 373px;">
<div data-drawio-colors="color: #262626; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(38, 38, 38); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Search
</div>
</div>
</div>
</foreignObject>
<text x="422" y="27" fill="#262626" font-family="Helvetica" font-size="12px" text-anchor="middle">
Search
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 23px; margin-left: 19px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
<font color="#262626">
Title
</font>
</div>
</div>
</div>
</foreignObject>
<text x="19" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="20px">
Tit...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,102 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="491px" height="261px" viewBox="-0.5 -0.5 491 261" content="&lt;mxfile&gt;&lt;diagram id=&quot;6E4AiNPWWr3a8GvC3Ypl&quot; name=&quot;Page-1&quot;&gt;xZZNj5swEIZ/DdL2gjAGA8cmm7SX9pJKPTvBAasGU8f56q/vOJjFLOxqpbC7WIrMO/5gnhlP7OFldfmmaFP+kDkTXhjkFw8/emGYogR+jXBthSRJW6FQPG8l1Asb/o9ZMbDqkefsMBiopRSaN0NxJ+ua7fRAo0rJ83DYXorhrg0t2EjY7KgYq795rkvrVhz0+nfGi7LbGQXWUtFusBUOJc3l2ZHwysNLJaVue9VlyYRh13Fp561fsD59mGK1ftOErJ1xouJonbMfpq+dt4WSx2a8sN3rxJRmlynsdNut0HsGGcFkxbS6wrhzzy7KLJDS4RYSK1Ibr+Jpbu8SdKxX0x6+wUHwr86ZGR94eHEuuWabhu6M9Qz5C1qpK1j/EUH3oJX8w5ZSSHWbjcnKNLBMIMpeYPRJLLrz48BYylqb752XyerW7mXSWbPUj8Osf2z1sMUDEZ9YxYEIs3xCJjhi7GM8A0o0QrmBorSlyjPpTgTst9jCCylM7+EX14DWmIBJ8JOeeEE1l/WX+8jvuRAO93Vi2odkKQqJn4TvlajhiO4aaiIzcB8WitF8p45Vc7iT3jtRuriEIoSTDKMARzEe5C2Qggx1n1ESY4L9MIlTEgYkzUgcj3Hf0tl9yAz08Ti3GVW78hXa6B7aeyhCjh4S02aLApQHJwrpsHpEaBiEcSVBAfK7yVEK4cjGUUDPQ5nOEIVo4p+rrSuG1+1/tkNG/h7NjWHRw3OktgLZAtQuAHu3a7S2UVyBrX4teLWs2bPqYyUqeFHDq2B7s4KJE4eL01crVzzPzSaTuTI8u+YD7dUvDD4mGRCaDqN7JLHv5kJKJs7kDNkAr/0l8GZzbtJ49R8=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="0" y="0" width="490" height="260" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<rect x="198.53" y="16.67" width="219.66" height="233.33" fill="rgb(255, 255, 255)" stroke="#e3e3e3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 133px; margin-left: 200px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Content
</div>
</div>
</div>
</foreignObject>
<text x="308" y="137" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Content
</text>
</switch>
</g>
<rect x="0" y="0" width="126.72" height="260" fill="#f7f7f7" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 130px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Sidebar
<br/>
(Title + Navigation)
</div>
</div>
</div>
</foreignObject>
<text x="63" y="134" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Sidebar...
</text>
</switch>
</g>
<rect x="126.72" y="226.67" width="363.28" height="33.33" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 361px; height: 1px; padding-top: 243px; margin-left: 128px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Footer (Breadcrumbs)
</div>
</div>
</div>
</foreignObject>
<text x="308" y="247" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Footer (Breadcrumbs)
</text>
</switch>
</g>
<rect x="12.67" y="41.67" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 50px; margin-left: 14px;">
<div data-drawio-colors="color: #262626; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(38, 38, 38); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Search
</div>
</div>
</div>
</foreignObject>
<text x="63" y="54" fill="#262626" font-family="Helvetica" font-size="12px" text-anchor="middle">
Search
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 20px; margin-left: 15px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
<font color="#262626">
Title
</font>
</div>
</div>
</div>
</foreignObject>
<text x="15" y="26" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="20px">
Tit...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,169 @@
#pragma once
#include <string>
namespace MyLibrary {
enum Color { red = 1, green = 2, blue = 3 };
/**
* @brief Example class to demonstrate the features of the custom CSS.
*
* @author jothepro
*
*/
class Example {
public:
/**
* @brief brief summary
*
* doxygen test documentation
*
* @param test this is the only parameter of this test function. It does nothing!
*
* # Supported elements
*
* These elements have been tested with the custom CSS.
*
* ## Tables
*
* <div class="tabbed">
*
* - <b class="tab-title">Basic</b>
* This theme supports normal markdown tables:<br>
* | Item | Title | Description | More |
* |-----:|-------|-----------------------|--------------------------------------------|
* | 1 | Foo | A placeholder | Some lorem ipsum to make this table wider. |
* | 2 | Bar | Also a placeholder | More lorem ipsum. |
* | 3 | Baz | The third placeholder | More lorem ipsum. |
* - <b class="tab-title">Centered</b>
* <center>
* A table can be centered with the `<center>` html tag:<br>
* | Item | Title | Description | More |
* |-----:|-------|-----------------------|--------------------------------------------|
* | 1 | Foo | A placeholder | Some lorem ipsum to make this table wider. |
* | 2 | Bar | Also a placeholder | More lorem ipsum. |
* | 3 | Baz | The third placeholder | More lorem ipsum. |
* </center>
* - <b class="tab-title">Stretched</b>
* A table wrapped in `<div class="full_width_table">` fills the full page width.
* <div class="full_width_table">
* | Item | Title | Description | More |
* |-----:|-------|-----------------------|--------------------------------------------|
* | 1 | Foo | A placeholder | Some lorem ipsum to make this table wider. |
* | 2 | Bar | Also a placeholder | More lorem ipsum. |
* | 3 | Baz | The third placeholder | More lorem ipsum. |
* </div>
* **Caution**: This will break the overflow scrolling support!
* - <b class="tab-title">Complex</b>
* Complex [Doxygen tables](https://www.doxygen.nl/manual/tables.html) are also supported as seen in @ref multi_row "this example":<br>
* <table>
* <caption id="multi_row">Complex table</caption>
* <tr><th>Column 1 <th>Column 2 <th>Column 3
* <tr><td rowspan="2">cell row=1+2,col=1<td>cell row=1,col=2<td>cell row=1,col=3
* <tr><td rowspan="2">cell row=2+3,col=2 <td>cell row=2,col=3
* <tr><td>cell row=3,col=1 <td>cell row=3,col=3
* </table>
* - <b class="tab-title">Overflow Scrolling</b> The table content is scrollable if the table gets too wide.<br>
* | first_column | second_column | third_column | fourth_column | fifth_column | sixth_column | seventh_column | eighth_column | ninth_column |
* |--------------|---------------|--------------|---------------|--------------|--------------|----------------|---------------|--------------|
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* - <b class="tab-title">Images</b>A table can contain images:<br>
* | Column 1 | Column 2 |
* |---------------------------|-------------------------------------------------|
* | ![doxygen](testimage.png) | ← the image should not be inverted in dark-mode |
*
*
* </div>
*
* ## Diagrams
*
* Graphviz diagrams support dark mode and can be scrolled once they get too wide:
*
* \dot Graphviz with a caption
* digraph example {
* node [fontsize="12"];
* rankdir="LR"
* a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k;
* }
* \enddot
*
* ## Lists
*
* - element 1
* - element 2
*
* 1. element 1
* ```
* code in lists
* ```
* 2. element 2
*
* ## Quotes
*
* > Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
* > ut labore et dolore magna aliqua. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra.
* > Velit sed ullamcorper morbi tincidunt ornare.
* >
* > Lorem ipsum dolor sit amet consectetur adipiscing elit duis.
* *- jothepro*
*
* ## Code block
*
* ```cpp
* auto x = "code within md fences";
* ```
*
* @code{.cpp}
* // code within @code block
* while(true) {
* auto example = std::make_shared<Example>(5);
* example->test("test");
* }
* @endcode
*
* // code within indented code block
* auto test = std::shared_ptr<Example(5);
*
*
* Inline `code` elements in a text. *Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.* This also works within multiline text and does not break the `layout`.
*
*
* ## Special hints
*
* @warning this is a warning only for demonstration purposes
*
* @note this is a note to show that notes work. They can also include `code`:
* @code{.c}
* void this_looks_awesome();
* @endcode
*
* @bug example bug
*
* @deprecated None of this will be deprecated, because it's beautiful!
*
* @invariant This is an invariant
*
* @pre This is a precondition
*
* @post This is a postcondition
*
* @todo This theme is never finished!
*
* @remark This is awesome!
*
*/
std::string test(const std::string& test);
virtual int virtualfunc() = 0;
static bool staticfunc();
};
class SecondExample {
std::string foo();
}
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <string>
#include "example.hpp"
#include <iostream>
namespace MyLibrary {
/**
* @brief some subclass
*/
template<typename TemplatedClass>
class SubclassExample : public Example {
public:
/**
* @bug second bug
* @return
*/
int virtualfunc() override;
/**
* @brief Template function function
*/
template <typename T>
std::shared_ptr<std::string> function_template_test(std::shared_ptr<T>& param);
/**
* @brief Extra long function with lots of parameters and many template types.
*
* Also has a long return type.
*
* @param param1 first parameter
* @param param2 second parameter
* @param parameter3 third parameter
*/
template <typename T, typename Foo, typename Bar, typename Alice, typename Bob, typename Charlie, typename Hello, typename World>
std::pair<std::string, std::string> long_function_with_many_parameters(std::shared_ptr<T>& param1, std::shared_ptr<std::string>& param2, bool parameter3, Alice paramater4 Bob parameter 5) {
if(true) {
std::cout << "this even has some code." << std::endl;
}
}
};
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="61px" height="74px" viewBox="-0.5 -0.5 61 74" content="&lt;mxfile host=&quot;drawio-plugin&quot; modified=&quot;2021-03-16T23:58:23.462Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36&quot; version=&quot;13.7.9&quot; etag=&quot;JoeaGLJ54FcERO7YrWLQ&quot; type=&quot;embed&quot;&gt;&lt;diagram id=&quot;JMB9aH8b_oZ7EWDuqJgx&quot; name=&quot;Page-1&quot;&gt;7VdNc5swEP01HDsjkGPDsSVJe+lMZnzoWYENaAwsI8ux6a+vCCtA4KSu62kmSS+M9LT7tB9P0uDxuDx8VaLOv2MKhRew9ODxay8Igigy3xZoCOC8AzIl0w7yB2AtfwKBjNCdTGHrGGrEQsvaBROsKki0gwmlcO+aPWDh7lqLDGbAOhHFHP0hU513aHjFBvwbyCy3O/uMVkphjQnY5iLF/QjiNx6PFaLuRuUhhqKtna1L53f7zGofmIJKn+RAcTyKYkfJUWC6sdlmCnc1mYHScDhWY3Fvzdk8Br/PzCgCsAStGmNCRJy2JDH4pIV8VMG+edS4rCcZcjMDSu+ZVP3fpwpV+rnVh5ndF5hsPP4l16VhvPbN8AErTWI0re7mMRaonpw5Y8tlHBvcsNzKwnpttVDaslZYgcXIhj3NFW56LS1bbrM44l6m4Wq5MLhxzEDfgZKmAKDWtUhklRFNgqVM7LYb0Enu8I9j9dkVC80KtgS6Lb3fGnYVgXSm/1Ez2fFu7oeTYA/CuIUWU1AILR9d/mN9pR3uUJqde7F88leOWhYLl2GLO5UAOY2FP+GxMm3c6CwNlXlKY9oompFZ3Rps59EOkuw8BoH2BTtNs8EfaZbUdYZkXQGuXhDgR9DYRBycXURj00D+UmMT2ktJLnr9B8HG0IzFcPkHYfUe3oPZqfOjMEiDs1+KEw5n9P/+/1f3f/gq1394lt7erqQ+0HVvpsPPRWc+/KHxm18=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 13 57 L 13.01 57.01 L 15.87 50.14 L 18.37 43.14 L 20.91 36.15 L 23.67 29.25 L 26.4 22.33 Q 30 13 33.71 22.28 L 33.55 22.22 L 35.48 26.91 L 37.49 31.64 L 39.48 36.36 L 41.2 40.97 L 43.05 45.63" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 47.51 56.77 L 47.65 56.93 L 45.43 54.91 L 43.41 53.11 L 41.43 51.35 L 39.63 49.8 L 37.48 47.86 L 37.39 47.64 L 39.79 47.17 L 41.9 45.98 L 44.24 45.37 L 46.48 44.52 L 48.62 43.4 L 48.54 43.39 L 48.58 46.09 L 48.04 48.74 L 48.04 51.43 L 47.8 54.1 L 47.51 56.77 Z Z" fill-opacity="0.1" fill="#010508" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 43 L 9.94 42.88 L 12.16 41.98 L 14.31 40.96 L 16.51 40.01 L 18.62 38.89 L 20.88 38.1 Q 30 34 40 34 L 40 33.75 L 42 33.83 L 44 33.8 L 46 33.79 L 48 34.05 L 50 34" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="7" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 54 L 9.97 53.99 L 12.69 47.07 L 15.43 40.16 L 18.07 33.21 L 20.65 26.24 L 23.4 19.33 Q 27 10 30.71 19.28 L 30.66 19.26 L 32.46 23.91 L 34.55 28.66 L 36.26 33.27 L 38.35 38.03 L 40.05 42.63" fill="none" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 44.51 53.77 L 44.56 53.83 L 42.48 51.97 L 40.5 50.21 L 38.48 48.41 L 36.41 46.56 L 34.48 44.86 L 34.55 45.02 L 36.72 44 L 39 43.24 L 41.21 42.28 L 43.48 41.51 L 45.62 40.4 L 45.78 40.42 L 45.51 43.09 L 45.01 45.74 L 44.87 48.42 L 44.94 51.12 L 44.51 53.77 Z Z" fill="#1982d2" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 7 40 L 7.02 40.05 L 9.28 39.25 L 11.33 38 L 13.48 36.96 L 15.73 36.14 L 17.88 35.1 Q 27 31 37 31 L 37 30.79 L 39 31.11 L 41 30.85 L 43 30.78 L 45 30.89 L 47 31" fill="none" stroke="#1982d2" stroke-width="8" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,34 @@
{
"name": "@jothepro/doxygen-awesome-css",
"version": "2.3.4",
"description": "Custom CSS theme for doxygen html-documentation with lots of customization parameters.",
"main": "",
"scripts": {
"npm-pack": "npm pack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/jothepro/doxygen-awesome-css.git"
},
"homepage": "https://jothepro.github.io/doxygen-awesome-css/",
"bugs": {
"url": "https://github.com/jothepro/doxygen-awesome-css/issues"
},
"keywords": [
"doxygen",
"css",
"theme",
"awesome"
],
"author": {
"name": "jothepro",
"url": "https://github.com/jothepro",
"git": "https://github.com/jothepro/doxygen-awesome-css"
},
"license": "MIT",
"config": {},
"dependencies": {},
"devDependencies": {},
"xpack": {}
}

129
docs/static/equations.md vendored Normal file
View File

@@ -0,0 +1,129 @@
# Equation MVP
Start with [the consumer sandbox](../tests/sandbox/equations_sandbox.cpp), then read
[the barotropic density balance](../src/include/serif/equations/barotropic_density.hpp) and
[primal gravity](../src/include/serif/equations/primal_gravity.hpp).
The sandbox assembles a manufactured equilibrium on a box; it is not a stellar solver.
## Physics and execution
Physics lives in `serif/equations/<name>.hpp`. An equation declares its test term,
existing `Operand` dependencies, local residual, explicit partial derivatives,
admissibility, and a numerical-order hint. EOS calls occur here, never in assembly.
`EquationPoint` and `PointLinearization` are the author-facing input and output types.
The `Equation` concept checks this interface without selecting a backend.
`EquationRegistry{...}` is the model's list of equations and validates unique residual
rows. It does not own coefficients, prepared data, or solver layout. Scalar H1/L2
values and gradients, and vector H1 values/divergences/gradients are executable volume operations. Declaring
another operation does not make it executable.
Execution lives in `serif/operators`. `EquationPlan` interprets the description;
`operators::backend::mfem::make_volume_equation_cache` declares a graph whose ordinary
dependency cache owns the accepted linearization. `prepare_volume` supplies current
inputs; free residual and Jacobian functions consume the accepted data. `Workspace`
is reusable scratch, separate from frozen element data. All equations use
the same graph construction and application functions. The physics files do not
include MFEM or an operator backend.
See [the operator architecture and extension guide](operators.md) for ownership,
restrictions, and the numerical implementation. Start with the consumer sandbox to
see how a model connects MFEM term bindings, named caches, and the MFEM Jacobian adapter.
## Mathematics and state
The density balance is `integral (A(rho) - A(rho_s) + Phi - Omega^2 Psi - U_s) q_rho dV` on stellar
domains, with the EOS pressure potential `A`. It reads `Omega` and the surface level `U_s` from global
scalar coefficients in the numerical state; its constructor specifies the rotation axis and center.
The generic Jacobian applies `d(w f) = w df + f dw`, including the equation's explicit
position derivative. Scalar pullback basis functions stay fixed when displacement varies;
gradients and divergences also differentiate the mapped operand and test. Packed Jacobian
columns follow `EquationPlan::Columns` (unique terms), then displacement.
Default preparation uses `PreparationMode::reuse_unchanged` and refreshes only changed
dependencies. Explicit `PreparationMode::rebuild` forces the requested graph to refresh. Caller vectors may change or be destroyed after
preparation without changing that snapshot. A rejected preparation invalidates the previous
snapshot; residual and Jacobian calls then fail until a successful preparation.
`try_prepare_volume` returns numerical rejections with element/point context. Incorrect bindings
and sizes remain programming errors reported through SERiF's always-active checks.
Mesh, spaces, mapping object, term bindings, and compactification grid function must outlive
the cache and any accepted data still in use. Rebuild term bindings after mesh topology or space changes. For coordinate
edits, notify MFEM with `NodesUpdated()` and prepare again. In-place coordinate changes that bypass
MFEM's update notification cannot be detected. Calls sharing MFEM spaces/mapper are serial.
`discretization::backend::mfem::TermBinding` associates an existing term with an MFEM
space, using its registered support and domain schema. It owns only its supported-DOF indices. It does not define
another field catalog. When a general DOF implementation is migrated, consolidate this
binding with it rather than retaining two maps.
The existing quadrature policy can override each equation's quadrature estimate. The shared rule
covers every operand form and includes an estimate for the displacement determinant's degree.
Fractional/tabulated EOS and curved mappings still require quadrature convergence checks.
## Gravity contributions
[`primal_gravity.hpp`](../src/include/serif/equations/primal_gravity.hpp) contains
`PrimalPoissonBalance{four_pi_G}`. The coefficient is supplied by the caller so dimensional
and normalized units use the same equation. Its volume residual is
```text
integral_all grad(v) . grad(Phi) + four_pi_G integral_star v rho
```
A row can return `contributions()` as a tuple. Each contribution declares a
`TestOperand`, `Support`, `Operands`, and a point evaluator returning the coefficient
of its test. `Contribution<Operand<Test, Operation>, Support>` supplies the first two
aliases and the row term. This keeps the gradient on the test and the restricted source
visible in the physics file. The zero potential at infinity is imposed separately.
Values are selected with `point.fields.get<Term>()` when a term appears once, or
`point.fields.get<Operand<Term, Operation>>()` when sampling more than one operation.
The divergence of a vector is a scalar. For a vector result use
`PointLinearization<Operands, discretization::forms::VectorValue>`; a derivative with
respect to a scalar is a three-vector, and with respect to a vector is a 3x3 matrix
indexed `[output_component][input_component]`. Existing scalar equations retain their
original interface.
Use the scalar binding for `gravity::potential` and the restricted density binding. The row uses
the same `make_volume_equation_cache` and numerical application functions as every other
equation; no gravity-specific MFEM operator is required.
## Scope and regression coverage
The backend handles scalar H1/L2 **value**, scalar H1 **gradient** and vector H1 **value/divergence/gradient** volume
equations in three dimensions on conforming serial meshes. It rejects unsupported
operations and spaces. Scalar boundary constraints, point constraints, global scalars, tensor contractions
and complete serial transpose actions are implemented; see [the operator guide](operators.md).
H(div), H(curl), surface integrals and MPI assembly remain deferred.
The closure numerical tests were in **MeanField**, not yet SERiF. The following contracts are
ported using SERiF naming/style and a small serial mesh with core, envelope and vacuum attributes.
They exercise the shared volume machinery through a test-only two-field closure,
`rho - rho_EOS(h)`, declared in `tests/include/serif/tests/equations/test_equations.hpp`; it is
not a production equation:
| MeanField test contract | SERiF coverage |
| --- | --- |
| Representable constant closure; stellar mass matrix | `closure_kernels.cpp` |
| Thermodynamic and geometry centered differences | `closure_kernels.cpp`, `closure_conditions.cpp` |
| Mapped volume, vacuum exclusion, directional linearity | `closure_kernels.cpp` |
| Supported dimensions; reduced/full restriction | `closure_prepared.cpp`, `state_validation.cpp` |
| EOS and folded-geometry rejection, then recovery | `closure_prepared.cpp` |
| Residual, individual columns, combined and packed actions vs manual assembly | `closure_prepared.cpp` |
| Frozen ownership and fresh preparation after rho/h/geometry changes | `closure_prepared.cpp` |
| n=1, 1.5, 3 and deformation conditions; balanced combined differences | `closure_conditions.cpp` |
| Exact closure remains zero under deformation | `closure_conditions.cpp` |
| Context identity/revision invalidation | Shared exact snapshots and binding/mesh validation; explicit full refresh remains available |
`manual_closure.cpp` ports the direct quadrature algorithm and evaluates the analytic polytrope
independently of the new equation evaluator and cached basis contractions. It is a test reference,
not a second production operator. The shared serial tests additionally compare tensor and dense
contractions and complete transpose actions; MPI assembly remains outside this port.
Additional checks cover tetrahedra/hexahedra, both displacement orderings, analytic dilation volume
and derivative, stale bindings, quadrature overrides and a compatible non-polytropic EOS.
No new convergence experiments or resolution study are part of this change.
Build with `meson compile -C build`, run the full suite with `meson test -C build --print-errorlogs`,
or select the new cases with `build/tests/serif_tests '[equations]'`.

3
docs/static/gravity-force.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# Gravity force
This document described the removed volume displacement-force formulation; the equation no longer exists.

3
docs/static/gravity-test-port.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# MeanField gravity test migration inventory
This document inventoried the migration of the old mixed (H(div)) gravity tests; that formulation and its tests have been removed.

172
docs/static/laneemden.md vendored Normal file
View File

@@ -0,0 +1,172 @@
# Lane–Emden profiles and radial projection
`serif::utils::laneemden` solves the dimensionless, regular-center Lane–Emden equation,
\[
\theta''+\frac{2}{\xi}\theta'+\theta^n=0,
\qquad \theta(0)=1,\qquad\theta'(0)=0.
\]
The dimensionless calculation is independent of MFEM, the equation of state, and any
choice of physical units. Seed construction and normalization can therefore share it.
## Dimensionless solution
```cpp
#include "serif/utils/laneemden/laneemden.hpp"
const auto solution = serif::utils::laneemden::solve(3.0, {
.step = 0.01,
.maximum_radius = 100.0,
.maximum_steps = 100000,
.surface_tolerance = 1e-10
});
```
The result is `std::expected<Solution, std::string>`. Check success before dereferencing.
A solution contains `n`, an ordered vector of `Sample{xi, theta, derivative}`, and an
optional `surface`. The final sample is the first zero when found. Otherwise it is the
requested maximum radius: this is a valid finite-interval solution, including the
infinite-radius profiles with `n >= 5`. Exhausting the step budget is an error.
`evaluate(solution, xi)` interpolates within the stored interval and rejects extrapolation.
The solver retains the central power-series recurrence from Boudreaux's original
`PolytropicStellarModel`, evaluating the coefficients iteratively. Both `theta` and its
derivative are initialized from that series and advance together through classical RK4.
The final approach uses shorter steps and a short root bracket, avoiding nonreal powers
of negative `theta` for fractional indices. `surface_tolerance` controls the final root
localization, **not the accumulated integration error**. Reduce `step` to establish
convergence. Fractional-index surface derivatives need not converge at fourth order,
because the solution loses smoothness there. The regular analytic solutions at
`n = 0, 1, 5` are covered by the tests.
## Choose a physical star
Three functions produce the same small `PhysicalScales` type:
| Function | Physical inputs, besides the solution and explicit `G` |
|---|---|
| `scale_from_mass` | Total mass and central density |
| `scale_from_enthalpy` | Central specific enthalpy and central density |
| `scale_from_polytropic_constant` | Polytropic EOS constant `K` and central density |
Each requires a found finite surface. The index and surface give
`mu = -xi_surface^2 * theta_prime_surface`. Mass and central density then fix
\[
\alpha=\left(\frac{M}{4\pi\rho_c\mu}\right)^{1/3},\qquad
R=\alpha\xi_{\mathrm{surface}},\qquad
h_c=4\pi G\rho_c\alpha^2.
\]
For an EOS prescription, `h_c = (n + 1) K rho_c^(1/n)` instead fixes `alpha`.
`scale_from_polytropic_constant` requires `n > 0`; the incompressible `n = 0` limit
can use either other prescription. `K` is a plain number because its dimensions depend
on `n`. Other physical inputs use SERiF's distinct quantity types.
Choosing mass and central density creates a reference star and reports its implied
`K`; it does not modify an existing EOS. To preserve a prescribed polytropic EOS,
use its `K`. In particular, at `n = 3` a fixed `K` and `G` already fix the mass:
changing central density changes the radius, not that mass.
Use one consistent unit system throughout. SERiF does not convert units implicitly.
For example, CGS inputs use grams, centimetres, seconds, density in grams per cubic
centimetre, and `G` in cubic centimetres per gram per square second. Neither physical
scaling nor integration contains a hardcoded gravitational constant.
`evaluate_physical(solution, scales, radius)` returns density, pressure, specific
enthalpy, gravitational potential, enclosed mass, and `gravity`. Here `gravity` is
positive outward `dPhi/dr`; inward gravitational acceleration is its negative.
Potential is zero at infinity. Beyond the surface the evaluator uses vacuum values:
zero density, pressure, and enthalpy, constant enclosed mass, `Phi = -GM/r`, and
`gravity = GM/r^2`. At the surface it uses the vacuum side of the density jump for
`n = 0`.
## Project a physical profile
Projection accepts a radial callable returning either `double` or
`std::expected<double, std::string>`. The latter propagates sampling failures. This
complete function prepares a reference polytrope and projects its density:
```cpp
#include "serif/utils/laneemden/laneemden.hpp"
#include "serif/utils/projection/projection.hpp"
/** @brief Construct a polytropic reference density on a supplied scalar H1 or L2 space. */
serif::utils::projection::backend::mfem::ProjectionResult polytropic_density(
mfem::FiniteElementSpace& space,
serif::dimensions::QuantityValue<serif::dimensions::Mass> mass,
serif::dimensions::QuantityValue<serif::dimensions::MassDensity> central_density,
serif::dimensions::QuantityValue<serif::dimensions::GravitationalConstant> gravitational_constant
) {
namespace laneemden = serif::utils::laneemden;
namespace projection = serif::utils::projection;
namespace dimensions = serif::dimensions;
const auto solution = laneemden::solve(3.0);
if (!solution) {
return std::unexpected(solution.error());
}
const auto scales = laneemden::scale_from_mass(
*solution, mass, central_density, gravitational_constant
);
if (!scales) {
return std::unexpected(scales.error());
}
const auto density = [&](double radius) -> projection::RadialValue {
return laneemden::evaluate_physical(
*solution, *scales, dimensions::QuantityValue<dimensions::Length>{radius}
).transform([](const laneemden::PhysicalSample& sample) {
return sample.density.value();
});
};
return projection::backend::mfem::project_scalar(space, density);
}
```
The returned grid function owns its coefficients and borrows the supplied finite-element
space. That space and its mesh must remain alive. Projection completes synchronously,
so the profile and scaling objects in the example do not need to outlive the call.
Use `project_vector` with the radial `gravity` amplitude for outward potential gradients
in component-vector spaces. The projection handles MFEM component ordering. For a displaced
star, options can supply `.center = {x, y, z}`. An optional `.mapping = &evaluator` samples
the physical geometry through SERiF's mapping and applies the required scalar-integral pullback.
The mapping must belong to the same mesh and be refreshed after its fields change.
These are the finite element's native coefficient projections, usually interpolation;
hanging-node constraints are enforced by conforming restriction before
publishing the field. They do not enforce a global mass constraint or perform a global L2 fit. There is no
implicit rescaling of the mesh or adjustment of the sampled profile. Numerical sample
failures discard the entire result. Incompatible space or mapping bindings throw a
caller-error exception. Compactified infinity needs an explicit limiting-value prescription,
as shown below.
## Compactified infinity
Finite physical samples are still taken from the radial profile. At points identified
by the mapping as infinity, `scalar_at_infinity` or `vector_at_infinity` can supply an
explicit limit. Each callback receives the original mesh position and returns the
limit **already pulled back into mesh coordinates**, before MFEM's own element mapping.
Without a callback, projection rejects those points. A failed, non-finite, or
wrong-dimensional limit also rejects the result.
For example, a star's vacuum potential tends to zero at infinity:
```cpp
namespace projection = serif::utils::projection;
const projection::backend::mfem::RadialProjectionOptions exterior{
.mapping = &evaluator,
.scalar_at_infinity = [](const mfem::Vector&) -> projection::RadialValue {
return 0.0;
}
};
```
Pass `exterior` as the last argument to the projection call. A displaced exterior or a
different asymptotic field must provide its own limit.
The callback is used only at points the mapper classifies as infinity. Finite points
extremely close to that boundary still use the existing Jacobian determinant and
inverse, whose conditioning can reduce accuracy.

2
docs/static/mainpage.md vendored Normal file
View File

@@ -0,0 +1,2 @@
# SERiF
This is an initial implementation of the numerics of SERiF

211
docs/static/normalization.md vendored Normal file
View File

@@ -0,0 +1,211 @@
# Stellar normalization
Normalization changes the coordinates used by the numerical solver. It does not
change the descriptive stellar model, its EOS, or the physical values supplied to
equation assembly. The default configuration estimates a reference star with the
shared dimensionless Lane–Emden solver and prepares dimensionless Riesz coordinates.
## Public configuration
Include `serif/backend/mfem/model/equilibrium.hpp`. Given a descriptive
`stellar_model`, existing `finite_elements` from `make_stellar_discretization`
(optionally `with_deformation`), and the usual physical `coefficients` and `geometry`:
```cpp
namespace stellar = serif::model::backend::mfem;
namespace numerical = serif::solver::normalization;
auto discretization = stellar::make_discretization(stellar::DiscretizationOptions{
.model = stellar_model,
.finite_elements = finite_elements,
.normalization = numerical::make_normalization()
});
auto evaluator = stellar::bind_stellar_model(discretization, coefficients, geometry);
auto result = evaluator.evaluate(coefficients, geometry);
if (result) {
auto system = *result;
// system->residual(), system->jacobian(), system->coordinates()
}
```
`StellarModel` describes the physics. `StellarState` owns one candidate's physical
coefficients. `EquilibriumEvaluator` evaluates candidates and returns owning,
immutable results. A successful evaluation does not accept a nonlinear step;
the solver makes that decision and retains whichever state and result it needs.
For a moving star, one state contains both fields and independent surface
coefficients. Given its physical `layout` and initialized packed coefficients:
```cpp
serif::model::StellarState state(layout, physical_coefficients);
auto evaluator = stellar::bind_stellar_model(discretization, state);
auto result = evaluator.evaluate(state);
```
Use `state.get<Term>()` for typed field access or `state.values()` for a packed
physical update. Copying the state creates an independent trial candidate.
Prescribed fields remain separate inputs, with no solved coordinate. Fixed-geometry
evaluation additionally takes the prescribed displacement. External field views
remain supported through the first example; both paths use the same computations.
`DiscretizationOptions{...}` and `NormalizationOptions{...}` are deduced aggregate
types. The explicit aggregate names let C++ infer their template arguments. A bare
`{...}` passed to a function template cannot do that deduction. The model,
reference strategy, coordinate convention, and explicit plan retain their types;
there is no runtime strategy registry.
The default is `n = 3`, the model's `FixedTotalMass` target, and `G = 1` in the
existing code-unit convention. No model prescribes central density, so supply its
reference value explicitly; the mass may also be given explicitly when the model has no
`FixedTotalMass`. Missing values are a compile-time error. To override the reference without changing the physical model:
```cpp
using namespace serif::dimensions;
auto normalization = numerical::make_normalization(numerical::NormalizationOptions{
.scheme = numerical::LaneEmdenApproximation{
.n = 3.0,
.mass = MassValue{2.0},
.central_density = DensityValue{0.9}
},
.coordinates = numerical::Dimensionless{},
.gravitational_constant = QuantityValue<GravitationalConstant>{1.0}
});
```
Either dimensional input can be omitted independently when the corresponding
model target exists. Integration controls belong to `scheme.integration`.
The coordinate choice is a type rather than a boolean so the selected plan is checked
at compilation. `IdentityCoordinate` remains available in explicit plans.
## Reference star and units
The Lane–Emden solution supplies its first zero `xi1` and the dimensionless mass
`mu1 = -xi1^2 theta'(xi1)`. With a reference mass `M` and central density `rho_c`,
\[
a = \left(\frac{M}{4\pi\rho_c\mu_1}\right)^{1/3},\qquad R = a\xi_1.
\]
The index and mass alone do not determine the radius. In particular, an `n = 3`
polytrope still needs a density or length scale. The normalization reference is
an estimate; it need not have the same polytropic index as the model's EOS.
The resulting scales include
\[
\rho_0=M/R^3,\quad h_0=GM/R,\quad P_0=GM^2/R^4,\quad
g_0=GM/R^2,\quad \Omega_0=\sqrt{GM/R^3},\quad F_0=GM^2/R^2.
\]
Here `rho_0` is a characteristic density, not the supplied central density.
The implied time scale is `T0 = 1 / Omega0`. Physical `G` must be supplied in the
same units as the model, mesh, EOS, and constraints. It is not inferred from mass
and radius. In SI or CGS, supply the corresponding numerical value of `G` and
consistent values everywhere else. These quantity types enforce dimensions, not
automatic SI/CGS conversion. The reference `G` must agree with the physical
Poisson equation to interpret its dimensionless value as one; generic equation
registries are not inspected to infer or replace their gravitational constant.
Invalid reference inputs, integration limits that do not reach a finite surface,
and nonrepresentable scales return preparation failures. For example, `n = 5`
has a valid dimensionless profile but cannot furnish a finite stellar radius.
## From equations to solver coordinates
The automatic plan reads state quantities from field metadata and residual
quantities from the equations. Their units need not be the same. Each reference
Gram diagonal is assembled once using the field's existing FE representation,
support, and ordering. RT flux coordinates retain the native Piola transform.
For a volume Gram diagonal entry `D`, a field scale `q`, and reference radius `R`,
the dimensionless factors are
\[
S_i=\frac{\sqrt{D_i}}{q R^{3/2}},\qquad
L_i=\frac{1}{q R^{3/2}\sqrt{D_i}}.
\]
The residual `q` describes the equation density before weak integration. Global
scalars use their own quantity scale without a volume measure. Strong surface
replacement rows use the selected boundary formulation's point quantity, which
may be density or pressure. Surface displacement and generalized force use the
reference surface metric and their appropriate primal and dual geometric factors.
With these diagonal transformations, the solver receives
\[
\widehat u=Su,\qquad \widehat r=Lr,\qquad
\widehat J=LJS^{-1}.
\]
`coordinates().denormalize_state()` also converts a normalized Newton correction
back to a physical correction. All transformations are linear and have no offset.
The existing scaled preconditioner adapter uses the same frozen factors and
passes the dimensional Jacobian to the physical inverse.
## Evaluation, ownership, and extension
The reference configuration is resolved once when constructing the configured
discretization. Numerical evaluation is deferred until `evaluate`. Reference
scales and metrics share the ordinary dependency graph and remain independent of
candidate field coefficients and surface displacement. Reference mesh revisions
invalidate the metrics through existing MFEM revision tracking.
A successful `evaluate()` returns an expected value containing a shared pointer to
the matching physical system, coordinate factors, normalized residual, and
normalized Jacobian. Keep that pointer through a linear solve. It survives later
evaluations, rejection, and destruction of the evaluator. A failed physical or
normalization computation leaves previously returned results intact and makes
the current cache unavailable. `system->physical()` exposes the dimensional
system for diagnostics and preconditioning. Evaluation never changes the state.
Operator scratch storage is reused, so one operator instance is not intended for
concurrent applications.
A configured discretization can also select an ordinary preconditioner recipe:
```cpp
auto configuration = make_discretization(DiscretizationOptions{
.model = stellar_model,
.finite_elements = finite_elements,
.normalization = normalization,
.preconditioner = preconditioner::make_primal_preconditioner(state.layout())
});
auto evaluator = bind_stellar_model(std::move(configuration), state);
auto evaluated = evaluator.evaluate(state);
auto linear_system = evaluator.prepare_linear_system(state);
```
The readable composed defaults live in
`solver/preconditioner/default_preconditioners.hpp`. The primal recipe covers
density, potential, optional surface motion, and all generated scalar constraints. It uses the existing composition factories; custom recipes remain
available through the same configuration member. Omitting the member keeps ordinary
evaluation available without adding inverse computations.
`evaluate()` requests only the evaluated system. `prepare_linear_system()` requests
its matching inverse in the same dependency graph and returns an expected owning
handle. Check that expected result before using `(*linear_system)->system()` and
`(*linear_system)->preconditioner()` with a linear solver. The former exposes the
same normalized residual and Jacobian interface as `evaluate()`. The latter uses
exactly those coordinates, retaining the dimensional inverse and its physical
Jacobian underneath. `contract()` distinguishes fixed linear and variable inverse
applications when selecting the outer Krylov method.
Unchanged inputs reuse prepared results. Candidate changes refresh the required
physical blocks and numerical factors, while reference geometry and normalization
can remain reusable. A trial evaluation does not request the inverse branch.
Numerical inverse setup failures are returned as `preconditioner::Failure` in the
equilibrium failure variant; earlier owning linear systems remain usable after
failure, later evaluation, and evaluator destruction. Successful preparation alone
does not accept a nonlinear step.
To change the reference or coordinate convention, construct a new configuration
and binding. For additional reference strategies, provide
`prepare_reference_scales(strategy, gravitational_constant)` returning
`std::expected<StellarReferenceScales, std::string>` in the strategy's namespace.
Explicit `StellarReferenceScales` are also accepted as the scheme.
Advanced configurations may supply a complete `NormalizationPlan`, plus ordinary
dependency `sources` and `computations`. Compile-time checks require one supported
rule per unknown and equation row. Existing runtime prescriptions and custom
metrics use these same hooks; no separate normalization cache or scheduler is
introduced.

66
docs/static/operator-code-size.md vendored Normal file
View File

@@ -0,0 +1,66 @@
# Operator migration: code-size estimate
Measured from the working trees on 2026-09-24, after the rotational-force port.
These are source-line counts, not a claim that every old capability has migrated.
The original repository is retained: total source across both repositories has grown.
The **replacement implementation** is substantially smaller.
| Production scope | MeanField | SERiF | Reduction |
| --- | ---: | ---: | ---: |
| Closure, hydrostatic balance, mixed gravity, gravitational force and rotational force, including their operator machinery | 15,995 | 2,145 | 13,850 lines (86.6%) |
| Same comparison, excluding blank and comment-only lines | 12,860 | 1,632 | 11,228 lines (87.3%) |
| More conservative baseline: only the old stateless kernels and rigid-rotation law, versus the entire new layer above | 4,319 | 2,145 | 2,174 lines (50.3%) |
SERiF's count charges all of `src/include/serif/equations/`, all of
`src/include/serif/operators/`, both MFEM binding/DOF-map headers in
`src/include/serif/discretization/backend/`, and `discretization/forms/value.hpp`
to these migrated equations. This includes the reusable infrastructure, not just
the short physics files. Both sides exclude mesh generation, general mapping,
EOS implementation, pre-existing term/form/quadrature infrastructure, build files,
documentation and tests. The old generic field/DOF infrastructure is also excluded;
charging the new bindings makes the estimate more conservative on the new side.
The old production manifest consists of interface and implementation files for:
| Old family | File stems under `libmeanfield/{interface,impl}/operators/` | Lines |
| --- | --- | ---: |
| Closure | `kernels/barotropic_closure_kernels`, `contexts/barotropic_closure_linearization_context`, `prepared_barotropic_closure`, `prepared_barotropic_closure_operator` | 2,401 |
| Hydrostatic | `kernels/hydrostatic_equilibrium_kernels`, `contexts/hydrostatic_equilibrium_context`, `prepared_hydrostatic_equilibrium`, `prepared_hydrostatic_equilibrium_operator` | 3,141 |
| Mixed gravity | `gravity_field`, `gravity_field_jacobian`, `kernels/gravity_kernels`, `contexts/gravity_field_context`, `prepared_hdiv_mass`, `prepared_gravity_source` | 5,560 |
| Gravity force | `kernels/gravity_displacement_force_kernels`, `prepared_gravity_displacement_force` | 2,203 |
| Rotation force | `kernels/rotation_displacement_force_kernels`, `contexts/rotation_displacement_force_context`, `prepared_rotation_displacement_force`, plus `interface/physics/rigid_rotation.cppm` | 2,518 |
| Shared force contractions | `impl/operators/interior_displacement_contractions.hpp` | 172 |
Each physical file is counted once. The 4,319-line conservative baseline includes
only the eleven kernel interface/implementation and rotation-law files from that
manifest. Physical lines include comments and blanks. The secondary code-line
estimate removes C++ comments while preserving quoted strings, then counts nonblank
lines; formatting differences still affect it.
## This addition
Rotation needs **121 lines** in two new production headers (87 excluding comments
and blanks), plus a net **5 lines** in hydrostatic equilibrium to share the law and
preserve the old constructor. The operator backend is unchanged. Its old dedicated
law/context/kernel/prepared implementation occupies 2,518 lines. The small marginal
addition is the clearest evidence that the shared backend is now paying off.
Tests are separate. The new rotational tests and their reference helper occupy
682 physical lines (615 code lines). The current equation-test directory, its
shared helpers, and operator compile-time checks total 4,240 lines (3,851 code lines).
The selected related old closure/hydrostatic/gravity/rotation tests occupy 17,184
lines (12,872 code lines). These test inventories have different scope, including
old solver and optimization checks, so their difference is not presented as a
coverage-equivalent saving. Existing test cases are retained without tolerance edits.
The old implementation includes selective cache invalidation, optimized tensor
paths, MPI/root-layout plumbing, and parts of the gravity solve that the present
SERiF backend does not reproduce. Some reduction therefore comes from deferring
capabilities, as well as removing repeated assembly and derivative code. Pressure,
global constraints and the complete coupled solver are outside this estimate.
Future migration will increase SERiF's count; this is a current footprint comparison,
not a prediction of the final percentage.
The final count includes concurrent formatting edits in the equation headers. A
compile-time count extracted during those edits needed `constexpr` for its
`static_assert`; that one-keyword correction preserves the rewritten structure.

587
docs/static/operators.md vendored Normal file
View File

@@ -0,0 +1,587 @@
# Applying equations as discrete operators
An equation describes local physics and its chosen weak formulation. An operator
evaluates that description on a discretization to produce a residual `R(u)` and a
Jacobian action `J(u) du`. New physics using supported operands needs an equation,
not another bespoke operator.
The MFEM volume backend supports global scalar VALUE, scalar H1/L2 VALUE, scalar H1 GRADIENT and vector H1
VALUE/DIVERGENCE/GRADIENT operands and tests on three-dimensional conforming serial meshes.
Scalar equations, mass and angular-momentum constraints, and primal gravity use the same volume graph and numerical application functions.
Surface integrals,
H(div), H(curl), and parallel assembly remain outside the current scope.
## Where things belong
| Responsibility | Implementation |
| --- | --- |
| Physical laws, parameters, admissibility, local partial derivatives | [`equations`](../src/include/serif/equations/) |
| Existing field and term identities, spaces, supports, operands | [`discretization`](../src/include/serif/discretization/) |
| Bind a spatial term to an MFEM space or a global scalar to its mesh | [`TermBinding`](../src/include/serif/backend/mfem/discretization/term_binding.hpp) |
| Supported coefficient indices, orientation, extraction, assembly | [`DofMap`](../src/include/serif/backend/mfem/discretization/dof_map.hpp) |
| Traverse an equation's additive contributions in declaration order | [`operators/contributions.hpp`](../src/include/serif/operators/contributions.hpp) |
| Interpret operand order, row support, and quadrature forms | [`EquationPlan`](../src/include/serif/operators/plan.hpp) |
| Bind physical terms to coefficient vectors in an ordinary dependency context | [`CoefficientView`, `make_term_coefficients`](../src/include/serif/backend/mfem/discretization/term_coefficients.hpp) |
| Verify and reuse canonical tensor factors | [`TensorBasisCache`](../src/include/serif/backend/mfem/discretization/tensor_basis.hpp) |
| Contract a dense basis or tensor factors with pointwise maps | [`BasisEvaluation`](../src/include/serif/backend/mfem/discretization/basis_evaluation.hpp) |
| Evaluate a supported operation and apply its basis or transpose | [`OperandEvaluator`](../src/include/serif/backend/mfem/operators/operand_evaluation.hpp) |
| Name volume stages and describe their fixed configuration and results | [`volume_data.hpp`](../src/include/serif/backend/mfem/operators/volume_data.hpp) |
| Sample bases and operands, map geometry, and evaluate one element's equation | [`element_preparation.hpp`](../src/include/serif/backend/mfem/operators/element_preparation.hpp) |
| Apply these stages over one contribution's supported elements | [`volume_stages.hpp`](../src/include/serif/backend/mfem/operators/volume_stages.hpp) |
| Validate and describe current volume sources | [`volume_inputs.hpp`](../src/include/serif/backend/mfem/operators/volume_inputs.hpp) |
| Declare volume graph nodes and return an ordinary dependency cache | [`volume_computations.hpp`](../src/include/serif/backend/mfem/operators/volume_computations.hpp) |
| Consume accepted volume data and apply its Jacobian or transpose | [`volume_application.hpp`](../src/include/serif/backend/mfem/operators/volume_application.hpp) |
| Integrate and differentiate accepted element data | [`element_evaluation.hpp`](../src/include/serif/backend/mfem/operators/element_evaluation.hpp) |
| Compare exact MFEM coefficient values and persistent binding identities | [`CoefficientTracking`](../src/include/serif/backend/mfem/discretization/coefficient_tracking.hpp) |
| Track changes, retain accepted results, and publish successful preparations | [`utils::dependencies`](dependencies.md) |
| Expose the frozen linearization to MFEM solvers | [`JacobianOperator`](../src/include/serif/backend/mfem/operators/jacobian_operator.hpp) |
The volume path separates input construction, graph declarations,
accepted data, and numerical application. The dependency cache is their only
preparation owner. Operators consume the equation interface; concrete physics
headers do not depend on operators or MFEM. Geometry and quadrature continue to use
the existing mapping and discretization modules.
A binding identifies a **term**, not a new field catalog and not an operand operation.
Gravity's potential and gradient are different terms. Multiple operations on a term
share one coefficient vector and one packed Jacobian column. `Plan::Operands` lists
unique sampled operations; `Plan::Columns` lists unique coefficient terms.
## Reading the calculation
`make_contribution_computations` in `volume_computations.hpp` reads in numerical
order: reference bases, geometry, operand samples, then equation values. Its short
lambdas call the ordinary numerical functions in `volume_stages.hpp` and
`element_preparation.hpp`. The declarations themselves do no numerical work;
`try_prepare` runs the required nodes later.
The contribution index distinguishes repeated equations, including repeated instances
of the same type. Each node returns data for the contribution's supported elements;
there is no separate C++ graph node for every mesh point.
| Result key (`Basis`, `Geometry`, and sample keys are in `backend::volume_keys`) | Declared numerical dependencies |
| --- | --- |
| `Basis<I>` | `MeshAndCoefficientBindings`: fixed-mesh coordinate sequence and binding identities; equation and integration options are fixed configuration. |
| `Geometry<I>` | `Basis<I>`, displacement coefficients, compactification coefficients. |
| `ReferenceValues<I, Operand>` | `Basis<I>` and the coefficient source identified by `Operand::term_type`. |
| `PhysicalValues<I, Operand>` | That operand's reference values and `Geometry<I>`; present only when the operand's physical representation depends on geometry. |
| `QuadratureLinearization<I>` | Basis, geometry, and each declared operand's reference or physical values. Residuals, derivatives, weights, and contribution scaling are computed together. |
| `Linearization` | Mesh and binding metadata and every contribution's `QuadratureLinearization`; retain ordered batches, assemble the residual, and validate complete integrals. |
There are no manual change flags inside these numerical stages. The dependency
engine follows the declared inputs to decide which stages need execution. For scalar VALUE,
```text
u(q) = sum_j E(q,j) c(j)
z(q) = W(q) f(u(q), x(q))
R_element = E_test^T z
```
`element_residual` performs the last contraction. `element_jacobian_action` differentiates
the same expression. Coefficients, explicit physical position, integration weight,
and (for gradients and H1 divergence) both operand and test bases vary. Scalar and vector H1 value bases stay fixed.
The evaluator's `evaluate` method and its transpose contraction work for all supported
operations by flattening a vector sample's components into three rows per point.
`OperandValue<Operand>` describes the sampled shape using **both** term rank and
operation: `Value(u)` of a vector is `std::array<double, 3>`, while `Divergence(u)` is `double`.
`PointLinearization<Operands, Result>` supplies scalar/vector/matrix partials of the
appropriate shape; the default result remains scalar for existing equations.
There are two maps. MFEM first maps from its canonical element to the fixed
computational mesh: RT `CalcVShape(transformation, ...)` and `CalcPhysDivShape(...)`
already include this map. `OperandEvaluator` then applies SERiF's additional map
`A = dx/dX`, `j = det(A)`: `g_x = A g_X / j`, `div_x(g_x) = div_X(g_X) / j`.
The same transformation is applied to the test basis. Geometry variations include
`delta(A g_X / j)` and `delta(div_X(g_X) / j)` at fixed coefficients, on both sides
of the contraction. Cached fixed-mesh samples make these variations independent of
subsequent changes to the caller's coefficient vectors.
H1 divergence uses a different transformation: `div_x(u) = trace(G A^-1)`, where
`G[c,k] = partial_X_k(u_c)`. Its evaluator retains all nine entries of `G` internally,
then produces one scalar per physical quadrature point. A scalar reference divergence
alone would lose the information needed under shear or anisotropic dilation. The
geometry derivative is `trace(G delta(A^-1))`, using the existing mapping variation.
Consequently an evaluator's reference sample and physical value can have different
shapes. It declares both sizes, reads its own reference samples, and advertises
`geometry_dependent`. Element assembly uses those declarations without branching on
a particular finite-element family. Coefficient evaluation and test integration
still share the same matrix-vector and transpose contractions.
`prepare_volume` validates the current coefficients, constructs their dependency
context, and asks the ordinary dependency cache to prepare the graph. The final
`volume_keys::Linearization` result owns the assembled residual and each contribution's
immutable element data. Ordinary application functions consume this result; there is
no second prepared-operator lifecycle.
Residual and Jacobian calls do not evaluate the EOS or silently prepare new values.
Packed columns remain unique terms in first-appearance order (`Plan::Columns`),
followed by displacement.
## Model ownership and startup
The model owns caches directly. Fixed discretization, current coefficient inputs,
and integration choices have separate names. With bindings, coefficient vectors,
and the mapping already constructed, one volume row can be set up as follows:
```cpp
namespace dependencies = serif::utils::dependencies;
namespace backend = serif::operators::backend::mfem;
namespace equations = serif::equations;
const auto closure_equation = equations::BarotropicDensityBalance{equation_of_state};
using Closure = std::remove_cvref_t<decltype(closure_equation)>;
const backend::VolumeDiscretization closure_discretization{
.residual_binding = density_binding,
.displacement_binding = displacement_binding,
.compactification = compactification,
.mapper = mapper
};
const auto term_coefficients = backend::make_term_coefficients(
backend::bind(density_binding, density),
backend::bind(potential_binding, potential),
backend::bind(surface_level_binding, surface_level),
backend::bind(angular_velocity_binding, angular_velocity)
);
const auto closure_inputs = backend::make_volume_inputs<Closure>(
closure_discretization, term_coefficients, displacement
);
auto closure_cache = backend::make_volume_equation_cache(
closure_equation, closure_discretization, closure_inputs
);
auto& closure = closure_cache;
// Repeat preparation with the current coefficients during nonlinear iterations.
backend::prepare_volume(closure, closure_discretization, term_coefficients, displacement);
const auto& linearization = closure.get<backend::volume_keys::Linearization>();
backend::volume_residual(linearization, density_residual);
backend::apply_jacobian(linearization, direction, displacement_direction, workspace, action);
backend::JacobianOperator jacobian(closure);
```
The snippets use `<type_traits>` for `std::remove_cvref_t` and `<utility>` for
`std::move`. The [equation sandbox](../../tests/sandbox/equations_sandbox.cpp) supplies
the complete manufactured equilibrium.
`VolumeOptions` optionally supplies named `quadrature_policy` and `contraction_mode`
members as the factory's fourth argument.
Cache construction declares work without evaluating physics. The graph owns the
equation, integration options, and reusable preparation scratch. Meshes, spaces,
term bindings, compactification, and mapper are borrowed and must outlive its uses.
Coefficient contexts own views, not the coefficient vectors. Keep those vectors
alive while preparing; accepted numerical results do not borrow their contents.
The workspace used for Jacobian application remains caller-owned and is not a
tracked dependency.
`make_volume_inputs` captures current mesh/binding metadata and borrows current
coefficients. Rebuild this context when the discretization changes. Calling
`prepare_volume` or `try_prepare_volume` does that explicitly on every call and
validates before requesting the complete linearization. Calling `cache.try_prepare`
directly is also available with a current, validated context; it is the generic
engine's interface and permits partial result requests.
Moving a cache after an adapter borrows it invalidates that reference. The cache must outlive
its adapter. The adapter reads the cache's latest accepted linearization on every
call, so rejection or a request that omits that linearization makes application
unavailable. References returned by `cache.get` must be reacquired after preparation
or invalidation.
Two adapters can share one cache, while separate caches retain independent
linearizations. Separate caches are not connected or published as one transaction; a
controller prepares them explicitly. Shared intermediates can be declared in a common
graph when that becomes useful.
## Equation contributions
An equation may describe itself as an additive tuple returned by `contributions()`; the
primal Poisson balance does so for its flux and source. Contributions share the same
residual term, while leaves retain independent test operations, supports, and
quadrature rules. `ContributionsOf` collects leaf types for concept checks and operand
planning. `operators::for_each_contribution` visits the corresponding leaf objects in
declaration order, borrowing the owned objects, including equations that cannot be copied.
## Global scalars: mass, angular momentum, C, and Ω
`barotropic_constant::mass_normalization` stores the unknown Bernoulli constant C and
receives the mass constraint. `rotation::angular_velocity` stores the signed angular
speed Ω about a prescribed axis and center. They are explicitly declared
`GlobalScalarTerm`s, with one coefficient each and no finite-element space. Other
nonspatial descriptors, such as surface-deformation coefficients, are not implicitly
admitted as scalars.
```cpp
using C = serif::discretization::blocks::barotropic_constant::mass_normalization;
using Omega = serif::discretization::blocks::rotation::angular_velocity;
using serif::discretization::backend::mfem::TermBinding;
namespace backend = serif::operators::backend::mfem;
TermBinding<C, Schema> c_binding(mesh);
TermBinding<Omega, Schema> omega_binding(mesh);
mfem::Vector c(1), omega(1);
c = surface_level;
omega = angular_velocity;
const auto term_coefficients = backend::make_term_coefficients(
backend::bind(density_binding, density),
backend::bind(potential_binding, potential),
backend::bind(c_binding, c),
backend::bind(omega_binding, omega)
);
const backend::VolumeDiscretization mass_discretization{
.residual_binding = c_binding,
.displacement_binding = displacement_binding,
.compactification = compactification,
.mapper = mapper
};
const auto mass_inputs = backend::make_volume_inputs<serif::equations::MassNormalization>(
mass_discretization, term_coefficients, displacement
);
auto mass = backend::make_volume_equation_cache(
serif::equations::MassNormalization{target_mass}, mass_discretization, mass_inputs
);
backend::prepare_volume(mass, mass_discretization, term_coefficients, displacement);
const backend::VolumeDiscretization angular_momentum_discretization{
.residual_binding = omega_binding,
.displacement_binding = displacement_binding,
.compactification = compactification,
.mapper = mapper
};
const auto angular_momentum_inputs = backend::make_volume_inputs<serif::equations::AngularMomentum>(
angular_momentum_discretization, term_coefficients, displacement
);
auto angular_momentum = backend::make_volume_equation_cache(
serif::equations::AngularMomentum{target_momentum, axis, center},
angular_momentum_discretization, angular_momentum_inputs
);
backend::prepare_volume(angular_momentum, angular_momentum_discretization, term_coefficients, displacement);
```
The scalar basis is one at every quadrature point. The same basis contractions,
geometry derivatives, coefficient validation, frozen snapshots, and packed adapter
serve spatial and global terms. A scalar row sums every supported element into its
single entry; there is no artificial order-zero FE space or per-element scalar DOF.
Global scalar reductions compensate cancellation at quadrature points and across
elements, preserving the old column-sum accuracy. Its mesh, support, and lifetime
checks still apply. Preparation also rejects overflow in the integrated scalar
residual, including its offset, before publishing the candidate. Calling `.space()` on a global
binding is an error; common assembly uses `.mesh()` and `.full_size()`.
`MassNormalization` evaluates rho over the stellar domains. Its `residual_offset()`
returns `-target_mass`, which final volume assembly adds **once after integration**.
This optional hook is restricted to global scalar rows and describes a constant,
state-independent offset. Arithmetic scales the offset along with its contribution;
the offset has no density or geometry derivative. The mass row depends on density
and displacement, not on C directly. C influences the density-balance row.
[`AngularMomentum`](../src/include/serif/equations/angular_momentum.hpp) receives the
Ω row and evaluates `rho Omega s^2`, where `s` is distance from the prescribed
physical axis through `center`. Its offset is `-target_momentum`. The target must be
finite and nonnegative; trial angular speeds may be signed or zero. It reuses
`RigidRotation` for `s^2 = 2 Psi_unit` and its position derivative. The equation
supplies `dF/drho = Omega s^2`, `dF/dOmega = rho s^2`, and
`dF/dx = 2 rho Omega grad(Psi_unit)`; the existing backend differentiates volume.
An equation may also define `validation_integrand(point, linearization)` and
`validate_integral(value)`. During the existing quadrature loop the backend sums
that scalar measure with the physical integration weight, then validates its total
over the contribution's support before publishing the preparation. Throwing
`std::domain_error` rejects the trial state. Each leaf is validated separately. Angular momentum uses its already computed
`dF/dOmega` to reject a nonfinite or negative total moment of inertia, including
at Ω = 0; it does not require every local trial density to be positive.
The density-balance constructor accepts an axis (default z) and center (default zero),
not C or Ω. The axis is normalized once. The balance reads Ω and C from the state.
It implements `d(A(rho) + Phi - Psi - C)/dC = -1` and `dPsi/dOmega = 2 Omega Psi_unit`.
These derivatives are defined at Ω = 0 without dividing by Ω. `RigidRotation` remains a
fixed-vector utility for independent calculations.
Packed density-balance columns are `rho, Phi, C, Omega, displacement`. Mass columns are
`density, displacement`; angular-momentum columns are `density, Omega, displacement`.
Each global scalar column occupies one entry. Directions
must bind the same scalar descriptors, including an explicit zero when a control is
held fixed. Reprepare after changing its value, as with spatial coefficients.
The mass port retains the old `1e-12` integral comparison, `5e-7` affine scaling,
`3e-8` density finite difference at step `1e-3`, `2e-7` geometry finite difference at
step `1e-6`, `2e-11` independent complete action, and `2e-15` column sum.
Serial transpose actions and preparation work reports are available. MPI reductions
and coupled root-layout routing remain deferred.
The angular-momentum port retains the old `2e-13` moment comparison, `7e-7`
fifth-power dilation law, `3e-13` parallel-axis identity, `2e-11` independent action,
`3e-15` column sum, and `2e-15` angular-speed action comparison. Density, geometry,
and angular-speed differences retain thresholds `4e-8`, `4e-7`, and `2e-10` at
steps `1e-3`, `1e-6`, and `1e-6`. Tests exercise tilted and displaced axes, both
element types and vector orderings, a curved star, frozen snapshots, rejection and
recovery, and default quadrature. Serial cache behavior is checked through the shared preparation reports. Distributed/root-layout
tests await their corresponding infrastructure.
The supported physical part of the old rotating-system test also checks one shared
Ω across density-balance and invariant rows (`3e-7` and `3e-10` at step `1e-5`),
its zero columns in mass, the test closure, and gravity, and the different
zero-speed limits. Seed generation, surface projection, and root manifests are not
part of this volume-row port.
## Prescribed surface pressure
[`Isobaric`](../src/include/serif/geometry/surface/isobaric.hpp) prescribes a typed
`PressureValue`. It describes the physical condition without selecting enthalpy or
any other field. The model's surface compiler selects a formulation from the
available thermodynamic fields, solved equation rows, and EOS relations:
1. Use a pressure unknown directly when one is available.
2. Otherwise use a declared inverse EOS relation to resolve a boundary field.
3. Otherwise evaluate a declared forward pressure relation and subtract the target.
An unavailable or ambiguous choice is rejected at compile time. The compiler uses
the EOS's declared relations; it does not numerically invert an EOS. A preferred
surface field can narrow the choice when the physical model admits several rows.
See [`AutomaticPressureSurface`](../src/include/serif/geometry/surface/automatic.hpp).
For a polytrope using density, the selected condition reduces to
```text
R_i = rho_i - rho_EOS(P_surface)
delta R_i = delta rho_i
```
Other EOS relations may depend on temperature, composition, or other bound fields.
Their values and derivatives are evaluated from the current boundary state. Zero
pressure also goes through the selected relation; its carrier value need not be zero.
A pressure unknown needs no EOS evaluation at all.
The finite-element backend compiles traces of the required fields at the selected
carrier nodes on `StellarSurfaceBoundary`. These fields may have different spaces
and polynomial orders. The scalar carrier must have a supported nodal H1 row;
Bernstein/positive coefficients are rejected because they are not point values.
For standalone operator work, the shared boundary graph can be used directly.
Here `Formulation` is the selected formulation, `eos_source` explicitly tracks the
EOS parameters, and `coefficients` binds the fields required by that formulation:
```cpp
namespace physics = serif::geometry::surface;
namespace surface = physics::backend::mfem;
namespace dependencies = serif::utils::dependencies;
const auto boundary = surface::make_pressure_boundary<Formulation>(coefficients);
const auto inputs = surface::make_pressure_boundary_inputs(
boundary, eos_source,
physics::Isobaric{serif::dimensions::PressureValue{0.03125}}, coefficients
);
auto cache = dependencies::make_computation_cache<physics::SurfaceFailure>(
surface::make_pressure_boundary_graph<Formulation>(), inputs
);
const auto prepared = cache.try_prepare(inputs);
if (prepared) {
const auto& linearization = cache.get<physics::keys::Linearization>();
// First assemble the volume residual and Jacobian action into these vectors.
surface::apply_residual_rows(*boundary, linearization, residual);
surface::apply_jacobian_rows(*boundary, linearization, direction, action);
}
// Handle prepared.error() before using a result from a rejected trial.
```
The graph tracks pressure, EOS parameters, and field coefficients. Preparation
owns the evaluated residuals and derivatives until the next preparation, so later
input changes do not silently change an already prepared action. Invalid numerical
states return a `SurfaceFailure`; invalid or stale bindings remain programming
errors. Both `coefficients` and `direction` must use the bindings that compiled the
traces, and those bindings and borrowed values must remain alive.
Apply boundary replacement **after** volume assembly. It removes every volume
coupling at a selected row and inserts every derivative required by the chosen
surface relation. For the transpose, mask selected residual-dual entries before
applying the volume transpose, then add the boundary transpose to each affected
field. Interior rows remain untouched. There is no surface measure or quadrature
weight in this strong condition. The model's assembled operator performs this
composition; a standalone volume operator alone does not.
The pressure regressions retain the MeanField residual/action comparisons at
`1e-13`, the analytic closure/density-balance thresholds of `2e-12`, and the mass
threshold of `2e-11 * target_mass`. The constrained density-balance finite difference
retains step `1e-5` and threshold `2e-6`. Analytic boundary values and independent
MFEM boundary masks check the shared graph on tetrahedra, hexahedra, both vector
orderings, and a curved star. General surface tests also cover non-enthalpy EOS
relations, state-dependent couplings, transpose actions, cache invalidation,
rejection, and recovery.
## Selective serial preparation
Preparation defaults to `PreparationMode::reuse_unchanged`, matching the generic
dependency engine. Request a full refresh explicitly with `PreparationMode::rebuild`:
```cpp
backend::prepare_volume(closure, closure_discretization, term_coefficients, displacement);
const auto report = backend::volume_work_report(closure);
// report.geometry_points, sampled_operands, physics_points and reused_elements
backend::prepare_volume(
closure, closure_discretization, term_coefficients, displacement,
backend::PreparationMode::rebuild
);
```
The dependency engine owns exact coefficient snapshots and accepted results.
`make_volume_inputs` supplies `MeshAndCoefficientBindings`, displacement,
compactification, and the equation plan's coefficient terms as source roles.
`CoefficientTracking` identifies each source by its persistent binding and compares
coefficient contents. Temporary context wrappers and copied equal vectors therefore
retain valid work. A changed operand refreshes its samples and the physics of
contributions using that operand. Other contributions retain their accepted data.
Displacement or compactification changes refresh mapping and mapped bases while
retaining unaffected fixed-mesh coefficient samples. A fixed-mesh coordinate change
rebuilds those bases too. No coefficient hashes or caller-maintained coefficient
revision counters are required.
Reuse assumes fixed equation configuration and any external data it references.
Request `PreparationMode::rebuild`, or invalidate the cache, after modifying external
EOS data; changes hidden behind a runtime EOS view cannot be discovered automatically.
Changing owned equation parameters requires a new graph. `preparation_count()` counts
successful generic cache preparations, including reuse and partial requests. Failed
attempts do not increment it; invalidation preserves the count. Numerical work counts
are derived from the most recent graph report, rather than separately stored state.
Inspect any cache directly through the dependency API:
```cpp
namespace stages = backend::volume_keys;
const auto& graph_report = closure.report();
const auto basis_status = graph_report.status<stages::Basis<0>>();
const auto geometry_status = graph_report.status<stages::Geometry<0>>();
const auto physics_status = graph_report.status<stages::QuadratureLinearization<0>>();
```
Statuses are `computed`, `reused`, or `not_required`. The numerical work report
summarizes these statuses in element and quadrature counts. Volume helpers request
the whole graph; `volume_keys::Linearization` retains accepted contributions and the
assembled residual together. Direct dependency requests can prepare selected
intermediates, but application requires the final linearization to be available.
Rejection makes current cache results unavailable. The generic engine keeps its last
accepted storage for possible later reuse; backend preparation helpers additionally
invalidate that storage on validation or numerical failure, ensuring a fresh retry.
Neither path publishes a partly prepared result. Reacquire accepted data after every
successful preparation rather than retaining a reference across cache updates.
Reuse depends only on unchanged tracked inputs. A recomputed node advances its
version even when it happens to produce the same value, so required downstream
computations also refresh. There is no output-equivalence test. Coefficient lookup
uses the ordinary dependency context; operand-list deduplication uses `CombinedKeyList`.
## Tensor contractions and complete transposes
On eligible H1/L2 and RT hexahedra, the backend verifies Cartesian quadrature ordering,
native signed DOF maps, and the factorization against MFEM basis values and derivatives.
It then performs successive one-dimensional contractions. H1 gradients and RT divergence
use the same factors and transpose routine. Owned tables are shared within one volume graph
and keyed by finite-element identity, operation and complete quadrature contents.
Temporary or modified rules cannot leave borrowed table data behind.
Small pointwise maps apply MFEM's canonical-to-fixed-mesh transformation and SERiF's
additional physical transformation. Tetrahedra, non-Cartesian rules and unverified
factorizations keep the dense path. Neither path changes the quadrature rule. Supply
`VolumeOptions{.contraction_mode = ContractionMode::dense}` to the cache factory
to use the dense reference path explicitly. `tensor_element_count(linearization)`
reports how many accepted contribution-elements use tensor contractions, including geometry.
`apply_jacobian_transpose(linearization, residual_dual, workspace, packed_result)` returns all
coefficient duals in `Plan::Columns` order followed by displacement. `JacobianOperator`
also implements MFEM's `MultTranspose`. The transpose includes weight, explicit
position, operand mapping, and test mapping derivatives. Geometry is differentiated
through twelve local inputs: three displacement values and nine canonical derivatives.
The mapping module supplies their exact directional variations; no finite differences
or global Jacobian-column probing are used in production.
A strong nodal replacement requires the following transpose order:
```cpp
backend::mask_residual_dual(boundary, residual_dual, volume_dual);
backend::apply_jacobian_transpose(volume, volume_dual, workspace, packed_dual);
// Obtain the constrained term's block from packed_dual using the volume plan's column order.
backend::add_jacobian_transpose_rows(boundary, residual_dual, constrained_term_dual);
```
Mask residual rows before the volume transpose, then add the constraint transpose to
the appropriate coefficient block. Zeroing coefficient entries after the volume
transpose would incorrectly remove contributions from unconstrained residual rows.
For centering, the constrained coefficient block is the displacement geometry block.
These remain serial coordinate transposes; no mass-matrix identification of duals or
MPI true-DOF ownership is implicit.
Tensor tests retain the
old scalar `2e-12` and gradient/RT `2e-11` comparisons. Full equation transposes are
checked against dense contractions, individual Jacobian columns, the old mass-adapter
`2e-12` pairing threshold, both element types/orderings, and curved compactified geometry. Existing tests and numerical tolerances are unchanged.
## Restrictions and what depends on them
| Boundary | Enforcement | Why the numerical code needs it |
| --- | --- | --- |
| Authoring interface | `equations::Equation` | Physics supplies the declared pointwise input/output contract. This does not promise execution support. |
| Current volume algorithm | `operators::VolumeEquation` | Every contribution needs a volume-domain support and implemented test/input operations. `ScalarVolumeEquation` remains available for existing callers. |
| Requested operand evaluation | `OperandEvaluator<Operand>` specialization; `EvaluableOperand` queries its interface | There is no generic fallback that ignores the operation tag. An unsupported operation cannot silently become VALUE sampling. |
| Actual MFEM element used for sampling | Always-active range/map checks in `sample_basis`; gradients and H1 divergence additionally require a transformation | Only scalar VALUE bases (one or three copies) are sampled. |
| Actual space bound to a term | `TermBinding`: family/rank compile-time checks; dimension, continuity, order, range, mapping runtime checks | Coefficient layout and basis representation must match the declared term. A valid binding still needs a supported operation; vector H1 VALUE and DIVERGENCE are implemented; GRADIENT and CURL remain unsupported. |
| Supported DOF mapping | `DofMap` checks serial conforming 3D spaces, sizes, support, orientation, and revisions | Extraction and assembly assume this representation. |
| Selected boundary rows | `select_boundary_dofs` and `DofSelection::validate` | Boundary tags must exist in the schema, have supported DOFs, and remain unchanged during use. |
| Cross-binding consistency | `make_volume_inputs` and the basis stage check schema, mesh, binding identities, and input support | Every operand must cover its contribution's integration support, which may be narrower than the row. No operand is silently extended into vacuum. |
| Inadmissible geometry or physics | Element preparation returns `PreparationRejection` with element and point; optional integral validation rejects at contribution level | A nonlinear trial state can be rejected without publishing incomplete data. Integral rejections have no single element/point. |
| Missing or stale preparation | Dependency result availability and `validate_volume_data` | Cached matrices and geometry must match the borrowed discretization. |
Malformed bindings and coefficient sizes are programming errors. Numerical trial-state
rejections are values returned from `try_prepare_volume`. The corresponding `prepare_*` convenience functions
translate a rejection for the caller. Physical admissibility
remains the equation/EOS author's responsibility.
## Extending the implementation later
For new physics expressible using supported operands, change only the equation and
model selection. Keep local physical partial derivatives in the equation; mesh and
mapping derivatives belong to numerical execution.
For a new space representation, start with `TermBinding` and the existing space
descriptors. For a new operation, start with `OperandEvaluator`: its result shape,
basis evaluation, coefficient action, test contraction, and geometry dependence must
agree. Then examine the element residual and its Jacobian together. In particular,
the existing gradient and H1 divergence implementations show why both operand and test
variations are needed. A new operation must provide these mathematics before its capability
check can admit it.
The volume admission contract should expand only after those calculations,
point/cache shapes, and quadrature handling exist. Unsupported operations remain
compile-time failures. There is no separate hand-maintained capability registry.
Compile-time checks in [`operators/plan.cpp`](../src/compile_time_checks/serif/operators/plan.cpp)
cover supported scalar, gradient and H1 divergence operations, sampled result shapes, packed columns,
and rejection of unsupported operations. Authoring an equation remains distinct from
executing it with this backend.
The existing equation tests retain independent quadrature comparisons, analytic
dilation checks, centered differences, ownership checks, and their original tolerances.
## Gravity without a separate assembly path
[`primal_gravity.hpp`](../src/include/serif/equations/primal_gravity.hpp) returns its weak
Poisson residual as a tuple of contributions: the potential-gradient flux on all domains and
the density source on `StellarDomains`. `Contribution<TestOperand, Support>` is an optional
authoring helper; each contribution supplies `Operands`, `evaluate`, and `additional_order`.
All contributions share the row term, but may select different test operations and supports.
Existing single-contribution equations need no wrapper. The source's `StellarDomains` support
prevents reading density on vacuum elements. This is volume-operator support, not a new
boundary-condition API.
## API migration
| Previous name | Current name |
| --- | --- |
| `equations::backend::mfem::EquationField` | `discretization::backend::mfem::TermBinding` |
| `equations::backend::mfem::FieldDofs` | `discretization::backend::mfem::DofMap` |
| `equations::backend::mfem::FieldState` | `operators::backend::mfem::CoefficientView` |
| `EquationState`, `State` | `make_term_coefficients(bind(...), ...)` returns a dependency context |
| `PreparedEquation`, `PreparedOperator`, `VolumeComputations` | `make_volume_equation_cache`, `prepare_volume`, and free application functions |
| `equations::EquationPlan` | `operators::EquationPlan` |
| `EQWorkspace`, `EQElementData` | `operators::backend::mfem::Workspace`, `ElementData` |
`bind(binding, coefficients)` now produces a view with `.binding` and `.values`.
Pointwise equation inputs and physical derivatives retain their existing interface.

3
docs/static/pressure-force.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# Pressure displacement force
This document described the removed volume displacement-force formulation; the equation no longer exists.

3
docs/static/rotational-force.md vendored Normal file
View File

@@ -0,0 +1,3 @@
# Rotational displacement force
This document described the removed volume displacement-force formulation; the equation no longer exists.