2024 - 2026 Simulation Software
Code Architecture:
Backend:
Tech Stack:
Python 3.12.x
Numpy
For faster array manipulation
Pandas
csv handling
SciPy
Interpolate the points
Design Considerations:
Architecture and Modularity
Separation
core/ (physics + MPPT)
simulators/ (time loop)
ui/ (desktop GUI)
data/ (models, profiles)
Clear Interfaces
Config first
single config.yaml / toml for model params, profiles, algos, plotting cadence
immutable at runtime
runtime overrides via CLI flags
Real-Time Behavior
clocking
fixed timestep engine
option to use real-time mode (wall clock) vs fast-forward (sim time)
threading
UI thread separate from sim
threads need to be safe
no blocking disk I/O on UI thread
decouple sim tick from plot refresh
queue telemetry frames to the UI thread
while sliders drag, coarsen step_v (use larger v increments)
smoother refresh
once idle, auto refine and recompute MPP
UI/UX
controls
sliders for G/T, optional wind/shading toggles
the goals is to make it interactive
plot design
IV+PV with current Vref marker
time series of (G, T, V, I, P)
power vs true Pmax
Legends + units
Efficiency Panes
Presets
Preset scenarios?
make it easy to use common scenarios
keyboard shortcuts?
persist UI state between runs?
Data & Reproducibility
File formats: CSV/Parquet
per tick telemetry
run_id, t, Vref, V, I, P, G, Tc, Voc, Isc, Pmax_theory, algo, mode, explore_active
Source-grid
V, G, Tc, I + metadata: STC parameters, grid sizes, interpolation method, git SHA, created_at.
Source grid exists because solving the single-diode equation every sim tick is expensive
so precompute the IV relationship for a grid of points:
Voltage (V) : 0 → Voc, in small steps (e.g. 200 points)
Irradiance (G) : 0 → ~1200 W m⁻², several levels
Cell temperature (Tc) : –10 → 70 °C, several levels
store those currents I(V, G, Tc) in a 3D array (the source-grid)
at run time, when MPPT algo asks for current at conditions, sim uses a tri-linear interpolation inside that grid to return the current
its a quick look up
Profiles
{t, G, Tc[, load]} with opeartional per-module/per-substring irradiance maps
seed for stochastic algos
autosave summary metrics + plots per run into timestamped folder
option to export MPPT traces?
Engine and Messaging
fixed timestep: default 100 hz? (0.01s)
threading: sim loop runs in its own thread; UI refresh decoupled
2 way messaging:
Engine → UI example:
{
"t": 2.35,
"V": 27.8, "I": 3.1, "P": 86.2,
"Vref": 28.0,
"G": 900, "Tc": 31.2,
"Pmax_theory": 92.7,
"algo": "pand_o",
"mode": "exploit",
"explore_active": false
}
UI → Engine example:
{ "cmd": "set", "path": "mppt.algo", "value": "hybrid_gs" }
{ "cmd": "pause", "value": true }
{ "cmd": "set", "path": "profile.G", "value": 750 }
msgs delivered through Qt signals/slots or a thread-safe queue
preference toward Qt
User Interface Scope
initial screens/widgets
IV/PV panel: live curves, Vref marker, optional explore samples
Time-series panel: V, I, P, G, Tc over time
Power vs P_max: MPPT output vs theoretical maximum
Efficiency Pane: instantaneous and rolling tracking efficiency
Controls:
irradiance and temp sliders
algo selector
start/stop buttons
Logging, Errors & Observability
structured logs with run_id, git_sha, seed, tick_ms, cache_hits
log levels: DEBUG, INFO, WARN, ERROR
On screen HUD: tick time, UI FPS, explore duty cycle
UI toasts for schema validation errors or out of bounds commands
Validation & Testing
Golden STC (Standard Test Conditions) tests: Vmpp, Impp, Voc, Isc within set tolerances.
simulated array should produce values close to known datasheet numbers
need to set a tolerance for this
Property tests: I(V=0)=Isc, V(I=0)=Voc, Pmax increases with G, Voc decreases with Tc.
make sure these relationships must always hold
Partial shading scenarios: multi-peak PV curve; Hybrid GS must outperform P&O.
make sure it can handle multiple peaks
Regression suite: locks expected metrics for each canned scenario.
Other Notes
Other teams should be able to use our functions
TrackSim needs to be able to input irradiance to model shading
deprioritize integration with other teams for now
Maintainability
When we add the aeroshell angle simulator, we should be able to reuse our functions without needing to change much
Array should be easy to build
We separate objects to Cell, Module, String
Dynamic environments with changing irradiance and temperature should be modeled
Read array value from user input so real-time changes could be reflected
array > queue for history
no point of queue if we will not exceed memory
User should be able to input a .txt file of cycles and then parse to simulate dynamic environments
User should be able to step through or auto-run dynamic environments
Environment should be a little noisy with bumps to replicate real curves
Power Curve Simulation:
Overview:
Cell → Substring → String → Array
Cell: basic photovoltaic unit, modeled with a single-diode equation.
Substring: series of cells, optionally grouped with bypass diodes.
String: series of modules, raising voltage for inverter input.
Array: parallel of strings, raising current for total output power.
Notes:
All objects should have the option of Ideal or Non-Ideal
Calculations will be different depending on state
keep Substring as first-class with bypass diode logic; precompute per-substring IV, then compose at Module/String. This keeps multi-peak P-V natural.
1. Cell (single-diode model)
Constants and Helpers:
Parameters (at STC, 1000 W/m², 25 °C):
These should either be imported from measuring the individual cell and input those parameters or just make them all the same. Some could be found from the sheet for ideal factors.
Isc_ref [A]: short-circuit current
Voc_ref [V]: open-circuit voltage
n [–]: diode ideality factor (≈ 1–2)
Rs [Ω]: series resistance
Rsh [Ω]: shunt (parallel) resistance
Temperature coefficients:
alpha_Isc [A/°C]: temperature coefficient of Isc
beta_Voc [V/°C]: temperature coefficient of Voc
Environment (runtime inputs):
G [W/m²]: irradiance on the cell
T_c [°C]: cell temperature
Numerical controls:
tol: solver tolerance
max_iter: max iterations for Newton/bisection
Functions:
set_conditions(G, T_c): update irradiance, temperature, and other values → None
We can consider calculating true irradiance from the angle that the sun hits the solar cell later.
i_of_v(V): current at a given voltage (single-diode model) → float
Here we are trying to solve the I (current) at a specific voltage according to the equation. We use the newton equation in order to get better estimates on what the correct I should be.
Later we can upgrade to a two diode equation which uses I1 and I2 and is more accurate but takes longer computation time.
v_at_i(I): module voltage at current I (sum of cell voltages at I) → float
This is the same equation as the one to solve for i_at_v but for solving voltage given current
voc(): open-circuit voltage (where I = 0) → float
iv_curve(points): sweep voltages, compute currents → List
solve i_of_v(V) for alot of points and return (V,I,P = VI)
mpp(): return Vmpp, Impp, Pmpp, Voc, Isc
2. Substring (cells in series, optional bypass)
Composition:
Contains a list of Cell objects.
Each module 6-8 cells has 1 bypass diode
Bypass Diode is a separate class
Rules:
Series wiring → current is common, voltages add.
Bypass diodes allow shaded substrings to drop out when reverse biased.
Functions:
set_conditions(G, T_c): broadcast to all cells → None
isc(): module short-circuit current (≈ min cell Isc) → float
we can upgrade to this equation later, but for now we can stick with min cell
voc(): module open-circuit voltage (sum of cell Voc) → float
v_at_i(I):
Call each cell’s v_at_i(I) to get the sum of all voltages
We then also take BypassDiode’s equation for finding Vbypass and take the max of both
iv_curve(points): sweep current 0 → Isc, compute V(I) → float
mpp(): return Vmpp, Impp, Pmpp, Voc, Isc
2.1 BypassDiode (helper class for substring)
Composition:
Contains a helper class for calculating bypass diode values
Rules:
none
Functions:
set_temperature(T_c)
v_at_i(I)
diode terminal voltage at current I
dv_dI(I)
slope of diode voltage curve, useful for newton solvers
activation_condition(Vcells, I)
check if the diode turns on if cells would go more negative than the bypass clamp
Clamp(Vcells, I)
take whatever voltage is greater
3. String (substrings in series)
Composition:
Contains a list of Module objects.
Rules:
Series wiring → same current, voltages add.
Functions:
set_conditions(G, T_c): broadcast to all modules → None
isc(): string short-circuit current (≈ min module Isc) → Float
voc(): string open-circuit voltage (sum of module Voc) → Float
v_at_i(I): sum of module voltages at current I → Float
We can just sum all of the voltages since we calculated it earlier
iv_curve(points): sweep current, compute V(I) → List
mpp(): return Vmpp, Impp, Pmpp, Voc, Isc
4. Array (strings in parallel)
Composition:
Contains a list of String objects.
Rules:
Parallel wiring → same voltage across strings, currents add.
Optional: account for bus/combiner resistance R_bus.
Functions:
set_conditions(G, T_c): broadcast to all strings
voc(): array open-circuit voltage (≈ max of string Voc)
isc(): array short-circuit current (sum of string Isc)
i_at_v(V): sum of string currents at a common voltage V
We can just sum all of the currents at V for the strings
iv_curve(points): sweep voltage, sum currents
mpp(): Vmpp, Impp, Pmpp, Voc, Isc
5. Caching for Performance
Each object (Cell, Module, String, Array) can cache its IV curve for the current conditions.
Cache should be invalidated when set_conditions() is called.
Higher-level objects reuse cached IVs from lower levels, speeding up array simulations.
6. Threading/Parallel Programming for Performance
Not exactly sure how fast the IV curve is calculated using newton method, but the more data points we get or times we need to recalculate points we might benefit from parallel computing.
MPPT Algorithms:
Should be used after curve is generated
Algorithms:
Perturb and Observe
condition: stable sunlight, simple
idea: nudge v_ref +/- change in V
if P went up, keep direction, else flip
Incremental Conductance
condition: better slope math, simple
idea: uses dI/dV about = -I/V at MPP
moves voltage based on sign of dP/dV without dithering
Hybrid Global Sampling
condition: partial shading/multi peaks
idea: short bursts of samples across V subset {V_min, V_max} using Sobol/Halton or stratified randomization
pick highest P, then hand it off to P&O/IC
each algo defines param defaults, update equations, filters/EMA, power-drop triggers, and bounds
Saftey considerations:
slew-rate on Vref, min/max Vref bounds, dwell/averaging per sample
Continuous Update Design:
small periodic probes, recency-weighted PV map triggers
Evaluation:
standardized metrics
instant and mean efficiency
P/P_max * 100%
%time within X% of GMPP
settiling time
overshoot
energy yield
Algorithm Tuner:
Should be able to run simulations using the algorithm and tune hyperparameters
small gird/random search over stride, dwell, and explore cadence
persist results in a timestamped runs/ dir with metrics CSV + plots
Hardware Simulator (DC-DC Converter):
ideal DC-DC converter that instantly presents commanded V_ref
hooks for future non-ideal features
efficiency map η(V,I)
first order dynamic response (time constant)
switching ripple for ripple-correlation control
Frontend:
Tech Stack:
ReactJS or TSX
Best features for frontend UI
Ideal for maintaining
PyQt6 + Qt WebEngine + Plotly
interactive GUI
Qt WebEngine is needed to embed Plotly into a desktop app
Plotly Dash for web based version
Design Considerations:
Users should be able to create cells, modules, strings, and arrays easily through an interactive UI
Users should be able to set cell irradiance and temperature
Future aeroshell simulation integration with putting cells on the curvature of the solar car
Flowchart:
High Level:
Frontend setup (irradiance, temperature, array setup, …) → calculate cells → calculate modules → calculate strings → calculate array → return List or object containing all points from graph → plot on graph → export data somewhere to reuse?
Repository Structure:
Power-Generation-Eclipse-SW/
│
├── README.md # High-level overview, quickstart instructions
├── pyproject.toml / setup.cfg
│ # (optional) for packaging if you want pip install -e .
├── requirements.txt # Python dependencies
├── LICENSE
│
├── eclipse/ # Python package (all importable modules live here)
│ ├── init.py
│ ││ ├── core/ # Shared simulation logic│ │ ├── cell.py # Single-diode PV cell model