delta-theory 6.9.0__tar.gz → 6.10.1__tar.gz
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.
- {delta_theory-6.9.0 → delta_theory-6.10.1}/PKG-INFO +2 -2
- {delta_theory-6.9.0 → delta_theory-6.10.1}/core/__init__.py +26 -2
- {delta_theory-6.9.0/validation → delta_theory-6.10.1/core}/fatigue_redis_api.py +158 -12
- {delta_theory-6.9.0 → delta_theory-6.10.1}/core/unified_yield_fatigue_v6_9.py +195 -80
- {delta_theory-6.9.0 → delta_theory-6.10.1}/delta_theory.egg-info/PKG-INFO +2 -2
- {delta_theory-6.9.0 → delta_theory-6.10.1}/delta_theory.egg-info/SOURCES.txt +2 -3
- delta_theory-6.10.1/delta_theory.egg-info/top_level.txt +2 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/pyproject.toml +2 -2
- delta_theory-6.9.0/delta_theory.egg-info/top_level.txt +0 -3
- delta_theory-6.9.0/validation/__init__.py +0 -10
- {delta_theory-6.9.0 → delta_theory-6.10.1}/LICENSE +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/README.md +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/apps/__init__.py +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/apps/delta_fatigue_app.py +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/core/Universal_Lindemann.py +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/core/dbt_unified.py +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/core/materials.py +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/delta_theory.egg-info/dependency_links.txt +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/delta_theory.egg-info/entry_points.txt +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/delta_theory.egg-info/requires.txt +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/setup.cfg +0 -0
- {delta_theory-6.9.0 → delta_theory-6.10.1}/tests/test_core.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: delta-theory
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.10.1
|
|
4
4
|
Summary: Unified materials strength and fatigue prediction based on geometric first principles
|
|
5
5
|
Author: Tamaki
|
|
6
|
-
Author-email: Masamichi Iizumi <
|
|
6
|
+
Author-email: Masamichi Iizumi <m.iizumi@miosync.email>
|
|
7
7
|
Maintainer-email: Masamichi Iizumi <masamichi@miosync.com>
|
|
8
8
|
License: MIT
|
|
9
9
|
Project-URL: Homepage, https://github.com/miosync/delta-theory
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""
|
|
2
2
|
δ-Theory Core Library
|
|
3
3
|
=====================
|
|
4
|
-
|
|
5
4
|
Unified yield stress and fatigue life prediction based on geometric first principles.
|
|
6
5
|
|
|
7
6
|
Modules:
|
|
8
7
|
- unified_yield_fatigue_v6_9: Main yield + fatigue model (v6.9b)
|
|
9
8
|
- dbt_unified: Ductile-Brittle Transition Temperature prediction
|
|
10
9
|
- materials: Material database
|
|
10
|
+
- fatigue_redis_api: FatigueData-AM2022 Redis API
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from .unified_yield_fatigue_v6_9 import (
|
|
@@ -31,5 +31,29 @@ from .dbt_unified import (
|
|
|
31
31
|
|
|
32
32
|
from .materials import MaterialGPU
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
from .fatigue_redis_api import FatigueDB # ← 追加!
|
|
35
|
+
|
|
36
|
+
__version__ = "6.10.1" # ← バージョンも上げる!
|
|
35
37
|
__author__ = "Masamichi Iizumi & Tamaki"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# v6.9
|
|
41
|
+
"Material",
|
|
42
|
+
"MATERIALS",
|
|
43
|
+
"calc_sigma_y",
|
|
44
|
+
"fatigue_life_const_amp",
|
|
45
|
+
"generate_sn_curve",
|
|
46
|
+
"yield_by_mode",
|
|
47
|
+
"FATIGUE_CLASS_PRESET",
|
|
48
|
+
# DBT
|
|
49
|
+
"DBTUnified",
|
|
50
|
+
"DBTCore",
|
|
51
|
+
"GrainSizeView",
|
|
52
|
+
"TemperatureView",
|
|
53
|
+
"SegregationView",
|
|
54
|
+
"MATERIAL_FE",
|
|
55
|
+
# Materials
|
|
56
|
+
"MaterialGPU",
|
|
57
|
+
# FatigueDB
|
|
58
|
+
"FatigueDB", # ← 追加!
|
|
59
|
+
]
|
|
@@ -32,6 +32,7 @@ Date: 2026-02-02
|
|
|
32
32
|
import json
|
|
33
33
|
from typing import Dict, List, Optional, Any
|
|
34
34
|
from dataclasses import dataclass
|
|
35
|
+
import os
|
|
35
36
|
|
|
36
37
|
try:
|
|
37
38
|
from upstash_redis import Redis
|
|
@@ -42,10 +43,17 @@ except ImportError:
|
|
|
42
43
|
# =============================================================================
|
|
43
44
|
# Configuration
|
|
44
45
|
# =============================================================================
|
|
45
|
-
UPSTASH_URL =
|
|
46
|
-
UPSTASH_TOKEN = "
|
|
47
|
-
|
|
46
|
+
UPSTASH_URL = os.environ.get("UPSTASH_URL")
|
|
47
|
+
UPSTASH_TOKEN = os.environ.get("UPSTASH_TOKEN")
|
|
48
48
|
|
|
49
|
+
class FatigueDB:
|
|
50
|
+
def __init__(self, url: str = None, token: str = None):
|
|
51
|
+
_url = url or UPSTASH_URL
|
|
52
|
+
_token = token or UPSTASH_TOKEN
|
|
53
|
+
if not _url or not _token:
|
|
54
|
+
raise ValueError("UPSTASH_URL and UPSTASH_TOKEN required! Set env vars or pass directly.")
|
|
55
|
+
self.redis = Redis(url=_url, token=_token)
|
|
56
|
+
|
|
49
57
|
# =============================================================================
|
|
50
58
|
# Data Classes
|
|
51
59
|
# =============================================================================
|
|
@@ -320,15 +328,153 @@ Updated: {meta['updated']}
|
|
|
320
328
|
# =============================================================================
|
|
321
329
|
# CLI
|
|
322
330
|
# =============================================================================
|
|
331
|
+
def build_cli():
|
|
332
|
+
import argparse
|
|
333
|
+
|
|
334
|
+
p = argparse.ArgumentParser(
|
|
335
|
+
description='FatigueData-AM2022 Redis API - δ理論検証用'
|
|
336
|
+
)
|
|
337
|
+
sub = p.add_subparsers(dest='cmd', required=True)
|
|
338
|
+
|
|
339
|
+
# list
|
|
340
|
+
sp_list = sub.add_parser('list', help='材料一覧')
|
|
341
|
+
sp_list.add_argument('--top', type=int, default=20)
|
|
342
|
+
sp_list.add_argument('--sort', choices=['sn_count', 'name'], default='sn_count')
|
|
343
|
+
|
|
344
|
+
# search
|
|
345
|
+
sp_search = sub.add_parser('search', help='材料検索')
|
|
346
|
+
sp_search.add_argument('query', help='検索クエリ (例: Ti, 316, Al)')
|
|
347
|
+
|
|
348
|
+
# info
|
|
349
|
+
sp_info = sub.add_parser('info', help='材料詳細')
|
|
350
|
+
sp_info.add_argument('material', help='材料名')
|
|
351
|
+
|
|
352
|
+
# get-sn
|
|
353
|
+
sp_sn = sub.add_parser('get-sn', help='S-Nデータ取得')
|
|
354
|
+
sp_sn.add_argument('material', help='材料名')
|
|
355
|
+
sp_sn.add_argument('--R', type=float, default=None, help='応力比フィルタ')
|
|
356
|
+
sp_sn.add_argument('--with-sigma-y', action='store_true', help='σ_yありのみ')
|
|
357
|
+
sp_sn.add_argument('--output', '-o', help='CSV出力ファイル')
|
|
358
|
+
sp_sn.add_argument('--limit', type=int, default=20, help='表示件数')
|
|
359
|
+
|
|
360
|
+
# delta
|
|
361
|
+
sp_delta = sub.add_parser('delta', help='δ理論検証用データ (r計算済み)')
|
|
362
|
+
sp_delta.add_argument('material', help='材料名')
|
|
363
|
+
sp_delta.add_argument('--R', type=float, default=-1.0)
|
|
364
|
+
sp_delta.add_argument('--output', '-o', help='CSV出力ファイル')
|
|
365
|
+
|
|
366
|
+
# summary
|
|
367
|
+
sub.add_parser('summary', help='DB全体サマリー')
|
|
368
|
+
|
|
369
|
+
return p
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def cmd_list(db, args):
|
|
373
|
+
print(f"\n📦 材料一覧 (top {args.top}, sort={args.sort}):")
|
|
374
|
+
print("-" * 70)
|
|
375
|
+
print(f"{'Material':<25} {'S-N':>8} {'ε-N':>8} {'σ_y range':>20}")
|
|
376
|
+
print("-" * 70)
|
|
377
|
+
for mat in db.list_materials(sort_by=args.sort)[:args.top]:
|
|
378
|
+
sy_min = mat.get('sigma_y_min', '-')
|
|
379
|
+
sy_max = mat.get('sigma_y_max', '-')
|
|
380
|
+
sy_range = f"{sy_min}-{sy_max}" if sy_min != '-' else '-'
|
|
381
|
+
print(f"{mat['name']:<25} {mat['sn_count']:>8} {mat['en_count']:>8} {sy_range:>20}")
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def cmd_search(db, args):
|
|
385
|
+
results = db.search_materials(args.query)
|
|
386
|
+
print(f"\n🔍 検索: '{args.query}' → {len(results)}件")
|
|
387
|
+
for name in results:
|
|
388
|
+
info = db.get_material_info(name)
|
|
389
|
+
print(f" {name}: S-N={info['sn_count']}, σ_y={info.get('sigma_y_min')}-{info.get('sigma_y_max')} MPa")
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def cmd_info(db, args):
|
|
393
|
+
info = db.get_material_info(args.material)
|
|
394
|
+
if not info:
|
|
395
|
+
print(f"❌ Material '{args.material}' not found")
|
|
396
|
+
return
|
|
397
|
+
|
|
398
|
+
print(f"\n📋 {args.material}")
|
|
399
|
+
print("=" * 50)
|
|
400
|
+
print(f" S-N points: {info['sn_count']}")
|
|
401
|
+
print(f" ε-N points: {info['en_count']}")
|
|
402
|
+
print(f" da/dN points: {info['dadn_count']}")
|
|
403
|
+
print(f" σ_y range: {info.get('sigma_y_min')} - {info.get('sigma_y_max')} MPa")
|
|
404
|
+
|
|
405
|
+
# R値の分布
|
|
406
|
+
sn_data = db.get_sn(args.material)
|
|
407
|
+
R_values = set(d.get('R') for d in sn_data if d.get('R') is not None)
|
|
408
|
+
print(f" R values: {sorted(R_values)}")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def cmd_get_sn(db, args):
|
|
412
|
+
data = db.get_sn(args.material, R=args.R, with_sigma_y=args.with_sigma_y)
|
|
413
|
+
|
|
414
|
+
print(f"\n📊 {args.material} S-N data")
|
|
415
|
+
if args.R is not None:
|
|
416
|
+
print(f" R = {args.R}")
|
|
417
|
+
if args.with_sigma_y:
|
|
418
|
+
print(f" (σ_y required)")
|
|
419
|
+
print(f" Total: {len(data)} points")
|
|
420
|
+
print("-" * 70)
|
|
421
|
+
|
|
422
|
+
if args.output:
|
|
423
|
+
import csv
|
|
424
|
+
with open(args.output, 'w', newline='') as f:
|
|
425
|
+
w = csv.DictWriter(f, fieldnames=['N', 'S', 'R', 'sigma_y', 'sigma_uts', 'runout', 'doi'])
|
|
426
|
+
w.writeheader()
|
|
427
|
+
w.writerows(data)
|
|
428
|
+
print(f"✅ Saved to {args.output}")
|
|
429
|
+
else:
|
|
430
|
+
print(f"{'N':>12} {'S [MPa]':>10} {'R':>6} {'σ_y':>10} {'runout':>8}")
|
|
431
|
+
print("-" * 50)
|
|
432
|
+
for d in data[:args.limit]:
|
|
433
|
+
print(f"{d['N']:>12.0f} {d['S']:>10.1f} {d.get('R', '-'):>6} {d.get('sigma_y', '-'):>10} {d.get('runout', 0):>8}")
|
|
434
|
+
if len(data) > args.limit:
|
|
435
|
+
print(f" ... and {len(data) - args.limit} more (use --output for full data)")
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def cmd_delta(db, args):
|
|
439
|
+
data = db.get_sn_for_delta(args.material, R=args.R)
|
|
440
|
+
|
|
441
|
+
print(f"\n🔬 {args.material} δ-theory data (R={args.R})")
|
|
442
|
+
print(f" Total: {len(data)} points with σ_y")
|
|
443
|
+
if data:
|
|
444
|
+
print(f" r range: {min(d['r'] for d in data):.4f} - {max(d['r'] for d in data):.4f}")
|
|
445
|
+
print("-" * 70)
|
|
446
|
+
|
|
447
|
+
if args.output:
|
|
448
|
+
import csv
|
|
449
|
+
with open(args.output, 'w', newline='') as f:
|
|
450
|
+
w = csv.DictWriter(f, fieldnames=['N', 'S', 'sigma_y', 'r', 'runout', 'doi'])
|
|
451
|
+
w.writeheader()
|
|
452
|
+
w.writerows(data)
|
|
453
|
+
print(f"✅ Saved to {args.output}")
|
|
454
|
+
else:
|
|
455
|
+
print(f"{'N':>12} {'S [MPa]':>10} {'σ_y [MPa]':>12} {'r':>10} {'runout':>8}")
|
|
456
|
+
print("-" * 60)
|
|
457
|
+
for d in data[:20]:
|
|
458
|
+
print(f"{d['N']:>12.0f} {d['S']:>10.1f} {d['sigma_y']:>12.1f} {d['r']:>10.4f} {d.get('runout', 0):>8}")
|
|
459
|
+
if len(data) > 20:
|
|
460
|
+
print(f" ... and {len(data) - 20} more")
|
|
461
|
+
|
|
462
|
+
|
|
323
463
|
if __name__ == '__main__':
|
|
324
|
-
|
|
325
|
-
|
|
464
|
+
parser = build_cli()
|
|
465
|
+
args = parser.parse_args()
|
|
326
466
|
|
|
327
|
-
|
|
328
|
-
for mat in db.list_materials()[:5]:
|
|
329
|
-
print(f" {mat['name']}: {mat['sn_count']} S-N, σ_y={mat.get('sigma_y_min')}-{mat.get('sigma_y_max')} MPa")
|
|
467
|
+
db = FatigueDB()
|
|
330
468
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
469
|
+
if args.cmd == 'summary':
|
|
470
|
+
print(db.summary())
|
|
471
|
+
elif args.cmd == 'list':
|
|
472
|
+
cmd_list(db, args)
|
|
473
|
+
elif args.cmd == 'search':
|
|
474
|
+
cmd_search(db, args)
|
|
475
|
+
elif args.cmd == 'info':
|
|
476
|
+
cmd_info(db, args)
|
|
477
|
+
elif args.cmd == 'get-sn':
|
|
478
|
+
cmd_get_sn(db, args)
|
|
479
|
+
elif args.cmd == 'delta':
|
|
480
|
+
cmd_delta(db, args)
|
|
@@ -25,6 +25,24 @@ from typing import Dict, Literal, Optional, Tuple
|
|
|
25
25
|
|
|
26
26
|
import numpy as np
|
|
27
27
|
|
|
28
|
+
# ==============================================================================
|
|
29
|
+
# Optional import: DBT/DBTT unified model (separate module)
|
|
30
|
+
# ------------------------------------------------------------------------------
|
|
31
|
+
# Place dbt_unified.py in the same folder as this file.
|
|
32
|
+
# Then you can do:
|
|
33
|
+
# from unified_yield_fatigue_v6_9b_tau_classes import DBTUnified
|
|
34
|
+
# model = DBTUnified()
|
|
35
|
+
#
|
|
36
|
+
# The import is optional so that this module remains usable even if dbt_unified.py
|
|
37
|
+
# is not present in your runtime environment.
|
|
38
|
+
try:
|
|
39
|
+
from dbt_unified import DBTUnified, DBTCore, Material as DBTMaterial, MATERIAL_FE
|
|
40
|
+
except Exception:
|
|
41
|
+
DBTUnified = None
|
|
42
|
+
DBTCore = None
|
|
43
|
+
DBTMaterial = None
|
|
44
|
+
MATERIAL_FE = None
|
|
45
|
+
|
|
28
46
|
# ==============================================================================
|
|
29
47
|
# Physical constants
|
|
30
48
|
# ==============================================================================
|
|
@@ -484,40 +502,74 @@ def generate_sn_curve(
|
|
|
484
502
|
Ns.append(out['N_fail'])
|
|
485
503
|
return np.array(Ns, dtype=float)
|
|
486
504
|
|
|
505
|
+
# ==============================================================================
|
|
506
|
+
# Alloy validation helper (for FatigueDB integration)
|
|
507
|
+
# ==============================================================================
|
|
508
|
+
|
|
509
|
+
def get_minimal_material(structure: Literal['BCC', 'FCC', 'HCP']) -> Material:
|
|
510
|
+
"""結晶構造から最小限のMaterialを取得(合金検証用ヘルパー)
|
|
511
|
+
|
|
512
|
+
FatigueDB等の実験データ検証時、σ_yは実測値を使うため
|
|
513
|
+
structure (r_th, n_cl) のみが必要なケース用
|
|
514
|
+
"""
|
|
515
|
+
base = {'BCC': MATERIALS['Fe'], 'FCC': MATERIALS['Cu'], 'HCP': MATERIALS['Ti']}
|
|
516
|
+
return base[structure]
|
|
517
|
+
|
|
518
|
+
|
|
487
519
|
# ==============================================================================
|
|
488
520
|
# CLI
|
|
489
521
|
# ==============================================================================
|
|
490
522
|
|
|
491
523
|
def cmd_point(args: argparse.Namespace) -> None:
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
524
|
+
# material 取得(metal or structure_only)
|
|
525
|
+
if args.metal is None and args.structure_only is None:
|
|
526
|
+
raise SystemExit("Error: --metal or --structure_only required")
|
|
527
|
+
|
|
528
|
+
if args.structure_only:
|
|
529
|
+
mat = get_minimal_material(args.structure_only)
|
|
530
|
+
else:
|
|
531
|
+
mat0 = MATERIALS[args.metal]
|
|
532
|
+
mat = replace(
|
|
533
|
+
mat0,
|
|
534
|
+
A_texture=float(args.A_texture),
|
|
535
|
+
T_twin=(float(args.T_twin) if args.T_twin is not None else mat0.T_twin),
|
|
536
|
+
R_comp=(float(args.R_comp) if args.R_comp is not None else mat0.R_comp),
|
|
537
|
+
c_a=float(args.c_a) if args.c_a is not None else mat0.c_a,
|
|
538
|
+
)
|
|
500
539
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
540
|
+
# σ_y 計算 or override
|
|
541
|
+
if args.sigma_y_override is not None:
|
|
542
|
+
sigma_y = args.sigma_y_override
|
|
543
|
+
y = {
|
|
544
|
+
'sigma_y': sigma_y,
|
|
545
|
+
'sigma_base': sigma_y,
|
|
546
|
+
'delta_ss': 0.0,
|
|
547
|
+
'delta_wh': 0.0,
|
|
548
|
+
'delta_ppt': 0.0,
|
|
549
|
+
'ppt_mechanism': 'N/A (override)',
|
|
550
|
+
}
|
|
551
|
+
else:
|
|
552
|
+
y = calc_sigma_y(
|
|
553
|
+
mat,
|
|
554
|
+
T_K=args.T_K,
|
|
555
|
+
c_wt_percent=args.c_wt,
|
|
556
|
+
k_ss=args.k_ss,
|
|
557
|
+
solute_type=args.solute_type,
|
|
558
|
+
eps=args.eps,
|
|
559
|
+
rho_0=args.rho_0,
|
|
560
|
+
r_ppt_nm=args.r_ppt_nm,
|
|
561
|
+
f_ppt=args.f_ppt,
|
|
562
|
+
gamma_apb=args.gamma_apb,
|
|
563
|
+
A_ppt=args.A_ppt,
|
|
564
|
+
)
|
|
565
|
+
sigma_y = y['sigma_y']
|
|
514
566
|
|
|
515
567
|
# Fatigue (optional)
|
|
516
568
|
if args.sigma_a is not None:
|
|
517
569
|
out = fatigue_life_const_amp(
|
|
518
570
|
mat,
|
|
519
571
|
sigma_a_MPa=float(args.sigma_a),
|
|
520
|
-
sigma_y_tension_MPa=float(
|
|
572
|
+
sigma_y_tension_MPa=float(sigma_y),
|
|
521
573
|
A_ext=float(args.A_ext),
|
|
522
574
|
mode=args.mode,
|
|
523
575
|
D_fail=args.D_fail,
|
|
@@ -528,14 +580,16 @@ def cmd_point(args: argparse.Namespace) -> None:
|
|
|
528
580
|
else:
|
|
529
581
|
out = None
|
|
530
582
|
|
|
583
|
+
# 表示
|
|
584
|
+
label = f"structure={mat.structure}" if args.structure_only else f"metal={mat.name} ({mat.structure})"
|
|
531
585
|
print("=" * 88)
|
|
532
|
-
print(f"v6.9b point |
|
|
586
|
+
print(f"v6.9b point | {label} | mode={args.mode}")
|
|
533
587
|
print("=" * 88)
|
|
534
588
|
|
|
535
589
|
# Yield summary
|
|
536
590
|
y_mode, diag = yield_by_mode(
|
|
537
591
|
mat,
|
|
538
|
-
sigma_y_tension_MPa=float(
|
|
592
|
+
sigma_y_tension_MPa=float(sigma_y),
|
|
539
593
|
mode=args.mode,
|
|
540
594
|
C_class=args.C_class,
|
|
541
595
|
bcc_w110=args.bcc_w110,
|
|
@@ -543,11 +597,15 @@ def cmd_point(args: argparse.Namespace) -> None:
|
|
|
543
597
|
)
|
|
544
598
|
|
|
545
599
|
print("[Yield v5.0]")
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
600
|
+
if args.sigma_y_override is not None:
|
|
601
|
+
print(f" σ_y(override) = {sigma_y:.2f} MPa")
|
|
602
|
+
else:
|
|
603
|
+
print(f" σ_base = {y['sigma_base']:.2f} MPa")
|
|
604
|
+
print(f" Δσ_ss = {y['delta_ss']:.2f} MPa")
|
|
605
|
+
print(f" Δσ_wh = {y['delta_wh']:.2f} MPa (rho_0={args.rho_0:.2e})")
|
|
606
|
+
print(f" Δσ_ppt = {y['delta_ppt']:.2f} MPa ({y['ppt_mechanism']})")
|
|
607
|
+
print(f" σ_y(t) = {y['sigma_y']:.2f} MPa")
|
|
608
|
+
|
|
551
609
|
print("[Class factors v4.1]")
|
|
552
610
|
print(f" C_class = {args.C_class:.4f} (apply to HCP: {args.apply_C_class_hcp})")
|
|
553
611
|
print(f" bcc_w110 = {args.bcc_w110:.3f}")
|
|
@@ -577,35 +635,47 @@ def cmd_point(args: argparse.Namespace) -> None:
|
|
|
577
635
|
|
|
578
636
|
def cmd_calibrate(args: argparse.Namespace) -> None:
|
|
579
637
|
"""Calibrate A_ext from one (σ_a, N_fail) point."""
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
638
|
+
# material 取得
|
|
639
|
+
if args.metal is None and args.structure_only is None:
|
|
640
|
+
raise SystemExit("Error: --metal or --structure_only required")
|
|
641
|
+
|
|
642
|
+
if args.structure_only:
|
|
643
|
+
mat = get_minimal_material(args.structure_only)
|
|
644
|
+
else:
|
|
645
|
+
mat0 = MATERIALS[args.metal]
|
|
646
|
+
mat = replace(
|
|
647
|
+
mat0,
|
|
648
|
+
A_texture=float(args.A_texture),
|
|
649
|
+
T_twin=(float(args.T_twin) if args.T_twin is not None else mat0.T_twin),
|
|
650
|
+
R_comp=(float(args.R_comp) if args.R_comp is not None else mat0.R_comp),
|
|
651
|
+
c_a=float(args.c_a) if args.c_a is not None else mat0.c_a,
|
|
652
|
+
)
|
|
588
653
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
654
|
+
# σ_y
|
|
655
|
+
if args.sigma_y_override is not None:
|
|
656
|
+
sigma_y = args.sigma_y_override
|
|
657
|
+
else:
|
|
658
|
+
y = calc_sigma_y(
|
|
659
|
+
mat,
|
|
660
|
+
T_K=args.T_K,
|
|
661
|
+
c_wt_percent=args.c_wt,
|
|
662
|
+
k_ss=args.k_ss,
|
|
663
|
+
solute_type=args.solute_type,
|
|
664
|
+
eps=args.eps,
|
|
665
|
+
rho_0=args.rho_0,
|
|
666
|
+
r_ppt_nm=args.r_ppt_nm,
|
|
667
|
+
f_ppt=args.f_ppt,
|
|
668
|
+
gamma_apb=args.gamma_apb,
|
|
669
|
+
A_ppt=args.A_ppt,
|
|
670
|
+
)
|
|
671
|
+
sigma_y = y['sigma_y']
|
|
602
672
|
|
|
603
673
|
preset = FATIGUE_CLASS_PRESET.get(mat.structure, FATIGUE_CLASS_PRESET['FCC'])
|
|
604
674
|
r_th, n = preset['r_th'], preset['n']
|
|
605
675
|
|
|
606
676
|
y_mode, _ = yield_by_mode(
|
|
607
677
|
mat,
|
|
608
|
-
sigma_y_tension_MPa=float(
|
|
678
|
+
sigma_y_tension_MPa=float(sigma_y),
|
|
609
679
|
mode=args.mode,
|
|
610
680
|
C_class=args.C_class,
|
|
611
681
|
bcc_w110=args.bcc_w110,
|
|
@@ -625,11 +695,12 @@ def cmd_calibrate(args: argparse.Namespace) -> None:
|
|
|
625
695
|
A_eff = rate_needed / ((r - r_th) ** n)
|
|
626
696
|
A_ext = A_eff / A_int
|
|
627
697
|
|
|
698
|
+
label = f"structure={mat.structure}" if args.structure_only else f"metal={mat.name} ({mat.structure})"
|
|
628
699
|
print("=" * 88)
|
|
629
700
|
print("v6.9b calibrate A_ext")
|
|
630
701
|
print("=" * 88)
|
|
631
|
-
print(f"
|
|
632
|
-
print(f"σ_y
|
|
702
|
+
print(f"{label}, mode={args.mode}")
|
|
703
|
+
print(f"σ_y = {sigma_y:.3f} MPa {'(override)' if args.sigma_y_override else '(calc)'}")
|
|
633
704
|
print(f"yield(mode) = {y_mode:.3f} MPa")
|
|
634
705
|
print(f"amp = {args.sigma_a:.3f} MPa {'(τ_a)' if args.mode=='shear' else ''}")
|
|
635
706
|
print(f"r={r:.6f}, r_th={r_th:.3f}, n={n:.2f}")
|
|
@@ -638,33 +709,45 @@ def cmd_calibrate(args: argparse.Namespace) -> None:
|
|
|
638
709
|
|
|
639
710
|
|
|
640
711
|
def cmd_sn(args: argparse.Namespace) -> None:
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
712
|
+
# material 取得
|
|
713
|
+
if args.metal is None and args.structure_only is None:
|
|
714
|
+
raise SystemExit("Error: --metal or --structure_only required")
|
|
715
|
+
|
|
716
|
+
if args.structure_only:
|
|
717
|
+
mat = get_minimal_material(args.structure_only)
|
|
718
|
+
else:
|
|
719
|
+
mat0 = MATERIALS[args.metal]
|
|
720
|
+
mat = replace(
|
|
721
|
+
mat0,
|
|
722
|
+
A_texture=float(args.A_texture),
|
|
723
|
+
T_twin=(float(args.T_twin) if args.T_twin is not None else mat0.T_twin),
|
|
724
|
+
R_comp=(float(args.R_comp) if args.R_comp is not None else mat0.R_comp),
|
|
725
|
+
c_a=float(args.c_a) if args.c_a is not None else mat0.c_a,
|
|
726
|
+
)
|
|
649
727
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
728
|
+
# σ_y
|
|
729
|
+
if args.sigma_y_override is not None:
|
|
730
|
+
sigma_y = args.sigma_y_override
|
|
731
|
+
else:
|
|
732
|
+
y = calc_sigma_y(
|
|
733
|
+
mat,
|
|
734
|
+
T_K=args.T_K,
|
|
735
|
+
c_wt_percent=args.c_wt,
|
|
736
|
+
k_ss=args.k_ss,
|
|
737
|
+
solute_type=args.solute_type,
|
|
738
|
+
eps=args.eps,
|
|
739
|
+
rho_0=args.rho_0,
|
|
740
|
+
r_ppt_nm=args.r_ppt_nm,
|
|
741
|
+
f_ppt=args.f_ppt,
|
|
742
|
+
gamma_apb=args.gamma_apb,
|
|
743
|
+
A_ppt=args.A_ppt,
|
|
744
|
+
)
|
|
745
|
+
sigma_y = y['sigma_y']
|
|
663
746
|
|
|
664
747
|
sigmas = np.linspace(args.sigma_min, args.sigma_max, args.num)
|
|
665
748
|
Ns = generate_sn_curve(
|
|
666
749
|
mat,
|
|
667
|
-
sigma_y_tension_MPa=float(
|
|
750
|
+
sigma_y_tension_MPa=float(sigma_y),
|
|
668
751
|
A_ext=args.A_ext,
|
|
669
752
|
sigmas_MPa=sigmas,
|
|
670
753
|
mode=args.mode,
|
|
@@ -674,17 +757,18 @@ def cmd_sn(args: argparse.Namespace) -> None:
|
|
|
674
757
|
apply_C_class_hcp=args.apply_C_class_hcp,
|
|
675
758
|
)
|
|
676
759
|
|
|
760
|
+
label = f"structure={mat.structure}" if args.structure_only else f"metal={mat.name} ({mat.structure})"
|
|
677
761
|
print("=" * 88)
|
|
678
|
-
print(f"v6.9b S-N |
|
|
762
|
+
print(f"v6.9b S-N | {label} | mode={args.mode}")
|
|
679
763
|
print("=" * 88)
|
|
680
|
-
print(f"σ_y
|
|
764
|
+
print(f"σ_y={sigma_y:.3f} MPa {'(override)' if args.sigma_y_override else '(calc)'} | A_ext={args.A_ext:.3e} | D_fail={args.D_fail:.3f}")
|
|
681
765
|
|
|
682
766
|
header_amp = 'sigma_a_MPa' if args.mode != 'shear' else 'tau_a_MPa'
|
|
683
767
|
print(f"{header_amp:>12} {'N_fail':>14} {'log10N':>10} {'r':>10} {'note':>10}")
|
|
684
768
|
for s, N in zip(sigmas, Ns):
|
|
685
769
|
y_mode, _ = yield_by_mode(
|
|
686
770
|
mat,
|
|
687
|
-
sigma_y_tension_MPa=float(
|
|
771
|
+
sigma_y_tension_MPa=float(sigma_y),
|
|
688
772
|
mode=args.mode,
|
|
689
773
|
C_class=args.C_class,
|
|
690
774
|
bcc_w110=args.bcc_w110,
|
|
@@ -702,7 +786,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
702
786
|
sub = p.add_subparsers(dest='cmd', required=True)
|
|
703
787
|
|
|
704
788
|
def add_common(sp: argparse.ArgumentParser):
|
|
705
|
-
|
|
789
|
+
# metal OR structure_only
|
|
790
|
+
sp.add_argument('--metal', choices=sorted(MATERIALS.keys()), default=None,
|
|
791
|
+
help='Material from database')
|
|
792
|
+
sp.add_argument('--structure_only', choices=['BCC', 'FCC', 'HCP'], default=None,
|
|
793
|
+
help='Use structure preset only (for alloy validation)')
|
|
794
|
+
sp.add_argument('--sigma_y_override', type=float, default=None,
|
|
795
|
+
help='Override σ_y with experimental value [MPa]')
|
|
796
|
+
|
|
706
797
|
sp.add_argument('--T_K', type=float, default=300.0)
|
|
707
798
|
sp.add_argument('--c_wt', type=float, default=0.0, help='solute wt%% (e.g., 0.10 for 0.10 wt%%)')
|
|
708
799
|
sp.add_argument('--k_ss', type=float, default=0.0, help='solid-solution k [MPa/(wt%%)^n]')
|
|
@@ -751,12 +842,36 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
751
842
|
|
|
752
843
|
return p
|
|
753
844
|
|
|
754
|
-
|
|
755
845
|
def main() -> None:
|
|
756
846
|
parser = build_parser()
|
|
757
847
|
args = parser.parse_args()
|
|
758
848
|
args.func(args)
|
|
759
849
|
|
|
850
|
+
BANNER = """
|
|
851
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
852
|
+
|
|
853
|
+
███╗ ███╗██╗ ██████╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗
|
|
854
|
+
████╗ ████║██║██╔═══██╗██╔════╝╚██╗ ██╔╝████╗ ██║██╔════╝
|
|
855
|
+
██╔████╔██║██║██║ ██║███████╗ ╚████╔╝ ██╔██╗ ██║██║
|
|
856
|
+
██║╚██╔╝██║██║██║ ██║╚════██║ ╚██╔╝ ██║╚██╗██║██║
|
|
857
|
+
██║ ╚═╝ ██║██║╚██████╔╝███████║ ██║ ██║ ╚████║╚██████╗
|
|
858
|
+
╚═╝ ╚═╝╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝
|
|
859
|
+
|
|
860
|
+
δ-theory Unified Model v6.9b
|
|
861
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
862
|
+
v5.0 Yield × v6.8 Fatigue × τ/σ Class
|
|
863
|
+
|
|
864
|
+
BCC: r_th=0.65 FCC: r_th=0.02 HCP: r_th=0.20
|
|
865
|
+
Λ(D) = D/(1-D) → Λ=1 : failure
|
|
866
|
+
|
|
867
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
868
|
+
"""
|
|
869
|
+
|
|
870
|
+
def main() -> None:
|
|
871
|
+
print(BANNER)
|
|
872
|
+
parser = build_parser()
|
|
873
|
+
args = parser.parse_args()
|
|
874
|
+
args.func(args)
|
|
760
875
|
|
|
761
876
|
if __name__ == '__main__':
|
|
762
877
|
main()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: delta-theory
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.10.1
|
|
4
4
|
Summary: Unified materials strength and fatigue prediction based on geometric first principles
|
|
5
5
|
Author: Tamaki
|
|
6
|
-
Author-email: Masamichi Iizumi <
|
|
6
|
+
Author-email: Masamichi Iizumi <m.iizumi@miosync.email>
|
|
7
7
|
Maintainer-email: Masamichi Iizumi <masamichi@miosync.com>
|
|
8
8
|
License: MIT
|
|
9
9
|
Project-URL: Homepage, https://github.com/miosync/delta-theory
|
|
@@ -6,6 +6,7 @@ apps/delta_fatigue_app.py
|
|
|
6
6
|
core/Universal_Lindemann.py
|
|
7
7
|
core/__init__.py
|
|
8
8
|
core/dbt_unified.py
|
|
9
|
+
core/fatigue_redis_api.py
|
|
9
10
|
core/materials.py
|
|
10
11
|
core/unified_yield_fatigue_v6_9.py
|
|
11
12
|
delta_theory.egg-info/PKG-INFO
|
|
@@ -14,6 +15,4 @@ delta_theory.egg-info/dependency_links.txt
|
|
|
14
15
|
delta_theory.egg-info/entry_points.txt
|
|
15
16
|
delta_theory.egg-info/requires.txt
|
|
16
17
|
delta_theory.egg-info/top_level.txt
|
|
17
|
-
tests/test_core.py
|
|
18
|
-
validation/__init__.py
|
|
19
|
-
validation/fatigue_redis_api.py
|
|
18
|
+
tests/test_core.py
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "delta-theory"
|
|
7
|
-
version = "6.
|
|
7
|
+
version = "6.10.1"
|
|
8
8
|
description = "Unified materials strength and fatigue prediction based on geometric first principles"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
11
11
|
authors = [
|
|
12
|
-
{name = "Masamichi Iizumi", email = "
|
|
12
|
+
{name = "Masamichi Iizumi", email = "m.iizumi@miosync.email"},
|
|
13
13
|
{name = "Tamaki"},
|
|
14
14
|
]
|
|
15
15
|
maintainers = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|