Module: spacecraftChargingEquilibrium
Executive Summary
The spacecraftChargingEquilibrium module computes spacecraft equilibrium (floating) electric potentials in a plasma environment using two sequential 1-D root solves (by enforcing a zero net current condition on each spacecraft surface). The model includes ambient plasma electron/ion collection, photoelectron emission, and secondary electron emission (SEE), electron backscattering, and electron beam coupling (from one spacecraft to another).
The charging and electron-beam coupling equations used by this module follow Hammerl and Schaub’s coupled spacecraft charging model [Hammerl2024] and the references cited therein as well as the Dr. Lai’s book: “Fundamentals of Spacecraft Charging”. Additional background references on plasma interactions and modeling include [Hippler2001], [DavisMandell2016], [DraineSalpeter1979], and [RomeroCalvo2022].
A Python unit test script is provided to verify expected charging trends for a coupled two-spacecraft case (see User Guide).
Module Assumptions and Limitations
Conducting spacecraft and single potential: the theory used assumes each spacecraft is fully conducting and can be described by a single surface potential \(\phi\) (no differential charging).
Exactly two spacecraft are required: the coupled electron-beam charging logic is implemented for exactly two spacecraft and assumes:
spacecraft index
0is the servicerspacecraft index
1is the target
Plasma environment input required: the plasma flux/energy grid message must be connected. If not connected, the module logs an error in
Reset().Surface areas: the module uses a configurable default exposed area and a configurable sunlit-area fallback. Sunlit area can be provided per spacecraft through input messages or per-spacecraft defaults via setters.
Numerical solution: equilibrium potentials are found using a bisection root solver. The bracket range must contain the root for each current balance equation.
Message Connection Descriptions
The following table lists all module input and output messages. The module msg variable name is set by the user from Python. The msg type contains a link to the message structure definition, while the description explains how the message is used.
Msg Variable Name |
Msg Type |
Description |
|---|---|---|
plasmaFluxInMsg |
plasma environment input message containing the energy grid and particle flux distributions used to compute ambient electron/ion collection currents and yield-weighted emission currents |
|
scStateInMsgs |
vector of spacecraft state input messages; entries are appended via |
|
eBeamInMsgs |
vector of electron beam parameter input messages (optional). If a spacecraft’s beam message is linked, its gun parameters are read from the message and used in beam-coupled charging terms |
|
voltOutMsgs |
output vector of equilibrium spacecraft potentials (voltage), one per spacecraft |
|
currentsOutMsgs |
output vector of per-spacecraft current-component breakdowns used in the equilibrium evaluation (ambient, beam, and total terms) |
|
scSunlitAreaInMsgs |
vector of optional sunlit facet area input messages, one per spacecraft. If linked and written, this overrides the configured per-spacecraft default or fallback sunlit area used in charging calculations. |
Detailed Module Description
Overview: zero net current equilibrium
The module computes equilibrium potential(s) by requiring the total current to each spacecraft to be zero:
For the servicer/target problem with electron beam emission and impact, the total currents for the servicer and target are broken down into the terms in the following section and the equilibrium is solved by root-finding these current-balance equations numerically.
A note on implementation style
Hammerl (2024) presents closed-form expressions for Maxwellian environments (e.g., ambient collection currents) and also defines yield-weighted current forms that can be evaluated from an energy distribution. The Basilisk implementation uses the same energy-shifted flux structure but evaluates currents with:
linear interpolation of tabulated flux/yield curves
trapezoidal integration (
trapz()) over a specified energy interval
This approach is intended to mimic the behavior of the analytical model while allowing non-Maxwellian or numerically provided flux distributions.
Ambient collection and emission currents
Plasma electron collection current
The implementation evaluates the plasma electron collection current directly from the tabulated electron flux distribution \(F_e(E)\) using an energy-shifted integral. This is consistent with the flux-transform structure used in yield-weighted current expressions (Eq. (5) in [Hammerl2024]), but here it is applied to the collection current itself.
The current is computed as:
where \(A\) is the area exposed to plasma, \(q\) is the elementary charge, and \(F_e(E)\) is the electron flux distribution provided by plasmaFluxInMsg.
Numerical details (as implemented)
The integrand used in code is:
(E/(E - phi)) * getFlux(E - phi, "electron")
The integration is performed by trapezoidal quadrature (
trapz).The integration bounds are chosen to keep the shifted argument \((E-\phi)\) inside the tabulated energy range and to avoid evaluating too close to the singular point:
If \(\phi < 0\), bounds are approximately:
\[E_{\min} \approx 0.1,\quad E_{\max} \approx E_{\mathrm{grid,max}}\]If \(\phi \ge 0\), bounds are shifted upward:
\[E_{\min} \approx 0.1 + |\phi|,\quad E_{\max} \approx E_{\mathrm{grid,max}} + |\phi|\]
This numerical choice is designed to mimic the potential-shift behavior of the analytical model while remaining stable for arbitrary tabulated flux distributions.
Plasma ion collection current
The ion collection current is computed in the same numerical style using the tabulated ion flux distribution \(F_i(E)\):
Numerical details (as implemented)
The integrand used in code is:
(E/(E + phi)) * getFlux(E + phi, "ion")
The bounds are chosen so that \((E+\phi)\) remains within the tabulated range:
If \(\phi > 0\):
\[E_{\min} \approx 0.1,\quad E_{\max} \approx E_{\mathrm{grid,max}}\]If \(\phi \le 0\):
\[E_{\min} \approx 0.1 + |\phi|,\quad E_{\max} \approx E_{\mathrm{grid,max}} + |\phi|\]
These bounds mirror the logic in the implementation and help avoid evaluating near \(E+\phi=0\).
Secondary electron emission due to ambient electrons
SEE current due to ambient electrons is computed by weighting the electron flux by the electron SEE yield curve and applying the same energy-shift/Jacobian factor. This corresponds to the yield-weighting structure described in [Hammerl2024] (Eq. (5) and the related discussion), evaluated numerically:
For \(\phi>0\), the emitted secondaries can be recollected, and the implementation applies an exponential suppression factor consistent with the form used in Hammerl (Eq. (3) in [Hammerl2024]):
where secondaryElectronTemperature is the model parameter for secondary electron temperature.
Secondary electron emission due to ambient ions
The ion-driven SEE current is evaluated using the tabulated ion yield curve:
and uses the same \(\exp(-\phi/T_{\mathrm{SEE}})\) suppression for \(\phi>0\).
Electron backscattering current due to ambient electrons
Backscattering due to ambient electrons is computed using the backscatter yield curve with the same shifted-flux structure:
and applies \(\exp(-\phi/T_{\mathrm{BS}})\) suppression for \(\phi>0\), where
backscatterElectronTemperature is the corresponding model parameter.
Photoelectric current
The photoelectric current is implemented in a standard piecewise form (Eq. (10) in [Hammerl2024]), with the underlying functional form commonly referenced to spacecraft charging texts such as [Lai2011]:
In code, photoelectronFlux corresponds to \(j_{\mathrm{ph},0}\) and photoelectronTemperature corresponds to \(T_{\mathrm{ph}}\).
Electron beam coupling currents
Effective energy for beam-induced yields
Beam electrons arriving at the target use the effective (landing) energy defined by the potential difference (Eq. (8) in [Hammerl2024]):
This value is used when evaluating yield curves for beam-induced SEE and backscattering.
Electron beam current on the target
The target beam current follows Hammerl’s reachability condition and exponential turn-on (Eq. (11) in [Hammerl2024]):
where \(\alpha\) is modeled as a geometry factor representing the fraction of Ebeam current reaching the target (provided as alphaEB in the message payload, constrained to \([0,1]\) at runtime), and \(T_{\mathrm{EB}}\) is represented by the model parameter beamElectronTemperature.
Electron beam current on the servicer
The servicer beam current uses the corresponding form (Eq. (13) in [Hammerl2024]):
Beam-induced SEE and backscattering on the target
Beam-induced SEE and backscattering are implemented by evaluating the yield at \(E_{\mathrm{eff}}\) and scaling by the magnitude of incoming target beam current:
- Implementation note:
Hammerl (2024) includes a recollection factor for emitted secondaries for \(\phi_T \ge 0\) (see Eq. (12) in [Hammerl2024]). The current implementation uses yield-scaling directly; adding an explicit \(\exp(-\phi_T/T_{\mathrm{SEE}})\) factor is straightforward future work if tighter agreement is required.
Servicer/target equilibrium solve workflow
The implementation uses a servicer-first sequential solve that follows the modeling workflow used in this module:
The implementation solves these sequentially:
Solve the servicer equilibrium potential \(\phi_S\) by finding the root of the servicer total-current function, using \(\phi_{T,\mathrm{ref}} = 0\,\mathrm{V}\) in the servicer beam term.
Fix \(\phi_S\) and solve the target equilibrium potential \(\phi_T\) by finding the root of the target total-current function.
Publish the resulting potentials to
voltOutMsgs.
This sequential approach keeps the servicer and target solves internally consistent with the implemented current expressions, while the specific numerical details (integration bounds, discretization, and bracketing choices) are selected to be stable for tabulated flux inputs.
Numerical utilities
Linear interpolation (interp)
Fluxes and yields are evaluated at arbitrary energies using linear interpolation over the tabulated energy grid. Negative values resulting from extrapolation are clamped to zero in getFlux() and getYield() to avoid nonphysical contributions.
Trapezoidal integration (trapz)
Current integrals are evaluated using trapezoidal quadrature:
The implementation includes checks for finite bounds and non-degenerate intervals.
User Guide
Creating and adding the module
from Basilisk.simulation import spacecraftChargingEquilibrium
from Basilisk.utilities import SimulationBaseClass, macros
sim = SimulationBaseClass.SimBaseClass()
proc = sim.CreateNewProcess("proc")
task = sim.CreateNewTask("task", macros.sec2nano(1.0))
proc.addTask(task)
chg = spacecraftChargingEquilibrium.SpacecraftChargingEquilibrium()
chg.ModelTag = "spacecraftChargingEquilibrium"
sim.AddModelToTask(task.Name, chg)
Connecting the plasma environment
chg.plasmaFluxInMsg.subscribeTo(plasmaMsg)
Adding spacecraft (servicer first, target second)
The coupled-beam logic assumes:
scStateInMsgs[0]is the servicerscStateInMsgs[1]is the target
chg.addSpacecraft(scServicer.scStateOutMsg) # index 0
chg.addSpacecraft(scTarget.scStateOutMsg) # index 1
Connecting electron beam parameters
Create and publish an ElectronBeamMsgPayload and connect it to the desired spacecraft index. Most coupled runs connect the beam parameters on the servicer (index 0):
from Basilisk.architecture import messaging
beamPayload = messaging.ElectronBeamMsgPayload()
beamPayload.energyEB = 20000.0 # [eV]
beamPayload.currentEB = 1.0e-6 # [A]
beamPayload.alphaEB = 1.0 # fraction of Ebeam current reaching the target
beamMsg = messaging.ElectronBeamMsg().write(beamPayload)
chg.eBeamInMsgs[0].subscribeTo(beamMsg)
If the beam message is not linked, the spacecraft is treated as not emitting an electron beam.
Configuring optional model parameters
The module provides setters for configurable defaults and numerical settings:
Default construction values
The table below summarizes the main defaults used at construction time.
Quantity |
Default |
How to change |
|---|---|---|
|
|
|
Surface area used in current equations |
|
|
Sunlit-area fallback |
|
|
Per-spacecraft sunlit-area defaults |
unset (NaN sentinel) |
|
Root solve lower/upper bracket |
|
|
Servicer solve target reference potential |
|
fixed internal default |
Bisection tolerance (servicer / target) |
|
fixed internal defaults |
Trapezoidal integration bins |
|
|
Minimum integration energy |
|
fixed internal default |
Photoelectron flux |
|
|
Photoelectron temperature |
|
|
Secondary-electron temperature |
|
|
Backscatter-electron temperature |
|
|
Beam-electron temperature |
|
|
Electron gun defaults (per spacecraft) |
|
via linked |
Yield tables (electron, ion, backscatter) |
empty vectors at construction |
|
# Optional per-spacecraft sunlit-area defaults [m^2]
chg.setSunlitAreaDefault(0, 12.0)
chg.setSunlitAreaDefault(1, 10.0)
# Optional fallback used only if no message and no per-spacecraft default is provided
chg.setSunlitAreaFallback(0.0)
# Optional bisection brackets [V]
chg.setRootSolveBounds(-4.0e5, 4.0e5)
# Optional environmental model constants (defaults shown)
chg.setPhotoelectronFlux(40e-6)
chg.setPhotoelectronTemperature(2.0)
chg.setSecondaryElectronTemperature(5.0)
chg.setBackscatterElectronTemperature(5.0)
chg.setBeamElectronTemperature(20.0)
# Optional trapezoidal integration resolution
chg.setTrapzBins(1000)
Logging output voltages
servicerVoltRec = chg.voltOutMsgs[0].recorder()
targetVoltRec = chg.voltOutMsgs[1].recorder()
sim.AddModelToTask(task.Name, servicerVoltRec)
sim.AddModelToTask(task.Name, targetVoltRec)
Logging output current components
servicerCurrRec = chg.currentsOutMsgs[0].recorder()
targetCurrRec = chg.currentsOutMsgs[1].recorder()
sim.AddModelToTask(task.Name, servicerCurrRec)
sim.AddModelToTask(task.Name, targetCurrRec)
Running the simulation
sim.InitializeSimulation()
sim.ExecuteSimulation()
References
Hammerl, J., and Schaub, H., “Coupled Spacecraft Charging Due to Continuous Electron Beam Emission and Impact,” 2024. (Equation numbers referenced in this document correspond to that paper.)
Lai, S. T., Fundamentals of Spacecraft Charging, Princeton Univ. Press, Princeton, NJ, 2011.
Hippler, R., Pfau, S., Schmidt, M., and Schoenbach, K. H., Low Temperature Plasma Physics: Fundamental Aspects and Applications, Wiley, Hoboken, NJ, 2001.
Davis, V. A., and Mandell, M. J., “NASCAP-2K Version 4.3 Scientific Documentation,” AFRL-RV-PS-TR-2017-0001, 2016.
Draine, B. T., and Salpeter, E. E., “On the Physics of Dust Grains in Hot Gas,” Astrophysical Journal, Vol. 231, 1979, pp. 77–94.
Romero-Calvo, Á., Cano-Gómez, G., and Schaub, H., “Simulation and Uncertainty Quantification of Electron Beams in Active Spacecraft Charging Scenarios,” 2022.
-
class SpacecraftChargingEquilibrium : public SysModel
- #include <spacecraftChargingEquilibrium.h>
This module computes coupled equilibrium electric potentials for exactly two spacecraft (servicer index 0 and target index 1).
Public Functions
-
SpacecraftChargingEquilibrium()
Constructor.
This is the constructor for the module class.
-
~SpacecraftChargingEquilibrium()
Destructor.
Module Destructor
-
void Reset(uint64_t CurrentSimNanos) override
Reset module state and validate required inputs.
This method is used to reset the module and checks that required input messages are connected.
- Parameters:
CurrentSimNanos – current simulation time in nano-seconds
-
void UpdateState(uint64_t CurrentSimNanos) override
Update module outputs by solving coupled equilibrium potentials.
This method updates the state of the module
- Parameters:
CurrentSimNanos – current simulation time in nano-seconds
-
void addSpacecraft(Message<SCStatesMsgPayload> *tmpScMsg)
Add one spacecraft state input. Exactly two spacecraft are required.
This method adds a spacecraft to the charging model. Exactly two spacecraft can be added: index 0 = servicer, index 1 = target.
- Parameters:
tmpScMsg – spacecraft state message (index 0: servicer, index 1: target)
-
void setEnableDebugPrints(bool enabled)
Enable or disable verbose debug prints in UpdateState().
-
bool getEnableDebugPrints() const
Return whether verbose debug prints are enabled.
-
void setYieldSEEelectron(const Eigen::VectorXd &yieldVector)
Set electron-impact SEE yield table aligned with the plasma energy grid.
-
void setYieldSEEion(const Eigen::VectorXd &yieldVector)
Set ion-impact SEE yield table aligned with the plasma energy grid.
-
void setYieldBackscattered(const Eigen::VectorXd &yieldVector)
Set backscatter yield table aligned with the plasma energy grid.
-
void setSunlitAreaDefault(unsigned int spacecraftIndex, double sunlitArea)
Set optional default sunlit area [m^2] for one spacecraft index.
-
double getSunlitAreaDefault(unsigned int spacecraftIndex) const
Get optional default sunlit area [m^2] for one spacecraft index.
-
void clearSunlitAreaDefault(unsigned int spacecraftIndex)
Clear optional default sunlit area for one spacecraft index.
-
void setSunlitAreaFallback(double sunlitArea)
Set fallback sunlit area [m^2] used when message/default are not available.
-
double getSunlitAreaFallback() const
Get fallback sunlit area [m^2].
-
void setRootSolveBounds(double lowerBound, double upperBound)
Set lower/upper [V] root-bracket bounds used by bisection solves.
-
double getRootSolveLowerBound() const
Get lower [V] root-bracket bound used by bisection solves.
-
double getRootSolveUpperBound() const
Get upper [V] root-bracket bound used by bisection solves.
-
void setSurfaceAreaDefault(double area)
Set default plasma-exposed spacecraft area [m^2].
-
double getSurfaceAreaDefault() const
Get default plasma-exposed spacecraft area [m^2].
-
void setPhotoelectronFlux(double photoelectronFluxIn)
Set nominal photoelectron flux [A/m^2].
-
double getPhotoelectronFlux() const
Get nominal photoelectron flux [A/m^2].
-
void setPhotoelectronTemperature(double photoelectronTemperatureIn)
Set photoelectron characteristic energy [eV].
-
double getPhotoelectronTemperature() const
Get photoelectron characteristic energy [eV].
-
void setSecondaryElectronTemperature(double secondaryElectronTemperatureIn)
Set secondary-electron characteristic energy [eV].
-
double getSecondaryElectronTemperature() const
Get secondary-electron characteristic energy [eV].
-
void setBackscatterElectronTemperature(double backscatterElectronTemperatureIn)
Set backscatter-electron characteristic energy [eV].
-
double getBackscatterElectronTemperature() const
Get backscatter-electron characteristic energy [eV].
-
void setBeamElectronTemperature(double beamElectronTemperatureIn)
Set beam electron characteristic energy [eV] for exponential turn-on.
-
double getBeamElectronTemperature() const
Get beam electron characteristic energy [eV] for exponential turn-on.
-
void setTrapzBins(int trapzBinsIn)
Set number of trapezoids used by numerical integration.
-
int getTrapzBins() const
Get number of trapezoids used by numerical integration.
Public Members
-
std::vector<ReadFunctor<SCStatesMsgPayload>> scStateInMsgs
spacecraft state input messages
-
ReadFunctor<PlasmaFluxMsgPayload> plasmaFluxInMsg
plasma flux input message
-
std::vector<ReadFunctor<ElectronBeamMsgPayload>> eBeamInMsgs
optional electron beam parameters
-
std::vector<ReadFunctor<ScSunlitFacetAreaMsgPayload>> scSunlitAreaInMsgs
optional sunlit area input messages
-
std::vector<Message<VoltMsgPayload>*> voltOutMsgs
spacecraft equilibrium potential output messages
-
std::vector<Message<ScChargingCurrentsMsgPayload>*> currentsOutMsgs
spacecraft current-term output messages
-
BSKLogger bskLogger
Basilisk logging interface.
Private Functions
-
void readMessages()
Read in the input messages
- Returns:
void
-
bool validateSolveConfiguration()
-
double electronCurrent(double phi, double A)
This function takes in a given potential and area value and calculates the electron current
- Parameters:
phi – double defining value for spacecraft potential
A – double defining value for area exposed to plasma [m^2]
- Returns:
double
-
double ionCurrent(double phi, double A)
This function takes in a given potential and area value and calculates the ion current
- Parameters:
phi – double defining value for spacecraft potential
A – double defining value for area exposed to plasma [m^2]
- Returns:
double
-
double SEEelectronCurrent(double phi, double A)
This function takes in a given potential and area value and calculates the SEE current due to electrons
- Parameters:
phi – double defining value for spacecraft potential
A – double defining value for area exposed to plasma [m^2]
- Returns:
double
-
double SEEionCurrent(double phi, double A)
This function takes in a given potential and area value and calculates the SEE current due to ions
- Parameters:
phi – double defining value for spacecraft potential
A – double defining value for area exposed to plasma [m^2]
- Returns:
double
-
double backscatteringCurrent(double phi, double A)
This function takes in a given potential and area value and calculates the SEE current due to backscattering
- Parameters:
phi – double defining value for spacecraft potential
A – double defining value for area exposed to plasma [m^2]
- Returns:
double
-
double photoelectricCurrent(double phi, double A)
This function takes in a given potential and area value and calculates the current due to the photoelectric effect
- Parameters:
phi – double defining value for spacecraft potential
A – double defining value for area exposed to plasma
- Returns:
double
-
double electronBeamCurrent(double phiS, double phiT, const std::string &craftType, double EEB, double IEB, double alphaEB)
-
double SEEelectronBeamCurrent(double phiS, double phiT, double EEB, double IEB, double alphaEB)
-
double electronBeamBackscattering(double phiS, double phiT, double EEB, double IEB, double alphaEB)
-
double interp(Eigen::VectorXd &xVector, Eigen::VectorXd &yVector, double x)
This function takes in a given set of discrete data points and an x-value and performs linear interpolation to compute the corresponding y-value.
- Parameters:
xVector – vector containing the independent variable data points
yVector – vector containing the dependent variable data points
x – x-value at which the data is linearly interpolated
- Returns:
interpolated y-value
-
double trapz(std::function<double(double)> &f, double a, double b, int N)
This function computes the integral of the passed function using trapezoidal integration
- Parameters:
f – function to compute the integral of
a – lower limit of integration
b – upper limit of integration
N – number of trapezoids to use
- Returns:
double
-
double getFlux(double E, const std::string &particleType)
This function returns the flux type for a given energy and impacting particle type
- Parameters:
E – energy of interest [eV]
particleType – particle of interest (“electron” or “ion”)
- Returns:
double
-
double getYield(double E, const std::string &yieldType)
This function returns the yield for a given energy and yield type
- Parameters:
E – energy of interest [eV]
yieldType – yield of interest (“electron”, “ion”, “backscattered”)
- Returns:
double
Private Members
-
unsigned int numSat = 0
number of spacecraft configured through addSpacecraft()
-
std::vector<double> scSunlitAreaDefaults
[m^2] optional per-spacecraft sunlit-area defaults
-
std::vector<bool> scSunlitAreaWarned
warn-once latch for missing sunlit area
-
bool enableDebugPrints = false
toggles verbose UpdateState diagnostics
-
double elementaryCharge = 1.60217663e-19
[C] elementary charge
-
int trapzBins = 1000
[-] trapezoids used by trapz integration
-
double minIntegrationEnergy = 0.1
[eV] lower baseline bound used in current integrals
-
double photoelectronFlux = 40e-6
[A/m^2] photoelectron current density at non-positive potential
-
double photoelectronTemperature = 2.0
[eV] photoelectron characteristic energy
-
double secondaryElectronTemperature = 5.0
[eV] secondary-electron emission characteristic energy
-
double backscatterElectronTemperature = 5.0
[eV] backscatter emission characteristic energy
-
double beamElectronTemperature = 20.0
[eV] beam turn-on characteristic energy
-
double defaultSurfaceArea = 50.264
[m^2] default spacecraft plasma-exposed area
-
double sunlitAreaFallback = 0.0
[m^2] fallback sunlit area when neither message nor default exists
-
double rootSolveLowerBound = -400000.0
[V] lower bracket for bisection equilibrium solve
-
double rootSolveUpperBound = 400000.0
[V] upper bracket for bisection equilibrium solve
-
double servicerSolveAccuracy = 1e-6
[V] bisection tolerance for servicer equilibrium
-
double targetSolveAccuracy = 1e-8
[V] bisection tolerance for target equilibrium
-
struct ElectronGunConfig
-
struct ChargingSpacecraftState
Public Members
-
double A = 0.0
[m^2] plasma-exposed area
-
double A_sunlit = 0.0
[m^2] sunlit area used for photoelectric current
-
bool emitsEB = false
true if e-beam message is linked for this spacecraft
-
ElectronGunConfig electronGun
e-beam configuration for this spacecraft
-
double A = 0.0
-
SpacecraftChargingEquilibrium()