/
registries.py
289 lines (230 loc) · 10.6 KB
/
registries.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
"""Registries are dictionaries that put together Solcore's functionality that share the
same purpose, eg. calculate solar cell properties, solve the electrical properties of
junctions or solve their optical properties. They can also perform validation and be
used to extend Solcore's functionality without the need of modifying the source code.
The main advantage of registries is that they allow you to define your own functionality
outside of the solcore code base, simplifying the process of trying new things:
>>> from solcore import registries
>>> @registries.register_action("pre-process")
... def pre_process_cell(*args, **kwargs):
... pass
>>> "pre-process" in registries.ACTIONS_REGISTRY
True
After this, `pre-process` will be one of the `task` that you can run when executing the
`solar_cell_solver` funciton.
You can also overwrite existing functionality in case you want to implement your own
alternative to an existing approach:
>>> from solcore import registries
>>> @registries.register_action("optics", overwrite=True)
... def custom_solve_optics(*args, **kwargs):
... pass
>>> registries.ACTIONS_REGISTRY["optics"] == custom_solve_optics
True
"""
from typing import Callable, Dict, Optional, Any
from warnings import warn
from .solar_cell import SolarCell
from .structure import Junction
from .state import State
def generic_register(
name: str,
registrator_name: str,
registry: Dict[str, Callable],
signature: Any,
overwrite: bool = False,
reason_to_exclude: Optional[str] = None,
) -> Callable:
"""Generic register that can be used by the other specific ones.
Args:
name: Name of the function to register
registrator_name (str): Name of the action of the specifric register, eg.
"Optics solver".
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: If there is any reason to exclude
this method from the registry. If not None, the method will be excluded.
Defaults to None.
Raises:
ValueError: If the name of the method exist already in the registry and
overwrite is False.
Returns:
Callable: The inner decorator that will actually register the function.
"""
if name in registry and not overwrite:
raise ValueError(
f"{registrator_name.capitalize()} '{name}' already exist in the registry. "
"Give it another name or set `overwrite = True`."
)
if reason_to_exclude is not None:
warn(
f"{registrator_name.capitalize()} '{name}' will not be available. "
f"{reason_to_exclude}"
)
def wrap(func: signature) -> signature:
if reason_to_exclude is None:
registry[name] = func
return func
return wrap
ACTIONS_SIGNATURE = Callable[[SolarCell, State], None]
ACTIONS_REGISTRY: Dict[str, ACTIONS_SIGNATURE] = {}
def register_action(
name: str, overwrite: bool = False, reason_to_exclude: Optional[str] = None
) -> Callable:
"""Registers a global action to be executed over the whole cell.
Examples of these actions could include soling the optics of the cell, calculate the
IV curve or the quantum efficiency.
Args:
name (str): Name of the action.
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 method from the registry. If not None, the method will be excluded.
Defaults to None.
Raises:
ValueError: If the name of the method exist 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="Action",
registry=ACTIONS_REGISTRY,
signature=ACTIONS_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)
OPTICS_METHOD_SIGNATURE = Callable[[SolarCell, Any], None]
OPTICS_METHOD_REGISTRY: Dict[str, OPTICS_METHOD_SIGNATURE] = {}
def register_optics(
name: str, overwrite: bool = False, reason_to_exclude: Optional[str] = None
) -> Callable:
"""Registers a function that calculates the optics of a solar cell.
The method must accept as first argument a SolarCell 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 SolarCell object will be updated with the
optical properties, such as the reflection, the transmision, the absorption, etc.
Args:
name (str): Name of the method.
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 method from the registry. If not None, the method will be excluded.
Defaults to None.
Raises:
ValueError: If the name of the method exist 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="Optics solver",
registry=OPTICS_METHOD_REGISTRY,
signature=OPTICS_METHOD_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)
SHORT_CIRCUIT_SOLVER_SIGNATURE = Callable[[Junction, Any], None]
SHORT_CIRCUIT_SOLVER_REGISTRY: Dict[str, SHORT_CIRCUIT_SOLVER_SIGNATURE] = {}
def register_short_circuit_solver(
name: str, overwrite: bool = False, reason_to_exclude: Optional[str] = None
) -> Callable:
"""Registers a function that solves a junction under short circuit conditions.
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
bandstructure and recombination profile at short circuit will be available in a new
`short_circuit_data` attribute of the junction object as a State object.
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="Short circuit solver",
registry=SHORT_CIRCUIT_SOLVER_REGISTRY,
signature=SHORT_CIRCUIT_SOLVER_SIGNATURE,
overwrite=overwrite,
reason_to_exclude=reason_to_exclude,
)
EQUILIBRIUM_SOLVER_SIGNATURE = Callable[[Junction, Any], None]
EQUILIBRIUM_SOLVER_REGISTRY: Dict[str, EQUILIBRIUM_SOLVER_SIGNATURE] = {}
def register_equilibrium_solver(
name: str, overwrite: bool = False, reason_to_exclude: Optional[str] = None
) -> Callable:
"""Registers a function that solves a junction under equilibrium conditions.
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
bandstructure profile at equilibrium, available in a new `equilibrium_data`
attribute of the junction object as a State object.
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="Equilibrium solver",
registry=EQUILIBRIUM_SOLVER_REGISTRY,
signature=EQUILIBRIUM_SOLVER_SIGNATURE,
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,
)