openmaterials-ai 0.1.3__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.
- omai/__init__.py +13 -0
- omai/badge.py +229 -0
- omai/composites/__init__.py +14 -0
- omai/composites/domain.py +37 -0
- omai/composites/operator/__init__.py +15 -0
- omai/composites/operator/dimensions_registry.py +52 -0
- omai/composites/operator/edges.py +295 -0
- omai/composites/operator/nodes.py +205 -0
- omai/composites/operator/vocabulary.py +40 -0
- omai/composites/representation/__init__.py +12 -0
- omai/configurations.py +501 -0
- omai/cross_code.py +229 -0
- omai/dft_ground_state/__init__.py +7 -0
- omai/dft_ground_state/domain.py +32 -0
- omai/dft_ground_state/operator/__init__.py +12 -0
- omai/dft_ground_state/operator/dimensions_registry.py +40 -0
- omai/dft_ground_state/operator/edges.py +195 -0
- omai/dft_ground_state/operator/nodes.py +160 -0
- omai/dft_ground_state/operator/vocabulary.py +35 -0
- omai/dft_ground_state/representation/__init__.py +107 -0
- omai/dft_ground_state/representation/ase.py +43 -0
- omai/dft_ground_state/representation/fairchem.py +200 -0
- omai/dft_ground_state/representation/mace.py +206 -0
- omai/dft_ground_state/representation/matgl.py +230 -0
- omai/dft_ground_state/representation/mp_api.py +129 -0
- omai/dft_ground_state/representation/pymatgen.py +148 -0
- omai/dft_ground_state/representation/qe.py +159 -0
- omai/dft_ground_state/representation/vasp.py +327 -0
- omai/distance_data.py +135 -0
- omai/doc_html.py +662 -0
- omai/electronic_transport/__init__.py +46 -0
- omai/electronic_transport/domain.py +31 -0
- omai/electronic_transport/operator/__init__.py +13 -0
- omai/electronic_transport/operator/dimensions_registry.py +39 -0
- omai/electronic_transport/operator/edges.py +256 -0
- omai/electronic_transport/operator/nodes.py +230 -0
- omai/electronic_transport/operator/vocabulary.py +30 -0
- omai/electronic_transport/representation/__init__.py +1 -0
- omai/electronic_transport/representation/amset.py +281 -0
- omai/external_solve.py +443 -0
- omai/gates.py +354 -0
- omai/genesis.py +194 -0
- omai/index_data.py +103 -0
- omai/lean_identities.py +448 -0
- omai/lean_roadmap.py +98 -0
- omai/lean_units.py +114 -0
- omai/lineages.py +1681 -0
- omai/map_data.py +1078 -0
- omai/materials/__init__.py +1 -0
- omai/materials/domain.py +26 -0
- omai/materials/operator/__init__.py +7 -0
- omai/materials/operator/dimensions_registry.py +50 -0
- omai/materials/operator/edges.py +187 -0
- omai/materials/operator/nodes.py +167 -0
- omai/materials/operator/shared_primitives.py +31 -0
- omai/materials/operator/vocabulary.py +42 -0
- omai/materials/representation/__init__.py +1 -0
- omai/materials/representation/diffcsp.py +77 -0
- omai/materials/representation/mat_diffusion_analysis.py +21 -0
- omai/materials/representation/mattergen.py +74 -0
- omai/materials/representation/pymatgen.py +81 -0
- omai/materials/representation/pymatgen_analysis_diffusion.py +139 -0
- omai/materials/representation/smol.py +95 -0
- omai/mechanics/__init__.py +11 -0
- omai/mechanics/domain.py +32 -0
- omai/mechanics/operator/__init__.py +12 -0
- omai/mechanics/operator/dimensions_registry.py +44 -0
- omai/mechanics/operator/edges.py +325 -0
- omai/mechanics/operator/nodes.py +167 -0
- omai/mechanics/operator/vocabulary.py +48 -0
- omai/mechanics/representation/__init__.py +21 -0
- omai/mechanics/representation/lammps.py +172 -0
- omai/mechanics/representation/mat_elasticity.py +108 -0
- omai/mechanics/representation/mat_equation_of_state.py +104 -0
- omai/mechanics/representation/mp_api.py +154 -0
- omai/mechanics/representation/pymatgen.py +200 -0
- omai/mechanics/representation/vasp.py +91 -0
- omai/mermaid.py +144 -0
- omai/molecular/__init__.py +9 -0
- omai/molecular/domain.py +29 -0
- omai/molecular/operator/__init__.py +13 -0
- omai/molecular/operator/dimensions_registry.py +29 -0
- omai/molecular/operator/edges.py +170 -0
- omai/molecular/operator/nodes.py +205 -0
- omai/molecular/operator/vocabulary.py +26 -0
- omai/molecular/representation/__init__.py +38 -0
- omai/molecular/representation/openmm.py +159 -0
- omai/molecular/representation/orca.py +303 -0
- omai/operator/__init__.py +45 -0
- omai/operator/compose.py +174 -0
- omai/operator/crystal_symmetry.py +52 -0
- omai/operator/dimcheck.py +358 -0
- omai/operator/dimensions.py +245 -0
- omai/operator/gauge.py +86 -0
- omai/operator/identity.py +157 -0
- omai/operator/learned.py +282 -0
- omai/operator/operator.py +157 -0
- omai/operator/registry.py +347 -0
- omai/operator/space.py +131 -0
- omai/operator/validate.py +235 -0
- omai/operator/vocabulary.py +78 -0
- omai/paper_db.py +188 -0
- omai/paper_parser/__init__.py +269 -0
- omai/paper_parser/__main__.py +80 -0
- omai/paper_parser/catalog.py +57 -0
- omai/paper_parser/detect.py +498 -0
- omai/paper_parser/env.py +67 -0
- omai/paper_parser/ingest.py +145 -0
- omai/paper_parser/map_nodes.py +272 -0
- omai/paper_parser/propose.py +303 -0
- omai/paper_parser/review.py +208 -0
- omai/paper_parser/validate.py +537 -0
- omai/physlean_export.py +412 -0
- omai/quasiharmonic/__init__.py +9 -0
- omai/quasiharmonic/domain.py +30 -0
- omai/quasiharmonic/operator/__init__.py +13 -0
- omai/quasiharmonic/operator/dimensions_registry.py +38 -0
- omai/quasiharmonic/operator/edges.py +200 -0
- omai/quasiharmonic/operator/nodes.py +157 -0
- omai/quasiharmonic/operator/vocabulary.py +26 -0
- omai/quasiharmonic/representation/__init__.py +32 -0
- omai/quasiharmonic/representation/phonopy.py +219 -0
- omai/render.py +293 -0
- omai/representation/__init__.py +71 -0
- omai/representation/adapter.py +252 -0
- omai/representation/compare.py +361 -0
- omai/representation/credits.py +381 -0
- omai/representation/executor.py +803 -0
- omai/representation/instance.py +67 -0
- omai/representation/normalizations.py +65 -0
- omai/representation/units.py +383 -0
- omai/representation/validation.py +91 -0
- omai/schema.py +354 -0
- omai/semantics.py +205 -0
- omai/stability/__init__.py +49 -0
- omai/stability/domain.py +32 -0
- omai/stability/operator/__init__.py +13 -0
- omai/stability/operator/dimensions_registry.py +60 -0
- omai/stability/operator/edges.py +310 -0
- omai/stability/operator/nodes.py +228 -0
- omai/stability/operator/vocabulary.py +47 -0
- omai/stability/representation/__init__.py +16 -0
- omai/stability/representation/mat_surface_adsorption.py +104 -0
- omai/stability/representation/mp_api.py +144 -0
- omai/stability/representation/pymatgen.py +320 -0
- omai/stability/representation/rxn_network.py +64 -0
- omai/stability/representation/xtb.py +28 -0
- omai/store.py +350 -0
- omai/sync.py +253 -0
- omai/thermal_transport/__init__.py +14 -0
- omai/thermal_transport/domain.py +126 -0
- omai/thermal_transport/operator/__init__.py +193 -0
- omai/thermal_transport/operator/dimensions_registry.py +157 -0
- omai/thermal_transport/operator/edges.py +1857 -0
- omai/thermal_transport/operator/gauges.py +57 -0
- omai/thermal_transport/operator/nodes.py +984 -0
- omai/thermal_transport/operator/vocabulary.py +185 -0
- omai/thermal_transport/representation/__init__.py +129 -0
- omai/thermal_transport/representation/ase.py +154 -0
- omai/thermal_transport/representation/gpumd.py +339 -0
- omai/thermal_transport/representation/ipi.py +210 -0
- omai/thermal_transport/representation/kaldo.py +1012 -0
- omai/thermal_transport/representation/lammps.py +511 -0
- omai/thermal_transport/representation/mescal.py +63 -0
- omai/thermal_transport/representation/mp_api.py +50 -0
- omai/thermal_transport/representation/phono3py.py +859 -0
- omai/thermal_transport/representation/phonopy.py +522 -0
- omai/thermal_transport/representation/plumed.py +148 -0
- omai/thermal_transport/representation/pymatgen.py +135 -0
- omai/thermal_transport/representation/qe.py +321 -0
- omai/thermal_transport/representation/shengbte.py +705 -0
- omai/thermal_transport/representation/vasp.py +69 -0
- omai/thermal_transport/representation/xtb.py +29 -0
- omai/thermochemistry/__init__.py +54 -0
- omai/thermochemistry/domain.py +33 -0
- omai/thermochemistry/operator/__init__.py +13 -0
- omai/thermochemistry/operator/dimensions_registry.py +43 -0
- omai/thermochemistry/operator/edges.py +248 -0
- omai/thermochemistry/operator/nodes.py +214 -0
- omai/thermochemistry/operator/vocabulary.py +28 -0
- omai/thermochemistry/representation/__init__.py +12 -0
- omai/thermochemistry/representation/pycalphad.py +374 -0
- omai/thermodynamic_identities/__init__.py +13 -0
- omai/thermodynamic_identities/domain.py +32 -0
- omai/thermodynamic_identities/operator/__init__.py +15 -0
- omai/thermodynamic_identities/operator/dimensions_registry.py +40 -0
- omai/thermodynamic_identities/operator/edges.py +282 -0
- omai/thermodynamic_identities/operator/nodes.py +175 -0
- omai/thermodynamic_identities/operator/vocabulary.py +41 -0
- omai/thermodynamic_identities/representation/__init__.py +11 -0
- omai/tools/__init__.py +1 -0
- omai/tools/gen_vectors.py +370 -0
- omai/tools/kaldo-direct-bte-si.json +66 -0
- omai/tools/proof-record-903616ec.json +102 -0
- omai/tools/vector_inputs.json +321 -0
- omai/vectors/lineage_ids.json +668 -0
- omai/vectors/records.json +1353 -0
- omai/vectors/render.json +290 -0
- omdc/__init__.py +46 -0
- omdc/adapters.py +37 -0
- omdc/benchmark.py +112 -0
- omdc/encoders/__init__.py +27 -0
- omdc/encoders/base.py +32 -0
- omdc/encoders/histogram.py +79 -0
- omdc/encoders/mace.py +65 -0
- omdc/envset.py +95 -0
- omdc/errors.py +16 -0
- omdc/index.py +99 -0
- omdc/metrics/__init__.py +0 -0
- omdc/metrics/amdmetric.py +43 -0
- omdc/metrics/comp.py +30 -0
- omdc/metrics/exact.py +12 -0
- omdc/metrics/ot.py +55 -0
- omdc/metrics/phonon.py +98 -0
- omdc/metrics/pooled.py +32 -0
- omdc/metrics/spectrum.py +57 -0
- omdc/metrics/traj.py +57 -0
- omdc/profile.py +41 -0
- omdc/registry.py +144 -0
- omdc/store.py +55 -0
- omds/__init__.py +31 -0
- omds/fields.py +90 -0
- omds/mapgraph.py +87 -0
- omds/records.py +41 -0
- omds/registry.py +91 -0
- openmaterials_ai-0.1.3.dist-info/METADATA +488 -0
- openmaterials_ai-0.1.3.dist-info/RECORD +232 -0
- openmaterials_ai-0.1.3.dist-info/WHEEL +5 -0
- openmaterials_ai-0.1.3.dist-info/licenses/LICENSE +202 -0
- openmaterials_ai-0.1.3.dist-info/licenses/LICENSE-DATA +423 -0
- openmaterials_ai-0.1.3.dist-info/licenses/NOTICE +18 -0
- openmaterials_ai-0.1.3.dist-info/top_level.txt +3 -0
omai/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""openmaterials-ai: typed operator and representation layers for computational materials science."""
|
|
2
|
+
|
|
3
|
+
# Read from the installed distribution so this can never disagree with
|
|
4
|
+
# pyproject.toml (it sat at 0.0.1 through the 0.1.0 release). The literal is
|
|
5
|
+
# the fallback for a source tree that was never installed, and is the one
|
|
6
|
+
# place to bump alongside pyproject.
|
|
7
|
+
try: # pragma: no cover - trivial packaging branch
|
|
8
|
+
from importlib.metadata import PackageNotFoundError
|
|
9
|
+
from importlib.metadata import version as _dist_version
|
|
10
|
+
|
|
11
|
+
__version__ = _dist_version("openmaterials-ai")
|
|
12
|
+
except (ImportError, PackageNotFoundError): # pragma: no cover
|
|
13
|
+
__version__ = "0.1.3"
|
omai/badge.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""The openmaterials version badge: a shields-style SVG naming the map version.
|
|
2
|
+
|
|
3
|
+
Left segment: the mark and the word "openmaterials" on neutral dark. Right
|
|
4
|
+
segment: a map version hash (12 hex characters, the canonical short form) on
|
|
5
|
+
the brand indigo. A repository that uses the map embeds the badge to state
|
|
6
|
+
which version its results were computed against, the same way a CI badge
|
|
7
|
+
states a build:
|
|
8
|
+
|
|
9
|
+
[](https://openmaterials.ai/)
|
|
10
|
+
|
|
11
|
+
Two implementations exist and MUST stay byte-identical: this module (writes
|
|
12
|
+
docs/badge.svg for the CURRENT version at map_data time) and the site
|
|
13
|
+
Worker's badge.js (renders /badge/<hash>.svg for any pinned version).
|
|
14
|
+
tests/test_badge.py pins the parity byte for byte, so neither can drift.
|
|
15
|
+
|
|
16
|
+
The text is width-pinned with the SVG textLength attribute, so the rendered
|
|
17
|
+
badge is pixel-stable regardless of the viewer's fonts; the per-character
|
|
18
|
+
width table below only has to MATCH badge.js, not match Verdana exactly.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import math
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
# Approximate Verdana-11 advance widths, px. Shared verbatim with badge.js:
|
|
28
|
+
# parity matters, typographic exactness does not (textLength pins rendering).
|
|
29
|
+
_WIDTHS = {
|
|
30
|
+
"o": 6.9, "p": 6.9, "e": 6.6, "n": 6.9, "m": 10.6, "a": 6.6, "t": 4.6,
|
|
31
|
+
"r": 4.8, "i": 3.2, "l": 3.2, "s": 5.9,
|
|
32
|
+
"0": 6.9, "1": 6.9, "2": 6.9, "3": 6.9, "4": 6.9, "5": 6.9, "6": 6.9,
|
|
33
|
+
"7": 6.9, "8": 6.9, "9": 6.9, "b": 6.9, "c": 6.0, "d": 6.9, "f": 3.7,
|
|
34
|
+
}
|
|
35
|
+
_FALLBACK = 7.0
|
|
36
|
+
|
|
37
|
+
LABEL = "openmaterials"
|
|
38
|
+
LEFT_BG = "#555"
|
|
39
|
+
RIGHT_BG = "#4f46e5"
|
|
40
|
+
|
|
41
|
+
# the mark, inlined from docs/assets/favicon.svg (16-grid), scaled to 14px
|
|
42
|
+
_MARK = (
|
|
43
|
+
'<g transform="translate(5,3) scale(0.875)">'
|
|
44
|
+
'<rect width="16" height="16" rx="4" fill="#4f46e5"/>'
|
|
45
|
+
'<path d="M4 4.5 L8 7.5 L4 10.5" stroke="#ffffff" stroke-width="1.6" '
|
|
46
|
+
'fill="none" stroke-linecap="round" stroke-linejoin="round"/>'
|
|
47
|
+
'<circle cx="11" cy="8" r="2.6" fill="#ffffff"/>'
|
|
48
|
+
'<circle cx="11" cy="8" r="1.1" fill="#4f46e5"/>'
|
|
49
|
+
"</g>"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _round_half_up(x: float) -> int:
|
|
54
|
+
"""JavaScript's Math.round for the positive values used here: ties go
|
|
55
|
+
UP. Python's round() is banker's rounding (ties to even), which would
|
|
56
|
+
break byte parity on any .5 width (e.g. the hash a00000000000)."""
|
|
57
|
+
return math.floor(x + 0.5)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _tenths(x: float) -> float:
|
|
61
|
+
return _round_half_up(x * 10) / 10
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _num(x: float) -> str:
|
|
65
|
+
"""Format a tenth-precision number the way JavaScript does: integers
|
|
66
|
+
bare (79 not 79.0), everything else with its single decimal."""
|
|
67
|
+
r = _tenths(x)
|
|
68
|
+
return str(int(r)) if float(r).is_integer() else str(r)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _text_width(s: str) -> float:
|
|
72
|
+
return _tenths(sum(_WIDTHS.get(c, _FALLBACK) for c in s))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def labeled_badge_svg(label: str, message: str) -> str:
|
|
76
|
+
"""A shields-style badge with an arbitrary label and message, sharing
|
|
77
|
+
the version badge's exact geometry, mark, and colors. Byte-identical
|
|
78
|
+
with badge.js statBadgeSVG for the same inputs (parity-pinned)."""
|
|
79
|
+
lw = _text_width(label)
|
|
80
|
+
rw = _text_width(message)
|
|
81
|
+
left = _round_half_up(5 + 14 + 4 + lw + 6)
|
|
82
|
+
right = _round_half_up(6 + rw + 6)
|
|
83
|
+
total = left + right
|
|
84
|
+
lx = _tenths(23 + lw / 2)
|
|
85
|
+
rx = _tenths(left + right / 2)
|
|
86
|
+
return (
|
|
87
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="' + str(total) +
|
|
88
|
+
'" height="20" role="img" aria-label="' + label + ": " + message + '">' +
|
|
89
|
+
"<title>" + label + ": " + message + "</title>" +
|
|
90
|
+
'<linearGradient id="s" x2="0" y2="100%">' +
|
|
91
|
+
'<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>' +
|
|
92
|
+
'<stop offset="1" stop-opacity=".1"/></linearGradient>' +
|
|
93
|
+
'<clipPath id="r"><rect width="' + str(total) +
|
|
94
|
+
'" height="20" rx="3" fill="#fff"/></clipPath>' +
|
|
95
|
+
'<g clip-path="url(#r)">' +
|
|
96
|
+
'<rect width="' + str(left) + '" height="20" fill="' + LEFT_BG + '"/>' +
|
|
97
|
+
'<rect x="' + str(left) + '" width="' + str(right) +
|
|
98
|
+
'" height="20" fill="' + RIGHT_BG + '"/>' +
|
|
99
|
+
'<rect width="' + str(total) + '" height="20" fill="url(#s)"/></g>' +
|
|
100
|
+
_MARK +
|
|
101
|
+
'<g fill="#fff" text-anchor="middle" ' +
|
|
102
|
+
'font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">' +
|
|
103
|
+
'<text x="' + _num(lx) + '" y="15" fill="#010101" fill-opacity=".3" ' +
|
|
104
|
+
'textLength="' + _num(lw) + '">' + label + "</text>" +
|
|
105
|
+
'<text x="' + _num(lx) + '" y="14" textLength="' + _num(lw) + '">' +
|
|
106
|
+
label + "</text>" +
|
|
107
|
+
'<text x="' + _num(rx) + '" y="15" fill="#010101" fill-opacity=".3" ' +
|
|
108
|
+
'textLength="' + _num(rw) + '">' + message + "</text>" +
|
|
109
|
+
'<text x="' + _num(rx) + '" y="14" textLength="' + _num(rw) + '">' +
|
|
110
|
+
message + "</text></g></svg>"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def map_stats() -> dict[str, int]:
|
|
115
|
+
"""The live map statistics the stat badges state, computed from the
|
|
116
|
+
same published data files every page reads: never hardcoded."""
|
|
117
|
+
docs = Path(__file__).resolve().parents[1] / "docs"
|
|
118
|
+
graph = json.loads((docs / "data" / "graph.json").read_text())
|
|
119
|
+
codes = json.loads((docs / "data" / "codes.json").read_text())
|
|
120
|
+
insts = json.loads((docs / "data" / "instances.json").read_text())
|
|
121
|
+
ops = {l["op"] for l in graph.get("links", []) if isinstance(l, dict) and l.get("op")}
|
|
122
|
+
return {
|
|
123
|
+
"nodes": len(graph.get("nodes", [])),
|
|
124
|
+
"operators": len(ops),
|
|
125
|
+
"codes": len(codes),
|
|
126
|
+
"values": len(insts if isinstance(insts, list) else insts.get("instances", [])),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def badge_svg(version: str) -> str:
|
|
131
|
+
"""The badge SVG for one map version hash (shown at the canonical 12)."""
|
|
132
|
+
v = str(version)[:12]
|
|
133
|
+
lw = _text_width(LABEL)
|
|
134
|
+
rw = _text_width(v)
|
|
135
|
+
# mark at x=5 (14 wide), 4px gap, label, 6px right pad
|
|
136
|
+
left = _round_half_up(5 + 14 + 4 + lw + 6)
|
|
137
|
+
right = _round_half_up(6 + rw + 6)
|
|
138
|
+
total = left + right
|
|
139
|
+
lx = _tenths(23 + lw / 2)
|
|
140
|
+
rx = _tenths(left + right / 2)
|
|
141
|
+
return (
|
|
142
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="' + str(total) +
|
|
143
|
+
'" height="20" role="img" aria-label="' + LABEL + ": " + v + '">' +
|
|
144
|
+
"<title>" + LABEL + " map version " + v + "</title>" +
|
|
145
|
+
'<linearGradient id="s" x2="0" y2="100%">' +
|
|
146
|
+
'<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>' +
|
|
147
|
+
'<stop offset="1" stop-opacity=".1"/></linearGradient>' +
|
|
148
|
+
'<clipPath id="r"><rect width="' + str(total) +
|
|
149
|
+
'" height="20" rx="3" fill="#fff"/></clipPath>' +
|
|
150
|
+
'<g clip-path="url(#r)">' +
|
|
151
|
+
'<rect width="' + str(left) + '" height="20" fill="' + LEFT_BG + '"/>' +
|
|
152
|
+
'<rect x="' + str(left) + '" width="' + str(right) +
|
|
153
|
+
'" height="20" fill="' + RIGHT_BG + '"/>' +
|
|
154
|
+
'<rect width="' + str(total) + '" height="20" fill="url(#s)"/></g>' +
|
|
155
|
+
_MARK +
|
|
156
|
+
'<g fill="#fff" text-anchor="middle" ' +
|
|
157
|
+
'font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">' +
|
|
158
|
+
'<text x="' + _num(lx) + '" y="15" fill="#010101" fill-opacity=".3" ' +
|
|
159
|
+
'textLength="' + _num(lw) + '">' + LABEL + "</text>" +
|
|
160
|
+
'<text x="' + _num(lx) + '" y="14" textLength="' + _num(lw) + '">' +
|
|
161
|
+
LABEL + "</text>" +
|
|
162
|
+
'<text x="' + _num(rx) + '" y="15" fill="#010101" fill-opacity=".3" ' +
|
|
163
|
+
'textLength="' + _num(rw) + '">' + v + "</text>" +
|
|
164
|
+
'<text x="' + _num(rx) + '" y="14" textLength="' + _num(rw) + '">' +
|
|
165
|
+
v + "</text></g></svg>"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def write_badge(path: Path | None = None) -> Path:
|
|
170
|
+
"""Write docs/badge.svg for the current map version, read from the
|
|
171
|
+
version stamp write_version() has already written (single source).
|
|
172
|
+
|
|
173
|
+
Also emits the PINNED form as a static file, docs/badge/<version12>.svg,
|
|
174
|
+
and prunes pins of other versions: before the DNS cutover the site is
|
|
175
|
+
served by GitHub Pages, which cannot run the Worker's /badge/<hash>.svg
|
|
176
|
+
route, so the pin the README carries must exist as real bytes. The
|
|
177
|
+
Worker serves the same bytes for the current version and generates any
|
|
178
|
+
other hash on demand; the static set is always exactly one file."""
|
|
179
|
+
docs = Path(__file__).resolve().parents[1] / "docs"
|
|
180
|
+
version = json.loads((docs / "data" / "version.json").read_text())["version"]
|
|
181
|
+
out = path or docs / "badge.svg"
|
|
182
|
+
out.write_text(badge_svg(version) + "\n", encoding="utf-8")
|
|
183
|
+
pin_dir = docs / "badge"
|
|
184
|
+
pin_dir.mkdir(exist_ok=True)
|
|
185
|
+
pin = pin_dir / (version[:12] + ".svg")
|
|
186
|
+
pin.write_text(badge_svg(version) + "\n", encoding="utf-8")
|
|
187
|
+
for stale in pin_dir.glob("*.svg"):
|
|
188
|
+
if stale.name != pin.name:
|
|
189
|
+
stale.unlink()
|
|
190
|
+
# the live stat badges, as static files for the Pages origin; the Worker
|
|
191
|
+
# computes the same numbers from the same data on /badge/stat/<name>.svg
|
|
192
|
+
stat_dir = pin_dir / "stat"
|
|
193
|
+
stat_dir.mkdir(exist_ok=True)
|
|
194
|
+
for name, count in map_stats().items():
|
|
195
|
+
(stat_dir / (name + ".svg")).write_text(
|
|
196
|
+
labeled_badge_svg(name, str(count)) + "\n", encoding="utf-8")
|
|
197
|
+
return out
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
import re as _re
|
|
201
|
+
|
|
202
|
+
_README_BADGE_RE = _re.compile(
|
|
203
|
+
r"https://openmaterials\.ai/badge/[0-9a-f]{8,64}\.svg"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def pin_readme_badge(readme: Path | None = None) -> Path:
|
|
208
|
+
"""Pin the README badge to the current map version.
|
|
209
|
+
|
|
210
|
+
The repository's own README carries the PINNED badge form, not the
|
|
211
|
+
live /badge.svg: an old commit's README then shows the version that
|
|
212
|
+
commit actually had. map_data calls this after write_version, so the
|
|
213
|
+
pin updates in the same commit that moves the version, and
|
|
214
|
+
tests/test_badge.py fails whenever README and version.json disagree,
|
|
215
|
+
so a stale pin cannot land."""
|
|
216
|
+
root = Path(__file__).resolve().parents[1]
|
|
217
|
+
version = json.loads(
|
|
218
|
+
(root / "docs" / "data" / "version.json").read_text())["version"][:12]
|
|
219
|
+
path = readme or root / "README.md"
|
|
220
|
+
text = path.read_text()
|
|
221
|
+
pinned = "https://openmaterials.ai/badge/" + version + ".svg"
|
|
222
|
+
new_text, n = _README_BADGE_RE.subn(pinned, text)
|
|
223
|
+
if n == 0:
|
|
224
|
+
raise RuntimeError(
|
|
225
|
+
"README.md carries no pinned badge URL to update "
|
|
226
|
+
"(expected https://openmaterials.ai/badge/<hash>.svg)")
|
|
227
|
+
if new_text != text:
|
|
228
|
+
path.write_text(new_text)
|
|
229
|
+
return path
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""The composites (effective-medium) domain: composite effective thermal conductivity.
|
|
2
|
+
|
|
3
|
+
Domain eleven: the effective thermal conductivity of a two-phase composite (a
|
|
4
|
+
dispersed filler in a continuous matrix) with interfacial (Kapitza) resistance,
|
|
5
|
+
via Nan-type effective-medium theory (Nan et al., J. Appl. Phys. 81, 6692 (1997)),
|
|
6
|
+
cross-checked in the spherical limit against Hasselman-Johnson (J. Compos. Mater.
|
|
7
|
+
21, 508 (1987)). Seven nodes (the matrix and filler conductivities as role-labeled
|
|
8
|
+
inputs, the new InterfaceConductance physics, the filler volume fraction, the
|
|
9
|
+
depolarization factors, and the random / aligned effective conductivities) and
|
|
10
|
+
five edges (the depolarization closed form, the Nan random and aligned mixing
|
|
11
|
+
formulas, the Hasselman-Johnson second producer, and the resolve onto the neutral
|
|
12
|
+
ThermalConductivity observable). One new dimension INTERFACE_CONDUCTANCE
|
|
13
|
+
(W/(m^2 K)) falls out of the interface conductance.
|
|
14
|
+
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""The composites (effective-medium) Domain descriptor."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from omai.map_data import Domain
|
|
5
|
+
from omai.composites import representation as comp_rep
|
|
6
|
+
from omai.composites.operator import EDGES, NODES
|
|
7
|
+
|
|
8
|
+
# Canonical LaTeX symbol per variable (consistent symbolic names, not words).
|
|
9
|
+
SYMBOLS: dict[str, str] = {
|
|
10
|
+
"ThermalConductivity[role=matrix]": r"\kappa_m",
|
|
11
|
+
"ThermalConductivity[role=filler]": r"\kappa_f",
|
|
12
|
+
"InterfaceConductance": r"G",
|
|
13
|
+
"FillerVolumeFraction": r"f",
|
|
14
|
+
"DepolarizationFactor": r"L",
|
|
15
|
+
"ThermalConductivity[effective_medium=nan,orientation=random]": r"\kappa_c",
|
|
16
|
+
"ThermalConductivity[effective_medium=nan,orientation=aligned]": r"\kappa_c^{\parallel}",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
COMPOSITES = Domain(
|
|
20
|
+
name="composites",
|
|
21
|
+
nodes=NODES,
|
|
22
|
+
edges=EDGES,
|
|
23
|
+
symbols=SYMBOLS,
|
|
24
|
+
param_promotions=(),
|
|
25
|
+
tiers=(
|
|
26
|
+
(
|
|
27
|
+
"Composite",
|
|
28
|
+
"Composite effective thermal conductivity of a filled matrix with "
|
|
29
|
+
"interfacial (Kapitza) resistance: the matrix and filler "
|
|
30
|
+
"conductivities, the interface conductance, the filler volume "
|
|
31
|
+
"fraction and depolarization factors, and the Nan-type "
|
|
32
|
+
"effective-medium conductivity (random and aligned) cross-checked "
|
|
33
|
+
"against the Hasselman-Johnson spherical limit.",
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
representation_package=comp_rep,
|
|
37
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Composites (effective-medium) operator DAG: re-exports the domain's NODES and EDGES.
|
|
2
|
+
|
|
3
|
+
Importing this package registers the domain's formula-symbol vocabulary and its
|
|
4
|
+
symbol-dimension bindings as side effects (mirroring the thermodynamic-identities /
|
|
5
|
+
electronic-transport / mechanics packages), so validate_dag and the dimensional
|
|
6
|
+
gate see this domain's symbols. Here that side effect is load-bearing: four of the
|
|
7
|
+
five edges are executable closed forms the dimensional gate PROVES, so the new
|
|
8
|
+
field-symbol dimension bindings must be live before the gate runs.
|
|
9
|
+
"""
|
|
10
|
+
from omai.composites.operator import vocabulary as _vocabulary # registers formula symbols
|
|
11
|
+
from omai.composites.operator import dimensions_registry as _dimensions_registry # registers symbol dimensions
|
|
12
|
+
from omai.composites.operator.edges import EDGES
|
|
13
|
+
from omai.composites.operator.nodes import NODES
|
|
14
|
+
|
|
15
|
+
__all__ = ["NODES", "EDGES"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
r"""Symbol-dimension registry of the composites (effective-medium) domain.
|
|
2
|
+
|
|
3
|
+
Registered into the core registry (`omai.operator.dimcheck`) when
|
|
4
|
+
`omai.composites.operator` is imported, next to the vocabulary module. Four of
|
|
5
|
+
this domain's five edges are executable closed forms the dimensional gate PROVES
|
|
6
|
+
(the fifth, depolarization_factors, is a Piecewise the gate skips), so these
|
|
7
|
+
bindings are load-bearing: they let dimension_of evaluate both sides of the Nan
|
|
8
|
+
random / aligned and Hasselman-Johnson formulas.
|
|
9
|
+
|
|
10
|
+
k_m -> THERMAL_CONDUCTIVITY (matrix conductivity km)
|
|
11
|
+
k_f -> THERMAL_CONDUCTIVITY (filler tensor; its diagonal k_f[1,1]=k11, k_f[3,3]=k33)
|
|
12
|
+
G_{int} -> INTERFACE_CONDUCTANCE (Kapitza interface conductance G, W/(m^2 K))
|
|
13
|
+
d_1 -> LENGTH (equatorial inclusion dimension)
|
|
14
|
+
d_3 -> LENGTH (polar inclusion dimension)
|
|
15
|
+
a_{rad} -> LENGTH (sphere radius, Hasselman-Johnson)
|
|
16
|
+
f_{vol} -> DIMENSIONLESS (filler volume fraction)
|
|
17
|
+
L_{11}, L_{33}-> DIMENSIONLESS (depolarization factors)
|
|
18
|
+
p -> DIMENSIONLESS (aspect ratio d3/d1)
|
|
19
|
+
\kappa_c -> THERMAL_CONDUCTIVITY (composite effective conductivity)
|
|
20
|
+
|
|
21
|
+
The gate then proves the Nan / HJ closed forms because the interface term
|
|
22
|
+
2 (1/G) k_ii / d_i = (m^2 K/W)(W/mK)/m is dimensionless, beta_ii is a ratio of
|
|
23
|
+
two THERMAL_CONDUCTIVITY quantities, and the Hasselman-Johnson alpha = (km/G)/a
|
|
24
|
+
is a length over a length; so the effective kappa carries km's
|
|
25
|
+
THERMAL_CONDUCTIVITY exactly. No collision: k_m, k_f, G_{int}, f_{vol},
|
|
26
|
+
L_{11}, L_{33}, \kappa_c, a_{rad}, p are otherwise-unused base names (d_1 / d_3
|
|
27
|
+
are this domain's; \kappa is already bound to THERMAL_CONDUCTIVITY by
|
|
28
|
+
thermal_transport, a harmless matching union).
|
|
29
|
+
"""
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from omai.operator.dimcheck import register_symbol_dimensions
|
|
33
|
+
from omai.operator.dimensions import (
|
|
34
|
+
DIMENSIONLESS,
|
|
35
|
+
INTERFACE_CONDUCTANCE,
|
|
36
|
+
LENGTH,
|
|
37
|
+
THERMAL_CONDUCTIVITY,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
register_symbol_dimensions({
|
|
41
|
+
"k_m": THERMAL_CONDUCTIVITY,
|
|
42
|
+
"k_f": THERMAL_CONDUCTIVITY,
|
|
43
|
+
"G_{int}": INTERFACE_CONDUCTANCE,
|
|
44
|
+
"d_1": LENGTH,
|
|
45
|
+
"d_3": LENGTH,
|
|
46
|
+
"a_{rad}": LENGTH,
|
|
47
|
+
"f_{vol}": DIMENSIONLESS,
|
|
48
|
+
"L_{11}": DIMENSIONLESS,
|
|
49
|
+
"L_{33}": DIMENSIONLESS,
|
|
50
|
+
"p": DIMENSIONLESS,
|
|
51
|
+
r"\kappa_c": THERMAL_CONDUCTIVITY,
|
|
52
|
+
})
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
r"""Operators (edges) of the composites (effective-medium) domain.
|
|
2
|
+
|
|
3
|
+
Five edges implementing the composite effective thermal conductivity of a
|
|
4
|
+
filled matrix with interfacial (Kapitza) resistance:
|
|
5
|
+
|
|
6
|
+
depolarization_factors : () -> DepolarizationFactor
|
|
7
|
+
nan_effective_kappa : (matrix, filler, interface, fraction,
|
|
8
|
+
depolarization) -> effective[orientation=random]
|
|
9
|
+
nan_effective_kappa_aligned : (matrix, filler, interface, fraction,
|
|
10
|
+
depolarization) -> effective[orientation=aligned]
|
|
11
|
+
hasselman_johnson : (matrix, filler, interface, fraction)
|
|
12
|
+
-> effective[orientation=random] (2nd producer)
|
|
13
|
+
resolve_effective_conductivity : (effective[orientation=random])
|
|
14
|
+
-> ThermalConductivity (neutral observable)
|
|
15
|
+
|
|
16
|
+
Physics (Nan et al., J. Appl. Phys. 81, 6692 (1997)) with the interface as a
|
|
17
|
+
per-direction series film (Nan et al., Appl. Phys. Lett. 85, 3549 (2004) form):
|
|
18
|
+
the interface-corrected filler conductivity along axis i is
|
|
19
|
+
kc_ii = k_ii / (1 + 2 R k_ii / d_i) with R = 1/G the interface resistance and
|
|
20
|
+
d_i the full inclusion dimension. The Bruggeman-Nan beta terms are
|
|
21
|
+
beta_ii = (kc_ii - km) / (km + L_ii (kc_ii - km)); the randomly-oriented scalar
|
|
22
|
+
and the perfectly-aligned tensor are the two mixing formulas. At aspect ratio 1
|
|
23
|
+
(a sphere, isotropic filler) the Nan random result reduces EXACTLY to the
|
|
24
|
+
Hasselman-Johnson sphere formula (J. Compos. Mater. 21, 508 (1987)); the domain
|
|
25
|
+
test pins that equality to machine precision.
|
|
26
|
+
|
|
27
|
+
Executability and the dimensional gate. Four of the five edges are explicit
|
|
28
|
+
closed-form sympy Eqs written INLINE (kc_ii, beta_ii substituted), so the
|
|
29
|
+
dimensional gate PROVES them: every sub-ratio is dimensionless (the interface
|
|
30
|
+
term 2 (1/G) k_ii / d_i is dimensionless because 1/G is m^2 K / W, k_ii is
|
|
31
|
+
W/(m K), d_i is m; beta_ii is a ratio of two THERMAL_CONDUCTIVITY quantities),
|
|
32
|
+
so the effective kappa carries km's THERMAL_CONDUCTIVITY exactly. The
|
|
33
|
+
depolarization_factors edge is a sp.Piecewise closed form in the dimensionless
|
|
34
|
+
aspect ratio; the gate SKIPS it (Piecewise is not in the dimension recursion),
|
|
35
|
+
which is acceptable: the dimensional gate only needs the dimensionless SHAPE,
|
|
36
|
+
and the numeric branches are exercised by the domain test and served by the
|
|
37
|
+
representation adapter. resolve_effective_conductivity is the identity edge that
|
|
38
|
+
anchors the composite result onto the neutral ThermalConductivity observable
|
|
39
|
+
(the connectivity bridge to the pre-existing map, the resolve_thermal_conductivity
|
|
40
|
+
precedent).
|
|
41
|
+
|
|
42
|
+
Symbols (all bound in dimensions_registry so the gate proves the four closed
|
|
43
|
+
forms): k_m (THERMAL_CONDUCTIVITY, matrix), the filler tensor kappa_f[alpha,beta]
|
|
44
|
+
whose diagonal gives k_{11} = kappa_f[1,1] and k_{33} = kappa_f[3,3]
|
|
45
|
+
(THERMAL_CONDUCTIVITY), G_{int} (INTERFACE_CONDUCTANCE), d_1 / d_3 (LENGTH),
|
|
46
|
+
f_{vol} / L_{11} / L_{33} (DIMENSIONLESS), and the outputs kappa_c
|
|
47
|
+
(THERMAL_CONDUCTIVITY). The Hasselman-Johnson edge additionally uses a_{rad}
|
|
48
|
+
(LENGTH, the sphere radius) and reads the filler diagonal k_{33} (= k_{11} for
|
|
49
|
+
the isotropic sphere it models).
|
|
50
|
+
"""
|
|
51
|
+
from __future__ import annotations
|
|
52
|
+
|
|
53
|
+
import sympy as sp
|
|
54
|
+
|
|
55
|
+
from omai.operator.operator import Operator
|
|
56
|
+
from omai.composites.operator.nodes import (
|
|
57
|
+
DEPOLARIZATION_FACTOR,
|
|
58
|
+
EFFECTIVE_CONDUCTIVITY_ALIGNED,
|
|
59
|
+
EFFECTIVE_CONDUCTIVITY_RANDOM,
|
|
60
|
+
FILLER_CONDUCTIVITY,
|
|
61
|
+
FILLER_VOLUME_FRACTION,
|
|
62
|
+
INTERFACE_CONDUCTANCE_NODE,
|
|
63
|
+
MATRIX_CONDUCTIVITY,
|
|
64
|
+
)
|
|
65
|
+
from omai.thermal_transport.operator.nodes import THERMAL_CONDUCTIVITY
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Symbols. Every base name is bound in this domain's dimensions_registry (or is
|
|
70
|
+
# a pure-number constant), so the dimensional gate proves the four closed forms.
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
_km = sp.Symbol("k_m", positive=True) # matrix kappa, THERMAL_CONDUCTIVITY
|
|
74
|
+
_kf = sp.IndexedBase("k_f") # filler kappa tensor, diagonal read below
|
|
75
|
+
_k11 = _kf[1, 1] # transverse (in-plane) filler kappa
|
|
76
|
+
_k33 = _kf[3, 3] # axial (through-plane) filler kappa
|
|
77
|
+
_G = sp.Symbol("G_{int}", positive=True) # interface conductance, INTERFACE_CONDUCTANCE
|
|
78
|
+
_d1 = sp.Symbol("d_1", positive=True) # equatorial dimension, LENGTH
|
|
79
|
+
_d3 = sp.Symbol("d_3", positive=True) # polar dimension, LENGTH
|
|
80
|
+
_f = sp.Symbol("f_{vol}", nonnegative=True) # filler volume fraction, DIMENSIONLESS
|
|
81
|
+
_L11 = sp.Symbol("L_{11}") # depolarization factor, DIMENSIONLESS
|
|
82
|
+
_L33 = sp.Symbol("L_{33}") # depolarization factor, DIMENSIONLESS
|
|
83
|
+
_kappa_c = sp.Symbol(r"\kappa_c", positive=True) # composite effective kappa, THERMAL_CONDUCTIVITY
|
|
84
|
+
_p = sp.Symbol("p", positive=True) # aspect ratio d3/d1, DIMENSIONLESS
|
|
85
|
+
_a = sp.Symbol("a_{rad}", positive=True) # sphere radius (HJ), LENGTH
|
|
86
|
+
|
|
87
|
+
# Interface resistance R = 1/G and the per-direction interface-corrected filler
|
|
88
|
+
# conductivity kc_ii = k_ii / (1 + 2 R k_ii / d_i). Written inline so the gate
|
|
89
|
+
# proves the effective-kappa closed forms.
|
|
90
|
+
_R = 1 / _G
|
|
91
|
+
_kc11 = _k11 / (1 + 2 * _R * _k11 / _d1)
|
|
92
|
+
_kc33 = _k33 / (1 + 2 * _R * _k33 / _d3)
|
|
93
|
+
_beta11 = (_kc11 - _km) / (_km + _L11 * (_kc11 - _km))
|
|
94
|
+
_beta33 = (_kc33 - _km) / (_km + _L33 * (_kc33 - _km))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Depolarization factors (closed form; sp.Piecewise, gate-skipped by design).
|
|
99
|
+
# L33(p) has an atanh branch for prolate p>1, an atan branch for oblate p<1, and
|
|
100
|
+
# the sphere value 1/3 at p=1; L11 = (1 - L33)/2 (the sum rule 2 L11 + L33 = 1).
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
_e_prolate = sp.sqrt(1 - 1 / _p**2)
|
|
104
|
+
_e_oblate = sp.sqrt(1 / _p**2 - 1)
|
|
105
|
+
_L33_expr = sp.Piecewise(
|
|
106
|
+
(sp.Rational(1, 3), sp.Eq(_p, 1)),
|
|
107
|
+
(((1 - _e_prolate**2) / _e_prolate**3) * (sp.atanh(_e_prolate) - _e_prolate), _p > 1),
|
|
108
|
+
(((1 + _e_oblate**2) / _e_oblate**3) * (_e_oblate - sp.atan(_e_oblate)), True),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
depolarization_factors = Operator(
|
|
112
|
+
name="depolarization_factors",
|
|
113
|
+
inputs=(),
|
|
114
|
+
outputs=(DEPOLARIZATION_FACTOR,),
|
|
115
|
+
formula=sp.Eq(_L33, _L33_expr),
|
|
116
|
+
auxiliary_formulas=(sp.Eq(_L11, (1 - _L33) / 2),),
|
|
117
|
+
description=(
|
|
118
|
+
"Spheroid depolarization (Eshelby) factors (L11, L33) of an "
|
|
119
|
+
"axially-symmetric inclusion (polar axis 3) from the aspect ratio "
|
|
120
|
+
"p = d3/d1: a closed form, L33(p) with an atanh branch for prolate p>1 "
|
|
121
|
+
"(fiber), an atan branch for oblate p<1 (platelet), and the sphere value "
|
|
122
|
+
"1/3 at p=1, with L11 = (1 - L33)/2 enforcing the sum rule 2 L11 + L33 = "
|
|
123
|
+
"1. Analytic limits sphere (1/3, 1/3), long fiber (1/2, 0), thin disk "
|
|
124
|
+
"(0, 1). The aspect ratio rides in the record conditions (this is a "
|
|
125
|
+
"geometry-from-conditions producer, nullary at the store level, like the "
|
|
126
|
+
"source provide_ edges). Formula is a sp.Piecewise: the dimensional gate "
|
|
127
|
+
"SKIPS it (the output is dimensionless by construction and the gate only "
|
|
128
|
+
"needs the shape), and the numeric branches are pinned by the domain test."
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
# Nan effective conductivity, randomly oriented filler (the scalar observable).
|
|
135
|
+
# kappa_c = km (3 + f (2 beta11 (1-L11) + beta33 (1-L33)))
|
|
136
|
+
# / (3 - f (2 beta11 L11 + beta33 L33)), written inline -> gate PROVES.
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
_nan_random_rhs = _km * (
|
|
140
|
+
3 + _f * (2 * _beta11 * (1 - _L11) + _beta33 * (1 - _L33))
|
|
141
|
+
) / (
|
|
142
|
+
3 - _f * (2 * _beta11 * _L11 + _beta33 * _L33)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
nan_effective_kappa = Operator(
|
|
146
|
+
name="nan_effective_kappa",
|
|
147
|
+
inputs=(MATRIX_CONDUCTIVITY, FILLER_CONDUCTIVITY, INTERFACE_CONDUCTANCE_NODE,
|
|
148
|
+
FILLER_VOLUME_FRACTION, DEPOLARIZATION_FACTOR),
|
|
149
|
+
outputs=(EFFECTIVE_CONDUCTIVITY_RANDOM,),
|
|
150
|
+
formula=sp.Eq(_kappa_c, _nan_random_rhs),
|
|
151
|
+
auxiliary_formulas=(
|
|
152
|
+
sp.Eq(sp.Symbol("kc_{11}"), _kc11),
|
|
153
|
+
sp.Eq(sp.Symbol("kc_{33}"), _kc33),
|
|
154
|
+
sp.Eq(sp.Symbol(r"\beta_{11}"), _beta11),
|
|
155
|
+
sp.Eq(sp.Symbol(r"\beta_{33}"), _beta33),
|
|
156
|
+
),
|
|
157
|
+
description=(
|
|
158
|
+
"Nan-type effective-medium thermal conductivity for RANDOMLY oriented "
|
|
159
|
+
"filler with a per-direction interfacial (Kapitza) series film "
|
|
160
|
+
"(Nan et al., J. Appl. Phys. 81, 6692 (1997)): the isotropic scalar "
|
|
161
|
+
"kappa_c = km (3 + f (2 beta11 (1-L11) + beta33 (1-L33))) / (3 - f (2 "
|
|
162
|
+
"beta11 L11 + beta33 L33)), with the interface-corrected filler "
|
|
163
|
+
"conductivities kc_ii = k_ii / (1 + 2 R k_ii / d_i) (R = 1/G) and the "
|
|
164
|
+
"Bruggeman-Nan terms beta_ii = (kc_ii - km) / (km + L_ii (kc_ii - km)) "
|
|
165
|
+
"substituted INLINE. The dimensional gate PROVES it: every sub-ratio is "
|
|
166
|
+
"dimensionless (2 (1/G) k_ii / d_i = (m^2 K/W)(W/mK)/m = 1; beta_ii is a "
|
|
167
|
+
"ratio of two W/(m K) quantities), so kappa_c carries km's "
|
|
168
|
+
"THERMAL_CONDUCTIVITY (1,1,-3,-1,0,0,0). Zero loading (f=0) returns km. "
|
|
169
|
+
"Below the Kapitza radius a_K = km/G a conductive filler LOWERS kappa_c. "
|
|
170
|
+
"The reference DGEBA epoxy + 5 vol% GNP (km=0.2, k11=1200, k33=6, d1=5um, "
|
|
171
|
+
"d3=0.02um, G=25 MW/m2K, f=0.05) gives kappa_c = 1.2452 W/(m K)."
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
# Nan effective conductivity, perfectly aligned filler (anisotropic tensor).
|
|
178
|
+
# Declared closed form is the AXIAL (through-plane) component al33; the transverse
|
|
179
|
+
# al11 is its L11 / beta11 companion (documented, rides in conditions).
|
|
180
|
+
# al33 = km (1 + f beta33 (1-L33)) / (1 - f beta33 L33), inline -> gate PROVES.
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
_nan_aligned33_rhs = _km * (1 + _f * _beta33 * (1 - _L33)) / (1 - _f * _beta33 * _L33)
|
|
184
|
+
|
|
185
|
+
nan_effective_kappa_aligned = Operator(
|
|
186
|
+
name="nan_effective_kappa_aligned",
|
|
187
|
+
inputs=(MATRIX_CONDUCTIVITY, FILLER_CONDUCTIVITY, INTERFACE_CONDUCTANCE_NODE,
|
|
188
|
+
FILLER_VOLUME_FRACTION, DEPOLARIZATION_FACTOR),
|
|
189
|
+
outputs=(EFFECTIVE_CONDUCTIVITY_ALIGNED,),
|
|
190
|
+
formula=sp.Eq(_kappa_c, _nan_aligned33_rhs),
|
|
191
|
+
auxiliary_formulas=(
|
|
192
|
+
sp.Eq(sp.Symbol(r"\kappa_c^{al11}"),
|
|
193
|
+
_km * (1 + _f * _beta11 * (1 - _L11)) / (1 - _f * _beta11 * _L11)),
|
|
194
|
+
),
|
|
195
|
+
description=(
|
|
196
|
+
"Nan-type effective conductivity for PERFECTLY ALIGNED filler "
|
|
197
|
+
"(Nan et al. 1997), a different mixing formula from the random "
|
|
198
|
+
"orientation: the anisotropic composite tensor whose AXIAL "
|
|
199
|
+
"(through-plane, along the alignment axis 3) component is the declared "
|
|
200
|
+
"closed form al33 = km (1 + f beta33 (1-L33)) / (1 - f beta33 L33), and "
|
|
201
|
+
"whose TRANSVERSE (in-plane, transverse to axis 3) component is the "
|
|
202
|
+
"L11/beta11 companion al11 = km (1 + f beta11 (1-L11)) / (1 - f beta11 "
|
|
203
|
+
"L11) (the auxiliary formula; it rides in instance conditions, the "
|
|
204
|
+
"elastic-constants precedent for tensor components). Same inline kc_ii / "
|
|
205
|
+
"beta_ii substitution, so the gate PROVES al33 carries km's "
|
|
206
|
+
"THERMAL_CONDUCTIVITY. Aligned platelets are the enhancement geometry a "
|
|
207
|
+
"thermal-interface material targets."
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ---------------------------------------------------------------------------
|
|
213
|
+
# Hasselman-Johnson sphere cross-check (J. Compos. Mater. 21, 508 (1987)), a
|
|
214
|
+
# SECOND PRODUCER (Pattern C) of the random effective-kappa node. For an
|
|
215
|
+
# isotropic spherical filler (kp = k11 = k33, radius a) with interfacial
|
|
216
|
+
# resistance, alpha = a_K / a = (km/G)/a (dimensionless), and
|
|
217
|
+
# kappa_c = km [kp(1+2a) + 2km + 2f(kp(1-a)-km)] / [kp(1+2a) + 2km - f(kp(1-a)-km)].
|
|
218
|
+
# At aspect 1 the Nan random result reduces to this EXACTLY; the domain test pins
|
|
219
|
+
# the agreement to machine precision. Written inline -> the gate PROVES it.
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
_alpha_hj = (_km / _G) / _a
|
|
223
|
+
_hj_diff = _k33 * (1 - _alpha_hj) - _km
|
|
224
|
+
_hj_rhs = _km * (
|
|
225
|
+
_k33 * (1 + 2 * _alpha_hj) + 2 * _km + 2 * _f * _hj_diff
|
|
226
|
+
) / (
|
|
227
|
+
_k33 * (1 + 2 * _alpha_hj) + 2 * _km - _f * _hj_diff
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
hasselman_johnson = Operator(
|
|
231
|
+
name="hasselman_johnson",
|
|
232
|
+
inputs=(MATRIX_CONDUCTIVITY, FILLER_CONDUCTIVITY, INTERFACE_CONDUCTANCE_NODE,
|
|
233
|
+
FILLER_VOLUME_FRACTION),
|
|
234
|
+
outputs=(EFFECTIVE_CONDUCTIVITY_RANDOM,),
|
|
235
|
+
formula=sp.Eq(_kappa_c, _hj_rhs),
|
|
236
|
+
description=(
|
|
237
|
+
"Hasselman-Johnson effective conductivity for spherical filler with "
|
|
238
|
+
"interfacial (Kapitza) resistance (J. Compos. Mater. 21, 508 (1987)), an "
|
|
239
|
+
"INDEPENDENT cross-check: for an isotropic sphere (kp = k11 = k33, radius "
|
|
240
|
+
"a) with alpha = a_K / a = (km/G)/a the Kapitza-radius-to-radius ratio "
|
|
241
|
+
"(dimensionless), kappa_c = km [kp(1+2 alpha) + 2 km + 2 f (kp(1-alpha) - "
|
|
242
|
+
"km)] / [kp(1+2 alpha) + 2 km - f (kp(1-alpha) - km)]. Reads the filler "
|
|
243
|
+
"diagonal k33 (= k11 for the isotropic sphere it models). SECOND PRODUCER "
|
|
244
|
+
"(Pattern C) of the random effective-kappa node alongside "
|
|
245
|
+
"nan_effective_kappa: at aspect ratio 1 (a sphere) the Nan random formula "
|
|
246
|
+
"reduces to THIS one EXACTLY, so the two must agree numerically (the "
|
|
247
|
+
"redundant-route pattern BulkModulus's three producers already bless); the "
|
|
248
|
+
"domain test pins the agreement to machine precision. The dimensional gate "
|
|
249
|
+
"PROVES it: alpha is dimensionless (km/G is a length, over the radius a), "
|
|
250
|
+
"so kappa_c carries km's THERMAL_CONDUCTIVITY."
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# ---------------------------------------------------------------------------
|
|
256
|
+
# Resolve onto the method-neutral observable. The composite effective
|
|
257
|
+
# conductivity IS a thermal conductivity: a measurement of the composite reports
|
|
258
|
+
# THIS observable, and the effective-medium route is its estimator. This edge
|
|
259
|
+
# anchors the random effective-kappa onto the neutral ThermalConductivity node
|
|
260
|
+
# (the resolve_thermal_conductivity precedent), which also connects this domain
|
|
261
|
+
# to the pre-existing map (connectivity: the neutral node is a pre-existing store
|
|
262
|
+
# node). Identity Eq, marked executable like resolve_thermal_conductivity.
|
|
263
|
+
# ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
_kappa_obs = sp.IndexedBase(r"\kappa")
|
|
266
|
+
|
|
267
|
+
resolve_effective_conductivity = Operator(
|
|
268
|
+
name="resolve_effective_conductivity",
|
|
269
|
+
inputs=(EFFECTIVE_CONDUCTIVITY_RANDOM,),
|
|
270
|
+
outputs=(THERMAL_CONDUCTIVITY,),
|
|
271
|
+
formula=sp.Eq(_kappa_obs[sp.Symbol("alpha"), sp.Symbol("beta")],
|
|
272
|
+
_kappa_obs[sp.Symbol("alpha"), sp.Symbol("beta")]),
|
|
273
|
+
is_executable_in_sympy_override=True,
|
|
274
|
+
description=(
|
|
275
|
+
"Identity: the randomly-oriented composite effective conductivity is the "
|
|
276
|
+
"physical thermal conductivity a measurement of the composite reports "
|
|
277
|
+
"(isotropic, so its scalar sits on the diagonal of the neutral kappa "
|
|
278
|
+
"tensor). This edge anchors the composite result onto the method-neutral "
|
|
279
|
+
"ThermalConductivity observable, the home for measured and "
|
|
280
|
+
"method-unspecified kappa: the Nan effective-medium route (and the "
|
|
281
|
+
"Hasselman-Johnson second producer) are ESTIMATORS of this observable, "
|
|
282
|
+
"related by the shared thermal_conductivity tag, exactly the "
|
|
283
|
+
"resolve_thermal_conductivity precedent that anchors the lattice LBTE "
|
|
284
|
+
"kappa. It also bridges this domain to the pre-existing map (the neutral "
|
|
285
|
+
"node already exists in the store)."
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
EDGES: tuple[Operator, ...] = (
|
|
290
|
+
depolarization_factors,
|
|
291
|
+
nan_effective_kappa,
|
|
292
|
+
nan_effective_kappa_aligned,
|
|
293
|
+
hasselman_johnson,
|
|
294
|
+
resolve_effective_conductivity,
|
|
295
|
+
)
|