Ejemplo n.º 1
0
 def test_evolve_constant_voltage(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'constant_voltage')
     ptree.put_double('voltage', 2.1)
     evolve_one_time_step = TimeEvolution.factory(ptree)
     evolve_one_time_step(device, 0.1)
     self.assertEqual(device.get_voltage(), 2.1)
Ejemplo n.º 2
0
    def test_builders(self):
        for AbstractClass in [Observer, Observable]:
            # AbstractClass takes a PropertyTree as argument.
            self.assertRaises(TypeError, AbstractClass)

            # The PropertyTree must specify what concrete class derived from
            # AbstractClass to instantiate.
            ptree = PropertyTree()
            self.assertRaises(RuntimeError, AbstractClass, ptree)

            # The derived concrete class must be registerd in the dictionary
            # that holds the builders.
            ptree.put_string('type', 'Invalid')
            self.assertRaises(KeyError, AbstractClass, ptree)

            # Now declare a concrete class.
            class ConcreteClass(AbstractClass):
                def __new__(cls, *args, **kwargs):
                    return object.__new__(ConcreteClass)
                def __init__(*args, **kwargs):
                    pass
            # Here is how to register a derived concrete class to the base abstract class.
            AbstractClass._builders['ConcreteClass'] = ConcreteClass

            # Now instantiation works.
            ptree.put_string('type', 'ConcreteClass')
            o = AbstractClass(ptree)

            # Also can build directly from derived class.
            o = ConcreteClass()

            # Remove from the dictionary.
            del AbstractClass._builders['ConcreteClass']
            self.assertRaises(KeyError, AbstractClass, ptree)
Ejemplo n.º 3
0
 def test_evolve_constant_current(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'constant_current')
     ptree.put_double('current', 100e-3)
     evolve_one_time_step = TimeEvolution.factory(ptree)
     evolve_one_time_step(device, 0.1)
     self.assertEqual(device.get_current(), 100e-3)
Ejemplo n.º 4
0
    def test_abstract_class(self):
        # Declare a concrete Experiment
        class DummyExperiment(Experiment):

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

            def __init__(self, ptree):
                Experiment.__init__(self)
        # Do not forget to register it to the builders dictionary.
        Observable._builders['Dummy'] = DummyExperiment

        # Construct directly via DummyExperiment with a PropertyTree as a
        # positional arguemnt
        ptree = PropertyTree()
        dummy = DummyExperiment(ptree)

        # ... or directly via Experiment by specifying the ``type`` of
        # Experiment.
        ptree.put_string('type', 'Dummy')
        dummy = Experiment(ptree)

        # The method run() must be overloaded.
        self.assertRaises(RuntimeError, dummy.run, None)

        # Override the method run().
        def run(self, device):
            pass
        DummyExperiment.run = run

        # Now calling it without raising an error.
        dummy.run(None)
Ejemplo n.º 5
0
 def test_evolve_constant_load(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'constant_load')
     ptree.put_double('load', 120)
     evolve_one_time_step = TimeEvolution.factory(ptree)
     evolve_one_time_step(device, 0.1)
     self.assertAlmostEqual(device.get_voltage()/device.get_current(), -120)
Ejemplo n.º 6
0
 def test_hold(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'hold')
     evolve_one_time_step = TimeEvolution.factory(ptree)
     device.evolve_one_time_step_constant_voltage(0.1, 1.4)
     evolve_one_time_step(device, 0.1)
     self.assertEqual(device.get_voltage(), 1.4)
Ejemplo n.º 7
0
 def test_time_limit(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'time')
     ptree.put_double('duration', 15)
     time_limit = EndCriterion.factory(ptree)
     time_limit.reset(0.0, device)
     self.assertFalse(time_limit.check(2.0, device))
     self.assertTrue(time_limit.check(15.0, device))
     self.assertTrue(time_limit.check(60.0, device))
 def testParallelRC(self):
     # make parallel RC equivalent circuit
     device_database = PropertyTree()
     device_database.put_string('type', 'ParallelRC')
     device_database.put_double('series_resistance', R)
     device_database.put_double('parallel_resistance', R_L)
     device_database.put_double('capacitance', C)
     device = EnergyStorageDevice(device_database, MPI.COMM_WORLD)
     # setup experiment and measure
     eis_database = setup_expertiment()
     spectrum_data = measure_impedance_spectrum(device, eis_database)
     # extract data
     f = spectrum_data['frequency']
     Z_computed = spectrum_data['impedance']
     M_computed = 20*log10(absolute(Z_computed))
     P_computed = angle(Z_computed)*180/pi
     # compute the exact solution
     Z_exact = R+R_L/(1+1j*R_L*C*2*pi*f)
     M_exact = 20*log10(absolute(Z_exact))
     P_exact = angle(Z_exact)*180/pi
     # ensure the error is small
     max_phase_error_in_degree = linalg.norm(P_computed-P_exact, inf)
     max_magniture_error_in_decibel = linalg.norm(M_computed-M_exact, inf)
     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)
Ejemplo n.º 9
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)
 def testRetrieveData(self):
     try:
         from h5py import File
     except ImportError:
         print('module h5py not found')
         return
     device_database = PropertyTree()
     device_database.put_string('type', 'SeriesRC')
     device_database.put_double('series_resistance', R)
     device_database.put_double('capacitance', C)
     device = EnergyStorageDevice(device_database, MPI.COMM_WORLD)
     eis_database  = setup_expertiment()
     eis_database.put_int('steps_per_decade', 1)
     eis_database.put_int('steps_per_cycle', 64)
     eis_database.put_int('cycles', 2)
     eis_database.put_int('ignore_cycles', 1)
     fout = File('trash.hdf5', 'w')
     spectrum_data = measure_impedance_spectrum(device, eis_database, fout)
     fout.close()
     fin = File('trash.hdf5', 'r')
     retrieved_data = retrieve_impedance_spectrum(fin)
     fin.close()
     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)
Ejemplo n.º 11
0
 def test_constant_current_charge_for_given_time(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'constant_current')
     ptree.put_double('current', 5e-3)
     ptree.put_string('end_criterion', 'time')
     ptree.put_double('duration', 15.0)
     ptree.put_double('time_step', 0.1)
     stage = Stage(ptree)
     data = initialize_data()
     steps = stage.run(device, data)
     self.assertEqual(steps, 150)
     self.assertEqual(steps, len(data['time']))
     self.assertAlmostEqual(data['time'][-1], 15.0)
     self.assertAlmostEqual(data['current'][-1], 5e-3)
Ejemplo n.º 12
0
 def test_time_limit(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'time')
     ptree.put_double('duration', 15)
     time_limit = EndCriterion.factory(ptree)
     time_limit.reset(0.0, device)
     self.assertFalse(time_limit.check(2.0, device))
     self.assertTrue(time_limit.check(15.0, device))
     self.assertTrue(time_limit.check(60.0, device))
Ejemplo n.º 13
0
 def test_pickle_support(self):
     import pickle
     src = PropertyTree()
     src.put_double('pi', 3.14)
     src.put_string('greet', 'bonjour')
     p = pickle.dumps(src)
     dst = pickle.loads(p)
     self.assertEqual(dst.get_double('pi'), 3.14)
     self.assertEqual(dst.get_string('greet'), 'bonjour')
Ejemplo n.º 14
0
 def test_setup_frequency_range(self):
     ptree = PropertyTree()
     ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy')
     # specify the upper and lower bounds of the range
     # the number of points per decades controls the spacing on the log
     # scale
     ptree.put_double('frequency_upper_limit', 1e+2)
     ptree.put_double('frequency_lower_limit', 1e-1)
     ptree.put_int('steps_per_decade', 3)
     eis = Experiment(ptree)
     print(eis._frequencies)
     f = eis._frequencies
     self.assertEqual(len(f), 10)
     self.assertAlmostEqual(f[0], 1e+2)
     self.assertAlmostEqual(f[3], 1e+1)
     self.assertAlmostEqual(f[9], 1e-1)
     # or directly specify the frequencies
     frequencies = [3, 2e3, 0.1]
     eis = Experiment(ptree, frequencies)
     self.assertTrue(all(equal(frequencies, eis._frequencies)))
Ejemplo n.º 15
0
 def test_setup_frequency_range(self):
     ptree = PropertyTree()
     ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy')
     # specify the upper and lower bounds of the range
     # the number of points per decades controls the spacing on the log
     # scale
     ptree.put_double('frequency_upper_limit', 1e+2)
     ptree.put_double('frequency_lower_limit', 1e-1)
     ptree.put_int('steps_per_decade', 3)
     eis = Experiment(ptree)
     print(eis._frequencies)
     f = eis._frequencies
     self.assertEqual(len(f), 10)
     self.assertAlmostEqual(f[0], 1e+2)
     self.assertAlmostEqual(f[3], 1e+1)
     self.assertAlmostEqual(f[9], 1e-1)
     # or directly specify the frequencies
     frequencies = [3, 2e3, 0.1]
     eis = Experiment(ptree, frequencies)
     self.assertTrue(all(equal(frequencies, eis._frequencies)))
Ejemplo n.º 16
0
 def test_voltage_limit(self):
     ptree = PropertyTree()
     ptree.put_double('voltage_limit', 1.7)
     # upper limit
     ptree.put_string('end_criterion', 'voltage_greater_than')
     voltage_limit = EndCriterion.factory(ptree)
     voltage_limit.reset(5.0, device)
     device.evolve_one_time_step_constant_voltage(0.2, 1.3)
     self.assertFalse(voltage_limit.check(0.0, device))
     self.assertFalse(voltage_limit.check(60.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 1.7)
     self.assertTrue(voltage_limit.check(45.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 2.1)
     self.assertTrue(voltage_limit.check(45.0, device))
     # lower limit
     ptree.put_string('end_criterion', 'voltage_less_than')
     voltage_limit = EndCriterion.factory(ptree)
     voltage_limit.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.2, 1.3)
     self.assertTrue(voltage_limit.check(0.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 1.7)
     self.assertTrue(voltage_limit.check(45.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 2.1)
     self.assertFalse(voltage_limit.check(45.0, device))
Ejemplo n.º 17
0
 def test_voltage_limit(self):
     ptree = PropertyTree()
     ptree.put_double('voltage_limit', 1.7)
     # upper limit
     ptree.put_string('end_criterion', 'voltage_greater_than')
     voltage_limit = EndCriterion.factory(ptree)
     voltage_limit.reset(5.0, device)
     device.evolve_one_time_step_constant_voltage(0.2, 1.3)
     self.assertFalse(voltage_limit.check(0.0, device))
     self.assertFalse(voltage_limit.check(60.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 1.7)
     self.assertTrue(voltage_limit.check(45.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 2.1)
     self.assertTrue(voltage_limit.check(45.0, device))
     # lower limit
     ptree.put_string('end_criterion', 'voltage_less_than')
     voltage_limit = EndCriterion.factory(ptree)
     voltage_limit.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.2, 1.3)
     self.assertTrue(voltage_limit.check(0.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 1.7)
     self.assertTrue(voltage_limit.check(45.0, device))
     device.evolve_one_time_step_constant_voltage(0.2, 2.1)
     self.assertFalse(voltage_limit.check(45.0, device))
Ejemplo n.º 18
0
    def test_builders(self):
        for AbstractClass in [Observer, Observable]:
            # AbstractClass takes a PropertyTree as argument.
            self.assertRaises(TypeError, AbstractClass)

            # The PropertyTree must specify what concrete class derived from
            # AbstractClass to instantiate.
            ptree = PropertyTree()
            self.assertRaises(KeyError, AbstractClass, ptree)

            # The derived concrete class must be registerd in the dictionary
            # that holds the builders.
            ptree.put_string('type', 'Invalid')
            self.assertRaises(KeyError, AbstractClass, ptree)

            # Now declare a concrete class.
            class ConcreteClass(AbstractClass):
                def __new__(cls, *args, **kwargs):
                    return object.__new__(ConcreteClass)

                def __init__(*args, **kwargs):
                    pass

            # Here is how to register a derived concrete class to the base
            # abstract class.
            AbstractClass._builders['ConcreteClass'] = ConcreteClass

            # Now instantiation works.
            ptree.put_string('type', 'ConcreteClass')
            AbstractClass(ptree)

            # Also can build directly from derived class.
            ConcreteClass()

            # Remove from the dictionary.
            del AbstractClass._builders['ConcreteClass']
            self.assertRaises(KeyError, AbstractClass, ptree)
Ejemplo n.º 19
0
 def test_rest(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'rest')
     evolve_one_time_step = TimeEvolution.factory(ptree)
     evolve_one_time_step(device, 0.1)
     self.assertEqual(device.get_current(), 0.0)
Ejemplo n.º 20
0
 def test_no_name(self):
     ptree = PropertyTree()
     ptree.put_double('initial_voltage', 0)
     ptree.put_double('final_voltage', 0)
     ptree.put_double('scan_limit_1', 1)
     ptree.put_double('scan_limit_2', 0)
     ptree.put_double('step_size', 0.1)
     ptree.put_double('scan_rate', 1)
     ptree.put_int('cycles', 1)
     cv = CyclicVoltammetry(ptree)
     try:
         cv.run(device)
     except:
         self.fail('calling run without data should not raise')
     data = initialize_data()
     cv.run(device, data)
     voltage = array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.,
                      0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.],
                     dtype=float)
     time = linspace(0, 2, 21)
     try:
         testing.assert_array_almost_equal(data['voltage'], voltage)
         testing.assert_array_almost_equal(data['time'], time)
     except AssertionError as e:
         print(e)
         self.fail()
Ejemplo n.º 21
0
 def test_invalid_time_evolution(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'unexpected')
     self.assertRaises(RuntimeError, TimeEvolution.factory, ptree)
Ejemplo n.º 22
0
 def test_current_limit(self):
     # lower
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'current_less_than')
     ptree.put_double('current_limit', -5e-3)
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
     ptree.put_double('current_limit', 0.0)
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
     ptree.put_double('current_limit', 5e-3)
     current_limit = EndCriterion.factory(ptree)
     device.evolve_one_time_step_constant_current(5.0, 0.0)
     self.assertTrue(current_limit.check(NaN, device))
     device.evolve_one_time_step_constant_current(5.0, 0.002)
     self.assertTrue(current_limit.check(NaN, device))
     device.evolve_one_time_step_constant_current(5.0, -0.001)
     self.assertTrue(current_limit.check(180.0, device))
     device.evolve_one_time_step_constant_current(5.0, 0.005)
     self.assertTrue(current_limit.check(180.0, device))
     device.evolve_one_time_step_constant_current(5.0, 0.007)
     self.assertFalse(current_limit.check(180.0, device))
     device.evolve_one_time_step_constant_current(5.0, -15e3)
     self.assertFalse(current_limit.check(180.0, device))
     # upper
     ptree.put_string('end_criterion', 'current_greater_than')
     ptree.put_double('current_limit', -5e-3)
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
     ptree.put_double('current_limit', 0.0)
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
     ptree.put_double('current_limit', 5e-3)
     current_limit = EndCriterion.factory(ptree)
     device.evolve_one_time_step_constant_current(5.0, -1e-3)
     self.assertFalse(current_limit.check(NaN, device))
     device.evolve_one_time_step_constant_current(5.0, 0.002)
     self.assertFalse(current_limit.check(NaN, device))
     device.evolve_one_time_step_constant_current(5.0, 0.005)
     self.assertTrue(current_limit.check(NaN, device))
     device.evolve_one_time_step_constant_current(5.0, -0.2)
     self.assertTrue(current_limit.check(NaN, device))
     device.evolve_one_time_step_constant_current(5.0, 3.0)
     self.assertTrue(current_limit.check(NaN, device))
Ejemplo n.º 23
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)
Ejemplo n.º 24
0
def run():
    # parse uq database
    input_database=PropertyTree()
    input_database.parse_xml('uq.xml')
    uq_database=input_database.get_child('uq')

    # declare parameters
    params=uq_database.get_int('params')
    parameter_list=[]
    for p in range(params):
        parameter_database=uq_database.get_child('param_'+str(p))
        distribution_type=parameter_database.get_string('distribution_type')
        parameter_name=parameter_database.get_string('name')
        if distribution_type=='uniform':
            parameter_range=parameter_database.get_array_double('range')
            parameter_list.append(UniformParameter('param_'+str(p),
                                                   parameter_name,
                                                   min=parameter_range[0],
                                                   max=parameter_range[1]))
        elif distribution_type=='normal':
            parameter_mean=parameter_database.get_double('mean')
            parameter_standard_deviation=parameter_database.get_double('standard_deviation')
            parameter_list.append(NormalParameter('param_'+str(p),
                                                  parameter_name,
                                                  mean=parameter_mean,
                                                  dev=parameter_standard_deviation))
        else:
            raise RuntimeError('invalid distribution type '+distribution_type+' for param_'+str(p))

    # create a host
    host_database=uq_database.get_child('host')
    host_type=host_database.get_string('type')
    if host_type=="Interactive":
        host=InteractiveHost(cpus=host_database.get_int_with_default_value('cpus',1),
                             cpus_per_node=host_database.get_int_with_default_value('cpus_per_node',0))
    elif host_type=="PBS":
        host=PBSHost(host_database.get_string('env'),
                     cpus=host_database.get_int_with_default_value('cpus',0),
                     cpus_per_node=host_database.get_int_with_default_value('cpus_per_node',0),
                     qname=host_database.get_string_with_default_value('qname','standby'),
                     walltime=host_database.get_string_with_default_value('walltime','1:00:00'),
                     modules=host_database.get_string_with_default_value('modules',''),
                     pack=host_database.get_int_with_default_value('pack',1),
                     qlimit=host_database.get_int_with_default_value('qlimit',200))
    else:
        raise RuntimeError('invalid host type '+host_type)

    # pick UQ method
    method=uq_database.get_string('method')
    if method=='SmolyakSparseGrid':
        level=uq_database.get_int('level')
        uq=Smolyak(parameter_list,level=level)
    elif method=='MonteCarlo':
        samples=uq_database.get_int('samples')
        uq=MonteCarlo(parameter_list,num=samples)
    elif method=='LatinHypercubeSampling':
        samples=uq_database.get_int('samples')
        uq=LHS(parameter_list,num=samples)
    else:
        raise RuntimeError('invalid UQ method '+method)

    # make a test program
    test_program_database=uq_database.get_child('test_program')
    description=test_program_database.get_string('description')
    executable_name=test_program_database.get_string('executable')
    for p in range(params):
        executable_name+=' --param_'+str(p)+' $param_'+str(p)
    prog=TestProgram(exe=executable_name,
                     desc=description)

    # run
    return Sweep(uq,host,prog)
Ejemplo n.º 25
0
 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)
     self.assertGreaterEqual(data['voltage'][-1], 1.4)
     self.assertAlmostEqual(data['time'][1] - data['time'][0], 0.2)
Ejemplo n.º 26
0
def run_discharge(device, ptree):
    data = initialize_data()

    # (re)charge the device
    initial_voltage = ptree.get_double('initial_voltage')

    charge_database = PropertyTree()
    charge_database.put_string('charge_mode', 'constant_current')
    charge_database.put_double('charge_current', 10.0)
    charge_database.put_string('charge_stop_at_1', 'voltage_greater_than')
    charge_database.put_double('charge_voltage_limit', initial_voltage)
    charge_database.put_bool('charge_voltage_finish', True)
    charge_database.put_double('charge_voltage_finish_current_limit', 1e-2)
    charge_database.put_double('charge_voltage_finish_max_time', 600)
    charge_database.put_double('charge_rest_time', 0)
    charge_database.put_double('time_step', 10.0)

    charge = Charge(charge_database)
    start = time()
    charge.run(device, data)
    end = time()
    # used for tracking time of this substep
    print('Charge: %s min' % ((end - start) / 60))

    data['time'] -= data['time'][-1]

    # discharge at constant power
    discharge_power = ptree.get_double('discharge_power')
    final_voltage = ptree.get_double('final_voltage')
    time_step = ptree.get_double('time_step')

    discharge_database = PropertyTree()
    discharge_database.put_string('discharge_mode', 'constant_power')
    discharge_database.put_double('discharge_power', discharge_power)
    discharge_database.put_string('discharge_stop_at_1', 'voltage_less_than')
    discharge_database.put_double('discharge_voltage_limit', final_voltage)
    discharge_database.put_double('discharge_rest_time', 10 * time_step)
    discharge_database.put_double('time_step', time_step)

    discharge = Discharge(discharge_database)
    start = time()
    discharge.run(device, data)
    end = time()
    # used for tracking time of this substep
    print('Discharge: %s min' % ((end - start) / 60))

    return data
Ejemplo n.º 27
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)
Ejemplo n.º 28
0
 def test_fourier_analysis(self):
     ptree = PropertyTree()
     ptree.put_int('steps_per_cycle', 3)
     ptree.put_int('cycles', 1)
     ptree.put_int('ignore_cycles', 0)
     ptree.put_string('harmonics', '1')
     # uninitialized data
     data = {}
     self.assertRaises(KeyError, fourier_analysis, data, ptree)
     # empty data
     data = initialize_data()
     self.assertRaises(IndexError, fourier_analysis, data, ptree)
     # bad data
     data['time'] = array([1, 2, 3], dtype=float)
     data['current'] = array([4, 5, 6], dtype=float)
     data['voltage'] = array([7, 8], dtype=float)
     self.assertRaises(AssertionError, fourier_analysis, data, ptree)
     # poor data (size not a power of 2)
     data['voltage'] = array([7, 8, 9], dtype=float)
     with catch_warnings():
         simplefilter("error")
         self.assertRaises(RuntimeWarning, fourier_analysis, data, ptree)
     # data unchanged after analyze
     dummy = array([1, 2, 3, 4, 5, 6, 7, 8], dtype=float)
     data['time'] = dummy
     data['current'] = dummy
     data['voltage'] = dummy
     # ptree needs to be updated
     self.assertRaises(AssertionError, fourier_analysis, data, ptree)
     ptree.put_int('steps_per_cycle', 4)
     ptree.put_int('cycles', 2)
     ptree.put_int('ignore_cycles', 0)
     fourier_analysis(data, ptree)
     self.assertTrue(all(equal(data['time'], dummy)))
     self.assertTrue(all(equal(data['current'], dummy)))
     self.assertTrue(all(equal(data['voltage'], dummy)))
Ejemplo n.º 29
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)
Ejemplo n.º 30
0
# Copyright (c) 2016, the Cap authors.
#
# 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)
Ejemplo n.º 31
0
 def test_charge_constant_voltage(self):
     ptree = PropertyTree()
     ptree.put_string('charge_mode', 'constant_voltage')
     ptree.put_double('charge_voltage', 1.4)
     ptree.put_string('charge_stop_at_1', 'current_less_than')
     ptree.put_double('charge_current_limit', 1e-6)
     ptree.put_string('charge_stop_at_2', 'time')
     ptree.put_double('charge_max_duration', 60)
     ptree.put_double('time_step', 0.2)
     charge = Charge(ptree)
     data = initialize_data()
     charge.run(device, data)
     self.assertTrue(data['time'][-1] >= 60 or
                     abs(data['current'][-1]) <= 1e-6)
     self.assertAlmostEqual(data['voltage'][-1], 1.4)
    def test_consistency_pycap_simulation(self):
        #
        # weak run test; simply ensures that Dualfoil object
        # can be run with pycap.Charge
        #
        df1 = Dualfoil(path=path)  # will use pycap
        df2 = Dualfoil(path=path)  # manual runs
        im = df_manip.InputManager(path=path)

        # testing a charge-to-hold-const-voltage
        # manual
        # use InputManager to set the input file
        c = -12.0  # constant current
        im.add_new_leg(c, 5.0, 1)
        df1.run()
        df1.outbot.update_output()
        v = 4.54  # constant voltage
        im.add_new_leg(v, 5.0, 0)
        df1.run()
        df1.outbot.update_output()

        # pycap simulation
        # build a ptree input
        ptree = PropertyTree()
        ptree.put_double('time_step', 300.0)  # 5 minutes
        ptree.put_string('charge_mode', 'constant_current')
        ptree.put_double('charge_current', 12.0)
        ptree.put_string('charge_stop_at_1', 'voltage_greater_than')
        ptree.put_double('charge_voltage_limit', 4.54)
        ptree.put_bool('charge_voltage_finish', True)
        # hold end voltage after either 5 minutes have passed
        # OR current falls under 1 ampere
        ptree.put_double('charge_voltage_finish_max_time', 300.0)
        ptree.put_double('charge_voltage_finish_current_limit', 1.0)

        const_current_const_voltage = Charge(ptree)
        const_current_const_voltage.run(df2)

        # check the output lists of both devices
        o1 = df1.outbot.output
        o2 = df2.outbot.output
        self.assertEqual(len(o1['time']), len(o2['time']))
        for i in range(len(o1['time'])):
            self.assertAlmostEqual(o1['time'][i], o2['time'][i])
            # BELOW: relaxed delta for voltage
            # REASON: dualfoil cuts off its voltages at 5
            #   decimal places, meaning that this end-digit
            #   is subject to roundoff errors
            error = 1e-5
            self.assertAlmostEqual(o1['voltage'][i],
                                   o2['voltage'][i],
                                   delta=error)
            self.assertAlmostEqual(o1['current'][i], o2['current'][i])
Ejemplo n.º 33
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'])
    def test_accuracy_pycap_simulation(self):
        #
        # tests the accuracy of a pycap simulation against a
        # straight run dualfoil sim with different timesteps
        #
        df1 = Dualfoil(path=path)  # manual runs
        df2 = Dualfoil(path=path)  # pycap simulation
        im = df_manip.InputManager(path=path)

        # testing a charge-to-hold-const-voltage
        # manual
        # use InputManager to set the input file
        c = -10.0  # constant charge current
        # charge for 5 minutes straight
        im.add_new_leg(c, 5, 1)
        df1.run()
        df1.outbot.update_output()
        v = 4.539  # expected voltage after 5 minutes
        # hold constant voltage for 3 minutes straight
        im.add_new_leg(v, 3.0, 0)
        df1.run()
        df1.outbot.update_output()

        # pycap simulation
        # build a ptree input
        ptree = PropertyTree()
        ptree.put_double('time_step', 30.0)  # 30 second time step
        ptree.put_string('charge_mode', 'constant_current')
        ptree.put_double('charge_current', 10.0)
        ptree.put_string('charge_stop_at_1', 'voltage_greater_than')
        ptree.put_double('charge_voltage_limit', v)
        ptree.put_bool('charge_voltage_finish', True)
        # hold end voltage after either 3 minutes have passed
        # OR current falls under 1 ampere
        ptree.put_double('charge_voltage_finish_max_time', 180.0)
        ptree.put_double('charge_voltage_finish_current_limit', 1.0)

        const_current_const_voltage = Charge(ptree)
        const_current_const_voltage.run(df2)

        o1 = df1.outbot.output  # contains sim1 output
        o2 = df2.outbot.output  # contains sim2 output

        # affirm we make it this far and have usable data
        self.assertTrue(len(o1['time']) > 0)
        self.assertTrue(len(o2['time']) > 0)
        # lengths of data should be different
        self.assertFalse(len(o1['time']) == len(o2['time']))

        # TEST LOGIC:
        #  -Merge the two outputs into one, sorted by
        #   increasing time stamps.
        #  -Compare the consistency of the two simulations
        #   by checking for smooth changes within the curves
        #   of the combined output lists
        o1['time'].extend(o2['time'])
        time = ar(o1['time'])  # nparray
        o1['voltage'].extend(o2['voltage'])
        voltage = ar(o1['voltage'])  # nparray
        o1['current'].extend(o2['current'])
        current = ar(o1['current'])  # np array
        # create a dictionary with the combined output lists
        output = {'time': time, 'voltage': voltage, 'current': current}
        # sort based on time, keeping the three types aligned
        key = argsort(output['time'])
        # for using the key to sort the list
        tmp = {'time': [], 'voltage': [], 'current': []}
        for i in key:
            tmp['time'].append(output['time'][i])
            tmp['voltage'].append(output['voltage'][i])
            tmp['current'].append(output['current'][i])
        # reassign ordered set to `output` as nparrays
        output['time'] = ar(tmp['time'])
        output['voltage'] = ar(tmp['voltage'])
        output['current'] = ar(tmp['current'])

        # BELOW: first 20 seconds are identical time stamps;
        #     skip these to avoid errors from incorrect sorting
        # REASON FOR ERROR: Dualfoil only prints time data as
        #     precice as minutes to three decimal places. So when
        #     the following is generated....
        #       Manual Run         |       Pycap Simulation
        #  (min)     (V)     (amp) |  (min)     (V)     (amp)
        #  .001   4.52345    10.0  |  .001   4.52345    10.0
        #  .001   4.52349    10.0  |  .001   4.52349    10.0
        #           ...                       ...
        #     ...python's `sorted()` function has no way of
        #     distinguishing entries; it instead returns this:
        # [
        #   (.001, 4.52345, 10.0),
        #   (.001, 4.52349, 10.0),  <- these two should
        #   (.001, 4.52345, 10.0),  <-   be switched
        #   (.001, 4.52349, 10.0)
        # ]
        # SOLUTION: consistency test affirms that the exact same
        #     time step will produce same current and voltage, so
        #     skip ahead to first instance where time stamps will
        #     be out of alignment
        i = 0
        while output['time'][i] <= 0.4:  # 24 seconds
            i = i + 1
        index_limit = len(output['time']) - 1

        # go through and affirm smoothness of curve
        while i < index_limit:
            # Check if time values are the same to 3 decimal places.
            # If so, current and voltage are not guarunteed
            #   to also be exactly the same, but should be close
            if output['time'][i] == output['time'][i - 1]:
                # affirm that current is virtually the same
                self.assertAlmostEqual(output['current'][i],
                                       output['current'][i - 1])
                # BELOW: delta is eased slightly
                # REASON: `sorted()` can't tell which entry came
                #   first from same time-stamp if from different
                #   simulations; allow for this with error
                error = 3e-5
                self.assertAlmostEqual(output['voltage'][i],
                                       output['voltage'][i - 1],
                                       delta=error)
            else:
                # Time values are different
                # Check to affirm that the variable NOT being held
                # constant is steadily increasing / decreasing

                # First part happens in first 4 minutes
                if output['time'][i] <= 5.0:  # part 1, const currrent
                    # current should be equal
                    self.assertEqual(output['current'][i],
                                     output['current'][i - 1])
                    # voltage should not have decreased
                    self.assertTrue(output['voltage'][i],
                                    output['voltage'][i - 1])
                else:  # part 2, const voltage
                    # current should be getting less positive
                    self.assertTrue(
                        output['current'][i] <= output['current'][i - 1],
                        msg=(output['current'][i - 2:i + 10],
                             output['time'][i - 2:i + 10]))
                    # voltage should decrease, then stay at 4.54
                    if output['voltage'][i - 1] == 4.54:
                        self.assertEqual(output['voltage'][i],
                                         output['voltage'][i - 1])
                    else:
                        self.assertTrue(
                            output['voltage'][i] <= output['voltage'][i - 1])
            # update index
            i = i + 1
Ejemplo n.º 35
0
 def test_property_tree(self):
     # ptree as container to store int, double, string, and bool
     ptree = PropertyTree()
     ptree.put_int('dim', 3)
     self.assertEqual(ptree.get_int('dim'), 3)
     ptree.put_double('path.to.pi', 3.14)
     self.assertEqual(ptree.get_double('path.to.pi'), 3.14)
     ptree.put_string('good.news', 'it works')
     self.assertEqual(ptree.get_string('good.news'), 'it works')
     ptree.put_bool('is.that.a.good.idea', False)
     self.assertEqual(ptree.get_bool('is.that.a.good.idea'), False)
Ejemplo n.º 36
0
def run_discharge(device, ptree):
    data = initialize_data()

    # (re)charge the device
    initial_voltage = ptree.get_double('initial_voltage')

    charge_database = PropertyTree()
    charge_database.put_string('charge_mode', 'constant_current')
    charge_database.put_double('charge_current', 10.0)
    charge_database.put_string('charge_stop_at_1', 'voltage_greater_than')
    charge_database.put_double('charge_voltage_limit', initial_voltage)
    charge_database.put_bool('charge_voltage_finish', True)
    charge_database.put_double('charge_voltage_finish_current_limit', 1e-2)
    charge_database.put_double('charge_voltage_finish_max_time', 600)
    charge_database.put_double('charge_rest_time', 0)
    charge_database.put_double('time_step', 10.0)

    charge = Charge(charge_database)
    start = time()
    charge.run(device, data)
    end = time()
    # used for tracking time of this substep
    print('Charge: %s min' % ((end-start) / 60))

    data['time'] -= data['time'][-1]

    # discharge at constant power
    discharge_power = ptree.get_double('discharge_power')
    final_voltage = ptree.get_double('final_voltage')
    time_step = ptree.get_double('time_step')

    discharge_database = PropertyTree()
    discharge_database.put_string('discharge_mode', 'constant_power')
    discharge_database.put_double('discharge_power', discharge_power)
    discharge_database.put_string('discharge_stop_at_1', 'voltage_less_than')
    discharge_database.put_double('discharge_voltage_limit', final_voltage)
    discharge_database.put_double('discharge_rest_time', 10 * time_step)
    discharge_database.put_double('time_step', time_step)

    discharge = Discharge(discharge_database)
    start = time()
    discharge.run(device, data)
    end = time()
    # used for tracking time of this substep
    print('Discharge: %s min' % ((end-start) / 60))

    return data
def setup_expertiment():
    eis_database = PropertyTree()
    eis_database.put_double('frequency_upper_limit', 1e+4)
    eis_database.put_double('frequency_lower_limit', 1e-6)
    eis_database.put_int('steps_per_decade', 3)
    eis_database.put_int('steps_per_cycle', 1024)
    eis_database.put_int('cycles', 2)
    eis_database.put_int('ignore_cycles', 1)
    eis_database.put_double('dc_voltage', 0)
    eis_database.put_string('harmonics', ' 3')
    eis_database.put_string('amplitudes', ' 5e-3')
    eis_database.put_string('phases', '0')

    return eis_database
Ejemplo n.º 38
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'])
Ejemplo n.º 39
0
 def test_compound_criterion(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'compound')
     ptree.put_string('criterion_0.end_criterion', 'time')
     ptree.put_double('criterion_0.duration', 5.0)
     ptree.put_string('criterion_1.end_criterion', 'voltage_greater_than')
     ptree.put_double('criterion_1.voltage_limit', 2.0)
     # no default value for now
     self.assertRaises(KeyError, EndCriterion.factory, ptree)
     ptree.put_string('logical_operator', 'bad_operator')
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
     ptree.put_string('logical_operator', 'or')
     compound_criterion = EndCriterion.factory(ptree)
     compound_criterion.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.1, 1.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     device.evolve_one_time_step_constant_voltage(0.1, 2.0)
     self.assertTrue(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     ptree.put_string('logical_operator', 'and')
     compound_criterion = EndCriterion.factory(ptree)
     compound_criterion.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.1, 1.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertFalse(compound_criterion.check(5.0, device))
     device.evolve_one_time_step_constant_voltage(0.1, 2.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     ptree.put_string('logical_operator', 'xor')
     compound_criterion = EndCriterion.factory(ptree)
     compound_criterion.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.1, 1.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     device.evolve_one_time_step_constant_voltage(0.1, 2.0)
     self.assertTrue(compound_criterion.check(3.0, device))
     self.assertFalse(compound_criterion.check(5.0, device))
Ejemplo n.º 40
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)
Ejemplo n.º 41
0
 def test_rest(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'rest')
     evolve_one_time_step = TimeEvolution.factory(ptree)
     evolve_one_time_step(device, 0.1)
     self.assertEqual(device.get_current(), 0.0)
Ejemplo n.º 42
0
 def test_constructor(self):
     self.assertRaises(TypeError, TimeEvolution)
     self.assertRaises(RuntimeError, TimeEvolution, PropertyTree())
Ejemplo n.º 43
0
 def test_get_children(self):
     ptree = PropertyTree()
     # put child
     child = PropertyTree()
     child.put_double('prune', 6.10)
     ptree.put_child('a.g', child)
     self.assertEqual(ptree.get_double('a.g.prune'), 6.10)
     # get child
     ptree.put_string('child.name', 'clement')
     ptree.put_int('child.age', -2)
     child = ptree.get_child('child')
     self.assertEqual(child.get_string('name'), 'clement')
     self.assertEqual(child.get_int('age'), -2)
Ejemplo n.º 44
0
 def test_force_discharge(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'constant_voltage')
     ptree.put_double('voltage', 0.0)
     ptree.put_string('end_criterion', 'current_less_than')
     ptree.put_double('current_limit', 1e-5)
     ptree.put_double('time_step', 1.0)
     stage = Stage(ptree)
     data = initialize_data()
     steps = stage.run(device, data)
     self.assertGreaterEqual(steps, 1)
     self.assertEqual(steps, len(data['time']))
     self.assertAlmostEqual(data['voltage'][-1], 0.0)
     self.assertLessEqual(data['current'][-1], 1e-5)
Ejemplo n.º 45
0
 def test_invalid_time_evolution(self):
     ptree = PropertyTree()
     ptree.put_string('mode', 'unexpected')
     self.assertRaises(RuntimeError, TimeEvolution.factory, ptree)
Ejemplo n.º 46
0
 def test_get_array(self):
     ptree = PropertyTree()
     # array of double
     ptree.put_string('array_double', '3.14,1.41')
     array_double = ptree.get_array_double('array_double')
     self.assertEqual(array_double, [3.14, 1.41])
     # ... string
     ptree.put_string('array_int', '1,2,3')
     array_int = ptree.get_array_int('array_int')
     self.assertEqual(array_int, [1, 2, 3])
     # ... int
     ptree.put_string('array_string', 'uno,dos,tres,cuatro')
     array_string = ptree.get_array_string('array_string')
     self.assertEqual(array_string, ['uno', 'dos', 'tres', 'cuatro'])
     # ... bool
     ptree.put_string('array_bool', 'true,FALSE,False')
     array_bool = ptree.get_array_bool('array_bool')
     self.assertEqual(array_bool, [True, False, False])
Ejemplo n.º 47
0
 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)
     self.assertGreaterEqual(data['voltage'][-1], 1.4)
     self.assertAlmostEqual(data['time'][1] - data['time'][0], 0.2)
Ejemplo n.º 48
0
 def test_no_name(self):
     ptree = PropertyTree()
     ptree.put_double('initial_voltage', 0)
     ptree.put_double('final_voltage', 0)
     ptree.put_double('scan_limit_1', 1)
     ptree.put_double('scan_limit_2', 0)
     ptree.put_double('step_size', 0.1)
     ptree.put_double('scan_rate', 1)
     ptree.put_int('cycles', 1)
     cv = CyclicVoltammetry(ptree)
     try:
         cv.run(device)
     except:
         self.fail('calling run without data should not raise')
     data = initialize_data()
     cv.run(device, data)
     voltage = array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.,
                      0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.],
                     dtype=float)
     time = linspace(0, 2, 21)
     try:
         testing.assert_array_almost_equal(data['voltage'], voltage)
         testing.assert_array_almost_equal(data['time'], time)
     except AssertionError as e:
         print(e)
         self.fail()
Ejemplo n.º 49
0
# Copyright (c) 2016, the Cap authors.
#
# 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)
Ejemplo n.º 50
0
def run():
    # parse uq database
    input_database = PropertyTree()
    input_database.parse_xml('uq.xml')
    uq_database = input_database.get_child('uq')

    # declare parameters
    params = uq_database.get_int('params')
    parameter_list = []
    for p in range(params):
        parameter_database = uq_database.get_child('param_' + str(p))
        distribution_type = parameter_database.get_string('distribution_type')
        parameter_name = parameter_database.get_string('name')
        if distribution_type == 'uniform':
            parameter_range = parameter_database.get_array_double('range')
            parameter_list.append(
                UniformParameter('param_' + str(p),
                                 parameter_name,
                                 min=parameter_range[0],
                                 max=parameter_range[1]))
        elif distribution_type == 'normal':
            parameter_mean = parameter_database.get_double('mean')
            parameter_standard_deviation = parameter_database.get_double(
                'standard_deviation')
            parameter_list.append(
                NormalParameter('param_' + str(p),
                                parameter_name,
                                mean=parameter_mean,
                                dev=parameter_standard_deviation))
        else:
            raise RuntimeError('invalid distribution type ' +
                               distribution_type + ' for param_' + str(p))

    # create a host
    host_database = uq_database.get_child('host')
    host_type = host_database.get_string('type')
    if host_type == "Interactive":
        host = InteractiveHost(
            cpus=host_database.get_int_with_default_value('cpus', 1),
            cpus_per_node=host_database.get_int_with_default_value(
                'cpus_per_node', 0))
    elif host_type == "PBS":
        host = PBSHost(
            host_database.get_string('env'),
            cpus=host_database.get_int_with_default_value('cpus', 0),
            cpus_per_node=host_database.get_int_with_default_value(
                'cpus_per_node', 0),
            qname=host_database.get_string_with_default_value(
                'qname', 'standby'),
            walltime=host_database.get_string_with_default_value(
                'walltime', '1:00:00'),
            modules=host_database.get_string_with_default_value('modules', ''),
            pack=host_database.get_int_with_default_value('pack', 1),
            qlimit=host_database.get_int_with_default_value('qlimit', 200))
    else:
        raise RuntimeError('invalid host type ' + host_type)

    # pick UQ method
    method = uq_database.get_string('method')
    if method == 'SmolyakSparseGrid':
        level = uq_database.get_int('level')
        uq = Smolyak(parameter_list, level=level)
    elif method == 'MonteCarlo':
        samples = uq_database.get_int('samples')
        uq = MonteCarlo(parameter_list, num=samples)
    elif method == 'LatinHypercubeSampling':
        samples = uq_database.get_int('samples')
        uq = LHS(parameter_list, num=samples)
    else:
        raise RuntimeError('invalid UQ method ' + method)

    # make a test program
    test_program_database = uq_database.get_child('test_program')
    description = test_program_database.get_string('description')
    executable_name = test_program_database.get_string('executable')
    for p in range(params):
        executable_name += ' --param_' + str(p) + ' $param_' + str(p)
    prog = TestProgram(exe=executable_name, desc=description)

    # run
    return Sweep(uq, host, prog)
Ejemplo n.º 51
0
 def test_charge_constant_voltage(self):
     ptree = PropertyTree()
     ptree.put_string('charge_mode', 'constant_voltage')
     ptree.put_double('charge_voltage', 1.4)
     ptree.put_string('charge_stop_at_1', 'current_less_than')
     ptree.put_double('charge_current_limit', 1e-6)
     ptree.put_string('charge_stop_at_2', 'time')
     ptree.put_double('charge_max_duration', 60)
     ptree.put_double('time_step', 0.2)
     charge = Charge(ptree)
     data = initialize_data()
     charge.run(device, data)
     self.assertTrue(data['time'][-1] >= 60
                     or abs(data['current'][-1]) <= 1e-6)
     self.assertAlmostEqual(data['voltage'][-1], 1.4)
Ejemplo n.º 52
0
 def test_never_statisfied(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'none')
     never_statisfied = EndCriterion.factory(ptree)
     never_statisfied.reset(0.0, device)
     self.assertFalse(never_statisfied.check(NaN, device))
Ejemplo n.º 53
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)
Ejemplo n.º 54
0
 def test_always_statisfied(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'skip')
     always_statisfied = EndCriterion.factory(ptree)
     always_statisfied.reset(0.0, device)
     self.assertTrue(always_statisfied.check(NaN, device))
Ejemplo n.º 55
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)
Ejemplo n.º 56
0
 def test_invalid_end_criterion(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'bad_name')
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
Ejemplo n.º 57
0
 def test_fourier_analysis(self):
     ptree = PropertyTree()
     ptree.put_int('steps_per_cycle', 3)
     ptree.put_int('cycles', 1)
     ptree.put_int('ignore_cycles', 0)
     ptree.put_string('harmonics', '1')
     # uninitialized data
     data = {}
     self.assertRaises(KeyError, fourier_analysis, data, ptree)
     # empty data
     data = initialize_data()
     self.assertRaises(IndexError, fourier_analysis, data, ptree)
     # bad data
     data['time'] = array([1, 2, 3], dtype=float)
     data['current'] = array([4, 5, 6], dtype=float)
     data['voltage'] = array([7, 8], dtype=float)
     self.assertRaises(AssertionError, fourier_analysis, data, ptree)
     # poor data (size not a power of 2)
     data['voltage'] = array([7, 8, 9], dtype=float)
     with catch_warnings():
         simplefilter("error")
         self.assertRaises(RuntimeWarning, fourier_analysis, data, ptree)
     # data unchanged after analyze
     dummy = array([1, 2, 3, 4, 5, 6, 7, 8], dtype=float)
     data['time'] = dummy
     data['current'] = dummy
     data['voltage'] = dummy
     # ptree needs to be updated
     self.assertRaises(AssertionError, fourier_analysis, data, ptree)
     ptree.put_int('steps_per_cycle', 4)
     ptree.put_int('cycles', 2)
     ptree.put_int('ignore_cycles', 0)
     fourier_analysis(data, ptree)
     self.assertTrue(all(equal(data['time'], dummy)))
     self.assertTrue(all(equal(data['current'], dummy)))
     self.assertTrue(all(equal(data['voltage'], dummy)))
Ejemplo n.º 58
0
 def test_constructor(self):
     self.assertRaises(TypeError, EndCriterion)
     self.assertRaises(RuntimeError, EndCriterion, PropertyTree())
Ejemplo n.º 59
0
 def test_time_steps(self):
     ptree = PropertyTree()
     ptree.put_int('stages', 2)
     ptree.put_int('cycles', 1)
     ptree.put_double('time_step', 1.0)
     ptree.put_string('stage_0.mode', 'hold')
     ptree.put_string('stage_0.end_criterion', 'time')
     ptree.put_double('stage_0.duration', 2.0)
     ptree.put_string('stage_1.mode', 'rest')
     ptree.put_string('stage_1.end_criterion', 'time')
     ptree.put_double('stage_1.duration', 1.0)
     ptree.put_double('stage_1.time_step', 0.1)
     multi = MultiStage(ptree)
     data = initialize_data()
     steps = multi.run(device, data)
     self.assertEqual(steps, 12)
     self.assertEqual(steps, len(data['time']))
     self.assertAlmostEqual(data['time'][5], 2.4)
     self.assertAlmostEqual(data['voltage'][0], data['voltage'][1])
     self.assertAlmostEqual(data['current'][3], 0.0)
Ejemplo n.º 60
0
 def test_compound_criterion(self):
     ptree = PropertyTree()
     ptree.put_string('end_criterion', 'compound')
     ptree.put_string('criterion_0.end_criterion', 'time')
     ptree.put_double('criterion_0.duration', 5.0)
     ptree.put_string('criterion_1.end_criterion', 'voltage_greater_than')
     ptree.put_double('criterion_1.voltage_limit', 2.0)
     # no default value for now
     self.assertRaises(KeyError, EndCriterion.factory, ptree)
     ptree.put_string('logical_operator', 'bad_operator')
     self.assertRaises(RuntimeError, EndCriterion.factory, ptree)
     ptree.put_string('logical_operator', 'or')
     compound_criterion = EndCriterion.factory(ptree)
     compound_criterion.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.1, 1.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     device.evolve_one_time_step_constant_voltage(0.1, 2.0)
     self.assertTrue(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     ptree.put_string('logical_operator', 'and')
     compound_criterion = EndCriterion.factory(ptree)
     compound_criterion.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.1, 1.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertFalse(compound_criterion.check(5.0, device))
     device.evolve_one_time_step_constant_voltage(0.1, 2.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     ptree.put_string('logical_operator', 'xor')
     compound_criterion = EndCriterion.factory(ptree)
     compound_criterion.reset(0.0, device)
     device.evolve_one_time_step_constant_voltage(0.1, 1.0)
     self.assertFalse(compound_criterion.check(3.0, device))
     self.assertTrue(compound_criterion.check(5.0, device))
     device.evolve_one_time_step_constant_voltage(0.1, 2.0)
     self.assertTrue(compound_criterion.check(3.0, device))
     self.assertFalse(compound_criterion.check(5.0, device))