Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tags to Experiment #2593

Merged
merged 9 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# [Unreleased](https://github.com/pybamm-team/PyBaMM/)

## Features

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

# [v22.12](https://github.com/pybamm-team/PyBaMM/tree/v22.12) - 2022-12-31

## Features
Expand Down
29 changes: 28 additions & 1 deletion pybamm/experiments/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def __repr__(self):

def read_string(self, cond, drive_cycles):
"""
Convert a string to a tuple of the right format
Convert a string to a dictionary of the right format

Parameters
----------
Expand All @@ -163,6 +163,11 @@ def read_string(self, cond, drive_cycles):
cond_CC, cond_CV = cond.split(" then ")
op_CC = self.read_string(cond_CC, drive_cycles)
op_CV = self.read_string(cond_CV, drive_cycles)
tag_CC = op_CC["tags"] or []
tag_CV = op_CV["tags"] or []
tags = list(np.unique(tag_CC + tag_CV))
if len(tags) == 0:
tags = None
outputs = {
"type": "CCCV",
"Voltage input [V]": op_CV["Voltage input [V]"],
Expand All @@ -171,13 +176,21 @@ def read_string(self, cond, drive_cycles):
"dc_data": None,
"string": cond,
"events": op_CV["events"],
"tags": tags,
}
if "Current input [A]" in op_CC:
outputs["Current input [A]"] = op_CC["Current input [A]"]
else:
outputs["C-rate input [-]"] = op_CC["C-rate input [-]"]
return outputs

# Read tags
if " [" in cond:
cond, tag_str = cond.split(" [")
tags = tag_str[0:-1].split(",")
else:
tags = None

# Read period
if " period)" in cond:
cond, time_period = cond.split(" (")
Expand Down Expand Up @@ -254,6 +267,7 @@ def read_string(self, cond, drive_cycles):
"dc_data": dc_data,
"string": cond,
"events": events,
"tags": tags,
}

def unit_to_type(self, electric):
Expand Down Expand Up @@ -440,3 +454,16 @@ def is_cccv(self, step, next_step):
if op["events"] == {k: v for k, v in next_op.items() if k in op["events"]}:
return True
return False

def search_tag(self, tag):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like this should use the "tags" entry of the dictionary?

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original idea, but then I couldn't find a clear mapping between operating_conditions and the actual cycle number. Going forward, when we implement #2322, that will have to be addressed and will make more sense to use the dictionary. But for the time being, processing the strings is fast enough.

cycles = []
for i, cycle in enumerate(self.operating_conditions_cycles):
for cond in cycle:
if " [" in cond:
cond, tag_str = cond.split(" [")
tags = tag_str[0:-1].split(",")
if tag in tags:
cycles.append(i)
break

return cycles
58 changes: 55 additions & 3 deletions tests/unit/test_experiments/test_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ def test_read_strings(self):

experiment = pybamm.Experiment(
[
"Discharge at 1C for 0.5 hours",
"Discharge at C/20 for 0.5 hours",
"Discharge at 1C for 0.5 hours [tag1]",
"Discharge at C/20 for 0.5 hours [tag2,tag3]",
"Charge at 0.5 C for 45 minutes",
"Discharge at 1 A for 0.5 hours",
"Charge at 200 mA for 45 minutes (1 minute period)",
"Discharge at 1W for 0.5 hours",
"Charge at 200mW for 45 minutes",
"Rest for 10 minutes (5 minute period)",
"Rest for 10 minutes (5 minute period) [tag1,tag3]",
"Hold at 1V for 20 seconds",
"Charge at 1 C until 4.1V",
"Hold at 4.1 V until 50mA",
Expand All @@ -53,6 +53,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Discharge at 1C for 0.5 hours",
"events": None,
"tags": ["tag1"],
},
{
"C-rate input [-]": 0.05,
Expand All @@ -62,6 +63,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Discharge at C/20 for 0.5 hours",
"events": None,
"tags": ["tag2", "tag3"],
},
{
"C-rate input [-]": -0.5,
Expand All @@ -71,6 +73,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Charge at 0.5 C for 45 minutes",
"events": None,
"tags": None,
},
{
"Current input [A]": 1,
Expand All @@ -80,6 +83,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Discharge at 1 A for 0.5 hours",
"events": None,
"tags": None,
},
{
"Current input [A]": -0.2,
Expand All @@ -89,6 +93,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Charge at 200 mA for 45 minutes",
"events": None,
"tags": None,
},
{
"Power input [W]": 1,
Expand All @@ -98,6 +103,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Discharge at 1W for 0.5 hours",
"events": None,
"tags": None,
},
{
"Power input [W]": -0.2,
Expand All @@ -107,6 +113,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Charge at 200mW for 45 minutes",
"events": None,
"tags": None,
},
{
"Current input [A]": 0,
Expand All @@ -116,6 +123,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Rest for 10 minutes",
"events": None,
"tags": ["tag1", "tag3"],
},
{
"Voltage input [V]": 1,
Expand All @@ -125,6 +133,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Hold at 1V for 20 seconds",
"events": None,
"tags": None,
},
{
"C-rate input [-]": -1,
Expand All @@ -134,6 +143,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Charge at 1 C until 4.1V",
"events": {"Voltage input [V]": 4.1, "type": "voltage"},
"tags": None,
},
{
"Voltage input [V]": 4.1,
Expand All @@ -143,6 +153,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Hold at 4.1 V until 50mA",
"events": {"Current input [A]": 0.05, "type": "current"},
"tags": None,
},
{
"Voltage input [V]": 3,
Expand All @@ -152,6 +163,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Hold at 3V until C/50",
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
"tags": None,
},
{
"C-rate input [-]": 1 / 3,
Expand All @@ -161,6 +173,7 @@ def test_read_strings(self):
"dc_data": None,
"string": "Discharge at C/3 for 2 hours or until 2.5 V",
"events": {"Voltage input [V]": 2.5, "type": "voltage"},
"tags": None,
},
],
)
Expand All @@ -180,18 +193,21 @@ def test_read_strings(self):
self.assertEqual(experiment.operating_conditions[-3]["type"], "current")
self.assertEqual(experiment.operating_conditions[-3]["time"], time_0)
self.assertEqual(experiment.operating_conditions[-3]["period"], period_0)
self.assertEqual(experiment.operating_conditions[-3]["tags"], None)
np.testing.assert_array_equal(
experiment.operating_conditions[-2]["dc_data"], drive_cycle_1
)
self.assertEqual(experiment.operating_conditions[-2]["type"], "voltage")
self.assertEqual(experiment.operating_conditions[-2]["time"], time_1)
self.assertEqual(experiment.operating_conditions[-2]["period"], period_1)
self.assertEqual(experiment.operating_conditions[-2]["tags"], None)
np.testing.assert_array_equal(
experiment.operating_conditions[-1]["dc_data"], drive_cycle_2
)
self.assertEqual(experiment.operating_conditions[-1]["type"], "power")
self.assertEqual(experiment.operating_conditions[-1]["time"], time_2)
self.assertEqual(experiment.operating_conditions[-1]["period"], period_2)
self.assertEqual(experiment.operating_conditions[-1]["tags"], None)
self.assertEqual(experiment.period, 20)

def test_read_strings_cccv_combined(self):
Expand All @@ -217,6 +233,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Discharge at C/20 for 0.5 hours",
"events": None,
"tags": None,
},
{
"type": "CCCV",
Expand All @@ -227,6 +244,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Charge at 0.5 C until 1V then hold at 1V until C/50",
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
"tags": None,
},
{
"C-rate input [-]": 0.05,
Expand All @@ -236,6 +254,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Discharge at C/20 for 0.5 hours",
"events": None,
"tags": None,
},
],
)
Expand All @@ -259,6 +278,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Charge at 0.5 C until 2V",
"events": {"Voltage input [V]": 2, "type": "voltage"},
"tags": None,
},
{
"Voltage input [V]": 1,
Expand All @@ -268,6 +288,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Hold at 1V until C/50",
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
"tags": None,
},
],
)
Expand All @@ -289,6 +310,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Charge at 0.5 C for 2 minutes",
"events": None,
"tags": None,
},
{
"Voltage input [V]": 1,
Expand All @@ -298,6 +320,7 @@ def test_read_strings_cccv_combined(self):
"dc_data": None,
"string": "Hold at 1V until C/50",
"events": {"C-rate input [-]": 0.02, "type": "C-rate"},
"tags": None,
},
],
)
Expand All @@ -321,6 +344,7 @@ def test_cycle_unpacking(self):
"dc_data": None,
"string": "Discharge at C/20 for 0.5 hours",
"events": None,
"tags": None,
},
{
"C-rate input [-]": -0.2,
Expand All @@ -330,6 +354,7 @@ def test_cycle_unpacking(self):
"dc_data": None,
"string": "Charge at C/5 for 45 minutes",
"events": None,
"tags": None,
},
{
"C-rate input [-]": 0.05,
Expand All @@ -339,6 +364,7 @@ def test_cycle_unpacking(self):
"dc_data": None,
"string": "Discharge at C/20 for 0.5 hours",
"events": None,
"tags": None,
},
{
"C-rate input [-]": -0.2,
Expand All @@ -348,6 +374,7 @@ def test_cycle_unpacking(self):
"dc_data": None,
"string": "Charge at C/5 for 45 minutes",
"events": None,
"tags": None,
},
],
)
Expand Down Expand Up @@ -447,6 +474,31 @@ def test_termination(self):
["Discharge at 1 C for 20 seconds"], termination="1 capacity"
)

def test_search_tag(self):
experiment = pybamm.Experiment(
[
("Discharge at 1C for 0.5 hours [tag1]",),
"Discharge at C/20 for 0.5 hours [tag2,tag3]",
(
"Charge at 0.5 C for 45 minutes [tag2]",
"Discharge at 1 A for 0.5 hours [tag3]",
),
"Charge at 200 mA for 45 minutes (1 minute period) [tag5]",
(
"Discharge at 1W for 0.5 hours [tag4]",
"Charge at 200mW for 45 minutes [tag4]",
),
"Rest for 10 minutes (5 minute period) [tag1,tag3,tag4]",
]
)

self.assertEqual(experiment.search_tag("tag1"), [0, 5])
self.assertEqual(experiment.search_tag("tag2"), [1, 2])
self.assertEqual(experiment.search_tag("tag3"), [1, 2, 5])
self.assertEqual(experiment.search_tag("tag4"), [4, 5])
self.assertEqual(experiment.search_tag("tag5"), [3])
self.assertEqual(experiment.search_tag("no_tag"), [])


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