def build_output_kpoints(self, parsed_parameters, structure): """Build the output kpoints from the raw parsed data. :param parsed_parameters: the raw parsed data :return: a `KpointsData` or None """ k_points_list = parsed_parameters.pop('k_points', None) k_points_units = parsed_parameters.pop('k_points_units', None) k_points_weights_list = parsed_parameters.pop('k_points_weights', None) if k_points_list is None or k_points_weights_list is None: return None if k_points_units != '1 / angstrom': self.logger.error('Error in kpoints units (should be cartesian)') self.exit_code_parser = self.exit_codes.ERROR_INVALID_KPOINT_UNITS return None kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints(k_points_list, cartesian=True, weights=k_points_weights_list) return kpoints
def generate_inputs_default(): """Return only those inputs that the parser will expect to be there.""" a = 5.43 structure = orm.StructureData( cell=[[a / 2., a / 2., 0], [a / 2., 0, a / 2.], [0, a / 2., a / 2.]]) structure.append_atom(position=(0., 0., 0.), symbols='Si', name='Si1') structure.append_atom(position=(a / 4., a / 4., a / 4.), symbols='Si', name='Si2') structure.store() parameters = { 'CONTROL': { 'calculation': 'scf' }, 'SYSTEM': { 'ecutrho': 240.0, 'ecutwfc': 30.0 } } kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints_mesh_from_density(0.15) return AttributeDict({ 'structure': structure, 'kpoints': kpoints, 'parameters': orm.Dict(dict=parameters), 'settings': orm.Dict() })
def populate_restapi_database(clear_database_before_test): """Populates the database with a considerable set of nodes to test the restAPI""" # pylint: disable=unused-argument from aiida import orm struct_forcif = orm.StructureData().store() orm.StructureData().store() orm.StructureData().store() orm.Dict().store() orm.Dict().store() orm.CifData(ase=struct_forcif.get_ase()).store() orm.KpointsData().store() orm.FolderData().store() orm.CalcFunctionNode().store() orm.CalcJobNode().store() orm.CalcJobNode().store() orm.WorkFunctionNode().store() orm.WorkFunctionNode().store() orm.WorkChainNode().store()
def test_ph_qpoint_list( aiida_profile, fixture_localhost, fixture_sandbox, generate_calc_job, fixture_code, generate_structure, generate_kpoints_mesh, generate_remote_data, file_regression ): """Test a `PhCalculation` with a qpoint list instead of a mesh.""" entry_point_name = 'quantumespresso.ph' parent_entry_point = 'quantumespresso.pw' remote_path = fixture_sandbox.abspath structure = generate_structure() kpoints = generate_kpoints_mesh(2).get_kpoints_mesh(print_list=True) qpoints = orm.KpointsData() qpoints.set_cell(structure.cell) qpoints.set_kpoints(kpoints) inputs = { 'code': fixture_code(entry_point_name), 'parent_folder': generate_remote_data(fixture_localhost, remote_path, parent_entry_point), 'qpoints': qpoints, 'parameters': orm.Dict(dict={'INPUTPH': {}}), 'metadata': { 'options': get_default_options() } } generate_calc_job(fixture_sandbox, entry_point_name, inputs) with fixture_sandbox.open('aiida.in') as handle: input_written = handle.read() file_regression.check(input_written, encoding='utf-8', extension='.in')
def generate_inputs_base(protocol: Dict, code: orm.Code, structure: StructureData, override: Dict[str, Any] = None) -> Dict[str, Any]: """Generate the inputs for the `AbinitBaseWorkChain` for a given code, structure and pseudo potential family. :param protocol: the dictionary with protocol inputs. :param code: the code to use. :param structure: the input structure. :param override: a dictionary to override specific inputs. :return: the fully defined input dictionary. """ protocol['abinit'] = generate_inputs_calculation( protocol['abinit'], code, structure, override.get('abinit', {})) merged = recursive_merge(protocol, override or {}) if isinstance(merged['abinit']['parameters'], dict): merged['abinit']['parameters'] = orm.Dict( dict=merged['abinit']['parameters']) if merged.get('kpoints_distance') is not None: dictionary = { 'abinit': merged['abinit'], 'kpoints_distance': orm.Float(merged['kpoints_distance']) } elif merged.get('kpoints') is not None: kpoints = orm.KpointsData() kpoints.set_kpoints_mesh(merged['kpoints']) dictionary = {'abinit': merged['abinit'], 'kpoints': kpoints} else: raise ValueError( 'Neither `kpoints_distance` nor `kpoints` were specified as inputs' ) return dictionary
def test_vasp_hf_wannier_input( configure_with_daemon, # pylint: disable=unused-argument assert_finished, get_insb_input # pylint: disable=redefined-outer-name ): """ Runs the workflow that calculates Wannier90 inputs from VASP + hybrids on InSb with a coarse grid. """ from aiida import orm from aiida.engine import run_get_node from aiida_tbextraction.fp_run.wannier_input import VaspWannierInput kpoints_mesh = orm.KpointsData() kpoints_mesh.set_kpoints_mesh([2, 2, 2]) wannier_projections = orm.List() wannier_projections.extend(['In : s; px; py; pz', 'Sb : px; py; pz']) result, node = run_get_node( VaspWannierInput, kpoints_mesh=kpoints_mesh, wannier_parameters=orm.Dict( dict=dict(num_wann=14, num_bands=36, spinors=True)), wannier_projections=wannier_projections, **get_insb_input) assert node.is_finished_ok assert all(key in result for key in ['wannier_input_folder', 'wannier_parameters', 'wannier_bands']) folder_list = result['wannier_input_folder'].get_folder_list() assert all( filename in folder_list for filename in ['wannier90.amn', 'wannier90.mmn', 'wannier90.eig'])
def create_kpoints_from_distance(structure: orm.StructureData, distance: orm.Float) -> orm.KpointsData: """Generate a uniformly spaced kpoint mesh for a given structure. The spacing between kpoints in reciprocal space is guaranteed to be at least the defined distance. :param structure: the StructureData to which the mesh should apply :param distance: a Float with the desired distance between kpoints in reciprocal space :returns: a KpointsData with the generated mesh """ epsilon = 1E-5 kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints_mesh_from_density(distance.value) lengths_vector = [np.linalg.norm(vector) for vector in structure.cell] lengths_kpoint = kpoints.get_kpoints_mesh()[0] is_symmetric_cell = all( abs(length - lengths_vector[0]) < epsilon for length in lengths_vector) is_symmetric_mesh = all(length == lengths_kpoint[0] for length in lengths_kpoint) # If the vectors of the cell all have the same length, the kpoint mesh should be isotropic as well if is_symmetric_cell and not is_symmetric_mesh: nkpoints = max(lengths_kpoint) kpoints.set_kpoints_mesh([nkpoints, nkpoints, nkpoints]) return kpoints
def test_siesta_bands_error(aiida_profile, fixture_localhost, generate_calc_job_node, generate_parser, generate_structure, data_regression): """ Test parsing of bands in a situation when the bands file is truncated. Also the time.json and MESSAGES file are added, therefore their parsing is tested as well. The MESSAGES file is the standard containing only "INFO: Job completed". """ name = 'bands_error' entry_point_calc_job = 'siesta.siesta' entry_point_parser = 'siesta.parser' structure=generate_structure() bandskpoints = orm.KpointsData() kpp = [(0.500, 0.250, 0.750), (0.500, 0.500, 0.500), (0., 0., 0.)] bandskpoints.set_cell(structure.cell, structure.pbc) bandskpoints.set_kpoints(kpp) inputs = AttributeDict({ 'structure': structure, 'bandskpoints': bandskpoints }) attributes=AttributeDict({'input_filename':'aiida.fdf', 'output_filename':'aiida.out', 'prefix':'aiida'}) node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, attributes) parser = generate_parser(entry_point_parser) results, calcfunction = parser.parse_from_node(node, store_provenance=False) assert calcfunction.is_finished assert calcfunction.exception is None assert not calcfunction.is_finished_ok assert calcfunction.exit_message == 'Failure while parsing the bands file' assert 'output_parameters' in results assert 'output_structure' in results
def generate_inputs(): """Return only those inputs that the parser will expect to be there.""" kpoints = orm.KpointsData() kpoints.set_kpoints_mesh([1, 1, 1]) return AttributeDict({ 'kpoints': kpoints, })
def generate_class_instance(data_class): """Generate a dummy `Data` instance for the given sub class.""" dirpath_fixtures = os.path.abspath( os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'fixtures')) if data_class is orm.CifData: instance = data_class( file=os.path.join(dirpath_fixtures, 'data', 'Si.cif')) return instance if data_class is orm.UpfData: filename = os.path.join( dirpath_fixtures, 'pseudos', 'Ba.pbesol-spn-rrkjus_psl.0.2.3-tot-pslib030.UPF') instance = data_class(file=filename) return instance if data_class is orm.StructureData: instance = orm.CifData(file=os.path.join( dirpath_fixtures, 'data', 'Si.cif')).get_structure() return instance if data_class is orm.BandsData: kpoints = orm.KpointsData() kpoints.set_cell([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) kpoints.set_kpoints([[0., 0., 0.], [0.1, 0.1, 0.1]]) instance = data_class() instance.set_kpointsdata(kpoints) instance.set_bands([[1.0, 2.0], [3.0, 4.0]]) return instance if data_class is orm.TrajectoryData: instance = data_class() stepids = numpy.array([60]) times = stepids * 0.01 cells = numpy.array([[[3., 0., 0.], [0., 3., 0.], [0., 0., 3.]]]) positions = numpy.array([[[0., 0., 0.]]]) instance.set_trajectory(stepids=stepids, cells=cells, symbols=['H'], positions=positions, times=times) return instance if data_class is orm.UpfData: filepath_base = os.path.abspath( os.path.join(__file__, os.pardir, os.pardir, os.pardir, 'fixtures', 'pseudos')) filepath_carbon = os.path.join(filepath_base, 'C_pbe_v1.2.uspp.F.UPF') instance = data_class(file=filepath_carbon) return instance raise RuntimeError( 'no instance generator implemented for class `{}`. If you have added a `_prepare_*` method ' 'for this data class, add a generator of a dummy instance here'. format(data_class))
def generate_cubic_grid(structure, centers, distance, dim): """Generate a cubic grids centered in `centers` of size `distance` and dimensionality `dim`. :param structure: aiida.orm.StructureData node used to get the cell of the material. :param centers: aiida.orm.ArrayData containing an array named `centers`. Each element of `centers` is used to generate a cubic grid around it. :param distance: aiida.orm.Float indicating the lateral size of the cubic grid. :param dim: aiida.orm.Int determining the dimensionality of the grid. e.g.: dim=1 -> 5x1x1 dim = 2 -> 5x5x1 dim = 3 -> 5x5x5 :return: aiida.orm.KpointsData containing the generated grids. """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(centers, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `centers`'.format(type(centers))) if not isinstance(distance, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `distance`'.format(type(distance))) npoints = 5 centers = centers.get_array('pinned') dist = distance.value / (npoints - 1) dim = dim.value # yapf: disable l = np.arange(-(npoints-1)//2, (npoints-1)//2 + 1) + ((npoints + 1)%2) * 0.5 lx = l ly = l if dim > 1 else [0,] lz = l if dim > 2 else [0,] grid = np.array(list(product(lx, ly, lz))) * dist res = np.empty((0,3)) for n,c in enumerate(centers): new = c + grid if n == 0: attach = new else: old_tree = KDTree(res) new_tree = KDTree(new) query = new_tree.query_ball_tree(old_tree, r=dist*1.74) attach = np.array([new[n] for n,q in enumerate(query) if not q]) if len(attach): res = np.vstack((res, attach)) kpt = orm.KpointsData() kpt.set_cell_from_structure(structure) kpt.set_kpoints(res, cartesian=True) return kpt
def generate_kpt_cross(structure, kpoints, step): """Generate a x,y,z cross around each point. :param structure: The StructureData to be used. :param kpoints: The original list of kpoints in crystal coordinates. :param step: The size of the step for the cross. :return: A KpointsData object containing all the kpt generated, including the original ones """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(kpoints, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `kpoints`'.format(type(kpoints))) if not isinstance(step, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `step`'.format(type(step))) try: kpt_cryst = kpoints.get_array('kpoints') except: kpt_cryst = kpoints.get_array('crossings') try: skips = kpoints.get_array('skips') except: skips = [0] * len(kpt_cryst) step = step.value cell = structure.cell recipr = recipr_base(cell) kpts_cart = np.dot(kpt_cryst, recipr) # Apply cross shifts to original kpts shifts = np.array([ [step, 0, 0], [0, step, 0], [0, 0, step], [0, 0, 0], [-step, 0, 0], [0, -step, 0], [0, 0, -step], ]) app = np.empty((0, 3)) for s, k in zip(skips, kpts_cart): if s: continue app = np.vstack((app, k + shifts)) new_kpt = orm.KpointsData() new_kpt.set_cell(cell) new_kpt.set_kpoints(app, cartesian=True) return new_kpt
def silicon_builder(db_test_app): """Prepare a mock - ready calculation for silicon""" silicon = orm.StructureData() r_unit = 2.6954645 silicon.set_cell(np.array([[1, 1, 0], [1, 0, 1], [0, 1, 1]]) * r_unit) silicon.append_atom(symbols=["Si"], position=[0, 0, 0]) silicon.append_atom(symbols=["Si"], position=[r_unit * 0.5] * 3) silicon.label = "Si" silicon.description = "A silicon structure" param_dict = { # Notice that the keywords are group into two sub-dictionaries # just like you would do when preparing the inputs by hand "CELL": { "symmetry_generate": True, "snap_to_symmetry": True, # Pass a list of string to set a BLOCK inputs #"cell_constraints": #["0 0 0", "0 0 0"] }, "PARAM": { "task": "singlepoint", "basis_precision": "medium", "fix_occupancy": True, # Use bool type to make it easy for querying "opt_strategy": "memory", "num_dump_cycles": 0, "write_formatted_density": True } } # We need to create a Dict node that holds the dictionary param = orm.Dict(dict=param_dict) kpoints = orm.KpointsData() # Use gamma and 0.25, 0.25, 0.25 kpoints.set_kpoints_mesh((4, 4, 4), offset=(0, 0, 0)) c9 = OTFGData(otfg_entry="C9") CastepCalculation = CalculationFactory('castep.castep') code_path = check_output(['which', 'castep.mock'], universal_newlines=True).strip() castep_mock = orm.Code((db_test_app.localhost, code_path), input_plugin_name='castep.castep') builder = CastepCalculation.get_builder() builder.structure = silicon builder.parameters = param builder.kpoints = kpoints builder.code = castep_mock builder.pseudos = {'Si': c9} builder.metadata.options.withmpi = False builder.metadata.options.resources = { 'num_machines': 1, 'tot_num_mpiprocs': 2 } builder.metadata.options.max_wallclock_seconds = 600 builder.metadata.label = "Si SINGLEPOINT" builder.metadata.description = 'A Example CASTEP calculation for silicon' return builder
def crop_kpoints(structure, kpt_data, centers, radius): """Crop a given set of k-points `kpt_data` that are within a spherical radius `r` from a set of centers `centers`. :param structure: aiida.orm.StructureData used to get the cell of the material. :param kpt_data: aiida.orm.KpointsData to crop. :param centers: aiida.orm.ArrayData containing an array named `centers`. Each element of `centers` is used as the center of a spherical cropping. :param radius: radius of the sphere cropping. :return: aiida.orm.KpointsData node containing the cropped kpoints """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(kpt_data, orm.KpointsData): raise InputValidationError( 'Invalide type {} for parameter `kpt_data`'.format(type(kpt_data))) if not isinstance(centers, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `centers`'.format(type(centers))) if not isinstance(radius, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `radius`'.format(type(radius))) centers = centers.get_array('centers') if len(centers.shape) != 2 or centers.shape[1] != 3: raise InputValidationError( 'Invalide shape {} for array `centers`. Expected (*,3)'.format( centers.shape)) r = radius.value cell = np.array(structure.cell) recipr = recipr_base(cell) try: kpt_cryst = np.array(kpt_data.get_kpoints_mesh(print_list=True)) except MemoryError: return orm.Bool(False) kpt_cart = np.dot(kpt_cryst, recipr) c_cryst = centers c_cart = np.dot(c_cryst, recipr) kpt_cart = KDTree(kpt_cart) centers = KDTree(c_cart) query = kpt_cart.query_ball_tree(centers, r=r) where = [n for n, l in enumerate(query) if len(l)] new = orm.KpointsData() new.set_kpoints(kpt_cryst[where]) return new
def merge_kpoints_inline(mesh_kpoints, band_kpoints): """ Merges the kpoints of mesh_kpoints and band_kpoints (in that order), giving weight 1 to the mesh_kpoints, and weight 0 to the band_kpoints. """ band_kpoints_array = band_kpoints.get_kpoints() mesh_kpoints_array = mesh_kpoints.get_kpoints_mesh(print_list=True) weights = [1.] * len(mesh_kpoints_array) + [0.] * len(band_kpoints_array) kpoints = orm.KpointsData() kpoints.set_kpoints( np.vstack([mesh_kpoints_array, band_kpoints_array]), weights=weights ) return {'kpoints': kpoints}
def parse(self, **kwargs): """Parse the retrieved files from a `MatdynCalculation`.""" try: output_folder = self.retrieved except exceptions.NotExistent: return self.exit(self.exit_codes.ERROR_NO_RETRIEVED_FOLDER) filename_stdout = self.node.get_option('output_filename') filename_frequencies = MatdynCalculation._PHONON_FREQUENCIES_NAME if filename_stdout not in output_folder.list_object_names(): return self.exit(self.exit_codes.ERROR_OUTPUT_STDOUT_READ) if 'JOB DONE' not in output_folder.get_object_content(filename_stdout): return self.exit(self.exit_codes.ERROR_OUTPUT_STDOUT_INCOMPLETE) if filename_frequencies not in output_folder.list_object_names(): return self.exit(self.exit_codes.ERROR_OUTPUT_STDOUT_READ) # Extract the kpoints from the input data and create the `KpointsData` for the `BandsData` try: kpoints = self.node.inputs.kpoints.get_kpoints() kpoints_for_bands = self.node.inputs.kpoints.clone() except AttributeError: kpoints = self.node.inputs.kpoints.get_kpoints_mesh( print_list=True) kpoints_for_bands = orm.KpointsData() kpoints_for_bands.set_kpoints(kpoints) parsed_data = parse_raw_matdyn_phonon_file( output_folder.get_object_content(filename_frequencies)) try: num_kpoints = parsed_data.pop('num_kpoints') except KeyError: return self.exit(self.exit_codes.ERROR_OUTPUT_KPOINTS_MISSING) if num_kpoints != kpoints.shape[0]: return self.exit( self.exit_codes.ERROR_OUTPUT_KPOINTS_INCOMMENSURATE) output_bands = orm.BandsData() output_bands.set_kpointsdata(kpoints_for_bands) output_bands.set_bands(parsed_data.pop('phonon_bands'), units='THz') for message in parsed_data['warnings']: self.logger.error(message) self.out('output_parameters', orm.Dict(dict=parsed_data)) self.out('output_phonon_bands', output_bands) return
def generate_inputs_calculation( protocol: Dict, code: orm.Code, structure: orm.StructureData, otfg_family: OTFGGroup, override: Dict[str, Any] = None ) -> Dict[str, Any]: """Generate the inputs for the `CastepCalculation` for a given code, structure and pseudo potential family. :param protocol: the dictionary with protocol inputs. :param code: the code to use. :param structure: the input structure. :param otfg_family: the pseudo potential family. :param override: a dictionary to override specific inputs. :return: the fully defined input dictionary. """ from aiida_castep.calculations.helper import CastepHelper override = {} if not override else override.get('calc', {}) # This merge perserves the merged `parameters` in the override merged_calc = recursive_merge(protocol['calc'], override) # Create KpointData for CastepCalculation, the kpoints_spacing passed is # already in the AiiDA convention, e.g. with 2pi factor built into it. kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints_mesh_from_density(protocol['kpoints_spacing']) # For bare calculation level, we need to make sure the dictionary is not "flat" param = merged_calc['parameters'] # Remove incompatible options: cut_off_energy and basis_precisions can not be # both specified if 'cut_off_energy' in param: param.pop('basis_precision', None) helper = CastepHelper() param = helper.check_dict(param, auto_fix=True, allow_flat=True) dictionary = { 'structure': structure, 'kpoints': kpoints, 'code': code, 'parameters': orm.Dict(dict=param), 'pseudos': get_pseudos_from_structure(structure, otfg_family.label), 'metadata': merged_calc.get('metadata', {}) } # Add the settings input if present if 'settings' in merged_calc: dictionary['settings'] = orm.Dict(dict=merged_calc['settings']) return dictionary
def _generate_inputs(calculation_type='scf', settings=None, metadata=None): structure = generate_structure() parameters = {'CONTROL': {'calculation': calculation_type}} kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints_mesh_from_density(0.15) return AttributeDict({ 'structure': generate_structure(), 'kpoints': kpoints, 'parameters': orm.Dict(dict=parameters), 'settings': orm.Dict(dict=settings), 'metadata': metadata or {} })
def test_bandspoints(aiida_profile, fixture_sandbox, generate_calc_job, fixture_code, generate_structure, generate_kpoints_mesh, generate_basis, generate_param, generate_psf_data, generate_psml_data, file_regression): """ Test that the fdf file contains the correct information when a band structure calculation is required by the user and he/she specifies single k-points. """ entry_point_name = 'siesta.siesta' psf = generate_psf_data('Si') psml = generate_psml_data('Si') structure=generate_structure() bandskpoints = orm.KpointsData() kpp = [(0.500, 0.250, 0.750), (0.500, 0.500, 0.500), (0., 0., 0.)] bandskpoints.set_cell(structure.cell, structure.pbc) bandskpoints.set_kpoints(kpp) inputs = { 'bandskpoints': bandskpoints, 'code': fixture_code(entry_point_name), 'structure': structure, 'kpoints': generate_kpoints_mesh(2), 'parameters': generate_param(), 'basis': generate_basis(), 'pseudos': { 'Si': psf, 'SiDiff': psml }, 'metadata': { 'options': { 'resources': {'num_machines': 1 }, 'max_wallclock_seconds': 1800, 'withmpi': False, } } } calc_info = generate_calc_job(fixture_sandbox, entry_point_name, inputs) retrieve_list = ['BASIS_ENTHALPY', 'MESSAGES','time.json','aiida.out','aiida.xml','aiida.bands','*.ion.xml'] assert sorted(calc_info.retrieve_list) == sorted(retrieve_list) with fixture_sandbox.open('aiida.fdf') as handle: input_written = handle.read() file_regression.check(input_written, encoding='utf-8', extension='.fdf')
def inner(): kpoints_mesh = orm.KpointsData() kpoints_mesh.set_kpoints_mesh([2, 2, 2]) kpoints = orm.KpointsData() kpoints.set_kpoints([[x, x, x] for x in np.linspace(0, 0.5, 6)], labels=((0, 'G'), (5, 'M'))) return { 'structure': insb_structure, 'kpoints_mesh': kpoints_mesh, 'kpoints': kpoints, 'wannier_parameters': orm.Dict(dict=dict(num_wann=14, num_bands=36, spinors=True, dis_num_iter=1000, num_iter=0)), 'wannier_projections': orm.List(list=['In : s; px; py; pz', 'Sb : px; py; pz']), }
def test_siesta_bandspoints(aiida_profile, fixture_localhost, generate_calc_job_node, generate_parser, generate_structure, data_regression): """ Test parsing of bands in a siesta calculation when the bandspoints option is set in the submission file. Also the time.json, MESSAGES and BASIS_ENTHALPY files are added, therefore their parsing is tested as well. The MESSAGES file is the standard containing only "INFO: Job completed". """ name = 'bandspoints' entry_point_calc_job = 'siesta.siesta' entry_point_parser = 'siesta.parser' structure=generate_structure() bandskpoints = orm.KpointsData() kpp = [(0.500, 0.250, 0.750), (0.500, 0.500, 0.500), (0., 0., 0.)] bandskpoints.set_cell(structure.cell, structure.pbc) bandskpoints.set_kpoints(kpp) inputs = AttributeDict({ 'structure': structure, 'bandskpoints': bandskpoints }) attributes=AttributeDict({'input_filename':'aiida.fdf', 'output_filename':'aiida.out', 'prefix':'aiida'}) node = generate_calc_job_node(entry_point_calc_job, fixture_localhost, name, inputs, attributes) parser = generate_parser(entry_point_parser) results, calcfunction = parser.parse_from_node(node, store_provenance=False) assert calcfunction.is_finished assert calcfunction.exception is None assert calcfunction.is_finished_ok assert calcfunction.exit_message is None assert not orm.Log.objects.get_logs_for(node) assert 'forces_and_stress' in results assert 'output_parameters' in results assert 'output_structure' in results assert 'bands' in results data_regression.check({ 'forces_and_stress': results['forces_and_stress'].attributes, 'output_parameters': results['output_parameters'].get_dict(), 'bands': results['bands'].attributes })
def make_explicit_kpoints(kpoints_mesh): """ Creates an explicit KpointsData from a KpointsData specified as a mesh. """ mesh, offset = kpoints_mesh.get_kpoints_mesh() kpts_explicit_array = np.array( list( itertools.product( *[ np.linspace(0, 1, mesh_size, endpoint=False) for mesh_size in mesh ] ) ) ) kpts_explicit_array += offset kpts_explicit = orm.KpointsData() kpts_explicit.set_kpoints(kpts_explicit_array) return kpts_explicit
def kpt_crop(kpoints: orm.KpointsData, centers: orm.ArrayData, radii: orm.ArrayData, anticrop: orm.Bool) -> orm.KpointsData: kpt_cryst = kpoints.get_kpoints_mesh(print_list=True) cell = kpoints.cell recipr = recipr_base(cell) centers = centers.get_array('centers') centers = centers.dot(recipr) radii = radii.get_array('radii') kpt, wgt = _kpt_crop(kpt_cryst, recipr, centers=centers, radii=radii, anticrop=anticrop.value) res = orm.KpointsData() res.set_cell(cell) res.set_kpoints(kpt, cartesian=True, weights=wgt) return res
def generate_inputs_calculation( protocol: Dict, code: orm.Code, structure: orm.StructureData, otfg_family: OTFGGroup, override: Dict[str, Any] = None) -> Dict[str, Any]: """Generate the inputs for the `CastepCalculation` for a given code, structure and pseudo potential family. :param protocol: the dictionary with protocol inputs. :param code: the code to use. :param structure: the input structure. :param otfg_family: the pseudo potential family. :param override: a dictionary to override specific inputs. :return: the fully defined input dictionary. """ from aiida_castep.calculations.helper import CastepHelper merged = recursive_merge(protocol, override or {}) kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints_mesh_from_density(protocol['kpoints_spacing'] * pi * 2) # For bare calculation level, we need to make sure the dictionary is not "flat" param = merged['calc']['parameters'] helper = CastepHelper() param = helper.check_dict(param, auto_fix=True, allow_flat=True) dictionary = { 'structure': structure, 'kpoints': kpoints, 'code': code, 'parameters': orm.Dict(dict=param), 'pseudos': get_pseudos_from_structure(structure, otfg_family.label), 'metadata': merged.get('metadata', {}) } # NOTE: Need to process settings return dictionary
def generate_trim(structure: orm.StructureData, dimensionality: orm.Int) -> orm.KpointsData: """Generate the TRIM point KpointsData for the given structure and dimensionality.""" from itertools import product null = [0.0] l = [0.0, 0.5] dim = dimensionality.value if dim == 2: grid = np.array(list(product(l, l, null))) elif dim == 3: grid = np.array(list(product(l, l, l))) else: raise exceptions.InputValidationError( 'Invalid dimensionality {}'.format(dim)) res = orm.KpointsData() res.set_cell_from_structure(structure) res.set_kpoints(grid) return res
def convert_kpoints_mesh_to_list(kmesh): """works just like kmesh.pl in Wannier90 :param kmesh: [description] :type kmesh: KpointsData :raises ValueError: [description] :return: [description] :rtype: [type] """ import numpy as np try: # test if it is a mesh results = kmesh.get_kpoints_mesh() except AttributeError: try: kmesh.get_kpoints() except AttributeError as e: # an empty input e.args = ('input kmesh is empty!', ) raise e else: return kmesh else: # currently we ignore offset mesh = results[0] # following is similar to wannier90/kmesh.pl totpts = np.prod(mesh) weights = np.ones([totpts]) / totpts kpoints = np.zeros([totpts, 3]) ind = 0 for x in range(mesh[0]): for y in range(mesh[1]): for z in range(mesh[2]): kpoints[ind, :] = [x / mesh[0], y / mesh[1], z / mesh[2]] ind += 1 klist = orm.KpointsData() klist.set_kpoints(kpoints=kpoints, cartesian=False, weights=weights) return klist
def generate_inputs_relax(): """Return only those inputs that the parser will expect to be there. This needs a separate input generation function from the default one, because the parser depends on certain values in the input parameters to determine what kind of calculation it was. For example, it will check the card `CONTROL.calculation` to determine whether the `TrajectoryData` should be attached. If we would not set it to `relax`, the parser would not parse that output node and the test would fail. Until we can make the raw output parser independent of the input parameters, this will have to remain a separate test inputs generator. """ a = 5.43 structure = orm.StructureData( cell=[[a / 2., a / 2., 0], [a / 2., 0, a / 2.], [0, a / 2., a / 2.]]) structure.append_atom(position=(0., 0., 0.), symbols='Si', name='Si1') structure.append_atom(position=(a / 4., a / 4., a / 4.), symbols='Si', name='Si2') structure.store() parameters = { 'CONTROL': { 'calculation': 'relax' }, 'SYSTEM': { 'ecutrho': 240.0, 'ecutwfc': 30.0 } } kpoints = orm.KpointsData() kpoints.set_cell_from_structure(structure) kpoints.set_kpoints_mesh_from_density(0.15) return AttributeDict({ 'structure': structure, 'kpoints': kpoints, 'parameters': orm.Dict(dict=parameters), 'settings': orm.Dict() })
def create_builder(): builder = OptimizeFirstPrinciplesTightBinding.get_builder() # Add the input structure builder.structure = orm.StructureData() builder.structure.set_ase(read_vasp('inputs/POSCAR')) # Specify that QuantumEspressoFirstPrinciplesRun should be used to run the first-principles calculations builder.fp_run_workflow = QuantumEspressoFirstPrinciplesRun # Set the inputs for the QuantumEspressoFirstPrinciplesRun workflow common_qe_parameters = orm.Dict( dict=dict( CONTROL=dict(etot_conv_thr=1e-3), SYSTEM=dict(noncolin=True, lspinorb=True, nbnd=36, ecutwfc=30), ) ) pseudo_dir = pathlib.Path(__file__ ).parent.absolute() / 'inputs' / 'pseudos' pseudos = { 'In': orm.UpfData( file=str(pseudo_dir / 'In.rel-pbe-dn-kjpaw_psl.1.0.0.UPF') ), 'Sb': orm.UpfData(file=str(pseudo_dir / 'Sb.rel-pbe-n-kjpaw_psl.1.0.0.UPF')) } # We use the same general Quantum ESPRESSO parameters for the # scf, nscf, and bands calculations. The calculation type and # k-points will be set by the workflow. repeated_pw_inputs = { 'pseudos': pseudos, 'parameters': common_qe_parameters, 'metadata': METADATA_PW, 'code': CODE_PW } builder.fp_run = { 'scf': repeated_pw_inputs, 'bands': { 'pw': repeated_pw_inputs }, 'to_wannier': { 'nscf': repeated_pw_inputs, 'pw2wannier': { 'code': CODE_PW2WANNIER, 'metadata': METADATA_PW2WANNIER }, 'wannier': { 'code': CODE_WANNIER, 'metadata': METADATA_WANNIER } } } # Setting the k-points for the reference bandstructure kpoints_list = [] kvals = np.linspace(0, 0.5, 20, endpoint=False) kvals_rev = np.linspace(0.5, 0, 20, endpoint=False) for k in kvals_rev: kpoints_list.append((k, k, 0)) # Z to Gamma for k in kvals: kpoints_list.append((0, k, k)) # Gamma to X for k in kvals: kpoints_list.append((k, 0.5, 0.5)) # X to L for k in kvals_rev: kpoints_list.append((k, k, k)) # L to Gamma for k in np.linspace(0, 0.375, 21, endpoint=True): kpoints_list.append((k, k, 2 * k)) # Gamma to K builder.kpoints = orm.KpointsData() builder.kpoints.set_kpoints( kpoints_list, labels=[(i * 20, label) for i, label in enumerate(['Z', 'G', 'X', 'L', 'G', 'K'])] ) # Setting the k-points mesh used to run the SCF and Wannier calculations builder.kpoints_mesh = orm.KpointsData() builder.kpoints_mesh.set_kpoints_mesh([6, 6, 6]) builder.wannier.code = CODE_WANNIER builder.code_tbmodels = orm.Code.get_from_string('tbmodels') # Setting the workflow to evaluate the tight-binding models builder.model_evaluation_workflow = BandDifferenceModelEvaluation # Setting the additional inputs for the model evaluation workflow builder.model_evaluation = dict( code_bands_inspect=orm.Code.get_from_string('bands_inspect') ) # Set the initial energy window value builder.initial_window = orm.List(list=[-1, 3, 10, 18]) # Tolerance for the energy window. builder.window_tol = orm.Float(1.5) # Tolerance for the 'cost_value'. builder.cost_tol = orm.Float(0.3) # The tolerances are set higher than might be appropriate for a 'production' # run to make the example run more quickly. # Setting the parameters for Wannier90 builder.wannier_parameters = orm.Dict( dict=dict( num_wann=14, num_bands=36, dis_num_iter=100, num_iter=0, spinors=True, # exclude_bands=range(1, ) ) ) # Choose the Wannier90 trial orbitals builder.wannier_projections = orm.List( list=['In : s; pz; px; py', 'Sb : pz; px; py'] ) # Set the resource requirements for the Wannier90 run builder.wannier.metadata = METADATA_WANNIER # Set the symmetry file builder.symmetries = orm.SinglefileData( file=os.path.abspath('inputs/symmetries.hdf5') ) # Pick the relevant bands from the reference calculation builder.slice_reference_bands = orm.List(list=list(range(12, 26))) return builder
def get_builder(self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals,too-many-branches,too-many-statements protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) protocol = copy.deepcopy(self.get_protocol(protocol)) code = calc_engines['relax']['code'] pseudo_family = orm.Group.objects.get( label=protocol.pop('pseudo_family')) override = { 'abinit': { 'metadata': { 'options': calc_engines['relax']['options'] }, 'pseudos': pseudo_family.get_pseudos(structure=structure) } } builder = self.process_class.get_builder() inputs = generate_inputs(self.process_class._process_class, protocol, code, structure, override) # pylint: disable=protected-access builder._update(inputs) # pylint: disable=protected-access # RelaxType if relax_type == RelaxType.NONE: builder.abinit['parameters'][ 'ionmov'] = 0 # do not move the ions, Abinit default elif relax_type == RelaxType.ATOMS: # protocol defaults to ATOMS pass elif relax_type == RelaxType.ATOMS_CELL: builder.abinit['parameters'][ 'optcell'] = 2 # fully optimize the cell geometry builder.abinit['parameters'][ 'dilatmx'] = 1.15 # book additional mem. for p.w. basis exp. elif relax_type == RelaxType.ATOMS_VOLUME: builder.abinit['parameters']['optcell'] = 1 # optimize volume only builder.abinit['parameters'][ 'dilatmx'] = 1.15 # book additional mem. for p.w. basis exp. elif relax_type == RelaxType.ATOMS_SHAPE: builder.abinit['parameters'][ 'optcell'] = 3 # constant-volume optimization of cell geometry else: raise ValueError('relax type `{}` is not supported'.format( relax_type.value)) # SpinType if spin_type == SpinType.NONE: # protocol defaults to NONE pass elif spin_type == SpinType.COLLINEAR: if np.all(np.isclose(magnetization_per_site, 0)): warnings.warn( 'All initial magnetizations are 0, doing a non-spin-polarized calculation.' ) elif np.isclose(sum(magnetization_per_site), 0): # antiferromagnetic builder.abinit['parameters'][ 'nsppol'] = 1 # antiferromagnetic system builder.abinit['parameters'][ 'nspden'] = 2 # scalar spin-magnetization in the z-axis builder.abinit['parameters']['spinat'] = [ [0.0, 0.0, mag] for mag in magnetization_per_site ] else: # ferromagnetic builder.abinit['parameters'][ 'nsppol'] = 2 # collinear spin-polarization builder.abinit['parameters'][ 'nspden'] = 2 # scalar spin-magnetization in the z-axis builder.abinit['parameters']['spinat'] = [ [0.0, 0.0, mag] for mag in magnetization_per_site ] elif spin_type == SpinType.NON_COLLINEAR: # LATER: support vector magnetization_per_site builder.abinit['parameters']['nspinor'] = 2 # w.f. as spinors builder.abinit['parameters'][ 'nsppol'] = 1 # spin-up and spin-down can't be disentangled builder.abinit['parameters']['nspden'] = 4 # vector magnetization builder.abinit['parameters']['spinat'] = [ [0.0, 0.0, mag] for mag in magnetization_per_site ] elif spin_type == SpinType.SPIN_ORBIT: if 'fr' not in protocol['pseudo_family']: raise ValueError('You must use the `stringent` protocol for SPIN_ORBIT calculations because '\ 'it provides fully-relativistic pseudopotentials (`fr` is not in the protocol\'s '\ '`pseudo_family` entry).') builder.abinit['parameters']['nspinor'] = 2 # w.f. as spinors else: raise ValueError('spin type `{}` is not supported'.format( spin_type.value)) # ElectronicType if electronic_type == ElectronicType.METAL: # protocal defaults to METAL pass elif electronic_type == ElectronicType.INSULATOR: # LATER: Support magnetization with insulators if spin_type not in [SpinType.NONE, SpinType.SPIN_ORBIT]: raise ValueError( '`spin_type` {} is not supported for insulating systems.'. format(spin_type.value)) builder.abinit['parameters'][ 'occopt'] = 1 # fixed occupations, Abinit default builder.abinit['parameters']['fband'] = 0.125 # Abinit default else: raise ValueError('electronic type `{}` is not supported'.format( electronic_type.value)) # force and stress thresholds if threshold_forces is not None: # The Abinit threshold_forces is in Ha/Bohr threshold_f = threshold_forces * units.eV_to_Ha / units.ang_to_bohr # eV/Å else: # ABINIT default value. Set it explicitly in case it is changed. # How can we warn the user that we are using the tolxmf Abinit default value? threshold_f = 5.0e-5 builder.abinit['parameters']['tolmxf'] = threshold_f if threshold_stress is not None: threshold_s = threshold_stress * units.eV_to_Ha / units.ang_to_bohr**3 # eV/Å^3 strfact = threshold_f / threshold_s builder.abinit['parameters']['strfact'] = strfact # previous workchain if previous_workchain is not None: try: previous_kpoints = previous_workchain.inputs.kpoints except exceptions.NotExistentAttributeError: query_builder = orm.QueryBuilder() query_builder.append(orm.WorkChainNode, tag='relax', filters={'id': previous_workchain.id}) query_builder.append( orm.WorkChainNode, tag='base', with_incoming='relax', ) query_builder.append( orm.CalcFunctionNode, tag='calcfunc', edge_filters={'label': 'create_kpoints_from_distance'}, with_incoming='base') query_builder.append(orm.KpointsData, tag='kpoints', with_incoming='calcfunc') query_builder.order_by({orm.KpointsData: {'ctime': 'desc'}}) query_builder_result = query_builder.all() if query_builder_result == []: raise ValueError( f'Could not find KpointsData associated with {previous_workchain}' ) previous_kpoints = query_builder_result[0][0] # ensure same k-points previous_kpoints_mesh, previous_kpoints_offset = previous_kpoints.get_kpoints_mesh( ) new_kpoints = orm.KpointsData() new_kpoints.set_cell_from_structure(structure) new_kpoints.set_kpoints_mesh(previous_kpoints_mesh, previous_kpoints_offset) builder.kpoints = new_kpoints # ensure same k-points shift shiftk = previous_workchain.inputs.abinit__parameters.get_dict( ).get('shiftk', None) if shiftk is not None: builder.abinit['parameters']['shiftk'] = shiftk nshiftk = previous_workchain.inputs.abinit__parameters.get_dict( ).get('nshiftk', None) if nshiftk is not None: builder.abinit['parameters']['nshiftk'] = nshiftk return builder
def get_builder(self, structure: StructureData, engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.POSITIONS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, reference_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param reference_workchain: a <Code>CommonRelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals,too-many-branches,too-many-statements protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, reference_workchain=reference_workchain, **kwargs) protocol = copy.deepcopy(self.get_protocol(protocol)) code = engines['relax']['code'] pseudo_family = orm.Group.objects.get( label=protocol.pop('pseudo_family')) cutoff_stringency = protocol['cutoff_stringency'] pseudo_type = pseudo_family.pseudo_type # Recommended cutoffs from `aiida-pseudo` are in eV recommended_ecut_wfc, recommended_ecut_rho = pseudo_family.get_recommended_cutoffs( structure=structure, stringency=cutoff_stringency) if pseudo_type == 'pseudo.jthxml': # JTH XML are PAW; we need `pawecutdg` cutoff_parameters = { 'ecut': np.ceil(recommended_ecut_wfc / units.Ha_to_eV), 'pawecutdg': np.ceil(recommended_ecut_rho / units.Ha_to_eV), } else: # All others are NC; no need for `pawecutdg` cutoff_parameters = {'ecut': recommended_ecut_wfc / units.Ha_to_eV} override = { 'abinit': { 'metadata': { 'options': engines['relax']['options'] }, 'pseudos': pseudo_family.get_pseudos(structure=structure), 'parameters': cutoff_parameters } } builder = self.process_class.get_builder() # Force threshold # NB we deal with this here because knowing threshold_f is necessary if the structure is a molecule. # threshold_f will be used later in the generator to set the relax threshold # (find "Continue force and stress thresholds" in this file.) if threshold_forces is not None: threshold_f = threshold_forces * units.eV_to_Ha / units.ang_to_bohr # eV/Å -> Ha/Bohr else: threshold_f = 5.0e-5 # Abinit default value in Ha/Bohr # Deal with molecular case if structure.pbc == (False, False, False): # We assume the structure is a molecule which already has an appropriate vacuum applied # NB: the vacuum around the molecule must maintain the molecule's symmetries! warnings.warn( f'The input structure {structure} has no periodic boundary conditions, so we ' 'assume the structure is a molecule. The structure will be modified to have full PBC. We assume that ' 'the cell contains appropriate symmetry-conserving vacuum, and various tweaks for molecular systems ' ' will be applied to the selected protocol!') # Set pbc to [True, True, True] pbc_structure = structure.clone() pbc_structure.set_pbc([True, True, True]) # Update protocol _ = protocol['base'].pop( 'kpoints_distance' ) # Remove k-points distance; we will use gamma only _ = protocol['base']['abinit']['parameters'].pop( 'tolvrs') # Remove tolvrs; we will use force tolerance for SCF # Set k-points to gamma-point protocol['base']['kpoints'] = [1, 1, 1] protocol['base']['abinit']['parameters']['shiftk'] = [[0, 0, 0]] protocol['base']['abinit']['parameters']['nkpt'] = 1 # Set a force tolerance for SCF convergence protocol['base']['abinit']['parameters'][ 'toldff'] = threshold_f * 1.0e-1 # Add a model macroscopic dielectric constant protocol['base']['abinit']['parameters']['diemac'] = 2.0 inputs = generate_inputs(self.process_class._process_class, protocol, code, pbc_structure, override) # pylint: disable=protected-access elif False in structure.pbc: raise ValueError( f'The input structure has periodic boundary conditions {structure.pbc}, but partial ' 'periodic boundary conditions are not supported.') else: inputs = generate_inputs(self.process_class._process_class, protocol, code, structure, override) # pylint: disable=protected-access builder._update(inputs) # pylint: disable=protected-access # RelaxType if relax_type == RelaxType.NONE: builder.abinit['parameters'][ 'ionmov'] = 0 # do not move the ions, Abinit default elif relax_type == RelaxType.POSITIONS: # protocol defaults to POSITIONS pass elif relax_type == RelaxType.POSITIONS_CELL: builder.abinit['parameters'][ 'optcell'] = 2 # fully optimize the cell geometry builder.abinit['parameters'][ 'dilatmx'] = 1.15 # book additional mem. for p.w. basis exp. elif relax_type == RelaxType.POSITIONS_VOLUME: builder.abinit['parameters']['optcell'] = 1 # optimize volume only builder.abinit['parameters'][ 'dilatmx'] = 1.15 # book additional mem. for p.w. basis exp. elif relax_type == RelaxType.POSITIONS_SHAPE: builder.abinit['parameters'][ 'optcell'] = 3 # constant-volume optimization of cell geometry builder.abinit['parameters'][ 'dilatmx'] = 1.05 # book additional mem. for p.w. basis exp. else: raise ValueError('relax type `{}` is not supported'.format( relax_type.value)) # SpinType if spin_type == SpinType.NONE: # protocol defaults to NONE pass elif spin_type == SpinType.COLLINEAR: if magnetization_per_site is None: magnetization_per_site = get_initial_magnetization(structure) warnings.warn( f'input magnetization per site was None, setting it to {magnetization_per_site}' ) magnetization_per_site = np.array(magnetization_per_site) sum_is_zero = np.isclose(sum(magnetization_per_site), 0.0) all_are_zero = np.all(np.isclose(magnetization_per_site, 0.0)) non_zero_mags = magnetization_per_site[ ~np.isclose(magnetization_per_site, 0.0)] all_non_zero_pos = np.all(non_zero_mags > 0.0) all_non_zero_neg = np.all(non_zero_mags < 0.0) if all_are_zero: # non-magnetic warnings.warn( 'all of the initial magnetizations per site are close to zero; doing a non-spin-polarized ' 'calculation') elif ((sum_is_zero and not all_are_zero) or (not all_non_zero_pos and not all_non_zero_neg)): # antiferromagnetic print('Detected antiferromagnetic!') builder.abinit['parameters'][ 'nsppol'] = 1 # antiferromagnetic system builder.abinit['parameters'][ 'nspden'] = 2 # scalar spin-magnetization in the z-axis builder.abinit['parameters']['spinat'] = [ [0.0, 0.0, mag] for mag in magnetization_per_site ] elif not all_are_zero and (all_non_zero_pos or all_non_zero_neg): # ferromagnetic print('Detected ferromagnetic!') builder.abinit['parameters'][ 'nsppol'] = 2 # collinear spin-polarization builder.abinit['parameters'][ 'nspden'] = 2 # scalar spin-magnetization in the z-axis builder.abinit['parameters']['spinat'] = [ [0.0, 0.0, mag] for mag in magnetization_per_site ] else: raise ValueError( f'Initial magnetization {magnetization_per_site} is ambiguous' ) elif spin_type == SpinType.NON_COLLINEAR: # LATER: support vector magnetization_per_site builder.abinit['parameters']['nspinor'] = 2 # w.f. as spinors builder.abinit['parameters'][ 'nsppol'] = 1 # spin-up and spin-down can't be disentangled builder.abinit['parameters']['nspden'] = 4 # vector magnetization builder.abinit['parameters']['spinat'] = [ [0.0, 0.0, mag] for mag in magnetization_per_site ] elif spin_type == SpinType.SPIN_ORBIT: builder.abinit['parameters']['nspinor'] = 2 # w.f. as spinors builder.abinit['parameters'][ 'kptopt'] = 4 # no time-reversal symmetry else: raise ValueError('spin type `{}` is not supported'.format( spin_type.value)) # ElectronicType if electronic_type == ElectronicType.METAL: # protocal defaults to METAL pass elif electronic_type == ElectronicType.INSULATOR: # LATER: Support magnetization with insulators if spin_type not in [SpinType.NONE, SpinType.SPIN_ORBIT]: raise ValueError( '`spin_type` {} is not supported for insulating systems.'. format(spin_type.value)) builder.abinit['parameters'][ 'occopt'] = 1 # fixed occupations, Abinit default builder.abinit['parameters']['fband'] = 0.125 # Abinit default else: raise ValueError('electronic type `{}` is not supported'.format( electronic_type.value)) # Continue force and stress thresholds from above (see molecule treatment) builder.abinit['parameters']['tolmxf'] = threshold_f if threshold_stress is not None: threshold_s = threshold_stress * units.eV_to_Ha / units.ang_to_bohr**3 # eV/Å^3 strfact = threshold_f / threshold_s builder.abinit['parameters']['strfact'] = strfact # previous workchain if reference_workchain is not None: try: previous_kpoints = reference_workchain.inputs.kpoints except exceptions.NotExistentAttributeError as not_existent_attr_error: query_builder = orm.QueryBuilder() query_builder.append(orm.WorkChainNode, tag='relax', filters={'id': reference_workchain.id}) query_builder.append( orm.WorkChainNode, tag='base', with_incoming='relax', ) query_builder.append( orm.CalcFunctionNode, tag='calcfunc', edge_filters={'label': 'create_kpoints_from_distance'}, with_incoming='base') query_builder.append(orm.KpointsData, tag='kpoints', with_incoming='calcfunc') query_builder.order_by({orm.KpointsData: {'ctime': 'desc'}}) query_builder_result = query_builder.all() if query_builder_result == []: msg = f'Could not find KpointsData associated with {reference_workchain}' raise ValueError(msg) from not_existent_attr_error previous_kpoints = query_builder_result[0][0] # ensure same k-points previous_kpoints_mesh, previous_kpoints_offset = previous_kpoints.get_kpoints_mesh( ) new_kpoints = orm.KpointsData() new_kpoints.set_cell_from_structure(structure) new_kpoints.set_kpoints_mesh(previous_kpoints_mesh, previous_kpoints_offset) builder.kpoints = new_kpoints # ensure same k-points shift shiftk = reference_workchain.inputs.abinit__parameters.get_dict( ).get('shiftk', None) if shiftk is not None: builder.abinit['parameters']['shiftk'] = shiftk nshiftk = reference_workchain.inputs.abinit__parameters.get_dict( ).get('nshiftk', None) if nshiftk is not None: builder.abinit['parameters']['nshiftk'] = nshiftk return builder