用户工具

站点工具


atk:如何为_quantumatk_创建新的附加组件

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
atk:如何为_quantumatk_创建新的附加组件 [2019/07/30 18:41] – [表] xie.congweiatk:如何为_quantumatk_创建新的附加组件 [2019/09/01 08:34] (当前版本) – [如何为 QuantumATK 创建新的附加组件] dong.dong
行 1: 行 1:
 ====== 如何为 QuantumATK 创建新的附加组件 ====== ====== 如何为 QuantumATK 创建新的附加组件 ======
-^ **版本:** 2015\\ \\ //对于 QuantumATK,附加组件是包含一个或多个插件的 Python 模块,可用于向软件添加新功能。有几种类型的插件可供选择。本教程将关注允许 QuantumATK 读写新数据格式的插件类型。在本教程中包含了三个示例。第一个是从 XYZ 文件中读取分子构型的插件,第二个是读取电子密度的插件。//  ^ {{ :atk:section_addon-20190730.png?400 }}  ^+^ **版本:** 2015 
 + 
 +在 QuantumATK,附加组件是包含一个或多个插件的 Python 模块,可用于向软件添加新功能。有几种类型的插件可供选择。本教程将关注允许 QuantumATK 读写新数据格式的插件类型。在本教程中包含了三个示例。第一个是从 XYZ 文件中读取分子构型的插件,第二个是读取电子密度的插件。  ^ {{ :atk:section_addon-20190730.png?400 }}  ^
 ===== 附加组件模块的基本结构 ===== ===== 附加组件模块的基本结构 =====
  
  
 +因为插件是包含在 Python 模块中的,所以它们应存在于自己的目录中。我们将研究一个读取 XYZ 文件的插件作为示例。它的目录结构如下:
 +
 +<code python>  
 +XYZFilters/
 +    __init__.py
 +    XYZLabFloor.py
 +    XYZFileRawReader.py
 +</code>
 +
 +''__init__.py'' 文件是一个特殊文件,用于告诉 Python 该文件夹是一个模块。它还包含导入模块时执行的代码。对于插件,此文件还需要包含一些信息用用以告知 QuantumATK 关于它自身。
 ===== 例一:读取 XYZ 文件的插件 ===== ===== 例一:读取 XYZ 文件的插件 =====
 +
 +''XYZFilters'' 附加组件中的 ''__init__.py'' 文件包含以下代码:
 +
 +<code python>  
 +1   import datetime
 +2   import XYZLabFloor
 +3   
 +4   __addon_description__ = "Plugins for importing and exporting XYZ configurations."
 +5   __addon_version__ = "1.0"
 +6   __addon_date__ = datetime.date(year=2015, month=8, day=6)
 +7   
 +8   __plugins__ = [XYZLabFloor.XYZLabFloor]
 +9   
 +10   def reloadPlugins():
 +11      reload(XYZLabFloor)
 +</code>
 +
 +此代码给出了附加组件的说明,分配版本号和上次更新的日期。它还定义了附加组件提供的插件列表。本例中,有一个 ''XYZLabFloor.py'' 文件,包含了一个名为 ''XYZLabFloor'' 的插件类。此外,还提供了一个名为 ''reloadPlugins'' 的函数,以便在源代码文件发生更改时,QuantumATK 可以重新加载插件。
 +
 +我们现在来看一下包含插件类的 ''XYZLabFloor.py'' 文件的结构。在文件的顶部,我们导入了编写插件所需的模块和类:
 +
 +
 +<code python>  
 +1   import os
 +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
 +</code>
 +
 +接下来我们需要定义插件类。插件必须从 QuantumATK 定义的特定类中继承。在这种情况下,我们将定义一个从 ''LabFloorImporterPlugin'' 继承的类:
 +
 +<code python>  
 +13  class XYZLabFloor(LabFloorImporterPlugin):
 +</code>
 +
 +这种类型的插件必须定义两种方式。第一种方式是 ''scan''。该方法的作用是确定插件是否处理特定文件,如果是的话,则确定该文件包含哪种类型的 LabFloor 对象。它会将项目列表返回给 LabFloor。第二种方式是 ''load'',负责解析文件并将对象加载到内存中。
 +
 +对于我们的 ''XYZLabFloor'' 插件,scan 法定义如下:
 +
 +<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, extension = os.path.splitext(basename)
 +33
 +34        # Return empty string if extension isn't ".xyz"
 +35        if extension != '.xyz':
 +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
 +</code>
 +
 +该代码通过首先测试文件名中是否含有 “xyz” 的扩展名,然后尝试实际解析文件来检测文件是否为有效的 XYZ 文件。如果文件不是有效的 XYZ 文件,则返回空列表。如果它是有效文件,则读入文件中包含的每个分子,并为每个分子创建 ''LabFloorItem''
 +''
 +LabFloorItem'' 是一个 LabFloor 上表示项目的分类。它包含对象的类型,在本例中,它是 ''MoleculeConfiguration'' 以及标题和工具提示(当鼠标光标悬停在项目上时可见的文本)。有关该项目的额外信息也可以作为关键参数传递。正如我们将在下面看到的,这些参数将传递给 load 方式。
 +
 +当用户与项目交互时,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["element"]])
 +86            positions.append(atom["coords"]*Angstrom)
 +87
 +88        # Create configuration.
 +89        configuration = MoleculeConfiguration(
 +90            elements=elements,
 +91            cartesian_coordinates=positions)
 +92
 +93        return configuration
 +</code>
 +
 +该方式读取文件内容并提取所要求分子的元素和位置。该方法被传递了要读取分子的索引(信息存储在 ''scan'' 式中创建的 ''LabFloorItem'' 中)并创建了一个 [[https://docs.quantumwise.com/manual/Types/MoleculeConfiguration/MoleculeConfiguration.html#NL.CommonConcepts.Configurations.MoleculeConfiguration.MoleculeConfiguration|MoleculeConfiguration]]。MoleculeConfiguration 类是 QuantumATK 中表示分子的方式。对于周期系统,有一个相应的 [[https://docs.quantumwise.com/manual/Types/BulkConfiguration/BulkConfiguration.html#NL.CommonConcepts.Configurations.BulkConfiguration.BulkConfiguration|BulkConfiguration]] 类,用于存储晶格矢量以及坐标和元素。
 +
 +此附加组件的完整源代码可在此处下载 [[https://docs.quantumwise.com/_downloads/XYZFilters.zip|↓ source code]]。
  
 ===== 例二:导出构型的插件 ===== ===== 例二:导出构型的插件 =====
  
-===== 例三:读取电子密度的插件 ===== 
  
 +在此示例中,我们将编写一个插件以允许 QuantumATK 将结构导出到 XYZ 文件。该模块的目录结构如下所示:
  
 +<code python>  
 +XYZExporter/
 +    __init__.py
 +    XYZExporter.py
 +</code>
 +
 +第一步是编译 ''__init__.py'' 文件:
 +
 +<code python>
 +1   import datetime
 +2   import XYZExporterPlugin
 +3
 +4   __addon_description__ = "Plugins for exporting XYZ files."
 +5   __addon_version__ = "1.0"
 +6   __addon_date__ = datetime.date(year=2015, month=8, day=6)
 +7
 +8   __plugins__ = [XYZExporterPlugin.XYZExporterPlugin]
 +9
 +10   def reloadPlugins():
 +11    reload(XYZExporterPlugin)
 +</code>
 +
 +下一步就是要编写真正的插件类。在之前的示例中,插件类是从 ''LabFloorImporterPlugin'' 类派生的,表明它是一个用于导入数据的插件。但本例的插件将派生自 ''ExportConfigurationPlugin''。这些插件用于扩展 builder 支持的导出格式数量。打开构建器后,可以选择 //File→Export// 查看支持的文件格式列表。
 +
 +从 ExportConfigurationPlugin 继承的类必须执行四种方式:''title'',''extension'',''canExport'' 和 ''export''。''Title'' 式需要返回此类文件的名称 (“XYZ”)。''Extension'' 式应该返回文件扩展名 (“xyz”)。''canExport'' 式确定此插件是否支持结构类型(''MoleculeConfiguration'',''BulkConfiguration'',''DeviceConfiguration'' 或 ''NudgedElasticBand'')(XYZ文件仅支持 ''MoleculeConfiguration'' 对象)。最后,''export'' 式是将结构传入并写进磁盘的位置。
 +
 +以下为 ''XYZExporterPlugin.py'' 文件中 ''XYZExporterPlugin'' 的代码:
 +
 +<code python> 
 +1      from NL.CommonConcepts.Configurations.MoleculeConfiguration import MoleculeConfiguration
 +2      from NL.CommonConcepts.PhysicalQuantity import Angstrom
 +3      
 +4      from API import ExportConfigurationPlugin, showMessage
 +5      
 +6      class XYZExporterPlugin(ExportConfigurationPlugin):
 +7          """ Class for handling the export of the XYZ input files. """
 +8      
 +9          def title(self):
 +10              """ Return the title file selection dialog. """
 +11      
 +12              return 'XYZ'
 +13      
 +14          def extension(self):
 +15              """ The default extension of XYZ. """
 +16      
 +17              return 'xyz'
 +18      
 +19          def export(self, configuration, path):
 +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, MoleculeConfiguration):
 +31                  showMessage('XYZExporter can only export MoleculeConfigurations')
 +32                  return
 +33      
 +34              # Open the file with write permission.
 +35              with open(path, 'w') as f:
 +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('%i\n' % number_of_atoms)
 +42                  f.write('Generated by XYZExporter\n')
 +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('%3s %16.8f %16.8f %16.8f\n' % (symbols[i], x, y, z))
 +53      
 +54      
 +55          def canExport(self, configuration):
 +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, supported_configurations)
 +</code>
 +
 +此附加组件的完整源代码可在此处下载 [[https://docs.quantumwise.com/_downloads/XYZExporter.zip|↓ source code]]。
 +
 +===== 例三:读取电子密度的插件 =====
 +
 +在这个例子中,我们将创建一个读取电子密度数据的新插件。在本教程中,我们将为存储在 NumPy 二进制 ''.npz'' 文件中的 3D 网格数据定义一种新格式。
 ==== 构造密度 ==== ==== 构造密度 ====
  
 +
 +此代码将定义电子密度并将其保存到文件 [[https://docs.quantumwise.com/_downloads/electron_density.npz|↓ electron_density.npz]]。以下将是我们的插件将读取的密度文件。
 +
 +<code python>
 +1   import numpy
 +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, 5.0)
 +9   y = numpy.linspace(0.0, 5.0)
 +10   z = numpy.linspace(0.0, 5.0)
 +11   xx, yy, zz = numpy.meshgrid(x, y, z, indexing='ij')
 +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   density = numpy.exp(-(xx-2.5)**2 - (yy-2.5)**2 - (zz-2.5)**2) * numpy.sin(yy-2.5)
 +16   
 +17   # Save the cell and density to a .npz file.
 +18   numpy.savez('electron_density.npz', cell=cell, density=density)
 +</code>
 +
 +这种电子密度不是物理意义上的,因为它在某些地方会是负的,但它可以让我们轻易地仔细检查数据是否被正确读取。可以在此处下载脚本 [[https://docs.quantumwise.com/_downloads/make_npz_electron_density.py|↓ source code]]。
 ==== 编写 NPZ 滤波器附加组件 ==== ==== 编写 NPZ 滤波器附加组件 ====
 +
 +附加组件的目录结构如下所示:
 +
 +<code python>  
 +NPZFilters/
 +    __init__.py
 +    NPZLabFloor.py
 +</code>
 +
 +与前面的示例一样,我们将首先关注 ''__init__.py'' 文件:
 +
 +<code python>  
 +1   import datetime
 +2   import NPZLabFloor
 +3   
 +4   __addon_description__ = "Plugin for reading a NPZ formatted electron density."
 +5   __addon_version__ = "1.0"
 +6   __addon_date__ = datetime.date(2014, 9, 1)
 +7   
 +8   __plugins__ = [NPZLabFloor.NPZLabFloor]
 +9   
 +10   def reloadPlugins():
 +11       reload(NPZLabFloor)
 +</code>
 +
 +这里没有什么新的内容。现在我们需要定义实际的插件类:
 +
 +<code python>  
 +1   import numpy
 +2   import os
 +3   
 +4   import NLEngine
 +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, extension = os.path.splitext(basename)
 +33
 +34        # Return an empty list if the extension isn't ".npz"
 +35        if extension != '.npz':
 +36            return []
 +37
 +38        item = LabFloorItem(ElectronDensity,
 +39                            title='NPZ Electron Density',
 +40                            tool_tip='NPZ Electron Density')
 +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 "empty" ElectronDensity object.
 +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 "data" key is the electron density. The units must be given in the
 +69        # "data_unit" key. The array should have have the x-axis as the first
 +70        # dimension, the y-axis as the second, and the z-axis as the third.
 +71        data['data'] = npz['density']
 +72
 +73        # The data in "data" has no units so they are assigned here.
 +74        data['data_unit'] = 1.0 * Angstrom**-3
 +75
 +76        # Set the origin to be at zero.
 +77        data['origo'] = numpy.zeros(3)
 +78
 +79        # The cell must be given in Bohr units.
 +80        data['cell'] = npz['cell']
 +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 "Periodic".
 +86        data['boundary_conditions'] = [2, 2, 2, 2, 2, 2]
 +87
 +88        # Construct the GridValues specific part of the object.
 +89        GridValues._populateFromDict(electron_density, data)
 +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
 +</code>
 +
 +这个附加组件的完整代码可在此处下载 [[https://docs.quantumwise.com/_downloads/NPZFilters.zip|↓ source code]]。
  
  
 ===== 如何安装附加组件 ===== ===== 如何安装附加组件 =====
  
 +有两种不同的安装附加组件的方法。第一种,设置环境变量 ''QUANTUM_ADDONS_PATH'' 到插件模块所在的目录。例如,在先前的章节中 NPZFilters 附件的路径为 ''$HOME/AddOns/NPZFilters'',然后设置环境变量为 ''QUANTUM_ADDONS_PATH=$HOME/AddOns'' 即可。
 +
 +另一种方法是压缩 Python 模块并通过图形界面安装。第一步是创建一个包含该模块的 zip 文件。按照上一段中的 NPZFilter 示例,可以通过运行 ''zip -r NPZFilters.zip $ HOME / AddOns / NPZFilters'' 来完成。然后,在 QuantumATK 中,//Help// 菜单下面有一个名为 //AddOn Manager// 的选项。打开 AddOn Manager 将会呈现出如下所示的窗口:
 +
 +
 +{{ :atk:addonmanager-20190730.png?700 |}}
 +
 +点击 //Local Install//,出现一个文件对话框。选择 ''NPZFilters.zip'',QuantumATK 将安装插件到 ''QUANTUM_ADDONS_PATH''
 ===== 测试 NPZ 滤波器附加组件 ===== ===== 测试 NPZ 滤波器附加组件 =====
  
 +按照上一节中的步骤操作后,现在应该已经安装上了 NPZFilters AddOn。您可以通过拉出 AddOn Manager 并查看列表中的 NPZFilters 再次确认已安装。如果我们在 QuantumATK 中创建一个新项目,且该文件夹包含我们创建的 ''electron_density.npz'' 文件,那么 ElectronDensity 对象就应该显示在 lab floor 上。
  
-===== 参考 ===== 
  
 +{{ :atk:npzlabfloor-20190730.png?900 |}}
 +
 +
 +点击右侧面板上的 //Viewer...// 按钮将电子密度可视化。在出现的对话框中,有 isosurface 和 cut plane 可供选择。选择 isosurface。默认的 isosurface 值是平均电荷密度,对于我们的电荷密度来说为零(因为一半是负,一半是正)。单击右侧面板中的 Properties ... 按钮,将 //Isovalue ...// 滑块拖动到接近 1 的地方。
 +
 +{{ :atk:isovalue-20190730.png?650 |}}
 +
 +
 +得到的等值面现在应该看起来像哑铃,两种不同的颜色分别代表负密度和正密度的区域,以及通过 x-z 轴的零密度平面。这证实了我们在模型密度函数中的读取结果正确。
 +
 +
 +{{ :atk:isosurface-20190730.png?650 |}}
 +
 +===== 参考 =====
  
  
 +  * 英文原文:https://docs.quantumwise.com/tutorials/addons/addons.html
  
  
atk/如何为_quantumatk_创建新的附加组件.1564483308.txt.gz · 最后更改: 2019/07/30 18:41 由 xie.congwei

© 2014-2022 费米科技(京ICP备14023855号