genice3 3.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- genice3/__init__.py +7 -0
- genice3/_build.py +43 -0
- genice3/cage.py +160 -0
- genice3/cli/__init__.py +20 -0
- genice3/cli/genice.py +168 -0
- genice3/cli/option_def.py +25 -0
- genice3/cli/option_parser.py +142 -0
- genice3/cli/options.py +556 -0
- genice3/cli/runner.py +207 -0
- genice3/cli/validator.py +157 -0
- genice3/dependencyengine.py +102 -0
- genice3/exporter/_KG.py +82 -0
- genice3/exporter/__init__.py +7 -0
- genice3/exporter/_pol.py +49 -0
- genice3/exporter/cage_survey.py +34 -0
- genice3/exporter/cif.py +117 -0
- genice3/exporter/gromacs.py +167 -0
- genice3/exporter/lammps.py +190 -0
- genice3/exporter/plotly.py +167 -0
- genice3/exporter/python.py +141 -0
- genice3/exporter/yaplot.py +175 -0
- genice3/genice.py +1741 -0
- genice3/group/__init__.py +18 -0
- genice3/group/methyl.py +26 -0
- genice3/molecule/3site.py +2 -0
- genice3/molecule/4site.py +2 -0
- genice3/molecule/5site.py +2 -0
- genice3/molecule/6site.py +3 -0
- genice3/molecule/7site.py +45 -0
- genice3/molecule/H2.py +22 -0
- genice3/molecule/NvdE.py +46 -0
- genice3/molecule/__init__.py +46 -0
- genice3/molecule/ch4.py +29 -0
- genice3/molecule/co2.py +18 -0
- genice3/molecule/empty.py +17 -0
- genice3/molecule/et.py +9 -0
- genice3/molecule/g12.py +8 -0
- genice3/molecule/g14.py +8 -0
- genice3/molecule/g15.py +8 -0
- genice3/molecule/g16.py +8 -0
- genice3/molecule/ice.py +45 -0
- genice3/molecule/me.py +9 -0
- genice3/molecule/mol.py +47 -0
- genice3/molecule/one.py +16 -0
- genice3/molecule/physical_water.py +31 -0
- genice3/molecule/spce.py +29 -0
- genice3/molecule/thf.py +43 -0
- genice3/molecule/tip3p.py +31 -0
- genice3/molecule/tip4p.py +32 -0
- genice3/molecule/tip5p.py +40 -0
- genice3/molecule/uathf.py +34 -0
- genice3/molecule/uathf6.py +30 -0
- genice3/plugin.py +434 -0
- genice3/unitcell/0.py +2 -0
- genice3/unitcell/11.py +2 -0
- genice3/unitcell/115_2_114.py +105 -0
- genice3/unitcell/11alt.py +177 -0
- genice3/unitcell/12.py +2 -0
- genice3/unitcell/12_1_11.py +111 -0
- genice3/unitcell/13.py +2 -0
- genice3/unitcell/14.py +2 -0
- genice3/unitcell/144_2_7301.py +89 -0
- genice3/unitcell/151_2_4949650.py +115 -0
- genice3/unitcell/153_2_155471.py +79 -0
- genice3/unitcell/16.py +2 -0
- genice3/unitcell/17.py +2 -0
- genice3/unitcell/176_2_5256.py +67 -0
- genice3/unitcell/1c.py +2 -0
- genice3/unitcell/1h.py +2 -0
- genice3/unitcell/1h_unit.py +2 -0
- genice3/unitcell/2.py +2 -0
- genice3/unitcell/207_1_4435.py +2 -0
- genice3/unitcell/2D3.py +117 -0
- genice3/unitcell/2_2_623457.py +133 -0
- genice3/unitcell/2d.py +2 -0
- genice3/unitcell/3.py +2 -0
- genice3/unitcell/4.py +2 -0
- genice3/unitcell/5.py +2 -0
- genice3/unitcell/5R.py +134 -0
- genice3/unitcell/6.py +2 -0
- genice3/unitcell/6h.py +47 -0
- genice3/unitcell/7.py +2 -0
- genice3/unitcell/8.py +2 -0
- genice3/unitcell/9.py +2 -0
- genice3/unitcell/A.py +2 -0
- genice3/unitcell/A15.py +2 -0
- genice3/unitcell/ACO.py +62 -0
- genice3/unitcell/B.py +2 -0
- genice3/unitcell/BSV.py +142 -0
- genice3/unitcell/C14.py +2 -0
- genice3/unitcell/C15.py +2 -0
- genice3/unitcell/C36.py +2 -0
- genice3/unitcell/CIF.py +91 -0
- genice3/unitcell/CRN1.py +3053 -0
- genice3/unitcell/CRN2.py +3053 -0
- genice3/unitcell/CRN3.py +12341 -0
- genice3/unitcell/CS1.py +92 -0
- genice3/unitcell/CS2.py +183 -0
- genice3/unitcell/CS4.py +2 -0
- genice3/unitcell/DDR.py +98 -0
- genice3/unitcell/DOH.py +110 -0
- genice3/unitcell/EMT.py +125 -0
- genice3/unitcell/FAU.py +232 -0
- genice3/unitcell/FK6layers.py +2 -0
- genice3/unitcell/FK9layers.py +2 -0
- genice3/unitcell/HS1.py +132 -0
- genice3/unitcell/HS2.py +2 -0
- genice3/unitcell/HS3.py +2 -0
- genice3/unitcell/Hcomp.py +2 -0
- genice3/unitcell/II.py +2 -0
- genice3/unitcell/III.py +2 -0
- genice3/unitcell/IV.py +2 -0
- genice3/unitcell/IWV.py +81 -0
- genice3/unitcell/IX.py +2 -0
- genice3/unitcell/Ic.py +2 -0
- genice3/unitcell/Ih.py +2 -0
- genice3/unitcell/LTA.py +70 -0
- genice3/unitcell/M.py +2 -0
- genice3/unitcell/MAR.py +118 -0
- genice3/unitcell/MEP.py +2 -0
- genice3/unitcell/MTN.py +2 -0
- genice3/unitcell/NON.py +134 -0
- genice3/unitcell/PCOD8007225.py +106 -0
- genice3/unitcell/PCOD8036144.py +139 -0
- genice3/unitcell/PCOD8204698.py +113 -0
- genice3/unitcell/PCOD8301974.py +169 -0
- genice3/unitcell/PCOD8321499.py +79 -0
- genice3/unitcell/PCOD8324623.py +75 -0
- genice3/unitcell/RHO.py +94 -0
- genice3/unitcell/SGT.py +110 -0
- genice3/unitcell/SOD.py +58 -0
- genice3/unitcell/Struct01.py +1094 -0
- genice3/unitcell/Struct02.py +1358 -0
- genice3/unitcell/Struct03.py +1037 -0
- genice3/unitcell/Struct04.py +473 -0
- genice3/unitcell/Struct05.py +1430 -0
- genice3/unitcell/Struct06.py +1430 -0
- genice3/unitcell/Struct07.py +746 -0
- genice3/unitcell/Struct08.py +1430 -0
- genice3/unitcell/Struct09.py +1430 -0
- genice3/unitcell/Struct10.py +746 -0
- genice3/unitcell/Struct11.py +746 -0
- genice3/unitcell/Struct12.py +746 -0
- genice3/unitcell/Struct13.py +746 -0
- genice3/unitcell/Struct14.py +1091 -0
- genice3/unitcell/Struct15.py +1430 -0
- genice3/unitcell/Struct16.py +731 -0
- genice3/unitcell/Struct17.py +509 -0
- genice3/unitcell/Struct18.py +374 -0
- genice3/unitcell/Struct19.py +470 -0
- genice3/unitcell/Struct20.py +404 -0
- genice3/unitcell/Struct21.py +818 -0
- genice3/unitcell/Struct22.py +1022 -0
- genice3/unitcell/Struct23.py +1022 -0
- genice3/unitcell/Struct24.py +677 -0
- genice3/unitcell/Struct25.py +983 -0
- genice3/unitcell/Struct26.py +713 -0
- genice3/unitcell/Struct27.py +1037 -0
- genice3/unitcell/Struct28.py +389 -0
- genice3/unitcell/Struct29.py +713 -0
- genice3/unitcell/Struct30.py +506 -0
- genice3/unitcell/Struct31.py +749 -0
- genice3/unitcell/Struct32.py +1091 -0
- genice3/unitcell/Struct33.py +199 -0
- genice3/unitcell/Struct34.py +542 -0
- genice3/unitcell/Struct35.py +542 -0
- genice3/unitcell/Struct36.py +542 -0
- genice3/unitcell/Struct37.py +578 -0
- genice3/unitcell/Struct38.py +578 -0
- genice3/unitcell/Struct39.py +608 -0
- genice3/unitcell/Struct40.py +614 -0
- genice3/unitcell/Struct41.py +1226 -0
- genice3/unitcell/Struct42.py +1226 -0
- genice3/unitcell/Struct43.py +1226 -0
- genice3/unitcell/Struct44.py +680 -0
- genice3/unitcell/Struct45.py +680 -0
- genice3/unitcell/Struct46.py +710 -0
- genice3/unitcell/Struct47.py +710 -0
- genice3/unitcell/Struct48.py +1370 -0
- genice3/unitcell/Struct49.py +746 -0
- genice3/unitcell/Struct50.py +746 -0
- genice3/unitcell/Struct51.py +404 -0
- genice3/unitcell/Struct52.py +1430 -0
- genice3/unitcell/Struct53.py +746 -0
- genice3/unitcell/Struct54.py +185 -0
- genice3/unitcell/Struct55.py +506 -0
- genice3/unitcell/Struct56.py +950 -0
- genice3/unitcell/Struct57.py +542 -0
- genice3/unitcell/Struct58.py +1022 -0
- genice3/unitcell/Struct59.py +1022 -0
- genice3/unitcell/Struct60.py +1022 -0
- genice3/unitcell/Struct61.py +1226 -0
- genice3/unitcell/Struct62.py +996 -0
- genice3/unitcell/Struct63.py +686 -0
- genice3/unitcell/Struct64.py +710 -0
- genice3/unitcell/Struct65.py +710 -0
- genice3/unitcell/Struct66.py +710 -0
- genice3/unitcell/Struct67.py +1358 -0
- genice3/unitcell/Struct68.py +268 -0
- genice3/unitcell/Struct69.py +407 -0
- genice3/unitcell/Struct70.py +746 -0
- genice3/unitcell/Struct71.py +744 -0
- genice3/unitcell/Struct72.py +746 -0
- genice3/unitcell/Struct73.py +746 -0
- genice3/unitcell/Struct74.py +746 -0
- genice3/unitcell/Struct75.py +746 -0
- genice3/unitcell/Struct76.py +1430 -0
- genice3/unitcell/Struct77.py +744 -0
- genice3/unitcell/Struct78.py +1430 -0
- genice3/unitcell/Struct79.py +1430 -0
- genice3/unitcell/Struct80.py +746 -0
- genice3/unitcell/Struct81.py +1091 -0
- genice3/unitcell/Struct82.py +1022 -0
- genice3/unitcell/Struct83.py +1022 -0
- genice3/unitcell/Struct84.py +542 -0
- genice3/unitcell/T.py +966 -0
- genice3/unitcell/TS1.py +213 -0
- genice3/unitcell/UNTESTED/11i.py +310 -0
- genice3/unitcell/UNTESTED/4R.py +818 -0
- genice3/unitcell/UNTESTED/YKD.py +389 -0
- genice3/unitcell/UNTESTED/__ice14.py +92 -0
- genice3/unitcell/UNTESTED/bilayer.py +266 -0
- genice3/unitcell/UNTESTED/c2te.py +91 -0
- genice3/unitcell/UNTESTED/ice1hte.py +91 -0
- genice3/unitcell/UNTESTED/iceR.py +77 -0
- genice3/unitcell/V.py +2 -0
- genice3/unitcell/VI.py +2 -0
- genice3/unitcell/VII.py +2 -0
- genice3/unitcell/VIII.py +2 -0
- genice3/unitcell/XI.py +2 -0
- genice3/unitcell/XII.py +2 -0
- genice3/unitcell/XIII.py +2 -0
- genice3/unitcell/XIc-a.py +77 -0
- genice3/unitcell/XVI.py +2 -0
- genice3/unitcell/XVII.py +2 -0
- genice3/unitcell/Z.py +2 -0
- genice3/unitcell/__init__.py +293 -0
- genice3/unitcell/__test1.py +190 -0
- genice3/unitcell/__test2.py +190 -0
- genice3/unitcell/__test3.py +65 -0
- genice3/unitcell/__test4.py +81 -0
- genice3/unitcell/_test/__init__.py +0 -0
- genice3/unitcell/_test/lattice_vs_unitcell.py +138 -0
- genice3/unitcell/_test/lattice_vs_unitcell_ordered.py +26 -0
- genice3/unitcell/c0te.py +78 -0
- genice3/unitcell/c1te.py +108 -0
- genice3/unitcell/delta.py +2 -0
- genice3/unitcell/dtc.py +76 -0
- genice3/unitcell/eleven.py +125 -0
- genice3/unitcell/engel01.py +5 -0
- genice3/unitcell/engel03.py +2 -0
- genice3/unitcell/engel04.py +2 -0
- genice3/unitcell/engel05.py +2 -0
- genice3/unitcell/engel17.py +2 -0
- genice3/unitcell/engel20.py +2 -0
- genice3/unitcell/engel23.py +5 -0
- genice3/unitcell/engel24.py +2 -0
- genice3/unitcell/engel26.py +2 -0
- genice3/unitcell/engel29.py +2 -0
- genice3/unitcell/engel30.py +2 -0
- genice3/unitcell/engel31.py +2 -0
- genice3/unitcell/engel34.py +2 -0
- genice3/unitcell/i.py +56 -0
- genice3/unitcell/ice0.py +53 -0
- genice3/unitcell/ice11.py +94 -0
- genice3/unitcell/ice12.py +63 -0
- genice3/unitcell/ice13.py +87 -0
- genice3/unitcell/ice14.py +84 -0
- genice3/unitcell/ice16.py +2 -0
- genice3/unitcell/ice17.py +51 -0
- genice3/unitcell/ice1c.py +79 -0
- genice3/unitcell/ice1h.py +59 -0
- genice3/unitcell/ice1h_unit.py +57 -0
- genice3/unitcell/ice2.py +84 -0
- genice3/unitcell/ice2d.py +112 -0
- genice3/unitcell/ice2rect.py +380 -0
- genice3/unitcell/ice3.py +53 -0
- genice3/unitcell/ice4.py +172 -0
- genice3/unitcell/ice5.py +71 -0
- genice3/unitcell/ice6.py +83 -0
- genice3/unitcell/ice7.py +101 -0
- genice3/unitcell/ice8.py +91 -0
- genice3/unitcell/ice9.py +83 -0
- genice3/unitcell/iceA.py +67 -0
- genice3/unitcell/iceB.py +62 -0
- genice3/unitcell/iceL.py +70 -0
- genice3/unitcell/iceM.py +76 -0
- genice3/unitcell/iceMd.py +18 -0
- genice3/unitcell/iceT.py +113 -0
- genice3/unitcell/iceT2.py +201 -0
- genice3/unitcell/mu.py +2 -0
- genice3/unitcell/one.py +92 -0
- genice3/unitcell/oprism.py +88 -0
- genice3/unitcell/psigma.py +2 -0
- genice3/unitcell/sH.py +2 -0
- genice3/unitcell/sI.py +2 -0
- genice3/unitcell/sII.py +2 -0
- genice3/unitcell/sIII.py +2 -0
- genice3/unitcell/sIV.py +2 -0
- genice3/unitcell/sTprime.py +65 -0
- genice3/unitcell/sV.py +2 -0
- genice3/unitcell/sVII.py +2 -0
- genice3/unitcell/sigma.py +2 -0
- genice3/unitcell/xFAU.py +218 -0
- genice3/unitcell/xdtc.py +150 -0
- genice3/unitcell/zra-d.py +2 -0
- genice3/util.py +586 -0
- genice3-3.0b0.dist-info/METADATA +110 -0
- genice3-3.0b0.dist-info/RECORD +313 -0
- genice3-3.0b0.dist-info/WHEEL +5 -0
- genice3-3.0b0.dist-info/entry_points.txt +2 -0
- genice3-3.0b0.dist-info/licenses/LICENSE +21 -0
- genice3-3.0b0.dist-info/top_level.txt +1 -0
genice3/__init__.py
ADDED
genice3/_build.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
カスタムビルドフック: symlinkを実際のファイルにコピーする
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from setuptools.command.build_py import build_py as _build_py
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BuildPy(_build_py):
|
|
11
|
+
"""symlinkを実際のファイルにコピーするカスタムビルドコマンド"""
|
|
12
|
+
|
|
13
|
+
def copy_file(self, src, dst, preserve_mode=True, preserve_times=True, link=None, level=1):
|
|
14
|
+
"""ファイルをコピーする際に、symlinkの場合は実際のファイルをコピー"""
|
|
15
|
+
# symlinkの場合は、ターゲットファイルをコピー
|
|
16
|
+
if os.path.islink(src):
|
|
17
|
+
# symlinkのターゲットを取得
|
|
18
|
+
target = os.readlink(src)
|
|
19
|
+
|
|
20
|
+
# 相対パスの場合は、srcのディレクトリからの相対パスとして解決
|
|
21
|
+
if not os.path.isabs(target):
|
|
22
|
+
src_dir = os.path.dirname(src)
|
|
23
|
+
target_path = os.path.normpath(os.path.join(src_dir, target))
|
|
24
|
+
else:
|
|
25
|
+
target_path = target
|
|
26
|
+
|
|
27
|
+
# ターゲットファイルが存在する場合のみコピー
|
|
28
|
+
if os.path.exists(target_path):
|
|
29
|
+
# ターゲットファイルをコピー(再帰的に呼び出し)
|
|
30
|
+
return super().copy_file(
|
|
31
|
+
target_path, dst, preserve_mode, preserve_times, link, level
|
|
32
|
+
)
|
|
33
|
+
else:
|
|
34
|
+
# ターゲットが見つからない場合は警告を出してスキップ
|
|
35
|
+
print(f"Warning: symlink target not found: {src} -> {target_path}")
|
|
36
|
+
return dst, False
|
|
37
|
+
|
|
38
|
+
# 通常のファイルの場合は親クラスのメソッドを呼び出す
|
|
39
|
+
return super().copy_file(src, dst, preserve_mode, preserve_times, link, level)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
genice3/cage.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for genice3
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
import string
|
|
8
|
+
from logging import getLogger
|
|
9
|
+
|
|
10
|
+
import networkx as nx
|
|
11
|
+
from cycless import center_of_graph
|
|
12
|
+
from cycless.cycles import cycles_iter
|
|
13
|
+
from cycless.polyhed import cage_to_graph, polyhedra_iter
|
|
14
|
+
from graphstat import GraphStat
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class CageSpec:
|
|
19
|
+
"""ケージの仕様。cage_type は "A12" などのケージ種別を表す文字列。"""
|
|
20
|
+
|
|
21
|
+
cage_type: str # A12, 1b, etc.
|
|
22
|
+
faces: str # 5^12 6^2, etc.
|
|
23
|
+
graph: nx.Graph # ケージを構成する水分子ノードのグラフ
|
|
24
|
+
|
|
25
|
+
def to_json_capable_data(self) -> dict:
|
|
26
|
+
return {
|
|
27
|
+
"cage_type": self.cage_type,
|
|
28
|
+
"faces": self.faces,
|
|
29
|
+
"nodes": [int(x) for x in self.graph],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def __repr__(self) -> str:
|
|
33
|
+
return (
|
|
34
|
+
f"CageSpec(cage_type={self.cage_type!r}, "
|
|
35
|
+
f"faces={self.faces!r}, "
|
|
36
|
+
f"n_nodes={self.graph.number_of_nodes()})"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def __str__(self) -> str:
|
|
40
|
+
return f"{self.cage_type} ({self.faces}) {self.graph.number_of_nodes()} nodes"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class CageSpecs:
|
|
45
|
+
"""拡大単位胞内の全ケージの位置と仕様。
|
|
46
|
+
|
|
47
|
+
specs: 各ケージの CageSpec(cage_type, faces, graph)。
|
|
48
|
+
positions: 分数座標の配列(Nx3)。specs と同順。
|
|
49
|
+
node_to_cage_indices: __post_init__ で生成。ノード番号 → そのノードを含むケージのインデックスリスト。
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
specs: list[CageSpec]
|
|
53
|
+
positions: np.ndarray # in fractional coordinates
|
|
54
|
+
|
|
55
|
+
def __post_init__(self) -> None:
|
|
56
|
+
"""ノード番号 → ケージ番号の逆引き辞書を生成。"""
|
|
57
|
+
self.node_to_cage_indices: dict[int, list[int]] = {}
|
|
58
|
+
for cage_idx, spec in enumerate(self.specs):
|
|
59
|
+
for node in spec.graph:
|
|
60
|
+
self.node_to_cage_indices.setdefault(int(node), []).append(cage_idx)
|
|
61
|
+
|
|
62
|
+
def to_json_capable_data(self) -> dict:
|
|
63
|
+
"""JSON シリアライズ用の辞書を返す。キーはケージインデックス、値は frac_pos と specs。"""
|
|
64
|
+
data = []
|
|
65
|
+
for position, specs in zip(self.positions, self.specs):
|
|
66
|
+
data.append(
|
|
67
|
+
{
|
|
68
|
+
"frac_pos": position.tolist(),
|
|
69
|
+
"specs": specs.to_json_capable_data(),
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
return dict(enumerate(data))
|
|
73
|
+
|
|
74
|
+
def __repr__(self) -> str:
|
|
75
|
+
return (
|
|
76
|
+
f"CageSpecs(n_cages={len(self.specs)}, "
|
|
77
|
+
f"positions_shape={self.positions.shape})"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _assign_cage_type(basename: int, existing_types: set[str]) -> str:
|
|
82
|
+
"""未使用のケージタイプ名(A12, A12a など)を生成する。"""
|
|
83
|
+
enum = 0
|
|
84
|
+
cage_type = f"A{basename}"
|
|
85
|
+
while cage_type in existing_types:
|
|
86
|
+
char = string.ascii_lowercase[enum]
|
|
87
|
+
cage_type = f"A{basename}{char}"
|
|
88
|
+
enum += 1
|
|
89
|
+
return cage_type
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _make_cage_expression(ring_ids: list, ringlist: list) -> str:
|
|
93
|
+
"""ケージを構成するリングのサイズから "5^12 6^2" のような面表現文字列を生成する。"""
|
|
94
|
+
ringcount = [0 for i in range(9)]
|
|
95
|
+
for ring in ring_ids:
|
|
96
|
+
ringcount[len(ringlist[ring])] += 1
|
|
97
|
+
index = []
|
|
98
|
+
for i in range(9):
|
|
99
|
+
if ringcount[i] > 0:
|
|
100
|
+
index.append(f"{i}^{ringcount[i]}")
|
|
101
|
+
index = " ".join(index)
|
|
102
|
+
return index
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def assess_cages(
|
|
106
|
+
graph: nx.Graph, node_frac: np.ndarray
|
|
107
|
+
) -> CageSpecs:
|
|
108
|
+
"""水素結合グラフとノードの分数座標からケージを検出・分類し、CageSpecs を返す。
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
graph: 水素結合ネットワーク(無向グラフ)。
|
|
112
|
+
node_frac: ノードの分数座標(Nx3)。
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
検出されたケージの位置(分数座標)と仕様(CageSpec のリスト)。
|
|
116
|
+
"""
|
|
117
|
+
logger = getLogger()
|
|
118
|
+
|
|
119
|
+
# Prepare the list of rings
|
|
120
|
+
# taking the positions in PBC into account.
|
|
121
|
+
ringlist = [
|
|
122
|
+
[int(x) for x in ring]
|
|
123
|
+
for ring in cycles_iter(nx.Graph(graph), 8, pos=node_frac)
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
MaxCageSize = 22
|
|
127
|
+
cage_fracs = []
|
|
128
|
+
# data storage of the found cages
|
|
129
|
+
db = GraphStat()
|
|
130
|
+
existing_cage_types: set[str] = set()
|
|
131
|
+
g_id_to_cage_type: dict[int, str] = {}
|
|
132
|
+
|
|
133
|
+
# Detect cages and classify
|
|
134
|
+
cages = [cage for cage in polyhedra_iter(ringlist, MaxCageSize)]
|
|
135
|
+
cage_graphs = [cage_to_graph(cage, ringlist) for cage in cages]
|
|
136
|
+
cage_fracs = [center_of_graph(g, node_frac) for g in cage_graphs]
|
|
137
|
+
if len(cage_fracs) == 0:
|
|
138
|
+
logger.info(" No cages detected.")
|
|
139
|
+
|
|
140
|
+
cagespecs = []
|
|
141
|
+
for cage, g in zip(cages, cage_graphs):
|
|
142
|
+
cagesize = len(cage)
|
|
143
|
+
graph_id = db.query_id(g)
|
|
144
|
+
# if it is a new cage type
|
|
145
|
+
if graph_id < 0:
|
|
146
|
+
# new type!
|
|
147
|
+
# register the last query
|
|
148
|
+
graph_id = db.register()
|
|
149
|
+
|
|
150
|
+
cage_type = _assign_cage_type(cagesize, existing_cage_types)
|
|
151
|
+
g_id_to_cage_type[graph_id] = cage_type
|
|
152
|
+
existing_cage_types.add(cage_type)
|
|
153
|
+
else:
|
|
154
|
+
cage_type = g_id_to_cage_type[graph_id]
|
|
155
|
+
faces = _make_cage_expression(cage, ringlist)
|
|
156
|
+
cagespecs.append(CageSpec(cage_type=cage_type, faces=faces, graph=g))
|
|
157
|
+
# print(f"{label=}, {faces=}, {ringlist=}")
|
|
158
|
+
# print([len(ringlist[ring]) for ring in cage])
|
|
159
|
+
|
|
160
|
+
return CageSpecs(specs=cagespecs, positions=np.array(cage_fracs))
|
genice3/cli/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
genice3 CLI パッケージ。
|
|
3
|
+
|
|
4
|
+
- option_parser: option_handling.md に基づくオプション文字列パーサー
|
|
5
|
+
- runner: argv のパースとプラグイン dispatch(parse_argv, validate_result)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from genice3.cli.option_parser import (
|
|
9
|
+
parse_options,
|
|
10
|
+
structure_for_display,
|
|
11
|
+
scalarize_single_item_lists,
|
|
12
|
+
structure_to_option_string,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"parse_options",
|
|
17
|
+
"structure_for_display",
|
|
18
|
+
"scalarize_single_item_lists",
|
|
19
|
+
"structure_to_option_string",
|
|
20
|
+
]
|
genice3/cli/genice.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from logging import getLogger, basicConfig, DEBUG, INFO
|
|
2
|
+
import sys
|
|
3
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
4
|
+
|
|
5
|
+
from genice3.plugin import safe_import
|
|
6
|
+
from genice3.genice import GenIce3, log_cation_cages, log_spot_cation_cages
|
|
7
|
+
from genice3.cli.runner import parse_argv, validate_result
|
|
8
|
+
from genice3.cli.options import (
|
|
9
|
+
BASE_HELP_ORDER,
|
|
10
|
+
get_option_def,
|
|
11
|
+
format_option_for_help,
|
|
12
|
+
extract_genice_args,
|
|
13
|
+
validate_parsed_options,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_version():
|
|
18
|
+
"""パッケージのバージョンを取得する。見つからない場合はデフォルト値を返す。"""
|
|
19
|
+
try:
|
|
20
|
+
return version("genice3")
|
|
21
|
+
except PackageNotFoundError:
|
|
22
|
+
return "3.*.*.*"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ヘルプ整形: 先頭2スペース+オプション書式が24文字超なら改行。説明は28〜72桁で折り返し。
|
|
26
|
+
_HELP_OPTION_MAX = 24 # 2スペース+オプションがこの桁数までなら1行に
|
|
27
|
+
_HELP_DESC_START = 28 # 説明開始桁(1-based)
|
|
28
|
+
_HELP_DESC_END = 72
|
|
29
|
+
_HELP_DESC_WIDTH = _HELP_DESC_END - _HELP_DESC_START + 1 # 45
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _wrap_desc(text: str, width: int) -> list[str]:
|
|
33
|
+
"""説明文を width 文字で折り返した行リストを返す"""
|
|
34
|
+
words = text.split()
|
|
35
|
+
lines: list[str] = []
|
|
36
|
+
current: list[str] = []
|
|
37
|
+
current_len = 0
|
|
38
|
+
for w in words:
|
|
39
|
+
need = len(w) + (1 if current else 0)
|
|
40
|
+
if current_len + need <= width:
|
|
41
|
+
current.append(w)
|
|
42
|
+
current_len += need
|
|
43
|
+
else:
|
|
44
|
+
if current:
|
|
45
|
+
lines.append(" ".join(current))
|
|
46
|
+
current = [w]
|
|
47
|
+
current_len = len(w)
|
|
48
|
+
if current:
|
|
49
|
+
lines.append(" ".join(current))
|
|
50
|
+
return lines
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _opt_line(option: str, description: str) -> list[str]:
|
|
54
|
+
"""オプション1件を指定ルールで整形。2スペース+オプションが24文字超なら改行。説明は28〜72桁で折り畳み。"""
|
|
55
|
+
head_len = 2 + len(option) # 先頭2スペース+オプション
|
|
56
|
+
desc_lines = _wrap_desc(description, _HELP_DESC_WIDTH)
|
|
57
|
+
indent = " " * (_HELP_DESC_START - 1) # 説明行の左インデント(27スペース)
|
|
58
|
+
|
|
59
|
+
if head_len > _HELP_OPTION_MAX:
|
|
60
|
+
# オプションだけ1行、説明は次の行から28桁目から
|
|
61
|
+
out = [" " + option]
|
|
62
|
+
for line in desc_lines:
|
|
63
|
+
out.append(indent + line)
|
|
64
|
+
return out
|
|
65
|
+
# オプション+説明を同じ行から(オプション後に空白を入れて28桁目に合わせる)
|
|
66
|
+
pad = _HELP_DESC_START - 1 - head_len # 2 + len(option) + pad == 27
|
|
67
|
+
out = [" " + option + " " * pad + desc_lines[0]]
|
|
68
|
+
for line in desc_lines[1:]:
|
|
69
|
+
out.append(indent + line)
|
|
70
|
+
return out
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def print_help():
|
|
74
|
+
"""ヘルプメッセージを表示(options のオプション定義から組み立て)"""
|
|
75
|
+
print("Usage: genice3 [OPTIONS] UNITCELL")
|
|
76
|
+
print()
|
|
77
|
+
print("Options:")
|
|
78
|
+
seen = set() # rep / replication_factors は同じ行なので1回だけ表示
|
|
79
|
+
for name in BASE_HELP_ORDER:
|
|
80
|
+
if name in seen:
|
|
81
|
+
continue
|
|
82
|
+
def_ = get_option_def(name)
|
|
83
|
+
if def_ is None or def_.help_text is None:
|
|
84
|
+
continue
|
|
85
|
+
if def_.name == "replication_factors":
|
|
86
|
+
seen.add("rep")
|
|
87
|
+
seen.add(def_.name)
|
|
88
|
+
opt_str = format_option_for_help(def_)
|
|
89
|
+
for line in _opt_line(opt_str, def_.help_text):
|
|
90
|
+
print(line)
|
|
91
|
+
print()
|
|
92
|
+
print("Arguments:")
|
|
93
|
+
for line in _opt_line("UNITCELL", "Unitcell plugin name (required)"):
|
|
94
|
+
print(line)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def main() -> None:
|
|
98
|
+
"""メイン関数"""
|
|
99
|
+
# --helpと--versionを先に処理
|
|
100
|
+
if "--help" in sys.argv or "-h" in sys.argv:
|
|
101
|
+
print_help()
|
|
102
|
+
sys.exit(0)
|
|
103
|
+
if "--version" in sys.argv or "-V" in sys.argv:
|
|
104
|
+
print(f"genice3 {get_version()}")
|
|
105
|
+
sys.exit(0)
|
|
106
|
+
|
|
107
|
+
# ロギングを初期化(デフォルトはINFOレベル)
|
|
108
|
+
basicConfig(level=INFO)
|
|
109
|
+
logger = getLogger()
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
result = parse_argv(sys.argv[1:])
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"パースエラー: {e}")
|
|
115
|
+
import traceback
|
|
116
|
+
|
|
117
|
+
traceback.print_exc()
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
|
|
120
|
+
# バリデーション
|
|
121
|
+
is_valid, errors = validate_result(result)
|
|
122
|
+
if not is_valid:
|
|
123
|
+
for error in errors:
|
|
124
|
+
logger.error(error)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
base_options = result["base_options"]
|
|
128
|
+
if base_options.get("debug"):
|
|
129
|
+
logger.setLevel(DEBUG)
|
|
130
|
+
for handler in logger.handlers:
|
|
131
|
+
handler.setLevel(DEBUG)
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
validate_parsed_options(base_options)
|
|
135
|
+
except ValueError as e:
|
|
136
|
+
logger.error(str(e))
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
|
|
139
|
+
genice_kwargs = extract_genice_args(base_options)
|
|
140
|
+
logger.debug(f"Settings: {genice_kwargs}")
|
|
141
|
+
|
|
142
|
+
unitcell_name = result["unitcell"]["name"]
|
|
143
|
+
unitcell_processed = result["unitcell"]["processed"]
|
|
144
|
+
exporter_name = result["exporter"]["name"] or "gromacs"
|
|
145
|
+
exporter_processed = result["exporter"]["processed"]
|
|
146
|
+
|
|
147
|
+
genice = GenIce3(**genice_kwargs)
|
|
148
|
+
|
|
149
|
+
# unitcellプラグインを設定
|
|
150
|
+
genice.unitcell = safe_import("unitcell", unitcell_name).UnitCell(
|
|
151
|
+
**unitcell_processed
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
log_spot_cation_cages(genice)
|
|
155
|
+
log_cation_cages(genice)
|
|
156
|
+
|
|
157
|
+
# コマンドライン全体を取得
|
|
158
|
+
command_line = " ".join(sys.argv)
|
|
159
|
+
exporter_processed["command_line"] = command_line
|
|
160
|
+
|
|
161
|
+
# exporterプラグインを実行
|
|
162
|
+
safe_import("exporter", exporter_name).dump(
|
|
163
|
+
genice, sys.stdout, **exporter_processed
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""OptionDef と型定数(--help と検証用)。options と validator の両方から参照する。"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Callable, Optional
|
|
5
|
+
|
|
6
|
+
OPTION_TYPE_FLAG = "flag"
|
|
7
|
+
OPTION_TYPE_STRING = "string"
|
|
8
|
+
OPTION_TYPE_TUPLE = "tuple"
|
|
9
|
+
OPTION_TYPE_KEYVALUE = "keyvalue"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class OptionDef:
|
|
14
|
+
"""CLI で認識するオプションの定義(ヘルプ・検証用)。"""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
short: Optional[str] = None
|
|
18
|
+
is_flag: bool = False
|
|
19
|
+
max_values: Optional[int] = None
|
|
20
|
+
level: str = "base"
|
|
21
|
+
parse_type: Optional[str] = None
|
|
22
|
+
metavar: Optional[str] = None
|
|
23
|
+
help_text: Optional[str] = None
|
|
24
|
+
help_format: Optional[str] = None
|
|
25
|
+
parse_validator: Optional[Callable[[Any], None]] = None
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
option_handling.md のルールに従うオプション文字列パーサー。
|
|
3
|
+
|
|
4
|
+
- 第一引数 = unitcell、以降は -- / : で第一・第二階層を区別。
|
|
5
|
+
- 引数は文字列のまま(= で分割しない)。各オプションはリストで、
|
|
6
|
+
要素はスカラー(引数1個のみ)または 1 キーの辞書 { 引数: サブオプション辞書 }。
|
|
7
|
+
- 構造体 ↔ オプション文字列 ↔ YAML 表現の往復が可能。
|
|
8
|
+
|
|
9
|
+
CLI は runner.parse_argv がこのモジュールを利用する。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any, Dict, List, Union
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def parse_options(line: str) -> Dict[str, Any]:
|
|
18
|
+
"""
|
|
19
|
+
空白で分割し、先頭を unitcell、以降を -- / : のルールで構造体に格納する。
|
|
20
|
+
|
|
21
|
+
- 第一引数 = unitcell(省略不可)
|
|
22
|
+
- -- で始まる = 第一階層オプション名、直後からが引数
|
|
23
|
+
- : で始まる = 第二階層(直前の第一階層に引数が1個だけのときのみ)
|
|
24
|
+
- 引数はすべて文字列のまま。= で分割しない。
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: unitcell が空、または第二階層を引数が1個でない第一階層に付けた場合。
|
|
28
|
+
"""
|
|
29
|
+
tokens = line.split()
|
|
30
|
+
if not tokens or tokens[0].startswith("-") or tokens[0].startswith(":"):
|
|
31
|
+
raise ValueError("unitcell が必要(先頭に単位胞名を指定してください)")
|
|
32
|
+
unitcell = tokens[0]
|
|
33
|
+
i = 1
|
|
34
|
+
result: Dict[str, Any] = {"unitcell": unitcell}
|
|
35
|
+
|
|
36
|
+
while i < len(tokens):
|
|
37
|
+
if not tokens[i].startswith("--"):
|
|
38
|
+
i += 1
|
|
39
|
+
continue
|
|
40
|
+
opt_name = tokens[i][2:]
|
|
41
|
+
i += 1
|
|
42
|
+
args: List[str] = []
|
|
43
|
+
subopts: Dict[str, List[str]] = {}
|
|
44
|
+
|
|
45
|
+
while i < len(tokens) and not tokens[i].startswith("--"):
|
|
46
|
+
if tokens[i].startswith(":"):
|
|
47
|
+
if len(args) != 1:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"第二階層は第一階層の引数が1個のときのみ: {opt_name} の引数数={len(args)}"
|
|
50
|
+
)
|
|
51
|
+
sub_name = tokens[i][1:]
|
|
52
|
+
i += 1
|
|
53
|
+
sub_args: List[str] = []
|
|
54
|
+
while i < len(tokens) and not tokens[i].startswith("--") and not tokens[i].startswith(":"):
|
|
55
|
+
sub_args.append(tokens[i])
|
|
56
|
+
i += 1
|
|
57
|
+
subopts[sub_name] = sub_args
|
|
58
|
+
else:
|
|
59
|
+
args.append(tokens[i])
|
|
60
|
+
i += 1
|
|
61
|
+
|
|
62
|
+
if opt_name not in result:
|
|
63
|
+
result[opt_name] = []
|
|
64
|
+
|
|
65
|
+
if subopts:
|
|
66
|
+
result[opt_name].append({args[0]: subopts})
|
|
67
|
+
else:
|
|
68
|
+
for a in args:
|
|
69
|
+
result[opt_name].append([a])
|
|
70
|
+
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _normalize_item(item: Union[list, dict]) -> Union[list, dict, str]:
|
|
75
|
+
"""要素1個のリストはスカラーに置き換えてよい、の正規化(表示用)。辞書はそのまま。"""
|
|
76
|
+
if isinstance(item, list) and len(item) == 1 and isinstance(item[0], str):
|
|
77
|
+
return item[0]
|
|
78
|
+
return item
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def structure_for_display(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
82
|
+
"""構造体を「要素1個のリスト→スカラー」で表示用に整える。"""
|
|
83
|
+
out: Dict[str, Any] = {"unitcell": data["unitcell"]}
|
|
84
|
+
for k, v in data.items():
|
|
85
|
+
if k == "unitcell":
|
|
86
|
+
continue
|
|
87
|
+
if isinstance(v, list):
|
|
88
|
+
out[k] = [_normalize_item(e) for e in v]
|
|
89
|
+
else:
|
|
90
|
+
out[k] = v
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def scalarize_single_item_lists(
|
|
95
|
+
obj: Union[Dict[str, Any], List[Any], str, int, float]
|
|
96
|
+
) -> Union[Dict[str, Any], List[Any], str, int, float]:
|
|
97
|
+
"""
|
|
98
|
+
再帰的に、要素が1個だけのリストをそのスカラーに置き換える。
|
|
99
|
+
YAML 出力前に適用すると、water: ['4site^ice'] → water: 4site^ice のようになる。
|
|
100
|
+
"""
|
|
101
|
+
if isinstance(obj, list):
|
|
102
|
+
if len(obj) == 1:
|
|
103
|
+
return scalarize_single_item_lists(obj[0])
|
|
104
|
+
return [scalarize_single_item_lists(e) for e in obj]
|
|
105
|
+
if isinstance(obj, dict):
|
|
106
|
+
return {k: scalarize_single_item_lists(v) for k, v in obj.items()}
|
|
107
|
+
return obj
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def structure_to_option_string(data: Dict[str, Any]) -> str:
|
|
111
|
+
"""
|
|
112
|
+
YAML 表現で使った構造体(単成分リストをスカラーにしたもの)を、
|
|
113
|
+
オプション文字列に戻して 1 行で返す。
|
|
114
|
+
サブオプションがない要素ばかりの場合は、--key を1回だけ出して引数を連続で並べる。
|
|
115
|
+
"""
|
|
116
|
+
parts: List[str] = [str(data["unitcell"])]
|
|
117
|
+
for key, value in data.items():
|
|
118
|
+
if key == "unitcell":
|
|
119
|
+
continue
|
|
120
|
+
items: List[Any] = value if isinstance(value, list) else [value]
|
|
121
|
+
run: List[str] = [] # サブオプションなしの連続要素
|
|
122
|
+
for item in items:
|
|
123
|
+
if isinstance(item, dict):
|
|
124
|
+
if run:
|
|
125
|
+
parts.append(f"--{key}")
|
|
126
|
+
parts.extend(run)
|
|
127
|
+
run = []
|
|
128
|
+
(arg, subopts), = item.items()
|
|
129
|
+
parts.append(f"--{key}")
|
|
130
|
+
parts.append(str(arg))
|
|
131
|
+
for subk, subv in subopts.items():
|
|
132
|
+
parts.append(f":{subk}")
|
|
133
|
+
if isinstance(subv, list):
|
|
134
|
+
parts.extend(str(x) for x in subv)
|
|
135
|
+
else:
|
|
136
|
+
parts.append(str(subv))
|
|
137
|
+
else:
|
|
138
|
+
run.append(str(item))
|
|
139
|
+
if run:
|
|
140
|
+
parts.append(f"--{key}")
|
|
141
|
+
parts.extend(run)
|
|
142
|
+
return " ".join(parts)
|