Skip to content

Commit

Permalink
Merge pull request #239 from qpv-research-group/register_iv_solvers
Browse files Browse the repository at this point in the history
Create the IV registry
  • Loading branch information
dalonsoa committed Dec 12, 2022
2 parents 2ff25da + 03b64dc commit 89cbd9e
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 30 deletions.
53 changes: 48 additions & 5 deletions solcore/registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ def generic_register(
"""Generic register that can be used by the other specific ones.
Args:
name (str): Name of the function to register
name: Name of the function to register
registrator_name (str): Name of the action of the specifric register, eg.
"Optics solver".
registry (Dict[str, Callable]): Registry in which to store the registered function.
signature (type): Signature of that function.
overwrite (bool, optional): If the method should overwrite an existing one with
registry: Registry in which to store the registered function.
signature: Signature of that function.
overwrite: If the method should overwrite an existing one with
the same name. Defaults to False.
reason_to_exclude (Optional[str], optional): If there is any reason to exclude
reason_to_exclude: If there is any reason to exclude
this method from the registry. If not None, the method will be excluded.
Defaults to None.
Expand Down Expand Up @@ -244,3 +244,46 @@ def register_equilibrium_solver(
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)


IV_SOLVER_SIGNATURE = Callable[[Junction, Any], None]
IV_SOLVER_REGISTRY: Dict[str, EQUILIBRIUM_SOLVER_SIGNATURE] = {}


def register_iv_solver(
name: str, overwrite: bool = False, reason_to_exclude: Optional[str] = None
) -> Callable:
"""Registers a function that solves the IV curve of an independent junction.
The solver must accept as first argument a Junction object and can also have as
input a variable number of parameters needed to perform the calculation, as well as
a generic **kwargs.
After running the function, the input Junction object will be updated with the
voltage, the current, a linear interpolator to calculate the IV curve at any voltage
and, potentialy, other auxiliary currents - related to different recombination
mechanisms or correspoinding to different regions of the cell.
Args:
name (str): Name of the solver.
overwrite (bool, optional): If the method should overwrite an existing one with
the same name. Defaults to False.
reason_to_exclude (Optional[str], optional): If there is any reason to exclude
this solver from the registry. If not None, the method will be excluded.
Defaults to None.
Raises:
ValueError: If the name of the solver exists already in the registry and
overwrite is False.
Returns:
Callable: The inner decorator that will actually register the function.
"""
return generic_register(
name=name,
registrator_name="IV solver",
registry=IV_SOLVER_REGISTRY,
signature=IV_SOLVER_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)
162 changes: 137 additions & 25 deletions tests/test_registries.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,165 @@
import pytest


def test_register_action():
def test_generic_register():
from solcore import registries
from typing import Callable

@registries.register_action("pre-process")
def pre_process_cell(*args, **kwargs):
REGISTRY = {}
SIGNATURE = Callable

register_a_solver = registries.generic_register(
"a-solver",
registrator_name="Cool features",
registry=REGISTRY,
signature=SIGNATURE,
)

@register_a_solver
def solver(*args, **kwargs):
pass

assert "pre-process" in registries.ACTIONS_REGISTRY
assert REGISTRY["a-solver"] == solver

with pytest.raises(ValueError):

@registries.register_action("pre-process")
def custom_pre_process_cell(*args, **kwargs):
pass
registries.generic_register(
"a-solver",
registrator_name="Cool features",
registry=REGISTRY,
signature=SIGNATURE,
)

register_a_solver = registries.generic_register(
"a-solver",
registrator_name="Cool features",
registry=REGISTRY,
signature=SIGNATURE,
overwrite=True,
)

@registries.register_action("pre-process", overwrite=True)
def another_pre_process_cell(*args, **kwargs):
@register_a_solver
def second_solver(*args, **kwargs):
pass

assert registries.ACTIONS_REGISTRY["pre-process"] == another_pre_process_cell
assert REGISTRY["a-solver"] == second_solver


def test_register_optics():
def test_register_action(mocker):
from solcore import registries

@registries.register_optics("approximate")
def approximate_optics(*args, **kwargs):
mock_gr = mocker.patch("solcore.registries.generic_register")
name = "pre-process"
overwrite = False
reason_to_exclude = None

@registries.register_action(
name, overwrite=overwrite, reason_to_exclude=reason_to_exclude
)
def solver(*args, **kwargs):
pass

assert "approximate" in registries.OPTICS_METHOD_REGISTRY
mock_gr.assert_called_once_with(
name=name,
registrator_name="Action",
registry=registries.ACTIONS_REGISTRY,
signature=registries.ACTIONS_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)


with pytest.raises(ValueError):
def test_register_optics(mocker):
from solcore import registries

@registries.register_optics("approximate")
def custom_approximate_optics(*args, **kwargs):
pass
mock_gr = mocker.patch("solcore.registries.generic_register")
name = "custom_optics"
overwrite = False
reason_to_exclude = None

@registries.register_optics("approximate", overwrite=True)
def another_approximate_optics(*args, **kwargs):
@registries.register_optics(
name, overwrite=overwrite, reason_to_exclude=reason_to_exclude
)
def solver(*args, **kwargs):
pass

assert (
registries.OPTICS_METHOD_REGISTRY["approximate"] == another_approximate_optics
mock_gr.assert_called_once_with(
name=name,
registrator_name="Optics solver",
registry=registries.OPTICS_METHOD_REGISTRY,
signature=registries.OPTICS_METHOD_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)

@registries.register_optics("final_approximate", reason_to_exclude="Doesn't work")
def final_approximate_optics(*args, **kwargs):

def test_register_short_circuit_solver(mocker):
from solcore import registries

mock_gr = mocker.patch("solcore.registries.generic_register")
name = "custom_short_circuit"
overwrite = False
reason_to_exclude = None

@registries.register_short_circuit_solver(
name, overwrite=overwrite, reason_to_exclude=reason_to_exclude
)
def solver(*args, **kwargs):
pass

assert "final_approximate" not in registries.OPTICS_METHOD_REGISTRY
mock_gr.assert_called_once_with(
name=name,
registrator_name="Short circuit solver",
registry=registries.SHORT_CIRCUIT_SOLVER_REGISTRY,
signature=registries.SHORT_CIRCUIT_SOLVER_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)


def test_register_equilibrium_solver(mocker):
from solcore import registries

mock_gr = mocker.patch("solcore.registries.generic_register")
name = "custom_equilibrium"
overwrite = False
reason_to_exclude = None

@registries.register_equilibrium_solver(
name, overwrite=overwrite, reason_to_exclude=reason_to_exclude
)
def solver(*args, **kwargs):
pass

mock_gr.assert_called_once_with(
name=name,
registrator_name="Equilibrium solver",
registry=registries.EQUILIBRIUM_SOLVER_REGISTRY,
signature=registries.EQUILIBRIUM_SOLVER_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)


def test_register_iv_solver(mocker):
from solcore import registries

mock_gr = mocker.patch("solcore.registries.generic_register")
name = "custom_iv_solver"
overwrite = False
reason_to_exclude = None

@registries.register_iv_solver(
name, overwrite=overwrite, reason_to_exclude=reason_to_exclude
)
def solver(*args, **kwargs):
pass

mock_gr.assert_called_once_with(
name=name,
registrator_name="IV solver",
registry=registries.IV_SOLVER_REGISTRY,
signature=registries.IV_SOLVER_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)

0 comments on commit 89cbd9e

Please sign in to comment.