def test_export_eclab_ascii_format(self):
        # define dummy experiment
        # it is quicker than building an actual EIS experiment
        class DummyExperiment(Experiment):

            def __new__(cls, *args, **kwargs):
                return object.__new__(DummyExperiment)

            def __init__(self, ptree):
                Experiment.__init__(self)
        dummy = DummyExperiment(PropertyTree())
        # produce dummy data for the experiment
        # here just a circle on the complex plane
        n = 10
        f = ones(n, dtype=float)
        Z = ones(n, dtype=complex)
        for i in range(n):
            f[i] = 10**(i / (n - 1))
            Z[i] = cos(2 * pi * i / (n - 1)) + 1j * sin(2 * pi * i / (n - 1))
        dummy._data['frequency'] = f
        dummy._data['impedance'] = Z
        # need a supercapacitor here to make sure method inspect() is kept in
        # sync with the EC-Lab headers
        ptree = PropertyTree()
        ptree.parse_info('super_capacitor.info')
        super_capacitor = EnergyStorageDevice(ptree)
        dummy._extra_data = super_capacitor.inspect()

        # export the data to ECLab format
        eclab = ECLabAsciiFile('untitled.mpt')
        eclab.update(dummy)

        # check that all lines end up with Windows-style line break '/r/n'
        # file need to be open in byte mode or the line ending will be
        # converted to '\n'...
        # also check that the number of lines in the headers has been computed
        # correctly and that the last one contains the column headers
        with open('untitled.mpt', mode='rb') as fin:
            lines = fin.readlines()
            for line in lines:
                self.assertNotEqual(line.find(b'\r\n'), -1)
                self.assertNotEqual(line.find(b'\r\n'), len(line) - 4)
            header_lines = int(lines[1].split(
                b':')[1].lstrip(b'').rstrip(b'\r\n'))
            self.assertEqual(
                header_lines,
                len(eclab._unformated_headers)
            )
            self.assertEqual(lines[header_lines - 1].find(b'freq/Hz'), 0)

        # check Nyquist plot does not throw
        nyquist = NyquistPlot('nyquist.png')
        nyquist.update(dummy)
示例#2
0
    def test_export_eclab_ascii_format(self):
        # define dummy experiment
        # it is quicker than building an actual EIS experiment
        class DummyExperiment(Experiment):

            def __new__(cls, *args, **kwargs):
                return object.__new__(DummyExperiment)

            def __init__(self, ptree):
                Experiment.__init__(self)
        dummy = DummyExperiment(PropertyTree())
        # produce dummy data for the experiment
        # here just a circle on the complex plane
        n = 10
        f = ones(n, dtype=float)
        Z = ones(n, dtype=complex)
        for i in range(n):
            f[i] = 10**(i / (n - 1))
            Z[i] = cos(2 * pi * i / (n - 1)) + 1j * sin(2 * pi * i / (n - 1))
        dummy._data['frequency'] = f
        dummy._data['impedance'] = Z
        # need a supercapacitor here to make sure method inspect() is kept in
        # sync with the EC-Lab headers
        ptree = PropertyTree()
        ptree.parse_info('super_capacitor.info')
        super_capacitor = EnergyStorageDevice(ptree)
        dummy._extra_data = super_capacitor.inspect()

        # export the data to ECLab format
        eclab = ECLabAsciiFile('untitled.mpt')
        eclab.update(dummy)

        # check that all lines end up with Windows-style line break '/r/n'
        # file need to be open in byte mode or the line ending will be
        # converted to '\n'...
        # also check that the number of lines in the headers has been computed
        # correctly and that the last one contains the column headers
        with open('untitled.mpt', mode='rb') as fin:
            lines = fin.readlines()
            for line in lines:
                self.assertNotEqual(line.find(b'\r\n'), -1)
                self.assertNotEqual(line.find(b'\r\n'), len(line) - 4)
            header_lines = int(lines[1].split(
                b':')[1].lstrip(b'').rstrip(b'\r\n'))
            self.assertEqual(
                header_lines,
                len(eclab._unformated_headers)
            )
            self.assertEqual(lines[header_lines - 1].find(b'freq/Hz'), 0)

        # check Nyquist plot does not throw
        nyquist = NyquistPlot('nyquist.png')
        nyquist.update(dummy)
示例#3
0
    def test_retrieve_data(self):
        ptree = PropertyTree()
        ptree.put_string('type', 'SeriesRC')
        ptree.put_double('series_resistance', 100e-3)
        ptree.put_double('capacitance', 2.5)
        device = EnergyStorageDevice(ptree)

        ptree = PropertyTree()
        ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy')
        ptree.put_double('frequency_upper_limit', 1e+2)
        ptree.put_double('frequency_lower_limit', 1e-1)
        ptree.put_int('steps_per_decade', 1)
        ptree.put_int('steps_per_cycle', 64)
        ptree.put_int('cycles', 2)
        ptree.put_int('ignore_cycles', 1)
        ptree.put_double('dc_voltage', 0)
        ptree.put_string('harmonics', '3')
        ptree.put_string('amplitudes', '5e-3')
        ptree.put_string('phases', '0')
        eis = Experiment(ptree)

        with File('trash.hdf5', 'w') as fout:
            eis.run(device, fout)
        spectrum_data = eis._data

        with File('trash.hdf5', 'r') as fin:
            retrieved_data = retrieve_impedance_spectrum(fin)

        print(spectrum_data['impedance'] - retrieved_data['impedance'])
        print(retrieved_data)
        self.assertEqual(linalg.norm(spectrum_data['frequency'] -
                                     retrieved_data['frequency'], inf), 0.0)
        # not sure why we don't get equality for the impedance
        self.assertLess(linalg.norm(spectrum_data['impedance'] -
                                    retrieved_data['impedance'], inf), 1e-10)
示例#4
0
    def test_verification_with_equivalent_circuit(self):
        R = 50e-3  # ohm
        R_L = 500  # ohm
        C = 3  # farad
        U_i = 2.7  # volt
        U_f = 1.2  # volt
        # setup experiment
        ptree = PropertyTree()
        ptree.put_double('discharge_power_lower_limit', 1e-2)
        ptree.put_double('discharge_power_upper_limit', 1e+2)
        ptree.put_int('steps_per_decade', 5)
        ptree.put_double('initial_voltage', U_i)
        ptree.put_double('final_voltage', U_f)
        ptree.put_double('time_step', 15)
        ptree.put_int('min_steps_per_discharge', 2000)
        ptree.put_int('max_steps_per_discharge', 3000)
        ragone = RagoneAnalysis(ptree)
        # setup equivalent circuit database
        device_database = PropertyTree()
        device_database.put_double('series_resistance', R)
        device_database.put_double('parallel_resistance', R_L)
        device_database.put_double('capacitance', C)
        # analytical solutions
        E = {}

        def E_SeriesRC(P):
            U_0 = U_i / 2 + sqrt(U_i**2 / 4 - R * P)
            return C / 2 * (-R * P * log(U_0**2 / U_f**2) + U_0**2 - U_f**2)

        E['SeriesRC'] = E_SeriesRC

        def E_ParallelRC(P):
            U_0 = U_i / 2 + sqrt(U_i**2 / 4 - R * P)
            tmp = (U_f**2 / R_L + P * (1 + R / R_L)) / \
                (U_0**2 / R_L + P * (1 + R / R_L))
            return C / 2 * (-R_L * P * log(tmp) - R * R_L /
                            (R + R_L) * P * log(tmp * U_0**2 / U_f**2))

        E['ParallelRC'] = E_ParallelRC
        for device_type in ['SeriesRC', 'ParallelRC']:
            # create a device
            device_database.put_string('type', device_type)
            device = EnergyStorageDevice(device_database)
            # setup experiment and measure
            ragone.reset()
            ragone.run(device)
            P = ragone._data['power']
            E_computed = ragone._data['energy']
            # compute the exact solution
            E_exact = E[device_type](P)
            # ensure the error is small
            max_percent_error = 100 * linalg.norm(
                (E_computed - E_exact) / E_computed, inf)
            self.assertLess(max_percent_error, 0.1)
示例#5
0
 def test_verification_with_equivalent_circuit(self):
     R = 50e-3   # ohm
     R_L = 500   # ohm
     C = 3       # farad
     # setup EIS experiment
     ptree = PropertyTree()
     ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy')
     ptree.put_double('frequency_upper_limit', 1e+4)
     ptree.put_double('frequency_lower_limit', 1e-6)
     ptree.put_int('steps_per_decade', 3)
     ptree.put_int('steps_per_cycle', 1024)
     ptree.put_int('cycles', 2)
     ptree.put_int('ignore_cycles', 1)
     ptree.put_double('dc_voltage', 0)
     ptree.put_string('harmonics', '3')
     ptree.put_string('amplitudes', '5e-3')
     ptree.put_string('phases', '0')
     eis = Experiment(ptree)
     # setup equivalent circuit database
     device_database = PropertyTree()
     device_database.put_double('series_resistance', R)
     device_database.put_double('parallel_resistance', R_L)
     device_database.put_double('capacitance', C)
     # analytical solutions
     Z = {}
     Z['SeriesRC'] = lambda f: R + 1 / (1j * C * 2 * pi * f)
     Z['ParallelRC'] = lambda f: R + R_L / (1 + 1j * R_L * C * 2 * pi * f)
     for device_type in ['SeriesRC', 'ParallelRC']:
         # create a device
         device_database.put_string('type', device_type)
         device = EnergyStorageDevice(device_database)
         # setup experiment and measure
         eis.reset()
         eis.run(device)
         f = eis._data['frequency']
         Z_computed = eis._data['impedance']
         # compute the exact solution
         Z_exact = Z[device_type](f)
         # ensure the error is small
         max_phase_error_in_degree = linalg.norm(
             angle(Z_computed) * 180 / pi - angle(Z_exact) * 180 / pi,
             inf)
         max_magniture_error_in_decibel = linalg.norm(
             20 * log10(absolute(Z_exact)) - 20 *
             log10(absolute(Z_computed)),
             inf)
         print(device_type)
         print(
             '-- max_phase_error_in_degree = {0}'.format(max_phase_error_in_degree))
         print(
             '-- max_magniture_error_in_decibel = {0}'.format(max_magniture_error_in_decibel))
         self.assertLessEqual(max_phase_error_in_degree, 1)
         self.assertLessEqual(max_magniture_error_in_decibel, 0.2)
示例#6
0
    def test_retrieve_data(self):
        ptree = PropertyTree()
        ptree.put_string('type', 'SeriesRC')
        ptree.put_double('series_resistance', 50e-3)
        ptree.put_double('capacitance', 3)
        device = EnergyStorageDevice(ptree)

        ptree = PropertyTree()
        ptree.put_string('type', 'RagoneAnalysis')
        ptree.put_double('discharge_power_lower_limit', 1e-1)
        ptree.put_double('discharge_power_upper_limit', 1e+1)
        ptree.put_int('steps_per_decade', 1)
        ptree.put_double('initial_voltage', 2.1)
        ptree.put_double('final_voltage', 0.7)
        ptree.put_double('time_step', 1.5)
        ptree.put_int('min_steps_per_discharge', 20)
        ptree.put_int('max_steps_per_discharge', 30)
        ragone = Experiment(ptree)

        with File('trash.hdf5', 'w') as fout:
            ragone.run(device, fout)
        performance_data = ragone._data

        fin = File('trash.hdf5', 'r')
        retrieved_data = retrieve_performance_data(fin)
        fin.close()
        # a few digits are lost when power is converted to string
        self.assertLess(
            linalg.norm(performance_data['power'] - retrieved_data['power'],
                        inf), 1e-12)
        self.assertEqual(
            linalg.norm(performance_data['energy'] - retrieved_data['energy'],
                        inf), 0.0)

        # TODO: probably want to move this into its own test
        ragoneplot = RagonePlot("ragone.png")
        ragoneplot.update(ragone)

        # check reset reinitialize the time step and empty the data
        ragone.reset()
        self.assertEqual(ragone._ptree.get_double('time_step'), 1.5)
        self.assertFalse(ragone._data['power'])
        self.assertFalse(ragone._data['energy'])
示例#7
0
#
# This file is subject to the Modified BSD License and may not be distributed
# without copyright and license information. Please refer to the file LICENSE
# for the text and further information on this license.

from pycap import PropertyTree, EnergyStorageDevice
from pycap import Charge
from pycap import initialize_data
from mpi4py import MPI
import unittest

comm = MPI.COMM_WORLD
filename = 'series_rc.info'
ptree = PropertyTree()
ptree.parse_info(filename)
device = EnergyStorageDevice(ptree, comm)


class capChargeTestCase(unittest.TestCase):
    def test_charge_constant_current(self):
        ptree = PropertyTree()
        ptree.put_string('charge_mode', 'constant_current')
        ptree.put_double('charge_current', 10e-3)
        ptree.put_string('charge_stop_at_1', 'voltage_greater_than')
        ptree.put_double('charge_voltage_limit', 1.4)
        ptree.put_double('time_step', 0.2)
        charge = Charge(ptree)
        data = initialize_data()
        charge.run(device, data)
        self.assertAlmostEqual(data['current'][0], 10e-3)
        self.assertAlmostEqual(data['current'][-1], 10e-3)
(options, args) = parser.parse_args()

# make device database
device_database = PropertyTree()
device_database.parse_xml('super_capacitor.xml')
# adjust the parameters in the database
options_dict = vars(options)
for var in options_dict:
    path = uq_database.get_string('uq.' + var + '.name')
    # next line is there to ensure that path already exists
    old_value = device_database.get_double(path)
    new_value = options_dict[var]
    print var, path, new_value
    device_database.put_double(path, new_value)
# build the energy storage device
device = EnergyStorageDevice(device_database.get_child('device'))

# parse the electrochmical impedance spectroscopy database
eis_database = PropertyTree()
eis_database.parse_xml('eis.xml')
# measure the impedance
impedance_spectrum_data = measure_impedance_spectrum(
    device, eis_database.get_child('eis'))
# extract the results
frequency = impedance_spectrum_data['frequency']
complex_impedance = impedance_spectrum_data['impedance']
resistance = real(complex_impedance)
reactance = imag(complex_impedance)
modulus = absolute(complex_impedance)
argument = angle(complex_impedance, deg=True)
magnitude = 20 * log10(absolute(complex_impedance))