genice3 3.0__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 (334) hide show
  1. .DS_Store +0 -0
  2. dependency_engine/__init__.py +218 -0
  3. dependency_engine/__main__.py +4 -0
  4. genice3/__init__.py +22 -0
  5. genice3/_build.py +43 -0
  6. genice3/cage.py +202 -0
  7. genice3/cli/__init__.py +20 -0
  8. genice3/cli/engine.py +71 -0
  9. genice3/cli/genice.py +317 -0
  10. genice3/cli/meta_schema.py +101 -0
  11. genice3/cli/option_def.py +25 -0
  12. genice3/cli/option_parser.py +158 -0
  13. genice3/cli/options.py +755 -0
  14. genice3/cli/runner.py +323 -0
  15. genice3/cli/validator.py +180 -0
  16. genice3/exporter/_KG.py +82 -0
  17. genice3/exporter/__init__.py +7 -0
  18. genice3/exporter/_pol.py +49 -0
  19. genice3/exporter/cage_survey.py +82 -0
  20. genice3/exporter/cif.py +118 -0
  21. genice3/exporter/g.py +5 -0
  22. genice3/exporter/gromacs.py +170 -0
  23. genice3/exporter/lammps.py +192 -0
  24. genice3/exporter/lmp.py +5 -0
  25. genice3/exporter/plotly.py +169 -0
  26. genice3/exporter/py.py +11 -0
  27. genice3/exporter/python.py +304 -0
  28. genice3/exporter/y.py +5 -0
  29. genice3/exporter/yaplot.py +208 -0
  30. genice3/genice.py +1681 -0
  31. genice3/group/__init__.py +18 -0
  32. genice3/group/methyl.py +26 -0
  33. genice3/logging_config.py +180 -0
  34. genice3/mcp.py +120 -0
  35. genice3/molecule/3site.py +2 -0
  36. genice3/molecule/4site.py +2 -0
  37. genice3/molecule/5site.py +2 -0
  38. genice3/molecule/6site.py +3 -0
  39. genice3/molecule/7site.py +45 -0
  40. genice3/molecule/H2.py +22 -0
  41. genice3/molecule/NvdE.py +46 -0
  42. genice3/molecule/__init__.py +46 -0
  43. genice3/molecule/ch4.py +29 -0
  44. genice3/molecule/co2.py +18 -0
  45. genice3/molecule/empty.py +17 -0
  46. genice3/molecule/et.py +9 -0
  47. genice3/molecule/g12.py +8 -0
  48. genice3/molecule/g14.py +8 -0
  49. genice3/molecule/g15.py +8 -0
  50. genice3/molecule/g16.py +8 -0
  51. genice3/molecule/ice.py +45 -0
  52. genice3/molecule/me.py +9 -0
  53. genice3/molecule/mol.py +47 -0
  54. genice3/molecule/one.py +16 -0
  55. genice3/molecule/physical_water.py +31 -0
  56. genice3/molecule/spce.py +29 -0
  57. genice3/molecule/thf.py +43 -0
  58. genice3/molecule/tip3p.py +31 -0
  59. genice3/molecule/tip4p.py +32 -0
  60. genice3/molecule/tip4p2005.py +32 -0
  61. genice3/molecule/tip5p.py +40 -0
  62. genice3/molecule/uathf.py +34 -0
  63. genice3/molecule/uathf6.py +30 -0
  64. genice3/plugin.py +642 -0
  65. genice3/service/__init__.py +36 -0
  66. genice3/service/config.py +86 -0
  67. genice3/service/exceptions.py +38 -0
  68. genice3/service/generate.py +133 -0
  69. genice3/service/media_type.py +53 -0
  70. genice3/service/meta.py +44 -0
  71. genice3/unitcell/0.py +2 -0
  72. genice3/unitcell/11.py +2 -0
  73. genice3/unitcell/115_2_114.py +104 -0
  74. genice3/unitcell/11alt.py +176 -0
  75. genice3/unitcell/11i.py +342 -0
  76. genice3/unitcell/12.py +2 -0
  77. genice3/unitcell/12_1_11.py +110 -0
  78. genice3/unitcell/13.py +2 -0
  79. genice3/unitcell/14.py +2 -0
  80. genice3/unitcell/144_2_7301.py +88 -0
  81. genice3/unitcell/151_2_4949650.py +114 -0
  82. genice3/unitcell/153_2_155471.py +78 -0
  83. genice3/unitcell/16.py +2 -0
  84. genice3/unitcell/17.py +2 -0
  85. genice3/unitcell/176_2_5256.py +66 -0
  86. genice3/unitcell/1c.py +2 -0
  87. genice3/unitcell/1c_2_2_2.py +236 -0
  88. genice3/unitcell/1h.py +2 -0
  89. genice3/unitcell/1h_2_2_2.py +428 -0
  90. genice3/unitcell/1h_unit.py +2 -0
  91. genice3/unitcell/2.py +2 -0
  92. genice3/unitcell/207_1_4435.py +2 -0
  93. genice3/unitcell/21.py +3 -0
  94. genice3/unitcell/2D3.py +117 -0
  95. genice3/unitcell/2_2_623457.py +132 -0
  96. genice3/unitcell/2d.py +2 -0
  97. genice3/unitcell/3.py +2 -0
  98. genice3/unitcell/4.py +2 -0
  99. genice3/unitcell/5.py +2 -0
  100. genice3/unitcell/5R.py +133 -0
  101. genice3/unitcell/6.py +2 -0
  102. genice3/unitcell/6h.py +46 -0
  103. genice3/unitcell/7.py +2 -0
  104. genice3/unitcell/8.py +2 -0
  105. genice3/unitcell/9.py +2 -0
  106. genice3/unitcell/A.py +2 -0
  107. genice3/unitcell/A15.py +2 -0
  108. genice3/unitcell/ACO.py +62 -0
  109. genice3/unitcell/B.py +2 -0
  110. genice3/unitcell/BSV.py +142 -0
  111. genice3/unitcell/C14.py +2 -0
  112. genice3/unitcell/C15.py +2 -0
  113. genice3/unitcell/C36.py +2 -0
  114. genice3/unitcell/CIF.py +102 -0
  115. genice3/unitcell/CRN1.py +3052 -0
  116. genice3/unitcell/CRN2.py +3052 -0
  117. genice3/unitcell/CRN3.py +12340 -0
  118. genice3/unitcell/CS1.py +91 -0
  119. genice3/unitcell/CS2.py +182 -0
  120. genice3/unitcell/CS4.py +2 -0
  121. genice3/unitcell/DDR.py +97 -0
  122. genice3/unitcell/DOH.py +110 -0
  123. genice3/unitcell/EMT.py +124 -0
  124. genice3/unitcell/FAU.py +231 -0
  125. genice3/unitcell/FK6layers.py +2 -0
  126. genice3/unitcell/FK9layers.py +2 -0
  127. genice3/unitcell/HS1.py +131 -0
  128. genice3/unitcell/HS2.py +2 -0
  129. genice3/unitcell/HS3.py +2 -0
  130. genice3/unitcell/Hcomp.py +2 -0
  131. genice3/unitcell/II.py +2 -0
  132. genice3/unitcell/III.py +2 -0
  133. genice3/unitcell/IV.py +2 -0
  134. genice3/unitcell/IWV.py +80 -0
  135. genice3/unitcell/IX.py +2 -0
  136. genice3/unitcell/Ic.py +2 -0
  137. genice3/unitcell/Ih.py +2 -0
  138. genice3/unitcell/LTA.py +70 -0
  139. genice3/unitcell/M.py +2 -0
  140. genice3/unitcell/MAR.py +118 -0
  141. genice3/unitcell/MEP.py +2 -0
  142. genice3/unitcell/MTN.py +2 -0
  143. genice3/unitcell/NON.py +134 -0
  144. genice3/unitcell/PCOD8007225.py +105 -0
  145. genice3/unitcell/PCOD8036144.py +138 -0
  146. genice3/unitcell/PCOD8204698.py +112 -0
  147. genice3/unitcell/PCOD8301974.py +168 -0
  148. genice3/unitcell/PCOD8321499.py +78 -0
  149. genice3/unitcell/PCOD8324623.py +74 -0
  150. genice3/unitcell/RHO.py +93 -0
  151. genice3/unitcell/SGT.py +110 -0
  152. genice3/unitcell/SOD.py +57 -0
  153. genice3/unitcell/Struct01.py +1093 -0
  154. genice3/unitcell/Struct02.py +1357 -0
  155. genice3/unitcell/Struct03.py +1036 -0
  156. genice3/unitcell/Struct04.py +472 -0
  157. genice3/unitcell/Struct05.py +1429 -0
  158. genice3/unitcell/Struct06.py +1429 -0
  159. genice3/unitcell/Struct07.py +745 -0
  160. genice3/unitcell/Struct08.py +1429 -0
  161. genice3/unitcell/Struct09.py +1429 -0
  162. genice3/unitcell/Struct10.py +745 -0
  163. genice3/unitcell/Struct11.py +745 -0
  164. genice3/unitcell/Struct12.py +745 -0
  165. genice3/unitcell/Struct13.py +745 -0
  166. genice3/unitcell/Struct14.py +1090 -0
  167. genice3/unitcell/Struct15.py +1429 -0
  168. genice3/unitcell/Struct16.py +730 -0
  169. genice3/unitcell/Struct17.py +508 -0
  170. genice3/unitcell/Struct18.py +373 -0
  171. genice3/unitcell/Struct19.py +469 -0
  172. genice3/unitcell/Struct20.py +403 -0
  173. genice3/unitcell/Struct21.py +817 -0
  174. genice3/unitcell/Struct22.py +1021 -0
  175. genice3/unitcell/Struct23.py +1021 -0
  176. genice3/unitcell/Struct24.py +676 -0
  177. genice3/unitcell/Struct25.py +982 -0
  178. genice3/unitcell/Struct26.py +712 -0
  179. genice3/unitcell/Struct27.py +1036 -0
  180. genice3/unitcell/Struct28.py +388 -0
  181. genice3/unitcell/Struct29.py +712 -0
  182. genice3/unitcell/Struct30.py +505 -0
  183. genice3/unitcell/Struct31.py +748 -0
  184. genice3/unitcell/Struct32.py +1090 -0
  185. genice3/unitcell/Struct33.py +198 -0
  186. genice3/unitcell/Struct34.py +541 -0
  187. genice3/unitcell/Struct35.py +541 -0
  188. genice3/unitcell/Struct36.py +541 -0
  189. genice3/unitcell/Struct37.py +577 -0
  190. genice3/unitcell/Struct38.py +577 -0
  191. genice3/unitcell/Struct39.py +607 -0
  192. genice3/unitcell/Struct40.py +613 -0
  193. genice3/unitcell/Struct41.py +1225 -0
  194. genice3/unitcell/Struct42.py +1225 -0
  195. genice3/unitcell/Struct43.py +1225 -0
  196. genice3/unitcell/Struct44.py +679 -0
  197. genice3/unitcell/Struct45.py +679 -0
  198. genice3/unitcell/Struct46.py +709 -0
  199. genice3/unitcell/Struct47.py +709 -0
  200. genice3/unitcell/Struct48.py +1369 -0
  201. genice3/unitcell/Struct49.py +745 -0
  202. genice3/unitcell/Struct50.py +745 -0
  203. genice3/unitcell/Struct51.py +403 -0
  204. genice3/unitcell/Struct52.py +1429 -0
  205. genice3/unitcell/Struct53.py +745 -0
  206. genice3/unitcell/Struct54.py +184 -0
  207. genice3/unitcell/Struct55.py +505 -0
  208. genice3/unitcell/Struct56.py +949 -0
  209. genice3/unitcell/Struct57.py +541 -0
  210. genice3/unitcell/Struct58.py +1021 -0
  211. genice3/unitcell/Struct59.py +1021 -0
  212. genice3/unitcell/Struct60.py +1021 -0
  213. genice3/unitcell/Struct61.py +1225 -0
  214. genice3/unitcell/Struct62.py +995 -0
  215. genice3/unitcell/Struct63.py +685 -0
  216. genice3/unitcell/Struct64.py +709 -0
  217. genice3/unitcell/Struct65.py +709 -0
  218. genice3/unitcell/Struct66.py +709 -0
  219. genice3/unitcell/Struct67.py +1357 -0
  220. genice3/unitcell/Struct68.py +267 -0
  221. genice3/unitcell/Struct69.py +406 -0
  222. genice3/unitcell/Struct70.py +745 -0
  223. genice3/unitcell/Struct71.py +743 -0
  224. genice3/unitcell/Struct72.py +745 -0
  225. genice3/unitcell/Struct73.py +745 -0
  226. genice3/unitcell/Struct74.py +745 -0
  227. genice3/unitcell/Struct75.py +745 -0
  228. genice3/unitcell/Struct76.py +1429 -0
  229. genice3/unitcell/Struct77.py +743 -0
  230. genice3/unitcell/Struct78.py +1429 -0
  231. genice3/unitcell/Struct79.py +1429 -0
  232. genice3/unitcell/Struct80.py +745 -0
  233. genice3/unitcell/Struct81.py +1090 -0
  234. genice3/unitcell/Struct82.py +1021 -0
  235. genice3/unitcell/Struct83.py +1021 -0
  236. genice3/unitcell/Struct84.py +541 -0
  237. genice3/unitcell/T.py +965 -0
  238. genice3/unitcell/TS1.py +222 -0
  239. genice3/unitcell/UNTESTED/4R.py +820 -0
  240. genice3/unitcell/UNTESTED/__ice14.py +91 -0
  241. genice3/unitcell/UNTESTED/bilayer.py +333 -0
  242. genice3/unitcell/V.py +2 -0
  243. genice3/unitcell/VI.py +2 -0
  244. genice3/unitcell/VII.py +2 -0
  245. genice3/unitcell/VIII.py +2 -0
  246. genice3/unitcell/XI.py +2 -0
  247. genice3/unitcell/XII.py +2 -0
  248. genice3/unitcell/XIII.py +2 -0
  249. genice3/unitcell/XIc-a.py +76 -0
  250. genice3/unitcell/XVI.py +2 -0
  251. genice3/unitcell/XVII.py +2 -0
  252. genice3/unitcell/XXI.py +3 -0
  253. genice3/unitcell/YKD.py +388 -0
  254. genice3/unitcell/Z.py +2 -0
  255. genice3/unitcell/__init__.py +322 -0
  256. genice3/unitcell/_test/__init__.py +0 -0
  257. genice3/unitcell/aeroice.py +7 -0
  258. genice3/unitcell/c0te.py +77 -0
  259. genice3/unitcell/c1te.py +108 -0
  260. genice3/unitcell/c2te.py +90 -0
  261. genice3/unitcell/delta.py +2 -0
  262. genice3/unitcell/dtc.py +76 -0
  263. genice3/unitcell/eleven.py +160 -0
  264. genice3/unitcell/engel01.py +5 -0
  265. genice3/unitcell/engel03.py +2 -0
  266. genice3/unitcell/engel04.py +2 -0
  267. genice3/unitcell/engel05.py +2 -0
  268. genice3/unitcell/engel17.py +2 -0
  269. genice3/unitcell/engel20.py +2 -0
  270. genice3/unitcell/engel23.py +5 -0
  271. genice3/unitcell/engel24.py +2 -0
  272. genice3/unitcell/engel26.py +2 -0
  273. genice3/unitcell/engel29.py +2 -0
  274. genice3/unitcell/engel30.py +2 -0
  275. genice3/unitcell/engel31.py +2 -0
  276. genice3/unitcell/engel34.py +2 -0
  277. genice3/unitcell/i.py +55 -0
  278. genice3/unitcell/ice0.py +52 -0
  279. genice3/unitcell/ice11.py +93 -0
  280. genice3/unitcell/ice12.py +62 -0
  281. genice3/unitcell/ice13.py +86 -0
  282. genice3/unitcell/ice14.py +82 -0
  283. genice3/unitcell/ice16.py +2 -0
  284. genice3/unitcell/ice17.py +50 -0
  285. genice3/unitcell/ice1c.py +78 -0
  286. genice3/unitcell/ice1h.py +58 -0
  287. genice3/unitcell/ice1h_unit.py +56 -0
  288. genice3/unitcell/ice1hte.py +90 -0
  289. genice3/unitcell/ice2.py +83 -0
  290. genice3/unitcell/ice21.py +144 -0
  291. genice3/unitcell/ice2d.py +111 -0
  292. genice3/unitcell/ice2rect.py +379 -0
  293. genice3/unitcell/ice3.py +52 -0
  294. genice3/unitcell/ice4.py +172 -0
  295. genice3/unitcell/ice5.py +70 -0
  296. genice3/unitcell/ice6.py +82 -0
  297. genice3/unitcell/ice7.py +100 -0
  298. genice3/unitcell/ice8.py +91 -0
  299. genice3/unitcell/ice9.py +82 -0
  300. genice3/unitcell/iceA.py +67 -0
  301. genice3/unitcell/iceB.py +62 -0
  302. genice3/unitcell/iceL.py +69 -0
  303. genice3/unitcell/iceM.py +74 -0
  304. genice3/unitcell/iceMd.py +17 -0
  305. genice3/unitcell/iceR.py +78 -0
  306. genice3/unitcell/iceT.py +112 -0
  307. genice3/unitcell/iceT2.py +199 -0
  308. genice3/unitcell/iceXXI.py +3 -0
  309. genice3/unitcell/mu.py +2 -0
  310. genice3/unitcell/one.py +120 -0
  311. genice3/unitcell/oprism.py +148 -0
  312. genice3/unitcell/prism.py +237 -0
  313. genice3/unitcell/psigma.py +2 -0
  314. genice3/unitcell/sH.py +2 -0
  315. genice3/unitcell/sI.py +2 -0
  316. genice3/unitcell/sII.py +2 -0
  317. genice3/unitcell/sIII.py +2 -0
  318. genice3/unitcell/sIV.py +2 -0
  319. genice3/unitcell/sTprime.py +64 -0
  320. genice3/unitcell/sV.py +2 -0
  321. genice3/unitcell/sVII.py +2 -0
  322. genice3/unitcell/sigma.py +2 -0
  323. genice3/unitcell/xFAU.py +236 -0
  324. genice3/unitcell/xdtc.py +150 -0
  325. genice3/unitcell/zeolite.py +162 -0
  326. genice3/unitcell/zra-d.py +2 -0
  327. genice3/util.py +660 -0
  328. genice3/webapi.py +355 -0
  329. genice3-3.0.dist-info/METADATA +176 -0
  330. genice3-3.0.dist-info/RECORD +334 -0
  331. genice3-3.0.dist-info/WHEEL +5 -0
  332. genice3-3.0.dist-info/entry_points.txt +4 -0
  333. genice3-3.0.dist-info/licenses/LICENSE +21 -0
  334. genice3-3.0.dist-info/top_level.txt +2 -0
.DS_Store ADDED
Binary file
@@ -0,0 +1,218 @@
1
+ """
2
+ Dependency-resolution engine for reactive pipelines.
3
+
4
+ Executes only the tasks needed to reach a given goal, working backwards
5
+ from the target. May be split into a separate package in the future.
6
+ """
7
+
8
+ import inspect
9
+ from logging import getLogger
10
+ import time
11
+
12
+ # @reactive でデコレートされた関数を (モジュール名, 関数) で保持。
13
+ # 利用側が _register_tasks で get_reactive_tasks(__name__) により自モジュール分だけ登録する。
14
+ _REACTIVE_REGISTRY = []
15
+
16
+ # Sentinel: Input に default も default_factory もない。
17
+ _MISSING = object()
18
+
19
+
20
+ class UnsetInputError(Exception):
21
+ """Raised when a required ``Input`` has not been assigned."""
22
+
23
+
24
+ def reactive(func=None, *, public=False):
25
+ """Marker decorator to register a function as a DependencyEngine task.
26
+
27
+ The decorated function name becomes the reactive property name
28
+ (for example, ``genice.cages``). Names should be nouns.
29
+
30
+ Args:
31
+ public: If True, the task is part of the public API listing.
32
+
33
+ Side effect: the function is registered in ``_REACTIVE_REGISTRY``.
34
+ Use ``get_reactive_tasks(module_name)`` to obtain the task list for a module.
35
+ """
36
+
37
+ def decorate(fn):
38
+ fn._reactive_public = public
39
+ _REACTIVE_REGISTRY.append((fn.__module__, fn))
40
+ return fn
41
+
42
+ if func is None:
43
+ return decorate
44
+ return decorate(func)
45
+
46
+
47
+ def get_reactive_tasks(module_name: str):
48
+ """Return functions decorated with ``@reactive`` in the given module."""
49
+ return [f for mod, f in _REACTIVE_REGISTRY if mod == module_name]
50
+
51
+
52
+ class Input:
53
+ """Descriptor for a DAG root (settable reactive input).
54
+
55
+ Assignment stores the value, optionally coerces it (same kind of object),
56
+ runs ``on_set``, and clears ``obj.engine.cache``. Getter and
57
+ ``collect_inputs`` are derived from the class-level declaration.
58
+
59
+ Do not use ``coerce`` to change the public type (for example str to a
60
+ constructed object). Keep a separate factory method for that.
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ *,
66
+ default=_MISSING,
67
+ default_factory=None,
68
+ public=False,
69
+ required=False,
70
+ coerce=None,
71
+ on_set=None,
72
+ unset_error=None,
73
+ doc=None,
74
+ ):
75
+ if required and (default is not _MISSING or default_factory is not None):
76
+ raise TypeError("required Input cannot have a default")
77
+ if default is not _MISSING and default_factory is not None:
78
+ raise TypeError("specify default or default_factory, not both")
79
+ self.default = default
80
+ self.default_factory = default_factory
81
+ self.public = public
82
+ self.required = required
83
+ self.coerce = coerce
84
+ self.on_set = on_set
85
+ self.unset_error = unset_error
86
+ self.name = None
87
+ self.private_name = None
88
+ if doc is not None:
89
+ self.__doc__ = doc
90
+
91
+ def __set_name__(self, owner, name):
92
+ self.name = name
93
+ self.private_name = f"_{name}"
94
+ if "_input_fields" not in owner.__dict__:
95
+ owner._input_fields = {}
96
+ owner._input_fields[name] = self
97
+
98
+ def is_set(self, obj) -> bool:
99
+ """Return True if this input has been assigned on ``obj``."""
100
+ return self.private_name in obj.__dict__
101
+
102
+ def __get__(self, obj, objtype=None):
103
+ if obj is None:
104
+ return self
105
+ if self.private_name in obj.__dict__:
106
+ return obj.__dict__[self.private_name]
107
+ if self.required:
108
+ if self.unset_error is not None:
109
+ err = self.unset_error
110
+ raise err() if callable(err) else err
111
+ raise UnsetInputError(
112
+ f"{type(obj).__name__}.{self.name} is not set."
113
+ )
114
+ if self.default_factory is not None:
115
+ return self.default_factory()
116
+ if self.default is not _MISSING:
117
+ return self.default
118
+ raise UnsetInputError(f"{type(obj).__name__}.{self.name} is not set.")
119
+
120
+ def __set__(self, obj, value):
121
+ if self.coerce is not None:
122
+ value = self.coerce(value)
123
+ obj.__dict__[self.private_name] = value
124
+ logger = getattr(obj, "logger", None)
125
+ if logger is not None:
126
+ logger.debug(" %s=%r", self.name, value)
127
+ if self.on_set is not None:
128
+ self.on_set(obj, value)
129
+ engine = getattr(obj, "engine", None)
130
+ if engine is not None:
131
+ engine.cache.clear()
132
+
133
+
134
+ def input_fields(cls) -> dict:
135
+ """Return ``{name: Input}`` declared on ``cls`` (including bases)."""
136
+ fields = {}
137
+ for base in reversed(cls.__mro__):
138
+ fields.update(getattr(base, "_input_fields", {}))
139
+ return fields
140
+
141
+
142
+ def collect_inputs(obj) -> dict:
143
+ """Return a dict of all ``Input`` values on ``obj`` for ``engine.resolve``."""
144
+ return {name: getattr(obj, name) for name in input_fields(type(obj))}
145
+
146
+
147
+ class DependencyEngine:
148
+ """Engine that back-solves dependencies and runs only needed tasks."""
149
+
150
+ logger = getLogger("dependency_engine")
151
+
152
+ def __init__(self):
153
+ self.registry = {} # { 'output_name': function }
154
+ self.cache = {}
155
+
156
+ def task(self, func):
157
+ """Decorator to register a task with ``func.__name__`` as its output name."""
158
+ self.registry[func.__name__] = func
159
+ return func
160
+
161
+ def resolve(self, target: str, inputs: dict):
162
+ """Resolve ``target`` by recursively computing its dependencies."""
163
+
164
+ # 1. 既に計算済み or 入力として与えられているならそれを返す
165
+ if target in inputs:
166
+ return inputs[target]
167
+ if target in self.cache:
168
+ return self.cache[target]
169
+
170
+ # 2. 生成ルール(関数)を探す
171
+ if target not in self.registry:
172
+ raise ValueError(f"Don't know how to make '{target}'")
173
+
174
+ func = self.registry[target]
175
+
176
+ # 3. その関数の引数(依存先)を調べて、再帰的に解決する
177
+ sig = inspect.signature(func)
178
+ dependencies = {}
179
+ for param_name in sig.parameters:
180
+ dependencies[param_name] = self.resolve(param_name, inputs)
181
+
182
+ # 4. 実行して結果を保存(所要時間は JSON ログの duration_ms にも載せる)
183
+ now = time.time()
184
+ result = func(**dependencies)
185
+ delta_ms = (time.time() - now) * 1000.0
186
+ self.logger.info(
187
+ "Executing: %s",
188
+ target,
189
+ extra={
190
+ "event": "task.execute",
191
+ "task": target,
192
+ "duration_ms": round(delta_ms, 3),
193
+ },
194
+ )
195
+ self.cache[target] = result
196
+ return result
197
+
198
+
199
+ def _demo():
200
+ """Package self-test demo (run with ``python -m dependency_engine``)."""
201
+ engine = DependencyEngine()
202
+
203
+ @engine.task
204
+ def reaction_A(raw_material: int):
205
+ return raw_material * 2
206
+
207
+ @engine.task
208
+ def reaction_B(reaction_A: int, catalyst: int):
209
+ return reaction_A + catalyst
210
+
211
+ result = engine.resolve(
212
+ target="reaction_B", inputs={"raw_material": 10, "catalyst": 5}
213
+ )
214
+ print(result)
215
+
216
+
217
+ if __name__ == "__main__":
218
+ _demo()
@@ -0,0 +1,4 @@
1
+ """python -m dependency_engine でデモを実行"""
2
+ from dependency_engine import _demo
3
+
4
+ _demo()
genice3/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ from logging import getLogger
3
+
4
+ from genice3.logging_config import setup_logging as _setup_logging_impl
5
+
6
+
7
+ class ConfigurationError(Exception):
8
+ """設定に関するエラーを表す例外"""
9
+
10
+ pass
11
+
12
+
13
+ def _setup_logging(debug: bool = False, fmt: str | None = None) -> str:
14
+ """ルートロガーの設定を統一する(:func:`genice3.logging_config.setup_logging` への薄いラップ)。"""
15
+ return _setup_logging_impl(debug=debug, fmt=fmt)
16
+
17
+
18
+ # API利用時にも明示的な呼び出しなしでINFOログが出るよう、
19
+ # ハンドラ未設定の場合だけデフォルト設定を適用する。
20
+ # (ユーザーが事前に basicConfig 等を呼んでいる場合はそちらを尊重する)
21
+ if not getLogger().handlers:
22
+ _setup_logging(debug=False)
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,202 @@
1
+ """
2
+ Utility functions for genice3
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import numpy as np
8
+ from dataclasses import dataclass
9
+ import string
10
+ from logging import getLogger
11
+ from typing import TYPE_CHECKING
12
+
13
+ import networkx as nx
14
+ from cycless import center_of_graph
15
+ from cycless.cycles import cycles_iter
16
+ from cycless.polyhed import cage_to_graph, polyhedra_iter
17
+ from graphstat import GraphStat
18
+
19
+ if TYPE_CHECKING:
20
+ from genice3.genice import GenIce3
21
+
22
+
23
+ @dataclass
24
+ class CageSpec:
25
+ """Specification of a cage.
26
+
27
+ ``cage_type`` is a string such as "A12" that represents the cage type.
28
+ """
29
+
30
+ cage_type: str # A12, 1b, etc.
31
+ faces: str # 5^12 6^2, etc.
32
+ graph: nx.Graph # ケージを構成する水分子ノードのグラフ
33
+
34
+ def to_json_capable_data(self) -> dict:
35
+ return {
36
+ "cage_type": self.cage_type,
37
+ "faces": self.faces,
38
+ "nodes": sorted(int(x) for x in self.graph),
39
+ }
40
+
41
+ def __repr__(self) -> str:
42
+ return (
43
+ f"CageSpec(cage_type={self.cage_type!r}, "
44
+ f"faces={self.faces!r}, "
45
+ f"n_nodes={self.graph.number_of_nodes()})"
46
+ )
47
+
48
+ def __str__(self) -> str:
49
+ return f"{self.cage_type} ({self.faces}) {self.graph.number_of_nodes()} nodes"
50
+
51
+
52
+ @dataclass
53
+ class CageSpecs:
54
+ """Positions and specifications of all cages in the expanded unit cell.
55
+
56
+ specs: A list of ``CageSpec`` (cage_type, faces, graph) for each cage.
57
+ positions: Array of fractional coordinates (Nx3), in the same order as ``specs``.
58
+ node_to_cage_indices: Mapping generated in ``__post_init__``; node index -> list of cage indices that contain the node.
59
+ """
60
+
61
+ specs: list[CageSpec]
62
+ positions: np.ndarray # in fractional coordinates
63
+
64
+ def __post_init__(self) -> None:
65
+ """Build a reverse lookup dictionary from node index to cage indices."""
66
+ self.node_to_cage_indices: dict[int, list[int]] = {}
67
+ for cage_idx, spec in enumerate(self.specs):
68
+ for node in spec.graph:
69
+ self.node_to_cage_indices.setdefault(int(node), []).append(cage_idx)
70
+
71
+ def to_json_capable_data(self) -> dict:
72
+ """Return a dict suitable for JSON serialization.
73
+
74
+ Keys are cage indices; values contain ``frac_pos`` and ``specs``.
75
+ """
76
+ data = []
77
+ for position, specs in zip(self.positions, self.specs):
78
+ data.append(
79
+ {
80
+ "frac_pos": position.tolist(),
81
+ "specs": specs.to_json_capable_data(),
82
+ }
83
+ )
84
+ return dict(enumerate(data))
85
+
86
+ def __repr__(self) -> str:
87
+ return (
88
+ f"CageSpecs(n_cages={len(self.specs)}, "
89
+ f"positions_shape={self.positions.shape})"
90
+ )
91
+
92
+
93
+ def _assign_cage_type(basename: int, existing_types: set[str]) -> str:
94
+ """Generate an unused cage type name (e.g., A12, A12a)."""
95
+ enum = 0
96
+ cage_type = f"A{basename}"
97
+ while cage_type in existing_types:
98
+ char = string.ascii_lowercase[enum]
99
+ cage_type = f"A{basename}{char}"
100
+ enum += 1
101
+ return cage_type
102
+
103
+
104
+ def _make_cage_expression(ring_ids: list, ringlist: list) -> str:
105
+ """Generate a face-expression string such as "5^12 6^2".
106
+
107
+ The expression is built from the sizes of the rings that form the cage.
108
+ """
109
+ ringcount = [0 for i in range(9)]
110
+ for ring in ring_ids:
111
+ ringcount[len(ringlist[ring])] += 1
112
+ index = []
113
+ for i in range(9):
114
+ if ringcount[i] > 0:
115
+ index.append(f"{i}^{ringcount[i]}")
116
+ index = " ".join(index)
117
+ return index
118
+
119
+
120
+ def assess_cages(
121
+ graph: nx.Graph,
122
+ node_frac: np.ndarray,
123
+ *,
124
+ max_cage_rings: int = 16,
125
+ ) -> CageSpecs:
126
+ """Detect and classify cages from a hydrogen-bond graph.
127
+
128
+ Cages are defined here as quasipolyhedra surrounded by cycles of
129
+ hydrogen bonds [Matsumoto 2007].
130
+
131
+ Args:
132
+ graph: Undirected graph representing the hydrogen-bond network.
133
+ node_frac: Fractional coordinates of nodes (Nx3).
134
+ max_cage_rings: Upper bound on the number of rings (faces) per cage
135
+ passed to ``polyhedra_iter`` (default 16, historical GenIce used 22).
136
+
137
+ Returns:
138
+ A ``CageSpecs`` object containing the positions (fractional
139
+ coordinates) and specifications (list of ``CageSpec``) of the
140
+ detected cages.
141
+ """
142
+ logger = getLogger()
143
+
144
+ # Prepare the list of rings
145
+ # taking the positions in PBC into account.
146
+ ringlist = [
147
+ [int(x) for x in ring]
148
+ for ring in cycles_iter(nx.Graph(graph), 8, pos=node_frac)
149
+ ]
150
+ # logger.info(f"{len(ringlist)=} {graph.number_of_nodes()=}")
151
+ cage_fracs = []
152
+ # data storage of the found cages
153
+ db = GraphStat()
154
+ existing_cage_types: set[str] = set()
155
+ g_id_to_cage_type: dict[int, str] = {}
156
+
157
+ # Detect cages and classify
158
+ cages = [cage for cage in polyhedra_iter(ringlist, max_cage_rings)]
159
+ cage_graphs = [cage_to_graph(cage, ringlist) for cage in cages]
160
+ cage_fracs = [center_of_graph(g, node_frac) for g in cage_graphs]
161
+ if len(cage_fracs) == 0:
162
+ logger.info(" No cages detected.")
163
+
164
+ cagespecs = []
165
+ for cage, g in zip(cages, cage_graphs):
166
+ cagesize = len(cage)
167
+ graph_id = db.query_id(g)
168
+ # if it is a new cage type
169
+ if graph_id < 0:
170
+ # new type!
171
+ # register the last query
172
+ graph_id = db.register()
173
+
174
+ cage_type = _assign_cage_type(cagesize, existing_cage_types)
175
+ g_id_to_cage_type[graph_id] = cage_type
176
+ existing_cage_types.add(cage_type)
177
+ else:
178
+ cage_type = g_id_to_cage_type[graph_id]
179
+ faces = _make_cage_expression(cage, ringlist)
180
+ cagespecs.append(CageSpec(cage_type=cage_type, faces=faces, graph=g))
181
+ # print(f"{label=}, {faces=}, {ringlist=}")
182
+ # print([len(ringlist[ring]) for ring in cage])
183
+
184
+ return CageSpecs(specs=cagespecs, positions=np.array(cage_fracs))
185
+
186
+
187
+ def apply_max_cage_rings(genice: GenIce3, max_cage_rings: int | None) -> None:
188
+ """単位胞のケージ検出のリング数上限を変え、GenIce3 のリアクティブキャッシュを無効化する。
189
+
190
+ CIF 等で既に ``_cages`` が埋まっている単位胞(``_cages_lazy`` が False)では何もしない。
191
+ """
192
+ if max_cage_rings is None:
193
+ return
194
+ uc = genice.unitcell
195
+ if not getattr(uc, "_cages_lazy", True):
196
+ return
197
+ v = int(max_cage_rings)
198
+ if v < 1:
199
+ raise ValueError(f"max_cage_rings must be >= 1, got {v}")
200
+ uc._max_cage_rings = v
201
+ uc._cages = None
202
+ genice.engine.cache.clear()
@@ -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/engine.py ADDED
@@ -0,0 +1,71 @@
1
+ """
2
+ CLI / Web 等が共有する GenIce3 実行コア(パース済み result → exporter 出力)。
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import time
8
+ from logging import getLogger
9
+ from typing import Any, Dict, TextIO
10
+
11
+ from genice3.cage import apply_max_cage_rings
12
+ from genice3.genice import GenIce3
13
+ from genice3.plugin import safe_import
14
+ from genice3.cli.options import extract_genice_args
15
+
16
+ logger = getLogger(__name__)
17
+
18
+
19
+ def run_parsed_result(
20
+ result: Dict[str, Any],
21
+ file: TextIO,
22
+ *,
23
+ command_line: str | None = None,
24
+ ) -> None:
25
+ """パース・検証済みの ``result`` で GenIce3 を実行し、exporter 出力を ``file`` に書く。
26
+
27
+ Args:
28
+ result: :func:`genice3.cli.runner.parse_argv` または
29
+ :func:`genice3.cli.runner.parsed_result_from_merged` が返す辞書。
30
+ file: 出力先(例: ``sys.stdout``、``io.StringIO()``)。
31
+ command_line: exporter に渡すメタ用。省略時は ``""``。
32
+
33
+ Raises:
34
+ プラグインや exporter の例外はそのまま伝播する(呼び出し側で捕捉してよい)。
35
+ """
36
+ base_options = result["base_options"]
37
+ genice_kwargs = extract_genice_args(base_options)
38
+
39
+ unitcell_name = result["unitcell"]["name"]
40
+ unitcell_processed = result["unitcell"]["processed"]
41
+ exporter_name = result["exporter"]["name"] or "gromacs"
42
+ exporter_processed = dict(result["exporter"]["processed"])
43
+
44
+ genice = GenIce3(**genice_kwargs)
45
+ genice.unitcell = safe_import("unitcell", unitcell_name).UnitCell(**unitcell_processed)
46
+
47
+ if exporter_name == "cage_survey":
48
+ apply_max_cage_rings(genice, exporter_processed.get("max_cage_rings"))
49
+
50
+ exporter_processed["command_line"] = command_line if command_line is not None else ""
51
+
52
+ logger.info(
53
+ "Executing exporter: %s",
54
+ exporter_name,
55
+ extra={"event": "exporter.execute", "exporter": exporter_name},
56
+ )
57
+ t0 = time.time()
58
+ try:
59
+ safe_import("exporter", exporter_name).dump(genice, file, **exporter_processed)
60
+ finally:
61
+ duration_ms = round((time.time() - t0) * 1000.0, 3)
62
+ logger.info(
63
+ " %.4f sec for exporter %s",
64
+ time.time() - t0,
65
+ exporter_name,
66
+ extra={
67
+ "event": "exporter.execute",
68
+ "exporter": exporter_name,
69
+ "duration_ms": duration_ms,
70
+ },
71
+ )