Esempio n. 1
0
    def sanity_check_insufficient_bands(self, calculation):
        """Perform a sanity check on the band occupations of a  successfully converged calculation.

        Verify that the occupation of the last band is below a certain threshold, unless `occupations` was explicitly
        set to `fixed` in the input parameters. If this is violated, the calculation used too few bands and cannot be
        trusted. The number of bands is increased and the calculation is restarted, starting from the last.
        """
        from aiida_quantumespresso.utils.bands import get_highest_occupied_band

        # Only skip the check on the highest band occupation if `occupations` was explicitly set to `fixed`.
        if calculation.inputs.parameters.get_attribute('SYSTEM', {}).get('occupations', None) == 'fixed':
            return

        try:
            bands = calculation.outputs.output_band
            get_highest_occupied_band(bands)
        except ValueError as exception:
            args = [self._process_class.__name__, calculation.pk]
            self.report('{}<{}> run with smearing and highest band is occupied'.format(*args))
            self.report('BandsData<{}> has invalid occupations: {}'.format(bands.pk, exception))
            self.report('{}<{}> had insufficient bands'.format(calculation.process_label, calculation.pk))

            nbnd_cur = calculation.outputs.output_parameters.get_dict()['number_of_bands']
            nbnd_new = nbnd_cur + max(int(nbnd_cur * self.defaults.delta_factor_nbnd), self.defaults.delta_minimum_nbnd)

            self.ctx.inputs.parameters.setdefault('SYSTEM', {})['nbnd'] = nbnd_new

            self.report('Action taken: increased number of bands to {} and restarting from scratch'.format(nbnd_new))
            return ProcessHandlerReport(True)
Esempio n. 2
0
    def sanity_check_insufficient_bands(self, calculation):
        """Perform a sanity check on the band occupations of a  successfully converged calculation.

        Verify that the occupation of the last band is below a certain threshold, unless `occupations` was explicitly
        set to `fixed` in the input parameters. If this is violated, the calculation used too few bands and cannot be
        trusted. The number of bands is increased and the calculation is restarted, starting from the last.
        """
        from aiida_quantumespresso.utils.bands import get_highest_occupied_band

        occupations = calculation.inputs.parameters.get_attribute(
            'SYSTEM', {}).get('occupations', None)

        if occupations is None:
            self.report(
                '`SYSTEM.occupations` parameter is not defined: performing band occupation check. '
                'If you want to disable this, explicitly set `SYSTEM.occupations` to `fixed`.'
            )

        # Only skip the check on the highest band occupation if `occupations` was explicitly set to `fixed`.
        if occupations == 'fixed':
            return

        try:
            bands = calculation.outputs.output_band
        except AttributeError:
            args = [self.ctx.process_name, calculation.pk]
            self.report(
                '{}<{}> does not have `output_band` output, skipping sanity check.'
                .format(*args))
            return

        try:
            get_highest_occupied_band(bands)
        except ValueError as exception:
            args = [self.ctx.process_name, calculation.pk]
            self.report(
                '{}<{}> run with smearing and highest band is occupied'.format(
                    *args))
            self.report(
                f'BandsData<{bands.pk}> has invalid occupations: {exception}')
            self.report(
                f'{calculation.process_label}<{calculation.pk}> had insufficient bands'
            )

            nbnd_cur = calculation.outputs.output_parameters.get_dict(
            )['number_of_bands']
            nbnd_new = nbnd_cur + max(
                int(nbnd_cur * self.defaults.delta_factor_nbnd),
                self.defaults.delta_minimum_nbnd)

            self.ctx.inputs.parameters.setdefault('SYSTEM',
                                                  {})['nbnd'] = nbnd_new

            self.report(
                f'Action taken: increased number of bands to {nbnd_new} and restarting from scratch'
            )
            return ProcessHandlerReport(True)
Esempio n. 3
0
    def _handle_calculation_sanity_checks(self, calculation):
        """The current `calculation` has finished successfully according to the parser, but double-check.

        Verify that the occupation of the last band is below a certain threshold, unless `occupations` was explicitly
        set to `fixed` in the input parameters. If this is violated, the calculation used too few bands and cannot be
        trusted. The number of bands is increased and the calculation is restarted, starting from the last.
        """
        from aiida_quantumespresso.utils.bands import get_highest_occupied_band

        # Only skip the check on the highest band occupation if `occupations` was explicitly set to `fixed`.
        if calculation.inputs.parameters.get_attribute('SYSTEM', {}).get('occupations', None) == 'fixed':
            return

        try:
            bands = calculation.outputs.output_band
            get_highest_occupied_band(bands)
        except ValueError as exception:
            self.report('calculation<{}> run with smearing and highest band is occupied'.format(calculation.pk))
            self.report('BandsData<{}> has invalid occupations: {}'.format(bands.pk, exception))
            return self._handle_insufficient_bands(calculation)
Esempio n. 4
0
    def test_threshold(self, fixture_database):
        """Test the `threshold` parameter."""
        from aiida.orm import BandsData

        threshold = 0.002

        bands = BandsData()
        bands.set_array('occupations', numpy.array([[2., 2., 2., 2., 0.001, 0.0015]]))
        bands.store()

        # All bands above the LUMO (occupation of 0.001) are below `2 * threshold`
        h**o = get_highest_occupied_band(bands, threshold=threshold)
        assert h**o == 4

        bands = BandsData()
        bands.set_array('occupations', numpy.array([[2., 2., 2., 2., 0.001, 0.003]]))
        bands.store()

        # A band above the LUMO (occupation of 0.001) has an occupation above `2 * threshold`
        with pytest.raises(ValueError):
            get_highest_occupied_band(bands, threshold=threshold)
    def test_spin_unpolarized():
        """Test the function for a non spin-polarized calculation meaning there will be a single spin channel."""
        from aiida.orm import BandsData

        occupations = numpy.array([
            [2., 2., 2., 2., 0.],
            [2., 2., 2., 2., 0.],
            [2., 2., 2., 2., 0.],
            [2., 2., 2., 2., 0.],
        ])

        bands = BandsData()
        bands.set_array('occupations', occupations)
        bands.store()
        h**o = get_highest_occupied_band(bands)
        assert h**o == 4
Esempio n. 6
0
    def test_spin_polarized(self, fixture_database):
        """Test the function for a spin-polarized calculation meaning there will be two spin channels."""
        from aiida.orm import BandsData

        occupations = numpy.array([
            [
                [2., 2., 2., 2., 0.],
                [2., 2., 2., 2., 0.],
            ],
            [
                [2., 2., 2., 2., 0.],
                [2., 2., 2., 2., 0.],
            ]
        ])

        bands = BandsData()
        bands.set_array('occupations', occupations)
        bands.store()
        h**o = get_highest_occupied_band(bands)
        assert h**o == 4
    def test_valid_node():
        """Test that the correct exceptions are thrown for incompatible nodes."""
        from aiida.orm import ArrayData, BandsData

        # Invalid node type
        node = ArrayData().store()
        with pytest.raises(ValueError):
            get_highest_occupied_band(node)

        # The `occupations` array is missing
        node = BandsData()
        node.set_array('not_occupations', numpy.array([]))
        node.store()
        with pytest.raises(ValueError):
            get_highest_occupied_band(node)

        # The `occupations` array has incorrect shape
        node = BandsData()
        node.set_array('occupations', numpy.array([1., 1.]))
        node.store()
        with pytest.raises(ValueError):
            get_highest_occupied_band(node)