fix(python): added stubs

code inspection now works with fourdst
This commit is contained in:
2025-11-25 11:54:08 -05:00
parent a799957f3e
commit a55a661b39
14 changed files with 604 additions and 48 deletions

View File

@@ -21,10 +21,31 @@ py_mod = py_installation.extension_module(
)
py_installation.install_sources(
meson.project_source_root() + '/src-pybind/fourdst/__init__.py',
files(
meson.project_source_root() + '/src-pybind/fourdst/__init__.pyi',
meson.project_source_root() + '/src-pybind/fourdst/__init__.py',
),
subdir: 'fourdst',
)
py_installation.install_sources(
files(
meson.project_source_root() + '/src-pybind/fourdst/_phys/__init__.pyi',
meson.project_source_root() + '/src-pybind/fourdst/_phys/atomic.pyi',
meson.project_source_root() + '/src-pybind/fourdst/_phys/config.pyi',
meson.project_source_root() + '/src-pybind/fourdst/_phys/constants.pyi',
),
subdir: 'fourdst/',
)
py_installation.install_sources(
files(
meson.project_source_root() + '/src-pybind/fourdst/_phys/composition/__init__.pyi',
meson.project_source_root() + '/src-pybind/fourdst/_phys/composition/utils.pyi',
),
subdir: 'fourdst/composition',
)
py_installation.install_sources(
files(
meson.project_source_root() + '/src-pybind/fourdst/cli/__init__.py',

View File

@@ -1,4 +1,4 @@
project('fourdst', 'cpp', version: 'v0.9.5', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0')
project('fourdst', 'cpp', version: 'v0.9.6', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0')
add_project_arguments('-fvisibility=default', language: 'cpp')
@@ -17,21 +17,3 @@ subdir('build-python')
# Build python bindings
subdir('src-pybind')
# Bundle the Python backend for the Electron app
#if get_option('electron-build-py-backend')
# pyinstaller_exe = find_program('pyinstaller', required : true)
# electron_src_dir = meson.current_source_dir() / 'electron'
#
# custom_target('fourdst-backend',
# input : electron_src_dir / 'fourdst-backend.spec',
# # The output is the directory that PyInstaller creates.
# # We are interested in the executable inside it.
# output : 'fourdst-backend',
# # The command to run. We tell PyInstaller where to put the final executable.
# command : [pyinstaller_exe, '--distpath', meson.current_build_dir() / 'electron/dist', '--workpath', meson.current_build_dir() / 'electron/build', '--noconfirm', '@INPUT@'],
# # This ensures the backend is built whenever you run 'meson compile'.
# build_by_default : true
# )
#endif

View File

@@ -9,12 +9,12 @@
PYBIND11_MODULE(_phys, m) {
m.doc() = "Python bindings for the fourdst utility modules which are a part of the 4D-STAR project.";
auto compMod = m.def_submodule("composition", "Composition-module bindings");
register_comp_bindings(compMod);
auto atomicMod = m.def_submodule("atomic", "Species bindings");
register_species_bindings(atomicMod);
auto compMod = m.def_submodule("composition", "Composition-module bindings");
register_comp_bindings(compMod);
auto constMod = m.def_submodule("constants", "Constants-module bindings");
register_const_bindings(constMod);

View File

@@ -10,6 +10,8 @@
#include "bindings.h"
#include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h"
#include "fourdst/composition/utils/composition_hash.h"
namespace py = pybind11;
@@ -24,8 +26,8 @@ std::string get_ostream_str(const fourdst::composition::Composition& comp) {
}
void register_comp_bindings(const pybind11::module &comp_submodule) {
py::class_<fourdst::composition::CanonicalComposition>(comp_submodule, "CanonicalComposition")
void register_comp_bindings(pybind11::module &m) {
py::class_<fourdst::composition::CanonicalComposition>(m, "CanonicalComposition")
.def_readonly("X", &fourdst::composition::CanonicalComposition::X)
.def_readonly("Y", &fourdst::composition::CanonicalComposition::Y)
.def_readonly("Z", &fourdst::composition::CanonicalComposition::Z)
@@ -37,7 +39,7 @@ void register_comp_bindings(const pybind11::module &comp_submodule) {
});
// --- Binding for the main Composition class ---
py::class_<fourdst::composition::Composition>(comp_submodule, "Composition")
py::class_<fourdst::composition::Composition>(m, "Composition")
// Constructors
.def(
py::init<>(),
@@ -46,10 +48,20 @@ void register_comp_bindings(const pybind11::module &comp_submodule) {
py::init<const std::vector<std::string>&>(),
py::arg("symbols"),
"Constructor taking a list of symbols to register")
.def(
py::init<const std::set<std::string>&>(),
py::arg("symbols"),
"Constructor taking a set of symbols to register"
)
.def(
py::init<const std::vector<fourdst::atomic::Species>&>(),
py::arg("species"),
"Constructor taking a list of species to register")
.def(
py::init<const std::set<fourdst::atomic::Species>&>(),
py::arg("species"),
"Constructor taking a set of species to register"
)
.def(
py::init<const std::vector<std::string>&, const std::vector<double>&>(),
py::arg("symbols"),
@@ -62,6 +74,32 @@ void register_comp_bindings(const pybind11::module &comp_submodule) {
py::arg("molarAbundances"),
"Constructor taking a list of species and molar abundances"
)
.def(
py::init<const std::set<std::string>&, const std::vector<double>&>(),
py::arg("symbols"),
py::arg("molarAbundances"),
"Constructor taking a set of symbols and a list of molar abundances"
)
.def(
py::init<const std::unordered_map<fourdst::atomic::Species, double>&>(),
py::arg("speciesMolarAbundances"),
"Constructor taking an unordered map of species to molar abundances"
)
.def (
py::init<const std::map<fourdst::atomic::Species, double>&>(),
py::arg("speciesMolarAbundances"),
"Constructor taking a map of species to molar abundances"
)
.def(
py::init<const std::unordered_map<std::string, double>&>(),
py::arg("speciesMolarAbundances"),
"Constructor taking an unordered map of species to molar abundances"
)
.def (
py::init<const std::map<std::string, double>&>(),
py::arg("speciesMolarAbundances"),
"Constructor taking a map of species to molar abundances"
)
.def(
"registerSymbol",
py::overload_cast<const std::string&>(&fourdst::composition::Composition::registerSymbol),
@@ -215,31 +253,123 @@ void register_comp_bindings(const pybind11::module &comp_submodule) {
[](const fourdst::composition::Composition& comp) {
return py::make_iterator(comp.begin(), comp.end());
},
py::keep_alive<0, 1>());
py::keep_alive<0, 1>())
.def(
"__eq__",
[](const fourdst::composition::Composition& self, const fourdst::composition::Composition& other) {
return self == other;
},
py::is_operator()
)
.def(
"__hash__",
[](const fourdst::composition::Composition& comp) {
return fourdst::composition::utils::CompositionHash::hash_exact<fourdst::composition::Composition>(comp);
}
);
// register new utils module
auto utils = m.def_submodule("utils", "Utility functions for Composition");
py::class_<fourdst::composition::utils::CompositionHash>(utils, "CompositionHash")
.def_static(
"hash_exact",
&fourdst::composition::utils::CompositionHash::hash_exact<fourdst::composition::Composition>,
py::arg("composition"),
"Compute a hash for a given Composition object."
)
.def_static(
"hash_quantized",
&fourdst::composition::utils::CompositionHash::hash_quantized<fourdst::composition::Composition>,
py::arg("composition"),
py::arg("eps"),
"Compute a quantized hash for a given Composition object with specified precision."
);
utils.def(
"buildCompositionFromMassFractions",
[](const std::vector<std::string>& symbols, const std::vector<double>& massFractions) {
return fourdst::composition::buildCompositionFromMassFractions(symbols, massFractions);
},
py::arg("symbols"),
py::arg("massFractions"),
"Build a Composition object from symbols and their corresponding mass fractions."
);
utils.def("buildCompositionFromMassFractions",
[](const std::vector<fourdst::atomic::Species>& species, const std::vector<double>& massFractions) {
return fourdst::composition::buildCompositionFromMassFractions(species, massFractions);
},
py::arg("species"),
py::arg("massFractions"),
"Build a Composition object from species and their corresponding mass fractions."
);
utils.def(
"buildCompositionFromMassFractions",
[](const std::set<fourdst::atomic::Species>& species, const std::vector<double>& massFractions) {
return fourdst::composition::buildCompositionFromMassFractions(species, massFractions);
},
py::arg("species"),
py::arg("massFractions"),
"Build a Composition object from species in a set and their corresponding mass fractions."
);
utils.def(
"buildCompositionFromMassFractions",
[](const std::unordered_map<fourdst::atomic::Species, double>& massFractionsMap) {
return fourdst::composition::buildCompositionFromMassFractions(massFractionsMap);
},
py::arg("massFractionsMap"),
"Build a Composition object from a map of species to mass fractions."
);
utils.def(
"buildCompositionFromMassFractions",
[](const std::map<fourdst::atomic::Species, double>& massFractionsMap) {
return fourdst::composition::buildCompositionFromMassFractions(massFractionsMap);
},
py::arg("massFractionsMap"),
"Build a Composition object from a map of species to mass fractions."
);
}
void register_species_bindings(const pybind11::module &chem_submodule) {
// --- Bindings for species module ---
py::class_<fourdst::atomic::Species>(chem_submodule, "Species")
.def("mass", &fourdst::atomic::Species::mass, "Get atomic mass (amu)")
.def("massUnc", &fourdst::atomic::Species::massUnc, "Get atomic mass uncertainty (amu)")
.def("bindingEnergy", &fourdst::atomic::Species::bindingEnergy, "Get binding energy (keV/nucleon?)") // Check units
.def("betaDecayEnergy", &fourdst::atomic::Species::betaDecayEnergy, "Get beta decay energy (keV?)") // Check units
.def("betaCode", [](const fourdst::atomic::Species& s){ return sv_to_string(s.betaCode()); }, "Get beta decay code") // Convert string_view
.def("name", [](const fourdst::atomic::Species& s){ return sv_to_string(s.name()); }, "Get species name (e.g., 'H-1')") // Convert string_view
.def("el", [](const fourdst::atomic::Species& s){ return sv_to_string(s.el()); }, "Get element symbol (e.g., 'H')") // Convert string_view
.def("nz", &fourdst::atomic::Species::nz, "Get NZ value")
.def("n", &fourdst::atomic::Species::n, "Get neutron number N")
.def("z", &fourdst::atomic::Species::z, "Get proton number Z")
.def("a", &fourdst::atomic::Species::a, "Get mass number A")
.def("mass", &fourdst::atomic::Species::mass, "Get atomic mass (amu)")
.def("massUnc", &fourdst::atomic::Species::massUnc, "Get atomic mass uncertainty (amu)")
.def("bindingEnergy", &fourdst::atomic::Species::bindingEnergy, "Get binding energy (keV/nucleon?)") // Check units
.def("betaDecayEnergy", &fourdst::atomic::Species::betaDecayEnergy, "Get beta decay energy (keV?)") // Check units
.def("betaCode", [](const fourdst::atomic::Species& s){ return sv_to_string(s.betaCode()); }, "Get beta decay code") // Convert string_view
.def("name", [](const fourdst::atomic::Species& s){ return sv_to_string(s.name()); }, "Get species name (e.g., 'H-1')") // Convert string_view
.def("el", [](const fourdst::atomic::Species& s){ return sv_to_string(s.el()); }, "Get element symbol (e.g., 'H')") // Convert string_view
.def("nz", &fourdst::atomic::Species::nz, "Get NZ value")
.def("n", &fourdst::atomic::Species::n, "Get neutron number N")
.def("z", &fourdst::atomic::Species::z, "Get proton number Z")
.def("a", &fourdst::atomic::Species::a, "Get mass number A")
.def("__repr__",
[](const fourdst::atomic::Species &s) {
std::ostringstream oss;
oss << s;
return oss.str();
}
)
.def(
"__eq__",
[](const fourdst::atomic::Species& self, const fourdst::atomic::Species& other) {
return self == other;
},
py::is_operator()
)
.def(
"__hash__",
[](const fourdst::atomic::Species& s) {
return std::hash<fourdst::atomic::Species>()(s);
}
);
.def("__repr__",
[](const fourdst::atomic::Species &s) {
std::ostringstream oss;
oss << s;
return oss.str();
});
chem_submodule.attr("species") = py::cast(fourdst::atomic::species); // Expose the species map
}

View File

@@ -2,5 +2,5 @@
#include <pybind11/pybind11.h>
void register_comp_bindings(const pybind11::module &m);
void register_comp_bindings(pybind11::module &m);
void register_species_bindings(const pybind11::module &m);

View File

@@ -1,4 +1,5 @@
from ._phys import *
# src-pybind/fourdst/__init__.py
from __future__ import annotations
import sys
from ._phys import atomic, composition, constants, config
@@ -8,4 +9,6 @@ sys.modules['fourdst.composition'] = composition
sys.modules['fourdst.constants'] = constants
sys.modules['fourdst.config'] = config
__all__ = ['atomic', 'composition', 'constants', 'config', 'core', 'cli']
__all__ = ['atomic', 'composition', 'constants', 'config', 'core', 'cli']
__version__ = 'v0.9.6'

View File

@@ -0,0 +1,9 @@
from __future__ import annotations
from fourdst import atomic
from fourdst import composition
from fourdst import config
from fourdst import constants
__all__: list = ['atomic', 'composition', 'constants', 'config', 'core', 'cli']

View File

@@ -0,0 +1,9 @@
"""
Python bindings for the fourdst utility modules which are a part of the 4D-STAR project.
"""
from __future__ import annotations
from . import atomic
from . import composition
from . import config
from . import constants
__all__: list[str] = ['atomic', 'composition', 'config', 'constants']

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,215 @@
"""
Composition-module bindings
"""
from __future__ import annotations
import fourdst.atomic
import typing
from . import utils
__all__: list[str] = ['CanonicalComposition', 'Composition', 'utils']
class CanonicalComposition:
def __repr__(self) -> str:
...
@property
def X(self) -> float:
...
@property
def Y(self) -> float:
...
@property
def Z(self) -> float:
...
class Composition:
def __eq__(self, arg0: Composition) -> bool:
...
def __hash__(self) -> int:
...
@typing.overload
def __init__(self) -> None:
"""
Default constructor
"""
@typing.overload
def __init__(self, symbols: list[str]) -> None:
"""
Constructor taking a list of symbols to register
"""
@typing.overload
def __init__(self, symbols: set[str]) -> None:
"""
Constructor taking a set of symbols to register
"""
@typing.overload
def __init__(self, species: list[fourdst.atomic.Species]) -> None:
"""
Constructor taking a list of species to register
"""
@typing.overload
def __init__(self, species: set[fourdst.atomic.Species]) -> None:
"""
Constructor taking a set of species to register
"""
@typing.overload
def __init__(self, symbols: list[str], molarAbundances: list[float]) -> None:
"""
Constructor taking a list of symbols and molar abundances
"""
@typing.overload
def __init__(self, species: list[fourdst.atomic.Species], molarAbundances: list[float]) -> None:
"""
Constructor taking a list of species and molar abundances
"""
@typing.overload
def __init__(self, symbols: set[str], molarAbundances: list[float]) -> None:
"""
Constructor taking a set of symbols and a list of molar abundances
"""
@typing.overload
def __init__(self, speciesMolarAbundances: dict[fourdst.atomic.Species, float]) -> None:
"""
Constructor taking an unordered map of species to molar abundances
"""
@typing.overload
def __init__(self, speciesMolarAbundances: dict[fourdst.atomic.Species, float]) -> None:
"""
Constructor taking a map of species to molar abundances
"""
def __iter__(self) -> typing.Iterator[tuple[fourdst.atomic.Species, float]]:
...
def __repr__(self) -> str:
...
@typing.overload
def contains(self, symbol: str) -> bool:
"""
Check if a symbol is in the composition.
"""
@typing.overload
def contains(self, species: fourdst.atomic.Species) -> bool:
"""
Check if a species is in the composition.
"""
def getCanonicalComposition(self) -> CanonicalComposition:
"""
Get a canonical composition (X, Y, Z). d
"""
@typing.overload
def getMassFraction(self, symbol: str) -> float:
"""
Get mass fraction for a symbol.
"""
@typing.overload
def getMassFraction(self, species: fourdst.atomic.Species) -> float:
"""
Get mass fraction for a species.
"""
@typing.overload
def getMassFraction(self) -> dict[fourdst.atomic.Species, float]:
"""
Get dictionary of all mass fractions.
"""
def getMassFractionVector(self) -> list[float]:
"""
Get mass fractions as a vector (ordered by species mass).
"""
def getMeanParticleMass(self) -> float:
"""
Get the mean particle mass (amu)
"""
@typing.overload
def getMolarAbundance(self, symbol: str) -> float:
"""
Get molar abundance for a symbol.
"""
@typing.overload
def getMolarAbundance(self, species: fourdst.atomic.Species) -> float:
"""
Get molar abundance for a species.
"""
def getMolarAbundanceVector(self) -> list[float]:
"""
Get molar abundances as a vector (ordered by species mass).
"""
@typing.overload
def getNumberFraction(self, symbol: str) -> float:
"""
Get number fraction for a symbol.
"""
@typing.overload
def getNumberFraction(self, species: fourdst.atomic.Species) -> float:
"""
Get number fraction for a species.
"""
@typing.overload
def getNumberFraction(self) -> dict[fourdst.atomic.Species, float]:
"""
Get dictionary of all number fractions.
"""
def getNumberFractionVector(self) -> list[float]:
"""
Get number fractions as a vector (ordered by species mass)
"""
def getRegisteredSpecies(self) -> set[fourdst.atomic.Species]:
"""
Get the set of registered species.
"""
def getRegisteredSymbols(self) -> set[str]:
"""
Get the set of registered symbols.
"""
def getSpeciesAtIndex(self, index: int) -> fourdst.atomic.Species:
"""
Get the species at a given index in the internal ordering.
"""
@typing.overload
def getSpeciesIndex(self, symbol: str) -> int:
"""
Get the index of a species in the internal ordering.
"""
@typing.overload
def getSpeciesIndex(self, species: fourdst.atomic.Species) -> int:
"""
Get the index of a species in the internal ordering.
"""
@typing.overload
def registerSpecies(self, species: fourdst.atomic.Species) -> None:
"""
Register a single species. The molar abundance will be initialized to zero.
"""
@typing.overload
def registerSpecies(self, species: list[fourdst.atomic.Species]) -> None:
"""
Register multiple species. Each molar abundance will be initialized to zero.
"""
@typing.overload
def registerSymbol(self, symbol: str) -> None:
"""
Register a single symbol. The molar abundance will be initialized to zero.
"""
@typing.overload
def registerSymbol(self, symbols: list[str]) -> None:
"""
Register multiple symbols. Each molar abundance will be initialized to zero.
"""
@typing.overload
def setMolarAbundance(self, symbol: str, molarAbundance: float) -> None:
"""
Set the molar abundance for a symbol.
"""
@typing.overload
def setMolarAbundance(self, species: fourdst.atomic.Species, molarAbundance: float) -> None:
"""
Set the molar abundance for a species.
"""
@typing.overload
def setMolarAbundance(self, symbols: list[str], molarAbundances: list[float]) -> None:
"""
Set the molar abundance for a list of symbols. The molar abundance vector must be parallel to the symbols vector.
"""
@typing.overload
def setMolarAbundance(self, species: list[fourdst.atomic.Species], molarAbundances: list[float]) -> None:
"""
Set the molar abundance for a list of species. The molar abundance vector must be parallel to the species vector.
"""
def size(self) -> int:
"""
Get the number of registered species in the composition.
"""

View File

@@ -0,0 +1,44 @@
"""
Utility functions for Composition
"""
from __future__ import annotations
import fourdst.atomic
import fourdst.composition
import typing
__all__: list[str] = ['CompositionHash', 'buildCompositionFromMassFractions']
class CompositionHash:
@staticmethod
def hash_exact(composition: fourdst.composition.Composition) -> int:
"""
Compute a hash for a given Composition object.
"""
@staticmethod
def hash_quantized(composition: fourdst.composition.Composition, eps: float) -> int:
"""
Compute a quantized hash for a given Composition object with specified precision.
"""
@typing.overload
def buildCompositionFromMassFractions(symbols: list[str], massFractions: list[float]) -> fourdst.composition.Composition:
"""
Build a Composition object from symbols and their corresponding mass fractions.
"""
@typing.overload
def buildCompositionFromMassFractions(species: list[fourdst.atomic.Species], massFractions: list[float]) -> fourdst.composition.Composition:
"""
Build a Composition object from species and their corresponding mass fractions.
"""
@typing.overload
def buildCompositionFromMassFractions(species: set[fourdst.atomic.Species], massFractions: list[float]) -> fourdst.composition.Composition:
"""
Build a Composition object from species in a set and their corresponding mass fractions.
"""
@typing.overload
def buildCompositionFromMassFractions(massFractionsMap: dict[fourdst.atomic.Species, float]) -> fourdst.composition.Composition:
"""
Build a Composition object from a map of species to mass fractions.
"""
@typing.overload
def buildCompositionFromMassFractions(massFractionsMap: dict[fourdst.atomic.Species, float]) -> fourdst.composition.Composition:
"""
Build a Composition object from a map of species to mass fractions.
"""

View File

@@ -0,0 +1,40 @@
"""
Configuration-module bindings
"""
from __future__ import annotations
import typing
__all__: list[str] = ['get', 'has', 'keys', 'loadConfig']
def __repr__() -> str:
...
@typing.overload
def get(key: str, defaultValue: int) -> int:
"""
Get configuration value (type inferred from default)
"""
@typing.overload
def get(key: str, defaultValue: float) -> float:
"""
Get configuration value (type inferred from default)
"""
@typing.overload
def get(key: str, defaultValue: str) -> str:
"""
Get configuration value (type inferred from default)
"""
@typing.overload
def get(key: str, defaultValue: bool) -> bool:
"""
Get configuration value (type inferred from default)
"""
def has(key: str) -> bool:
"""
Check if a key exists in the configuration.
"""
def keys() -> typing.Any:
"""
Get a list of all configuration keys.
"""
def loadConfig(configFilePath: str) -> bool:
"""
Load configuration from a YAML file.
"""

View File

@@ -0,0 +1,46 @@
"""
Constants-module bindings
"""
from __future__ import annotations
import typing
__all__: list[str] = ['Constant', 'Constants']
class Constant:
def __repr__(self) -> str:
...
@property
def name(self) -> str:
...
@property
def reference(self) -> str:
...
@property
def uncertainty(self) -> float:
...
@property
def unit(self) -> str:
...
@property
def value(self) -> float:
...
class Constants:
@staticmethod
def __class_getitem__(arg0: str) -> typing.Any:
...
@staticmethod
def get(arg0: str) -> typing.Any:
"""
Get a constant by name. Returns None if not found.
"""
@staticmethod
def has(arg0: str) -> bool:
"""
Check if a constant exists by name.
"""
@staticmethod
def keys() -> typing.Any:
"""
Get a list of all constant names.
"""
@property
def loaded(self) -> bool:
...

View File

@@ -1,4 +1,4 @@
[wrap-git]
url = https://github.com/4D-STAR/libcomposition.git
revision = v2.1.0
revision = v2.2.0
depth = 1