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()

../_images/examples_diode_pymoo_5_0.png

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')
../_images/examples_diode_pymoo_11_0.png
[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()
../_images/examples_diode_pymoo_12_0.png