Antonio0616 commited on
Commit
373ade9
ยท
verified ยท
1 Parent(s): 23d37b8

Upload make_data_grid.py

Browse files
Files changed (1) hide show
  1. make_data_grid.py +259 -0
make_data_grid.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # make_data_grid.py (clean + robust paths)
2
+
3
+ from __future__ import annotations
4
+ from decimal import Decimal
5
+ from pathlib import Path
6
+ from typing import Dict, List, Tuple, Union
7
+ import itertools
8
+ import warnings
9
+ import os
10
+
11
+ import numpy as np
12
+ import pandas as pd
13
+
14
+ warnings.filterwarnings("ignore", category=FutureWarning)
15
+
16
+ # =============================
17
+ # Grid & helpers
18
+ # =============================
19
+ def dseq(start: float, stop: float, step: float, q: str = "0.001") -> List[float]:
20
+ s, e, st = map(lambda x: Decimal(str(x)), [start, stop, step])
21
+ vals, cur = [], s
22
+ while cur <= e + Decimal("1e-12"):
23
+ vals.append(float(cur.quantize(Decimal(q))))
24
+ cur += st
25
+ return vals
26
+
27
+ def bead_to_lr(bead_value: Union[str, None]) -> Tuple[int, int]:
28
+ mapping = {None:(2,2), "none":(0,0), "right":(0,1), "left":(1,0), "double":(1,1)}
29
+ key = bead_value.lower() if isinstance(bead_value, str) else bead_value
30
+ return mapping.get(key, (0,0))
31
+
32
+ def make_all_combinations(cfg: Dict) -> pd.DataFrame:
33
+ bead_values = cfg.get("beads") or [None]
34
+ bead_info = [(b, *bead_to_lr(b)) for b in bead_values]
35
+
36
+ materials = cfg["materials"]
37
+ thickness = dseq(cfg["min_thickness"], cfg["max_thickness"], cfg["thickness_step"])
38
+ diameter = [int(x) for x in dseq(cfg["min_diameter"], cfg["max_diameter"], cfg["diameter_step"], q="1")]
39
+ upper_r = dseq(cfg["upper_min"], cfg["upper_max"], cfg["upper_step"])
40
+ lower_r = dseq(cfg["lower_min"], cfg["lower_max"], cfg["lower_step"])
41
+ degree = [int(x) for x in dseq(cfg["min_degree"], cfg["max_degree"], cfg["degree_step"], q="1")]
42
+
43
+ grid = itertools.product(materials, thickness, upper_r, lower_r, diameter, degree, bead_info)
44
+
45
+ rows = []
46
+ for mat, th, ur, lr, dia, deg, bead_t in grid:
47
+ bead_name, lb, rb = bead_t
48
+ rows.append((str(mat), float(th), float(ur), float(lr), int(dia), int(deg), bead_name, int(lb), int(rb)))
49
+
50
+ return pd.DataFrame(
51
+ rows,
52
+ columns=["material","thickness","upper_radius","lower_radius",
53
+ "diameter","degree","bead","LB","RB"]
54
+ )
55
+
56
+ # =============================
57
+ # Sweep-limit helpers
58
+ # =============================
59
+ def build_limit_dicts(df_sweep: pd.DataFrame):
60
+ t = df_sweep.copy().replace("F", np.nan)
61
+ if "Sweep" in t.columns:
62
+ t = t.set_index("Sweep")
63
+
64
+ new_cols = []
65
+ for c in t.columns:
66
+ try: new_cols.append(int(c))
67
+ except: new_cols.append(c)
68
+ t.columns = new_cols
69
+
70
+ long = t.stack(dropna=False).reset_index()
71
+ long.columns = ["row","degree","limit"]
72
+
73
+ tmp = long["row"].str.extract(r"(?P<diameter>\d+)_(?P<which>upper|lower)_radius")
74
+ long = pd.concat([long, tmp], axis=1)
75
+ long["diameter"] = pd.to_numeric(long["diameter"], errors="coerce")
76
+ long["degree"] = pd.to_numeric(long["degree"], errors="coerce")
77
+ long["limit"] = pd.to_numeric(long["limit"], errors="coerce")
78
+
79
+ upper = long[long["which"]=="upper"].dropna(subset=["diameter","degree"])
80
+ lower = long[long["which"]=="lower"].dropna(subset=["diameter","degree"])
81
+
82
+ upper_dict = {(int(d), int(g)): v for d, g, v in zip(upper["diameter"], upper["degree"], upper["limit"])}
83
+ lower_dict = {(int(d), int(g)): v for d, g, v in zip(lower["diameter"], lower["degree"], lower["limit"])}
84
+ return upper_dict, lower_dict
85
+
86
+ def filter_grid_by_sweep_limits(df_grid: pd.DataFrame, df_sweep: pd.DataFrame) -> pd.DataFrame:
87
+ upper_dict, lower_dict = build_limit_dicts(df_sweep)
88
+ key = list(zip(df_grid["diameter"].astype(int), df_grid["degree"].astype(int)))
89
+
90
+ df_grid = df_grid.copy()
91
+ df_grid["limit_upper"] = [upper_dict.get(k, np.nan) for k in key]
92
+ df_grid["limit_lower"] = [lower_dict.get(k, np.nan) for k in key]
93
+
94
+ not_nan = df_grid["limit_upper"].notna() & df_grid["limit_lower"].notna()
95
+ within = (df_grid["upper_radius"] <= df_grid["limit_upper"]) & \
96
+ (df_grid["lower_radius"] <= df_grid["limit_lower"])
97
+ return df_grid[not_nan & within].reset_index(drop=True)
98
+
99
+ # =============================
100
+ # Rule-by-bead helpers
101
+ # =============================
102
+ SHEET_BY_BEAD = {"left":"left", "right":"right", "double":"both", "none":"none"}
103
+
104
+ def _normalize_input_df(df: pd.DataFrame) -> pd.DataFrame:
105
+ df2 = df.copy()
106
+ df2.columns = [str(c).strip() for c in df2.columns]
107
+ rename = {}
108
+ for c in df2.columns:
109
+ lc = c.lower().strip()
110
+ if lc == "diamater": rename[c] = "diameter"
111
+ elif lc in ("material","diameter","degree","bead"): rename[c] = lc
112
+ df2 = df2.rename(columns=rename)
113
+ need = {"material","diameter","degree","bead"}
114
+ missing = need - set(df2.columns)
115
+ if missing:
116
+ raise ValueError(f"์ž…๋ ฅ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์— ํ•„์š”ํ•œ ์ปฌ๋Ÿผ์ด ์—†์Šต๋‹ˆ๋‹ค: {missing}")
117
+ df2["bead"] = df2["bead"].astype(str).str.strip().str.lower()
118
+ for c in ["material","diameter","degree"]:
119
+ df2[c] = pd.to_numeric(df2[c], errors="coerce")
120
+ df2 = df2.dropna(subset=["material","diameter","degree"]).copy()
121
+ return df2
122
+
123
+ def _read_rule_sheet(xlsx_path: str, sheet_name: str) -> pd.DataFrame:
124
+ rule = pd.read_excel(xlsx_path, sheet_name=sheet_name)
125
+ rule.columns = rule.columns.str.strip().str.lower()
126
+ rule = rule.rename(columns={"diamater":"diameter"})
127
+ need_cols = {"material","diameter","min_degree","max_degree"}
128
+ missing = need_cols - set(rule.columns)
129
+ if missing:
130
+ raise ValueError(f"๊ทœ์น™ ์‹œํŠธ '{sheet_name}'์— ํ•„์š”ํ•œ ์ปฌ๋Ÿผ์ด ์—†์Šต๋‹ˆ๋‹ค: {missing}")
131
+ for c in need_cols:
132
+ rule[c] = pd.to_numeric(rule[c], errors="coerce")
133
+ rule = rule.dropna(subset=list(need_cols)).copy()
134
+ rule = rule.astype({"material":"int64","diameter":"int64"})
135
+ return rule[["material","diameter","min_degree","max_degree"]]
136
+
137
+ def _apply_rules(df_part: pd.DataFrame, rule: pd.DataFrame) -> pd.DataFrame:
138
+ if df_part.empty:
139
+ return df_part.copy()
140
+ df_part = df_part[df_part["material"].isin(rule["material"].unique())].copy()
141
+ if df_part.empty:
142
+ return df_part
143
+ merged = df_part.merge(rule, on=["material","diameter"], how="left")
144
+ mask = (
145
+ merged["min_degree"].notna()
146
+ & merged["max_degree"].notna()
147
+ & (merged["degree"] >= merged["min_degree"])
148
+ & (merged["degree"] <= merged["max_degree"])
149
+ )
150
+ kept = merged.loc[mask, df_part.columns].reset_index(drop=True)
151
+ return kept
152
+
153
+ def filter_all_by_bead(df: pd.DataFrame, rules_xlsx: str) -> pd.DataFrame:
154
+ base = _normalize_input_df(df)
155
+ outs = []
156
+ for bead_value, sheet in SHEET_BY_BEAD.items():
157
+ part = base[base["bead"] == bead_value].copy()
158
+ if part.empty:
159
+ continue
160
+ rule = _read_rule_sheet(rules_xlsx, sheet)
161
+ kept = _apply_rules(part, rule)
162
+ outs.append(kept)
163
+ if not outs:
164
+ return base.iloc[0:0].copy()
165
+ result = pd.concat(outs, axis=0, ignore_index=True)
166
+ return result.reset_index(drop=True)
167
+
168
+ # =============================
169
+ # Predictors (blend models)
170
+ # =============================
171
+ DISPLAY_TO_MODEL = {"440":"440.0", "590":"590.0", "780":"780.0"}
172
+
173
+ def _here() -> Path:
174
+ try: return Path(__file__).resolve().parent
175
+ except Exception: return Path.cwd()
176
+
177
+ def _abs(p: Path | str) -> str:
178
+ return str(Path(p).resolve())
179
+
180
+ def _find_art_dir(name: str) -> str:
181
+ """
182
+ ์ ˆ๋Œ€๊ฒฝ๋กœ ํƒ์ƒ‰ ์šฐ์„ ์ˆœ์œ„:
183
+ 1) ํ™˜๊ฒฝ๋ณ€์ˆ˜ FS_MF_ART_DIR / FS_THIN_ART_DIR
184
+ 2) ์ด ๋ชจ๋“ˆ ํŒŒ์ผ ๊ธฐ์ค€
185
+ 3) ํ˜„์žฌ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ
186
+ 4) ๋ชจ๋“ˆ ์ƒ์œ„ ๊ฒฝ๋กœ
187
+ """
188
+ env_map = {
189
+ "artifacts_blend": os.getenv("FS_MF_ART_DIR"),
190
+ "artifacts_blend_thinning": os.getenv("FS_THIN_ART_DIR"),
191
+ }
192
+ hinted = env_map.get(name)
193
+ if hinted and Path(hinted).exists():
194
+ return _abs(hinted)
195
+
196
+ for base in (_here(), Path.cwd(), _here().parent):
197
+ cand = (base / name).resolve()
198
+ if cand.exists():
199
+ return _abs(cand)
200
+ return _abs(name)
201
+
202
+ _predictor_mf = None
203
+ _predictor_th = None
204
+
205
+ def _prep_pred_df(df: pd.DataFrame) -> pd.DataFrame:
206
+ need = ["material","thickness","diameter","degree","upper_radius","lower_radius","LB","RB"]
207
+ missing = [c for c in need if c not in df.columns]
208
+ if missing:
209
+ raise ValueError(f"์˜ˆ์ธก ์ž…๋ ฅ ์ปฌ๋Ÿผ ๋ˆ„๋ฝ: {missing}")
210
+ x = df.copy()
211
+ x["material"] = x["material"].astype(str).map(lambda v: DISPLAY_TO_MODEL.get(v, v))
212
+ x["LB"] = x["LB"].astype(int); x["RB"] = x["RB"].astype(int)
213
+ for c in ["thickness","diameter","degree","upper_radius","lower_radius"]:
214
+ x[c] = pd.to_numeric(x[c], errors="coerce")
215
+ if x[["thickness","diameter","degree","upper_radius","lower_radius"]].isnull().any().any():
216
+ raise ValueError("์ˆซ์ž ์ปฌ๋Ÿผ์— NaN์ด ์žˆ์Šต๋‹ˆ๋‹ค.")
217
+ return x[need]
218
+
219
+ def _get_predictor_mf():
220
+ global _predictor_mf
221
+ if _predictor_mf is not None:
222
+ return _predictor_mf
223
+ from predict_blend import BlendPredictor
224
+ art_dir = _find_art_dir("artifacts_blend")
225
+ _predictor_mf = BlendPredictor(art_dir)
226
+ return _predictor_mf
227
+
228
+ def _get_predictor_th():
229
+ global _predictor_th
230
+ if _predictor_th is not None:
231
+ return _predictor_th
232
+ try:
233
+ from predict_blend_thinning import BlendPredictor as ThinPredictor
234
+ art_dir = _find_art_dir("artifacts_blend_thinning")
235
+ _predictor_th = ThinPredictor(art_dir)
236
+ return _predictor_th
237
+ except FileNotFoundError:
238
+ # ํด๋”๊ฐ€ ์—†์„ ๋•Œ ๊ฐ„๋‹จ ํœด๋ฆฌ์Šคํ‹ฑ ํด๋ฐฑ
239
+ def _heuristic_thinning(thickness, upperR, lowerR):
240
+ t = float(thickness); ur = float(upperR); lr = float(lowerR)
241
+ base = 0.18 + (0.9 - t) * 0.25
242
+ geom = max(0.0, ur - lr) * 0.01
243
+ return float(max(0.05, min(0.8, base + geom)))
244
+ class _HeuristicThinPredictor:
245
+ def predict_blend(self, df: pd.DataFrame):
246
+ return np.array([
247
+ _heuristic_thinning(r["thickness"], r["upper_radius"], r["lower_radius"])
248
+ for _, r in df.iterrows()
249
+ ], dtype=float)
250
+ _predictor_th = _HeuristicThinPredictor()
251
+ return _predictor_th
252
+
253
+ def predict_max_failure(df: pd.DataFrame) -> np.ndarray:
254
+ pred_df = _prep_pred_df(df)
255
+ return _get_predictor_mf().predict_blend(pred_df)
256
+
257
+ def predict_thinning(df: pd.DataFrame) -> np.ndarray:
258
+ pred_df = _prep_pred_df(df)
259
+ return _get_predictor_th().predict_blend(pred_df)