class TestVerdiDataArray(AiidaTestCase): """ Testing verdi data array """ @classmethod def setUpClass(cls): super(TestVerdiDataArray, cls).setUpClass() def setUp(self): self.a = ArrayData() self.a.set_array('test_array', np.array([0, 1, 3])) self.a.store() self.cli_runner = CliRunner() def test_arrayshowhelp(self): output = sp.check_output(['verdi', 'data', 'array', 'show', '--help']) self.assertIn('Usage:', output, "Sub-command verdi data array show --help failed.") def test_arrayshow(self): # with captured_output() as (out, err): options = [str(self.a.id)] res = self.cli_runner.invoke(cmd_array.show, options, catch_exceptions=False) self.assertEquals(res.exit_code, 0, "The command did not finish " "correctly")
def get_transport_data(self, nd_path): """ Parses the open channels and transmission files to get ArrayData objects that can be stored in the database """ import numpy as np from aiida.orm.data.array import ArrayData f = open(nd_path) lines = f.readlines() x = [] y = [] for line in lines: try: c1 = float(line.split()[0]) x.append(c1) c2 = float(line.split()[1]) y.append(c2) except: pass X = np.array(x, dtype=float) Y = np.array(y, dtype=float) arraydata = ArrayData() arraydata.set_array('X', X) arraydata.set_array('Y', Y) return arraydata
def parse_with_retrieved(self, retrieved): """ Parses the datafolder, stores results. """ # suppose at the start that the job is successful successful = True # select the folder object # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") return False, () # check what is inside the folder list_of_files = out_folder.get_folder_list() # OUTPUT file should exist if not self._calc._OUTPUT_FILE_NAME in list_of_files: successful = False self.logger.error("Output file not found") return successful, () # Get file and do the parsing outfile = out_folder.get_abs_path(self._calc._OUTPUT_FILE_NAME) force_constants = parse_FORCE_CONSTANTS(outfile) # look at warnings warnings = [] with open(out_folder.get_abs_path(self._calc._SCHED_ERROR_FILE)) as f: errors = f.read() if errors: warnings = [errors] # ====================== prepare the output node ====================== # save the outputs new_nodes_list = [] # save force constants into node try: array_data = ArrayData() array_data.set_array('force_constants', force_constants) new_nodes_list.append(('array_data', array_data)) except KeyError: # keys not found in json pass # add the dictionary with warnings new_nodes_list.append((self.get_linkname_outparams(), ParameterData(dict={'warnings': warnings}))) return successful, new_nodes_list
def parse_kappa(filename): import numpy as np import h5py from aiida.orm.data.array import ArrayData kappa = ArrayData() f = h5py.File(filename, 'r') for item in f: array = np.array(f[item]) kappa.set_array(item, array) f.close() return kappa
def _parser_function(self): """ Parses the vasprun.xml. """ # Get born charges and epsilon nodes_list = [] array_data = ArrayData() try: import xml.etree.cElementTree as ET tree = ET.parse(self._out_folder.get_abs_path('vasprun.xml')) root = tree.getroot() for elements in root.iter('varray'): if elements.attrib['name'] == 'epsilon': epsilon = [] for row in elements: epsilon.append(np.array(row.text.split(), dtype=float)) epsilon = np.array(epsilon) array_data.set_array('epsilon', epsilon) break for elements in root.iter('array'): try: if elements.attrib['name'] == 'born_charges': born_charges = [] for atom in elements[1:]: atom_array = [] for c in atom: atom_array.append( np.array(c.text.split(), dtype=float)) born_charges.append(atom_array) born_charges = np.array(born_charges) array_data.set_array('born_charges', born_charges) break except KeyError: pass except: pass # Use pymatgen vasp parser to get atomic forces and stress tensor vspr = Vasprun(self._out_folder.get_abs_path('vasprun.xml'), exception_on_bad_xml=False) # Get forces using pymatgen try: forces = np.array([vspr.ionic_steps[-1]['forces']]) array_data.set_array('forces', forces) except Exception, e: msg = ("Processing forces, " "with error Message:\n>> {}".format(e)) raise OutputParsingError(msg)
def create_supercells_with_displacements_using_phonopy(structure, phonopy_input): """ Create the supercells with the displacements to use the finite displacements methodology to calculate the force constants :param structure: Aiida StructureData Object :param phonopy_input: Aiida Parametersdata object containing a dictionary with the data needed to run phonopy: supercells matrix, primitive matrix and displacement distance. :return: dictionary of Aiida StructureData Objects containing the cells with displacements """ from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy import numpy as np # Generate phonopy phonon object bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonopy_input = phonopy_input.get_dict() phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive']) phonon.generate_displacements(distance=phonopy_input['distance']) cells_with_disp = phonon.get_supercells_with_displacements() # Transform cells to StructureData and set them ready to return data_sets = phonon.get_displacement_dataset() data_sets_object = ArrayData() for i, first_atoms in enumerate(data_sets['first_atoms']): data_sets_array = np.array([ first_atoms['direction'], first_atoms['number'], first_atoms['displacement'] ]) data_sets_object.set_array('data_sets_{}'.format(i), data_sets_array) disp_cells = {'data_sets': data_sets_object} for i, phonopy_supercell in enumerate(cells_with_disp): supercell = StructureData(cell=phonopy_supercell.get_cell()) for symbol, position in zip(phonopy_supercell.get_chemical_symbols(), phonopy_supercell.get_positions()): supercell.append_atom(position=position, symbols=symbol) disp_cells["structure_{}".format(i)] = supercell return disp_cells
def get_force_constants_from_phonopy(**kwargs): """ Calculate the force constants using phonopy :param kwargs: :return: """ from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy import numpy as np # print 'function',kwargs structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() # Generate phonopy phonon object bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive'], distance=phonopy_input['distance']) phonon.generate_displacements(distance=phonopy_input['distance']) # Build data_sets from forces of supercells with displacments data_sets = phonon.get_displacement_dataset() for i, first_atoms in enumerate(data_sets['first_atoms']): forces = kwargs.pop('forces_{}'.format(i)).get_array('forces')[0] first_atoms['forces'] = np.array(forces, dtype='double', order='c') # LOCAL calculation # Calculate and get force constants phonon.set_displacement_dataset(data_sets) phonon.produce_force_constants() force_constants = phonon.get_force_constants() array_data = ArrayData() array_data.set_array('force_constants', force_constants) return {'array_data': array_data}
def create_forces_set(**kwargs): # Build data_sets from forces of supercells with displacments import numpy as np data_set = kwargs.pop('data_sets') force_sets = ArrayData() for i in data_set.get_arraynames(): force_array = kwargs.pop(i.replace('data_sets', 'forces')).get_array('forces')[0] data_set_array = np.array([ data_set.get_array(i)[0], data_set.get_array(i)[1], data_set.get_array(i)[2], force_array ]) force_sets.set_array(i, data_set_array) return {'force_sets': force_sets}
def _aiida_ndb_hf(self, data): """ Save the data from ndb.HF_and_locXC """ pdata = ArrayData() pdata.set_array('Sx', numpy.array(data['Sx'])) pdata.set_array('Vxc', numpy.array(data['Vxc'])) return pdata
def parse_thermal_properties(filename): import yaml temperature = [] free_energy = [] entropy = [] cv = [] with open(filename, 'r') as stream: thermal_properties = dict(yaml.load(stream)) for tp in thermal_properties['thermal_properties']: temperature.append(tp['temperature']) entropy.append(tp['entropy']) free_energy.append(tp['free_energy']) cv.append(tp['heat_capacity']) tp_object = ArrayData() tp_object.set_array('temperature', np.array(temperature)) tp_object.set_array('free_energy', np.array(free_energy)) tp_object.set_array('entropy', np.array(entropy)) tp_object.set_array('heat_capacity', np.array(cv)) return tp_object
def parse_with_retrieved(self, retrieved): """ Parses the calculation-output datafolder, and stores results. :param retrieved: a dictionary of retrieved nodes, where the keys are the link names of retrieved nodes, and the values are the nodes. """ from aiida.common.exceptions import InvalidOperation from aiida.orm.data.array.trajectory import TrajectoryData from aiida.orm.data.array import ArrayData import os import numpy import copy successful = True # check if I'm not to overwrite anything #state = self._calc.get_state() #if state != calc_states.PARSING: # raise InvalidOperation("Calculation not in {} state" # .format(calc_states.PARSING) ) # look for eventual flags of the parser try: parser_opts = self._calc.inp.settings.get_dict()[ self.get_parser_settings_key()] except (AttributeError, KeyError): parser_opts = {} # load the pw input dictionary pw_input_dict = self._calc.inp.pw_parameters.get_dict() # load the pw input dictionary neb_input_dict = self._calc.inp.neb_parameters.get_dict() # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") successful = False return successful, () # check what is inside the folder list_of_files = out_folder.get_folder_list() # at least the stdout should exist if not self._calc._OUTPUT_FILE_NAME in list_of_files: self.logger.error("Standard output not found") successful = False return successful, () out_file = os.path.join(out_folder.get_abs_path('.'), self._calc._OUTPUT_FILE_NAME) # First parse the Neb output neb_out_dict, iteration_data, raw_successful = parse_raw_output_neb( out_file, neb_input_dict) # if calculation was not considered failed already, use the new value successful = raw_successful if successful else successful # Retrieve the number of images try: num_images = neb_input_dict['num_of_images'] except KeyError: try: num_images = neb_out_dict['num_of_images'] except KeyError: self.logger.error( "Impossible to understand the number of images") successful = False return successful, () # Now parse the information from the single pw calculations for the different images image_data = {} positions = [] cells = [] for i in range(num_images): # look for xml and parse xml_file = os.path.join(out_folder.get_abs_path('.'), self._calc._PREFIX + '_{}'.format(i + 1), self._calc._PREFIX + '.save', self._calc._DATAFILE_XML_BASENAME) try: with open(xml_file, 'r') as f: xml_lines = f.read() # Note: read() and not readlines() except IOError: self.logger.error( "No xml file found for image {} at {}".format( i + 1, xml_file)) successful = False return successful, () xml_data, structure_dict, bands_data = parse_pw_xml_output( xml_lines) # convert the dictionary obtained from parsing the xml to an AiiDA StructureData structure_data = convert_qe2aiida_structure(structure_dict) # look for pw output and parse it pw_out_file = os.path.join( out_folder.get_abs_path('.'), self._calc._PREFIX + '_{}'.format(i + 1), 'PW.out') try: with open(pw_out_file, 'r') as f: pw_out_lines = f.read() # Note: read() and not readlines() except IOError: self.logger.error( "No pw output file found for image {}".format(i + 1)) successful = False return successful, () pw_out_data, trajectory_data, critical_messages = parse_pw_text_output( pw_out_lines, xml_data, structure_dict, pw_input_dict) # I add in the out_data all the last elements of trajectory_data values. # Safe for some large arrays, that I will likely never query. skip_keys = [ 'forces', 'atomic_magnetic_moments', 'atomic_charges', 'lattice_vectors_relax', 'atomic_positions_relax', 'atomic_species_name' ] tmp_trajectory_data = copy.copy(trajectory_data) for x in tmp_trajectory_data.iteritems(): if x[0] in skip_keys: continue pw_out_data[x[0]] = x[1][-1] if len(x[1]) == 1: # delete eventual keys that are not arrays trajectory_data.pop(x[0]) # As the k points are an array that is rather large, and again it's not something I'm going to parse likely # since it's an info mainly contained in the input file, I move it to the trajectory data for key in ['k_points', 'k_points_weights']: try: trajectory_data[key] = xml_data.pop(key) except KeyError: pass key = 'pw_output_image_{}'.format(i + 1) image_data[key] = dict(pw_out_data.items() + xml_data.items()) positions.append([site.position for site in structure_data.sites]) cells.append(structure_data.cell) # If a warning was already present in the NEB, add also PW warnings to the neb output data, # avoiding repetitions. if neb_out_dict['warnings']: for warning in pw_out_data['warnings']: if warning not in neb_out_dict['warnings']: neb_out_dict['warnings'].append(warning) # Symbols can be obtained simply from the last image symbols = [str(site.kind_name) for site in structure_data.sites] new_nodes_list = [] # convert the dictionary into an AiiDA object output_params = ParameterData(dict=dict(neb_out_dict.items() + image_data.items())) # return it to the execmanager new_nodes_list.append((self.get_linkname_outparams(), output_params)) # convert data on structure of images into a TrajectoryData traj = TrajectoryData() traj.set_trajectory( stepids=numpy.arange(1, num_images + 1), cells=numpy.array(cells), symbols=numpy.array(symbols), positions=numpy.array(positions), ) # return it to the execmanager new_nodes_list.append((self.get_linkname_outtrajectory(), traj)) if parser_opts.get('all_iterations', False): if iteration_data: from aiida.orm.data.array import ArrayData arraydata = ArrayData() for x in iteration_data.iteritems(): arraydata.set_array(x[0], numpy.array(x[1])) new_nodes_list.append( (self.get_linkname_iterationarray(), arraydata)) # Load the original and interpolated energy profile along the minimum-energy path (mep) try: mep_file = os.path.join(out_folder.get_abs_path('.'), self._calc._PREFIX + '.dat') mep = numpy.loadtxt(mep_file) except Exception: self.logger.warning( "Impossible to find the file with image energies " "versus reaction coordinate.") mep = numpy.array([[]]) try: interp_mep_file = os.path.join(out_folder.get_abs_path('.'), self._calc._PREFIX + '.int') interp_mep = numpy.loadtxt(interp_mep_file) except Exception: self.logger.warning( "Impossible to find the file with the interpolation " "of image energies versus reaction coordinate.") interp_mep = numpy.array([[]]) # Create an ArrayData with the energy profiles mep_arraydata = ArrayData() mep_arraydata.set_array('mep', mep) mep_arraydata.set_array('interpolated_mep', interp_mep) new_nodes_list.append((self.get_linkname_meparray(), mep_arraydata)) return successful, new_nodes_list
def parse_with_retrieved(self, retrieved): """ Parses the datafolder, stores results. """ # suppose at the start that the job is successful successful = True # select the folder object # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") return False, () # check what is inside the folder list_of_files = out_folder.get_folder_list() # OUTPUT file should exist if not self._calc._OUTPUT_FILE_NAME in list_of_files: successful = False self.logger.error("Output file not found") return successful, () # Get file and do the parsing outfile = out_folder.get_abs_path(self._calc._OUTPUT_FILE_NAME) ouput_trajectory = out_folder.get_abs_path( self._calc._OUTPUT_TRAJECTORY_FILE_NAME) output_data, cell, stress_tensor = read_log_file(outfile) positions, forces, symbols, cell2 = read_lammps_positions_and_forces( ouput_trajectory) # look at warnings warnings = [] with open(out_folder.get_abs_path(self._calc._SCHED_ERROR_FILE)) as f: errors = f.read() if errors: warnings = [errors] # ====================== prepare the output node ====================== # save the outputs new_nodes_list = [] # save optimized structure into node structure = StructureData(cell=cell) for i, position in enumerate(positions[-1]): structure.append_atom(position=position.tolist(), symbols=symbols[i]) new_nodes_list.append(('output_structure', structure)) # save forces into node array_data = ArrayData() array_data.set_array('forces', forces) array_data.set_array('stress', stress_tensor) new_nodes_list.append(('output_array', array_data)) # add the dictionary with warnings output_data.update({'warnings': warnings}) parameters_data = ParameterData(dict=output_data) new_nodes_list.append((self.get_linkname_outparams(), parameters_data)) # add the dictionary with warnings # new_nodes_list.append((self.get_linkname_outparams(), ParameterData(dict={'warnings': warnings}))) return successful, new_nodes_list
def parse_with_retrieved(self, retrieved): """ Parse the output nodes for a PwCalculations from a dictionary of retrieved nodes. Two nodes that are expected are the default 'retrieved' FolderData node which will store the retrieved files permanently in the repository. The second required node is the unstored FolderData node with the temporary retrieved files, which should be passed under the key 'retrieved_temporary_folder_key' of the Parser class. :param retrieved: a dictionary of retrieved nodes """ import os successful = True # Load the input dictionary parameters = self._calc.inp.parameters.get_dict() # Look for optional settings input node and potential 'parser_options' dictionary within it try: settings = self._calc.inp.settings.get_dict() parser_opts = settings[self.get_parser_settings_key()] except (AttributeError, KeyError): settings = {} parser_opts = {} # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") return False, () # Verify that the retrieved_temporary_folder is within the arguments if temporary files were specified if self._calc._get_retrieve_temporary_list(): try: temporary_folder = retrieved[ self.retrieved_temporary_folder_key] dir_with_bands = temporary_folder.get_abs_path('.') except KeyError: self.logger.error( 'the {} was not passed as an argument'.format( self.retrieved_temporary_folder_key)) return False, () else: dir_with_bands = None list_of_files = out_folder.get_folder_list() # The stdout is required for parsing if self._calc._OUTPUT_FILE_NAME not in list_of_files: self.logger.error( "The standard output file '{}' was not found but is required". format(self._calc._OUTPUT_FILE_NAME)) return False, () # The xml file is required for parsing if self._calc._DATAFILE_XML_BASENAME not in list_of_files: self.logger.error( "The xml output file '{}' was not found but is required". format(self._calc._DATAFILE_XML_BASENAME)) successful = False xml_file = None else: xml_file = os.path.join(out_folder.get_abs_path('.'), self._calc._DATAFILE_XML_BASENAME) out_file = os.path.join(out_folder.get_abs_path('.'), self._calc._OUTPUT_FILE_NAME) # Call the raw parsing function parsing_args = [ out_file, parameters, parser_opts, xml_file, dir_with_bands ] out_dict, trajectory_data, structure_data, bands_data, raw_successful = parse_raw_output( *parsing_args) # If calculation was not considered failed already, use the new value successful = raw_successful if successful else successful # If the parser option 'all_symmetries' is not set to True, we reduce the raw parsed symmetries to safe space all_symmetries = parser_opts.get('all_symmetries', False) if not all_symmetries: # In the standard output, each symmetry operation print two rotation matrices: # # * S_cryst^T: matrix in crystal coordinates, transposed # * S_cart: matrix in cartesian coordinates, # # The XML files only print one matrix: # # * S_cryst: matrix in crystal coordinates # # The raw parsed symmetry information from the XML is large and will load the database heavily if stored as # is for each calculation. Instead, we will map these dictionaries onto a static dictionary of rotation # matrices generated by the _get_qe_symmetry_list static method. This dictionary will return the rotation # matrices in cartesian coordinates, i.e. S_cart. In order to compare the raw matrices from the XML to these # static matrices we have to convert S_cryst into S_cart. We derive here how that is done: # # S_cryst * v_cryst = v_cryst' # # where v_cryst' is the rotated vector v_cryst under S_cryst # We define `cell` where cell vectors are rows. Converting a vector from crystal to cartesian # coordinates is defined as: # # cell^T * v_cryst = v_cart # # The inverse of this operation is defined as # # v_cryst = cell^Tinv * v_cart # # Replacing the last equation into the first we find: # # S_cryst * cell^Tinv * v_cart = cell^Tinv * v_cart' # # Multiply on the left with cell^T gives: # # cell^T * S_cryst * cell^Tinv * v_cart = v_cart' # # which can be rewritten as: # # S_cart * v_cart = v_cart' # # where: # # S_cart = cell^T * S_cryst * cell^Tinv # # We compute here the transpose and its inverse of the structure cell basis, which is needed to transform # the parsed rotation matrices, which are in crystal coordinates, to cartesian coordinates, which are the # matrices that are returned by the _get_qe_symmetry_list staticmethod cell = structure_data['cell']['lattice_vectors'] cell_T = numpy.transpose(cell) cell_Tinv = numpy.linalg.inv(cell_T) try: if 'symmetries' in out_dict.keys(): old_symmetries = out_dict['symmetries'] new_symmetries = [] for this_sym in old_symmetries: name = this_sym['name'].strip() for i, this in enumerate(self._possible_symmetries): # Since we do an exact comparison we strip the string name from whitespace # and as soon as it is matched, we break to prevent it from matching another if name == this['name'].strip(): index = i break else: index = None self.logger.error( 'Symmetry {} not found'.format(name)) new_dict = {} if index is not None: # The raw parsed rotation matrix is in crystal coordinates, whereas the mapped rotation # in self._possible_symmetries is in cartesian coordinates. To allow them to be compared # to make sure we matched the correct rotation symmetry, we first convert the parsed matrix # to cartesian coordinates. For explanation of the method, see comment above. rotation_cryst = this_sym['rotation'] rotation_cart_new = self._possible_symmetries[ index]['matrix'] rotation_cart_old = numpy.dot( cell_T, numpy.dot(rotation_cryst, cell_Tinv)) inversion = self._possible_symmetries[index][ 'inversion'] if not are_matrices_equal( rotation_cart_old, rotation_cart_new, swap_sign_matrix_b=inversion): self.logger.error( 'Mapped rotation matrix {} does not match the original rotation {}' .format(rotation_cart_new, rotation_cart_old)) new_dict['all_symmetries'] = this_sym else: # Note: here I lose the information about equivalent ions and fractional_translation. new_dict['t_rev'] = this_sym['t_rev'] new_dict['symmetry_number'] = index else: new_dict['all_symmetries'] = this_sym new_symmetries.append(new_dict) out_dict[ 'symmetries'] = new_symmetries # and overwrite the old one except KeyError: # no symmetries were parsed (failed case, likely) self.logger.error("No symmetries were found in output") new_nodes_list = [] # I eventually save the new structure. structure_data is unnecessary after this in_struc = self._calc.get_inputs_dict()['structure'] type_calc = parameters['CONTROL']['calculation'] struc = in_struc if type_calc in ['relax', 'vc-relax', 'md', 'vc-md']: if 'cell' in structure_data.keys(): struc = convert_qe2aiida_structure(structure_data, input_structure=in_struc) new_nodes_list.append( (self.get_linkname_outstructure(), struc)) k_points_list = trajectory_data.pop('k_points', None) k_points_weights_list = trajectory_data.pop('k_points_weights', None) if k_points_list is not None: # Build the kpoints object if out_dict['k_points_units'] not in ['2 pi / Angstrom']: raise QEOutputParsingError( 'Error in kpoints units (should be cartesian)') kpoints_from_output = KpointsData() kpoints_from_output.set_cell_from_structure(struc) kpoints_from_output.set_kpoints(k_points_list, cartesian=True, weights=k_points_weights_list) kpoints_from_input = self._calc.inp.kpoints if not bands_data: try: kpoints_from_input.get_kpoints() except AttributeError: new_nodes_list += [(self.get_linkname_out_kpoints(), kpoints_from_output)] # Converting bands into a BandsData object (including the kpoints) if bands_data: kpoints_for_bands = kpoints_from_output try: kpoints_from_input.get_kpoints() kpoints_for_bands.labels = kpoints_from_input.labels except (AttributeError, ValueError, TypeError): # AttributeError: no list of kpoints in input # ValueError: labels from input do not match the output # list of kpoints (some kpoints are missing) # TypeError: labels are not set, so kpoints_from_input.labels=None pass # Get the bands occupations and correct the occupations of QE: # If it computes only one component, it occupies it with half number of electrons try: bands_data['occupations'][1] the_occupations = bands_data['occupations'] except IndexError: the_occupations = 2. * numpy.array( bands_data['occupations'][0]) try: bands_data['bands'][1] bands_energies = bands_data['bands'] except IndexError: bands_energies = bands_data['bands'][0] the_bands_data = BandsData() the_bands_data.set_kpointsdata(kpoints_for_bands) the_bands_data.set_bands(bands_energies, units=bands_data['bands_units'], occupations=the_occupations) new_nodes_list += [('output_band', the_bands_data)] out_dict['linknames_band'] = ['output_band'] # Separate the atomic_occupations dictionary in its own node if it is present atomic_occupations = out_dict.get('atomic_occupations', {}) if atomic_occupations: out_dict.pop('atomic_occupations') atomic_occupations_node = ParameterData(dict=atomic_occupations) new_nodes_list.append( ('output_atomic_occupations', atomic_occupations_node)) output_params = ParameterData(dict=out_dict) new_nodes_list.append((self.get_linkname_outparams(), output_params)) if trajectory_data: from aiida.orm.data.array.trajectory import TrajectoryData from aiida.orm.data.array import ArrayData try: positions = numpy.array( trajectory_data.pop('atomic_positions_relax')) try: cells = numpy.array( trajectory_data.pop('lattice_vectors_relax')) # if KeyError, the MD was at fixed cell except KeyError: cells = numpy.array([in_struc.cell] * len(positions)) symbols = numpy.array( [str(i.kind_name) for i in in_struc.sites]) stepids = numpy.arange( len(positions)) # a growing integer per step # I will insert time parsing when they fix their issues about time # printing (logic is broken if restart is on) traj = TrajectoryData() traj.set_trajectory( stepids=stepids, cells=cells, symbols=symbols, positions=positions, ) for x in trajectory_data.iteritems(): traj.set_array(x[0], numpy.array(x[1])) new_nodes_list.append( (self.get_linkname_outtrajectory(), traj)) except KeyError: # forces, atomic charges and atomic mag. moments, in scf calculation (when outputed) arraydata = ArrayData() for x in trajectory_data.iteritems(): arraydata.set_array(x[0], numpy.array(x[1])) new_nodes_list.append( (self.get_linkname_outarray(), arraydata)) return successful, new_nodes_list
def setUp(self): self.a = ArrayData() self.a.set_array('test_array', np.array([0, 1, 3])) self.a.store() self.cli_runner = CliRunner()
def _aiida_array(self, data): arraydata = ArrayData() for ky in data.keys(): arraydata.set_array(ky, data[ky]) return arraydata
def get_properties_from_phonopy(structure, phonopy_input, force_constants): """ Calculate DOS and thermal properties using phonopy (locally) :param structure: Aiida StructureData Object :param phonopy_input: Aiida Parametersdata object containing a dictionary with the data needed to run phonopy: supercells matrix, primitive matrix and q-points mesh. :param force_constants: :return: """ from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy # Generate phonopy phonon object bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonopy_input = phonopy_input.get_dict() force_constants = force_constants.get_array('force_constants') phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive']) phonon.set_force_constants(force_constants) #Normalization factor primitive to unit cell normalization_factor = phonon.unitcell.get_number_of_atoms( ) / phonon.primitive.get_number_of_atoms() phonon.set_mesh(phonopy_input['mesh'], is_eigenvectors=True, is_mesh_symmetry=False) phonon.set_total_DOS() phonon.set_partial_DOS() # get DOS (normalized to unit cell) total_dos = phonon.get_total_DOS() * normalization_factor partial_dos = phonon.get_partial_DOS() * normalization_factor # Stores DOS data in DB as a workflow result dos = ArrayData() dos.set_array('frequency', total_dos[0]) dos.set_array('total_dos', total_dos[1]) dos.set_array('partial_dos', partial_dos[1]) #THERMAL PROPERTIES (per primtive cell) phonon.set_thermal_properties() t, free_energy, entropy, cv = phonon.get_thermal_properties() # Stores thermal properties (per unit cell) data in DB as a workflow result thermal_properties = ArrayData() thermal_properties.set_array('temperature', t) thermal_properties.set_array('free_energy', free_energy * normalization_factor) thermal_properties.set_array('entropy', entropy * normalization_factor) thermal_properties.set_array('cv', cv * normalization_factor) return {'thermal_properties': thermal_properties, 'dos': dos}
def parse_with_retrieved(self, retrieved): """ Receives in input a dictionary of retrieved nodes. Does all the logic here. """ from aiida.common.exceptions import InvalidOperation import os import glob successful = True # check if I'm not to overwrite anything #state = self._calc.get_state() #if state != calc_states.PARSING: # raise InvalidOperation("Calculation not in {} state" # .format(calc_states.PARSING) ) # look for eventual flags of the parser try: parser_opts = self._calc.inp.settings.get_dict()[ self.get_parser_settings_key()] except (AttributeError, KeyError): parser_opts = {} # load the input dictionary # TODO: pass this input_dict to the parser. It might need it. input_dict = self._calc.inp.parameters.get_dict() # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") return False, () # check what is inside the folder list_of_files = out_folder.get_folder_list() # at least the stdout should exist if not self._calc._OUTPUT_FILE_NAME in list_of_files: self.logger.error("Standard output not found") successful = False return successful, () # if there is something more, I note it down, so to call the raw parser # with the right options # look for xml has_xml = False if self._calc._DATAFILE_XML_BASENAME in list_of_files: has_xml = True # look for bands has_bands = False if glob.glob(os.path.join(out_folder.get_abs_path('.'), 'K*[0-9]')): # Note: assuming format of kpoints subfolder is K*[0-9] has_bands = True # TODO: maybe it can be more general than bands only? out_file = os.path.join(out_folder.get_abs_path('.'), self._calc._OUTPUT_FILE_NAME) xml_file = os.path.join(out_folder.get_abs_path('.'), self._calc._DATAFILE_XML_BASENAME) dir_with_bands = out_folder.get_abs_path('.') # call the raw parsing function parsing_args = [out_file, input_dict, parser_opts] if has_xml: parsing_args.append(xml_file) if has_bands: if not has_xml: self.logger.warning("Cannot parse bands if xml file not " "found") else: parsing_args.append(dir_with_bands) out_dict, trajectory_data, structure_data, bands_data, raw_successful = parse_raw_output( *parsing_args) # if calculation was not considered failed already, use the new value successful = raw_successful if successful else successful # The symmetry info has large arrays, that occupy most of the database. # turns out most of this is due to 64 matrices that are repeated over and over again. # therefore I map part of the results in a list of dictionaries wrote here once and for all # if the parser_opts has a key all_symmetries set to True, I don't reduce it all_symmetries = parser_opts.get('all_symmetries', False) if not all_symmetries: try: if 'symmetries' in out_dict.keys(): old_symmetries = out_dict['symmetries'] new_symmetries = [] for this_sym in old_symmetries: name = this_sym['name'] index = None for i, this in enumerate(self._possible_symmetries): if name in this['name']: index = i if index is None: self.logger.error( "Symmetry {} not found".format(name)) new_dict = {} # note: here I lose the information about equivalent # ions and fractional_translation. # They will be present with all_symmetries=True new_dict['t_rev'] = this_sym['t_rev'] new_dict['symmetry_number'] = index new_symmetries.append(new_dict) out_dict[ 'symmetries'] = new_symmetries # and overwrite the old one except KeyError: # no symmetries were parsed (failed case, likely) self.logger.error("No symmetries were found in output") new_nodes_list = [] # I eventually save the new structure. structure_data is unnecessary after this in_struc = self._calc.get_inputs_dict()['structure'] type_calc = input_dict['CONTROL']['calculation'] struc = in_struc if type_calc in ['relax', 'vc-relax', 'md', 'vc-md']: if 'cell' in structure_data.keys(): struc = convert_qe2aiida_structure(structure_data, input_structure=in_struc) new_nodes_list.append( (self.get_linkname_outstructure(), struc)) k_points_list = trajectory_data.pop('k_points', None) k_points_weights_list = trajectory_data.pop('k_points_weights', None) if k_points_list is not None: # build the kpoints object if out_dict['k_points_units'] not in ['2 pi / Angstrom']: raise QEOutputParsingError( 'Error in kpoints units (should be cartesian)') # converting bands into a BandsData object (including the kpoints) kpoints_from_output = KpointsData() kpoints_from_output.set_cell_from_structure(struc) kpoints_from_output.set_kpoints(k_points_list, cartesian=True, weights=k_points_weights_list) kpoints_from_input = self._calc.inp.kpoints if not bands_data: try: kpoints_from_input.get_kpoints() except AttributeError: new_nodes_list += [(self.get_linkname_out_kpoints(), kpoints_from_output)] if bands_data: import numpy # converting bands into a BandsData object (including the kpoints) kpoints_for_bands = kpoints_from_output try: kpoints_from_input.get_kpoints() kpoints_for_bands.labels = kpoints_from_input.labels except (AttributeError, ValueError, TypeError): # AttributeError: no list of kpoints in input # ValueError: labels from input do not match the output # list of kpoints (some kpoints are missing) # TypeError: labels are not set, so kpoints_from_input.labels=None pass # get the bands occupations. # correct the occupations of QE: if it computes only one component, # it occupies it with half number of electrons try: bands_data['occupations'][1] the_occupations = bands_data['occupations'] except IndexError: the_occupations = 2. * numpy.array( bands_data['occupations'][0]) try: bands_data['bands'][1] bands_energies = bands_data['bands'] except IndexError: bands_energies = bands_data['bands'][0] the_bands_data = BandsData() the_bands_data.set_kpointsdata(kpoints_for_bands) the_bands_data.set_bands(bands_energies, units=bands_data['bands_units'], occupations=the_occupations) new_nodes_list += [('output_band', the_bands_data)] out_dict['linknames_band'] = ['output_band'] # convert the dictionary into an AiiDA object output_params = ParameterData(dict=out_dict) # return it to the execmanager new_nodes_list.append((self.get_linkname_outparams(), output_params)) if trajectory_data: import numpy from aiida.orm.data.array.trajectory import TrajectoryData from aiida.orm.data.array import ArrayData try: positions = numpy.array( trajectory_data.pop('atomic_positions_relax')) try: cells = numpy.array( trajectory_data.pop('lattice_vectors_relax')) # if KeyError, the MD was at fixed cell except KeyError: cells = numpy.array([in_struc.cell] * len(positions)) symbols = numpy.array( [str(i.kind_name) for i in in_struc.sites]) stepids = numpy.arange( len(positions)) # a growing integer per step # I will insert time parsing when they fix their issues about time # printing (logic is broken if restart is on) traj = TrajectoryData() traj.set_trajectory( stepids=stepids, cells=cells, symbols=symbols, positions=positions, ) for x in trajectory_data.iteritems(): traj.set_array(x[0], numpy.array(x[1])) # return it to the execmanager new_nodes_list.append( (self.get_linkname_outtrajectory(), traj)) except KeyError: # forces, atomic charges and atomic mag. moments, in scf # calculation (when outputed) arraydata = ArrayData() for x in trajectory_data.iteritems(): arraydata.set_array(x[0], numpy.array(x[1])) # return it to the execmanager new_nodes_list.append( (self.get_linkname_outarray(), arraydata)) return successful, new_nodes_list
def parse_with_retrieved(self, retrieved): """ Receives in input a dictionary of retrieved nodes. Does all the logic here. """ from aiida.common.exceptions import InvalidOperation import os import glob successful = True # check if I'm not to overwrite anything #state = self._calc.get_state() #if state != calc_states.PARSING: # raise InvalidOperation("Calculation not in {} state" # .format(calc_states.PARSING) ) # retrieve the input parameter calc_input = self._calc.inp.parameters # look for eventual flags of the parser try: parser_opts = self._calc.inp.settings.get_dict()[ self.get_parser_settings_key()] except (AttributeError, KeyError): parser_opts = {} # load the input dictionary # TODO: pass this input_dict to the parser. It might need it. input_dict = self._calc.inp.parameters.get_dict() # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") return False, () # check what is inside the folder list_of_files = out_folder.get_folder_list() # at least the stdout should exist if not self._calc._OUTPUT_FILE_NAME in list_of_files: self.logger.error("Standard output not found") successful = False return successful, () # if there is something more, I note it down, so to call the raw parser # with the right options # look for xml has_xml = False if self._calc._DATAFILE_XML_BASENAME in list_of_files: has_xml = True # look for bands has_bands = False if glob.glob(os.path.join(out_folder.get_abs_path('.'), 'K*[0-9]')): # Note: assuming format of kpoints subfolder is K*[0-9] has_bands = True # TODO: maybe it can be more general than bands only? out_file = os.path.join(out_folder.get_abs_path('.'), self._calc._OUTPUT_FILE_NAME) xml_file = os.path.join(out_folder.get_abs_path('.'), self._calc._DATAFILE_XML_BASENAME) dir_with_bands = out_folder.get_abs_path('.') # call the raw parsing function parsing_args = [out_file, input_dict, parser_opts] if has_xml: parsing_args.append(xml_file) if has_bands: if not has_xml: self.logger.warning("Cannot parse bands if xml file not " "found") else: parsing_args.append(dir_with_bands) out_dict, trajectory_data, structure_data, raw_successful = parse_raw_output( *parsing_args) # if calculation was not considered failed already, use the new value successful = raw_successful if successful else successful new_nodes_list = [] # I eventually save the new structure. structure_data is unnecessary after this in_struc = self._calc.get_inputs_dict()['structure'] type_calc = input_dict['CONTROL']['calculation'] struc = in_struc if type_calc in ['relax', 'vc-relax', 'md', 'vc-md']: if 'cell' in structure_data.keys(): struc = convert_qe2aiida_structure(structure_data, input_structure=in_struc) new_nodes_list.append( (self.get_linkname_outstructure(), struc)) k_points_list = trajectory_data.pop('k_points', None) k_points_weights_list = trajectory_data.pop('k_points_weights', None) if k_points_list is not None: # build the kpoints object if out_dict['k_points_units'] not in ['2 pi / Angstrom']: raise QEOutputParsingError( 'Error in kpoints units (should be cartesian)') # converting bands into a BandsData object (including the kpoints) kpoints_from_output = KpointsData() kpoints_from_output.set_cell_from_structure(struc) kpoints_from_output.set_kpoints(k_points_list, cartesian=True, weights=k_points_weights_list) kpoints_from_input = self._calc.inp.kpoints try: kpoints_from_input.get_kpoints() except AttributeError: new_nodes_list += [(self.get_linkname_out_kpoints(), kpoints_from_output)] # convert the dictionary into an AiiDA object output_params = ParameterData(dict=out_dict) # return it to the execmanager new_nodes_list.append((self.get_linkname_outparams(), output_params)) if trajectory_data: import numpy from aiida.orm.data.array.trajectory import TrajectoryData from aiida.orm.data.array import ArrayData try: positions = numpy.array( trajectory_data.pop('atomic_positions_relax')) try: cells = numpy.array( trajectory_data.pop('lattice_vectors_relax')) # if KeyError, the MD was at fixed cell except KeyError: cells = numpy.array([in_struc.cell] * len(positions)) symbols = numpy.array( [str(i.kind_name) for i in in_struc.sites]) stepids = numpy.arange( len(positions)) # a growing integer per step # I will insert time parsing when they fix their issues about time # printing (logic is broken if restart is on) traj = TrajectoryData() traj.set_trajectory( stepids=stepids, cells=cells, symbols=symbols, positions=positions, ) for x in trajectory_data.iteritems(): traj.set_array(x[0], numpy.array(x[1])) # return it to the execmanager new_nodes_list.append( (self.get_linkname_outtrajectory(), traj)) except KeyError: # forces in scf calculation (when outputed) arraydata = ArrayData() for x in trajectory_data.iteritems(): arraydata.set_array(x[0], numpy.array(x[1])) # return it to the execmanager new_nodes_list.append( (self.get_linkname_outarray(), arraydata)) return successful, new_nodes_list
def execute(args): """ The main execution of the script, which will run some preliminary checks on the command line arguments before passing them to the workchain and running it """ try: code = Code.get_from_string(args.codename) except NotExistent as exception: print "Execution failed: could not retrieve the code '{}'".format( args.codename) print "Exception report: {}".format(exception) return try: vibra_code = Code.get_from_string(args.vibra_codename) except NotExistent as exception: print "Execution failed: could not retrieve the code '{}'".format( args.stm_codename) print "Exception report: {}".format(exception) return protocol = Str(args.protocol) # Structure. Bulk silicon SuperCell_1 = 1 SuperCell_2 = 1 SuperCell_3 = 1 scnumbers = np.array([SuperCell_1, SuperCell_2, SuperCell_3]) scarray = ArrayData() scarray.set_array('sca', scnumbers) alat = 5.43 # Angstrom. Not passed to the fdf file (only for internal use) cell = [[ 0., alat / 2, alat / 2, ], [ alat / 2, 0., alat / 2, ], [ alat / 2, alat / 2, 0., ]] pf = alat * 0.125 na = 2 x0 = [[pf, pf, pf], [-pf, -pf, -pf]] s1 = StructureData(cell=cell) for i in range(na): s1.append_atom(position=(x0[i][0], x0[i][1], x0[i][2]), symbols=['Si']) bandskpoints = KpointsData() kpp = [(1, 1., 1., 1.), (15, 0., 0.5, 0.5), (25, 0., 0., 0.), (20, 0.5, 0.5, 0.5), (20, 0., 0.5, 0.5), (15, 0.25, 0.5, 0.75), (20, 0.5, 0.5, 0.5)] lpp = [[0, '\Gamma'], [1, 'X'], [2, '\Gamma'], [3, 'L'], [4, 'X'], [5, 'W'], [6, 'L']] bandskpoints.set_cell(s1.cell, s1.pbc) bandskpoints.set_kpoints(kpp, labels=lpp) if args.structure > 0: structure = load_node(args.structure) else: structure = s1 run(SiestaVibraWorkChain, code=code, vibra_code=vibra_code, scarray=scarray, structure=structure, protocol=protocol, bandskpoints=bandskpoints)
def _aiida_bands_data(self, data, cell, kpoints_dict): if not data: return False kpt_idx = sorted(data.keys()) # list of kpoint indices try: k_list = [kpoints_dict[i] for i in kpt_idx] # list of k-point triplet except KeyError: # kpoint triplets are not present (true for .qp and so on, can not use BandsData) # We use the internal Yambo Format [ [Eo_1, Eo_2,... ], ...[So_1,So_2,] ] # QP_TABLE [[ib_1,ik_1,isp_1] ,[ib_n,ik_n,isp_n]] # Each entry in DATA has corresponding legend in QP_TABLE that defines its details # like ib= Band index, ik= kpoint index, isp= spin polarization index. # Eo_1 => at ib_1, ik_1 isp_1. pdata = ArrayData() QP_TABLE = [] ORD = [] Eo = [] E_minus_Eo = [] So = [] Z = [] for ky in data.keys(): # kp == kpoint index as a string 1,2,.. for ind in range(len(data[ky]['Band'])): try: Eo.append(data[ky]['Eo'][ind]) except KeyError: pass try: E_minus_Eo.append(data[ky]['E-Eo'][ind]) except KeyError: pass try: So.append(data[ky]['Sc|Eo'][ind]) except KeyError: pass try: Z.append(data[ky]['Z'][ind]) except KeyError: pass ik = int(ky) ib = data[ky]['Band'][ind] isp = 0 if 'Spin_Pol' in data[ky].keys(): isp = data[ky]['Spin_Pol'][ind] QP_TABLE.append([ik, ib, isp]) pdata.set_array('Eo', numpy.array(Eo)) pdata.set_array('E_minus_Eo', numpy.array(E_minus_Eo)) pdata.set_array('So', numpy.array(So)) pdata.set_array('Z', numpy.array(Z)) pdata.set_array('qp_table', numpy.array(QP_TABLE)) return pdata quasiparticle_bands = BandsData() quasiparticle_bands.set_cell(cell) quasiparticle_bands.set_kpoints(k_list, cartesian=True) # labels will come from any of the keys in the nested kp_point data, # there is a uniform set of observables for each k-point, ie Band, Eo, ... # ***FIXME BUG does not seem to handle spin polarizes at all when constructing bandsdata*** bands_labels = [ legend for legend in sorted(data[data.keys()[0]].keys()) ] append_list = [[] for i in bands_labels] for kp in kpt_idx: for i in range(len(bands_labels)): append_list[i].append(data[kp][bands_labels[i]]) generalised_bands = [numpy.array(it) for it in append_list] quasiparticle_bands.set_bands(bands=generalised_bands, units='eV', labels=bands_labels) return quasiparticle_bands
def parse_from_calc(self): """ Parses the datafolder, stores results. This parser for this simple code does simply store in the DB a node representing the file of forces in real space """ from aiida.common.exceptions import InvalidOperation from aiida.common import aiidalogger from aiida.djsite.utils import get_dblogger_extra import ase, ase.io parserlogger = aiidalogger.getChild('aseparser') logger_extra = get_dblogger_extra(self._calc) # suppose at the start that the job is successful successful = True # check that calculation is in the right state state = self._calc.get_state() if state != calc_states.PARSING: raise InvalidOperation("Calculation not in {} state".format( calc_states.PARSING)) # select the folder object out_folder = self._calc.get_retrieved_node() # check what is inside the folder list_of_files = out_folder.get_folder_list() # at least the stdout should exist if not self._calc._OUTPUT_FILE_NAME in list_of_files: successful = False parserlogger.error("Standard output not found", extra=logger_extra) return successful, () # output structure has_out_atoms = True if self._calc._output_aseatoms in list_of_files else False if has_out_atoms: out_atoms = ase.io.read( out_folder.get_abs_path(self._calc._output_aseatoms)) out_structure = StructureData().set_ase(out_atoms) # load the results dictionary json_outfile = out_folder.get_abs_path(self._calc._OUTPUT_FILE_NAME) with open(json_outfile, 'r') as f: json_params = json.load(f) # extract arrays from json_params dictionary_array = {} for k, v in list(json_params.iteritems()): if isinstance(v, (list, tuple)): dictionary_array[k] = json_params.pop(k) # look at warnings warnings = [] with open(out_folder.get_abs_path(self._calc._SCHED_ERROR_FILE)) as f: errors = f.read() if errors: warnings = [errors] json_params['warnings'] = warnings # save the outputs new_nodes_list = [] # save the arrays if dictionary_array: array_data = ArrayData() for k, v in dictionary_array.iteritems(): array_data.set_array(k, numpy.array(v)) new_nodes_list.append((self._outarray_name, array_data)) # save the parameters if json_params: parameter_data = ParameterData(dict=json_params) new_nodes_list.append((self._outdict_name, parameter_data)) if has_out_atoms: structure_data = StructureData() new_nodes_list.append((self._outstruc_name, structure_data)) return successful, new_nodes_list
def _sigma_c(self, ndbqp, ndbhf): """ Calculate S_c if missing from information parsed from the ndb.* Sc = 1/Z[ E-Eo] -S_x + Vxc """ Eo = numpy.array(ndbqp['E-Eo']) Z = numpy.array(ndbqp['Z']) E_minus_Eo = numpy.array(ndbqp['E-Eo']) Sx = numpy.array(ndbhf['Sx']) Vxc = numpy.array(ndbhf['Vxc']) try: Sc = numpy.array(ndbqp['So']) except KeyError: Sc = 1 / Z * E_minus_Eo - Sx + Vxc pdata = ArrayData() pdata.set_array('Eo', Eo) pdata.set_array('E_minus_Eo', E_minus_Eo) pdata.set_array('Z', Z) pdata.set_array('Sx', Sx) pdata.set_array('Sc', Sc) pdata.set_array('Vxc', Vxc) pdata.set_array('qp_table', numpy.array(ndbqp['qp_table'])) return pdata
def get_stm_data(self, plot_path): """ Parses the STM plot file to get an Array object with X, Y, and Z arrays in the 'meshgrid' setting, as in the example code: import numpy as np xlist = np.linspace(-3.0, 3.0, 3) ylist = np.linspace(-3.0, 3.0, 4) X, Y = np.meshgrid(xlist, ylist) Z = np.sqrt(X**2 + Y**2) X: [[-3. 0. 3.] [-3. 0. 3.] [-3. 0. 3.] [-3. 0. 3.]] Y: [[-3. -3. -3.] [-1. -1. -1.] [ 1. 1. 1.] [ 3. 3. 3.]] Z: [[ 4.24264069 3. 4.24264069] [ 3.16227766 1. 3.16227766] [ 3.16227766 1. 3.16227766] [ 4.24264069 3. 4.24264069]] These can then be used in matplotlib to get a contour plot. """ import numpy as np from itertools import groupby from aiida.common.exceptions import InputValidationError from aiida.common.exceptions import ValidationError file = open(plot_path, "r") # aiida.CH.STM or aiida.CC.STM... data = file.read().split('\n') data = [i.split() for i in data] # The data in the file is organized in "lines" parallel to the Y axes # (that is, for constant X) separated by blank lines. # In the following we use the 'groupby' function to get at the individual # blocks one by one, and set the appropriate arrays. # I am not sure about the mechanics of groupby, # so repeat xx = [] yy = [] zz = [] # # Function to separate the blocks h = lambda x: len(x) == 0 # for k, g in groupby(data, h): if not k: xx.append([i[0] for i in g]) for k, g in groupby(data, h): if not k: yy.append([i[1] for i in g]) for k, g in groupby(data, h): if not k: zz.append([i[2] for i in g]) # Now, transpose, since x runs fastest in our fortran code, # the opposite convention of the meshgrid paradigm. X = np.array(xx, dtype=float).transpose() Y = np.array(yy, dtype=float).transpose() Z = np.array(zz, dtype=float).transpose() from aiida.orm.data.array import ArrayData arraydata = ArrayData() arraydata.set_array('X', np.array(X)) arraydata.set_array('Y', np.array(Y)) arraydata.set_array('Z', np.array(Z)) return arraydata
def _get_output_nodes(self, output_path, messages_path, xml_path, json_path, bands_path): """ Extracts output nodes from the standard output and standard error files. (And XML and JSON files) """ from aiida.orm.data.array.trajectory import TrajectoryData import re parser_version = 'aiida-0.11.0--plugin-0.11.5' parser_info = {} parser_info['parser_info'] = 'AiiDA Siesta Parser V. {}'.format( parser_version) parser_info['parser_warnings'] = [] result_list = [] if xml_path is None: self.logger.error("Could not find a CML file to parse") # NOTE aiida.xml is not there? raise SiestaOutputParsingError( "Could not find a CML file to parse") # We get everything from the CML file xmldoc = get_parsed_xml_doc(xml_path) if xmldoc is None: self.logger.error("Malformed CML file: cannot parse") raise SiestaCMLParsingError("Malformed CML file: cannot parse") # These are examples of how we can access input items # # Structure (mandatory) # in_struc = self._calc.get_inputs_dict()['structure'] # # Settings (optional) # try: in_settings = self._calc.get_inputs_dict()['settings'] except KeyError: in_settings = None result_dict = get_dict_from_xml_doc(xmldoc) # Add timing information if json_path is None: self.logger.info("Could not find a time.json file to parse") else: from json_time import get_timing_info global_time, timing_decomp = get_timing_info(json_path) if global_time is None: self.logger.info("Cannot fully parse the time.json file") else: result_dict["global_time"] = global_time result_dict["timing_decomposition"] = timing_decomp # Add warnings successful = True if messages_path is None: # Perhaps using an old version of Siesta warnings_list = ['WARNING: No MESSAGES file...'] else: successful, warnings_list = self.get_warnings_from_file( messages_path) result_dict["warnings"] = warnings_list # Add parser info dictionary parsed_dict = dict(result_dict.items() + parser_info.items()) output_data = ParameterData(dict=parsed_dict) link_name = self.get_linkname_outparams() result_list.append((link_name, output_data)) # If the structure has changed, save it if is_variable_geometry(xmldoc): # Get the input structure to copy its site names, # as the CML file traditionally contained only the # atomic symbols. # struc = get_last_structure(xmldoc, in_struc) result_list.append((self.get_linkname_outstructure(), struc)) # Save forces and stress in an ArrayData object forces, stress = get_final_forces_and_stress(xmldoc) if forces is not None and stress is not None: from aiida.orm.data.array import ArrayData arraydata = ArrayData() arraydata.set_array('forces', np.array(forces)) arraydata.set_array('stress', np.array(stress)) result_list.append((self.get_linkname_outarray(), arraydata)) # Parse band-structure information if available if bands_path is not None: bands, coords = self.get_bands(bands_path) from aiida.orm.data.array.bands import BandsData arraybands = BandsData() arraybands.set_kpoints( self._calc.inp.bandskpoints.get_kpoints(cartesian=True)) arraybands.set_bands(bands, units="eV") result_list.append((self.get_linkname_bandsarray(), arraybands)) bandsparameters = ParameterData(dict={"kp_coordinates": coords}) result_list.append( (self.get_linkname_bandsparameters(), bandsparameters)) return successful, result_list
def parse_with_retrieved(self, retrieved): """ Parse the output nodes for a PwCalculations from a dictionary of retrieved nodes. Two nodes that are expected are the default 'retrieved' FolderData node which will store the retrieved files permanently in the repository. The second required node is the unstored FolderData node with the temporary retrieved files, which should be passed under the key 'retrieved_temporary_folder_key' of the Parser class. :param retrieved: a dictionary of retrieved nodes """ import os import numpy successful = True # Load the input dictionary parameters = self._calc.inp.parameters.get_dict() # Look for optional settings input node and potential 'parser_options' dictionary within it try: settings = self._calc.inp.settings.get_dict() parser_opts = settings[self.get_parser_settings_key()] except (AttributeError, KeyError): settings = {} parser_opts = {} # Check that the retrieved folder is there try: out_folder = retrieved[self._calc._get_linkname_retrieved()] except KeyError: self.logger.error("No retrieved folder found") return False, () # Verify that the retrieved_temporary_folder is within the arguments if temporary files were specified if self._calc._get_retrieve_temporary_list(): try: temporary_folder = retrieved[self.retrieved_temporary_folder_key] dir_with_bands = temporary_folder.get_abs_path('.') except KeyError: self.logger.error('the {} was not passed as an argument'.format(self.retrieved_temporary_folder_key)) return False, () else: dir_with_bands = None list_of_files = out_folder.get_folder_list() # The stdout is required for parsing if not self._calc._OUTPUT_FILE_NAME in list_of_files: self.logger.error("The standard output file '{}' was not found but is required".format(self._calc._OUTPUT_FILE_NAME)) return False, () # The xml file is required for parsing if not self._calc._DATAFILE_XML_BASENAME in list_of_files: self.logger.error("The xml output file '{}' was not found but is required".format(self._calc._DATAFILE_XML_BASENAME)) successful = False xml_file = None else: xml_file = os.path.join(out_folder.get_abs_path('.'), self._calc._DATAFILE_XML_BASENAME) out_file = os.path.join(out_folder.get_abs_path('.'), self._calc._OUTPUT_FILE_NAME) # Call the raw parsing function parsing_args = [out_file, parameters, parser_opts, xml_file, dir_with_bands] out_dict, trajectory_data, structure_data, bands_data, raw_successful = parse_raw_output(*parsing_args) # If calculation was not considered failed already, use the new value successful = raw_successful if successful else successful # The symmetry info has large arrays, that occupy most of the database. # turns out most of this is due to 64 matrices that are repeated over and over again. # therefore I map part of the results in a list of dictionaries wrote here once and for all # if the parser_opts has a key all_symmetries set to True, I don't reduce it all_symmetries = parser_opts.get('all_symmetries', False) if not all_symmetries: try: if 'symmetries' in out_dict.keys(): old_symmetries = out_dict['symmetries'] new_symmetries = [] for this_sym in old_symmetries: name = this_sym['name'] index = None for i,this in enumerate(self._possible_symmetries): if name in this['name']: index = i if index is None: self.logger.error("Symmetry {} not found".format(name)) new_dict = {} # note: here I lose the information about equivalent # ions and fractional_translation. # They will be present with all_symmetries=True new_dict['t_rev'] = this_sym['t_rev'] new_dict['symmetry_number'] = index new_symmetries.append(new_dict) out_dict['symmetries'] = new_symmetries # and overwrite the old one except KeyError: # no symmetries were parsed (failed case, likely) self.logger.error("No symmetries were found in output") new_nodes_list = [] # I eventually save the new structure. structure_data is unnecessary after this in_struc = self._calc.get_inputs_dict()['structure'] type_calc = parameters['CONTROL']['calculation'] struc = in_struc if type_calc in ['relax', 'vc-relax', 'md', 'vc-md']: if 'cell' in structure_data.keys(): struc = convert_qe2aiida_structure(structure_data, input_structure=in_struc) new_nodes_list.append((self.get_linkname_outstructure(), struc)) k_points_list = trajectory_data.pop('k_points', None) k_points_weights_list = trajectory_data.pop('k_points_weights', None) if k_points_list is not None: # Build the kpoints object if out_dict['k_points_units'] not in ['2 pi / Angstrom']: raise QEOutputParsingError('Error in kpoints units (should be cartesian)') kpoints_from_output = KpointsData() kpoints_from_output.set_cell_from_structure(struc) kpoints_from_output.set_kpoints(k_points_list, cartesian=True, weights=k_points_weights_list) kpoints_from_input = self._calc.inp.kpoints if not bands_data: try: kpoints_from_input.get_kpoints() except AttributeError: new_nodes_list += [(self.get_linkname_out_kpoints(), kpoints_from_output)] # Converting bands into a BandsData object (including the kpoints) if bands_data: kpoints_for_bands = kpoints_from_output try: kpoints_from_input.get_kpoints() kpoints_for_bands.labels = kpoints_from_input.labels except (AttributeError, ValueError, TypeError): # AttributeError: no list of kpoints in input # ValueError: labels from input do not match the output # list of kpoints (some kpoints are missing) # TypeError: labels are not set, so kpoints_from_input.labels=None pass # Get the bands occupations and correct the occupations of QE: # If it computes only one component, it occupies it with half number of electrons try: bands_data['occupations'][1] the_occupations = bands_data['occupations'] except IndexError: the_occupations = 2.*numpy.array(bands_data['occupations'][0]) try: bands_data['bands'][1] bands_energies = bands_data['bands'] except IndexError: bands_energies = bands_data['bands'][0] the_bands_data = BandsData() the_bands_data.set_kpointsdata(kpoints_for_bands) the_bands_data.set_bands(bands_energies, units = bands_data['bands_units'], occupations = the_occupations) new_nodes_list += [('output_band', the_bands_data)] out_dict['linknames_band'] = ['output_band'] # Separate the atomic_occupations dictionary in its own node if it is present atomic_occupations = out_dict.get('atomic_occupations', {}) if atomic_occupations: out_dict.pop('atomic_occupations') atomic_occupations_node = ParameterData(dict=atomic_occupations) new_nodes_list.append(('output_atomic_occupations', atomic_occupations_node)) output_params = ParameterData(dict=out_dict) new_nodes_list.append((self.get_linkname_outparams(), output_params)) if trajectory_data: from aiida.orm.data.array.trajectory import TrajectoryData from aiida.orm.data.array import ArrayData try: positions = numpy.array( trajectory_data.pop('atomic_positions_relax')) try: cells = numpy.array( trajectory_data.pop('lattice_vectors_relax')) # if KeyError, the MD was at fixed cell except KeyError: cells = numpy.array([in_struc.cell] * len(positions)) symbols = numpy.array([str(i.kind_name) for i in in_struc.sites]) stepids = numpy.arange(len(positions)) # a growing integer per step # I will insert time parsing when they fix their issues about time # printing (logic is broken if restart is on) traj = TrajectoryData() traj.set_trajectory( stepids = stepids, cells = cells, symbols = symbols, positions = positions, ) for x in trajectory_data.iteritems(): traj.set_array(x[0],numpy.array(x[1])) new_nodes_list.append((self.get_linkname_outtrajectory(),traj)) except KeyError: # forces, atomic charges and atomic mag. moments, in scf calculation (when outputed) arraydata = ArrayData() for x in trajectory_data.iteritems(): arraydata.set_array(x[0],numpy.array(x[1])) new_nodes_list.append((self.get_linkname_outarray(),arraydata)) return successful, new_nodes_list
def _aiida_ndb_qp(self, data): """ Save the data from ndb.QP to the db """ pdata = ArrayData() pdata.set_array('Eo', numpy.array(data['Eo'])) pdata.set_array('E_minus_Eo', numpy.array(data['E-Eo'])) pdata.set_array('Z', numpy.array(data['Z'])) pdata.set_array('qp_table', numpy.array(data['qp_table'])) try: pdata.set_array('So', numpy.array(data['So'])) except KeyError: pass return pdata
def test_pw_translation(self): from aiida.tools.dbexporters.tcod \ import translate_calculation_specific_values # from aiida.tools.dbexporters.tcod_plugins.pw \ # import PwTcodtranslator as PWT # from aiida.tools.dbexporters.tcod_plugins.cp \ # import CpTcodtranslator as CPT from aiida.orm.code import Code from aiida.orm.data.array import ArrayData from aiida.orm.data.array.kpoints import KpointsData from aiida.orm.data.parameter import ParameterData import numpy from aiida.common.pluginloader import get_plugin PWT = get_plugin('tools.dbexporters.tcod_plugins', 'quantumespresso.pw') CPT = get_plugin('tools.dbexporters.tcod_plugins', 'quantumespresso.cp') code = Code() code._set_attr('remote_exec_path', '/test') kpoints = KpointsData() kpoints.set_kpoints_mesh([2, 3, 4], offset=[0.25, 0.5, 0.75]) def empty_list(): return [] calc = FakeObject({ "inp": { "parameters": ParameterData(dict={}), "kpoints": kpoints, "code": code }, "out": { "output_parameters": ParameterData(dict={}) }, "get_inputs": empty_list }) res = translate_calculation_specific_values(calc, PWT) self.assertEquals( res, { '_dft_BZ_integration_grid_X': 2, '_dft_BZ_integration_grid_Y': 3, '_dft_BZ_integration_grid_Z': 4, '_dft_BZ_integration_grid_shift_X': 0.25, '_dft_BZ_integration_grid_shift_Y': 0.5, '_dft_BZ_integration_grid_shift_Z': 0.75, '_dft_pseudopotential_atom_type': [], '_dft_pseudopotential_type': [], '_dft_pseudopotential_type_other_name': [], '_tcod_software_package': 'Quantum ESPRESSO', '_tcod_software_executable_path': '/test', }) calc = FakeObject({ "inp": { "parameters": ParameterData(dict={ 'SYSTEM': { 'ecutwfc': 40, 'occupations': 'smearing' } }) }, "out": { "output_parameters": ParameterData(dict={ 'number_of_electrons': 10, }) }, "get_inputs": empty_list }) res = translate_calculation_specific_values(calc, PWT) self.assertEquals( res, { '_dft_cell_valence_electrons': 10, '_tcod_software_package': 'Quantum ESPRESSO', '_dft_BZ_integration_smearing_method': 'Gaussian', '_dft_pseudopotential_atom_type': [], '_dft_pseudopotential_type': [], '_dft_pseudopotential_type_other_name': [], '_dft_kinetic_energy_cutoff_EEX': 2176.910676048, '_dft_kinetic_energy_cutoff_charge_density': 2176.910676048, '_dft_kinetic_energy_cutoff_wavefunctions': 544.227669012, }) calc = FakeObject({ "inp": { "parameters": ParameterData(dict={}) }, "out": { "output_parameters": ParameterData(dict={ 'energy_xc': 5, }) }, "get_inputs": empty_list }) with self.assertRaises(ValueError): translate_calculation_specific_values(calc, PWT) calc = FakeObject({ "inp": { "parameters": ParameterData(dict={}) }, "out": { "output_parameters": ParameterData(dict={ 'energy_xc': 5, 'energy_xc_units': 'meV' }) }, "get_inputs": empty_list }) with self.assertRaises(ValueError): translate_calculation_specific_values(calc, PWT) energies = { 'energy': -3701.7004199449257, 'energy_one_electron': -984.0078459766, 'energy_xc': -706.6986753641559, 'energy_ewald': -2822.6335103043157, 'energy_hartree': 811.6396117001462, 'fermi_energy': 10.25208617898623, } dct = energies for key in energies.keys(): dct["{}_units".format(key)] = 'eV' calc = FakeObject({ "inp": { "parameters": ParameterData(dict={'SYSTEM': { 'smearing': 'mp' }}) }, "out": { "output_parameters": ParameterData(dict=dct) }, "get_inputs": empty_list }) res = translate_calculation_specific_values(calc, PWT) self.assertEquals( res, { '_tcod_total_energy': energies['energy'], '_dft_1e_energy': energies['energy_one_electron'], '_dft_correlation_energy': energies['energy_xc'], '_dft_ewald_energy': energies['energy_ewald'], '_dft_hartree_energy': energies['energy_hartree'], '_dft_fermi_energy': energies['fermi_energy'], '_tcod_software_package': 'Quantum ESPRESSO', '_dft_BZ_integration_smearing_method': 'Methfessel-Paxton', '_dft_BZ_integration_MP_order': 1, '_dft_pseudopotential_atom_type': [], '_dft_pseudopotential_type': [], '_dft_pseudopotential_type_other_name': [], }) dct = energies dct['number_of_electrons'] = 10 for key in energies.keys(): dct["{}_units".format(key)] = 'eV' calc = FakeObject({ "inp": { "parameters": ParameterData(dict={'SYSTEM': { 'smearing': 'unknown-method' }}) }, "out": { "output_parameters": ParameterData(dict=dct) }, "get_inputs": empty_list }) res = translate_calculation_specific_values(calc, CPT) self.assertEquals( res, { '_dft_cell_valence_electrons': 10, '_tcod_software_package': 'Quantum ESPRESSO' }) ad = ArrayData() ad.set_array("forces", numpy.array([[[1, 2, 3], [4, 5, 6]]])) calc = FakeObject({ "inp": { "parameters": ParameterData(dict={'SYSTEM': { 'smearing': 'unknown-method' }}) }, "out": { "output_parameters": ParameterData(dict={}), "output_array": ad }, "get_inputs": empty_list }) res = translate_calculation_specific_values(calc, PWT) self.assertEquals( res, { '_tcod_software_package': 'Quantum ESPRESSO', '_dft_BZ_integration_smearing_method': 'other', '_dft_BZ_integration_smearing_method_other': 'unknown-method', '_dft_pseudopotential_atom_type': [], '_dft_pseudopotential_type': [], '_dft_pseudopotential_type_other_name': [], ## Residual forces are no longer produced, as they should ## be in the same CIF loop with coordinates -- to be ## implemented later, since it's not yet clear how. # '_tcod_atom_site_resid_force_Cartn_x': [1,4], # '_tcod_atom_site_resid_force_Cartn_y': [2,5], # '_tcod_atom_site_resid_force_Cartn_z': [3,6], })
def parse_with_retrieved(self, retrieved): """ Parse the results of retrieved nodes :param retrieved: dictionary of retrieved nodes """ is_success = True output_nodes = [] try: output_folder = retrieved[ self.calculation._get_linkname_retrieved()] except KeyError: self.logger.error('no retrieved folder found') return False, () # Verify the standard output file is present, parse it and attach as output parameters try: filepath_stdout = output_folder.get_abs_path( self.calculation.output_file_name) except OSError as exception: self.logger.error( "expected output file '{}' was not found".format(filepath)) return False, () is_success, dict_stdout = self.parse_stdout(filepath_stdout) output_nodes.append( (self.get_linkname_outparams(), ParameterData(dict=dict_stdout))) # The final chi and hubbard files are only written by a serial or post-processing calculation complete_calculation = True # We cannot use get_abs_path of the output_folder, since that will check for file existence and will throw output_path = output_folder.get_abs_path('.') filepath_chi = os.path.join(output_path, self.calculation.output_file_name_chi) filepath_hubbard = os.path.join( output_path, self.calculation.output_file_name_hubbard) filepath_hubbard_file = os.path.join( output_path, self.calculation.output_file_name_hubbard_file) for filepath in [filepath_chi, filepath_hubbard]: if not os.path.isfile(filepath): complete_calculation = False self.logger.info( "output file '{}' was not found, assuming partial calculation" .format(filepath)) if os.path.isfile(filepath_hubbard_file): output_hubbard_file = SinglefileData(file=filepath_hubbard_file) output_nodes.append( (self.get_linkname_hubbard_file(), output_hubbard_file)) if complete_calculation: dict_hubbard = self.parse_hubbard(filepath_hubbard) dict_chi = self.parse_chi(filepath_chi) output_matrices = ArrayData() output_matrices.set_array('chi0', dict_hubbard['chi0']) output_matrices.set_array('chi1', dict_hubbard['chi1']) output_matrices.set_array('chi0_inv', dict_hubbard['chi0_inv']) output_matrices.set_array('chi1_inv', dict_hubbard['chi1_inv']) output_matrices.set_array('hubbard', dict_hubbard['hubbard']) output_chi = ArrayData() output_chi.set_array('chi0', dict_chi['chi0']) output_chi.set_array('chi1', dict_chi['chi1']) output_hubbard = ParameterData(dict=dict_hubbard['hubbard_U']) output_nodes.append( (self.get_linkname_matrices(), output_matrices)) output_nodes.append((self.get_linkname_hubbard(), output_hubbard)) output_nodes.append((self.get_linkname_chi(), output_chi)) return is_success, output_nodes