File size: 3,063 Bytes
cdcc0fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import csv
import json

import numpy as np

from modeling import preprocess

N = 96


def _coords():
    yy, xx = np.meshgrid(np.linspace(0, 1, N), np.linspace(0, 1, N), indexing="ij")
    return xx, yy


def _to_field(v):
    a = np.asarray(v, dtype=np.float64)
    if a.ndim == 0:
        return np.full((N, N), float(a))
    if a.shape == (N, N):
        return a
    yi = np.linspace(0, a.shape[0] - 1, N).round().astype(int)
    xi = np.linspace(0, a.shape[1] - 1, N).round().astype(int)
    return a[yi][:, xi]


def cylinder_mask(rows, cols, radius_frac=0.4):
    xx, yy = _coords()
    m = np.zeros((N, N))
    px, py = 1.0 / cols, 1.0 / rows
    r = radius_frac * min(px, py)
    for i in range(rows):
        cy = (i + 0.5) * py
        for j in range(cols):
            cx = (j + 0.5) * px
            m[(xx - cx) ** 2 + (yy - cy) ** 2 <= r * r] = 1.0
    return m


def rect_mask(aspect=1.0, fill=0.7):
    xx, yy = _coords()
    m = np.zeros((N, N))
    hh = min(0.98, (fill / aspect) ** 0.5)
    ww = min(0.98, aspect * hh)
    x0, x1 = 0.5 - ww / 2, 0.5 + ww / 2
    y0, y1 = 0.5 - hh / 2, 0.5 + hh / 2
    m[(xx >= x0) & (xx <= x1) & (yy >= y0) & (yy <= y1)] = 1.0
    return m


def from_fields(
    mask, q_source_W_m3, k_field_W_mK, h_conv_W_m2K, T_amb_degC, domain_L_m
):
    m = (_to_field(mask) > 0.5).astype(np.float64)
    q = _to_field(q_source_W_m3) * m
    k = _to_field(k_field_W_mK)
    h = _to_field(h_conv_W_m2K)
    ta = _to_field(T_amb_degC)
    xx, yy = _coords()
    logL = np.full((N, N), float(np.log(domain_L_m)))
    field = np.stack([m, q, k, h, ta, xx, yy, logL], 0).astype(np.float32)
    return preprocess(field)


def from_pack(
    mask,
    current_A,
    soc,
    R0_ohm,
    k_cell_W_mK,
    k_coolant_W_mK,
    h_conv_W_m2K,
    T_amb_degC,
    domain_L_m,
    beta=2.0,
):
    m = (_to_field(mask) > 0.5).astype(np.float64)
    hg = domain_L_m / (N - 1)
    R_int = R0_ohm * (1.0 + beta * (1.0 - soc) ** 2)
    P_total = current_A**2 * R_int
    area = max(m.sum() * hg * hg, hg * hg)
    q = m * (P_total / area)
    k = np.where(m > 0, k_cell_W_mK, k_coolant_W_mK)
    return from_fields(m, q, k, h_conv_W_m2K, T_amb_degC, domain_L_m)


def from_params(d):
    if d.get("rows") not in (None, "") and d.get("cols") not in (None, ""):
        mask = cylinder_mask(
            int(float(d["rows"])),
            int(float(d["cols"])),
            float(d.get("radius_frac") or 0.4),
        )
    else:
        mask = rect_mask(float(d.get("aspect") or 1.0), float(d.get("fill") or 0.7))
    return from_pack(
        mask,
        float(d["current_A"]),
        float(d["soc"]),
        float(d["R0_ohm"]),
        float(d["k_cell_W_mK"]),
        float(d["k_coolant_W_mK"]),
        float(d["h_conv_W_m2K"]),
        float(d["T_amb_degC"]),
        float(d["domain_L_m"]),
        beta=float(d.get("beta") or 2.0),
    )


def from_json(path):
    with open(path) as f:
        return from_params(json.load(f))


def from_csv(path):
    with open(path) as f:
        return from_params(next(csv.DictReader(f)))