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.
Files changed (313) hide show
  1. genice3/__init__.py +7 -0
  2. genice3/_build.py +43 -0
  3. genice3/cage.py +160 -0
  4. genice3/cli/__init__.py +20 -0
  5. genice3/cli/genice.py +168 -0
  6. genice3/cli/option_def.py +25 -0
  7. genice3/cli/option_parser.py +142 -0
  8. genice3/cli/options.py +556 -0
  9. genice3/cli/runner.py +207 -0
  10. genice3/cli/validator.py +157 -0
  11. genice3/dependencyengine.py +102 -0
  12. genice3/exporter/_KG.py +82 -0
  13. genice3/exporter/__init__.py +7 -0
  14. genice3/exporter/_pol.py +49 -0
  15. genice3/exporter/cage_survey.py +34 -0
  16. genice3/exporter/cif.py +117 -0
  17. genice3/exporter/gromacs.py +167 -0
  18. genice3/exporter/lammps.py +190 -0
  19. genice3/exporter/plotly.py +167 -0
  20. genice3/exporter/python.py +141 -0
  21. genice3/exporter/yaplot.py +175 -0
  22. genice3/genice.py +1741 -0
  23. genice3/group/__init__.py +18 -0
  24. genice3/group/methyl.py +26 -0
  25. genice3/molecule/3site.py +2 -0
  26. genice3/molecule/4site.py +2 -0
  27. genice3/molecule/5site.py +2 -0
  28. genice3/molecule/6site.py +3 -0
  29. genice3/molecule/7site.py +45 -0
  30. genice3/molecule/H2.py +22 -0
  31. genice3/molecule/NvdE.py +46 -0
  32. genice3/molecule/__init__.py +46 -0
  33. genice3/molecule/ch4.py +29 -0
  34. genice3/molecule/co2.py +18 -0
  35. genice3/molecule/empty.py +17 -0
  36. genice3/molecule/et.py +9 -0
  37. genice3/molecule/g12.py +8 -0
  38. genice3/molecule/g14.py +8 -0
  39. genice3/molecule/g15.py +8 -0
  40. genice3/molecule/g16.py +8 -0
  41. genice3/molecule/ice.py +45 -0
  42. genice3/molecule/me.py +9 -0
  43. genice3/molecule/mol.py +47 -0
  44. genice3/molecule/one.py +16 -0
  45. genice3/molecule/physical_water.py +31 -0
  46. genice3/molecule/spce.py +29 -0
  47. genice3/molecule/thf.py +43 -0
  48. genice3/molecule/tip3p.py +31 -0
  49. genice3/molecule/tip4p.py +32 -0
  50. genice3/molecule/tip5p.py +40 -0
  51. genice3/molecule/uathf.py +34 -0
  52. genice3/molecule/uathf6.py +30 -0
  53. genice3/plugin.py +434 -0
  54. genice3/unitcell/0.py +2 -0
  55. genice3/unitcell/11.py +2 -0
  56. genice3/unitcell/115_2_114.py +105 -0
  57. genice3/unitcell/11alt.py +177 -0
  58. genice3/unitcell/12.py +2 -0
  59. genice3/unitcell/12_1_11.py +111 -0
  60. genice3/unitcell/13.py +2 -0
  61. genice3/unitcell/14.py +2 -0
  62. genice3/unitcell/144_2_7301.py +89 -0
  63. genice3/unitcell/151_2_4949650.py +115 -0
  64. genice3/unitcell/153_2_155471.py +79 -0
  65. genice3/unitcell/16.py +2 -0
  66. genice3/unitcell/17.py +2 -0
  67. genice3/unitcell/176_2_5256.py +67 -0
  68. genice3/unitcell/1c.py +2 -0
  69. genice3/unitcell/1h.py +2 -0
  70. genice3/unitcell/1h_unit.py +2 -0
  71. genice3/unitcell/2.py +2 -0
  72. genice3/unitcell/207_1_4435.py +2 -0
  73. genice3/unitcell/2D3.py +117 -0
  74. genice3/unitcell/2_2_623457.py +133 -0
  75. genice3/unitcell/2d.py +2 -0
  76. genice3/unitcell/3.py +2 -0
  77. genice3/unitcell/4.py +2 -0
  78. genice3/unitcell/5.py +2 -0
  79. genice3/unitcell/5R.py +134 -0
  80. genice3/unitcell/6.py +2 -0
  81. genice3/unitcell/6h.py +47 -0
  82. genice3/unitcell/7.py +2 -0
  83. genice3/unitcell/8.py +2 -0
  84. genice3/unitcell/9.py +2 -0
  85. genice3/unitcell/A.py +2 -0
  86. genice3/unitcell/A15.py +2 -0
  87. genice3/unitcell/ACO.py +62 -0
  88. genice3/unitcell/B.py +2 -0
  89. genice3/unitcell/BSV.py +142 -0
  90. genice3/unitcell/C14.py +2 -0
  91. genice3/unitcell/C15.py +2 -0
  92. genice3/unitcell/C36.py +2 -0
  93. genice3/unitcell/CIF.py +91 -0
  94. genice3/unitcell/CRN1.py +3053 -0
  95. genice3/unitcell/CRN2.py +3053 -0
  96. genice3/unitcell/CRN3.py +12341 -0
  97. genice3/unitcell/CS1.py +92 -0
  98. genice3/unitcell/CS2.py +183 -0
  99. genice3/unitcell/CS4.py +2 -0
  100. genice3/unitcell/DDR.py +98 -0
  101. genice3/unitcell/DOH.py +110 -0
  102. genice3/unitcell/EMT.py +125 -0
  103. genice3/unitcell/FAU.py +232 -0
  104. genice3/unitcell/FK6layers.py +2 -0
  105. genice3/unitcell/FK9layers.py +2 -0
  106. genice3/unitcell/HS1.py +132 -0
  107. genice3/unitcell/HS2.py +2 -0
  108. genice3/unitcell/HS3.py +2 -0
  109. genice3/unitcell/Hcomp.py +2 -0
  110. genice3/unitcell/II.py +2 -0
  111. genice3/unitcell/III.py +2 -0
  112. genice3/unitcell/IV.py +2 -0
  113. genice3/unitcell/IWV.py +81 -0
  114. genice3/unitcell/IX.py +2 -0
  115. genice3/unitcell/Ic.py +2 -0
  116. genice3/unitcell/Ih.py +2 -0
  117. genice3/unitcell/LTA.py +70 -0
  118. genice3/unitcell/M.py +2 -0
  119. genice3/unitcell/MAR.py +118 -0
  120. genice3/unitcell/MEP.py +2 -0
  121. genice3/unitcell/MTN.py +2 -0
  122. genice3/unitcell/NON.py +134 -0
  123. genice3/unitcell/PCOD8007225.py +106 -0
  124. genice3/unitcell/PCOD8036144.py +139 -0
  125. genice3/unitcell/PCOD8204698.py +113 -0
  126. genice3/unitcell/PCOD8301974.py +169 -0
  127. genice3/unitcell/PCOD8321499.py +79 -0
  128. genice3/unitcell/PCOD8324623.py +75 -0
  129. genice3/unitcell/RHO.py +94 -0
  130. genice3/unitcell/SGT.py +110 -0
  131. genice3/unitcell/SOD.py +58 -0
  132. genice3/unitcell/Struct01.py +1094 -0
  133. genice3/unitcell/Struct02.py +1358 -0
  134. genice3/unitcell/Struct03.py +1037 -0
  135. genice3/unitcell/Struct04.py +473 -0
  136. genice3/unitcell/Struct05.py +1430 -0
  137. genice3/unitcell/Struct06.py +1430 -0
  138. genice3/unitcell/Struct07.py +746 -0
  139. genice3/unitcell/Struct08.py +1430 -0
  140. genice3/unitcell/Struct09.py +1430 -0
  141. genice3/unitcell/Struct10.py +746 -0
  142. genice3/unitcell/Struct11.py +746 -0
  143. genice3/unitcell/Struct12.py +746 -0
  144. genice3/unitcell/Struct13.py +746 -0
  145. genice3/unitcell/Struct14.py +1091 -0
  146. genice3/unitcell/Struct15.py +1430 -0
  147. genice3/unitcell/Struct16.py +731 -0
  148. genice3/unitcell/Struct17.py +509 -0
  149. genice3/unitcell/Struct18.py +374 -0
  150. genice3/unitcell/Struct19.py +470 -0
  151. genice3/unitcell/Struct20.py +404 -0
  152. genice3/unitcell/Struct21.py +818 -0
  153. genice3/unitcell/Struct22.py +1022 -0
  154. genice3/unitcell/Struct23.py +1022 -0
  155. genice3/unitcell/Struct24.py +677 -0
  156. genice3/unitcell/Struct25.py +983 -0
  157. genice3/unitcell/Struct26.py +713 -0
  158. genice3/unitcell/Struct27.py +1037 -0
  159. genice3/unitcell/Struct28.py +389 -0
  160. genice3/unitcell/Struct29.py +713 -0
  161. genice3/unitcell/Struct30.py +506 -0
  162. genice3/unitcell/Struct31.py +749 -0
  163. genice3/unitcell/Struct32.py +1091 -0
  164. genice3/unitcell/Struct33.py +199 -0
  165. genice3/unitcell/Struct34.py +542 -0
  166. genice3/unitcell/Struct35.py +542 -0
  167. genice3/unitcell/Struct36.py +542 -0
  168. genice3/unitcell/Struct37.py +578 -0
  169. genice3/unitcell/Struct38.py +578 -0
  170. genice3/unitcell/Struct39.py +608 -0
  171. genice3/unitcell/Struct40.py +614 -0
  172. genice3/unitcell/Struct41.py +1226 -0
  173. genice3/unitcell/Struct42.py +1226 -0
  174. genice3/unitcell/Struct43.py +1226 -0
  175. genice3/unitcell/Struct44.py +680 -0
  176. genice3/unitcell/Struct45.py +680 -0
  177. genice3/unitcell/Struct46.py +710 -0
  178. genice3/unitcell/Struct47.py +710 -0
  179. genice3/unitcell/Struct48.py +1370 -0
  180. genice3/unitcell/Struct49.py +746 -0
  181. genice3/unitcell/Struct50.py +746 -0
  182. genice3/unitcell/Struct51.py +404 -0
  183. genice3/unitcell/Struct52.py +1430 -0
  184. genice3/unitcell/Struct53.py +746 -0
  185. genice3/unitcell/Struct54.py +185 -0
  186. genice3/unitcell/Struct55.py +506 -0
  187. genice3/unitcell/Struct56.py +950 -0
  188. genice3/unitcell/Struct57.py +542 -0
  189. genice3/unitcell/Struct58.py +1022 -0
  190. genice3/unitcell/Struct59.py +1022 -0
  191. genice3/unitcell/Struct60.py +1022 -0
  192. genice3/unitcell/Struct61.py +1226 -0
  193. genice3/unitcell/Struct62.py +996 -0
  194. genice3/unitcell/Struct63.py +686 -0
  195. genice3/unitcell/Struct64.py +710 -0
  196. genice3/unitcell/Struct65.py +710 -0
  197. genice3/unitcell/Struct66.py +710 -0
  198. genice3/unitcell/Struct67.py +1358 -0
  199. genice3/unitcell/Struct68.py +268 -0
  200. genice3/unitcell/Struct69.py +407 -0
  201. genice3/unitcell/Struct70.py +746 -0
  202. genice3/unitcell/Struct71.py +744 -0
  203. genice3/unitcell/Struct72.py +746 -0
  204. genice3/unitcell/Struct73.py +746 -0
  205. genice3/unitcell/Struct74.py +746 -0
  206. genice3/unitcell/Struct75.py +746 -0
  207. genice3/unitcell/Struct76.py +1430 -0
  208. genice3/unitcell/Struct77.py +744 -0
  209. genice3/unitcell/Struct78.py +1430 -0
  210. genice3/unitcell/Struct79.py +1430 -0
  211. genice3/unitcell/Struct80.py +746 -0
  212. genice3/unitcell/Struct81.py +1091 -0
  213. genice3/unitcell/Struct82.py +1022 -0
  214. genice3/unitcell/Struct83.py +1022 -0
  215. genice3/unitcell/Struct84.py +542 -0
  216. genice3/unitcell/T.py +966 -0
  217. genice3/unitcell/TS1.py +213 -0
  218. genice3/unitcell/UNTESTED/11i.py +310 -0
  219. genice3/unitcell/UNTESTED/4R.py +818 -0
  220. genice3/unitcell/UNTESTED/YKD.py +389 -0
  221. genice3/unitcell/UNTESTED/__ice14.py +92 -0
  222. genice3/unitcell/UNTESTED/bilayer.py +266 -0
  223. genice3/unitcell/UNTESTED/c2te.py +91 -0
  224. genice3/unitcell/UNTESTED/ice1hte.py +91 -0
  225. genice3/unitcell/UNTESTED/iceR.py +77 -0
  226. genice3/unitcell/V.py +2 -0
  227. genice3/unitcell/VI.py +2 -0
  228. genice3/unitcell/VII.py +2 -0
  229. genice3/unitcell/VIII.py +2 -0
  230. genice3/unitcell/XI.py +2 -0
  231. genice3/unitcell/XII.py +2 -0
  232. genice3/unitcell/XIII.py +2 -0
  233. genice3/unitcell/XIc-a.py +77 -0
  234. genice3/unitcell/XVI.py +2 -0
  235. genice3/unitcell/XVII.py +2 -0
  236. genice3/unitcell/Z.py +2 -0
  237. genice3/unitcell/__init__.py +293 -0
  238. genice3/unitcell/__test1.py +190 -0
  239. genice3/unitcell/__test2.py +190 -0
  240. genice3/unitcell/__test3.py +65 -0
  241. genice3/unitcell/__test4.py +81 -0
  242. genice3/unitcell/_test/__init__.py +0 -0
  243. genice3/unitcell/_test/lattice_vs_unitcell.py +138 -0
  244. genice3/unitcell/_test/lattice_vs_unitcell_ordered.py +26 -0
  245. genice3/unitcell/c0te.py +78 -0
  246. genice3/unitcell/c1te.py +108 -0
  247. genice3/unitcell/delta.py +2 -0
  248. genice3/unitcell/dtc.py +76 -0
  249. genice3/unitcell/eleven.py +125 -0
  250. genice3/unitcell/engel01.py +5 -0
  251. genice3/unitcell/engel03.py +2 -0
  252. genice3/unitcell/engel04.py +2 -0
  253. genice3/unitcell/engel05.py +2 -0
  254. genice3/unitcell/engel17.py +2 -0
  255. genice3/unitcell/engel20.py +2 -0
  256. genice3/unitcell/engel23.py +5 -0
  257. genice3/unitcell/engel24.py +2 -0
  258. genice3/unitcell/engel26.py +2 -0
  259. genice3/unitcell/engel29.py +2 -0
  260. genice3/unitcell/engel30.py +2 -0
  261. genice3/unitcell/engel31.py +2 -0
  262. genice3/unitcell/engel34.py +2 -0
  263. genice3/unitcell/i.py +56 -0
  264. genice3/unitcell/ice0.py +53 -0
  265. genice3/unitcell/ice11.py +94 -0
  266. genice3/unitcell/ice12.py +63 -0
  267. genice3/unitcell/ice13.py +87 -0
  268. genice3/unitcell/ice14.py +84 -0
  269. genice3/unitcell/ice16.py +2 -0
  270. genice3/unitcell/ice17.py +51 -0
  271. genice3/unitcell/ice1c.py +79 -0
  272. genice3/unitcell/ice1h.py +59 -0
  273. genice3/unitcell/ice1h_unit.py +57 -0
  274. genice3/unitcell/ice2.py +84 -0
  275. genice3/unitcell/ice2d.py +112 -0
  276. genice3/unitcell/ice2rect.py +380 -0
  277. genice3/unitcell/ice3.py +53 -0
  278. genice3/unitcell/ice4.py +172 -0
  279. genice3/unitcell/ice5.py +71 -0
  280. genice3/unitcell/ice6.py +83 -0
  281. genice3/unitcell/ice7.py +101 -0
  282. genice3/unitcell/ice8.py +91 -0
  283. genice3/unitcell/ice9.py +83 -0
  284. genice3/unitcell/iceA.py +67 -0
  285. genice3/unitcell/iceB.py +62 -0
  286. genice3/unitcell/iceL.py +70 -0
  287. genice3/unitcell/iceM.py +76 -0
  288. genice3/unitcell/iceMd.py +18 -0
  289. genice3/unitcell/iceT.py +113 -0
  290. genice3/unitcell/iceT2.py +201 -0
  291. genice3/unitcell/mu.py +2 -0
  292. genice3/unitcell/one.py +92 -0
  293. genice3/unitcell/oprism.py +88 -0
  294. genice3/unitcell/psigma.py +2 -0
  295. genice3/unitcell/sH.py +2 -0
  296. genice3/unitcell/sI.py +2 -0
  297. genice3/unitcell/sII.py +2 -0
  298. genice3/unitcell/sIII.py +2 -0
  299. genice3/unitcell/sIV.py +2 -0
  300. genice3/unitcell/sTprime.py +65 -0
  301. genice3/unitcell/sV.py +2 -0
  302. genice3/unitcell/sVII.py +2 -0
  303. genice3/unitcell/sigma.py +2 -0
  304. genice3/unitcell/xFAU.py +218 -0
  305. genice3/unitcell/xdtc.py +150 -0
  306. genice3/unitcell/zra-d.py +2 -0
  307. genice3/util.py +586 -0
  308. genice3-3.0b0.dist-info/METADATA +110 -0
  309. genice3-3.0b0.dist-info/RECORD +313 -0
  310. genice3-3.0b0.dist-info/WHEEL +5 -0
  311. genice3-3.0b0.dist-info/entry_points.txt +2 -0
  312. genice3-3.0b0.dist-info/licenses/LICENSE +21 -0
  313. genice3-3.0b0.dist-info/top_level.txt +1 -0
genice3/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ class ConfigurationError(Exception):
5
+ """設定に関するエラーを表す例外"""
6
+
7
+ pass
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))
@@ -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)