这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
atk:如何为_quantumatk_创建新的附加组件 [2019/07/30 18:41] – [表] xie.congwei | atk:如何为_quantumatk_创建新的附加组件 [2019/09/01 08:34] (当前版本) – [如何为 QuantumATK 创建新的附加组件] dong.dong | ||
---|---|---|---|
行 1: | 行 1: | ||
====== 如何为 QuantumATK 创建新的附加组件 ====== | ====== 如何为 QuantumATK 创建新的附加组件 ====== | ||
- | ^ **版本:** 2015\\ \\ // | + | ^ **版本:** 2015 |
+ | |||
+ | 在 QuantumATK,附加组件是包含一个或多个插件的 Python 模块,可用于向软件添加新功能。有几种类型的插件可供选择。本教程将关注允许 QuantumATK 读写新数据格式的插件类型。在本教程中包含了三个示例。第一个是从 XYZ 文件中读取分子构型的插件,第二个是读取电子密度的插件。 | ||
===== 附加组件模块的基本结构 ===== | ===== 附加组件模块的基本结构 ===== | ||
+ | 因为插件是包含在 Python 模块中的,所以它们应存在于自己的目录中。我们将研究一个读取 XYZ 文件的插件作为示例。它的目录结构如下: | ||
+ | |||
+ | <code python> | ||
+ | XYZFilters/ | ||
+ | __init__.py | ||
+ | XYZLabFloor.py | ||
+ | XYZFileRawReader.py | ||
+ | </ | ||
+ | |||
+ | '' | ||
===== 例一:读取 XYZ 文件的插件 ===== | ===== 例一:读取 XYZ 文件的插件 ===== | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code python> | ||
+ | 1 | ||
+ | 2 | ||
+ | 3 | ||
+ | 4 | ||
+ | 5 | ||
+ | 6 | ||
+ | 7 | ||
+ | 8 | ||
+ | 9 | ||
+ | 10 def reloadPlugins(): | ||
+ | 11 reload(XYZLabFloor) | ||
+ | </ | ||
+ | |||
+ | 此代码给出了附加组件的说明,分配版本号和上次更新的日期。它还定义了附加组件提供的插件列表。本例中,有一个 '' | ||
+ | |||
+ | 我们现在来看一下包含插件类的 '' | ||
+ | |||
+ | |||
+ | <code python> | ||
+ | 1 | ||
+ | 2 | ||
+ | 3 from API import LabFloorImporterPlugin | ||
+ | 4 from API import LabFloorItem | ||
+ | 5 from NL.CommonConcepts.Configurations.MoleculeConfiguration import MoleculeConfiguration | ||
+ | 6 from NL.CommonConcepts.PeriodicTable import SYMBOL_TO_ELEMENT | ||
+ | 7 from NL.CommonConcepts.PhysicalQuantity import Angstrom | ||
+ | 8 from NL.ComputerScienceUtilities import Exceptions | ||
+ | 9 | ||
+ | 10 from XYZFileRawReader import XYZFileRawReader | ||
+ | </ | ||
+ | |||
+ | 接下来我们需要定义插件类。插件必须从 QuantumATK 定义的特定类中继承。在这种情况下,我们将定义一个从 '' | ||
+ | |||
+ | <code python> | ||
+ | 13 class XYZLabFloor(LabFloorImporterPlugin): | ||
+ | </ | ||
+ | |||
+ | 这种类型的插件必须定义两种方式。第一种方式是 '' | ||
+ | |||
+ | 对于我们的 '' | ||
+ | |||
+ | <code python> | ||
+ | 18 def scan(self, filename): | ||
+ | 19 """ | ||
+ | 20 Scans a file to check if it is supported by the plugin | ||
+ | 21 | ||
+ | 22 @param filename : The path to be scanned. | ||
+ | 23 @type : string | ||
+ | 24 | ||
+ | 25 @return A list of LabFloorItems | ||
+ | 26 """ | ||
+ | 27 # Setup a resulting vector. | ||
+ | 28 result = [] | ||
+ | 29 | ||
+ | 30 # Determine extension | ||
+ | 31 basename = os.path.basename(filename) | ||
+ | 32 no_extension_name, | ||
+ | 33 | ||
+ | 34 # Return empty string if extension isn't " | ||
+ | 35 if extension != ' | ||
+ | 36 return result | ||
+ | 37 | ||
+ | 38 # Try to load configuration | ||
+ | 39 try: | ||
+ | 40 reader = XYZFileRawReader(filename) | ||
+ | 41 except Exception: | ||
+ | 42 return result | ||
+ | 43 | ||
+ | 44 for molecule_idx in xrange(reader.numOfMolecules()): | ||
+ | 45 | ||
+ | 46 # Read the comment for this molecule. | ||
+ | 47 comment = reader.comment(molecule=molecule_idx) | ||
+ | 48 | ||
+ | 49 # Create and add LabFloorItem to list | ||
+ | 50 if reader.numOfMolecules() == 1: | ||
+ | 51 title = no_extension_name | ||
+ | 52 else: | ||
+ | 53 title = no_extension_name + " (" + str(molecule_idx) + " | ||
+ | 54 | ||
+ | 55 # Create labfloor item. | ||
+ | 56 item = LabFloorItem(MoleculeConfiguration, | ||
+ | 57 title=title, | ||
+ | 58 tool_tip=comment, | ||
+ | 59 molecule_idx=molecule_idx) | ||
+ | 60 | ||
+ | 61 # Add to result list. | ||
+ | 62 result.append(item) | ||
+ | 63 | ||
+ | 64 # Return the result list. | ||
+ | 65 return result | ||
+ | </ | ||
+ | |||
+ | 该代码通过首先测试文件名中是否含有 “xyz” 的扩展名,然后尝试实际解析文件来检测文件是否为有效的 XYZ 文件。如果文件不是有效的 XYZ 文件,则返回空列表。如果它是有效文件,则读入文件中包含的每个分子,并为每个分子创建 '' | ||
+ | '' | ||
+ | LabFloorItem'' | ||
+ | |||
+ | 当用户与项目交互时,QuantumATK 会调用 load 方式。例如,当使用 viewer 可视化结构或将其导入 builder 时会发生这种情况。Load 法的定义为: | ||
+ | |||
+ | <code python> | ||
+ | 67 def load(self, filename, molecule_idx=0): | ||
+ | 68 """ | ||
+ | 69 Load the desired object in memory. | ||
+ | 70 | ||
+ | 71 @param filename : The path of the XYZ-file. | ||
+ | 72 @type : string | ||
+ | 73 | ||
+ | 74 @return Desired object (MoleculeConfiguration) | ||
+ | 75 """ | ||
+ | 76 # Read the file | ||
+ | 77 reader = XYZFileRawReader(filename) | ||
+ | 78 | ||
+ | 79 # Lists of elements and positions. | ||
+ | 80 elements = [] | ||
+ | 81 positions = [] | ||
+ | 82 | ||
+ | 83 # Loop over atoms. | ||
+ | 84 for atom in reader.atomList(molecule=molecule_idx): | ||
+ | 85 elements.append(SYMBOL_TO_ELEMENT[atom[" | ||
+ | 86 positions.append(atom[" | ||
+ | 87 | ||
+ | 88 # Create configuration. | ||
+ | 89 configuration = MoleculeConfiguration( | ||
+ | 90 elements=elements, | ||
+ | 91 cartesian_coordinates=positions) | ||
+ | 92 | ||
+ | 93 return configuration | ||
+ | </ | ||
+ | |||
+ | 该方式读取文件内容并提取所要求分子的元素和位置。该方法被传递了要读取分子的索引(信息存储在 '' | ||
+ | |||
+ | 此附加组件的完整源代码可在此处下载 [[https:// | ||
===== 例二:导出构型的插件 ===== | ===== 例二:导出构型的插件 ===== | ||
- | ===== 例三:读取电子密度的插件 ===== | ||
+ | 在此示例中,我们将编写一个插件以允许 QuantumATK 将结构导出到 XYZ 文件。该模块的目录结构如下所示: | ||
+ | <code python> | ||
+ | XYZExporter/ | ||
+ | __init__.py | ||
+ | XYZExporter.py | ||
+ | </ | ||
+ | |||
+ | 第一步是编译 '' | ||
+ | |||
+ | <code python> | ||
+ | 1 | ||
+ | 2 | ||
+ | 3 | ||
+ | 4 | ||
+ | 5 | ||
+ | 6 | ||
+ | 7 | ||
+ | 8 | ||
+ | 9 | ||
+ | 10 def reloadPlugins(): | ||
+ | 11 reload(XYZExporterPlugin) | ||
+ | </ | ||
+ | |||
+ | 下一步就是要编写真正的插件类。在之前的示例中,插件类是从 '' | ||
+ | |||
+ | 从 ExportConfigurationPlugin 继承的类必须执行四种方式:'' | ||
+ | |||
+ | 以下为 '' | ||
+ | |||
+ | <code python> | ||
+ | 1 from NL.CommonConcepts.Configurations.MoleculeConfiguration import MoleculeConfiguration | ||
+ | 2 from NL.CommonConcepts.PhysicalQuantity import Angstrom | ||
+ | 3 | ||
+ | 4 from API import ExportConfigurationPlugin, | ||
+ | 5 | ||
+ | 6 class XYZExporterPlugin(ExportConfigurationPlugin): | ||
+ | 7 """ | ||
+ | 8 | ||
+ | 9 def title(self): | ||
+ | 10 """ | ||
+ | 11 | ||
+ | 12 return ' | ||
+ | 13 | ||
+ | 14 def extension(self): | ||
+ | 15 """ | ||
+ | 16 | ||
+ | 17 return ' | ||
+ | 18 | ||
+ | 19 def export(self, | ||
+ | 20 """ | ||
+ | 21 Export the configuration. | ||
+ | 22 | ||
+ | 23 @param configuration : The configuration to export. | ||
+ | 24 @param path : The path to save the configuration to. | ||
+ | 25 | ||
+ | 26 @return None | ||
+ | 27 """ | ||
+ | 28 | ||
+ | 29 # XYZ files only supports molecules. | ||
+ | 30 if not isinstance(configuration, | ||
+ | 31 showMessage(' | ||
+ | 32 return | ||
+ | 33 | ||
+ | 34 # Open the file with write permission. | ||
+ | 35 with open(path, ' | ||
+ | 36 | ||
+ | 37 # Get the total number of atoms. | ||
+ | 38 number_of_atoms = len(configuration) | ||
+ | 39 | ||
+ | 40 # Write out the header to the file. | ||
+ | 41 f.write(' | ||
+ | 42 f.write(' | ||
+ | 43 | ||
+ | 44 # Get the list of atomic symbols. | ||
+ | 45 symbols = [ element.symbol() for element in configuration.elements() ] | ||
+ | 46 # Get the cartesian coordinates in units of Angstrom. | ||
+ | 47 coordinates = configuration.cartesianCoordinates().inUnitsOf(Angstrom) | ||
+ | 48 | ||
+ | 49 # Loop over each atom and write out its symbol and coordinates. | ||
+ | 50 for i in xrange(number_of_atoms): | ||
+ | 51 x, y, z = coordinates[i] | ||
+ | 52 f.write(' | ||
+ | 53 | ||
+ | 54 | ||
+ | 55 def canExport(self, | ||
+ | 56 """ | ||
+ | 57 Method to determine if an exporter class can export a given configuration. | ||
+ | 58 | ||
+ | 59 @param configuration : The configuration to test. | ||
+ | 60 | ||
+ | 61 @return A bool, True if the plugin can export, False if it cannot. | ||
+ | 62 """ | ||
+ | 63 | ||
+ | 64 supported_configurations = [MoleculeConfiguration] | ||
+ | 65 | ||
+ | 66 return isinstance(configuration, | ||
+ | </ | ||
+ | |||
+ | 此附加组件的完整源代码可在此处下载 [[https:// | ||
+ | |||
+ | ===== 例三:读取电子密度的插件 ===== | ||
+ | |||
+ | 在这个例子中,我们将创建一个读取电子密度数据的新插件。在本教程中,我们将为存储在 NumPy 二进制 '' | ||
==== 构造密度 ==== | ==== 构造密度 ==== | ||
+ | |||
+ | 此代码将定义电子密度并将其保存到文件 [[https:// | ||
+ | |||
+ | <code python> | ||
+ | 1 | ||
+ | 2 | ||
+ | 3 # Define a orthogonal cell. | ||
+ | 4 cell = numpy.array( [ [ 5.0, 0.0, 0.0 ], | ||
+ | 5 [ 0.0, 5.0, 0.0 ], | ||
+ | 6 [ 0.0, 0.0, 5.0 ] ] ) | ||
+ | 7 # Create a 3D grid of points from 0 to 5 in x, y, and z. | ||
+ | 8 x = numpy.linspace(0.0, | ||
+ | 9 y = numpy.linspace(0.0, | ||
+ | 10 z = numpy.linspace(0.0, | ||
+ | 11 xx, yy, zz = numpy.meshgrid(x, | ||
+ | 12 | ||
+ | 13 # Define an electron density as a Gaussian centered at (2.5, 2.5, 2.5) times a | ||
+ | 14 # sine wave in the x direction. | ||
+ | 15 | ||
+ | 16 | ||
+ | 17 # Save the cell and density to a .npz file. | ||
+ | 18 | ||
+ | </ | ||
+ | |||
+ | 这种电子密度不是物理意义上的,因为它在某些地方会是负的,但它可以让我们轻易地仔细检查数据是否被正确读取。可以在此处下载脚本 [[https:// | ||
==== 编写 NPZ 滤波器附加组件 ==== | ==== 编写 NPZ 滤波器附加组件 ==== | ||
+ | |||
+ | 附加组件的目录结构如下所示: | ||
+ | |||
+ | <code python> | ||
+ | NPZFilters/ | ||
+ | __init__.py | ||
+ | NPZLabFloor.py | ||
+ | </ | ||
+ | |||
+ | 与前面的示例一样,我们将首先关注 '' | ||
+ | |||
+ | <code python> | ||
+ | 1 | ||
+ | 2 | ||
+ | 3 | ||
+ | 4 | ||
+ | 5 | ||
+ | 6 | ||
+ | 7 | ||
+ | 8 | ||
+ | 9 | ||
+ | 10 def reloadPlugins(): | ||
+ | 11 | ||
+ | </ | ||
+ | |||
+ | 这里没有什么新的内容。现在我们需要定义实际的插件类: | ||
+ | |||
+ | <code python> | ||
+ | 1 | ||
+ | 2 | ||
+ | 3 | ||
+ | 4 | ||
+ | 5 | ||
+ | 6 from API import LabFloorImporterPlugin | ||
+ | 7 from API import LabFloorItem | ||
+ | 8 from NL.Analysis.ElectronDensity import ElectronDensity | ||
+ | 9 from NL.Analysis.GridValues import GridValues | ||
+ | 10 from NL.ComputerScienceUtilities.NLFlag import Spin | ||
+ | 11 from NL.CommonConcepts.PhysicalQuantity import Angstrom | ||
+ | 12 | ||
+ | 13class NPZLabFloor(LabFloorImporterPlugin): | ||
+ | 14 """ | ||
+ | 15 Class for handling the importing of NPZ-files as LabFloor items. | ||
+ | 16 """ | ||
+ | 17 | ||
+ | 18 def scan(self, filename): | ||
+ | 19 """ | ||
+ | 20 Scans a file to check if it is supported by the plugin | ||
+ | 21 | ||
+ | 22 @param filename : The path to be scanned. | ||
+ | 23 @type : string | ||
+ | 24 | ||
+ | 25 @return A list of LabFloorItems | ||
+ | 26 """ | ||
+ | 27 # Setup a vector for the LabFloorItems that will be returned. | ||
+ | 28 lab_floor_items = [] | ||
+ | 29 | ||
+ | 30 # Determine extension | ||
+ | 31 basename = os.path.basename(filename) | ||
+ | 32 no_extension_name, | ||
+ | 33 | ||
+ | 34 # Return an empty list if the extension isn't " | ||
+ | 35 if extension != ' | ||
+ | 36 return [] | ||
+ | 37 | ||
+ | 38 item = LabFloorItem(ElectronDensity, | ||
+ | 39 title=' | ||
+ | 40 tool_tip=' | ||
+ | 41 | ||
+ | 42 # Add to the list of items. | ||
+ | 43 lab_floor_items.append(item) | ||
+ | 44 | ||
+ | 45 # Return the list of items. | ||
+ | 46 return lab_floor_items | ||
+ | 47 | ||
+ | 48 def load(self, filename): | ||
+ | 49 """ | ||
+ | 50 Load the desired object in memory. | ||
+ | 51 | ||
+ | 52 @param filename : The path of the NPZ-file. | ||
+ | 53 @type : string | ||
+ | 54 | ||
+ | 55 @return Desired object (MoleculeConfiguration) | ||
+ | 56 """ | ||
+ | 57 | ||
+ | 58 # Read the file | ||
+ | 59 npz = numpy.load(filename) | ||
+ | 60 | ||
+ | 61 # Create an " | ||
+ | 62 electron_density = ElectronDensity.__new__(ElectronDensity) | ||
+ | 63 | ||
+ | 64 # We will now fill out a dictionary that contains the information | ||
+ | 65 # needed by the ElectronDensity class. | ||
+ | 66 data = {} | ||
+ | 67 | ||
+ | 68 # The " | ||
+ | 69 # " | ||
+ | 70 # dimension, the y-axis as the second, and the z-axis as the third. | ||
+ | 71 data[' | ||
+ | 72 | ||
+ | 73 # The data in " | ||
+ | 74 data[' | ||
+ | 75 | ||
+ | 76 # Set the origin to be at zero. | ||
+ | 77 data[' | ||
+ | 78 | ||
+ | 79 # The cell must be given in Bohr units. | ||
+ | 80 data[' | ||
+ | 81 | ||
+ | 82 # The boundary conditions are expressed as a list of 6 numbers that should | ||
+ | 83 # map to: | ||
+ | 84 # { Dirichlet, Neumann, Periodic, Multipole }; | ||
+ | 85 # A value of 2 corresponds to " | ||
+ | 86 data[' | ||
+ | 87 | ||
+ | 88 # Construct the GridValues specific part of the object. | ||
+ | 89 GridValues._populateFromDict(electron_density, | ||
+ | 90 | ||
+ | 91 # Set the spin_type to unpolarized. | ||
+ | 92 spin_type = NLEngine.UNPOLARIZED | ||
+ | 93 electron_density._AnalysisSpin__spin_type = spin_type | ||
+ | 94 | ||
+ | 95 sp = Spin.All | ||
+ | 96 electron_density._AnalysisSpin__spin = sp | ||
+ | 97 electron_density._setSupportedSpins(sp) | ||
+ | 98 | ||
+ | 99 return electron_density | ||
+ | </ | ||
+ | |||
+ | 这个附加组件的完整代码可在此处下载 [[https:// | ||
===== 如何安装附加组件 ===== | ===== 如何安装附加组件 ===== | ||
+ | 有两种不同的安装附加组件的方法。第一种,设置环境变量 '' | ||
+ | |||
+ | 另一种方法是压缩 Python 模块并通过图形界面安装。第一步是创建一个包含该模块的 zip 文件。按照上一段中的 NPZFilter 示例,可以通过运行 '' | ||
+ | |||
+ | |||
+ | {{ : | ||
+ | |||
+ | 点击 //Local Install// | ||
===== 测试 NPZ 滤波器附加组件 ===== | ===== 测试 NPZ 滤波器附加组件 ===== | ||
+ | 按照上一节中的步骤操作后,现在应该已经安装上了 NPZFilters AddOn。您可以通过拉出 AddOn Manager 并查看列表中的 NPZFilters 再次确认已安装。如果我们在 QuantumATK 中创建一个新项目,且该文件夹包含我们创建的 '' | ||
- | ===== 参考 ===== | ||
+ | {{ : | ||
+ | |||
+ | |||
+ | 点击右侧面板上的 // | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | |||
+ | 得到的等值面现在应该看起来像哑铃,两种不同的颜色分别代表负密度和正密度的区域,以及通过 x-z 轴的零密度平面。这证实了我们在模型密度函数中的读取结果正确。 | ||
+ | |||
+ | |||
+ | {{ : | ||
+ | |||
+ | ===== 参考 ===== | ||
+ | * 英文原文:https:// | ||