pymoo GA: Fit non ideal diode equation to dark JV-curves
This notebook was written to fit dark JV-curves with the non-ideal diode equation below:
For dark:
\[J = J_0\left[\exp\left(-\frac{V-R_s J}{n k_b T}\right)-1\right] + \frac{V-R_s J}{R_{sh}}\]
using the solving method decribed in Solid-State Electronics 44 (2000) 1861-1864
[1]:
# Import necessary libraries
import warnings, os, sys, shutil
# remove warnings from the output
os.environ["PYTHONWARNINGS"] = "ignore"
warnings.filterwarnings(action='ignore', category=FutureWarning)
warnings.filterwarnings(action='ignore', category=UserWarning)
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from copy import deepcopy
import torch, copy, uuid
# Ax on the desktop
sys.path.append('../ax') # add the path to the ax module
import ax, logging
from ax.utils.notebook.plotting import init_notebook_plotting, render
init_notebook_plotting() # for Jupyter notebooks
try:
from optimpv import *
from optimpv.pymooOpti.pymooOptimizer import PymooOptimizer
from optimpv.Diodefits.DiodeAgent import DiodeAgent
from optimpv.Diodefits.DiodeModel import *
except Exception as e:
sys.path.append('../') # add the path to the optimpv module
from optimpv import *
from optimpv.pymooOpti.pymooOptimizer import PymooOptimizer
from optimpv.Diodefits.DiodeAgent import DiodeAgent
from optimpv.Diodefits.DiodeModel import *
[INFO 08-13 16:55:05] ax.utils.notebook.plotting: Injecting Plotly library into cell. Do not overwrite or delete cell.
[INFO 08-13 16:55:05] ax.utils.notebook.plotting: Please see
(https://ax.dev/tutorials/visualizations.html#Fix-for-plots-that-are-not-rendering)
if visualizations are not rendering.
Define the parameters for the simulation
[2]:
params = []
J0 = FitParam(name = 'J0', value = 1e-5, bounds = [1e-6,1e-3], log_scale = True, rescale = False, value_type = 'float', type='range', display_name=r'$J_0$', unit='A m$^{-2}$', axis_type = 'log',force_log=True)
params.append(J0)
n = FitParam(name = 'n', value = 1.5, bounds = [1,2], log_scale = False, value_type = 'float', type='range', display_name=r'$n$', unit='', axis_type = 'linear')
params.append(n)
R_series = FitParam(name = 'R_series', value = 1e-4, bounds = [1e-5,1e-3], log_scale = True, rescale = False, value_type = 'float', type='range', display_name=r'$R_{\text{series}}$', unit=r'$\Omega$ m$^2$', axis_type = 'log',force_log=True)
params.append(R_series)
R_shunt = FitParam(name = 'R_shunt', value = 1e-1, bounds = [1e-2,1e2], log_scale = True, rescale = False, value_type = 'float', type='range', display_name=r'$R_{\text{shunt}}$', unit=r'$\Omega$ m$^2$', axis_type = 'log',force_log=True)
params.append(R_shunt)
# original values
params_orig = copy.deepcopy(params)
Generate some fake data for the dark JV
[3]:
# Create JV to fit
X = np.linspace(0.001,1,100)
y = NonIdealDiode_dark(X, J0.value, n.value, R_series.value, R_shunt.value)
plt.figure(figsize=(10,6))
plt.semilogy(X,y)
plt.xlabel('Voltage [V]')
plt.ylabel('Current density [A m$^{-2}$]')
plt.grid()
plt.show()

Run the optimization
While very inefficient we first try to fit the data using Bayesian optimization, then we use scipy.optimize.minize
to demonstrate the difference in speed and accuracy.
[4]:
# Define the Agent and the target metric/loss function
metric = 'mse' # can be 'nrmse', 'mse', 'mae'
loss = 'soft_l1' # can be 'linear', 'huber', 'soft_l1'
exp_format = 'dark' # can be 'dark', 'light' depending on the type of data you have
use_pvlib = False # if True, use pvlib to calculate the diode model if not use the implementation in DiodeModel.py
diode = DiodeAgent(params, X, y, metric = metric, loss = loss, minimize=True,exp_format=exp_format,use_pvlib=use_pvlib,compare_type='log')
[5]:
# Create PymooOptimizer for single-objective optimization
optimizer = PymooOptimizer(params=params, agents=[diode], algorithm='GA', pop_size=20, n_gen=100, name='pymoo_single_obj', verbose_logging=True)
[6]:
optimizer.optimize()
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Starting optimization using GA algorithm
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Population size: 20, Generations: 100
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 1: Best objective = 0.064137
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 2: Best objective = 0.049712
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 3: Best objective = 0.049712
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 4: Best objective = 0.022419
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 5: Best objective = 0.019096
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 6: Best objective = 0.012562
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 7: Best objective = 0.012562
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 8: Best objective = 0.006527
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 9: Best objective = 0.003254
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 10: Best objective = 0.001240
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 11: Best objective = 0.001149
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 12: Best objective = 0.000512
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 13: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 14: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 15: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 16: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 17: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 18: Best objective = 0.000511
=================================================
n_gen | n_eval | f_avg | f_min
=================================================
1 | 20 | 0.6527036514 | 0.0641368574
2 | 40 | 0.2416962186 | 0.0497119606
3 | 60 | 0.1190201186 | 0.0497119606
4 | 80 | 0.0725129754 | 0.0224194148
5 | 100 | 0.0502671678 | 0.0190955640
6 | 120 | 0.0352498785 | 0.0125624438
7 | 140 | 0.0221394043 | 0.0125624438
8 | 160 | 0.0154107468 | 0.0065272506
9 | 180 | 0.0103262709 | 0.0032539460
10 | 200 | 0.0062074357 | 0.0012395147
11 | 220 | 0.0040588049 | 0.0011493693
12 | 240 | 0.0024628325 | 0.0005116998
13 | 260 | 0.0016052119 | 0.0005109022
14 | 280 | 0.0010709881 | 0.0005109022
15 | 300 | 0.0007908521 | 0.0005109022
16 | 320 | 0.0005712246 | 0.0005108450
17 | 340 | 0.0005442443 | 0.0005108450
18 | 360 | 0.0005243296 | 0.0005108409
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 19: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 20: Best objective = 0.000511
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 21: Best objective = 0.000510
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 22: Best objective = 0.000510
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 23: Best objective = 0.000500
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 24: Best objective = 0.000500
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 25: Best objective = 0.000499
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 26: Best objective = 0.000494
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 27: Best objective = 0.000494
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 28: Best objective = 0.000494
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 29: Best objective = 0.000494
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 30: Best objective = 0.000493
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 31: Best objective = 0.000482
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 32: Best objective = 0.000482
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 33: Best objective = 0.000480
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 34: Best objective = 0.000479
[INFO 08-13 16:55:06] optimpv.pymooOptimizer: Generation 35: Best objective = 0.000478
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 36: Best objective = 0.000478
19 | 380 | 0.0005159560 | 0.0005108409
20 | 400 | 0.0005132289 | 0.0005108409
21 | 420 | 0.0005114007 | 0.0005097638
22 | 440 | 0.0005112651 | 0.0005097638
23 | 460 | 0.0005105012 | 0.0005003071
24 | 480 | 0.0005091914 | 0.0004995100
25 | 500 | 0.0005068181 | 0.0004991364
26 | 520 | 0.0005021070 | 0.0004938509
27 | 540 | 0.0005000971 | 0.0004938509
28 | 560 | 0.0004991609 | 0.0004938509
29 | 580 | 0.0004980710 | 0.0004938509
30 | 600 | 0.0004972039 | 0.0004932747
31 | 620 | 0.0004950918 | 0.0004818834
32 | 640 | 0.0004933125 | 0.0004818834
33 | 660 | 0.0004910454 | 0.0004800684
34 | 680 | 0.0004873844 | 0.0004789107
35 | 700 | 0.0004835519 | 0.0004777296
36 | 720 | 0.0004812247 | 0.0004777296
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 37: Best objective = 0.000478
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 38: Best objective = 0.000478
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 39: Best objective = 0.000478
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 40: Best objective = 0.000478
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 41: Best objective = 0.000477
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 42: Best objective = 0.000477
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 43: Best objective = 0.000477
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 44: Best objective = 0.000477
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 45: Best objective = 0.000477
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 46: Best objective = 0.000473
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 47: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 48: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 49: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 50: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 51: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 52: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 53: Best objective = 0.000437
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 54: Best objective = 0.000437
37 | 740 | 0.0004797610 | 0.0004776815
38 | 760 | 0.0004792009 | 0.0004776815
39 | 780 | 0.0004787665 | 0.0004776815
40 | 800 | 0.0004785617 | 0.0004776035
41 | 820 | 0.0004783031 | 0.0004774847
42 | 840 | 0.0004778719 | 0.0004773888
43 | 860 | 0.0004776427 | 0.0004773832
44 | 880 | 0.0004775365 | 0.0004772057
45 | 900 | 0.0004774764 | 0.0004770858
46 | 920 | 0.0004771511 | 0.0004731157
47 | 940 | 0.0004748758 | 0.0004374115
48 | 960 | 0.0004726247 | 0.0004371206
49 | 980 | 0.0004714256 | 0.0004371206
50 | 1000 | 0.0004688521 | 0.0004371206
51 | 1020 | 0.0004663665 | 0.0004371206
52 | 1040 | 0.0004658181 | 0.0004371206
53 | 1060 | 0.0004639544 | 0.0004371206
54 | 1080 | 0.0004587411 | 0.0004371206
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 55: Best objective = 0.000436
55 | 1100 | 0.0004526142 | 0.0004357534
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 56: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 57: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 58: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 59: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 60: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 61: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 62: Best objective = 0.000436
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 63: Best objective = 0.000431
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 64: Best objective = 0.000431
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 65: Best objective = 0.000431
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 66: Best objective = 0.000431
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 67: Best objective = 0.000431
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 68: Best objective = 0.000431
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 69: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 70: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 71: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 72: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 73: Best objective = 0.000423
56 | 1120 | 0.0004471877 | 0.0004357534
57 | 1140 | 0.0004371726 | 0.0004355852
58 | 1160 | 0.0004366269 | 0.0004355852
59 | 1180 | 0.0004359477 | 0.0004355409
60 | 1200 | 0.0004356548 | 0.0004355401
61 | 1220 | 0.0004355842 | 0.0004355401
62 | 1240 | 0.0004355721 | 0.0004355321
63 | 1260 | 0.0004353522 | 0.0004313618
64 | 1280 | 0.0004353499 | 0.0004313618
65 | 1300 | 0.0004353268 | 0.0004313618
66 | 1320 | 0.0004350889 | 0.0004311857
67 | 1340 | 0.0004350710 | 0.0004311857
68 | 1360 | 0.0004350408 | 0.0004311857
69 | 1380 | 0.0004341084 | 0.0004232695
70 | 1400 | 0.0004330581 | 0.0004232695
71 | 1420 | 0.0004320266 | 0.0004232695
72 | 1440 | 0.0004315621 | 0.0004232695
73 | 1460 | 0.0004303237 | 0.0004232695
74 | 1480 | 0.0004287925 | 0.0004232695
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 74: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 75: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 76: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 77: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 78: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 79: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 80: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 81: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 82: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 83: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 84: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 85: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 86: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 87: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 88: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 89: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 90: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 91: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 92: Best objective = 0.000423
75 | 1500 | 0.0004264139 | 0.0004232507
76 | 1520 | 0.0004253324 | 0.0004232507
77 | 1540 | 0.0004236411 | 0.0004232507
78 | 1560 | 0.0004232736 | 0.0004232496
79 | 1580 | 0.0004232670 | 0.0004232147
80 | 1600 | 0.0004232529 | 0.0004232147
81 | 1620 | 0.0004232412 | 0.0004232124
82 | 1640 | 0.0004232311 | 0.0004232108
83 | 1660 | 0.0004232200 | 0.0004232108
84 | 1680 | 0.0004232134 | 0.0004232108
85 | 1700 | 0.0004232130 | 0.0004232108
86 | 1720 | 0.0004232124 | 0.0004232108
87 | 1740 | 0.0004232120 | 0.0004232108
88 | 1760 | 0.0004232116 | 0.0004232108
89 | 1780 | 0.0004232112 | 0.0004232108
90 | 1800 | 0.0004232110 | 0.0004232108
91 | 1820 | 0.0004232108 | 0.0004232108
92 | 1840 | 0.0004232108 | 0.0004232108
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 93: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 94: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 95: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 96: Best objective = 0.000423
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 97: Best objective = 0.000421
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 98: Best objective = 0.000421
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 99: Best objective = 0.000421
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Generation 100: Best objective = 0.000421
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Optimization completed after 101 generations
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Number of function evaluations: 2000
[INFO 08-13 16:55:07] optimpv.pymooOptimizer: Best objective value: 0.000421
93 | 1860 | 0.0004232108 | 0.0004232108
94 | 1880 | 0.0004232108 | 0.0004232108
95 | 1900 | 0.0004232108 | 0.0004232108
96 | 1920 | 0.0004232108 | 0.0004232108
97 | 1940 | 0.0004231211 | 0.0004214165
98 | 1960 | 0.0004231211 | 0.0004214165
99 | 1980 | 0.0004231195 | 0.0004214165
100 | 2000 | 0.0004231046 | 0.0004214165
[6]:
<pymoo.core.result.Result at 0x7b84cac0c190>
[7]:
# Update parameters with best results
optimizer.update_params_with_best_balance()
diode.params = optimizer.params # update the params list in the agent with the best parameters
# print the best parameters
print('Best parameters:')
for p,po in zip(optimizer.params, params_orig):
print(p.name, 'fitted value:', p.value, 'original value:', po.value)
Best parameters:
J0 fitted value: 1.2522816944637977e-06 original value: 1e-05
n fitted value: 1.3176973775055325 original value: 1.5
R_series fitted value: 0.00011563294472169332 original value: 0.0001
R_shunt fitted value: 0.09797599766422245 original value: 0.1
[8]:
optimizer.plot_convergence(yscale='log', xscale='linear')

[9]:
# rerun the simulation with the best parameters
yfit = diode.run(parameters={})
plt.figure(figsize=(10,10))
plt.plot(X,y,label='data')
plt.plot(X,yfit,label='fit',linestyle='--')
plt.xscale('linear')
plt.yscale('log')
plt.xlabel('Applied voltage [V]')
plt.ylabel('Current density [A m$^{-2}$]')
plt.legend()
plt.show()
