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

Issue 1500 print parameter info by submodel #3846

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
917a8d1
Add "by_submodel" feature and modify docstring
cringeyburger Dec 17, 2023
58586de
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Dec 17, 2023
d1513b8
Added line in CHANGELOG.md
cringeyburger Dec 17, 2023
699c329
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Dec 21, 2023
5d8c1fc
Implemented error handling if `get_parameter_info` method is used dir…
cringeyburger Dec 21, 2023
e4c05df
Implement changes to fix problem: Keep a track of what variables were…
cringeyburger Dec 21, 2023
27e7554
Implement changes to fix problem: Keep a track of what variables were…
cringeyburger Dec 21, 2023
bba8061
Created object `self.variables_by_submodel` which contains submodel's…
cringeyburger Dec 24, 2023
4ae39a5
Removed redundant comments.
cringeyburger Dec 24, 2023
087ba1b
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Dec 29, 2023
6c6b996
style: pre-commit fixes
pre-commit-ci[bot] Dec 29, 2023
5c22f90
Implemented "NotImplementedError" when user tries to use "get_paramet…
cringeyburger Dec 29, 2023
f5d5bf7
Implemented "NotImplementedError" when user tries to use "get_paramet…
cringeyburger Dec 29, 2023
c643b53
Updated Docstring of "get_parameter_info"
cringeyburger Dec 31, 2023
a792f49
Added Test Case for "NotImplementedError" when "get_parameter_info" o…
cringeyburger Jan 1, 2024
f0b6401
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Jan 3, 2024
2a156f7
Renamed variables, updated test, updated docstring
cringeyburger Jan 3, 2024
c23a55d
Added "_find_symbols_by_submodel" method and modified "get_parameter_…
cringeyburger Jan 4, 2024
d77b7cc
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Jan 13, 2024
30f66d5
Optimised "print_parameter_info" by simplification, improved readabil…
cringeyburger Jan 13, 2024
78c3785
Removed "calculate_max_lengths" and "format_table_row" from being nes…
cringeyburger Jan 15, 2024
b636650
Tests for "get_parameter_info" for both "by_submodel=False" and "by_s…
cringeyburger Jan 15, 2024
42a7fae
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Jan 21, 2024
c7ef815
Removed duplicate test in "test_base_submodel" for when "get_paramete…
cringeyburger Jan 21, 2024
7d3fb83
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Feb 1, 2024
a600580
added `test_get_parameter_info_submodel` test using custom model
cringeyburger Feb 1, 2024
f3ad7c4
added `test_print_parameter_info` and `test_print_parameter_info_subm…
cringeyburger Feb 3, 2024
29358f4
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Feb 3, 2024
77a126d
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Feb 11, 2024
be88384
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Feb 17, 2024
f1851e9
Modified `test_get_parameter_info_submodel` to include edge cases
cringeyburger Feb 17, 2024
77bbb0b
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
rtimms Feb 20, 2024
70aef8f
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Feb 25, 2024
ae0737b
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Feb 28, 2024
67a5eeb
formatted `test_base_model.py`
cringeyburger Feb 28, 2024
6575b9e
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Feb 28, 2024
a8f425c
Moved change log to unreleased
cringeyburger Feb 28, 2024
cdd8e74
Changed the formatted table's style
cringeyburger Feb 28, 2024
ac3788e
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Mar 3, 2024
8e20011
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Mar 4, 2024
fc83000
Added `UTF-8` encoding to the format table
cringeyburger Mar 5, 2024
e3e198c
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
arjxn-py Mar 5, 2024
56c08d5
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Mar 5, 2024
91832a5
Updated jupyter notebook `parameterization.ipynb` and added `UTF-8` e…
cringeyburger Mar 5, 2024
712556f
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Mar 11, 2024
e19bdd8
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Mar 17, 2024
eec5cac
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
kratman Mar 18, 2024
3310a94
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Mar 18, 2024
3302945
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
agriyakhetarpal Mar 23, 2024
00bf8e0
Merge branch 'pybamm-team:develop' into issue-1500-print-parameter-in…
cringeyburger Mar 26, 2024
800ffc4
added utf-8 encoding in PYBAMM_ENV
cringeyburger Mar 26, 2024
c2cbccc
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Mar 29, 2024
1384908
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
kratman Apr 2, 2024
9a5b80d
Resolve minor issues
Apr 2, 2024
8da6fa7
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
rtimms Apr 3, 2024
58e10e1
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
cringeyburger Apr 4, 2024
70ab920
Merge branch 'develop' into issue-1500-print-parameter-info-by-submodel
kratman Apr 4, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Modified `step` function to take an array of time `t_eval` as an argument and deprecated use of `npts`. ([#3627](https://github.com/pybamm-team/PyBaMM/pull/3627))
- Renamed "electrode diffusivity" to "particle diffusivity" as a non-breaking change with a deprecation warning ([#3624](https://github.com/pybamm-team/PyBaMM/pull/3624))
- Add support for BPX version 0.4.0 which allows for blended electrodes and user-defined parameters in BPX([#3414](https://github.com/pybamm-team/PyBaMM/pull/3414))
- Added `by_submodel` feature in `print_parameter_info` method to allow users to print parameters and types of submodels in a tabular and readable format ([#3628](https://github.com/pybamm-team/PyBaMM/pull/3628))

## Bug Fixes

Expand Down
297 changes: 151 additions & 146 deletions docs/source/examples/notebooks/parameterization/parameterization.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
PYBAMM_ENV = {
"SUNDIALS_INST": f"{homedir}/.local",
"LD_LIBRARY_PATH": f"{homedir}/.local/lib",
"PYTHONIOENCODING": "utf-8",
}
VENV_DIR = Path("./venv").resolve()

Expand Down
299 changes: 240 additions & 59 deletions pybamm/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def __init__(self, name="Unnamed model"):
self._algebraic = {}
self._initial_conditions = {}
self._boundary_conditions = {}
self._variables_by_submodel = {}
self._variables = pybamm.FuzzyDict({})
self._events = []
self._concatenated_rhs = None
Expand Down Expand Up @@ -421,83 +422,232 @@ def input_parameters(self):
self._input_parameters = self._find_symbols(pybamm.InputParameter)
return self._input_parameters

def get_parameter_info(self):
def get_parameter_info(self, by_submodel=False):
"""
Extracts the parameter information and returns it as a dictionary.
To get a list of all parameter-like objects without extra information,
use :py:attr:`model.parameters`.

Parameters
----------
by_submodel : bool, optional
Whether to return the parameter info sub-model wise or not (default False)
"""
parameter_info = {}
parameters = self._find_symbols(pybamm.Parameter)
for param in parameters:
parameter_info[param.name] = (param, "Parameter")

input_parameters = self._find_symbols(pybamm.InputParameter)
for input_param in input_parameters:
if not input_param.domain:
parameter_info[input_param.name] = (input_param, "InputParameter")
else:
parameter_info[input_param.name] = (
input_param,
f"InputParameter in {input_param.domain}",

if by_submodel:
for submodel_name, submodel_vars in self._variables_by_submodel.items():
submodel_info = {}
for var_name, var_symbol in submodel_vars.items():
if isinstance(var_symbol, pybamm.Parameter):
submodel_info[var_name] = (var_symbol, "Parameter")
elif isinstance(var_symbol, pybamm.InputParameter):
if not var_symbol.domain:
submodel_info[var_name] = (var_symbol, "InputParameter")
else:
submodel_info[var_name] = (
var_symbol,
f"InputParameter in {var_symbol.domain}",
)
elif isinstance(var_symbol, pybamm.FunctionParameter):
cringeyburger marked this conversation as resolved.
Show resolved Hide resolved
input_names = "', '".join(var_symbol.input_names)
submodel_info[var_name] = (
var_symbol,
f"FunctionParameter with inputs(s) '{input_names}'",
)
else:
submodel_info[var_name] = (var_symbol, "Unknown Type")

parameters = self._find_symbols_by_submodel(
pybamm.Parameter, submodel_name
)
for param in parameters:
submodel_info[param.name] = (param, "Parameter")

function_parameters = self._find_symbols(pybamm.FunctionParameter)
for func_param in function_parameters:
if func_param.name not in parameter_info:
input_names = "', '".join(func_param.input_names)
parameter_info[func_param.name] = (
func_param,
f"FunctionParameter with inputs(s) '{input_names}'",
input_parameters = self._find_symbols_by_submodel(
pybamm.InputParameter, submodel_name
)
for input_param in input_parameters:
if not input_param.domain:
submodel_info[input_param.name] = (
input_param,
"InputParameter",
)
else:
submodel_info[input_param.name] = (
input_param,
f"InputParameter in {input_param.domain}",
)

return parameter_info
function_parameters = self._find_symbols_by_submodel(
pybamm.FunctionParameter, submodel_name
)
for func_param in function_parameters:
if func_param.name not in parameter_info:
input_names = "', '".join(func_param.input_names)
submodel_info[func_param.name] = (
func_param,
f"FunctionParameter with inputs(s) '{input_names}'",
)

parameter_info[submodel_name] = submodel_info

else:
parameters = self._find_symbols(pybamm.Parameter)
for param in parameters:
parameter_info[param.name] = (param, "Parameter")

input_parameters = self._find_symbols(pybamm.InputParameter)
for input_param in input_parameters:
if not input_param.domain:
parameter_info[input_param.name] = (input_param, "InputParameter")
else:
parameter_info[input_param.name] = (
input_param,
f"InputParameter in {input_param.domain}",
)

def print_parameter_info(self):
"""Print parameter information in a formatted table from a dictionary of parameters"""
info = self.get_parameter_info()
max_param_name_length = 0
max_param_type_length = 0
function_parameters = self._find_symbols(pybamm.FunctionParameter)
for func_param in function_parameters:
if func_param.name not in parameter_info:
input_names = "', '".join(func_param.input_names)
parameter_info[func_param.name] = (
func_param,
f"FunctionParameter with inputs(s) '{input_names}'",
)

for param, param_type in info.values():
param_name_length = len(getattr(param, "name", str(param)))
param_type_length = len(param_type)
max_param_name_length = max(max_param_name_length, param_name_length)
max_param_type_length = max(max_param_type_length, param_type_length)
return parameter_info

header_format = (
f"| {{:<{max_param_name_length}}} | {{:<{max_param_type_length}}} |"
def _calculate_max_lengths(self, parameter_dict):
"""
Calculate the maximum length of parameters and parameter type in a dictionary

Parameters
----------
parameter_dict : dict
The dict from which maximum lengths are calculated
"""
max_name_length = max(
len(getattr(parameter, "name", str(parameter)))
for parameter, _ in parameter_dict.values()
)
row_format = (
f"| {{:<{max_param_name_length}}} | {{:<{max_param_type_length}}} |"
max_type_length = max(
len(parameter_type) for _, parameter_type in parameter_dict.values()
)

table = [
header_format.format("Parameter", "Type of parameter"),
header_format.format(
"=" * max_param_name_length, "=" * max_param_type_length
),
return max_name_length, max_type_length

def _format_table_row(
self, param_name, param_type, max_name_length, max_type_length
):
"""
Format the parameter information in a formatted table

Parameters
----------
param_name : str
The name of the parameter
param_type : str
The type of the parameter
max_name_length : int
The maximum length of the parameter in the dictionary
max_type_length : int
The maximum length of the parameter type in the dictionary
"""
param_name_lines = [
param_name[i : i + max_name_length]
for i in range(0, len(param_name), max_name_length)
]
param_type_lines = [
param_type[i : i + max_type_length]
for i in range(0, len(param_type), max_type_length)
]
max_lines = max(len(param_name_lines), len(param_type_lines))

return [
f"│ {param_name_lines[i]:<{max_name_length}} │ {param_type_lines[i]:<{max_type_length}} │"
for i in range(max_lines)
]

for param, param_type in info.values():
param_name = getattr(param, "name", str(param))
param_name_lines = [
param_name[i : i + max_param_name_length]
for i in range(0, len(param_name), max_param_name_length)
]
param_type_lines = [
param_type[i : i + max_param_type_length]
for i in range(0, len(param_type), max_param_type_length)
def print_parameter_info(self, by_submodel=False):
"""
Print parameter information in a formatted table from a dictionary of parameters

Parameters
----------
by_submodel : bool, optional
Whether to print the parameter info sub-model wise or not (default False)
"""

if by_submodel:
parameter_info = self.get_parameter_info(by_submodel=True)
for submodel_name, submodel_vars in parameter_info.items():
if not submodel_vars:
print(f"'{submodel_name}' submodel parameters: \nNo parameters\n")
else:
print(f"'{submodel_name}' submodel parameters:")
agriyakhetarpal marked this conversation as resolved.
Show resolved Hide resolved
(
max_param_name_length,
max_param_type_length,
) = self._calculate_max_lengths(submodel_vars)

table = [
f"┌─{'─' * max_param_name_length}─┬─{'─' * max_param_type_length}─┐",
f"│ {'Parameter':<{max_param_name_length}} │ {'Type of parameter':<{max_param_type_length}} │",
f"├─{'─' * max_param_name_length}─┼─{'─' * max_param_type_length}─┤",
]

for param, param_type in submodel_vars.values():
param_name = getattr(param, "name", str(param))
table.extend(
self._format_table_row(
param_name,
param_type,
max_param_name_length,
max_param_type_length,
)
)
table.extend(
[
f"└─{'─' * max_param_name_length}─┴─{'─' * max_param_type_length}─┘",
]
)
table = "\n".join(table) + "\n"
table.encode("utf-8")
print(table)

else:
info = self.get_parameter_info()
max_param_name_length, max_param_type_length = self._calculate_max_lengths(
info
)

table = [
f"┌─{'─' * max_param_name_length}─┬─{'─' * max_param_type_length}─┐",
f"│ {'Parameter':<{max_param_name_length}} │ {'Type of parameter':<{max_param_type_length}} │",
f"├─{'─' * max_param_name_length}─┼─{'─' * max_param_type_length}─┤",
]
max_lines = max(len(param_name_lines), len(param_type_lines))

for i in range(max_lines):
param_line = param_name_lines[i] if i < len(param_name_lines) else ""
type_line = param_type_lines[i] if i < len(param_type_lines) else ""
table.append(row_format.format(param_line, type_line))
for param, param_type in info.values():
param_name = getattr(param, "name", str(param))
table.extend(
self._format_table_row(
param_name,
param_type,
max_param_name_length,
max_param_type_length,
)
)

for line in table:
print(line)
table.extend(
[
f"└─{'─' * max_param_name_length}─┴─{'─' * max_param_type_length}─┘",
]
)

table = "\n".join(table) + "\n"
table.encode("utf-8")
print(table)

def _find_symbols(self, typ):
"""Find all the instances of `typ` in the model"""
Expand All @@ -516,6 +666,23 @@ def _find_symbols(self, typ):
)
return list(all_input_parameters)

def _find_symbols_by_submodel(self, typ, submodel):
"""Find all the instances of `typ` in the submodel"""
unpacker = pybamm.SymbolUnpacker(typ)
all_input_parameters = unpacker.unpack_list_of_symbols(
list(self.submodels[submodel].rhs.values())
+ list(self.submodels[submodel].algebraic.values())
+ list(self.submodels[submodel].initial_conditions.values())
+ [
x[side][0]
for x in self.submodels[submodel].boundary_conditions.values()
for side in x.keys()
]
+ list(self._variables_by_submodel[submodel].values())
+ [event.expression for event in self.submodels[submodel].events]
)
return list(all_input_parameters)

def new_copy(self):
"""
Creates a copy of the model, explicitly copying all the mutable attributes
Expand Down Expand Up @@ -555,11 +722,16 @@ def update(self, *submodels):

def build_fundamental(self):
# Get the fundamental variables
self._variables_by_submodel = {submodel: {} for submodel in self.submodels}
for submodel_name, submodel in self.submodels.items():
pybamm.logger.debug(
f"Getting fundamental variables for {submodel_name} submodel ({self.name})"
)
self.variables.update(submodel.get_fundamental_variables())
submodel_fundamental_variables = submodel.get_fundamental_variables()
self._variables_by_submodel[submodel_name].update(
submodel_fundamental_variables
)
self.variables.update(submodel_fundamental_variables)

self._built_fundamental = True

Expand All @@ -581,9 +753,18 @@ def build_coupled_variables(self):
f"Getting coupled variables for {submodel_name} submodel ({self.name})"
)
try:
self.variables.update(
submodel.get_coupled_variables(self.variables)
model_var_copy = self.variables.copy()
updated_variables = submodel.get_coupled_variables(
self.variables
)
self._variables_by_submodel[submodel_name].update(
{
key: updated_variables[key]
for key in updated_variables
if key not in model_var_copy
}
)
self.variables.update(updated_variables)
submodels.remove(submodel_name)
except KeyError as key:
if len(submodels) == 1 or count == 100:
Expand Down
Loading
Loading