delta-theory 8.1.2__py3-none-any.whl → 8.2.1__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.
@@ -0,0 +1,768 @@
1
+ #!/usr/bin/env python3
2
+ """δ-Theory Unified FLC v8.1 - Integrated with v6.9
3
+
4
+ =============================================================================
5
+ v8.0 → v8.1: Integration with unified_yield_fatigue_v6_9
6
+ =============================================================================
7
+
8
+ 【新機能】
9
+ - v6.9 から材料パラメータを自動取得
10
+ - FLCMaterial.from_v69() で簡単に材料作成
11
+ - FLC₀ 1点校正で全モード予測
12
+ - v6.9のMaterialクラスから直接変換
13
+
14
+ 【使い方】
15
+ # v6.9 材料から直接 FLC 予測
16
+ flc = FLCPredictor()
17
+ flc.add_from_v69('Fe', flc0=0.225) # SPCC相当
18
+ eps1 = flc.predict('Fe', 'Plane Strain')
19
+
20
+ # カスタム材料
21
+ flc.add_from_v69(
22
+ name='MyAlloy',
23
+ base_element='Fe',
24
+ structure='BCC',
25
+ flc0=0.28,
26
+ T_twin=1.0,
27
+ )
28
+
29
+ # v6.9のMaterialクラスから直接
30
+ # (v6.9がimport可能な場合)
31
+ from unified_yield_fatigue_v6_9 import MATERIALS
32
+ flc.add_from_v69_material(MATERIALS['Fe'], flc0=0.225, name='SPCC')
33
+
34
+ Author: δ-Theory Team (Masamichi Iizumi & Tamaki)
35
+ Version: 8.1.0
36
+ Date: 2025-02
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import warnings
42
+ from dataclasses import dataclass
43
+ from typing import Dict, List, Optional, Tuple, Union
44
+ import numpy as np
45
+
46
+
47
+ # ==============================================================================
48
+ # Try to import v6.9b
49
+ # ==============================================================================
50
+
51
+ V69_AVAILABLE = False
52
+ V69_MATERIALS = {}
53
+ V69_Material = None
54
+
55
+ try:
56
+ from unified_yield_fatigue_v6_9 import (
57
+ tau_over_sigma as v69_tau_over_sigma,
58
+ sigma_c_over_sigma_t as v69_sigma_c_over_sigma_t,
59
+ sigma_base_delta,
60
+ calc_sigma_y,
61
+ Material as V69_Material,
62
+ MATERIALS as V69_MATERIALS,
63
+ T_TWIN as V69_T_TWIN,
64
+ R_COMP as V69_R_COMP,
65
+ C_CLASS_DEFAULT,
66
+ DEFAULT_BCC_W110,
67
+ )
68
+ V69_AVAILABLE = True
69
+ except ImportError:
70
+ try:
71
+ # Try relative import (as package)
72
+ from .unified_yield_fatigue_v6_9 import (
73
+ tau_over_sigma as v69_tau_over_sigma,
74
+ sigma_c_over_sigma_t as v69_sigma_c_over_sigma_t,
75
+ sigma_base_delta,
76
+ calc_sigma_y,
77
+ Material as V69_Material,
78
+ MATERIALS as V69_MATERIALS,
79
+ T_TWIN as V69_T_TWIN,
80
+ R_COMP as V69_R_COMP,
81
+ C_CLASS_DEFAULT,
82
+ DEFAULT_BCC_W110,
83
+ )
84
+ V69_AVAILABLE = True
85
+ except ImportError:
86
+ warnings.warn(
87
+ "unified_yield_fatigue_v6_9 not found. "
88
+ "Using built-in parameters. "
89
+ "For full functionality, ensure v6.9 is in the same directory."
90
+ )
91
+ V69_AVAILABLE = False
92
+ # Fallback defaults
93
+ v69_tau_over_sigma = None
94
+ v69_sigma_c_over_sigma_t = None
95
+ V69_MATERIALS = {}
96
+ C_CLASS_DEFAULT = 1.415
97
+ DEFAULT_BCC_W110 = 0.0
98
+ V69_T_TWIN = {}
99
+ V69_R_COMP = {}
100
+
101
+ # ==============================================================================
102
+ # Built-in Parameters (Fallback when v6.9 not available)
103
+ # ==============================================================================
104
+
105
+ # τ/σ ratios from δ-theory
106
+ BUILTIN_TAU_SIGMA = {
107
+ # Pure metals
108
+ 'Fe': 0.565, 'Cu': 0.565, 'Al': 0.565, 'Ni': 0.565,
109
+ 'Ti': 0.546, 'Mg': 0.327, 'Zn': 0.480,
110
+ # Crystal structures (default)
111
+ 'BCC': 0.565, 'FCC': 0.565, 'HCP': 0.500,
112
+ }
113
+
114
+ # R_comp (σ_c/σ_t) ratios
115
+ BUILTIN_R_COMP = {
116
+ 'Fe': 1.00, 'Cu': 1.00, 'Al': 1.00, 'Ni': 1.00,
117
+ 'Ti': 1.00, 'Mg': 0.60, 'Zn': 1.20,
118
+ 'BCC': 1.00, 'FCC': 1.00, 'HCP': 0.80,
119
+ }
120
+
121
+ # Element to structure mapping
122
+ ELEMENT_STRUCTURE = {
123
+ 'Fe': 'BCC', 'Cr': 'BCC', 'Mo': 'BCC', 'W': 'BCC', 'V': 'BCC',
124
+ 'Cu': 'FCC', 'Al': 'FCC', 'Ni': 'FCC', 'Ag': 'FCC', 'Au': 'FCC',
125
+ 'Ti': 'HCP', 'Mg': 'HCP', 'Zn': 'HCP', 'Zr': 'HCP',
126
+ }
127
+
128
+
129
+ def get_tau_sigma(element_or_structure: str, T_twin: float = 1.0) -> float:
130
+ """
131
+ Get τ/σ ratio.
132
+
133
+ Uses v6.9b if available for BCC/FCC, built-in interpolation for HCP.
134
+
135
+ Args:
136
+ element_or_structure: Element name ('Fe', 'Cu') or structure ('BCC', 'FCC')
137
+ T_twin: Twinning factor for HCP (0.0=twin-dominated, 1.0=slip-dominated)
138
+
139
+ Returns:
140
+ τ/σ ratio
141
+
142
+ Note:
143
+ For HCP, we always use built-in interpolation because:
144
+ - v6.9's tau_over_sigma multiplies by T_twin directly (→ 0 when T_twin=0)
145
+ - FLC needs T_twin as interpolation parameter between slip/twin modes
146
+ - T_twin=0.0 → Mg-like twin-dominated (τ/σ ≈ 0.327)
147
+ - T_twin=1.0 → slip-dominated (τ/σ ≈ 0.565)
148
+ """
149
+ structure = ELEMENT_STRUCTURE.get(element_or_structure, element_or_structure)
150
+
151
+ # HCP: Always use built-in interpolation (v6.9 T_twin semantics differ)
152
+ if structure == 'HCP':
153
+ tau_slip = 0.565 # Normal slip-dominated
154
+ if element_or_structure == 'Mg':
155
+ tau_twin = 0.327 # Mg twin-dominated (calibrated)
156
+ elif element_or_structure == 'Ti':
157
+ tau_twin = 0.546 # Ti (mostly slip even with twinning)
158
+ elif element_or_structure == 'Zn':
159
+ tau_twin = 0.480 # Zn
160
+ else:
161
+ tau_twin = 0.50 # Generic HCP
162
+
163
+ # T_twin=1.0 → slip-dominated (0.565)
164
+ # T_twin=0.0 → twin-dominated (element-specific)
165
+ return tau_slip * T_twin + tau_twin * (1 - T_twin)
166
+
167
+ # BCC/FCC: Use v6.9 if available
168
+ if V69_AVAILABLE and element_or_structure in V69_MATERIALS:
169
+ mat = V69_MATERIALS[element_or_structure]
170
+ return v69_tau_over_sigma(mat, C_CLASS_DEFAULT, DEFAULT_BCC_W110)
171
+
172
+ # Fallback to built-in
173
+ if element_or_structure in BUILTIN_TAU_SIGMA:
174
+ return BUILTIN_TAU_SIGMA[element_or_structure]
175
+ else:
176
+ return BUILTIN_TAU_SIGMA.get(structure, 0.565)
177
+
178
+
179
+ def get_R_comp(element_or_structure: str, T_twin: float = 1.0) -> float:
180
+ """
181
+ Get R_comp (σ_c/σ_t) ratio.
182
+
183
+ Uses v6.9b if available for BCC/FCC, built-in interpolation for HCP.
184
+
185
+ Args:
186
+ element_or_structure: Element name or structure
187
+ T_twin: Twinning factor for HCP (0.0=twin-dominated, 1.0=slip-dominated)
188
+
189
+ Returns:
190
+ R_comp ratio
191
+
192
+ Note:
193
+ For HCP, T_twin affects tension/compression asymmetry:
194
+ - T_twin=0.0 → strong asymmetry (Mg: R_comp ≈ 0.6)
195
+ - T_twin=1.0 → symmetric (R_comp ≈ 1.0)
196
+ """
197
+ structure = ELEMENT_STRUCTURE.get(element_or_structure, element_or_structure)
198
+
199
+ # HCP: Always use built-in interpolation
200
+ if structure == 'HCP':
201
+ R_symmetric = 1.0 # Slip-dominated (symmetric)
202
+ if element_or_structure == 'Mg':
203
+ R_twin = 0.60 # Mg twin-dominated (strong asymmetry)
204
+ elif element_or_structure == 'Zn':
205
+ R_twin = 1.20 # Zn (compression stronger)
206
+ elif element_or_structure == 'Ti':
207
+ R_twin = 1.00 # Ti (mostly symmetric)
208
+ else:
209
+ R_twin = 0.80 # Generic HCP
210
+
211
+ # T_twin=1.0 → symmetric (1.0)
212
+ # T_twin=0.0 → asymmetric (element-specific)
213
+ return R_symmetric * T_twin + R_twin * (1 - T_twin)
214
+
215
+ # BCC/FCC: Use v6.9 if available
216
+ if V69_AVAILABLE and element_or_structure in V69_MATERIALS:
217
+ mat = V69_MATERIALS[element_or_structure]
218
+ return v69_sigma_c_over_sigma_t(mat)
219
+
220
+ # Fallback to built-in
221
+ if element_or_structure in BUILTIN_R_COMP:
222
+ return BUILTIN_R_COMP[element_or_structure]
223
+ else:
224
+ return BUILTIN_R_COMP.get(structure, 1.0)
225
+
226
+
227
+ def get_v69_material(element: str) -> Optional[object]:
228
+ """Get v6.9b Material object if available."""
229
+ if V69_AVAILABLE and element in V69_MATERIALS:
230
+ return V69_MATERIALS[element]
231
+ return None
232
+
233
+
234
+ # ==============================================================================
235
+ # Constants (Frozen)
236
+ # ==============================================================================
237
+
238
+ K1_LOCALIZATION = 0.75
239
+ K2_LOCALIZATION = 0.48
240
+
241
+ STANDARD_MODES = {
242
+ 'Uniaxial': {'j': 1, 'beta': -0.370, 'w_sigma': 1.246, 'w_tau': 0.319, 'w_c': 0.000},
243
+ 'Deep Draw': {'j': 2, 'beta': -0.306, 'w_sigma': 1.354, 'w_tau': 0.458, 'w_c': 0.000},
244
+ 'Draw-Plane': {'j': 3, 'beta': -0.169, 'w_sigma': 1.552, 'w_tau': 0.723, 'w_c': 0.000},
245
+ 'Plane Strain': {'j': 4, 'beta': 0.000, 'w_sigma': 1.732, 'w_tau': 1.000, 'w_c': 0.000},
246
+ 'Plane-Stretch': {'j': 5, 'beta': 0.133, 'w_sigma': 1.829, 'w_tau': 0.813, 'w_c': 0.133},
247
+ 'Stretch': {'j': 6, 'beta': 0.247, 'w_sigma': 1.889, 'w_tau': 0.670, 'w_c': 0.247},
248
+ 'Equi-biaxial': {'j': 7, 'beta': 0.430, 'w_sigma': 1.949, 'w_tau': 0.469, 'w_c': 0.430},
249
+ }
250
+
251
+ MODE_ORDER = ['Uniaxial', 'Deep Draw', 'Draw-Plane', 'Plane Strain',
252
+ 'Plane-Stretch', 'Stretch', 'Equi-biaxial']
253
+
254
+ R_TH_VIRGIN = {'BCC': 0.65, 'FCC': 0.02, 'HCP': 0.20}
255
+
256
+ # Pre-calculate C_j
257
+ LOCALIZATION_COEFFS = {
258
+ mode: 1 + K1_LOCALIZATION * data['beta'] + K2_LOCALIZATION * data['beta']**2
259
+ for mode, data in STANDARD_MODES.items()
260
+ }
261
+
262
+
263
+ # ==============================================================================
264
+ # FLCMaterial Class
265
+ # ==============================================================================
266
+
267
+ @dataclass
268
+ class FLCMaterial:
269
+ """Material data for δ-FLC prediction."""
270
+ name: str
271
+ tau_sigma: float
272
+ R_comp: float
273
+ V_eff: float
274
+ structure: str = 'FCC'
275
+ sigma_y: float = 0.0
276
+ base_element: str = ''
277
+
278
+ @property
279
+ def r_th(self) -> float:
280
+ return R_TH_VIRGIN.get(self.structure, 0.20)
281
+
282
+ @classmethod
283
+ def from_v69(cls,
284
+ name: str,
285
+ flc0: float,
286
+ base_element: Optional[str] = None,
287
+ structure: Optional[str] = None,
288
+ T_twin: float = 1.0,
289
+ sigma_y: float = 0.0) -> 'FLCMaterial':
290
+ """
291
+ Create FLCMaterial from v6.9 parameters.
292
+
293
+ Args:
294
+ name: Material name
295
+ flc0: Experimental FLC₀ (Plane Strain)
296
+ base_element: Base element ('Fe', 'Cu', 'Al', etc.)
297
+ structure: Crystal structure (auto-detected if base_element given)
298
+ T_twin: Twinning factor for HCP (0.0-1.0)
299
+ sigma_y: Yield stress [MPa]
300
+
301
+ Returns:
302
+ FLCMaterial with calibrated V_eff
303
+
304
+ Example:
305
+ >>> mat = FLCMaterial.from_v69('SPCC', flc0=0.225, base_element='Fe')
306
+ >>> mat = FLCMaterial.from_v69('Mg_AZ31', flc0=0.265, base_element='Mg', T_twin=0.0)
307
+ """
308
+ # Determine structure
309
+ if structure is None:
310
+ if base_element:
311
+ structure = ELEMENT_STRUCTURE.get(base_element, 'FCC')
312
+ else:
313
+ structure = 'FCC'
314
+
315
+ # Get multiaxial parameters
316
+ key = base_element if base_element else structure
317
+ tau_sigma = get_tau_sigma(key, T_twin)
318
+ R_comp = get_R_comp(key, T_twin)
319
+
320
+ # Calibrate V_eff from FLC₀
321
+ V_eff = calibrate_V_eff(flc0, tau_sigma, R_comp)
322
+
323
+ return cls(
324
+ name=name,
325
+ tau_sigma=tau_sigma,
326
+ R_comp=R_comp,
327
+ V_eff=V_eff,
328
+ structure=structure,
329
+ sigma_y=sigma_y,
330
+ base_element=base_element or '',
331
+ )
332
+
333
+
334
+ # ==============================================================================
335
+ # Core Functions
336
+ # ==============================================================================
337
+
338
+ def calc_R_eff(tau_sigma: float, R_comp: float, mode: str) -> float:
339
+ """Calculate effective resistance R_j."""
340
+ m = STANDARD_MODES[mode]
341
+ R_eff = m['w_sigma'] + m['w_tau'] / tau_sigma
342
+ if m['w_c'] > 0:
343
+ R_eff += m['w_c'] / R_comp
344
+ return R_eff
345
+
346
+
347
+ def calc_K_coeff(tau_sigma: float, R_comp: float, mode: str) -> float:
348
+ """Calculate combined coefficient K_j = C_j / R_j."""
349
+ C_j = LOCALIZATION_COEFFS[mode]
350
+ R_j = calc_R_eff(tau_sigma, R_comp, mode)
351
+ return C_j / R_j
352
+
353
+
354
+ def calibrate_V_eff(flc0: float, tau_sigma: float, R_comp: float = 1.0) -> float:
355
+ """Calibrate V_eff from FLC₀ (Plane Strain)."""
356
+ R_ps = calc_R_eff(tau_sigma, R_comp, 'Plane Strain')
357
+ C_ps = LOCALIZATION_COEFFS['Plane Strain'] # = 1.0
358
+ return flc0 * R_ps / C_ps
359
+
360
+
361
+ def predict_flc_mode(material: Union[str, FLCMaterial], mode: str) -> float:
362
+ """Predict FLC for a specific mode."""
363
+ if isinstance(material, str):
364
+ mat = FLC_MATERIALS.get(material)
365
+ if mat is None:
366
+ raise ValueError(f"Material '{material}' not found.")
367
+ else:
368
+ mat = material
369
+
370
+ K_j = calc_K_coeff(mat.tau_sigma, mat.R_comp, mode)
371
+ return mat.V_eff * K_j
372
+
373
+
374
+ # ==============================================================================
375
+ # Built-in Material Database (Optimized)
376
+ # ==============================================================================
377
+
378
+ FLC_MATERIALS: Dict[str, FLCMaterial] = {
379
+ 'Cu': FLCMaterial('Cu', 0.565, 1.00, 1.2235, 'FCC', 69.5, 'Cu'),
380
+ 'Ti': FLCMaterial('Ti', 0.546, 1.00, 1.0391, 'HCP', 270.8, 'Ti'),
381
+ 'SPCC': FLCMaterial('SPCC', 0.565, 1.00, 0.8024, 'BCC', 237.0, 'Fe'),
382
+ 'DP590': FLCMaterial('DP590', 0.565, 1.00, 0.6911, 'BCC', 360.0, 'Fe'),
383
+ 'Al5052': FLCMaterial('Al5052', 0.565, 1.00, 0.6189, 'FCC', 85.0, 'Al'),
384
+ 'SUS304': FLCMaterial('SUS304', 0.565, 1.00, 1.4234, 'FCC', 275.0, 'Ni'),
385
+ 'Mg_AZ31': FLCMaterial('Mg_AZ31', 0.327, 0.60, 1.1798, 'HCP', 136.0, 'Mg'),
386
+ }
387
+
388
+
389
+ # ==============================================================================
390
+ # FLC Predictor Class
391
+ # ==============================================================================
392
+
393
+ class FLCPredictor:
394
+ """
395
+ δ-Theory FLC Predictor (v8.1) - Integrated with v6.9
396
+
397
+ Example:
398
+ >>> flc = FLCPredictor()
399
+
400
+ # Use built-in material
401
+ >>> flc.predict('Cu', 'Plane Strain')
402
+ 0.346
403
+
404
+ # Add from v6.9 parameters
405
+ >>> flc.add_from_v69('MySteel', flc0=0.28, base_element='Fe')
406
+ >>> flc.predict('MySteel', 'Uniaxial')
407
+ """
408
+
409
+ def __init__(self):
410
+ self._materials = FLC_MATERIALS.copy()
411
+
412
+ @property
413
+ def v69_available(self) -> bool:
414
+ """Check if v6.9 is available."""
415
+ return V69_AVAILABLE
416
+
417
+ def add_material(self, mat: FLCMaterial) -> None:
418
+ """Add custom material."""
419
+ self._materials[mat.name] = mat
420
+
421
+ def add_from_v69(self,
422
+ name: str,
423
+ flc0: float,
424
+ base_element: Optional[str] = None,
425
+ structure: Optional[str] = None,
426
+ T_twin: float = 1.0,
427
+ sigma_y: float = 0.0) -> FLCMaterial:
428
+ """
429
+ Add material using v6.9 parameters.
430
+
431
+ Args:
432
+ name: Material name
433
+ flc0: Experimental FLC₀
434
+ base_element: Base element ('Fe', 'Cu', 'Al', 'Ti', 'Mg', etc.)
435
+ structure: Crystal structure (auto-detected from base_element)
436
+ T_twin: Twinning factor for HCP (0.0=twin-dominated, 1.0=slip-dominated)
437
+ sigma_y: Yield stress [MPa]
438
+
439
+ Returns:
440
+ Created FLCMaterial
441
+
442
+ Examples:
443
+ # BCC steel
444
+ >>> flc.add_from_v69('SPCC', flc0=0.225, base_element='Fe')
445
+
446
+ # FCC aluminum alloy
447
+ >>> flc.add_from_v69('A5052', flc0=0.165, base_element='Al')
448
+
449
+ # HCP magnesium (twin-dominated)
450
+ >>> flc.add_from_v69('AZ31', flc0=0.265, base_element='Mg', T_twin=0.0)
451
+
452
+ # HCP titanium (slip-dominated)
453
+ >>> flc.add_from_v69('Ti64', flc0=0.30, base_element='Ti', T_twin=1.0)
454
+ """
455
+ mat = FLCMaterial.from_v69(
456
+ name=name,
457
+ flc0=flc0,
458
+ base_element=base_element,
459
+ structure=structure,
460
+ T_twin=T_twin,
461
+ sigma_y=sigma_y,
462
+ )
463
+ self._materials[name] = mat
464
+ return mat
465
+
466
+ def add_from_flc0(self,
467
+ name: str,
468
+ flc0: float,
469
+ tau_sigma: float,
470
+ R_comp: float = 1.0,
471
+ structure: str = 'FCC',
472
+ sigma_y: float = 0.0) -> FLCMaterial:
473
+ """Add material with explicit τ/σ and R_comp."""
474
+ V_eff = calibrate_V_eff(flc0, tau_sigma, R_comp)
475
+ mat = FLCMaterial(
476
+ name=name,
477
+ tau_sigma=tau_sigma,
478
+ R_comp=R_comp,
479
+ V_eff=V_eff,
480
+ structure=structure,
481
+ sigma_y=sigma_y,
482
+ )
483
+ self._materials[name] = mat
484
+ return mat
485
+
486
+ def add_from_v69_material(self,
487
+ v69_mat,
488
+ flc0: float,
489
+ name: Optional[str] = None,
490
+ T_twin: float = 1.0) -> FLCMaterial:
491
+ """
492
+ Add material from v6.9 Material object.
493
+
494
+ Args:
495
+ v69_mat: v6.9 Material object (has .structure, .sigma_y, etc.)
496
+ flc0: Experimental FLC₀
497
+ name: Material name (default: v69_mat.name if available)
498
+ T_twin: Twinning factor for HCP
499
+
500
+ Returns:
501
+ Created FLCMaterial
502
+
503
+ Example:
504
+ >>> from unified_yield_fatigue_v6_9 import MATERIALS
505
+ >>> flc.add_from_v69_material(MATERIALS['Fe'], flc0=0.225, name='SPCC')
506
+ """
507
+ # Extract info from v6.9 Material
508
+ mat_name = name or getattr(v69_mat, 'name', 'Unknown')
509
+ structure = getattr(v69_mat, 'structure', 'FCC')
510
+ sigma_y = getattr(v69_mat, 'sigma_y', 0.0)
511
+
512
+ # Get τ/σ and R_comp from v6.9 functions or built-in
513
+ tau_sigma = get_tau_sigma(structure, T_twin)
514
+ R_comp = get_R_comp(structure, T_twin)
515
+
516
+ # Calibrate V_eff
517
+ V_eff = calibrate_V_eff(flc0, tau_sigma, R_comp)
518
+
519
+ mat = FLCMaterial(
520
+ name=mat_name,
521
+ tau_sigma=tau_sigma,
522
+ R_comp=R_comp,
523
+ V_eff=V_eff,
524
+ structure=structure,
525
+ sigma_y=sigma_y,
526
+ base_element=getattr(v69_mat, 'element', ''),
527
+ )
528
+ self._materials[mat_name] = mat
529
+ return mat
530
+
531
+ def get_material(self, name: str) -> FLCMaterial:
532
+ """Get material by name."""
533
+ if name not in self._materials:
534
+ available = list(self._materials.keys())
535
+ raise ValueError(f"Material '{name}' not found. Available: {available}")
536
+ return self._materials[name]
537
+
538
+ def list_materials(self) -> List[str]:
539
+ """List available materials."""
540
+ return list(self._materials.keys())
541
+
542
+ def predict(self,
543
+ material: Union[str, FLCMaterial],
544
+ mode: str,
545
+ include_breakdown: bool = False) -> Union[float, Tuple[float, Dict]]:
546
+ """Predict FLC for a mode."""
547
+ if isinstance(material, str):
548
+ mat = self.get_material(material)
549
+ else:
550
+ mat = material
551
+
552
+ m = STANDARD_MODES[mode]
553
+ C_j = LOCALIZATION_COEFFS[mode]
554
+ R_j = calc_R_eff(mat.tau_sigma, mat.R_comp, mode)
555
+ K_j = C_j / R_j
556
+ eps1 = mat.V_eff * K_j
557
+
558
+ if include_breakdown:
559
+ return eps1, {
560
+ 'mode': mode, 'j': m['j'], 'beta': m['beta'],
561
+ 'C_j': C_j, 'R_j': R_j, 'K_j': K_j,
562
+ 'V_eff': mat.V_eff, 'tau_sigma': mat.tau_sigma, 'R_comp': mat.R_comp,
563
+ }
564
+ return eps1
565
+
566
+ def predict_all_modes(self, material: Union[str, FLCMaterial]) -> Dict[str, float]:
567
+ """Predict FLC for all 7 modes."""
568
+ return {mode: self.predict(material, mode) for mode in MODE_ORDER}
569
+
570
+ def predict_curve(self, material: Union[str, FLCMaterial]) -> Tuple[np.ndarray, np.ndarray]:
571
+ """Predict FLC curve."""
572
+ predictions = self.predict_all_modes(material)
573
+ betas = np.array([STANDARD_MODES[m]['beta'] for m in MODE_ORDER])
574
+ eps1s = np.array([predictions[m] for m in MODE_ORDER])
575
+ return betas, eps1s
576
+
577
+ def flc0(self, material: Union[str, FLCMaterial]) -> float:
578
+ """Get FLC₀ (Plane Strain)."""
579
+ return self.predict(material, 'Plane Strain')
580
+
581
+ def summary(self, material: Union[str, FLCMaterial]) -> str:
582
+ """Generate summary report."""
583
+ if isinstance(material, str):
584
+ mat = self.get_material(material)
585
+ else:
586
+ mat = material
587
+
588
+ lines = [
589
+ f"δ-FLC Summary: {mat.name}",
590
+ "=" * 55,
591
+ f"Structure: {mat.structure}",
592
+ f"Base element: {mat.base_element}" if mat.base_element else "",
593
+ f"σ_y: {mat.sigma_y:.1f} MPa" if mat.sigma_y > 0 else "",
594
+ f"τ/σ: {mat.tau_sigma:.4f}",
595
+ f"R_comp: {mat.R_comp:.2f}",
596
+ f"|V|_eff: {mat.V_eff:.4f}",
597
+ "",
598
+ "FLC Predictions:",
599
+ "-" * 55,
600
+ f"{'Mode':<15} {'β':>7} {'C_j':>7} {'R_j':>7} {'K_j':>7} {'ε₁':>8}",
601
+ "-" * 55,
602
+ ]
603
+
604
+ for mode in MODE_ORDER:
605
+ eps1, bd = self.predict(mat, mode, include_breakdown=True)
606
+ lines.append(
607
+ f"{mode:<15} {bd['beta']:>7.3f} {bd['C_j']:>7.4f} "
608
+ f"{bd['R_j']:>7.3f} {bd['K_j']:>7.4f} {eps1:>8.4f}"
609
+ )
610
+
611
+ lines.extend(["-" * 55, f"FLC₀: {self.flc0(mat):.4f}"])
612
+ return '\n'.join(filter(None, lines))
613
+
614
+
615
+ # ==============================================================================
616
+ # Convenience Functions
617
+ # ==============================================================================
618
+
619
+ def predict_flc(material: str, mode: str = 'Plane Strain') -> float:
620
+ """Quick FLC prediction."""
621
+ return FLCPredictor().predict(material, mode)
622
+
623
+
624
+ def predict_flc_curve(material: str) -> Tuple[np.ndarray, np.ndarray]:
625
+ """Quick FLC curve."""
626
+ return FLCPredictor().predict_curve(material)
627
+
628
+
629
+ def get_flc0(material: str) -> float:
630
+ """Get FLC₀."""
631
+ return FLCPredictor().flc0(material)
632
+
633
+
634
+ def create_material_from_v69(name: str, flc0: float, base_element: str,
635
+ T_twin: float = 1.0) -> FLCMaterial:
636
+ """Create material using v6.9 parameters."""
637
+ return FLCMaterial.from_v69(name, flc0, base_element, T_twin=T_twin)
638
+
639
+
640
+ # ==============================================================================
641
+ # Demo
642
+ # ==============================================================================
643
+
644
+ def demo():
645
+ """Demonstration of v8.1 capabilities."""
646
+
647
+ print("="*70)
648
+ print("δ-Theory FLC v8.1: Integrated with v6.9")
649
+ print("="*70)
650
+ print(f"\nv6.9 available: {V69_AVAILABLE}")
651
+
652
+ flc = FLCPredictor()
653
+
654
+ # 1. Built-in materials
655
+ print("\n[1] Built-in Materials")
656
+ print("-"*70)
657
+ print(f"Available: {flc.list_materials()}")
658
+
659
+ # 2. Add from v6.9 parameters
660
+ print("\n[2] Add Materials from v6.9 Parameters")
661
+ print("-"*70)
662
+
663
+ # BCC steel example
664
+ mat1 = flc.add_from_v69('NewSteel', flc0=0.28, base_element='Fe')
665
+ print(f"Added: {mat1.name}")
666
+ print(f" τ/σ = {mat1.tau_sigma:.4f} (from Fe/BCC)")
667
+ print(f" R_comp = {mat1.R_comp:.2f}")
668
+ print(f" V_eff = {mat1.V_eff:.4f} (calibrated from FLC₀=0.28)")
669
+
670
+ # HCP Mg example (twin-dominated)
671
+ mat2 = flc.add_from_v69('MgAlloy', flc0=0.25, base_element='Mg', T_twin=0.0)
672
+ print(f"\nAdded: {mat2.name}")
673
+ print(f" τ/σ = {mat2.tau_sigma:.4f} (from Mg/HCP, T_twin=0)")
674
+ print(f" R_comp = {mat2.R_comp:.2f}")
675
+ print(f" V_eff = {mat2.V_eff:.4f}")
676
+
677
+ # 3. Simulate v6.9 Material object integration
678
+ print("\n[3] v6.9 Material Object Integration (Simulated)")
679
+ print("-"*70)
680
+
681
+ # Create a mock v6.9 Material object
682
+ class MockV69Material:
683
+ def __init__(self, name, structure, sigma_y=0.0, element=''):
684
+ self.name = name
685
+ self.structure = structure
686
+ self.sigma_y = sigma_y
687
+ self.element = element
688
+
689
+ # Simulate v6.9 Material
690
+ v69_fe = MockV69Material('Fe_BCC', 'BCC', sigma_y=250.0, element='Fe')
691
+ mat3 = flc.add_from_v69_material(v69_fe, flc0=0.225, name='SPCC_v69')
692
+ print(f"Added from v6.9 Material: {mat3.name}")
693
+ print(f" Structure: {mat3.structure}")
694
+ print(f" σ_y: {mat3.sigma_y:.1f} MPa")
695
+ print(f" τ/σ = {mat3.tau_sigma:.4f}")
696
+ print(f" R_comp = {mat3.R_comp:.2f}")
697
+ print(f" V_eff = {mat3.V_eff:.4f}")
698
+
699
+ # 4. Predictions
700
+ print("\n[4] FLC Predictions")
701
+ print("-"*70)
702
+
703
+ for mat_name in ['NewSteel', 'MgAlloy', 'SPCC_v69']:
704
+ print(f"\n{mat_name}:")
705
+ for mode in ['Uniaxial', 'Plane Strain', 'Equi-biaxial']:
706
+ eps1 = flc.predict(mat_name, mode)
707
+ print(f" {mode}: ε₁ = {eps1:.4f}")
708
+
709
+ # 5. Full summary
710
+ print("\n[5] Material Summary: SPCC_v69")
711
+ print("-"*70)
712
+ print(flc.summary('SPCC_v69'))
713
+
714
+ # 6. τ/σ comparison (pure metal native values)
715
+ print("\n[6] τ/σ Values by Base Element (Pure Metal Native)")
716
+ print("-"*70)
717
+ print(f"{'Element':<10} {'Structure':<10} {'τ/σ':>10} {'R_comp':>10} {'Note':>15}")
718
+ print("-"*60)
719
+
720
+ # Pure metals: BCC/FCC use T_twin=1.0, HCP uses native characteristic
721
+ elements_info = [
722
+ ('Fe', 'BCC', 1.0, ''),
723
+ ('Cu', 'FCC', 1.0, ''),
724
+ ('Al', 'FCC', 1.0, ''),
725
+ ('Ni', 'FCC', 1.0, ''),
726
+ ('Ti', 'HCP', 0.0, 'HCP, c/a effect'), # Ti native
727
+ ('Mg', 'HCP', 0.0, 'twin-dominant'), # Mg native
728
+ ]
729
+
730
+ for elem, struct, T_twin, note in elements_info:
731
+ tau_s = get_tau_sigma(elem, T_twin)
732
+ r_c = get_R_comp(elem, T_twin)
733
+ print(f"{elem:<10} {struct:<10} {tau_s:>10.4f} {r_c:>10.2f} {note:>15}")
734
+
735
+ # 7. HCP T_twin interpolation
736
+ print("\n[7] HCP τ/σ vs T_twin (Mg)")
737
+ print("-"*70)
738
+ for T_twin in [0.0, 0.25, 0.5, 0.75, 1.0]:
739
+ tau_s = get_tau_sigma('Mg', T_twin)
740
+ r_c = get_R_comp('Mg', T_twin)
741
+ print(f" T_twin = {T_twin:.2f} → τ/σ = {tau_s:.4f}, R_comp = {r_c:.2f}")
742
+
743
+ # 8. Quick usage example
744
+ print("\n[8] Quick Usage Example")
745
+ print("-"*70)
746
+ print("""
747
+ # Basic usage:
748
+ from unified_flc_v8_1 import FLCPredictor, predict_flc
749
+
750
+ # Use built-in material
751
+ eps1 = predict_flc('Cu', 'Plane Strain') # 0.346
752
+
753
+ # Add new material from element
754
+ flc = FLCPredictor()
755
+ flc.add_from_v69('MySteel', flc0=0.28, base_element='Fe')
756
+ curve = flc.predict_curve('MySteel')
757
+
758
+ # Add from explicit parameters
759
+ flc.add_from_flc0('Custom', flc0=0.30, tau_sigma=0.565, R_comp=1.0)
760
+
761
+ # If v6.9 is available:
762
+ # from unified_yield_fatigue_v6_9 import MATERIALS
763
+ # flc.add_from_v69_material(MATERIALS['Fe'], flc0=0.225, name='SPCC')
764
+ """)
765
+
766
+
767
+ if __name__ == '__main__':
768
+ demo()