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