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

TRI-AMDD / mpet / 8476080312

29 Mar 2024 01:46AM UTC coverage: 55.461% (-7.2%) from 62.648%
8476080312

Pull #126

github

d-cogswell
Adds a benchmark section to docs.
Pull Request #126: v1.0.0

2351 of 4239 relevant lines covered (55.46%)

2.22 hits per line

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

87.43
/mpet/sim.py
1
"""This module defines the actual simulation to be carried out.
2

3
In this, the model(s) are created and their variables are given initial conditions (if they are
4
differential variables) or initial guesses (if they never appear in equations in which they have
5
been differentiated in time).
6
"""
7
import sys
4✔
8
import os.path as osp
4✔
9

10
import daetools.pyDAE as dae
4✔
11
import numpy as np
4✔
12
import h5py
4✔
13

14
import mpet.mod_cell as mod_cell
4✔
15
import mpet.daeVariableTypes
4✔
16
import mpet.utils as utils
4✔
17
from mpet.config import constants
4✔
18

19

20
class SimMPET(dae.daeSimulation):
4✔
21
    def __init__(self, config, tScale=None):
4✔
22
        dae.daeSimulation.__init__(self)
4✔
23
        self.config = config
4✔
24
        self.tScale = tScale
4✔
25
        config["currPrev"] = 0.
4✔
26
        config["phiPrev"] = 0.
4✔
27
        if config["prevDir"] and config["prevDir"] != "false":
4✔
28
            # Get the data mat file from prevDir
29
            self.dataPrev = osp.join(config["prevDir"], "output_data")
4✔
30
            data = utils.open_data_file(self.dataPrev)
4✔
31
            config["currPrev"] = utils.get_dict_key(data, "current", final=True)
4✔
32
            config["phiPrev"] = utils.get_dict_key(data, "phi_applied", final=True)
4✔
33

34
            # close file if it is a h5py file
35
            if isinstance(data, h5py._hl.files.File):
4✔
36
                data.close()
4✔
37

38
        # Set absolute tolerances for variableTypes
39
        mpet.daeVariableTypes.mole_frac_t.AbsoluteTolerance = config["absTol"]
4✔
40
        mpet.daeVariableTypes.conc_t.AbsoluteTolerance = config["absTol"]
4✔
41
        mpet.daeVariableTypes.elec_pot_t.AbsoluteTolerance = config["absTol"]
4✔
42

43
        # Define the model we're going to simulate
44
        self.m = mod_cell.ModCell(config, "mpet")
4✔
45

46
    def SetUpParametersAndDomains(self):
4✔
47
        # Domains
48
        config = self.config
4✔
49
        if config["Nvol"]["s"]:
4✔
50
            self.m.DmnCell["s"].CreateArray(config["Nvol"]["s"])
4✔
51
        for tr in config["trodes"]:
4✔
52
            self.m.DmnCell[tr].CreateArray(config["Nvol"][tr])
4✔
53
            self.m.DmnPart[tr].CreateArray(config["Npart"][tr])
4✔
54
            for i in range(config["Nvol"][tr]):
4✔
55
                for j in range(config["Npart"][tr]):
4✔
56
                    self.m.particles[tr][i, j].Dmn.CreateArray(
4✔
57
                        int(config["psd_num"][tr][i,j]))
58
                    if config[f"simInterface_{tr}"]:
4✔
59
                        self.m.interfaces[tr][i, j].Dmn.CreateArray(
4✔
60
                            int(config["Nvol_i"]))
61

62
    def SetUpVariables(self):
4✔
63
        config = self.config
4✔
64
        Nvol = config["Nvol"]
4✔
65
        Npart = config["Npart"]
4✔
66
        phi_cathode = config["phi_cathode"]
4✔
67
        if not config["prevDir"] or config["prevDir"] == "false":
4✔
68
            # Solids
69
            for tr in config["trodes"]:
4✔
70
                cs0 = config['cs0'][tr]
4✔
71
                # Guess initial filling fractions
72
                self.m.ffrac[tr].SetInitialGuess(cs0)
4✔
73
                for i in range(Nvol[tr]):
4✔
74
                    # Guess initial volumetric reaction rates
75
                    self.m.R_Vp[tr].SetInitialGuess(i, 0.0)
4✔
76
                    # Guess initial value for the potential of the
77
                    # electrodes
78
                    if tr == "a":  # anode
4✔
79
                        self.m.phi_bulk[tr].SetInitialGuess(i, config["a", "phiRef"])
4✔
80
                    else:  # cathode
81
                        self.m.phi_bulk[tr].SetInitialGuess(i, phi_cathode)
4✔
82
                    for j in range(Npart[tr]):
4✔
83
                        Nij = config["psd_num"][tr][i,j]
4✔
84
                        part = self.m.particles[tr][i,j]
4✔
85
                        # Guess initial value for the average solid
86
                        # concentrations and set initial value for
87
                        # solid concentrations
88
                        solidType = self.config[tr, "type"]
4✔
89
                        if solidType in constants.one_var_types:
4✔
90
                            part.cbar.SetInitialGuess(cs0)
4✔
91
                            for k in range(Nij):
4✔
92
                                part.c.SetInitialCondition(k, cs0)
4✔
93
                                if self.config["c","type"] in ["ACR_Diff"]:
4✔
94
                                    part.c_left_GP.SetInitialGuess(cs0)
×
95
                                    part.c_right_GP.SetInitialGuess(cs0)
×
96
                        elif solidType in constants.two_var_types:
4✔
97
                            part.c1bar.SetInitialGuess(cs0)
4✔
98
                            part.c2bar.SetInitialGuess(cs0)
4✔
99
                            part.cbar.SetInitialGuess(cs0)
4✔
100
                            epsrnd = 0.0001
4✔
101
                            rnd1 = epsrnd*(np.random.rand(Nij) - 0.5)
4✔
102
                            rnd2 = epsrnd*(np.random.rand(Nij) - 0.5)
4✔
103
                            rnd1 -= np.mean(rnd1)
4✔
104
                            rnd2 -= np.mean(rnd2)
4✔
105
                            for k in range(Nij):
4✔
106
                                part.c1.SetInitialCondition(k, cs0+rnd1[k])
4✔
107
                                part.c2.SetInitialCondition(k, cs0+rnd2[k])
4✔
108

109
            # Cell potential initialization
110
            if config['tramp'] > 0:
4✔
111
                phi_guess = 0
4✔
112
            elif config['profileType'] == 'CV':
4✔
113
                phi_guess = config['Vset']
×
114
            elif config['profileType'] == 'CVsegments':
4✔
115
                phi_guess = config['segments'][0][0]
×
116
            else:
117
                phi_guess = 0
4✔
118
            self.m.phi_applied.SetInitialGuess(phi_guess)
4✔
119
            self.m.phi_cell.SetInitialGuess(phi_guess)
4✔
120

121
            # Initialize the ghost points used for boundary conditions
122
            if not self.m.SVsim:
4✔
123
                self.m.c_lyteGP_L.SetInitialGuess(config["c0"])
4✔
124
                self.m.phi_lyteGP_L.SetInitialGuess(0)
4✔
125

126
            # Separator electrolyte initialization
127
            if config["Nvol"]["s"]:
4✔
128
                for i in range(Nvol["s"]):
4✔
129
                    self.m.c_lyte["s"].SetInitialCondition(i, config['c0'])
4✔
130
                    self.m.phi_lyte["s"].SetInitialGuess(i, 0)
4✔
131

132
            # Anode and cathode electrolyte initialization
133
            for tr in config["trodes"]:
4✔
134
                for i in range(Nvol[tr]):
4✔
135
                    self.m.c_lyte[tr].SetInitialCondition(i, config['c0'])
4✔
136
                    self.m.phi_lyte[tr].SetInitialGuess(i, 0)
4✔
137

138
                    # Set electrolyte concentration in each particle
139
                    for j in range(Npart[tr]):
4✔
140
                        self.m.particles[tr][i,j].c_lyte.SetInitialGuess(config["c0"])
4✔
141
                        # Set concentration and potential in interface region
142
                        if config[f"simInterface_{tr}"]:
4✔
143
                            for k in range(config["Nvol_i"]):
4✔
144
                                self.m.interfaces[tr][i,j].c.SetInitialCondition(k,
4✔
145
                                                                                 config["c0_int"])
146
                                self.m.interfaces[tr][i,j].phi.SetInitialGuess(k, 0)
4✔
147

148
            # set up cycling stuff
149
            if config['profileType'] == "CCCVCPcycle":
4✔
150
                cyc = self.m.cycle
4✔
151
                cyc.last_current.AssignValue(0)
4✔
152
                cyc.last_phi_applied.AssignValue(phi_guess)
4✔
153
                cyc.maccor_cycle_counter.AssignValue(1)
4✔
154
                cyc.maccor_step_number.SetInitialGuess(1)
4✔
155

156
                # used to determine new time cutoffs at each section
157
                cyc.time_counter.AssignValue(0)
4✔
158
                cyc.cycle_number.AssignValue(1)
4✔
159

160
        else:
161
            dPrev = self.dataPrev
4✔
162
            data = utils.open_data_file(dPrev)
4✔
163
            for tr in config["trodes"]:
4✔
164
                self.m.ffrac[tr].SetInitialGuess(
4✔
165
                    utils.get_dict_key(data, "ffrac_" + tr, final=True))
166
                for i in range(Nvol[tr]):
4✔
167
                    self.m.R_Vp[tr].SetInitialGuess(
4✔
168
                        i, data["R_Vp_" + tr][-1,i])
169
                    self.m.phi_bulk[tr].SetInitialGuess(
4✔
170
                        i, data["phi_bulk_" + tr][-1,i])
171
                    for j in range(Npart[tr]):
4✔
172
                        Nij = config["psd_num"][tr][i,j]
4✔
173
                        part = self.m.particles[tr][i,j]
4✔
174
                        solidType = self.config[tr, "type"]
4✔
175
                        partStr = "partTrode{l}vol{i}part{j}_".format(
4✔
176
                            l=tr, i=i, j=j)
177

178
                        # Set the inlet port variables for each particle
179
                        part.c_lyte.SetInitialGuess(data["c_lyte_" + tr][-1,i])
4✔
180
                        part.phi_lyte.SetInitialGuess(data["phi_lyte_" + tr][-1,i])
4✔
181
                        part.phi_m.SetInitialGuess(data["phi_bulk_" + tr][-1,i])
4✔
182

183
                        if solidType in constants.one_var_types:
4✔
184
                            part.cbar.SetInitialGuess(
4✔
185
                                utils.get_dict_key(data, partStr + "cbar", final=True))
186
                            for k in range(Nij):
4✔
187
                                part.c.SetInitialCondition(
4✔
188
                                    k, data[partStr + "c"][-1,k])
189
                        elif solidType in constants.two_var_types:
×
190
                            part.c1bar.SetInitialGuess(
×
191
                                utils.get_dict_key(data, partStr + "c1bar", final=True))
192
                            part.c2bar.SetInitialGuess(
×
193
                                utils.get_dict_key(data, partStr + "c2bar", final=True))
194
                            part.cbar.SetInitialGuess(
×
195
                                utils.get_dict_key(data, partStr + "cbar", final=True))
196
                            for k in range(Nij):
×
197
                                part.c1.SetInitialCondition(
×
198
                                    k, data[partStr + "c1"][-1,k])
199
                                part.c2.SetInitialCondition(
×
200
                                    k, data[partStr + "c2"][-1,k])
201
            if config["Nvol"]["s"]:
4✔
202
                for i in range(Nvol["s"]):
×
203
                    self.m.c_lyte["s"].SetInitialCondition(
×
204
                        i, data["c_lyte_s"][-1,i])
205
                    self.m.phi_lyte["s"].SetInitialGuess(
×
206
                        i, data["phi_lyte_s"][-1,i])
207
            for tr in config["trodes"]:
4✔
208
                for i in range(Nvol[tr]):
4✔
209
                    self.m.c_lyte[tr].SetInitialCondition(
4✔
210
                        i, data["c_lyte_" + tr][-1,i])
211
                    self.m.phi_lyte[tr].SetInitialGuess(
4✔
212
                        i, data["phi_lyte_" + tr][-1,i])
213

214
            # Read in the ghost point values
215
            if not self.m.SVsim:
4✔
216
                self.m.c_lyteGP_L.SetInitialGuess(
4✔
217
                    utils.get_dict_key(data, "c_lyteGP_L", final=True))
218
                self.m.phi_lyteGP_L.SetInitialGuess(
4✔
219
                    utils.get_dict_key(data, "phi_lyteGP_L", final=True))
220

221
            # Guess the initial cell voltage
222
            self.m.phi_applied.SetInitialGuess(utils.get_dict_key(data, "phi_applied", final=True))
4✔
223
            self.m.phi_cell.SetInitialGuess(utils.get_dict_key(data, "phi_cell", final=True))
4✔
224

225
            # set up cycling stuff
226
            if config['profileType'] == "CCCVCPcycle":
4✔
227
                cycle_header = "CCCVCPcycle_"
×
228
                cyc = self.m.cycle
×
229
                cyc.last_current.AssignValue(
×
230
                    utils.get_dict_key(
231
                        data,
232
                        cycle_header
233
                        + "last_current",
234
                        final=True))
235
                cyc.last_phi_applied.AssignValue(utils.get_dict_key(
×
236
                    data, cycle_header + "last_phi_applied", final=True))
237
                cyc.maccor_cycle_counter.AssignValue(utils.get_dict_key(
×
238
                    data, cycle_header + "maccor_cycle_counter", final=True))
239
                cyc.maccor_step_number.AssignValue(utils.get_dict_key(
×
240
                    data, cycle_header + "maccor_step_number", final=True))
241

242
                # used to determine new time cutoffs at each section
243
                cyc.time_counter.AssignValue(
×
244
                    utils.get_dict_key(
245
                        data,
246
                        cycle_header
247
                        + "time_counter",
248
                        final=True))
249
                cyc.cycle_number.AssignValue(
×
250
                    utils.get_dict_key(
251
                        data,
252
                        cycle_header
253
                        + "cycle_number",
254
                        final=True))
255

256
            # close file if it is a h5py file
257
            if isinstance(data, h5py._hl.files.File):
4✔
258
                data.close()
4✔
259

260
        # The simulation runs when the endCondition is 0
261
        self.m.endCondition.AssignValue(0)
4✔
262

263
    def Run(self):
4✔
264
        """
265
        Overload the simulation "Run" function so that the simulation
266
        terminates when the specified condition is satisfied.
267
        """
268
        tScale = self.tScale
4✔
269
        for nextTime in self.ReportingTimes:
4✔
270

271
            # Print logging information
272
            progressStr = "{0} {1}".format(self.Log.PercentageDone,self.Log.ETA)
4✔
273
            message = "Integrating from {t0:.2f} to {t1:.2f} s ...".format(
4✔
274
                t0=self.CurrentTime*tScale, t1=nextTime*tScale)
275
            sys.stdout.write(f"\r{progressStr[:-1]} {message}")
4✔
276
            sys.stdout.flush()
4✔
277

278
            # Integrate the equations
279
            self.IntegrateUntilTime(nextTime, dae.eStopAtModelDiscontinuity, True)
4✔
280
            self.ReportData(self.CurrentTime)
4✔
281
            self.Log.SetProgress(int(100. * self.CurrentTime/self.TimeHorizon))
4✔
282

283
            # Break when an end condition has been met
284
            if self.m.endCondition.npyValues:
4✔
285
                description = mod_cell.endConditions[int(self.m.endCondition.npyValues)]
4✔
286
                sys.stdout.write("\nEnding condition: " + description)
4✔
287
                break
4✔
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