Developer’s Guide
Welcome to the rbamlib project! This guide provides information for contributors.
Contribution Process
For guidelines on contributing to this project, please refer to the CONTRIBUTING.md.
Please ensure all code submissions and contributions adhere to the PyHC Coding Standards.
Project Structure
The rbamlib library is organized into Python packages, each serving as a module containing related functions.
Note, the library is design to allow direct function imports from the package without referencing specific pythong files (e.g., Python modules).
Core Components:
rbamlib/: Contains the main packages and sub-packages and modules of the library.Conversion Module (
conv/): Functions for various physical conversions.Motion Module (
motion/): Functions related to particle motion.Empirical Models (
models/): Implementations of empirical models, such as radial diffusion coefficients.Simulation Support Module (
sim/): Routine tasks for radiation bells models such as VERB code.Utilities (Various functions) (
utils/): Helper functions and utilities used across the library.Web (
web/): Functions for retrieving data from online sources.
Support for different planets [TBD]
The core components are designed to be ether universal or related to Earth’s radiation belts
The support for other planets must follow similar core components structure but withing a subpackage of specific planet (
rbamlib/jupiter)
Adding New Empirical Models:
When introducing a new empirical model, typically based on a specific research paper, follow these steps:
Create a New Module: Add a Python file in the appropriate sub-package (e.g.,
models/lpp), named after the authors and year of the paper (e.g.,CA1992.pyfor Carpenter and Anderson, 1992).Implement the Model: Define the model’s functions within this Python files (e.g., Python module).
Update sub-package
__init__.py:Include a reference to the corresponding paper, such as “Carpenter and Anderson (1992)”.
Import the new model to make it accessible at the package level:
from .CA1992 import CA1992
Documentation: Include documentation and updated corresponding .rst files located in
doc/folder.Testing: Implement testing for the new functionality in
tests/folder. Test-driven development approach is preferable.
Naming Conventions
Packages: Use lowercase letters (e.g.,
conv). The name of the packages and sub-packages should be designed to be short and relatable to the content. Use common definitions or abbreviations (e.g.,dllfor radial diffusion coefficent,lppfor plasmapause location)Functions based on papers: Use capital letter of the authors names and year of the paper (e.g.,
def CA1992()for Carpenter and Anderson, 1992). Use single capital letter for the paper with many authors (e.g.,def D2017()forDrozdov et al., (2017)). Note, the corresponding Python module (file) must have the same name. The supporting function that related to the same paper can be defined locally within the same file.Symbol: Refer to symbols.rst. Use uppercase only for commonly defined physical variables with capital letters, such as
Lfor L-shell,Kfor second adiabatic invariant orBfor magnetic field. Use lowercase for other commonly defined variables, such aspfor momentum. Use two or more lowercase letter for other variable to avoid ambiguity or common greek letter, such asmu- for first adiabatic invarianten- for energy, oral- for pitch angle or alpha.Convertion Functions: Use lowercase or uppercase without underscores for general variables to convert from, follow by
2and general variable to convert to (e.g.,Lal2Korenral2mu). If one of physical variable can be omitted as an input (e.g., common default values) it can be also omitted in the functions name. However, creating an additional alias is preferable in such case.Aliases: The corresponding alias can be created to use fewer variables in the name of the function (e.g.,
enral2muanden2muare equivalent), see example:def en2mu(en, r, al=np.pi/2, *B0): r""" Convert energy in MeV to first adiabatic invariant, mu, in MeV/G. See Also -------- enral2mu: Alias of the `enral2mu` function. """ return enral2mu(en, r, al, *B0)
Other Functions and Variables [TBD]: Use lowercase with underscores (e.g.,
drift_v). Similar to packages and sub-packages, the names should be designed to be short and relatable to the content. Use common definitions or abbreviations.Classes: Use CamelCase (e.g.,
RadiationModel).
Documentation
The NumPy/SciPy docstring style should be used. This format ensures automated documentation generation using Sphinx.
Docstring Style:
Docstrings: Use triple double-quoted strings for module, class, and function docstrings. Use
rstrings (denoted by a prefixrbefore the string:r""") to avoid conflicts with backslashes in equations.Summary: A brief description of the function’s purpose, with a references to relevant paper.
Parameters: A list of input parameters with their types, descriptions and units.
Returns: Details of the output, including type, description and units.
Math Expressions: Mathematical formulas rendered using LaTeX syntax.
See Also (optional): Add this section for related functions or in aliases.
Example:
def T(al):
r"""
Approximation of the integral function T related to the bounce period, derived in the dipole approximation.
Parameters
----------
al : float or ndarray
Equatorial pitch angle, in radians.
Returns
-------
float or ndarray
Value of T
Notes
-----
See Schulz & Lanzerotti :cite:p:`schulz:1974`.
.. math::
T( \\alpha ) \\approx T_0 - \\frac{1}{2}(T_0 - T_1) \\cdot \\left( \\sin( \\alpha ) + \\sin( \\alpha)^1/2 \\right)
"""
Guidelines:
Input and output: Use commonly defined physical variables names and units. Refer to symbols.rst. Always define units when applicable.
Mathematical Expressions: Use LaTeX syntax within the
.. math::directive to render equations properly in Sphinx-generated documentation in the Notes section.References: When implementing functions based on specific research papers, include citations using
:cite:p:`key`parenthetical or:cite:t:`key`contextual or:cite:yearpart:`key`year only format. All references are managed indocs/bibliography.bib.Aliases: For aliases, use only a one line summary (see example in Naming Conventions section). Add See Also section with the original function.
__init__.py: When describing the package or a sub-package, start with the name of the pacakge using', explanation of its name and what it provides. Add the description and list of the Main Features. In the sub-package, include list of models using a short reference to the papers.
Examples
Package:
"""
The 'conv' (conversion) provides tools for the conversion between various physical quantities pertinent to particle physics.
This module includes handling the system properties calculation and conversion such as
adiabatic invariant calculations and facilitating unit transformations in radiation belt studies.
Main Features:
- Calculation of adiabatic invariants.
- Energy and momentum conversion (en2pc, pc2en).
...
"""
Sub-package of the models:
"""
The `lpp` provides functionalities for calculating the plasmapause location.
Models:
- Carpenter and Anderson (1992)
- O’Brien and Moldwin (2003)
- Moldwin et al., (2002)
"""
Typical template for a function:
import numpy as np
def D2017():
r"""
Calculates the EMIC wave trigger following Drozdov et al. :cite:p:`drozdov:2017` model.
Parameters
----------
Returns
-------
Notes
-----
.. math::
"""
# Function code
return
Adding New References
When adding a new reference to the documentation:
Add to Bibliography Database: Add the BibTeX entry to
docs/bibliography.bib:@article{author:year, title = {{Paper Title}}, author = {First Author and Second Author}, year = 2020, journal = {Journal Name}, volume = 10, number = 5, pages = {100--110}, doi = {10.xxxx/xxxxx} }
Citation Key Format: Use
author:year(lowercase), e.g.,carpenter:1992. For multiple papers by the same author in the same year, add a keyword:gu:2012:correction.Cite in Docstring: Use
:cite:p:`key`,:cite:t:`key`or:cite:yearpar:`key`citations:Parenthetical: “:cite:p:`author:2020` “ → “[Author 2020]”
Textual: “following :cite:t:`author:2020` showed” → “following Author et al. [2020] showed”
Year only: “Author et al. :cite:yearpar:`author:2020` showed” → “Author et al. [2020] showed” Use year only style to emphasize the authors of the model directly in docstring.
Sphynx documentation
We utilize Sphinx to generate documentation. This allows the automatic inclusion of docstrings from the code and synchronize documentation with the source code.
Sphynx Documentation Organization
Location: The Sphinx documentation is organized in
docs/folder and mirrors the project’s core structure.Structure: Each sub-package is represented by corresponding reStructuredText (
.rst) file with the same name and additional folder for the next level structure.
Guidelines:
Update the Respective
.rstFile:Navigate to the appropriate
.rstfile within thedocs/directory that corresponds to your sub-package.If a corresponding
.rstfile does not exist (new sub-package), create one using the same name.
Add the Function to the
.rstFile:The
.rtsfile should start withcurrentmoduledirective of its sub-package:.. currentmodule:: rbamlib.your_package_name
Use the
automoduledirective to include description for new sub-packages. This will automatically pull docstring from__init__.py.Use the
automodsummdirective afterautomoduleto automatically generate a summary table of functions at the top of the page.Your Package Name ----------------- .. automodule:: rbamlib.your_package_name .. automodsumm:: rbamlib.your_package_name
Use the
autofunctiondirective for new functions. This will automatically pull in the function’s docstring.Use
.. rubric::directive for “Functions” headings to avoid creating TOC entries. Use:heading-level: 2to match the size of standard h2 headings... rubric:: Functions :heading-level: 2 .. autofunction:: new_function Aliases ======= .. autofunction:: new_function_alias
Note: Only “Functions” headings use
.. rubric::with:heading-level: 2. Section headings like “Aliases” continue to use the standard=======underline to appear in the TOC.
Update the Table of Contents:
If you’ve added a new
.rstfile (corresponding to your new sub-package), for example intomodelspackage, ensure it’s included in the project’s table of contents viatoctreedirective.Edit the corresponding
rstfile, for examplemodule.rst, to include a reference to your new fileyour_package_name.rst:.. currentmodule:: rbamlib.models Models ------ .. automodule:: rbamlib.models .. toctree:: :maxdepth: 1 :caption: Models models/dip models/your_package_name ...
If your sub-package has additional structure level, make sure that the corresponding table of content is also created.
Testing
We utilize Python’s built-in unittest framework for writing and executing tests.
Test Organization:
Location: All test modules are located in the
tests/directory.Structure: The
testsdirectory mirrors the structure of therbamlibpackage, however Python test files corresponding to each sub-package (module).
Writing Tests:
Naming Conventions:
Test Files: Name test files starting with
test_followed by the package name (e.g.,test_conv.py). The test should be located in the corresponding test sub-package mirroring the structure or the library (e.g.,tests/models/test_lpp.py).Test Classes: Name test classes with
Testfollowed by the package name but using CamelCase (e.g.,TestConv).Test Methods: Prefix test method names with
test_(e.g.,test_en2pc_single_value).
Content:
Methods: Inspect and extend
setUp()andteadDown()methods (if any) to prepare a common parameters. Inspect additional supporting methods that may be included in the class. Be consistent with the provided testing framework and workflow.Test: Call the function or method under test. The minimum test must include the value validation of the function’s expected output.
Assertions: Use
unittest’s assertion methods to verify expected outcomes or local existing test methods. Provide the error message of the unexpected test.
Examples of Test Class:
Example of the single value test with existing method:
import unittest
from rbamlib.conv import pc2en
class ConvTest(unittest.TestCase):
def setUp(self):
# Input values
self.in_float = 0.1
# Expected values
self.res_pc_float = 0.3350
def assertSingleValue(self, function, input, expected):
"""Assert function works for single float values"""
result = function(input)
self.assertIsInstance(result, float, "Result should be a float for single value input")
self.assertAlmostEqual(result, expected, places=4, msg="Single value output incorrect")
# Tests for en2pc
def test_en2pc_single_value(self):
self.assertSingleValue(en2pc, self.in_float, self.res_pc_float)
if __name__ == '__main__':
unittest.main()
Example of the array values assertion:
import unittest
import numpy as np
from rbamlib.models.lpp import CA1992
class TestModelsLpp(unittest.TestCase):
def test_CA1992_values(self):
"""Test the CA1992 function with known values."""
time = np.array([0, 1, 2]) # Example time in days
kp = np.array([1, 2, 3]) # Example Kp-index values
expected_output = np.array([5.14, 4.68, 4.22]) # Expected plasmapause locations
# Call the CA1992 function
result = CA1992(time, kp)
# Assert that the result is as expected
np.testing.assert_almost_equal(result, expected_output, decimal=2,
err_msg="CA1992 did not return expected values.")
if __name__ == '__main__':
unittest.main()
Running Tests:
To execute all tests, navigate to the project’s root directory and run:
python -m unittest discover tests
This command will discover and run all test files in the tests/ directory.
Licensing and Dependencies
License: This project is licensed under the BSD-3-Clause License.
Dependencies:
The project aims to maintain minimal dependencies, currently limited to
numpyandscipy.If additional dependencies are necessary, please discuss them by opening an issue before implementation.