• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TRI-AMDD / mpet / 5084042774

pending completion
5084042774

push

github

GitHub
Merge pull request #84 from TRI-AMDD/feature/mod_battery_cycle_only

2108 of 3597 relevant lines covered (58.6%)

2.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.7
/mpet/plot/plot_data.py
1
import os
4✔
2

3
import matplotlib as mpl
4✔
4
import matplotlib.animation as manim
4✔
5
import matplotlib.collections as mcollect
4✔
6
import matplotlib.pyplot as plt
4✔
7
import numpy as np
4✔
8
import scipy.integrate as integrate
4✔
9
from scipy.interpolate import interp1d
4✔
10

11
import mpet.geometry as geom
4✔
12
import mpet.mod_cell as mod_cell
4✔
13
import mpet.utils as utils
4✔
14
from mpet.config import Config, constants
4✔
15

16
"""Set list of matplotlib rc parameters to make more readable plots."""
2✔
17
# axtickfsize = 18
18
# labelfsize = 20
19
# mpl.rcParams['xtick.labelsize'] = axtickfsize
20
# mpl.rcParams['ytick.labelsize'] = axtickfsize
21
# mpl.rcParams['axes.labelsize'] = labelfsize
22
# mpl.rcParams['font.size'] = labelfsize - 2
23
# mpl.rcParams['legend.fontsize'] = labelfsize - 2
24
# mpl.rcParams['lines.linewidth'] = 3
25
# mpl.rcParams['lines.markersize'] = 10
26
# mpl.rcParams['lines.markeredgewidth'] = 0.1
27
# mpl.rcParams['text.usetex'] = True
28

29

30
def show_data(indir, plot_type, print_flag, save_flag, data_only, vOut=None, pOut=None, tOut=None):
4✔
31
    pfx = 'mpet.'
×
32
    sStr = "_"
×
33
    ttl_fmt = "% = {perc:2.1f}"
×
34
    # Read in the simulation results and calcuations data
35
    dataFileName = "output_data"
×
36
    dataFile = os.path.join(indir, dataFileName)
×
37
    data = utils.open_data_file(dataFile)
×
38
    try:
×
39
        utils.get_dict_key(data, pfx + 'current')
×
40
    except KeyError:
×
41
        pfx = ''
×
42
    try:
×
43
        utils.get_dict_key(data, pfx + "partTrodecvol0part0" + sStr + "cbar")
×
44
    except KeyError:
×
45
        sStr = "."
×
46
    # Read in the parameters used to define the simulation
47
    config = Config.from_dicts(indir)
×
48
    # simulated (porous) electrodes
49
    trodes = config["trodes"]
×
50
    # Pick out some useful calculated values
51
    limtrode = config["limtrode"]
×
52
    tot_cycle = config["totalCycle"]
×
53
    k = constants.k                      # Boltzmann constant, J/(K Li)
×
54
    Tref = constants.T_ref               # Temp, K
×
55
    e = constants.e                      # Charge of proton, C
×
56
    F = constants.F                      # C/mol
×
57
    c_ref = constants.c_ref
×
58
    td = config["t_ref"]
×
59
    Etheta = {"a": 0.}
×
60
    cap = config[limtrode, "cap"]
×
61
    for trode in trodes:
×
62
        Etheta[trode] = -(k*Tref/e) * config[trode, "phiRef"]
×
63
    Vstd = Etheta["c"] - Etheta["a"]
×
64
    dataReporter = config["dataReporter"]
×
65
    Nvol = config["Nvol"]
×
66
    Npart = config["Npart"]
×
67
    psd_len = config["psd_len"]
×
68
    # Discretization (and associated porosity)
69
    Lfac = 1e6
×
70
    Lunit = r"$\mu$m"
×
71
    dxc = config["L"]["c"]/Nvol["c"]
×
72
    dxvec = np.array(Nvol["c"] * [dxc])
×
73
    porosvec = np.array(Nvol["c"] * [config["poros"]["c"]])
×
74
    cellsvec = dxc*np.arange(Nvol["c"]) + dxc/2.
×
75
    if config["Nvol"]["s"]:
×
76
        dxs = config["L"]["s"]/Nvol["s"]
×
77
        dxvec_s = np.array(Nvol["s"] * [dxs])
×
78
        dxvec = np.hstack((dxvec_s, dxvec))
×
79
        poros_s = np.array(Nvol["s"] * [config["poros"]["s"]])
×
80
        porosvec = np.hstack((poros_s, porosvec))
×
81
        cellsvec += config["L"]["s"] / config["L"]["c"]
×
82
        cellsvec_s = dxs*np.arange(Nvol["s"]) + dxs/2.
×
83
        cellsvec = np.hstack((cellsvec_s, cellsvec))
×
84
    if "a" in trodes:
×
85
        dxa = config["L"]["a"]/Nvol["a"]
×
86
        dxvec_a = np.array(Nvol["a"] * [dxa])
×
87
        dxvec = np.hstack((dxvec_a, dxvec))
×
88
        poros_a = np.array(Nvol["a"] * [config["poros"]["a"]])
×
89
        porosvec = np.hstack((poros_a, porosvec))
×
90
        cellsvec += config["L"]["a"] / config["L"]["c"]
×
91
        cellsvec_a = dxa*np.arange(Nvol["a"]) + dxa/2.
×
92
        cellsvec = np.hstack((cellsvec_a, cellsvec))
×
93
    cellsvec *= config["L_ref"] * Lfac
×
94
    facesvec = np.insert(np.cumsum(dxvec), 0, 0.) * config["L_ref"] * Lfac
×
95
    # Extract the reported simulation times
96
    times = utils.get_dict_key(data, pfx + 'phi_applied_times')
×
97
    numtimes = len(times)
×
98
    tmin = np.min(times)
×
99
    tmax = np.max(times)
×
100
    # Simulation type
101
    profileType = config['profileType']
×
102
    # Colors for plotting concentrations
103
    to_yellow = 0.3
×
104
    to_red = 0.7
×
105
    scl = 1.0  # static
×
106
#    scl = 1.2  # movies
107
    figsize = (scl*6, scl*4)
×
108

109
    # Print relevant simulation info
110
    if print_flag:
×
111
        print("profileType:", profileType)
×
112
#        for i in trodes:
113
#            print "PSD_{l}:".format(l=l)
114
#            print psd_len[l].transpose()
115
#            print "Actual psd_mean [nm]:", np.mean(psd_len[l])
116
#            print "Actual psd_stddev [nm]:", np.std(psd_len[l])
117
        print("Cell structure:")
×
118
        print(("porous anode | " if "a" in config["trodes"] else "flat anode | ")
×
119
              + ("sep | " if config["Nvol"]["s"] else "") + "porous cathode")
120
        if "a" in config["trodes"]:
×
121
            print("capacity ratio cathode:anode, 'z':", config["z"])
×
122
        for trode in trodes:
×
123
            print("solidType_{t}:".format(t=trode), config[trode, 'type'])
×
124
            print("solidShape_{t}".format(t=trode), config[trode, 'shape'])
×
125
            print("rxnType_{t}:".format(t=trode), config[trode, 'rxnType'])
×
126
        if profileType == "CC":
×
127
            print("C_rate:", config['Crate'])
×
128
            theoretical_1C_current = config[config['limtrode'], 'cap'] / 3600.
×
129
            currset_dim = config['currset'] * theoretical_1C_current * config['curr_ref']
×
130
            print("current:", currset_dim, "A/m^2")
×
131
        elif profileType == "CV":  # CV
×
132
            Vref = config['c', 'phiRef']
×
133
            if 'a' in config["trodes"]:
×
134
                Vref -= config['a', 'phiRef']
×
135
            Vset_dim = -(config['Vset'] * k * Tref / e - Vref)
×
136
            print("Vset:", Vset_dim)
×
137
        print("Specified psd_mean, c [{unit}]:".format(unit=Lunit),
×
138
              np.array(config['mean']["c"])*Lfac)
139
        print("Specified psd_stddev, c [{unit}]:".format(unit=Lunit),
×
140
              np.array(config['stddev']["c"])*Lfac)
141
        print("ndim B_c:", config["c", "B"])
×
142
        if config["Nvol"]["s"]:
×
143
            print("Nvol_s:", Nvol["s"])
×
144
        print("Nvol_c:", Nvol["c"])
×
145
        if 'a' in config["trodes"]:
×
146
            print("Nvol_a:", Nvol["a"])
×
147
        print("Npart_c:", Npart["c"])
×
148
        if 'a' in config["trodes"]:
×
149
            print("Npart_a:", Npart["a"])
×
150
        print("td [s]:", config["t_ref"])
×
151
        for trode in trodes:
×
152
            if trode == "a":
×
153
                k0 = config.D_a["k0"]
×
154
            elif trode == "c":
×
155
                k0 = config.D_c["k0"]
×
156
            else:
157
                raise Exception(f"Unknown trode: {trode}")
×
158
            print("k0_{t} [A/m^2]:".format(t=trode), k0)
×
159
            rxnType = config[trode, 'rxnType']
×
160
            if rxnType == "BV":
×
161
                print("alpha_" + trode + ":", config[trode, 'alpha'])
×
162
            elif rxnType in ["Marcus", "MHC"]:
×
163
                print("lambda_" + trode + "/(kTref):", config[trode, "lambda"]
×
164
                      * k * Tref)
165
            if config['simBulkCond'][trode]:
×
166
                print(trode + " bulk conductivity loss: Yes -- "
×
167
                      + "sigma_s [S/m]: " + str(config['sigma_s'][trode] * config['sigma_s_ref']))
168
            else:
169
                print(trode + " bulk conductivity loss: No")
×
170
            try:
×
171
                simSurfCond = config[trode, 'simSurfCond']
×
172
                if simSurfCond:
×
173
                    print(trode + " surface conductivity loss: Yes -- "
×
174
                          + "dim_scond [S]: " + str(config[trode, 'scond']))
175
                else:
176
                    print(trode + " surface conductivity loss: No")
×
177
            except Exception:
×
178
                pass
×
179
#            if ndD['simSurfCond'][l]:
180
#                print (l + " surface conductivity loss: Yes -- " +
181
#                        "dim_scond [S]: " + str(dD['scond'][l]))
182
#            else:
183
#                print l + " surface conductivity loss: No"
184

185
    if plot_type in ["params"]:
×
186
        # return ndD_s, dD_s, ndD_e, dD_e
187
        return config
×
188
    if plot_type in ["discData"]:
×
189
        return cellsvec/Lfac, facesvec/Lfac
×
190

191
    # Plot voltage profile
192
    if plot_type in ["v", "vt"]:
×
193
        voltage = (Vstd
×
194
                   - (k*Tref/e)*utils.get_dict_key(data, pfx + 'phi_applied'))
195
        ffvec = utils.get_dict_key(data, pfx + 'ffrac_c')
×
196
        fig, ax = plt.subplots(figsize=figsize)
×
197
        if plot_type == "v":
×
198
            if data_only:
×
199
                plt.close(fig)
×
200
                return ffvec, voltage
×
201
            ax.plot(ffvec, voltage)
×
202
            xmin = 0.
×
203
            xmax = 1.
×
204
            ax.set_xlim((xmin, xmax))
×
205
            ax.set_xlabel("Cathode Filling Fraction [dimensionless]")
×
206
        elif plot_type == "vt":
×
207
            if data_only:
×
208
                plt.close(fig)
×
209
                return times*td, voltage
×
210
            ax.plot(times*td, voltage)
×
211
            ax.set_xlabel("Time [s]")
×
212
        ax.set_ylabel("Voltage [V]")
×
213
        if save_flag:
×
214
            fig.savefig("mpet_v.pdf", bbox_inches="tight")
×
215
        return fig, ax
×
216

217
    # Plot surface conc.
218
    if plot_type[:-2] in ["surf"]:
×
219
        if dataReporter == "hdf5Fast":
×
220
            # hdf5Fast does not print internal particle concentrations
221
            raise Exception("hdf5Fast dataReporter does not print internal particle "
×
222
                            "concentrations, rerun simulation with another data reporter")
223
        trode = plot_type[-1]
×
224
        str_base = (pfx
×
225
                    + "partTrode{trode}vol{{vInd}}part{{pInd}}".format(trode=trode)
226
                    + sStr + "c")
227
        if data_only:
×
228
            sol_str = str_base.format(pInd=pOut, vInd=vOut)
×
229
            datay = utils.get_dict_key(data, sol_str, squeeze=False)[:,-1]
×
230
            return times*td, datay
×
231
        fig, ax = plt.subplots(Npart[trode], Nvol[trode], squeeze=False, sharey=True,
×
232
                               figsize=figsize)
233
        ylim = (0, 1.01)
×
234
        datax = times
×
235
        for pInd in range(Npart[trode]):
×
236
            for vInd in range(Nvol[trode]):
×
237
                sol_str = str_base.format(pInd=pInd, vInd=vInd)
×
238
                # Remove axis ticks
239
                ax[pInd,vInd].xaxis.set_major_locator(plt.NullLocator())
×
240
                datay = utils.get_dict_key(data, sol_str, squeeze=False)[:,-1]
×
241
                line, = ax[pInd,vInd].plot(times, datay)
×
242
        return fig, ax
×
243

244
    # Plot SoC profile
245
    if plot_type[:-2] in ["soc"]:
×
246
        trode = plot_type[-1]
×
247
        ffvec = utils.get_dict_key(data, pfx + 'ffrac_{trode}'.format(trode=trode))
×
248
        if data_only:
×
249
            return times*td, ffvec
×
250
        fig, ax = plt.subplots(figsize=figsize)
×
251
        ax.plot(times*td, ffvec)
×
252
        xmin = np.min(ffvec)
×
253
        xmax = np.max(ffvec)
×
254
        ax.set_ylim((0, 1.05))
×
255
        ax.set_xlabel("Time [s]")
×
256
        ax.set_ylabel("Filling Fraciton [dimless]")
×
257
        if save_flag:
×
258
            fig.savefig("mpet_soc.pdf", bbox_inches="tight")
×
259
        return fig, ax
×
260

261
    # Check to make sure mass is conserved in elyte
262
    if plot_type == "elytecons":
×
263
        fig, ax = plt.subplots(figsize=figsize)
×
264
        eps = 1e-2
×
265
        ymin = 1-eps
×
266
        ymax = 1+eps
×
267
#        ax.set_ylim((ymin, ymax))
268
        ax.set_ylabel('Avg. Concentration of electrolyte [nondim]')
×
269
        sep = pfx + 'c_lyte_s'
×
270
        anode = pfx + 'c_lyte_a'
×
271
        cath = pfx + 'c_lyte_c'
×
272
        ax.set_xlabel('Time [s]')
×
273
        cvec = utils.get_dict_key(data, cath)
×
274
        if Nvol["s"]:
×
275
            cvec_s = utils.get_dict_key(data, sep)
×
276
            cvec = np.hstack((cvec_s, cvec))
×
277
        if "a" in trodes:
×
278
            cvec_a = utils.get_dict_key(data, anode)
×
279
            cvec = np.hstack((cvec_a, cvec))
×
280
        cavg = np.sum(porosvec*dxvec*cvec, axis=1)/np.sum(porosvec*dxvec)
×
281
        if data_only:
×
282
            plt.close(fig)
×
283
            return times*td, cavg
×
284
        np.set_printoptions(precision=8)
×
285
        ax.plot(times*td, cavg)
×
286
        return fig, ax
×
287

288
    # Plot current profile
289
    if plot_type == "curr":
×
290
        theoretical_1C_current = config[config['limtrode'], "cap"] / 3600.  # A/m^2
×
291
        current = (utils.get_dict_key(data, pfx + 'current')
×
292
                   * theoretical_1C_current / config['1C_current_density'] * config['curr_ref'])
293
        ffvec = utils.get_dict_key(data, pfx + 'ffrac_c')
×
294
        if data_only:
×
295
            return times*td, current
×
296
        fig, ax = plt.subplots(figsize=figsize)
×
297
        ax.plot(times*td, current)
×
298
        xmin = np.min(ffvec)
×
299
        xmax = np.max(ffvec)
×
300
        ax.set_xlabel("Time [s]")
×
301
        ax.set_ylabel("Current [C-rate]")
×
302
        if save_flag:
×
303
            fig.savefig("mpet_current.png", bbox_inches="tight")
×
304
        return fig, ax
×
305

306
    if plot_type == "power":
×
307
        current = utils.get_dict_key(data, pfx + 'current') * (3600/td) * (cap/3600)  # in A/m^2
×
308
        voltage = (Vstd - (k*Tref/e)*utils.get_dict_key(data, pfx + 'phi_applied'))  # in V
×
309
        power = np.multiply(current, voltage)
×
310
        if data_only:
×
311
            return times*td, power
×
312
        fig, ax = plt.subplots(figsize=figsize)
×
313
        ax.plot(times*td, power)
×
314
        ax.set_xlabel("Time [s]")
×
315
        ax.set_ylabel(r"Power [W/m$^2$]")
×
316
        if save_flag:
×
317
            fig.savefig("mpet_power.png", bbox_inches="tight")
×
318
        return fig, ax
×
319

320
    # Plot electrolyte concentration or potential
321
    elif plot_type in ["elytec", "elytep", "elytecf", "elytepf",
×
322
                       "elytei", "elyteif", "elytedivi", "elytedivif"]:
323
        fplot = (True if plot_type[-1] == "f" else False)
×
324
        t0ind = (0 if not fplot else -1)
×
325
        datax = cellsvec
×
326
        c_sep, p_sep = pfx + 'c_lyte_s', pfx + 'phi_lyte_s'
×
327
        c_anode, p_anode = pfx + 'c_lyte_a', pfx + 'phi_lyte_a'
×
328
        c_cath, p_cath = pfx + 'c_lyte_c', pfx + 'phi_lyte_c'
×
329
        datay_c = utils.get_dict_key(data, c_cath, squeeze=False)
×
330
        datay_p = utils.get_dict_key(data, p_cath, squeeze=False)
×
331
        L_c = config['L']["c"] * config['L_ref'] * Lfac
×
332
        Ltot = L_c
×
333
        if config["Nvol"]["s"]:
×
334
            datay_s_c = utils.get_dict_key(data, c_sep, squeeze=False)
×
335
            datay_s_p = utils.get_dict_key(data, p_sep, squeeze=False)
×
336
            datay_c = np.hstack((datay_s_c, datay_c))
×
337
            datay_p = np.hstack((datay_s_p, datay_p))
×
338
            L_s = config['L']["s"] * config['L_ref'] * Lfac
×
339
            Ltot += L_s
×
340
        else:
341
            L_s = 0
×
342
        if "a" in trodes:
×
343
            datay_a_c = utils.get_dict_key(data, c_anode, squeeze=False)
×
344
            datay_a_p = utils.get_dict_key(data, p_anode, squeeze=False)
×
345
            datay_c = np.hstack((datay_a_c, datay_c))
×
346
            datay_p = np.hstack((datay_a_p, datay_p))
×
347
            L_a = config['L']["a"] * config['L_ref'] * Lfac
×
348
            Ltot += L_a
×
349
        else:
350
            L_a = 0
×
351
        xmin = 0
×
352
        xmax = Ltot
×
353
        if plot_type in ["elytec", "elytecf"]:
×
354
            ylbl = 'Concentration of electrolyte [M]'
×
355
            datay = datay_c * c_ref / 1000.
×
356
        elif plot_type in ["elytep", "elytepf"]:
×
357
            ylbl = 'Potential of electrolyte [V]'
×
358
            datay = datay_p*(k*Tref/e) - Vstd
×
359
        elif plot_type in ["elytei", "elyteif", "elytedivi", "elytedivif"]:
×
360
            cGP_L = utils.get_dict_key(data, "c_lyteGP_L")
×
361
            pGP_L = utils.get_dict_key(data, "phi_lyteGP_L")
×
362
            cmat = np.hstack((cGP_L.reshape((-1,1)), datay_c, datay_c[:,-1].reshape((-1,1))))
×
363
            pmat = np.hstack((pGP_L.reshape((-1,1)), datay_p, datay_p[:,-1].reshape((-1,1))))
×
364
            disc = geom.get_elyte_disc(
×
365
                Nvol, config["L"], config["poros"], config["BruggExp"])
366
            i_edges = np.zeros((numtimes, len(facesvec)))
×
367
            for tInd in range(numtimes):
×
368
                i_edges[tInd, :] = mod_cell.get_lyte_internal_fluxes(
×
369
                    cmat[tInd, :], pmat[tInd, :], disc, config)[1]
370
            if plot_type in ["elytei", "elyteif"]:
×
371
                ylbl = r'Current density of electrolyte [A/m$^2$]'
×
372
                datax = facesvec
×
373
                datay = i_edges * (F*constants.c_ref*config["D_ref"]/config["L_ref"])
×
374
            elif plot_type in ["elytedivi", "elytedivif"]:
×
375
                ylbl = r'Divergence of electrolyte current density [A/m$^3$]'
×
376
                datax = cellsvec
×
377
                datay = np.diff(i_edges, axis=1) / disc["dxvec"]
×
378
                datay *= (F*constants.c_ref*config["D_ref"]/config["L_ref"]**2)
×
379
        if fplot:
×
380
            datay = datay[t0ind]
×
381
        if data_only:
×
382
            return datax, datay, L_a, L_s
×
383
        dataMin, dataMax = np.min(datay), np.max(datay)
×
384
        dataRange = dataMax - dataMin
×
385
        ymin = dataMin - 0.05*dataRange
×
386
        ymax = dataMax + 0.05*dataRange
×
387
        fig, ax = plt.subplots(figsize=figsize)
×
388
        ax.set_xlabel('Battery Position [{unit}]'.format(unit=Lunit))
×
389
        ax.set_ylabel(ylbl)
×
390
        ttl = ax.text(
×
391
            0.5, 1.05, ttl_fmt.format(perc=0),
392
            transform=ax.transAxes, verticalalignment="center",
393
            horizontalalignment="center")
394
        ax.set_ylim((ymin, ymax))
×
395
        ax.set_xlim((xmin, xmax))
×
396
        # returns tuble of line objects, thus comma
397
        if fplot:
×
398
            line1, = ax.plot(datax, datay, '-')
×
399
        else:
400
            line1, = ax.plot(datax, datay[t0ind,:], '-')
×
401
        ax.axvline(x=L_a, linestyle='--', color='g')
×
402
        ax.axvline(x=(L_a+L_s), linestyle='--', color='g')
×
403
        if fplot:
×
404
            print("time =", times[t0ind]*td, "s")
×
405
            if save_flag:
×
406
                fig.savefig("mpet_{pt}.png".format(pt=plot_type),
×
407
                            bbox_inches="tight")
408
            return fig, ax
×
409

410
        def init():
×
411
            line1.set_ydata(np.ma.array(datax, mask=True))
×
412
            ttl.set_text('')
×
413
            return line1, ttl
×
414

415
        def animate(tind):
×
416
            line1.set_ydata(datay[tind])
×
417
            t_current = times[tind]
×
418
            tfrac = (t_current - tmin)/(tmax - tmin) * 100
×
419
            ttl.set_text(ttl_fmt.format(perc=tfrac))
×
420
            return line1, ttl
×
421

422
    # Plot solid particle-average concentrations
423
    elif plot_type[:-2] in ["cbarLine", "dcbardtLine"]:
×
424
        trode = plot_type[-1]
×
425
        fig, ax = plt.subplots(Npart[trode], Nvol[trode], squeeze=False, sharey=True,
×
426
                               figsize=figsize)
427
        partStr = "partTrode{trode}vol{{vInd}}part{{pInd}}".format(trode=trode) + sStr
×
428
        type2c = False
×
429
        if config[trode, "type"] in constants.one_var_types:
×
430
            if plot_type[:-2] in ["cbarLine"]:
×
431
                str_base = pfx + partStr + "cbar"
×
432
            elif plot_type[:-2] in ["dcbardtLine"]:
×
433
                str_base = pfx + partStr + "dcbardt"
×
434
        elif config[trode, "type"] in constants.two_var_types:
×
435
            type2c = True
×
436
            if plot_type[:-2] in ["cbarLine"]:
×
437
                str1_base = pfx + partStr + "c1bar"
×
438
                str2_base = pfx + partStr + "c2bar"
×
439
            elif plot_type[:-2] in ["dcbardtLine"]:
×
440
                str1_base = pfx + partStr + "dc1bardt"
×
441
                str2_base = pfx + partStr + "dc2bardt"
×
442
        ylim = (0, 1.01)
×
443
        datax = times*td
×
444
        if data_only:
×
445
            plt.close(fig)
×
446
            if type2c:
×
447
                sol1_str = str1_base.format(pInd=pOut, vInd=vOut)
×
448
                sol2_str = str2_base.format(pInd=pOut, vInd=vOut)
×
449
                datay1 = utils.get_dict_key(data, sol1_str)
×
450
                datay2 = utils.get_dict_key(data, sol2_str)
×
451
                datay = (datay1, datay2)
×
452
            else:
453
                sol_str = str_base.format(pInd=pOut, vInd=vOut)
×
454
                datay = utils.get_dict_key(data, sol_str)
×
455
            return datax, datay
×
456
        xLblNCutoff = 4
×
457
        xLbl = "Time [s]"
×
458
        yLbl = "Particle Average Filling Fraction"
×
459
        for pInd in range(Npart[trode]):
×
460
            for vInd in range(Nvol[trode]):
×
461
                if type2c:
×
462
                    sol1_str = str1_base.format(pInd=pInd, vInd=vInd)
×
463
                    sol2_str = str2_base.format(pInd=pInd, vInd=vInd)
×
464
                    if Nvol[trode] > xLblNCutoff:
×
465
                        # Remove axis ticks
466
                        ax[pInd,vInd].xaxis.set_major_locator(plt.NullLocator())
×
467
                    else:
468
                        ax[pInd,vInd].set_xlabel(xLbl)
×
469
                        ax[pInd,vInd].set_ylabel(yLbl)
×
470
                    datay1 = utils.get_dict_key(data, sol1_str)
×
471
                    datay2 = utils.get_dict_key(data, sol2_str)
×
472
                    line1, = ax[pInd,vInd].plot(times, datay1)
×
473
                    line2, = ax[pInd,vInd].plot(times, datay2)
×
474
                else:
475
                    sol_str = str_base.format(pInd=pInd, vInd=vInd)
×
476
                    if Nvol[trode] > xLblNCutoff:
×
477
                        # Remove axis ticks
478
                        ax[pInd,vInd].xaxis.set_major_locator(plt.NullLocator())
×
479
                    else:
480
                        ax[pInd,vInd].set_xlabel(xLbl)
×
481
                        ax[pInd,vInd].set_ylabel(yLbl)
×
482
                    datay = utils.get_dict_key(data, sol_str)
×
483
                    line, = ax[pInd,vInd].plot(times, datay)
×
484
        return fig, ax
×
485

486
    # Plot all solid concentrations or potentials
487
    elif plot_type[:-2] in ["csld"]:
×
488
        if dataReporter == "hdf5Fast":
×
489
            # hdf5Fast does not print internal particle concentrations
490
            raise Exception("hdf5Fast dataReporter does not print internal particle "
×
491
                            "concentrations, rerun simulation with another data reporter")
492

493
        timettl = False  # Plot the current simulation time as title
×
494
        # Plot title in seconds
495
        ttlscl, ttlunit = 1, "s"
×
496
        t0ind = 0
×
497
        trode = plot_type[-1]
×
498
        if plot_type[0] == "c":
×
499
            plt_cavg = True
×
500
        else:
501
            plt_cavg = False
×
502
        plt_axlabels = True
×
503
        if config[trode, "type"] in constants.one_var_types:
×
504
            type2c = False
×
505
        elif config[trode, "type"] in constants.two_var_types:
×
506
            type2c = True
×
507
        Nv, Np = Nvol[trode], Npart[trode]
×
508
        partStr = "partTrode{trode}vol{vInd}part{pInd}" + sStr
×
509
        fig, ax = plt.subplots(Np, Nv, squeeze=False, sharey=True, figsize=figsize)
×
510
        if not type2c:
×
511
            cstr_base = pfx + partStr + "c"
×
512
            cbarstr_base = pfx + partStr + "cbar"
×
513
            cstr = np.empty((Np, Nv), dtype=object)
×
514
            cbarstr = np.empty((Np, Nv), dtype=object)
×
515
            lines = np.empty((Np, Nv), dtype=object)
×
516
        elif type2c:
×
517
            c1str_base = pfx + partStr + "c1"
×
518
            c2str_base = pfx + partStr + "c2"
×
519
            c1barstr_base = pfx + partStr + "c1bar"
×
520
            c2barstr_base = pfx + partStr + "c2bar"
×
521
            c1str = np.empty((Np, Nv), dtype=object)
×
522
            c2str = np.empty((Np, Nv), dtype=object)
×
523
            c1barstr = np.empty((Np, Nv), dtype=object)
×
524
            c2barstr = np.empty((Np, Nv), dtype=object)
×
525
            lines1 = np.empty((Np, Nv), dtype=object)
×
526
            lines2 = np.empty((Np, Nv), dtype=object)
×
527
            lines3 = np.empty((Np, Nv), dtype=object)
×
528
        lens = np.zeros((Np, Nv))
×
529
        if data_only:
×
530
            print("tInd_{}".format(tOut), "time =", times[tOut]*td, "s")
×
531
            lenval = psd_len[trode][vOut, pOut]
×
532
            if type2c:
×
533
                c1str = c1str_base.format(trode=trode, pInd=pOut, vInd=vOut)
×
534
                c2str = c2str_base.format(trode=trode, pInd=pOut, vInd=vOut)
×
535
                c1barstr = c1barstr_base.format(trode=trode, pInd=pOut, vInd=vOut)
×
536
                c2barstr = c2barstr_base.format(trode=trode, pInd=pOut, vInd=vOut)
×
537
                datay1 = utils.get_dict_key(data, c1str[pOut,vOut])
×
538
                datay2 = utils.get_dict_key(data, c2str[pOut,vOut])
×
539
                datay = (datay1, datay2)
×
540
                numy = len(datay1)
×
541
            else:
542
                cstr = cstr_base.format(trode=trode, pInd=pOut, vInd=vOut)
×
543
                cbarstr = cbarstr_base.format(trode=trode, pInd=pOut, vInd=vOut)
×
544
                datay = utils.get_dict_key(data, cstr)[tOut]
×
545
                numy = len(datay)
×
546
            datax = np.linspace(0, lenval * Lfac, numy)
×
547
            plt.close(fig)
×
548
            return datax, datay
×
549
        ylim = (0, 1.01)
×
550
        for pInd in range(Np):
×
551
            for vInd in range(Nv):
×
552
                lens[pInd,vInd] = psd_len[trode][vInd,pInd]
×
553
                if type2c:
×
554
                    c1str[pInd,vInd] = c1str_base.format(trode=trode, pInd=pInd, vInd=vInd)
×
555
                    c2str[pInd,vInd] = c2str_base.format(trode=trode, pInd=pInd, vInd=vInd)
×
556
                    c1barstr[pInd,vInd] = c1barstr_base.format(trode=trode, pInd=pInd, vInd=vInd)
×
557
                    c2barstr[pInd,vInd] = c2barstr_base.format(trode=trode, pInd=pInd, vInd=vInd)
×
558
                    datay1 = utils.get_dict_key(data, c1str[pInd,vInd])[t0ind]
×
559
                    datay2 = utils.get_dict_key(data, c2str[pInd,vInd])[t0ind]
×
560
                    datay3 = 0.5*(datay1 + datay2)
×
561
                    lbl1, lbl2 = r"$\widetilde{c}_1$", r"$\widetilde{c}_2$"
×
562
                    lbl3 = r"$\overline{c}$"
×
563
                    numy = len(datay1) if isinstance(datay1, np.ndarray) else 1
×
564
                    datax = np.linspace(0, lens[pInd,vInd] * Lfac, numy)
×
565
                    line1, = ax[pInd,vInd].plot(datax, datay1, label=lbl1)
×
566
                    line2, = ax[pInd,vInd].plot(datax, datay2, label=lbl2)
×
567
                    if plt_cavg:
×
568
                        line3, = ax[pInd,vInd].plot(datax, datay3, '--', label=lbl3)
×
569
                        lines3[pInd,vInd] = line3
×
570
                    lines1[pInd,vInd] = line1
×
571
                    lines2[pInd,vInd] = line2
×
572
                else:
573
                    cstr[pInd,vInd] = cstr_base.format(trode=trode, pInd=pInd, vInd=vInd)
×
574
                    cbarstr[pInd,vInd] = cbarstr_base.format(trode=trode, pInd=pInd, vInd=vInd)
×
575
                    datay = utils.get_dict_key(data, cstr[pInd,vInd])[t0ind]
×
576
                    numy = len(datay)
×
577
                    datax = np.linspace(0, lens[pInd,vInd] * Lfac, numy)
×
578
                    line, = ax[pInd,vInd].plot(datax, datay)
×
579
                    lines[pInd,vInd] = line
×
580
                ax[pInd,vInd].set_ylim(ylim)
×
581
                ax[pInd,vInd].set_xlim((0, lens[pInd,vInd] * Lfac))
×
582
                if plt_axlabels:
×
583
                    ax[pInd, vInd].set_xlabel(r"$r$ [{Lunit}]".format(Lunit=Lunit))
×
584
                    if plot_type[0] == "c":
×
585
                        ax[pInd, vInd].set_ylabel(r"$\widetilde{{c}}$")
×
586
                    elif plot_type[:2] == "mu":
×
587
                        ax[pInd, vInd].set_ylabel(r"$\mu/k_\mathrm{B}T$")
×
588
                if timettl:
×
589
                    ttl = ax[pInd, vInd].text(
×
590
                        0.5, 1.04, "t = {tval:3.3f} {ttlu}".format(
591
                            tval=times[t0ind]*td*ttlscl, ttlu=ttlunit),
592
                        verticalalignment="center", horizontalalignment="center",
593
                        transform=ax[pInd, vInd].transAxes)
594

595
        def init():
×
596
            toblit = []
×
597
            for pInd in range(Npart[trode]):
×
598
                for vInd in range(Nvol[trode]):
×
599
                    if type2c:
×
600
                        data_c1str = utils.get_dict_key(data, c1str[pInd,vInd])[t0ind]
×
601
                        # check if it is array, then return length. otherwise return 1
602
                        numy = len(data_c1str) if isinstance(data_c1str, np.ndarray) else 1
×
603
                        maskTmp = np.zeros(numy)
×
604
                        lines1[pInd,vInd].set_ydata(np.ma.array(maskTmp, mask=True))
×
605
                        lines2[pInd,vInd].set_ydata(np.ma.array(maskTmp, mask=True))
×
606
                        lines_local = np.vstack((lines1, lines2))
×
607
                        if plt_cavg:
×
608
                            lines3[pInd,vInd].set_ydata(np.ma.array(maskTmp, mask=True))
×
609
                            lines_local = np.vstack((lines_local, lines3))
×
610
                    else:
611
                        data_cstr = utils.get_dict_key(data, cstr[pInd,vInd])[t0ind]
×
612
                        numy = len(data_cstr) if isinstance(data_cstr, np.ndarray) else 1
×
613
                        maskTmp = np.zeros(numy)
×
614
                        lines[pInd,vInd].set_ydata(np.ma.array(maskTmp, mask=True))
×
615
                        lines_local = lines.copy()
×
616
                    toblit.extend(lines_local.reshape(-1))
×
617
                    if timettl:
×
618
                        ttl.set_text("")
×
619
                        toblit.extend([ttl])
×
620
            return tuple(toblit)
×
621

622
        def animate(tind):
×
623
            toblit = []
×
624
            for pInd in range(Npart[trode]):
×
625
                for vInd in range(Nvol[trode]):
×
626
                    if type2c:
×
627
                        datay1 = utils.get_dict_key(data, c1str[pInd,vInd])[tind]
×
628
                        datay2 = utils.get_dict_key(data, c2str[pInd,vInd])[tind]
×
629
                        datay3 = 0.5*(datay1 + datay2)
×
630
                        lines1[pInd,vInd].set_ydata(datay1)
×
631
                        lines2[pInd,vInd].set_ydata(datay2)
×
632
                        lines_local = np.vstack((lines1, lines2))
×
633
                        if plt_cavg:
×
634
                            lines3[pInd,vInd].set_ydata(datay3)
×
635
                            lines_local = np.vstack((lines_local, lines3))
×
636
                    else:
637
                        datay = utils.get_dict_key(data, cstr[pInd,vInd])[tind]
×
638
                        lines[pInd,vInd].set_ydata(datay)
×
639
                        lines_local = lines.copy()
×
640
                    toblit.extend(lines_local.reshape(-1))
×
641
                    if timettl:
×
642
                        ttl.set_text("t = {tval:3.3f} {ttlu}".format(
×
643
                            tval=times[tind]*td*ttlscl, ttlu=ttlunit))
644
                        toblit.extend([ttl])
×
645
            return tuple(toblit)
×
646

647
    # Plot average solid concentrations
648
    elif plot_type in ["cbar_c", "cbar_a", "cbar_full"]:
×
649
        if plot_type[-4:] == "full":
×
650
            trvec = ["a", "c"]
×
651
        elif plot_type[-1] == "a":
×
652
            trvec = ["a"]
×
653
        else:
654
            trvec = ["c"]
×
655
        dataCbar = {}
×
656
        for trode in trodes:
×
657
            dataCbar[trode] = np.zeros((numtimes, Nvol[trode], Npart[trode]))
×
658
            for tInd in range(numtimes):
×
659
                for vInd in range(Nvol[trode]):
×
660
                    for pInd in range(Npart[trode]):
×
661
                        dataStr = (
×
662
                            pfx
663
                            + "partTrode{t}vol{vInd}part{pInd}".format(
664
                                t=trode, vInd=vInd, pInd=pInd)
665
                            + sStr + "cbar")
666
                        dataCbar[trode][tInd,vInd,pInd] = (
×
667
                            np.squeeze(utils.get_dict_key(data, dataStr))[tInd])
668
        if data_only:
×
669
            return dataCbar
×
670
        # Set up colors.
671
        # Define if you want smooth or discrete color changes
672
        # Option: "smooth" or "discrete"
673
        color_changes = "discrete"
×
674
#        color_changes = "smooth"
675
        # Discrete color changes:
676
        if color_changes == "discrete":
×
677
            # Make a discrete colormap that goes from green to yellow
678
            # to red instantaneously
679
            cdict = {
×
680
                "red": [(0.0, 0.0, 0.0),
681
                        (to_yellow, 0.0, 1.0),
682
                        (1.0, 1.0, 1.0)],
683
                "green": [(0.0, 0.502, 0.502),
684
                          (to_yellow, 0.502, 1.0),
685
                          (to_red, 1.0, 0.0),
686
                          (1.0, 0.0, 0.0)],
687
                "blue": [(0.0, 0.0, 0.0),
688
                         (1.0, 0.0, 0.0)]
689
                }
690
            cmap = mpl.colors.LinearSegmentedColormap(
×
691
                "discrete", cdict)
692
        # Smooth colormap changes:
693
        if color_changes == "smooth":
×
694
            # generated with colormap.org
695
            cmaps = np.load("colormaps_custom.npz")
×
696
            cmap_data = cmaps["GnYlRd_3"]
×
697
            cmap = mpl.colors.ListedColormap(cmap_data/255.)
×
698

699
        size_frac_min = 0.10
×
700
        fig, axs = plt.subplots(1, len(trvec), squeeze=False, figsize=figsize)
×
701
        ttlx = 0.5 if len(trvec) < 2 else 1.1
×
702
        ttl = axs[0,0].text(
×
703
            ttlx, 1.05, ttl_fmt.format(perc=0),
704
            transform=axs[0,0].transAxes, verticalalignment="center",
705
            horizontalalignment="center")
706
        collection = np.empty(len(trvec), dtype=object)
×
707
        for indx, trode in enumerate(trvec):
×
708
            ax = axs[0,indx]
×
709
            # Get particle sizes (and max size) (length-based)
710
            lens = psd_len[trode]
×
711
            len_max = np.max(lens)
×
712
            len_min = np.min(lens)
×
713
            ax.patch.set_facecolor('white')
×
714
            # Don't stretch axes to fit figure -- keep 1:1 x:y ratio.
715
            ax.set_aspect('equal', 'box')
×
716
            # Don't show axis ticks
717
            ax.xaxis.set_major_locator(plt.NullLocator())
×
718
            ax.yaxis.set_major_locator(plt.NullLocator())
×
719
            ax.set_xlim(0, 1.)
×
720
            ax.set_ylim(0, float(Npart[trode])/Nvol[trode])
×
721
            # Label parts of the figure
722
#            ylft = ax.text(-0.07, 0.5, "Separator",
723
#                    transform=ax.transAxes, rotation=90,
724
#                    verticalalignment="center",
725
#                    horizontalalignment="center")
726
#            yrht = ax.text(1.09, 0.5, "Current Collector",
727
#                    transform=ax.transAxes, rotation=90,
728
#                    verticalalignment="center",
729
#                    horizontalalignment="center")
730
#            xbtm = ax.text(.50, -0.05, "Electrode Depth -->",
731
#                    transform=ax.transAxes, rotation=0,
732
#                    verticalalignment="center",
733
#                    horizontalalignment="center")
734
            # Geometric parameters for placing the rectangles on the axes
735
            spacing = 1.0 / Nvol[trode]
×
736
            size_fracs = 0.4*np.ones((Nvol[trode], Npart[trode]))
×
737
            if len_max != len_min:
×
738
                size_fracs = (lens - len_min)/(len_max - len_min)
×
739
            sizes = (size_fracs*(1-size_frac_min) + size_frac_min) / Nvol[trode]
×
740
            # Create rectangle "patches" to add to figure axes.
741
            rects = np.empty((Nvol[trode], Npart[trode]), dtype=object)
×
742
            color = 'green'  # value is irrelevant -- it will be animated
×
743
            for (vInd, pInd), c in np.ndenumerate(sizes):
×
744
                size = sizes[vInd,pInd]
×
745
                center = np.array([spacing*(vInd + 0.5), spacing*(pInd + 0.5)])
×
746
                bottom_left = center - size / 2
×
747
                rects[vInd,pInd] = plt.Rectangle(
×
748
                    bottom_left, size, size, color=color)
749
            # Create a group of rectange "patches" from the rects array
750
            collection[indx] = mcollect.PatchCollection(rects.reshape(-1))
×
751
            # Put them on the axes
752
            ax.add_collection(collection[indx])
×
753
        # Have a "background" image of rectanges representing the
754
        # initial state of the system.
755

756
        def init():
×
757
            for indx, trode in enumerate(trvec):
×
758
                cbar_mat = dataCbar[trode][0,:,:]
×
759
                colors = cmap(cbar_mat.reshape(-1))
×
760
                collection[indx].set_color(colors)
×
761
                ttl.set_text('')
×
762
            out = [collection[i] for i in range(len(collection))]
×
763
            out.append(ttl)
×
764
            out = tuple(out)
×
765
            return out
×
766

767
        def animate(tind):
×
768
            for indx, trode in enumerate(trvec):
×
769
                cbar_mat = dataCbar[trode][tind,:,:]
×
770
                colors = cmap(cbar_mat.reshape(-1))
×
771
                collection[indx].set_color(colors)
×
772
            t_current = times[tind]
×
773
            tfrac = (t_current - tmin)/(tmax - tmin) * 100
×
774
            ttl.set_text(ttl_fmt.format(perc=tfrac))
×
775
            out = [collection[i] for i in range(len(collection))]
×
776
            out.append(ttl)
×
777
            out = tuple(out)
×
778
            return out
×
779

780
    # Plot cathode potential
781
    elif plot_type[0:5] in ["bulkp"]:
×
782
        trode = plot_type[-1]
×
783
        fplot = (True if plot_type[-3] == "f" else False)
×
784
        t0ind = (0 if not fplot else -1)
×
785
        fig, ax = plt.subplots(figsize=figsize)
×
786
        ax.set_xlabel('Position in electrode [{unit}]'.format(unit=Lunit))
×
787
        ax.set_ylabel('Potential of cathode [nondim]')
×
788
        ttl = ax.text(0.5, 1.05, ttl_fmt.format(perc=0),
×
789
                      transform=ax.transAxes, verticalalignment="center",
790
                      horizontalalignment="center")
791
        bulkp = pfx + 'phi_bulk_{trode}'.format(trode=trode)
×
792
        datay = utils.get_dict_key(data, bulkp)
×
793
        ymin = np.min(datay) - 0.2
×
794
        ymax = np.max(datay) + 0.2
×
795
        if trode == "a":
×
796
            datax = cellsvec[:Nvol["a"]]
×
797
        elif trode == "c":
×
798
            datax = cellsvec[-Nvol["c"]:]
×
799
        if data_only:
×
800
            plt.close(fig)
×
801
            return datax, datay
×
802
        # returns tuble of line objects, thus comma
803
        line1, = ax.plot(datax, datay[t0ind])
×
804

805
        def init():
×
806
            line1.set_ydata(np.ma.array(datax, mask=True))
×
807
            ttl.set_text('')
×
808
            return line1, ttl
×
809

810
        def animate(tind):
×
811
            line1.set_ydata(datay[tind])
×
812
            t_current = times[tind]
×
813
            tfrac = (t_current - tmin)/(tmax - tmin) * 100
×
814
            ttl.set_text(ttl_fmt.format(perc=tfrac))
×
815
            return line1, ttl
×
816

817
    # plot cycling plots
818
    if plot_type[0:5] == "cycle":
×
819
        current = utils.get_dict_key(data, pfx + 'current') / td  # gives us C-rates in /s
×
820
        # the capacity we calculate is the apparent capacity from experimental measurement,
821
        # not the real capacity of the electrode
822
        charge_discharge = utils.get_dict_key(data, pfx + "CCCVCPcycle_charge_discharge")
×
823
        ind_start_disch, ind_end_disch, ind_start_ch, ind_end_ch = \
×
824
            utils.get_negative_sign_change_arrays(charge_discharge)
825
        # get segments that indicate 1s for the charge/discharge segments, one for each
826
        # charge/discharge in the y axis
827
        cycle_numbers = np.arange(1, tot_cycle + 1)  # get cycle numbers on x axis
×
828
        # first figure out the number of cycles
829
        # find mass of limiting electrode
830
        # get the currents (are multiplied by 0 if it is not the segment we want)
831
        currents = cap * current  # A/m^2
×
832
        voltage = (Vstd - (k*Tref/e)*utils.get_dict_key(data, pfx + 'phi_applied'))  # in V
×
833
        # Q(t) array for the ith cycle for discharge_cap_func[i]
834
        discharge_cap_func = np.zeros((tot_cycle, 400))
×
835
        charge_cap_func = np.zeros((tot_cycle, 400))
×
836
        # V(t) array for the ith cycle for discharge_volt[i]
837
        discharge_volt = np.zeros((tot_cycle, 400))
×
838
        charge_volt = np.zeros((tot_cycle, 400))
×
839
        # total discharge capacity
840
        discharge_capacities = np.zeros(tot_cycle)
×
841
        charge_capacities = np.zeros(tot_cycle)
×
842
        discharge_total_cap = np.zeros((tot_cycle, len(times)))
×
843
        charge_total_cap = np.zeros((tot_cycle, len(times)))
×
844
        # only save discharge_cap_func and discharge_volt up to those values
845
        for j in range(tot_cycle):
×
846
            print("hi", ind_start_disch[j], ind_end_disch[j])
×
847
            discharge_cap_temp = integrate.cumtrapz(
×
848
                currents[ind_start_disch[j]:ind_end_disch[j]],
849
                times[ind_start_disch[j]:ind_end_disch[j]]*td, initial=0)/3600
850
            # get the total one padded with zeros so we can sum
851
            discharge_total_cap[j,:] = np.append(np.zeros(ind_start_disch[j]),
×
852
                                                 np.append(discharge_cap_temp, np.zeros(
853
                                                     len(currents)-ind_end_disch[j])))
854
            # integrate Q = int(I)dt, units in A hr/m^2
855
            # Ahr. pad w zero because the first number is always 0
856
            dis_volt_temp = voltage[ind_start_disch[j]:ind_end_disch[j]]
×
857
            # only fill the interpolated values
858
            discharge_volt[j,:] = np.linspace(dis_volt_temp[0], dis_volt_temp[-1], 400)
×
859
            f = interp1d(dis_volt_temp, discharge_cap_temp, fill_value='extrapolate')
×
860
            discharge_cap_func[j,:] = f(np.linspace(dis_volt_temp[0], dis_volt_temp[-1], 400))
×
861
            discharge_capacities[j] = discharge_cap_func[j,-1] * 1000  # mAh/m^2
×
862

863
        for j in range(tot_cycle):
×
864
            charge_cap_temp = integrate.cumtrapz(
×
865
                currents[ind_start_ch[j]:ind_end_ch[j]],
866
                times[ind_start_ch[j]:ind_end_ch[j]]*td, initial=0)/3600
867
            # get the total one padded with zeros so we can sum
868
            charge_total_cap[j,:] = np.append(np.zeros(ind_start_ch[j]),
×
869
                                              np.append(charge_cap_temp, np.zeros(
870
                                                  len(currents)-ind_end_ch[j])))
871
            # integrate Q = int(I)dt, units in A hr/m^2
872
            # Ahr. pad w zero because the first number is always 0
873
            ch_volt_temp = voltage[ind_start_ch[j]:ind_end_ch[j]]
×
874
            # only fill the interpolated values
875
            charge_volt[j,:] = np.linspace(ch_volt_temp[0], ch_volt_temp[-1], 400)
×
876
            f = interp1d(ch_volt_temp, charge_cap_temp, fill_value='extrapolate')
×
877
            charge_cap_func[j,:] = f(np.linspace(ch_volt_temp[0], ch_volt_temp[-1], 400))
×
878
            charge_capacities[j] = charge_cap_func[j,-1] * 1000  # mAh/m^2
×
879

880
        # units will be in Ahr/m^2*m^2 = Ah
881
        # discharge_voltages and Q store each of the V, Q data. cycle i is stored in row i for
882
        # both of these arrays
883
        gravimetric_caps_disch = -discharge_capacities/(config["P_L"][limtrode] * (
×
884
            1-config["poros"][limtrode]) * (config["L"][limtrode]*config['L_ref']))  # mAh/m^3
885
        gravimetric_caps_ch = charge_capacities/(config["P_L"][limtrode] * (
×
886
            1-config["poros"][limtrode]) * (config["L"][limtrode]*config['L_ref']))  # mAh/m^3
887
        # discharge_capacities = np.trapz(discharge_currents, times*td) *1000/3600
888
        # #mAh/m^2 since int over time
889
        # get the total capacites that we output in the data file with padded zeros
890
        discharge_total_capacities = np.sum(discharge_total_cap, axis=0)
×
891
        charge_total_capacities = np.sum(charge_total_cap, axis=0)
×
892

893
        # for QV or dQdV plots:
894
        # plot all cycles if less than six cycles, otherwise use equal spacing and plot six
895
        plot_indexes = 0
×
896
        if tot_cycle > 7:
×
897
            plot_indexes = (np.arange(0, 7)*(tot_cycle-1)/6).astype(int)
×
898
        else:
899
            plot_indexes = np.arange(0, tot_cycle)
×
900

901
        if plot_type == "cycle_capacity":  # plots discharge capacity
×
902
            if len(gravimetric_caps_disch) != len(cycle_numbers):
×
903
                # if we weren't able to complete the simulation, we only plot up to the
904
                # cycle we were able to calculate
905
                cycle_numbers = cycle_numbers[:len(gravimetric_caps_disch)]
×
906
            if data_only:
×
907
                return cycle_numbers, gravimetric_caps_ch, gravimetric_caps_disch
×
908
            fig, ax = plt.subplots(figsize=figsize)
×
909
            ax.plot(cycle_numbers, np.round(gravimetric_caps_ch, decimals=2), 'o', label='Charge')
×
910
            ax.plot(
×
911
                cycle_numbers,
912
                np.round(
913
                    gravimetric_caps_disch,
914
                    decimals=2),
915
                'o',
916
                label='Discharge')
917
            ax.legend()
×
918
            ax.set_xlabel("Cycle Number")
×
919
            ax.set_ylabel(r"Capacity [mAh/$m^3$]")
×
920
            ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(integer=True))
×
921
            if save_flag:
×
922
                fig.savefig("mpet_cycle_capacity.png", bbox_inches="tight")
×
923
            return fig, ax
×
924
        elif plot_type == "cycle_efficiency":
×
925
            # do we need to change this q because molweight changed? should be okay because Nm
926
            # still same efficiency = discharge_cap/charge_cap
927
            efficiencies = np.abs(np.divide(discharge_capacities, charge_capacities))
×
928
            if len(efficiencies) != len(cycle_numbers):
×
929
                # if we weren't able to complete the simulation, we only plot up to the
930
                # cycle we were able to calculate
931
                cycle_numbers = cycle_numbers[:len(efficiencies)]
×
932
            if data_only:
×
933
                return cycle_numbers, efficiencies
×
934
            fig, ax = plt.subplots(figsize=figsize)
×
935
            ax.plot(cycle_numbers, efficiencies, 'o')
×
936
            ax.set_xlabel("Cycle Number")
×
937
            ax.set_ylabel("Cycle Efficiency")
×
938
            ax.set_ylim(0, 1.1)
×
939
            ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(integer=True))
×
940
            if save_flag:
×
941
                fig.savefig("mpet_cycle_efficiency.png", bbox_inches="tight")
×
942
            return fig, ax
×
943
        elif plot_type == "cycle_cap_frac":
×
944
            discharge_cap_fracs = discharge_capacities/discharge_capacities[0]
×
945
            if len(discharge_cap_fracs) != len(cycle_numbers):
×
946
                # if we weren't able to complete the simulation, we only plot up to the
947
                # cycle we were able to calculate
948
                cycle_numbers = cycle_numbers[:len(discharge_cap_fracs)]
×
949
            if data_only:
×
950
                return cycle_numbers, discharge_cap_fracs
×
951
            # normalize by the first discharge capacity
952
            fig, ax = plt.subplots(figsize=figsize)
×
953
            ax.plot(cycle_numbers, np.round(discharge_cap_fracs, decimals=2), 'o')
×
954
            ax.set_xlabel("Cycle Number")
×
955
            ax.set_ylabel("State of Health")
×
956
            ax.set_ylim(0, 1.1)
×
957
            ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(integer=True))
×
958
            if save_flag:
×
959
                fig.savefig("mpet_cycle_cap_frac.png", bbox_inches="tight")
×
960
            return fig, ax
×
961
        elif plot_type == "cycle_Q_V":
×
962

963
            if data_only:
×
964
                return discharge_volt, discharge_cap_func
×
965

966
            fig, ax = plt.subplots(figsize=figsize)
×
967
            for i in plot_indexes:
×
968
                ax.plot(discharge_cap_func[i,:], discharge_volt[i,:])
×
969
            ax.legend(plot_indexes+1)
×
970
            ax.set_xlabel(r'Capacity (A hr/m$^2$)')
×
971
            ax.set_ylabel("Voltage (V)")
×
972
            ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(integer=True))
×
973
            if save_flag:
×
974
                fig.savefig("mpet_Q_V.png", bbox_inches="tight")
×
975
            return fig, ax
×
976

977
        elif plot_type == "cycle_dQ_dV":
×
978
            # nondimensionalize dQ and dV by initial discharge cap
979
            max_cap = discharge_capacities[0]/1000  # in Ahr/m^2
×
980
            # calculates dQdV along each curve
981
            dQ_dV = np.divide(np.diff(discharge_cap_func/max_cap, axis=1),
×
982
                              np.diff(discharge_volt, axis=1))
983
            volt = (discharge_volt[:,1:]+discharge_volt[:,:-1])/2
×
984

985
            if data_only:
×
986
                return volt, dQ_dV
×
987

988
            fig, ax = plt.subplots(figsize=figsize)
×
989
            for i in plot_indexes:
×
990
                ax.plot(discharge_volt[i,:], dQ_dV[i,:])
×
991
            ax.legend(plot_indexes+1)
×
992
            ax.set_xlabel("Voltage (V)")
×
993
            ax.set_ylabel('d%Q/dV (%/V))')
×
994
            ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(integer=True))
×
995
            if save_flag:
×
996
                fig.savefig("mpet_dQ_dV.png", bbox_inches="tight")
×
997
            return fig, ax
×
998

999
        elif plot_type == "cycle_data":
×
1000
            discharge_energies = discharge_total_capacities*voltage
×
1001
            charge_energies = charge_total_capacities*voltage
×
1002
            if data_only:
×
1003
                return discharge_total_capacities, charge_total_capacities, discharge_energies, \
×
1004
                    charge_energies
1005
            return
×
1006

1007
    else:
1008
        raise Exception("Unexpected plot type argument. See README.md.")
×
1009

1010
    ani = manim.FuncAnimation(
×
1011
        fig, animate, frames=numtimes, interval=50, blit=True, repeat=False, init_func=init)
1012
    if save_flag:
×
1013
        fig.tight_layout()
×
1014
        ani.save("mpet_{type}.mp4".format(type=plot_type), fps=25, bitrate=5500)
×
1015

1016
    return fig, ax, ani
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc