Skip to content

Commit fa5ec1b

Browse files
Merge pull request #2593 from pybamm-team/issue-2538-tag-experiments
Add tags to `Experiment`
2 parents 59b63d5 + 709cccb commit fa5ec1b

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# [Unreleased](https://github.com/pybamm-team/PyBaMM/)
22

3+
## Features
4+
5+
- Steps in `Experiment` can now be tagged and cycle numbers be searched based on those tags ([#2593](https://github.com/pybamm-team/PyBaMM/pull/2593)).
6+
37
# [v22.12](https://github.com/pybamm-team/PyBaMM/tree/v22.12) - 2022-12-31
48

59
## Features

pybamm/experiments/experiment.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def __repr__(self):
145145

146146
def read_string(self, cond, drive_cycles):
147147
"""
148-
Convert a string to a tuple of the right format
148+
Convert a string to a dictionary of the right format
149149
150150
Parameters
151151
----------
@@ -163,6 +163,11 @@ def read_string(self, cond, drive_cycles):
163163
cond_CC, cond_CV = cond.split(" then ")
164164
op_CC = self.read_string(cond_CC, drive_cycles)
165165
op_CV = self.read_string(cond_CV, drive_cycles)
166+
tag_CC = op_CC["tags"] or []
167+
tag_CV = op_CV["tags"] or []
168+
tags = list(np.unique(tag_CC + tag_CV))
169+
if len(tags) == 0:
170+
tags = None
166171
outputs = {
167172
"type": "CCCV",
168173
"Voltage input [V]": op_CV["Voltage input [V]"],
@@ -171,13 +176,21 @@ def read_string(self, cond, drive_cycles):
171176
"dc_data": None,
172177
"string": cond,
173178
"events": op_CV["events"],
179+
"tags": tags,
174180
}
175181
if "Current input [A]" in op_CC:
176182
outputs["Current input [A]"] = op_CC["Current input [A]"]
177183
else:
178184
outputs["C-rate input [-]"] = op_CC["C-rate input [-]"]
179185
return outputs
180186

187+
# Read tags
188+
if " [" in cond:
189+
cond, tag_str = cond.split(" [")
190+
tags = tag_str[0:-1].split(",")
191+
else:
192+
tags = None
193+
181194
# Read period
182195
if " period)" in cond:
183196
cond, time_period = cond.split(" (")
@@ -254,6 +267,7 @@ def read_string(self, cond, drive_cycles):
254267
"dc_data": dc_data,
255268
"string": cond,
256269
"events": events,
270+
"tags": tags,
257271
}
258272

259273
def unit_to_type(self, electric):
@@ -440,3 +454,16 @@ def is_cccv(self, step, next_step):
440454
if op["events"] == {k: v for k, v in next_op.items() if k in op["events"]}:
441455
return True
442456
return False
457+
458+
def search_tag(self, tag):
459+
cycles = []
460+
for i, cycle in enumerate(self.operating_conditions_cycles):
461+
for cond in cycle:
462+
if " [" in cond:
463+
cond, tag_str = cond.split(" [")
464+
tags = tag_str[0:-1].split(",")
465+
if tag in tags:
466+
cycles.append(i)
467+
break
468+
469+
return cycles

tests/unit/test_experiments/test_experiment.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ def test_read_strings(self):
2121

2222
experiment = pybamm.Experiment(
2323
[
24-
"Discharge at 1C for 0.5 hours",
25-
"Discharge at C/20 for 0.5 hours",
24+
"Discharge at 1C for 0.5 hours [tag1]",
25+
"Discharge at C/20 for 0.5 hours [tag2,tag3]",
2626
"Charge at 0.5 C for 45 minutes",
2727
"Discharge at 1 A for 0.5 hours",
2828
"Charge at 200 mA for 45 minutes (1 minute period)",
2929
"Discharge at 1W for 0.5 hours",
3030
"Charge at 200mW for 45 minutes",
31-
"Rest for 10 minutes (5 minute period)",
31+
"Rest for 10 minutes (5 minute period) [tag1,tag3]",
3232
"Hold at 1V for 20 seconds",
3333
"Charge at 1 C until 4.1V",
3434
"Hold at 4.1 V until 50mA",
@@ -53,6 +53,7 @@ def test_read_strings(self):
5353
"dc_data": None,
5454
"string": "Discharge at 1C for 0.5 hours",
5555
"events": None,
56+
"tags": ["tag1"],
5657
},
5758
{
5859
"C-rate input [-]": 0.05,
@@ -62,6 +63,7 @@ def test_read_strings(self):
6263
"dc_data": None,
6364
"string": "Discharge at C/20 for 0.5 hours",
6465
"events": None,
66+
"tags": ["tag2", "tag3"],
6567
},
6668
{
6769
"C-rate input [-]": -0.5,
@@ -71,6 +73,7 @@ def test_read_strings(self):
7173
"dc_data": None,
7274
"string": "Charge at 0.5 C for 45 minutes",
7375
"events": None,
76+
"tags": None,
7477
},
7578
{
7679
"Current input [A]": 1,
@@ -80,6 +83,7 @@ def test_read_strings(self):
8083
"dc_data": None,
8184
"string": "Discharge at 1 A for 0.5 hours",
8285
"events": None,
86+
"tags": None,
8387
},
8488
{
8589
"Current input [A]": -0.2,
@@ -89,6 +93,7 @@ def test_read_strings(self):
8993
"dc_data": None,
9094
"string": "Charge at 200 mA for 45 minutes",
9195
"events": None,
96+
"tags": None,
9297
},
9398
{
9499
"Power input [W]": 1,
@@ -98,6 +103,7 @@ def test_read_strings(self):
98103
"dc_data": None,
99104
"string": "Discharge at 1W for 0.5 hours",
100105
"events": None,
106+
"tags": None,
101107
},
102108
{
103109
"Power input [W]": -0.2,
@@ -107,6 +113,7 @@ def test_read_strings(self):
107113
"dc_data": None,
108114
"string": "Charge at 200mW for 45 minutes",
109115
"events": None,
116+
"tags": None,
110117
},
111118
{
112119
"Current input [A]": 0,
@@ -116,6 +123,7 @@ def test_read_strings(self):
116123
"dc_data": None,
117124
"string": "Rest for 10 minutes",
118125
"events": None,
126+
"tags": ["tag1", "tag3"],
119127
},
120128
{
121129
"Voltage input [V]": 1,
@@ -125,6 +133,7 @@ def test_read_strings(self):
125133
"dc_data": None,
126134
"string": "Hold at 1V for 20 seconds",
127135
"events": None,
136+
"tags": None,
128137
},
129138
{
130139
"C-rate input [-]": -1,
@@ -134,6 +143,7 @@ def test_read_strings(self):
134143
"dc_data": None,
135144
"string": "Charge at 1 C until 4.1V",
136145
"events": {"Voltage input [V]": 4.1, "type": "voltage"},
146+
"tags": None,
137147
},
138148
{
139149
"Voltage input [V]": 4.1,
@@ -143,6 +153,7 @@ def test_read_strings(self):
143153
"dc_data": None,
144154
"string": "Hold at 4.1 V until 50mA",
145155
"events": {"Current input [A]": 0.05, "type": "current"},
156+
"tags": None,
146157
},
147158
{
148159
"Voltage input [V]": 3,
@@ -152,6 +163,7 @@ def test_read_strings(self):
152163
"dc_data": None,
153164
"string": "Hold at 3V until C/50",
154165
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
166+
"tags": None,
155167
},
156168
{
157169
"C-rate input [-]": 1 / 3,
@@ -161,6 +173,7 @@ def test_read_strings(self):
161173
"dc_data": None,
162174
"string": "Discharge at C/3 for 2 hours or until 2.5 V",
163175
"events": {"Voltage input [V]": 2.5, "type": "voltage"},
176+
"tags": None,
164177
},
165178
],
166179
)
@@ -180,18 +193,21 @@ def test_read_strings(self):
180193
self.assertEqual(experiment.operating_conditions[-3]["type"], "current")
181194
self.assertEqual(experiment.operating_conditions[-3]["time"], time_0)
182195
self.assertEqual(experiment.operating_conditions[-3]["period"], period_0)
196+
self.assertEqual(experiment.operating_conditions[-3]["tags"], None)
183197
np.testing.assert_array_equal(
184198
experiment.operating_conditions[-2]["dc_data"], drive_cycle_1
185199
)
186200
self.assertEqual(experiment.operating_conditions[-2]["type"], "voltage")
187201
self.assertEqual(experiment.operating_conditions[-2]["time"], time_1)
188202
self.assertEqual(experiment.operating_conditions[-2]["period"], period_1)
203+
self.assertEqual(experiment.operating_conditions[-2]["tags"], None)
189204
np.testing.assert_array_equal(
190205
experiment.operating_conditions[-1]["dc_data"], drive_cycle_2
191206
)
192207
self.assertEqual(experiment.operating_conditions[-1]["type"], "power")
193208
self.assertEqual(experiment.operating_conditions[-1]["time"], time_2)
194209
self.assertEqual(experiment.operating_conditions[-1]["period"], period_2)
210+
self.assertEqual(experiment.operating_conditions[-1]["tags"], None)
195211
self.assertEqual(experiment.period, 20)
196212

197213
def test_read_strings_cccv_combined(self):
@@ -217,6 +233,7 @@ def test_read_strings_cccv_combined(self):
217233
"dc_data": None,
218234
"string": "Discharge at C/20 for 0.5 hours",
219235
"events": None,
236+
"tags": None,
220237
},
221238
{
222239
"type": "CCCV",
@@ -227,6 +244,7 @@ def test_read_strings_cccv_combined(self):
227244
"dc_data": None,
228245
"string": "Charge at 0.5 C until 1V then hold at 1V until C/50",
229246
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
247+
"tags": None,
230248
},
231249
{
232250
"C-rate input [-]": 0.05,
@@ -236,6 +254,7 @@ def test_read_strings_cccv_combined(self):
236254
"dc_data": None,
237255
"string": "Discharge at C/20 for 0.5 hours",
238256
"events": None,
257+
"tags": None,
239258
},
240259
],
241260
)
@@ -259,6 +278,7 @@ def test_read_strings_cccv_combined(self):
259278
"dc_data": None,
260279
"string": "Charge at 0.5 C until 2V",
261280
"events": {"Voltage input [V]": 2, "type": "voltage"},
281+
"tags": None,
262282
},
263283
{
264284
"Voltage input [V]": 1,
@@ -268,6 +288,7 @@ def test_read_strings_cccv_combined(self):
268288
"dc_data": None,
269289
"string": "Hold at 1V until C/50",
270290
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
291+
"tags": None,
271292
},
272293
],
273294
)
@@ -289,6 +310,7 @@ def test_read_strings_cccv_combined(self):
289310
"dc_data": None,
290311
"string": "Charge at 0.5 C for 2 minutes",
291312
"events": None,
313+
"tags": None,
292314
},
293315
{
294316
"Voltage input [V]": 1,
@@ -298,6 +320,7 @@ def test_read_strings_cccv_combined(self):
298320
"dc_data": None,
299321
"string": "Hold at 1V until C/50",
300322
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
323+
"tags": None,
301324
},
302325
],
303326
)
@@ -321,6 +344,7 @@ def test_cycle_unpacking(self):
321344
"dc_data": None,
322345
"string": "Discharge at C/20 for 0.5 hours",
323346
"events": None,
347+
"tags": None,
324348
},
325349
{
326350
"C-rate input [-]": -0.2,
@@ -330,6 +354,7 @@ def test_cycle_unpacking(self):
330354
"dc_data": None,
331355
"string": "Charge at C/5 for 45 minutes",
332356
"events": None,
357+
"tags": None,
333358
},
334359
{
335360
"C-rate input [-]": 0.05,
@@ -339,6 +364,7 @@ def test_cycle_unpacking(self):
339364
"dc_data": None,
340365
"string": "Discharge at C/20 for 0.5 hours",
341366
"events": None,
367+
"tags": None,
342368
},
343369
{
344370
"C-rate input [-]": -0.2,
@@ -348,6 +374,7 @@ def test_cycle_unpacking(self):
348374
"dc_data": None,
349375
"string": "Charge at C/5 for 45 minutes",
350376
"events": None,
377+
"tags": None,
351378
},
352379
],
353380
)
@@ -447,6 +474,31 @@ def test_termination(self):
447474
["Discharge at 1 C for 20 seconds"], termination="1 capacity"
448475
)
449476

477+
def test_search_tag(self):
478+
experiment = pybamm.Experiment(
479+
[
480+
("Discharge at 1C for 0.5 hours [tag1]",),
481+
"Discharge at C/20 for 0.5 hours [tag2,tag3]",
482+
(
483+
"Charge at 0.5 C for 45 minutes [tag2]",
484+
"Discharge at 1 A for 0.5 hours [tag3]",
485+
),
486+
"Charge at 200 mA for 45 minutes (1 minute period) [tag5]",
487+
(
488+
"Discharge at 1W for 0.5 hours [tag4]",
489+
"Charge at 200mW for 45 minutes [tag4]",
490+
),
491+
"Rest for 10 minutes (5 minute period) [tag1,tag3,tag4]",
492+
]
493+
)
494+
495+
self.assertEqual(experiment.search_tag("tag1"), [0, 5])
496+
self.assertEqual(experiment.search_tag("tag2"), [1, 2])
497+
self.assertEqual(experiment.search_tag("tag3"), [1, 2, 5])
498+
self.assertEqual(experiment.search_tag("tag4"), [4, 5])
499+
self.assertEqual(experiment.search_tag("tag5"), [3])
500+
self.assertEqual(experiment.search_tag("no_tag"), [])
501+
450502

451503
if __name__ == "__main__":
452504
print("Add -v for more debug output")

0 commit comments

Comments
 (0)