def get_spectrum(force_constants: ForceConstants, *, bin_width: Quantity, max_energy: Quantity, q: Quantity = (0.1 * ureg('1/angstrom')), npts: int = 1000, npts_density: bool = False, sampling: str = 'golden', jitter: bool = True, dos: bool = False, smear_width: Optional[Quantity] = (1 * ureg('meV'))): assert isinstance(q, Quantity) if npts_density: npts = int(np.ceil(npts * (q.to('1/angstrom').magnitude**2))) energy_bins = np.arange(0, (max_energy.to('meV').magnitude), bin_width.to('meV').magnitude) * max_energy.units if dos: sampling_function = sample_sphere_dos else: sampling_function = sample_sphere_structure_factor spectrum = sampling_function(force_constants, mod_q=q, npts=npts, sampling=sampling, jitter=jitter, energy_bins=energy_bins) if smear_width is None: return spectrum else: return spectrum.broaden(smear_width, shape='gauss')
class TestQpointPhononModesCalculateDos: @pytest.mark.parametrize( 'material, qpt_ph_modes_file, expected_dos_json, ebins', [('quartz', 'quartz-666-grid.phonon', 'quartz_666_dos.json', np.arange(0, 155, 0.5) * ureg('meV')), ('CaHgO2', 'CaHgO2-666-grid.yaml', 'CaHgO2_666_dos.json', np.arange(0, 95, 0.4) * ureg('meV'))]) def test_calculate_dos(self, material, qpt_ph_modes_file, expected_dos_json, ebins): if qpt_ph_modes_file.endswith('.phonon'): qpt_ph_modes = QpointPhononModes.from_castep( get_castep_path(material, qpt_ph_modes_file)) else: qpt_ph_modes = QpointPhononModes.from_phonopy( phonon_name=get_phonopy_path(material, qpt_ph_modes_file)) dos = qpt_ph_modes.calculate_dos(ebins) expected_dos = get_expected_spectrum1d(expected_dos_json) check_spectrum1d(dos, expected_dos) def test_calculate_dos_with_0_inv_cm_bin_doesnt_raise_runtime_warn(self): qpt_ph_modes = get_qpt_ph_modes('quartz') ebins = np.arange(0, 1300, 4) * ureg('1/cm') with pytest.warns(None) as warn_record: dos = qpt_ph_modes.calculate_dos(ebins) assert len(warn_record) == 0
def setUp(self): # Test creation of BandsData object (which reads NaH.bands file in # test/data dir). BandsData object will also read extra data (ion_r # and ion_type) from the NaH.castep file # Create trivial object so attributes can be assigned to it expctd_data = type('', (), {})() # Expected data expctd_data.cell_vec = np.array( [[0.000000, 4.534397, 4.534397], [4.534397, 0.000000, 4.534397], [4.534397, 4.534397, 0.000000]]) * ureg('bohr') expctd_data.ion_r = np.array([[0.500000, 0.500000, 0.500000], [0.000000, 0.000000, 0.000000]]) expctd_data.ion_type = np.array(['H', 'Na']) expctd_data.qpts = np.array([[-0.45833333, -0.37500000, -0.45833333], [-0.45833333, -0.37500000, -0.20833333]]) expctd_data.weights = np.array([0.00347222, 0.00694444]) expctd_data.freqs = np.array([[ -1.83230180, -0.83321119, -0.83021854, -0.83016941, -0.04792334 ], [-1.83229571, -0.83248269, -0.83078961, -0.83036048, -0.05738470] ]) * ureg('hartree') expctd_data.freq_down = np.array([]) * ureg('hartree') expctd_data.fermi = np.array([-0.009615]) * ureg('hartree') self.expctd_data = expctd_data seedname = 'NaH' path = 'data' data = BandsData.from_castep(seedname, path=path) self.data = data
def check_unit_conversion(obj, attr, unit): """ Utility function to check unit conversions in Euphonic objects Parameters ---------- obj : Object The object to check attr : str The name of the attribute to change the units of unit : str The unit to change attr to """ unit_attr = attr + '_unit' original_attr_value = np.copy(getattr(obj, attr).magnitude) * getattr( obj, attr).units setattr(obj, unit_attr, unit) # Check unit value (e.g. 'frequencies_unit') has changed assert getattr(obj, unit_attr) == unit # Check when returning the attrbute (e.g. 'frequencies') it is in # the desired unit and has the correct magnitude if attr == 'temperature': assert str(getattr(obj, attr).units) == str(ureg(unit).units) else: assert getattr(obj, attr).units == ureg(unit) npt.assert_allclose( getattr(obj, attr).magnitude, original_attr_value.to(unit).magnitude)
def test_get_default_bins(mocker, units): qpm = mocker.MagicMock() qpm.frequencies = np.array([-1., 0.3, (10 / 1.05), 4.2]) * ureg(units) default_bins = _get_default_bins(qpm, nbins=10) assert default_bins.units == ureg(units) npt.assert_almost_equal(default_bins.magnitude, [0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
class TestSpectrum1DCollectionMethods: @pytest.mark.parametrize( 'spectrum, split_kwargs, expected_spectra', [(get_spectrum1dcollection('methane_pdos.json'), { 'indices': [50] }, [ get_spectrum1dcollection(f'methane_pdos_split50_{i}.json') for i in range(2) ])]) def test_split(self, spectrum, split_kwargs, expected_spectra): spectra = spectrum.split(**split_kwargs) for split, expected_split in zip(spectra, expected_spectra): check_spectrum1dcollection(split, expected_split) @pytest.mark.parametrize( 'spectrum_file, expected_bin_edges_file, expected_units', [('gan_bands.json', 'gan_bands_bin_edges.npy', ureg('1/angstrom')), ('quartz_dos_collection.json', 'quartz_dos_collection_bin_edges.npy', ureg('meV'))]) def test_get_bin_edges(self, spectrum_file, expected_bin_edges_file, expected_units): spec = get_spectrum1dcollection(spectrum_file) bin_edges = spec.get_bin_edges() expected_bin_edges = np.load( os.path.join(get_spectrum_dir(), expected_bin_edges_file)) assert bin_edges.units == expected_units assert_allclose(bin_edges.magnitude, expected_bin_edges) @pytest.mark.parametrize( 'spectrum_file, expected_bin_centres_file, expected_units', [('gan_bands.json', 'gan_bands_bin_centres.npy', ureg('1/angstrom')), ('quartz_dos_collection.json', 'quartz_dos_collection_bin_centres.npy', ureg('meV'))]) def test_get_bin_centres(self, spectrum_file, expected_bin_centres_file, expected_units): spec = get_spectrum1dcollection(spectrum_file) bin_centres = spec.get_bin_centres() expected_bin_centres = np.load( os.path.join(get_spectrum_dir(), expected_bin_centres_file)) assert bin_centres.units == expected_units assert_allclose(bin_centres.magnitude, expected_bin_centres) def test_get_bin_edges_with_invalid_data_shape_raises_value_error(self): spec = get_spectrum1dcollection('gan_bands.json') spec._y_data = spec._y_data[:, :51] with pytest.raises(ValueError): spec.get_bin_edges() def test_get_bin_centres_with_invalid_data_shape_raises_value_error(self): spec = get_spectrum1dcollection('quartz_dos_collection.json') spec._x_data = spec._x_data[:31] with pytest.raises(ValueError): spec.get_bin_centres()
def test_calculate_qpoint_frequencies_with_mode_widths( self, fc, material, all_args, expected_qpoint_frequencies_file, expected_modw_file, n_threads): func_kwargs = all_args[1] if n_threads == 0: func_kwargs['use_c'] = False else: func_kwargs['use_c'] = True func_kwargs['n_threads'] = n_threads qpt_freqs, modw = fc.calculate_qpoint_frequencies( all_args[0], **func_kwargs) with open(os.path.join(get_fc_dir(), expected_modw_file), 'r') as fp: modw_dict = json.load(fp) expected_modw = modw_dict['mode_widths'] * ureg( modw_dict['mode_widths_unit']) expected_qpt_freqs = get_expected_qpt_freqs( material, expected_qpoint_frequencies_file) # Only give gamma-acoustic modes special treatment if the acoustic # sum rule has been applied if not 'asr' in func_kwargs.keys(): gamma_atol = None else: gamma_atol = 0.5 check_qpt_freqs(qpt_freqs, expected_qpt_freqs, frequencies_atol=1e-4, frequencies_rtol=2e-5, acoustic_gamma_atol=gamma_atol) assert modw.units == expected_modw.units npt.assert_allclose(modw.magnitude, expected_modw.magnitude, atol=2e-4, rtol=5e-5)
def get_mp_grid_spec( self, spacing: Quantity = 0.1 * ureg('1/angstrom') ) -> Tuple[int, int, int]: """ Get suggested divisions for Monkhorst-Pack grid Determine a mesh for even Monkhorst-Pack sampling of the reciprocal cell Parameters ---------- spacing Scalar float quantity in 1/length units. Maximum reciprocal-space distance between q-point samples Returns ------- grid_spec The number of divisions for each reciprocal lattice vector """ recip_length_unit = spacing.units lattice = self.reciprocal_cell().to(recip_length_unit) grid_spec = np.linalg.norm(lattice.magnitude, axis=1) / spacing.magnitude # math.ceil is better than np.ceil because it returns ints return tuple([ceil(x) for x in grid_spec])
def test_calculate_dos_with_0_inv_cm_bin_doesnt_raise_runtime_warn(self): qpt_freqs = get_qpt_freqs('quartz', 'quartz_666_qpoint_frequencies.json') ebins = np.arange(0, 1300, 4) * ureg('1/cm') with pytest.warns(None) as warn_record: dos = qpt_freqs.calculate_dos(ebins) assert len(warn_record) == 0
def temperature(self): if self._temperature is not None: # See https://pint.readthedocs.io/en/latest/nonmult.html return Quantity(self._temperature, ureg('K')).to(self.temperature_unit) else: return None
def diff_1d(spectrum: Spectrum1D, reference: Spectrum1D, fractional: bool = False, threshold: float = 1e-12) -> Quantity: """Compare the values of two Spectrum1D objects, returning an array Args: spectrum, reference: Spectra with same x-values for comparison fractional: Calculate relative difference by formula (S - R)/R. If False, use the unscaled difference between values instead. threshold: Ignore error from values smaller than this threshold in reference spectrum when fractional=True Returns: array Quantity: Difference between spectra """ assert spectrum.x_data_unit == reference.x_data_unit assert allclose(spectrum.x_data.magnitude, reference.x_data.magnitude) diff = (spectrum.y_data.to(reference.y_data_unit).magnitude - reference.y_data.magnitude) if fractional: ref_values = reference.y_data.magnitude mask = absolute(ref_values) > threshold diff = diff[mask] / ref_values[mask] * ureg(None) else: diff *= spectrum.y_data.units return diff
def _check_unit_conversion(obj: object, attr_name: str, attr_value: Any, unit_attrs: List[str]) -> None: """ If setting an attribute on an object that relates to the units of a Quantity (e.g. 'frequencies_unit' in QpointPhononModes) check that the unit conversion is valid before allowing the value to be set Parameters ---------- obj The object to check attr_name The name of the attribute that is being set attr_value The new value of the attribute unit_attrs Only check the unit conversion if the attribute is one of unit_attrs Raises ------ ValueError If the unit conversion is not valid """ if hasattr(obj, attr_name): if attr_name in unit_attrs: try: _ = ureg(getattr(obj, attr_name)).to(attr_value) except DimensionalityError: raise ValueError( (f'"{attr_value}" is not a known dimensionally-consistent ' f'unit for "{attr_name}"'))
def setUp(self): data = type('', (), {})() # Input values data._e_units = 'E_h' data.dos = np.array([ 2.30e-01, 1.82e-01, 8.35e-02, 3.95e-02, 2.68e-02, 3.89e-02, 6.15e-02, 6.75e-02, 6.55e-02, 5.12e-02, 3.60e-02, 2.80e-02, 5.22e-02, 1.12e-01, 1.52e-01, 1.37e-01, 9.30e-02, 6.32e-02, 7.92e-02, 1.32e-01, 1.53e-01, 8.88e-02, 2.26e-02, 2.43e-03, 1.08e-04, 2.00e-06, 8.11e-07, 4.32e-05, 9.63e-04, 8.85e-03, 3.35e-02, 5.22e-02, 3.35e-02, 8.85e-03, 9.63e-04, 4.32e-05, 7.96e-07, 6.81e-09, 9.96e-08, 5.40e-06, 1.21e-04, 1.13e-03, 4.71e-03, 1.19e-02, 2.98e-02, 6.07e-02, 6.91e-02, 3.79e-02, 9.33e-03, 9.85e-04, 4.40e-05, 2.24e-05, 4.82e-04, 4.43e-03, 1.67e-02, 2.61e-02, 1.67e-02, 4.43e-03, 4.82e-04, 2.16e-05, 3.98e-07 ]) data.dos_down = np.array([ 6.05e-09, 7.97e-07, 4.33e-05, 9.71e-04, 9.08e-03, 3.72e-02, 8.06e-02, 1.37e-01, 1.84e-01, 1.47e-01, 7.37e-02, 3.84e-02, 2.67e-02, 3.80e-02, 5.36e-02, 4.24e-02, 4.28e-02, 5.76e-02, 5.03e-02, 3.55e-02, 2.32e-02, 3.15e-02, 7.39e-02, 1.24e-01, 1.40e-01, 1.11e-01, 7.48e-02, 5.04e-02, 5.22e-02, 8.75e-02, 1.37e-01, 1.30e-01, 6.37e-02, 1.47e-02, 1.51e-03, 1.09e-04, 9.64e-04, 8.85e-03, 3.35e-02, 5.22e-02, 3.35e-02, 8.85e-03, 9.63e-04, 4.33e-05, 6.19e-06, 1.21e-04, 1.13e-03, 4.71e-03, 1.19e-02, 2.98e-02, 6.07e-02, 6.91e-02, 3.79e-02, 9.33e-03, 9.85e-04, 4.40e-05, 2.24e-05, 4.82e-04, 4.43e-03, 1.67e-02, 2.61e-02 ]) data.dos_bins = np.array([ 0.58, 0.78, 0.98, 1.18, 1.38, 1.58, 1.78, 1.98, 2.18, 2.38, 2.58, 2.78, 2.98, 3.18, 3.38, 3.58, 3.78, 3.98, 4.18, 4.38, 4.58, 4.78, 4.98, 5.18, 5.38, 5.58, 5.78, 5.98, 6.18, 6.38, 6.58, 6.78, 6.98, 7.18, 7.38, 7.58, 7.78, 7.98, 8.18, 8.38, 8.58, 8.78, 8.98, 9.18, 9.38, 9.58, 9.78, 9.98, 10.18, 10.38, 10.58, 10.78, 10.98, 11.18, 11.38, 11.58, 11.78, 11.98, 12.18, 12.38, 12.58, 12.78 ]) * ureg('E_h') data.fermi = np.array([4.71, 4.71]) * ureg('E_h') self.data = data self.title = 'Iron' self.mirror = False # Results self.fig = plot_dos(self.data, self.title, self.mirror) self.ax = self.fig.axes[0]
def test_qpts_cart_to_frac_trivial(trivial_crystal, trivial_qpts): """Check internal method for q-point conversion with trivial example""" cart_qpts = np.asarray(trivial_qpts['cart'], dtype=float ) * ureg('1 / angstrom') frac_qpts = np.asarray(trivial_qpts['frac'], dtype=float) calc_frac_qpts = _qpts_cart_to_frac(cart_qpts, trivial_crystal) npt.assert_almost_equal(calc_frac_qpts, frac_qpts)
def from_castep_phonon_dos(cls: SC, filename: str) -> SC: """ Reads total DOS and per-element PDOS from a CASTEP .phonon_dos file Parameters ---------- filename The path and name of the .phonon_dos file to read """ data = read_phonon_dos_data(filename) items = data['dos'].items() metadata = {'line_data': [{'label': item[0]} for item in items]} y_data = np.stack([item[1] for item in items]) return Spectrum1DCollection(data['dos_bins'] * ureg(data['dos_bins_unit']), y_data * ureg('dimensionless'), metadata=metadata)
def test_qpts_cart_to_frac_roundtrip(random_qpts_array, nontrivial_crystal): frac_qpts = random_qpts_array cart_qpts = frac_qpts.dot(nontrivial_crystal.reciprocal_cell() .to('1 / angstrom').magnitude ) * ureg('1 / angstrom') calc_frac_qpts = _qpts_cart_to_frac(cart_qpts, nontrivial_crystal) npt.assert_almost_equal(calc_frac_qpts, frac_qpts)
def mock_crystal(self, mocker): crystal = mocker.MagicMock() crystal.configure_mock( **{'reciprocal_cell.return_value': np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) * ureg('1 / angstrom'), 'get_mp_grid_spec.return_value': (2, 2, 2)}) return crystal
def test_sample_sphere_structure_factor(self, mocker, mock_crystal, mock_fc, mock_qpm, mock_s, mock_dw, random_qpts_array, options): # Make sure the same instance of mock DebyeWaller is used everywhere if options['dw'] == 'mock_dw': options['dw'] = mock_dw # Fixed return values for dummy functions return_bins = self._energy_bins return_scattering_lengths = self._scattering_lengths # Dummy out functions called by sample_sphere_structure_factor # that are tested elsewhere self.mock_get_default_bins(mocker, return_bins) get_qpts_sphere = self.mock_get_qpts_sphere(mocker, random_qpts_array) get_ref_data = self.mock_get_reference_data(mocker, return_scattering_lengths) assert (sample_sphere_structure_factor( mock_fc, **options) == 'calculate_1d_average_return_value') # Check scattering lengths were looked up as expected if isinstance(options['scattering_lengths'], str): assert get_ref_data.call_args == ( (), {'physical_property': 'coherent_scattering_length', 'collection': 'Sears1992'}) else: assert isinstance(options['scattering_lengths'], dict) # Check qpts sphere called as expected assert get_qpts_sphere.call_args == ((options['npts'],), {'sampling': options['sampling'], 'jitter': options['jitter']}) # Check expected list of qpoints was passed to forceconstants # (fractional q = cart q because the lattice vectors are unit cube) npt.assert_almost_equal( random_qpts_array * options['mod_q'].magnitude, mock_fc.calculate_qpoint_phonon_modes.call_args[0][0]) # Check structure factor args were as expected assert (mock_qpm.calculate_structure_factor.call_args == (tuple(), {'scattering_lengths': self._scattering_lengths, 'dw': mock_dw})) # Check auto grid was used if temperature given if options.get('temperature') is not None: assert (mock_qpm.calculate_debye_waller.call_args[0][0] == options['temperature']) assert (mock_crystal.get_mp_grid_spec.call_args[0][0] == 0.025 * ureg('1/angstrom')) # Check expected bins set for 1d averaging assert mock_s.calculate_1d_average.call_args == ((return_bins,),)
def _grid_spec_from_args(crystal: Crystal, grid: Optional[Sequence[int]] = None, grid_spacing: Quantity = 0.1 * ureg('1/angstrom') ) -> Sequence[int]: """Get Monkorst-Pack mesh divisions from user arguments""" if grid: grid_spec = grid else: grid_spec = crystal.get_mp_grid_spec(spacing=grid_spacing) return grid_spec
def _bose_corrected_structure_factor( self, e_bins: Quantity, calc_bose: bool = True, temperature: Optional[Quantity] = None) -> Quantity: """Bin structure factor in energy, return (Bose-populated) array Parameters ---------- e_bins Shape (n_e_bins + 1,) float Quantity. The energy bin edges calc_bose Whether to calculate and apply the Bose population factor temperature Temperature used to calculate the Bose factor. This is only required if StructureFactor.temperature = None, otherwise the temperature stored in StructureFactor will be used. Returns ------- intensities Scattering intensities as array over (qpt, energy) """ # Convert units freqs = self._frequencies e_bins_internal = e_bins.to('hartree').magnitude # Create initial sqw_map with an extra an energy bin either # side, for any branches that fall outside the energy bin range sqw_map = np.zeros((self.n_qpts, len(e_bins) + 1)) sf = self._structure_factors p_intensity = sf n_intensity = sf if calc_bose: try: bose = self._bose_factor(temperature) p_intensity = (1 + bose) * p_intensity n_intensity = bose * n_intensity except NoTemperatureError: pass p_bin = np.digitize(freqs, e_bins_internal) n_bin = np.digitize(-freqs, e_bins_internal) # Sum intensities into bins first_index = np.transpose( np.tile(range(self.n_qpts), (3 * self.crystal.n_atoms, 1))) np.add.at(sqw_map, (first_index, p_bin), p_intensity) np.add.at(sqw_map, (first_index, n_bin), n_intensity) # Exclude values outside ebin range sqw_map = sqw_map[:, 1:-1] * ureg('bohr**2').to( self.structure_factors_unit) return sqw_map
def calculate_dos(self, dos_bins: Quantity, mode_widths: Optional[np.ndarray] = None, mode_widths_min: Quantity = Quantity(0.01, 'meV') ) -> Spectrum1D: """ Calculates a density of states Parameters ---------- dos_bins Shape (n_e_bins + 1,) float Quantity. The energy bin edges to use for calculating the DOS mode_widths Shape (n_qpts, n_branches) float Quantity in energy units. The broadening width for each mode at each q-point, for adaptive broadening mode_widths_min Scalar float Quantity in energy units. Sets a lower limit on the mode widths, as mode widths of zero will result in infinitely sharp peaks Returns ------- dos A spectrum containing the energy bins on the x-axis and dos on the y-axis """ freqs = self._frequencies n_modes = self.frequencies.shape[1] # dos_bins commonly contains a 0 bin, and converting from 0 1/cm # to 0 hartree causes a RuntimeWarning, so suppress it with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=RuntimeWarning) dos_bins_calc = dos_bins.to('hartree').magnitude if mode_widths is not None: from scipy.stats import norm dos_bins_calc = Spectrum1D._bin_edges_to_centres(dos_bins_calc) dos = np.zeros(len(dos_bins_calc)) mode_widths = mode_widths.to('hartree').magnitude mode_widths = np.maximum(mode_widths, mode_widths_min.to('hartree').magnitude) for q in range(self.n_qpts): for m in range(n_modes): pdf = norm.pdf(dos_bins_calc, loc=freqs[q,m], scale=mode_widths[q,m]) dos += pdf*self.weights[q]/n_modes else: weights = np.repeat(self.weights[:, np.newaxis], n_modes, axis=1) dos, _ = np.histogram(freqs, dos_bins_calc, weights=weights, density=True) return Spectrum1D( dos_bins, dos*ureg('dimensionless'))
def from_castep_phonon_dos(cls: S1D, filename: str, element: Optional[str] = None) -> S1D: """ Reads DOS from a CASTEP .phonon_dos file Parameters ---------- filename The path and name of the .phonon_dos file to read element Which element's PDOS to read. If not supplied reads the total DOS. """ data = read_phonon_dos_data(filename) if element is None: element = 'Total' return Spectrum1D(data['dos_bins'] * ureg(data['dos_bins_unit']), data['dos'][element] * ureg('dimensionless'), metadata={'label': element})
def test_calculate_dos_with_mode_widths(self, material, qpt_freqs_json, mode_widths_json, expected_dos_json, ebins): qpt_freqs = get_qpt_freqs(material, qpt_freqs_json) with open(os.path.join(get_fc_dir(), mode_widths_json), 'r') as fp: modw_dict = json.load(fp) mode_widths = modw_dict['mode_widths'] * ureg( modw_dict['mode_widths_unit']) dos = qpt_freqs.calculate_dos(ebins, mode_widths=mode_widths) expected_dos = get_expected_spectrum1d(expected_dos_json) check_spectrum1d(dos, expected_dos)
def cell_volume(self) -> Quantity: """ Calculates the cell volume Returns ------- volume Scalar float quantity in length**3 units. The cell volume """ vol = self._cell_volume() * ureg.bohr**3 return vol.to(ureg(self.cell_vectors_unit)**3)
def broaden(self: S1D, x_width: Quantity, shape: str = 'gauss') -> S1D: """ Broaden y_data and return a new broadened spectrum object Parameters ---------- x_width Scalar float Quantity. The broadening FWHM shape One of {'gauss', 'lorentz'}. The broadening shape Returns ------- broadened_spectrum A new Spectrum1D object with broadened y_data Raises ------ ValueError If shape is not one of the allowed strings """ if shape == 'gauss': xsigma = self._gfwhm_to_sigma(x_width, self.get_bin_centres()) y_broadened = gaussian_filter1d( self.y_data.magnitude, xsigma, mode='constant') * ureg( self.y_data_unit) elif shape == 'lorentz': broadening = _distribution_1d(self.get_bin_centres().magnitude, x_width.to( self.x_data_unit).magnitude, shape=shape) y_broadened = correlate1d(self.y_data.magnitude, broadening, mode='constant') * ureg(self.y_data_unit) else: raise ValueError(f"Distribution shape '{shape}' not recognised") return Spectrum1D( np.copy(self.x_data.magnitude) * ureg(self.x_data_unit), y_broadened, deepcopy( (self.x_tick_labels)), deepcopy(self.metadata))
def _get_q_distance(length_unit_string: str, q_distance: float) -> Quantity: """ Parse user arguments to obtain reciprocal-length spacing Quantity """ try: length_units = ureg(length_unit_string) except UndefinedUnitError: raise ValueError("Length unit not known. Euphonic uses Pint for units." " Try 'angstrom' or 'bohr'. Metric prefixes " "are also allowed, e.g 'nm'.") recip_length_units = 1 / length_units return q_distance * recip_length_units
def calculate_dos_map(modes: euphonic.QpointPhononModes, ebins: euphonic.Quantity) -> euphonic.Spectrum2D: from euphonic.util import _calc_abscissa q_bins = _calc_abscissa(modes.crystal.reciprocal_cell(), modes.qpts) bin_indices = np.digitize(modes.frequencies.magnitude, ebins.magnitude) intensity_map = np.zeros((modes.n_qpts, len(ebins) + 1)) first_index = np.tile(range(modes.n_qpts), (3 * modes.crystal.n_atoms, 1)).transpose() np.add.at(intensity_map, (first_index, bin_indices), 1) return euphonic.Spectrum2D(q_bins, ebins, intensity_map[:, :-1] * ureg('dimensionless'))
class TestStructureFactorCalculate1dAverage: @pytest.mark.parametrize( 'material, sf_json, expected_1d_json, ebins, kwargs', [('quartz', 'quartz_666_300K_structure_factor.json', 'quartz_666_300K_sf_1d_average_with_weights.json', np.arange(0, 156) * ureg('meV'), {}), ('quartz', 'quartz_666_300K_structure_factor_noweights.json', 'quartz_666_300K_sf_1d_average_with_weights.json', np.arange(0, 156) * ureg('meV'), { 'weights': np.load( os.path.join(get_sf_dir('quartz'), 'quartz_666_weights.npy')) }), ('quartz', 'quartz_666_300K_structure_factor_noweights.json', 'quartz_666_300K_sf_1d_average_noweights.json', np.arange(0, 156) * ureg('meV'), {})]) def test_calculate_1d_average(self, material, sf_json, expected_1d_json, ebins, kwargs): sf = get_sf(material, sf_json) sw = sf.calculate_1d_average(ebins, **kwargs) expected_sw = get_expected_spectrum1d(expected_1d_json) check_spectrum1d(sw, expected_sw)
def _qpts_cart_to_frac(qpts: Quantity, crystal: Crystal) -> np.ndarray: """Convert set of q-points from Cartesian to fractional coordinates Parameters ---------- qpts Array of q-points in Cartesian coordinates. crystal Crystal structure determining reciprocal lattice Returns ------- np.ndarray Dimensionless array of q-points in fractional coordinates """ lattice = crystal.reciprocal_cell() return np.linalg.solve(lattice.to(ureg('1/bohr')).magnitude.T, qpts.to(ureg('1/bohr')).magnitude.T ).T
def setUp(self): # Test creation of BandsData object (which reads Fe.bands file in # test/data dir). There is no Fe.castep file in test/data so the ion_r # and ion_pos attributes shouldn't exist # Create trivial function object so attributes can be assigned to it expctd_data = type('', (), {})() # Expected data expctd_data.cell_vec = np.array( [[-2.708355, 2.708355, 2.708355], [2.708355, -2.708355, 2.708355], [2.708355, 2.708355, -2.708355]]) * ureg('bohr') expctd_data.qpts = np.array([[-0.37500000, -0.45833333, 0.29166667], [-0.37500000, -0.37500000, 0.29166667]]) expctd_data.weights = np.array([0.01388889, 0.01388889]) expctd_data.freqs = np.array([[ 0.02278248, 0.02644693, 0.12383402, 0.15398152, 0.17125020, 0.43252010 ], [ 0.02760952, 0.02644911, 0.12442671, 0.14597457, 0.16728951, 0.35463529 ]]) * ureg('hartree') expctd_data.freq_down = np.array( [[ 0.08112495, 0.08345039, 0.19185076, 0.22763689, 0.24912308, 0.46511567 ], [ 0.08778721, 0.08033338, 0.19288937, 0.21817779, 0.24476910, 0.39214129 ]]) * ureg('hartree') expctd_data.fermi = [0.173319, 0.173319] * ureg('hartree') self.expctd_data = expctd_data seedname = 'Fe' path = 'data' data = BandsData.from_castep(seedname, path=path) self.data = data