docs(mainpage): updated examples in documentation
This commit is contained in:
464
docs/static/mainpage.md
vendored
464
docs/static/mainpage.md
vendored
@@ -1,5 +1,67 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/4D-STAR/GridFire/blob/main/assets/logo/GridFire.png?raw=true" width="300" alt="OPAT Core Libraries Logo">
|
||||||
|
</p>
|
||||||
|
|
||||||

|
---
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
'&style=for-the-badge&label=GitHub%20Main%20Branch)
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [Design Philosophy and Workflow](#design-philosophy-and-workflow)
|
||||||
|
- [Funding](#funding)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Python installation](#python-installation)
|
||||||
|
- [pypi](#pypi)
|
||||||
|
- [source](#source)
|
||||||
|
- [source for developers](#source-for-developers)
|
||||||
|
- [patching shared object files](#patching-shared-object-files)
|
||||||
|
- [Automatic Build and Installation](#automatic-build-and-installation)
|
||||||
|
- [Script Build and Installation Instructions](#script-build-and-installation-instructions)
|
||||||
|
- [Currently, known good platforms](#currently-known-good-platforms)
|
||||||
|
- [Manual Build Instructions](#manual-build-instructions)
|
||||||
|
- [Prerequisites](#prerequisites)
|
||||||
|
- [Install Scripts](#install-scripts)
|
||||||
|
- [Dependency Installation on Common Platforms](#dependency-installation-on-common-platforms)
|
||||||
|
- [Building the C++ Library](#building-the-c-library)
|
||||||
|
- [Installing the Library](#installing-the-library)
|
||||||
|
- [Minimum compiler versions](#minimum-compiler-versions)
|
||||||
|
- [Code Architecture and Logical Flow](#code-architecture-and-logical-flow)
|
||||||
|
- [Engines](#engines)
|
||||||
|
- [GraphEngine](#graphengine)
|
||||||
|
- [GraphEngine Configuration Options](#graphengine-configuration-options)
|
||||||
|
- [Available Partition Functions](#available-partition-functions)
|
||||||
|
- [AutoDiff](#autodiff)
|
||||||
|
- [Reaclib in GridFire](#reaclib-in-gridfire)
|
||||||
|
- [Engine Views](#engine-views)
|
||||||
|
- [A Note about composability](#a-note-about-composability)
|
||||||
|
- [Numerical Solver Strategies](#numerical-solver-strategies)
|
||||||
|
- [NetworkSolverStrategy<EngineT>](#networksolverstrategyenginet)
|
||||||
|
- [NetIn and NetOut](#netin-and-netout)
|
||||||
|
- [DirectNetworkSolver (Implicit Rosenbrock Method)](#directnetworksolver-implicit-rosenbrock-method)
|
||||||
|
- [Algorithmic Workflow in DirectNetworkSolver](#algorithmic-workflow-in-directnetworksolver)
|
||||||
|
- [Future Solver Implementations](#future-solver-implementations)
|
||||||
|
- [Python Extensibility](#python-extensibility)
|
||||||
|
- [Usage Examples](#usage-examples)
|
||||||
|
- [C++](#c)
|
||||||
|
- [GraphEngine Initialization](#graphengine-initialization)
|
||||||
|
- [Adaptive Network View](#adaptive-network-view)
|
||||||
|
- [Composition Initialization](#composition-initialization)
|
||||||
|
- [Common Workflow Example](#common-workflow-example)
|
||||||
|
- [Callback Example](#callback-example)
|
||||||
|
- [Python](#python)
|
||||||
|
- [Common Workflow Example](#common-workflow-example-1)
|
||||||
|
- [Python callbacks](#python-callbacks)
|
||||||
|
- [Related Projects](#related-projects)
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
GridFire is a C++ library designed to perform general nuclear network
|
GridFire is a C++ library designed to perform general nuclear network
|
||||||
@@ -7,10 +69,10 @@ evolution. It is part of the larger SERiF project within the 4D-STAR
|
|||||||
collaboration. GridFire is primarily focused on modeling the most relevant
|
collaboration. GridFire is primarily focused on modeling the most relevant
|
||||||
burning stages for stellar evolution modeling. Currently, there is limited
|
burning stages for stellar evolution modeling. Currently, there is limited
|
||||||
support for inverse reactions. Therefore, GridFire has a limited set of tools
|
support for inverse reactions. Therefore, GridFire has a limited set of tools
|
||||||
to evolves a fusing plasma in NSE; however, this is not the primary focus of
|
to evolve a fusing plasma in NSE; however, this is not the primary focus of
|
||||||
the library and has therefor not had significant development. For those
|
the library and has therefore not had significant development. For those
|
||||||
interested in modeling super nova, neutron star mergers, or other high-energy
|
interested in modeling super nova, neutron star mergers, or other high-energy
|
||||||
astrophysical phenomena, we **strongly** recomment using
|
astrophysical phenomena, we **strongly** recommend using
|
||||||
[SkyNet](https://bitbucket.org/jlippuner/skynet/src/master/).
|
[SkyNet](https://bitbucket.org/jlippuner/skynet/src/master/).
|
||||||
|
|
||||||
## Design Philosophy and Workflow
|
## Design Philosophy and Workflow
|
||||||
@@ -45,23 +107,26 @@ By far the easiest way to install is with pip. This will install either
|
|||||||
pre-compiled wheels or, if your system has not had a wheel compiled for it, it
|
pre-compiled wheels or, if your system has not had a wheel compiled for it, it
|
||||||
will try to build locally (this may take **a long time**). The python bindings
|
will try to build locally (this may take **a long time**). The python bindings
|
||||||
are just that and should maintain nearly the same speed as the C++ code. End
|
are just that and should maintain nearly the same speed as the C++ code. End
|
||||||
users are strongly encourages to use the python module rather than the C++ code.
|
users are strongly encouraged to use the python module rather than the C++ code.
|
||||||
|
|
||||||
### pypi
|
### pypi
|
||||||
Installing from pip is as simple as
|
Installing from pip is as simple as.
|
||||||
```bash
|
```bash
|
||||||
pip install gridfire
|
pip install gridfire
|
||||||
```
|
```
|
||||||
|
|
||||||
These wheels have been compiled on many systems
|
These wheels have been compiled on many systems
|
||||||
|
|
||||||
| Version | Platform | Architecture | CPython Versions | PyPy Versions |
|
| Version | Platform | Architecture | CPython Versions | PyPy Versions |
|
||||||
|---------|----------|--------------|------------------------------------------------------------|---------------|
|
|-----------|----------|--------------|------------------------------------------------------------|---------------|
|
||||||
| 0.5.0 | macOS | arm64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
| 0.7.0_rc1 | macOS | arm64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
||||||
| 0.5.0 | Linux | aarch64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
| 0.7.0_rc1 | Linux | aarch64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
||||||
| 0.5.0 | Linux | x86\_64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
| 0.7.0_rc1 | Linux | x86\_64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
||||||
|
| 0.5.0 | macOS | arm64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
||||||
|
| 0.5.0 | Linux | aarch64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
||||||
|
| 0.5.0 | Linux | x86\_64 | 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 (std & t), 3.14 (std & t) | 3.10, 3.11 |
|
||||||
|
|
||||||
> **Note**: Currently macOS x86\_64 does **not** have a precompiled wheel. Do
|
> **Note**: Currently macOS x86\_64 does **not** have a precompiled wheel. Due
|
||||||
> to that platform being phased out it is likely that there will never be
|
> to that platform being phased out it is likely that there will never be
|
||||||
> precompiled wheels or releases for it.
|
> precompiled wheels or releases for it.
|
||||||
|
|
||||||
@@ -89,6 +154,8 @@ pip install .
|
|||||||
> **Note:** that if you do not have all system dependencies installed this will
|
> **Note:** that if you do not have all system dependencies installed this will
|
||||||
> fail, the steps in further sections address these in more detail.
|
> fail, the steps in further sections address these in more detail.
|
||||||
|
|
||||||
|
> **Note:** If you are using macos you should use the included `pip_install_mac_patch.sh` script instead of `pip install .` as this will automatically patch the build shared object libraries such that they can be loaded by the macos dynamic loader.
|
||||||
|
|
||||||
### source for developers
|
### source for developers
|
||||||
If you are a developer and would like an editable and incremental python
|
If you are a developer and would like an editable and incremental python
|
||||||
install `meson-python` makes this very easy
|
install `meson-python` makes this very easy
|
||||||
@@ -105,6 +172,31 @@ of these it does still take a few seconds to recompile regardless of how small
|
|||||||
a source code change you have made). It is **strongly** recommended that
|
a source code change you have made). It is **strongly** recommended that
|
||||||
developers use this approach and end users *do not*.
|
developers use this approach and end users *do not*.
|
||||||
|
|
||||||
|
#### Patching Shared Object Files
|
||||||
|
If you need to patch shared object files generated by meson-python directly you should first locate the shared object file
|
||||||
|
these will be in the site-packages and site-packages/fourdst directories for your python environment.
|
||||||
|
|
||||||
|
Look for files named
|
||||||
|
|
||||||
|
- `site-packages/gridfire.cpython-3*-darwin.so`
|
||||||
|
- `site-packages/fourdst/_phys.cpython-3*-darwin.so`
|
||||||
|
|
||||||
|
then, for each of these files, run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
otool -l <Path/to/file> | grep RPATH -A2
|
||||||
|
```
|
||||||
|
|
||||||
|
count the number of occurrences of duplicate RPATH entries (these should look like `@loaderpath/.gridfire.mesonpy.libs` or `@loaderpath/../.fourdst.mesonpy.libs`). Then use `install_name_tool` to remove **all but one of these** from each shared object file.
|
||||||
|
|
||||||
|
If for example there are 4 occurrences of the path `@loader_path/../.fourdst.mesonpy.libs` in `_phys.cpython-3*-darwin.so` then you should run the following command 3 times
|
||||||
|
```bash
|
||||||
|
install_name_tool -delete_rpath @loader_path/../.fourdst.mesonpy.libs site-packages/fourdst/_phys.cpython-314-darwin.so
|
||||||
|
```
|
||||||
|
|
||||||
|
the same for the other shared object file (make sure to count the duplicate rpath entries for each separately as there may be a different number of duplicates in each shared object file).
|
||||||
|
|
||||||
|
We also include a script at `pip_install_mac_patch.sh` which will do this automatically for you.
|
||||||
|
|
||||||
## Automatic Build and Installation
|
## Automatic Build and Installation
|
||||||
### Script Build and Installation Instructions
|
### Script Build and Installation Instructions
|
||||||
@@ -290,8 +382,13 @@ include:
|
|||||||
2000](https://www.sciencedirect.com/science/article/pii/S0092640X00908349?via%3Dihub]))
|
2000](https://www.sciencedirect.com/science/article/pii/S0092640X00908349?via%3Dihub]))
|
||||||
to weight reaction rates based on nuclear properties.
|
to weight reaction rates based on nuclear properties.
|
||||||
- **Solver Module:** Defines numerical integration strategies (e.g.,
|
- **Solver Module:** Defines numerical integration strategies (e.g.,
|
||||||
`DirectNetworkSolver`) for solving the stiff ODE systems arising from reaction
|
`CVODESolverStrategy`) for solving the stiff ODE systems arising from reaction
|
||||||
networks.
|
networks.
|
||||||
|
- **io Module:** Defines shared interface for parsing network data from files
|
||||||
|
- **trigger Module:** Defines interface for complex trigger logic so that repartitioning can be followed.
|
||||||
|
- **Policy Module:** Contains "policies" which are small modular units of code that enforce certain contracts.
|
||||||
|
For example the `ProtonProtonReactionChainPolicy` enforces than an engine must include at least all the reactions
|
||||||
|
in the proton-proton chain. This module exposes the primary construction interface for users. I.e. select a policy (such as `MainSequencePolicy`), provide a composition, and get back an engine which satisfies that policy.
|
||||||
- **Python Interface:** Exposes *almost* all C++ functionality to Python,
|
- **Python Interface:** Exposes *almost* all C++ functionality to Python,
|
||||||
allowing users to define compositions, configure engines, and run simulations
|
allowing users to define compositions, configure engines, and run simulations
|
||||||
directly from Python scripts.
|
directly from Python scripts.
|
||||||
@@ -308,7 +405,7 @@ abundances and diagnostics.
|
|||||||
## Engines
|
## Engines
|
||||||
GridFire is, at its core, based on a series of `Engines`. These are constructs
|
GridFire is, at its core, based on a series of `Engines`. These are constructs
|
||||||
which know how to report information on series of ODEs which need to be solved
|
which know how to report information on series of ODEs which need to be solved
|
||||||
to evolver abundances. The important thing to understand about `Engines` is
|
to evolve abundances. The important thing to understand about `Engines` is
|
||||||
that they contain all the detailed physics GridFire uses. For example a
|
that they contain all the detailed physics GridFire uses. For example a
|
||||||
`Solver` takes an `Engine` but does not compute physics itself. Rather, it asks
|
`Solver` takes an `Engine` but does not compute physics itself. Rather, it asks
|
||||||
the `Engine` for stuff like the jacobian matrix, stoichiometry, nuclear energy
|
the `Engine` for stuff like the jacobian matrix, stoichiometry, nuclear energy
|
||||||
@@ -343,6 +440,7 @@ construction and rate evaluations:
|
|||||||
member of the `NetworkBuildDepth` enum or an integer.
|
member of the `NetworkBuildDepth` enum or an integer.
|
||||||
- `partition::PartitionFunction`: Partition function used when evaluating
|
- `partition::PartitionFunction`: Partition function used when evaluating
|
||||||
detailed balance for inverse rates.
|
detailed balance for inverse rates.
|
||||||
|
- `NetworkConstructionFlags`: A bitwise flag telling the network how to construct itself. That is, what reaction types should be used in construction. For example one might use `NetworkConstructionFlags::STRONG | NetworkConstructionFlags::BETA_PLUS` to use all strong reactions and β+ decay. By Default this is set to use reaclib strong and reaclib weak (no WRL included by default due to current pathological stiffness issues).
|
||||||
|
|
||||||
- **setPrecomputation(bool precompute):**
|
- **setPrecomputation(bool precompute):**
|
||||||
- Enable/disable caching of reaction rates and stoichiometric data at initialization.
|
- Enable/disable caching of reaction rates and stoichiometric data at initialization.
|
||||||
@@ -445,7 +543,7 @@ A `NetIn` struct contains
|
|||||||
- The composition to start the timestep at. (`NetIn::composition`)
|
- The composition to start the timestep at. (`NetIn::composition`)
|
||||||
- The temperature in Kelvin (`NetIn::temperature`)
|
- The temperature in Kelvin (`NetIn::temperature`)
|
||||||
- The density in g/cm^3 (`NetIn::density`)
|
- The density in g/cm^3 (`NetIn::density`)
|
||||||
- The max time to evolve the network too in seconds (`NetIn::tMax`)
|
- The max time to evolve the network to in seconds (`NetIn::tMax`)
|
||||||
- The initial timestep to use in seconds (`NetIn::dt0`)
|
- The initial timestep to use in seconds (`NetIn::dt0`)
|
||||||
- The initial energy in the system in ergs (`NetIn::energy`)
|
- The initial energy in the system in ergs (`NetIn::energy`)
|
||||||
|
|
||||||
@@ -472,48 +570,23 @@ A `NetOut` struct contains
|
|||||||
>**Note:** Currently `GraphEngine` only considers energy due to nuclear mass
|
>**Note:** Currently `GraphEngine` only considers energy due to nuclear mass
|
||||||
>defect and not neutrino loss.
|
>defect and not neutrino loss.
|
||||||
|
|
||||||
|
### CVODESolverStrategy
|
||||||
|
|
||||||
### DirectNetworkSolver (Implicit Rosenbrock Method)
|
We use the CVODE module from [SUNDIALS](https://computing.llnl.gov/projects/sundials/cvode) as our primary numerical
|
||||||
|
solver. Specifically we use the BDF linear multistep method from that which includes advanced adaptive timestepping.
|
||||||
|
|
||||||
- **Integrator:** Implicit Rosenbrock4 scheme (order 4) via `Boost.Odeint`’s
|
Further, we use a trigger system to periodically repartition the network as the state of the network changes. This
|
||||||
`rosenbrock4<double>`, optimized for stiff reaction networks with adaptive step
|
keeps the stiffness of the network tractable. The algorithm we use for that is
|
||||||
size control using configurable absolute and relative tolerances.
|
|
||||||
- **Jacobian Assembly:** Asks the base engine for the Jacobian Matrix
|
|
||||||
- **RHS Evaluation:** Asks the base engine for RHS of the abundance evolution
|
|
||||||
equations
|
|
||||||
- **Linear Algebra:** Utilizes `Boost.uBLAS` for state vectors and dense Jacobian
|
|
||||||
matrices, with sparse access patterns supported via coordinate lists of nonzero
|
|
||||||
entries.
|
|
||||||
- **Error Control and Logging:** Absolute and relative tolerance parameters
|
|
||||||
(`absTol`, `relTol`) are read from configuration; Quill loggers, which run in a
|
|
||||||
separate non blocking thread, capture integration diagnostics and step
|
|
||||||
statistics.
|
|
||||||
|
|
||||||
### Algorithmic Workflow in DirectNetworkSolver
|
1. Trigger every 1000th time that the simulation time exceeds the simulationTimeInterval
|
||||||
1. **Initialization:** Convert input temperature to T9 units, retrieve
|
2. OR if any off-diagonal Jacobian entry exceeds the offDiagonalThreshold
|
||||||
tolerances, and initialize state vector `Y` from equilibrated composition.
|
3. OR every 10th time that the timestep growth exceeds the timestepGrowthThreshold (relative or absolute)
|
||||||
2. **Integrator Setup:** Construct the controlled Rosenbrock4 stepper and bind
|
4. OR if the number of convergence failures grows more than 100% from one step to the next or exceeds 5 at any given step.
|
||||||
`RHSManager` and `JacobianFunctor`.
|
|
||||||
3. **Adaptive Integration Loop:**
|
|
||||||
- Perform `integrate_adaptive` advancing until `tMax`, catching any
|
|
||||||
`StaleEngineTrigger` to repartition the network and update composition.
|
|
||||||
- On each substep, observe states and log via `RHSManager::observe`.
|
|
||||||
4. **Finalization:** Assemble final mass fractions, compute accumulated energy,
|
|
||||||
and populate `NetOut` with updated composition and diagnostics.
|
|
||||||
|
|
||||||
### Future Solver Implementations
|
Moreover, callback functions can be registered in either python or C++ which will take a `const CVODESolverStrategy::TimestepContext&` struct
|
||||||
- **Operator Splitting Solvers:** Strategies to decouple thermodynamics,
|
as argument. This allows for more complex logging logic. Note that callbacks **do not** let you reach inside the
|
||||||
screening, and reaction substeps for performance on stiff, multiscale
|
solver and adjust the state of the network. They are only intended for investigation not extension of physics. If you
|
||||||
networks.
|
wish to extend the physics this must be implemented at the engine or engine view level.
|
||||||
- **GPU-Accelerated Solvers:** Planned use of CUDA/OpenCL backends for
|
|
||||||
large-scale network integration.
|
|
||||||
- **Callback observer support:** Currently we use an observer built into our
|
|
||||||
`RHSManager` (`RHSManager::observe`); however, we intend to include support for
|
|
||||||
custom, user defined, observer method.
|
|
||||||
|
|
||||||
These strategies can be developed by inheriting from `NetworkSolverStrategy`
|
|
||||||
and registering against the same engine types without modifying existing engine
|
|
||||||
code.
|
|
||||||
|
|
||||||
## Python Extensibility
|
## Python Extensibility
|
||||||
Through the Python bindings, users can subclass engine view classes directly in
|
Through the Python bindings, users can subclass engine view classes directly in
|
||||||
@@ -554,21 +627,18 @@ int main(){
|
|||||||
### Composition Initialization
|
### Composition Initialization
|
||||||
```c++
|
```c++
|
||||||
#include "fourdst/composition/composition.h"
|
#include "fourdst/composition/composition.h"
|
||||||
|
#include "fourdst/composition/utils.h" // for buildCompositionFromMassFractions
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
fourdst::composition::Composition comp;
|
|
||||||
|
|
||||||
std::vector<std::string> symbols = {"H-1", "He-4", "C-12"};
|
std::vector<std::string> symbols = {"H-1", "He-4", "C-12"};
|
||||||
std::vector<double> massFractions = {0.7, 0.29, 0.01};
|
std::vector<double> massFractions = {0.7, 0.29, 0.01};
|
||||||
|
|
||||||
comp.registerSymbols(symbols);
|
const fourdst::composition::Composition comp = fourdst::composition::buildCompositionFromMassFractions(symbols, massFractions);
|
||||||
comp.setMassFraction(symbols, massFractions);
|
|
||||||
|
|
||||||
comp.finalize(true);
|
|
||||||
|
|
||||||
std::cout << comp << std::endl;
|
std::cout << comp << std::endl;
|
||||||
}
|
}
|
||||||
@@ -580,21 +650,23 @@ A representative workflow often composes multiple engine views to balance
|
|||||||
accuracy, stability, and performance when integrating stiff nuclear networks:
|
accuracy, stability, and performance when integrating stiff nuclear networks:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#include "gridfire/engine/engine.h" // Unified header for real usage
|
#include "gridfire/gridfire.h" // Unified header for real usage
|
||||||
#include "gridfire/solver/solver.h" // Unified header for solvers
|
|
||||||
#include "fourdst/composition/composition.h"
|
#include "fourdst/composition/composition.h"
|
||||||
|
#include "fourdst/composition/utils.h" // for buildCompositionFromMassFractions
|
||||||
|
|
||||||
int main(){
|
int main(){
|
||||||
// 1. Define initial composition
|
// 1. Define initial composition
|
||||||
fourdst::composition::Composition comp;
|
std::unordered_map<std::string, double> initialMassFractions = {
|
||||||
|
{"H-1", 0.7},
|
||||||
std::vector<std::string> symbols = {"H-1", "He-4", "C-12"};
|
{"He-4", 0.29},
|
||||||
std::vector<double> massFractions = {0.7, 0.29, 0.01};
|
{"C-12", 0.01}
|
||||||
|
};
|
||||||
comp.registerSymbols(symbols);
|
const fourdst::composition::Composition composition = fourdst::composition::buildCompositionFromMassFractions(initialMassFractions);
|
||||||
comp.setMassFraction(symbols, massFractions);
|
|
||||||
|
// In this example we will not use the policy module (for sake of demonstration of what is happening under the hood)
|
||||||
comp.finalize(true);
|
// however, for end users we **strongly** recommend using the policy module to construct engines. It will
|
||||||
|
// ensure that you are not missing important reactions or seed species.
|
||||||
|
|
||||||
// 2. Create base network engine (full reaction graph)
|
// 2. Create base network engine (full reaction graph)
|
||||||
gridfire::GraphEngine baseEngine(comp, NetworkBuildDepth::SecondOrder)
|
gridfire::GraphEngine baseEngine(comp, NetworkBuildDepth::SecondOrder)
|
||||||
@@ -606,7 +678,7 @@ int main(){
|
|||||||
gridfire::AdaptiveEngineView adaptView(msView);
|
gridfire::AdaptiveEngineView adaptView(msView);
|
||||||
|
|
||||||
// 5. Construct implicit solver (handles remaining stiffness)
|
// 5. Construct implicit solver (handles remaining stiffness)
|
||||||
gridfire::DirectNetworkSolver solver(adaptView);
|
gridfire::CVODESolverStrategey solver(adaptView);
|
||||||
|
|
||||||
// 6. Prepare input conditions
|
// 6. Prepare input conditions
|
||||||
NetIn input{
|
NetIn input{
|
||||||
@@ -623,35 +695,22 @@ int main(){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Workflow Components and Effects
|
### Callback and Policy Example
|
||||||
- **GraphEngine** constructs the full reaction network, capturing all species
|
|
||||||
and reactions.
|
|
||||||
- **MultiscalePartitioningEngineView** segregates reactions by characteristic
|
|
||||||
timescales (Hix & Thielemann), reducing the effective stiffness by treating
|
|
||||||
fast processes separately.
|
|
||||||
- **AdaptiveEngineView** prunes low-flux species/reactions at runtime,
|
|
||||||
decreasing dimensionality and improving computational efficiency.
|
|
||||||
- **DirectNetworkSolver** employs an implicit Rosenbrock method to stably
|
|
||||||
integrate the remaining stiff system with adaptive step control.
|
|
||||||
|
|
||||||
This layered approach enhances stability for stiff networks while maintaining
|
|
||||||
accuracy and performance.
|
|
||||||
|
|
||||||
### Callback Example
|
|
||||||
Custom callback functions can be registered with any solver. Because it might make sense for each solver to provide
|
Custom callback functions can be registered with any solver. Because it might make sense for each solver to provide
|
||||||
different context to the callback function, you should use the struct `gridfire::solver::<SolverName>::TimestepContext`
|
different context to the callback function, you should use the struct `gridfire::solver::<SolverName>::TimestepContext`
|
||||||
as the argument type for the callback function. This struct contains all the information provided by that solver to
|
as the argument type for the callback function. This struct contains all the information provided by that solver to
|
||||||
the callback function.
|
the callback function.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#include "gridfire/engine/engine.h" // Unified header for real usage
|
#include "gridfire/gridfire.h" // Unified header for real usage
|
||||||
#include "gridfire/solver/solver.h" // Unified header for solvers
|
|
||||||
#include "fourdst/composition/composition.h"
|
#include "fourdst/composition/composition.h" // for Composition
|
||||||
#include "fourdst/atomic/species.h"
|
#include "fourdst/composition/utils.h" // for buildCompositionFromMassFractions
|
||||||
|
#include "fourdst/atomic/species.h" // For strongly typed species
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void callback(const gridfire::solver::DirectNetworkSolver::TimestepContext& context) {
|
void callback(const gridfire::solver::CVODESolverStrategy::TimestepContext& context) {
|
||||||
int H1Index = context.engine.getSpeciesIndex(fourdst::atomic::H_1);
|
int H1Index = context.engine.getSpeciesIndex(fourdst::atomic::H_1);
|
||||||
int He4Index = context.engine.getSpeciesIndex(fourdst::atomic::He_4);
|
int He4Index = context.engine.getSpeciesIndex(fourdst::atomic::He_4);
|
||||||
|
|
||||||
@@ -659,32 +718,19 @@ void callback(const gridfire::solver::DirectNetworkSolver::TimestepContext& cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(){
|
int main(){
|
||||||
// 1. Define initial composition
|
|
||||||
fourdst::composition::Composition comp;
|
|
||||||
|
|
||||||
std::vector<std::string> symbols = {"H-1", "He-4", "C-12"};
|
std::vector<std::string> symbols = {"H-1", "He-4", "C-12"};
|
||||||
std::vector<double> massFractions = {0.7, 0.29, 0.01};
|
std::vector<double> X = {0.7, 0.29, 0.01};
|
||||||
|
|
||||||
comp.registerSymbols(symbols);
|
|
||||||
comp.setMassFraction(symbols, massFractions);
|
|
||||||
|
|
||||||
comp.finalize(true);
|
const fourdst::composition::Composition composition = fourdst::composition::buildCompositionFromMassFractions(symbols, X);
|
||||||
|
gridfire::policy::MainSequencePolicy stellarPolicy(netIn.composition);
|
||||||
// 2. Create base network engine (full reaction graph)
|
gridfire::engine::DynamicEngine& engine = stellarPolicy.construct();
|
||||||
gridfire::GraphEngine baseEngine(comp, NetworkBuildDepth::SecondOrder)
|
|
||||||
|
gridfire::solver::CVODESolverStrategy solver(adaptView);
|
||||||
// 3. Partition network into fast/slow subsets (reduces stiffness)
|
|
||||||
gridfire::MultiscalePartitioningEngineView msView(baseEngine);
|
|
||||||
|
|
||||||
// 4. Adaptively cull negligible flux pathways (reduces dimension & stiffness)
|
|
||||||
gridfire::AdaptiveEngineView adaptView(msView);
|
|
||||||
|
|
||||||
// 5. Construct implicit solver (handles remaining stiffness)
|
|
||||||
gridfire::DirectNetworkSolver solver(adaptView);
|
|
||||||
solver.set_callback(callback);
|
solver.set_callback(callback);
|
||||||
|
|
||||||
// 6. Prepare input conditions
|
// 6. Prepare input conditions
|
||||||
NetIn input{
|
gridfire::NetIn input{
|
||||||
comp, // composition
|
comp, // composition
|
||||||
1.5e7, // temperature [K]
|
1.5e7, // temperature [K]
|
||||||
1.5e2, // density [g/cm^3]
|
1.5e2, // density [g/cm^3]
|
||||||
@@ -693,21 +739,38 @@ int main(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 7. Execute integration
|
// 7. Execute integration
|
||||||
NetOut output = solver.evaluate(input);
|
gridfire::NetOut output = solver.evaluate(input);
|
||||||
std::cout << "Final results are: " << output << std::endl;
|
std::cout << "Final results are: " << output << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
>**Note:** If you want to see exactly why each repartitioning stage was triggered in a human readable manner add the flag True to `solver.evaluate` (`solver.evaluate(input, true)`).
|
||||||
|
|
||||||
>**Note:** A fully detailed list of all available information in the TimestepContext struct is available in the API documentation.
|
>**Note:** A fully detailed list of all available information in the TimestepContext struct is available in the API documentation.
|
||||||
|
|
||||||
>**Note:** The order of species in the boost state vector (`ctx.state`) is **not guaranteed** to be any particular order run over run. Therefore, in order to reliably extract
|
>**Note:** The order of species in the boost state vector (`ctx.state`) is **not guaranteed** to be any particular order run over run. Therefore, in order to reliably extract
|
||||||
> values from it, you **must** use the `getSpeciesIndex` method of the engine to get the index of the species you are interested in (these will always be in the same order).
|
> values from it, you **must** use the `getSpeciesIndex` method of the engine to get the index of the species you are interested in (these will always be in the same order).
|
||||||
|
|
||||||
|
If you wish to know what is provided by a solver context without investigating the code you can simply do
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void callback(const gridfire::solver::SolverContextBase& context) {
|
||||||
|
for (const auto& [parameterName, description] : context.describe()) {
|
||||||
|
std::cout << parameterName << ": " << description << "\n";
|
||||||
|
}
|
||||||
|
std::cout << std::flush();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you set this as the callback (to any solver strategy) it will print out the available parameters and what they
|
||||||
|
are and then close the code. This is useful when writing new callbacks.
|
||||||
|
|
||||||
|
|
||||||
#### Callback Context
|
#### Callback Context
|
||||||
|
|
||||||
Since each solver may provide different context to the callback function, and it may be frustrating to refer to the
|
Since each solver may provide different context to the callback function, and it may be frustrating to refer to the
|
||||||
documentation every time, we also enforce that all solvers must implement a `descripe_callback_context` method which
|
documentation every time, we also enforce that all solvers must implement a `descripe_callback_context` method which
|
||||||
returns a vector of tuples<string, string> where the first element is the name of the field and the second is its
|
returns a vector of tuples<string, string> where the first element is the name of the field and the second is its
|
||||||
datatype. It is on the developer to ensure that this information is accurate.
|
datatype. It is on the developer to ensure that this information is accurate.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
@@ -724,103 +787,114 @@ with imports of modules such that
|
|||||||
|
|
||||||
All GridFire C++ types have been bound and can be passed around as one would expect.
|
All GridFire C++ types have been bound and can be passed around as one would expect.
|
||||||
|
|
||||||
### Common Workflow Example
|
|
||||||
This example implements the same logic as the above C++ example
|
### Python Example for End Users
|
||||||
|
|
||||||
|
|
||||||
|
The syntax for registration is very similar to C++. There are a few things to note about this more robust example
|
||||||
|
|
||||||
|
1. Note how I use a callback and a log object to store the state of the simulation at each timestep.
|
||||||
|
2. If you have tools such as mypy installed you will see that the python bindings are strongly typed. This is
|
||||||
|
intentional to help users avoid mistakes when writing code.
|
||||||
```python
|
```python
|
||||||
from gridfire.engine import GraphEngine, MultiscalePartitioningEngineView, AdaptiveEngineView
|
|
||||||
from gridfire.solver import DirectNetworkSolver
|
|
||||||
from gridfire.type import NetIn
|
|
||||||
|
|
||||||
from fourdst.composition import Composition
|
from fourdst.composition import Composition
|
||||||
|
from gridfire.type import NetIn
|
||||||
|
from gridfire.policy import MainSequencePolicy
|
||||||
|
from gridfire.solver import CVODESolverStrategy
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Dict, Union, SupportsFloat
|
||||||
|
import json
|
||||||
|
import dicttoxml
|
||||||
|
|
||||||
symbols : list[str] = ["H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"]
|
def init_composition() -> Composition:
|
||||||
X : list[float] = [0.708, 2.94e-5, 0.276, 0.003, 0.0011, 9.62e-3, 1.62e-3, 5.16e-4]
|
Y = [7.0262E-01, 9.7479E-06, 6.8955E-02, 2.5000E-04, 7.8554E-05, 6.0144E-04, 8.1031E-05, 2.1513E-05] # Note these are molar abundances
|
||||||
|
S = ["H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"]
|
||||||
|
return Composition(S, Y)
|
||||||
|
|
||||||
|
def init_netIn(temp: float, rho: float, time: float, comp: Composition) -> NetIn:
|
||||||
|
netIn = NetIn()
|
||||||
|
netIn.temperature = temp
|
||||||
|
netIn.density = rho
|
||||||
|
netIn.tMax = time
|
||||||
|
netIn.dt0 = 1e-12
|
||||||
|
netIn.composition = comp
|
||||||
|
return netIn
|
||||||
|
|
||||||
|
class StepData(Enum):
|
||||||
|
TIME = 0
|
||||||
|
DT = 1
|
||||||
|
COMP = 2
|
||||||
|
CONTRIB = 3
|
||||||
|
|
||||||
|
|
||||||
comp = Composition()
|
class StepLogger:
|
||||||
comp.registerSymbol(symbols)
|
def __init__(self):
|
||||||
comp.setMassFraction(symbols, X)
|
self.num_steps: int = 0
|
||||||
comp.finalize(True)
|
self.step_data: Dict[int, Dict[StepData, Union[SupportsFloat, Dict[str, SupportsFloat]]]] = {}
|
||||||
|
|
||||||
print(f"Initial H-1 mass fraction {comp.getMassFraction("H-1")}")
|
def log_step(self, context):
|
||||||
|
engine = context.engine
|
||||||
|
self.step_data[self.num_steps] = {}
|
||||||
|
self.step_data[self.num_steps][StepData.TIME] = context.t
|
||||||
|
self.step_data[self.num_steps][StepData.DT] = context.dt
|
||||||
|
comp_data: Dict[str, SupportsFloat] = {}
|
||||||
|
for species in engine.getNetworkSpecies():
|
||||||
|
sid = engine.getSpeciesIndex(species)
|
||||||
|
comp_data[species.name()] = context.state[sid]
|
||||||
|
self.step_data[self.num_steps][StepData.COMP] = comp_data
|
||||||
|
self.num_steps += 1
|
||||||
|
|
||||||
netIn = NetIn()
|
def to_json (self, filename: str):
|
||||||
netIn.composition = comp
|
serializable_data = {
|
||||||
netIn.temperature = 1.5e7
|
stepNum: {
|
||||||
netIn.density = 1.6e2
|
StepData.TIME.name: step[StepData.TIME],
|
||||||
netIn.tMax = 1e-9
|
StepData.DT.name: step[StepData.DT],
|
||||||
netIn.dt0 = 1e-12
|
StepData.COMP.name: step[StepData.COMP],
|
||||||
|
}
|
||||||
|
for stepNum, step in self.step_data.items()
|
||||||
|
}
|
||||||
|
with open(filename, 'w') as f:
|
||||||
|
json.dump(serializable_data, f, indent=4)
|
||||||
|
|
||||||
baseEngine = GraphEngine(netIn.composition, 2)
|
def to_xml(self, filename: str):
|
||||||
baseEngine.setUseReverseReactions(False)
|
serializable_data = {
|
||||||
|
stepNum: {
|
||||||
|
StepData.TIME.name: step[StepData.TIME],
|
||||||
|
StepData.DT.name: step[StepData.DT],
|
||||||
|
StepData.COMP.name: step[StepData.COMP],
|
||||||
|
}
|
||||||
|
for stepNum, step in self.step_data.items()
|
||||||
|
}
|
||||||
|
xml_data = dicttoxml.dicttoxml(serializable_data, custom_root='StepLog', attr_type=False)
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(xml_data)
|
||||||
|
|
||||||
qseEngine = MultiscalePartitioningEngineView(baseEngine)
|
def main(temp: float, rho: float, time: float):
|
||||||
|
comp = init_composition()
|
||||||
|
netIn = init_netIn(temp, rho, time, comp)
|
||||||
|
|
||||||
adaptiveEngine = AdaptiveEngineView(qseEngine)
|
policy = MainSequencePolicy(comp)
|
||||||
|
engine = policy.construct()
|
||||||
|
|
||||||
solver = DirectNetworkSolver(adaptiveEngine)
|
solver = CVODESolverStrategy(engine)
|
||||||
|
|
||||||
results = solver.evaluate(netIn)
|
step_logger = StepLogger()
|
||||||
|
solver.set_callback(lambda context: step_logger.log_step(context))
|
||||||
|
|
||||||
|
solver.evaluate(netIn, False)
|
||||||
|
step_logger.to_xml("log_data.xml")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
parser = argparse.ArgumentParser(description="Simple python example of GridFire usage")
|
||||||
|
parser.add_argument("-t", "--temp", type=float, help="Temperature in K", default=1.5e7)
|
||||||
|
parser.add_argument("-r", "--rho", type=float, help="Density in g/cm^3", default=1.5e2)
|
||||||
|
parser.add_argument("--tMax", type=float, help="Time in s", default=3.1536 * 1e17)
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args.temp, args.rho, args.tMax)
|
||||||
|
|
||||||
print(f"Final H-1 mass fraction {results.composition.getMassFraction("H-1")}")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Python callbacks
|
|
||||||
|
|
||||||
Just like in C++, python users can register callbacks to be called at the end of each successful timestep. Note that
|
|
||||||
these may slow down code significantly as the interpreter needs to jump up into the slower python code therefore these
|
|
||||||
should likely only be used for debugging purposes.
|
|
||||||
|
|
||||||
The syntax for registration is very similar to C++
|
|
||||||
```python
|
|
||||||
from gridfire.engine import GraphEngine, MultiscalePartitioningEngineView, AdaptiveEngineView
|
|
||||||
from gridfire.solver import DirectNetworkSolver
|
|
||||||
from gridfire.type import NetIn
|
|
||||||
|
|
||||||
from fourdst.composition import Composition
|
|
||||||
from fourdst.atomic import species
|
|
||||||
|
|
||||||
symbols : list[str] = ["H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"]
|
|
||||||
X : list[float] = [0.708, 2.94e-5, 0.276, 0.003, 0.0011, 9.62e-3, 1.62e-3, 5.16e-4]
|
|
||||||
|
|
||||||
|
|
||||||
comp = Composition()
|
|
||||||
comp.registerSymbol(symbols)
|
|
||||||
comp.setMassFraction(symbols, X)
|
|
||||||
comp.finalize(True)
|
|
||||||
|
|
||||||
print(f"Initial H-1 mass fraction {comp.getMassFraction("H-1")}")
|
|
||||||
|
|
||||||
netIn = NetIn()
|
|
||||||
netIn.composition = comp
|
|
||||||
netIn.temperature = 1.5e7
|
|
||||||
netIn.density = 1.6e2
|
|
||||||
netIn.tMax = 1e-9
|
|
||||||
netIn.dt0 = 1e-12
|
|
||||||
|
|
||||||
baseEngine = GraphEngine(netIn.composition, 2)
|
|
||||||
baseEngine.setUseReverseReactions(False)
|
|
||||||
|
|
||||||
qseEngine = MultiscalePartitioningEngineView(baseEngine)
|
|
||||||
|
|
||||||
adaptiveEngine = AdaptiveEngineView(qseEngine)
|
|
||||||
|
|
||||||
solver = DirectNetworkSolver(adaptiveEngine)
|
|
||||||
|
|
||||||
|
|
||||||
def callback(context):
|
|
||||||
H1Index = context.engine.getSpeciesIndex(species["H-1"])
|
|
||||||
He4Index = context.engine.getSpeciesIndex(species["He-4"])
|
|
||||||
C12ndex = context.engine.getSpeciesIndex(species["C-12"])
|
|
||||||
Mgh24ndex = context.engine.getSpeciesIndex(species["Mg-24"])
|
|
||||||
print(f"Time: {context.t}, H-1: {context.state[H1Index]}, He-4: {context.state[He4Index]}, C-12: {context.state[C12ndex]}, Mg-24: {context.state[Mgh24ndex]}")
|
|
||||||
|
|
||||||
solver.set_callback(callback)
|
|
||||||
results = solver.evaluate(netIn)
|
|
||||||
|
|
||||||
print(f"Final H-1 mass fraction {results.composition.getMassFraction("H-1")}")
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Related Projects
|
# Related Projects
|
||||||
|
|||||||
Reference in New Issue
Block a user