Beispiel #1
0
 def test_time(self):
     a = Time(20, "h")
     self.assertAlmostEqual(float(a.to("s")), 3600 * 20)
     #Test left and right multiplication.
     b = a * 3
     self.assertAlmostEqual(float(b), 60.0)
     self.assertEqual(str(b.unit), "h")
     self.assertEqual(float(3 * a), 60.0)
Beispiel #2
0
 def test_time(self):
     a = Time(20, "h")
     self.assertAlmostEqual(float(a.to("s")), 3600 * 20)
     #Test left and right multiplication.
     b = a * 3
     self.assertAlmostEqual(float(b), 60.0)
     self.assertEqual(str(b.unit), "h")
     self.assertEqual(float(3 * a), 60.0)
Beispiel #3
0
def slurm_parse_timestr(s):
    """
    A slurm time parser. Accepts a string in one the following forms:

        # "days-hours",
        # "days-hours:minutes",
        # "days-hours:minutes:seconds".
        # "minutes",
        # "minutes:seconds",
        # "hours:minutes:seconds",

    Returns:
        Time in seconds.

    Raises:
        `ValueError` if string is not valid.
    """
    days, hours, minutes, seconds = 0, 0, 0, 0

    if type(s) == type(1):
        return Time(s, "s")

    if '-' in s:
        # "days-hours",
        # "days-hours:minutes",
        # "days-hours:minutes:seconds".
        days, s = s.split("-")
        days = int(days)

        if ':' not in s:
            hours = int(float(s))
        elif s.count(':') == 1:
            hours, minutes = map(int, s.split(':'))
        elif s.count(':') == 2:
            hours, minutes, seconds = map(int, s.split(':'))
        else:
            raise ValueError("More that 2 ':' in string!")

    else:
        # "minutes",
        # "minutes:seconds",
        # "hours:minutes:seconds",
        if ':' not in s:
            minutes = int(float(s))
        elif s.count(':') == 1:
            minutes, seconds = map(int, s.split(':'))
        elif s.count(':') == 2:
            hours, minutes, seconds = map(int, s.split(':'))
        else:
            raise ValueError("More than 2 ':' in string!")

    return Time((days * 24 + hours) * 3600 + minutes * 60 + seconds, "s")
Beispiel #4
0
 def test_compound_operations(self):
     g = 10 * Length(1, "m") / (Time(1, "s") ** 2)
     e = Mass(1, "kg") * g * Length(1, "m")
     self.assertEqual(str(e), "10.0 N m")
     form_e = FloatWithUnit(10, unit="kJ mol^-1")
     self.assertEqual(str(form_e.to("eV atom^-1")), "0.103642691905 eV atom^-1")
     self.assertRaises(UnitError, form_e.to, "m s^-1")
     a = FloatWithUnit(1.0, "Ha^3")
     self.assertEqual(str(a.to("J^3")), "8.28672661615e-53 J^3")
     a = FloatWithUnit(1.0, "Ha bohr^-2")
     self.assertEqual(str(a.to("J m^-2")), "1556.89291457 J m^-2")
Beispiel #5
0
def time2pbspro(timeval, unit="s"):
    """
    Convert a number representing a time value in the given unit (Default: seconds)
    to a string following the PbsPro convention: "hours:minutes:seconds".

    >>> assert time2pbspro(2, unit="d") == '48:0:0'
    """
    h, m, s = 3600, 60, 1

    timeval = Time(timeval, unit).to("s")
    hours, minutes = divmod(timeval, h)
    minutes, secs = divmod(minutes, m)

    return "%d:%d:%d" % (hours, minutes, secs)
Beispiel #6
0
def time2loadlever(timeval, unit="s"):
    """
    Convert a number representing a time value in the given unit (Default: seconds)
    to a string following the LoadLever convention. format hh:mm:ss (hours:minutes:seconds)

    >>> assert time2loadlever(2, unit="d") == '48:00:00'
    """
    h, m, s = 3600, 60, 1

    timeval = Time(timeval, unit).to("s")
    hours, minutes = divmod(timeval, h)
    minutes, secs = divmod(minutes, m)

    return "%d:%02d:%02d" % (hours, minutes, secs)
Beispiel #7
0
 def test_compound_operations(self):
     g = 10 * Length(1, "m") / (Time(1, "s")**2)
     e = Mass(1, "kg") * g * Length(1, "m")
     self.assertEqual(str(e), "10.0 N m")
     form_e = FloatWithUnit(10, unit="kJ mol^-1").to("eV atom^-1")
     self.assertAlmostEqual(float(form_e), 0.103642691905)
     self.assertEqual(str(form_e.unit), "eV atom^-1")
     self.assertRaises(UnitError, form_e.to, "m s^-1")
     a = FloatWithUnit(1.0, "Ha^3")
     b = a.to("J^3")
     self.assertAlmostEqual(b, 8.28672661615e-53)
     self.assertEqual(str(b.unit), "J^3")
     a = FloatWithUnit(1.0, "Ha bohr^-2")
     b = a.to("J m^-2")
     self.assertAlmostEqual(b, 1556.893078472351)
     self.assertEqual(str(b.unit), "J m^-2")
Beispiel #8
0
def time2slurm(timeval, unit="s"):
    """
    Convert a number representing a time value in the given unit (Default: seconds)
    to a string following the slurm convention: "days-hours:minutes:seconds".

    >>> assert time2slurm(61) == '0-0:1:1' and time2slurm(60*60+1) == '0-1:0:1'
    >>> assert time2slurm(0.5, unit="h") == '0-0:30:0'
    """
    d, h, m, s = 24 * 3600, 3600, 60, 1

    timeval = Time(timeval, unit).to("s")
    days, hours = divmod(timeval, d)
    hours, minutes = divmod(hours, h)
    minutes, secs = divmod(minutes, m)

    return "%d-%d:%d:%d" % (days, hours, minutes, secs)
Beispiel #9
0
    def __init__(self, entry1, entry2, working_ion_entry):
        #initialize some internal variables
        working_element = working_ion_entry.composition.elements[0]

        entry_charge = entry1
        entry_discharge = entry2
        if entry_charge.composition.get_atomic_fraction(working_element) \
                > entry2.composition.get_atomic_fraction(working_element):
            (entry_charge, entry_discharge) = (entry_discharge, entry_charge)

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        ion_sym = working_element.symbol

        frame_charge_comp = Composition({
            el: comp_charge[el]
            for el in comp_charge if el.symbol != ion_sym
        })
        frame_discharge_comp = Composition({
            el: comp_discharge[el]
            for el in comp_discharge if el.symbol != ion_sym
        })

        #Data validation

        #check that the ion is just a single element
        if not working_ion_entry.composition.is_element:
            raise ValueError("VoltagePair: The working ion specified must be "
                             "an element")

        #check that at least one of the entries contains the working element
        if not comp_charge.get_atomic_fraction(working_element) > 0 and \
                not comp_discharge.get_atomic_fraction(working_element) > 0:
            raise ValueError("VoltagePair: The working ion must be present in "
                             "one of the entries")

        #check that the entries do not contain the same amount of the workin
        #element
        if comp_charge.get_atomic_fraction(working_element) == \
                comp_discharge.get_atomic_fraction(working_element):
            raise ValueError("VoltagePair: The working ion atomic percentage "
                             "cannot be the same in both the entries")

        #check that the frameworks of the entries are equivalent
        if not frame_charge_comp.reduced_formula == \
                frame_discharge_comp.reduced_formula:
            raise ValueError("VoltagePair: the specified entries must have the"
                             " same compositional framework")

        #Initialize normalization factors, charged and discharged entries

        valence_list = Element(ion_sym).oxidation_states
        working_ion_valence = max(valence_list)

        (self.framework,
         norm_charge) = frame_charge_comp.get_reduced_composition_and_factor()
        norm_discharge = \
            frame_discharge_comp.get_reduced_composition_and_factor()[1]

        self._working_ion_entry = working_ion_entry

        #Initialize normalized properties
        self._vol_charge = entry_charge.structure.volume / norm_charge
        self._vol_discharge = entry_discharge.structure.volume / norm_discharge

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        self._mass_charge = comp_charge.weight / norm_charge
        self._mass_discharge = comp_discharge.weight / norm_discharge

        self._num_ions_transferred = \
            (comp_discharge[working_element] / norm_discharge) \
            - (comp_charge[working_element] / norm_charge)

        self._voltage = \
            (((entry_charge.energy / norm_charge) -
             (entry_discharge.energy / norm_discharge)) / \
            self._num_ions_transferred + working_ion_entry.energy_per_atom) / working_ion_valence
        self._mAh = self._num_ions_transferred * Charge(1, "e").to("C") * \
            Time(1, "s").to("h") * AVOGADROS_CONST * 1000 * working_ion_valence

        #Step 4: add (optional) hull and muO2 data
        self.decomp_e_charge = \
            entry_charge.data.get("decomposition_energy", None)
        self.decomp_e_discharge = \
            entry_discharge.data.get("decomposition_energy", None)

        self.muO2_charge = entry_charge.data.get("muO2", None)
        self.muO2_discharge = entry_discharge.data.get("muO2", None)

        self.entry_charge = entry_charge
        self.entry_discharge = entry_discharge
        self.normalization_charge = norm_charge
        self.normalization_discharge = norm_discharge
        self._frac_charge = comp_charge.get_atomic_fraction(working_element)
        self._frac_discharge = \
            comp_discharge.get_atomic_fraction(working_element)
Beispiel #10
0
    def from_steps(cls,
                   step1,
                   step2,
                   normalization_els,
                   framework_formula=None):
        """
        Creates a ConversionVoltagePair from two steps in the element profile
        from a PD analysis.

        Args:
            step1: Starting step
            step2: Ending step
            normalization_els: Elements to normalize the reaction by. To
                ensure correct capacities.
        """
        working_ion_entry = step1["element_reference"]
        working_ion = working_ion_entry.composition.elements[0].symbol
        working_ion_valence = max(Element(working_ion).oxidation_states)
        voltage = (-step1["chempot"] +
                   working_ion_entry.energy_per_atom) / working_ion_valence
        mAh = ((step2["evolution"] - step1["evolution"]) *
               Charge(1, "e").to("C") * Time(1, "s").to("h") * N_A * 1000 *
               working_ion_valence)
        licomp = Composition(working_ion)
        prev_rxn = step1["reaction"]
        reactants = {
            comp: abs(prev_rxn.get_coeff(comp))
            for comp in prev_rxn.products if comp != licomp
        }

        curr_rxn = step2["reaction"]
        products = {
            comp: abs(curr_rxn.get_coeff(comp))
            for comp in curr_rxn.products if comp != licomp
        }

        reactants[licomp] = step2["evolution"] - step1["evolution"]

        rxn = BalancedReaction(reactants, products)

        for el, amt in normalization_els.items():
            if rxn.get_el_amount(el) > 1e-6:
                rxn.normalize_to_element(el, amt)
                break

        prev_mass_dischg = (sum([
            prev_rxn.all_comp[i].weight * abs(prev_rxn.coeffs[i])
            for i in range(len(prev_rxn.all_comp))
        ]) / 2)
        vol_charge = sum([
            abs(prev_rxn.get_coeff(e.composition)) * e.structure.volume
            for e in step1["entries"]
            if e.composition.reduced_formula != working_ion
        ])
        mass_discharge = (sum([
            curr_rxn.all_comp[i].weight * abs(curr_rxn.coeffs[i])
            for i in range(len(curr_rxn.all_comp))
        ]) / 2)
        mass_charge = prev_mass_dischg
        mass_discharge = mass_discharge
        vol_discharge = sum([
            abs(curr_rxn.get_coeff(e.composition)) * e.structure.volume
            for e in step2["entries"]
            if e.composition.reduced_formula != working_ion
        ])

        totalcomp = Composition({})
        for comp in prev_rxn.products:
            if comp.reduced_formula != working_ion:
                totalcomp += comp * abs(prev_rxn.get_coeff(comp))
        frac_charge = totalcomp.get_atomic_fraction(Element(working_ion))

        totalcomp = Composition({})
        for comp in curr_rxn.products:
            if comp.reduced_formula != working_ion:
                totalcomp += comp * abs(curr_rxn.get_coeff(comp))
        frac_discharge = totalcomp.get_atomic_fraction(Element(working_ion))

        rxn = rxn
        entries_charge = step2["entries"]
        entries_discharge = step1["entries"]

        return cls(
            rxn=rxn,
            voltage=voltage,
            mAh=mAh,
            vol_charge=vol_charge,
            vol_discharge=vol_discharge,
            mass_charge=mass_charge,
            mass_discharge=mass_discharge,
            frac_charge=frac_charge,
            frac_discharge=frac_discharge,
            entries_charge=entries_charge,
            entries_discharge=entries_discharge,
            working_ion_entry=working_ion_entry,
            _framework_formula=framework_formula,
        )
Beispiel #11
0
    def from_entries(cls, entry1, entry2, working_ion_entry):
        """
        Args:
            entry1: Entry corresponding to one of the entries in the voltage step.
            entry2: Entry corresponding to the other entry in the voltage step.
            working_ion_entry: A single ComputedEntry or PDEntry representing
                the element that carries charge across the battery, e.g. Li.
        """
        # initialize some internal variables
        working_element = working_ion_entry.composition.elements[0]

        entry_charge = entry1
        entry_discharge = entry2
        if entry_charge.composition.get_atomic_fraction(
                working_element) > entry2.composition.get_atomic_fraction(
                    working_element):
            (entry_charge, entry_discharge) = (entry_discharge, entry_charge)

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        ion_sym = working_element.symbol

        frame_charge_comp = Composition({
            el: comp_charge[el]
            for el in comp_charge if el.symbol != ion_sym
        })
        frame_discharge_comp = Composition({
            el: comp_discharge[el]
            for el in comp_discharge if el.symbol != ion_sym
        })

        # Data validation

        # check that the ion is just a single element
        if not working_ion_entry.composition.is_element:
            raise ValueError(
                "VoltagePair: The working ion specified must be an element")

        # check that at least one of the entries contains the working element
        if (not comp_charge.get_atomic_fraction(working_element) > 0 and
                not comp_discharge.get_atomic_fraction(working_element) > 0):
            raise ValueError(
                "VoltagePair: The working ion must be present in one of the entries"
            )

        # check that the entries do not contain the same amount of the workin
        # element
        if comp_charge.get_atomic_fraction(
                working_element) == comp_discharge.get_atomic_fraction(
                    working_element):
            raise ValueError(
                "VoltagePair: The working ion atomic percentage cannot be the same in both the entries"
            )

        # check that the frameworks of the entries are equivalent
        if not frame_charge_comp.reduced_formula == frame_discharge_comp.reduced_formula:
            raise ValueError(
                "VoltagePair: the specified entries must have the same compositional framework"
            )

        # Initialize normalization factors, charged and discharged entries

        valence_list = Element(ion_sym).oxidation_states
        working_ion_valence = abs(max(valence_list))

        (
            framework,
            norm_charge,
        ) = frame_charge_comp.get_reduced_composition_and_factor()
        norm_discharge = frame_discharge_comp.get_reduced_composition_and_factor(
        )[1]

        # Initialize normalized properties
        if hasattr(entry_charge, "structure"):
            _vol_charge = entry_charge.structure.volume / norm_charge
        else:
            _vol_charge = entry_charge.data.get("volume")

        if hasattr(entry_discharge, "structure"):
            _vol_discharge = entry_discharge.structure.volume / norm_discharge
        else:
            _vol_discharge = entry_discharge.data.get("volume")

        comp_charge = entry_charge.composition
        comp_discharge = entry_discharge.composition

        _mass_charge = comp_charge.weight / norm_charge
        _mass_discharge = comp_discharge.weight / norm_discharge

        _num_ions_transferred = (
            comp_discharge[working_element] /
            norm_discharge) - (comp_charge[working_element] / norm_charge)

        _voltage = (
            ((entry_charge.energy / norm_charge) -
             (entry_discharge.energy / norm_discharge)) / _num_ions_transferred
            + working_ion_entry.energy_per_atom) / working_ion_valence
        _mAh = _num_ions_transferred * Charge(1, "e").to("C") * Time(
            1, "s").to("h") * N_A * 1000 * working_ion_valence

        _frac_charge = comp_charge.get_atomic_fraction(working_element)
        _frac_discharge = comp_discharge.get_atomic_fraction(working_element)

        vpair = InsertionVoltagePair(  # pylint: disable=E1123
            voltage=_voltage,
            mAh=_mAh,
            mass_charge=_mass_charge,
            mass_discharge=_mass_discharge,
            vol_charge=_vol_charge,
            vol_discharge=_vol_discharge,
            frac_charge=_frac_charge,
            frac_discharge=_frac_discharge,
            working_ion_entry=working_ion_entry,
            entry_charge=entry_charge,
            entry_discharge=entry_discharge,
            framework_formula=framework.reduced_formula,
        )

        # Step 4: add (optional) hull and muO2 data
        vpair.decomp_e_charge = entry_charge.data.get("decomposition_energy",
                                                      None)
        vpair.decomp_e_discharge = entry_discharge.data.get(
            "decomposition_energy", None)

        vpair.muO2_charge = entry_charge.data.get("muO2", None)
        vpair.muO2_discharge = entry_discharge.data.get("muO2", None)

        return vpair
Beispiel #12
0
 def test_time(self):
     a = Time(20, "h")
     self.assertAlmostEqual(a.to("s"), 3600 * 20)
     #Test left and right multiplication.
     self.assertEqual(str(a * 3), "60.0 h")
     self.assertEqual(str(3 * a), "60.0 h")
Beispiel #13
0
def timelimit_parser(s):
    """Convert a float or a string into time in seconds."""
    try:
        return Time(float(s), "s")
    except ValueError:
        return slurm_parse_timestr(s)