forked from federicoscarpioni/pyeclab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtechniques.py
385 lines (326 loc) · 16.9 KB
/
techniques.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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
'''
This module contain functions to create a technique object to be loaded on the
BioLogic potentiost to perform electrochemical experiments.
For each technique a set of parameters are allowed (see OEM User's Guide).
NOTE: the following explanation is not contained in the manual (which uses only
Delphi language as example) but it can be found in the examples provided with
the Python wrapper in the installation folder of EC-lab Developer Package.
A technique object must be prepared in the following way:
- Python type numbers (int or floats) representing the parameters must be
converted to c-types using the OEM function make_ecc_parm
- All the parameters must be incorporated in one object using the function
make_ecc_parms (mind the 's')
- For convenience technique file (.ecc) and the parameters object can be converted
to a namedtuple; this way, the technique file and parameters belong to one
name space and can be easily accessed with the attribute notation.
The namedtuple istance can be used in the LoadTechnique function of the Python API.
Calling such function multiple times creates a sequence of techniques.
This library supports the following techniques (not all!):
- Open Circuit Voltage
- Chono-Amperometry with Potential Limitation
- Chrono-Potentiometry
- Loop
Note: some techniques like CP and CA allows multiple steps but in the following
functions, only one is abilitated. For most battery-related reasearch that is
enough.
For each technique are provided:
- A dictionary XXX_params for storing all the parameters
- A function convert_XXX_ecc_params to create the parameters object
- A function make_XXX_tech to create the namedtuple
'''
import pyeclab.api.kbio_types as KBIO
from pyeclab.api.kbio_api import KBIO_api
from pyeclab.api.kbio_tech import ECC_parm, make_ecc_parm, make_ecc_parms
from dataclasses import dataclass
from collections import namedtuple
import pyeclab.tech_names as tn
#=== Auxiliary functions ======================================================#
def set_duration_to_1s(api, technique, tech_id):
'''
Update the duration of CP or CA to 1s. It is used to force the technique to
terminate not being present any specific function in the SDK.
'''
new_duration = 1
parameters={
'current_step': ECC_parm("Current_step", float),
'voltage_step': ECC_parm("Voltage_step", float),
'step_duration': ECC_parm("Duration_step", float),
'vs_init': ECC_parm("vs_initial", bool),
}
idx = 0 # Only one current step is used
p_current_steps = list()
if tech_id == 155:
p_current_steps.append( make_ecc_parm(api, parameters['current_step'], technique.user_params.current, idx ) )
elif tech_id == 101:
p_current_steps.append( make_ecc_parm(api, parameters['voltage_step'], technique.user_params.voltage, idx ) )
p_current_steps.append( make_ecc_parm(api, parameters['step_duration'], new_duration, idx ) )
p_current_steps.append( make_ecc_parm(api, parameters['vs_init'], technique.user_params.vs_init, idx ) )
return make_ecc_parms(api,*p_current_steps)
#------------------------------------------------------------------------------#
def reset_duration(api, technique, tech_id):
parameters={
'current_step': ECC_parm("Current_step", float),
'voltage_step': ECC_parm("Voltage_step", float),
'step_duration': ECC_parm("Duration_step", float),
'vs_init': ECC_parm("vs_initial", bool),}
idx = 0 # Only one current step is used
p_current_steps = list()
if tech_id == 155:
p_current_steps.append( make_ecc_parm(api, parameters['current_step'], technique.user_params.current, idx ) )
elif tech_id == 101:
p_current_steps.append( make_ecc_parm(api, parameters['voltage_step'], technique.user_params.voltage, idx ) )
p_current_steps.append( make_ecc_parm(api, parameters['step_duration'], technique.user_params.duration, idx ) )
p_current_steps.append( make_ecc_parm(api, parameters['vs_init'], technique.user_params.vs_init, idx ) )
return make_ecc_parms(api,*p_current_steps)
#=== Techniques definition functions =========================================#
# ------OCV------- #
@dataclass
class OCV_params :
duration : float
record_dt : float
e_range : float
bandwidth : int
def OCV_tech(api,is_VMP3, parameters):
# .ecc file names
ocv3_tech_file = "ocv.ecc"
ocv4_tech_file = "ocv4.ecc"
# Dictionary of parameters used to call the labrary later
OCV_parm_names = {
'duration': ECC_parm("Rest_time_T", float),
'record_dt': ECC_parm("Record_every_dT", float),
'record_dE': ECC_parm("Record_every_dE", float),
'E_range': ECC_parm("E_Range", int),
'bandwidth': ECC_parm('Bandwidth', int),
}
# pick the correct ecc file based on the instrument family
tech_file_OCV = ocv3_tech_file if is_VMP3 else ocv4_tech_file
p_duration = make_ecc_parm(api, OCV_parm_names['duration'], parameters.duration)
p_record = make_ecc_parm(api, OCV_parm_names['record_dt'], parameters.record_dt)
p_erange = make_ecc_parm(api, OCV_parm_names['E_range'], parameters.e_range)
p_band = make_ecc_parm(api, OCV_parm_names['bandwidth'], parameters.bandwidth)
ecc_parms_OCV = make_ecc_parms(api,
p_duration,
p_record,
p_erange,
p_band)
# Use namedtuple to store the data to upload to BioLogic FPGA
OCV_tech = namedtuple('OCV_tech', 'ecc_file ecc_params user_params')
ocv_tech = OCV_tech(tech_file_OCV, ecc_parms_OCV, parameters)
return ocv_tech
# ------CPLIM------- #
@dataclass
class CPLIM_params :
current : float
duration : float
vs_init : bool
nb_steps : int
record_dt : float
record_dE : float
repeat : int
i_range : int
e_range : int
exit_cond : int
xctr : int
limit_variable : int
limit_values : float
bandwidth : int
# analog_filter : int
def make_CPLIM_ecc_params(api, parameters):
# dictionary of CP parameters (non exhaustive)
CPLIM_parm_names = {
'current_step': ECC_parm("Current_step", float),
'step_duration': ECC_parm("Duration_step", float),
'vs_init': ECC_parm("vs_initial", bool),
'nb_steps': ECC_parm("Step_number", int),
'record_dt': ECC_parm("Record_every_dT", float),
'record_dE': ECC_parm("Record_every_dE", float),
'repeat': ECC_parm("N_Cycles", int),
'i_range': ECC_parm("I_Range", int),
'e_range': ECC_parm("E_Range", int),
'exit_cond': ECC_parm("Exit_Cond",int),
'xctr': ECC_parm("xctr",int),
'test1_config': ECC_parm("Test1_Config", int),
'test1_value': ECC_parm("Test1_Value", float),
'bandwidth': ECC_parm('Bandwidth', int),
# 'analog_filter': ECC_parm('Filter', int)
}
idx = 0 # Only one current step is used
p_current_steps = list()
p_current_steps.append( make_ecc_parm(api, CPLIM_parm_names['current_step'], parameters.current, idx ) )
p_current_steps.append( make_ecc_parm(api, CPLIM_parm_names['step_duration'], parameters.duration, idx ) )
p_current_steps.append( make_ecc_parm(api, CPLIM_parm_names['vs_init'], parameters.vs_init, idx ) )
p_current_steps.append( make_ecc_parm(api, CPLIM_parm_names['exit_cond'], parameters.exit_cond, idx) )
p_current_steps.append( make_ecc_parm( api, CPLIM_parm_names['test1_config'], parameters.limit_variable, idx ) )
p_current_steps.append( make_ecc_parm( api, CPLIM_parm_names['test1_value'], parameters.limit_values, idx ) )
p_nb_steps = make_ecc_parm( api, CPLIM_parm_names['nb_steps'], parameters.nb_steps )
p_record_dt = make_ecc_parm( api, CPLIM_parm_names['record_dt'], parameters.record_dt )
p_record_dE = make_ecc_parm( api, CPLIM_parm_names['record_dE'], parameters.record_dE )
p_xctr = make_ecc_parm( api, CPLIM_parm_names['xctr'], parameters.xctr )
p_repeat = make_ecc_parm( api, CPLIM_parm_names['repeat'], parameters.repeat )
p_IRange = make_ecc_parm( api, CPLIM_parm_names['i_range'], parameters.i_range)
p_ERange = make_ecc_parm( api, CPLIM_parm_names['e_range'], parameters.e_range)
p_band = make_ecc_parm( api, CPLIM_parm_names['bandwidth'], parameters.bandwidth )
# p_filter = make_ecc_parm( api, CPLIM_parm_names['analog_filter'], 0)#KBIO.FILTER[parameters.analog_filter].value)
# make the technique parameter array
ecc_parms_CPLIM = make_ecc_parms(api,
*p_current_steps,
p_nb_steps,
p_record_dt,
p_record_dE,
p_IRange,
p_ERange,
p_repeat,
p_xctr,
p_band,
# p_filter,
)
return ecc_parms_CPLIM
def CPLIM_tech(api, is_VMP3, parameters):
# Name of the dll for the CPLIM technique (for both types of instruments VMP3/VSP300)
cplim3_tech_file = "cplimit.ecc"
cplim4_tech_file = "cplimit4.ecc"
# pick the correct ecc file based on the instrument family
tech_file_CPLIM = cplim3_tech_file if is_VMP3 else cplim4_tech_file
# Define parameters for loading in the device using the templates
ecc_parms_CPLIM = make_CPLIM_ecc_params(api, parameters)
CPLIM_tech = namedtuple('CPLIM_tech', 'ecc_file ecc_params user_params')
cplim_tech = CPLIM_tech(tech_file_CPLIM, ecc_parms_CPLIM, parameters)
return cplim_tech
# ------CA------- #
@dataclass
class CA_params :
voltage : float
duration : float
vs_init : bool
nb_steps : int
record_dt : float
record_dI : float
repeat : int
i_range : int
e_range : int
exit_cond : int
xctr : int
bandwidth : int
def make_CA_ecc_params(api, parameters):
# dictionary of CP parameters (non exhaustive)
CA_parm_names = {
'voltage_step': ECC_parm("Voltage_step", float),
'step_duration': ECC_parm("Duration_step", float),
'vs_init': ECC_parm("vs_initial", bool),
'nb_steps': ECC_parm("Step_number", int),
'record_dt': ECC_parm("Record_every_dT", float),
'record_dI': ECC_parm("Record_every_dI", float),
'repeat': ECC_parm("N_Cycles", int),
'i_range': ECC_parm("I_Range", int),
'e_range': ECC_parm("E_Range", int),
'exit_cond': ECC_parm("Exit_Cond",int),
'xctr': ECC_parm("xctr",int),
'bandwidth': ECC_parm('Bandwidth', int),
}
idx = 0 # Only one current step is used
p_voltage_steps = list()
p_voltage_steps.append( make_ecc_parm(api, CA_parm_names['voltage_step'], parameters.voltage, idx ) )
p_voltage_steps.append( make_ecc_parm(api, CA_parm_names['step_duration'], parameters.duration, idx ) )
p_voltage_steps.append( make_ecc_parm(api, CA_parm_names['vs_init'], parameters.vs_init, idx ) )
p_nb_steps = make_ecc_parm( api, CA_parm_names['nb_steps'], parameters.nb_steps )
p_record_dt = make_ecc_parm( api, CA_parm_names['record_dt'], parameters.record_dt )
p_record_dI = make_ecc_parm( api, CA_parm_names['record_dI'], parameters.record_dI )
p_xctr = make_ecc_parm( api, CA_parm_names['xctr'], parameters.xctr )
p_repeat = make_ecc_parm( api, CA_parm_names['repeat'], parameters.repeat )
p_IRange = make_ecc_parm( api, CA_parm_names['i_range'], parameters.i_range)
p_ERange = make_ecc_parm(api, CA_parm_names['e_range'], parameters.e_range)
p_band = make_ecc_parm( api, CA_parm_names['bandwidth'], parameters.bandwidth )
# make the technique parameter array
ecc_parms_CA = make_ecc_parms(api,
*p_voltage_steps, # all array type paramaters goes together
p_nb_steps,
p_record_dt,
p_record_dI,
p_IRange,
p_ERange,
p_repeat,
p_xctr,
p_band)
return ecc_parms_CA
def CA_tech(api, is_VMP3, parameters):
# Name of the dll for the CPLIM technique (for both types of instruments VMP3/VSP300)
cplim3_tech_file = "ca.ecc"
cplim4_tech_file = "ca4.ecc"
# pick the correct ecc file based on the instrument family
tech_file_CA = cplim3_tech_file if is_VMP3 else cplim4_tech_file
# Define parameters for loading in the device using the templates
ecc_parms_CA = make_CA_ecc_params(api, parameters)
CA_tech = namedtuple('CA_tech', 'ecc_file ecc_params user_params')
ca_tech = CA_tech(tech_file_CA, ecc_parms_CA, parameters)
return ca_tech
# ------Loop------- #
@dataclass
class LOOP_params :
repeat_N : int
loop_start : int
def make_loop_ecc_params(api, parameters):
# Dictionary of parameters used to call the labrary later
loop_parms = {
'reapeat' : ECC_parm("loop_N_times", int),
'loop_start' : ECC_parm('protocol_number', int),
}
p_repeat_N = make_ecc_parm(api, loop_parms['reapeat'], parameters.repeat_N)
p_loop_start = make_ecc_parm(api, loop_parms['loop_start'], parameters.loop_start)
ecc_parms_loop = make_ecc_parms(api, p_repeat_N,
p_loop_start)
return ecc_parms_loop
def loop_tech(api, is_VMP3, parameters):
# .ecc file names
loop3_tech_file = "loop.ecc"
loop4_tech_file = "loop4.ecc"
# pick the correct ecc file based on the instrument family
tech_file_loop = loop3_tech_file if is_VMP3 else loop4_tech_file
ecc_parms_loop = make_loop_ecc_params(api, parameters)
LOOP_tech = namedtuple('LOOP_tech', 'ecc_file ecc_params user_params')
loop_tech = LOOP_tech(tech_file_loop, ecc_parms_loop, parameters)
return loop_tech
# !!! From old module. Must review them later
# def duration_to_1s(api, technique, tech_id):
# new_duration = 1
# parameters={
# 'current_step': ECC_parm("Current_step", float),
# 'voltage_step': ECC_parm("Voltage_step", float),
# 'step_duration': ECC_parm("Duration_step", float),
# 'vs_init': ECC_parm("vs_initial", bool),
# }
# idx = 0 # Only one current step is used
# p_current_steps = list()
# if tech_id == 155:
# p_current_steps.append( make_ecc_parm(api, parameters['current_step'], technique.user_params.current, idx ) )
# elif tech_id == 101:
# p_current_steps.append( make_ecc_parm(api, parameters['voltage_step'], technique.user_params.voltage, idx ) )
# p_current_steps.append( make_ecc_parm(api, parameters['step_duration'], new_duration, idx ) )
# p_current_steps.append( make_ecc_parm(api, parameters['vs_init'], technique.user_params.vs_init, idx ) )
# return make_ecc_parms(api,*p_current_steps)
# def reset_duration(api, technique, tech_id):
# parameters={
# 'current_step': ECC_parm("Current_step", float),
# 'voltage_step': ECC_parm("Voltage_step", float),
# 'step_duration': ECC_parm("Duration_step", float),
# 'vs_init': ECC_parm("vs_initial", bool),}
# idx = 0 # Only one current step is used
# p_current_steps = list()
# if tech_id == 155:
# p_current_steps.append( make_ecc_parm(api, parameters['current_step'], technique.user_params.current, idx ) )
# elif tech_id == 101:
# p_current_steps.append( make_ecc_parm(api, parameters['voltage_step'], technique.user_params.voltage, idx ) )
# p_current_steps.append( make_ecc_parm(api, parameters['step_duration'], technique.user_params.duration, idx ) )
# p_current_steps.append( make_ecc_parm(api, parameters['vs_init'], technique.user_params.vs_init, idx ) )
# return make_ecc_parms(api,*p_current_steps)
# def update_CA_voltage(api, Ewe, technique):
# CA_parm_names = {
# 'voltage_step': ECC_parm("Voltage_step", float),
# 'step_duration': ECC_parm("Duration_step", float),
# 'vs_init': ECC_parm("vs_initial", bool),
# }
# idx = 0 # Only one current step is used
# p_voltage_steps = list()
# p_voltage_steps.append( make_ecc_parm(api, CA_parm_names['voltage_step'], Ewe, idx ) )
# p_voltage_steps.append( make_ecc_parm(api, CA_parm_names['step_duration'], technique.user_params.duration, idx ) )
# p_voltage_steps.append( make_ecc_parm(api, CA_parm_names['vs_init'], technique.user_params.vs_init, idx ) )
# return make_ecc_parms(api,*p_voltage_steps)