ASCI Integration¶
The Activity-Stability-Cost Index (ASCI) combines three individual scores into a unified catalyst ranking metric.
The ASCI Formula¶
Where:
| Symbol | Component | Range | Weight |
|---|---|---|---|
| \(S_a\) | Activity score | [0, 1] | \(w_a\) |
| \(S_s\) | Stability score | [0, 1] | \(w_s\) |
| \(S_c\) | Cost score | [0, 1] | \(w_c\) |
Constraint: \(w_a + w_s + w_c = 1\)
Properties¶
Bounded Output¶
Since all \(S_i \in [0, 1]\) and weights sum to 1:
- \(\phi_{ASCI} = 1\): Perfect catalyst (S_a = S_s = S_c = 1)
- \(\phi_{ASCI} = 0\): Worst catalyst (all scores = 0)
Interpretability¶
The ASCI score directly answers: "On a 0-1 scale, how good is this catalyst?"
Transparency¶
Each component's contribution is explicit:
Python Usage¶
Direct Calculation¶
from ascicat.scoring import calculate_asci
phi = calculate_asci(
activity_score=0.90,
stability_score=0.70,
cost_score=0.65,
w_a=0.33,
w_s=0.33,
w_c=0.34
)
print(f"ASCI: {phi:.3f}") # 0.749
Array Operations¶
import numpy as np
# Multiple catalysts
S_a = np.array([0.9, 0.7, 0.5, 0.3])
S_s = np.array([0.8, 0.9, 0.7, 0.6])
S_c = np.array([0.6, 0.5, 0.95, 0.98])
# Calculate ASCI for all
asci = calculate_asci(S_a, S_s, S_c, 0.33, 0.33, 0.34)
print("ASCI scores:", asci.round(3))
# [0.764, 0.694, 0.718, 0.630]
Using ScoringFunctions Class¶
from ascicat.scoring import ScoringFunctions
scorer = ScoringFunctions()
asci = scorer.combined_asci_score(
activity_score=0.85,
stability_score=0.75,
cost_score=0.80,
w_a=0.4, w_s=0.35, w_c=0.25
)
Weight Selection¶
Default: Equal Weights¶
When to Use
- Initial screening with no prior preference
- Comparing catalysts without bias
- Exploratory research
Activity-Focused¶
When to Use
- Fundamental research
- When stability is not limiting
- Performance benchmarking
Stability-Focused¶
When to Use
- Industrial applications
- Harsh operating conditions
- Long-term operation required
Cost-Focused¶
When to Use
- Large-scale deployment
- Commercial applications
- Earth-abundant materials priority
Weight Validation¶
Weights must satisfy constraints:
from ascicat.config import validate_weights
# Valid weights
validate_weights(0.33, 0.33, 0.34) # OK
validate_weights(0.5, 0.3, 0.2) # OK
validate_weights(0.0, 0.5, 0.5) # OK (zero weight allowed)
# Invalid weights
validate_weights(0.5, 0.3, 0.3) # Error: sum = 1.1
validate_weights(-0.1, 0.6, 0.5) # Error: negative weight
validate_weights(0.5, 0.3, 0.1) # Error: sum = 0.9
Weight Normalization¶
For approximate weights, use normalization:
from ascicat.config import normalize_weights
# Input weights that don't sum to 1
w_a, w_s, w_c = normalize_weights(1, 1, 1)
print(w_a, w_s, w_c) # 0.333, 0.333, 0.333
Score Validation¶
Input scores are validated:
import numpy as np
# Scores outside [0, 1] trigger warning
activity = np.array([0.9, 1.2, 0.8]) # 1.2 is out of range
asci = calculate_asci(activity, [0.7]*3, [0.8]*3, 0.33, 0.33, 0.34)
# Warning: Activity scores outside [0, 1] range...
# Scores clipped to [0, 1]
Component Analysis¶
Decomposing ASCI¶
# For a catalyst with ASCI = 0.75
S_a, S_s, S_c = 0.90, 0.70, 0.65
w_a, w_s, w_c = 0.33, 0.33, 0.34
# Contributions
contrib_a = w_a * S_a # 0.297
contrib_s = w_s * S_s # 0.231
contrib_c = w_c * S_c # 0.221
print(f"Activity contribution: {contrib_a:.3f} ({contrib_a/0.749*100:.1f}%)")
print(f"Stability contribution: {contrib_s:.3f} ({contrib_s/0.749*100:.1f}%)")
print(f"Cost contribution: {contrib_c:.3f} ({contrib_c/0.749*100:.1f}%)")
Identifying Limiting Factor¶
import pandas as pd
# For top catalyst
top = results.iloc[0]
# Which score is limiting?
scores = {
'Activity': top['activity_score'],
'Stability': top['stability_score'],
'Cost': top['cost_score']
}
limiting = min(scores, key=scores.get)
print(f"Limiting factor: {limiting} ({scores[limiting]:.3f})")
ASCI vs. Individual Scores¶
| Scenario | \(S_a\) | \(S_s\) | \(S_c\) | ASCI | Interpretation |
|---|---|---|---|---|---|
| Balanced | 0.8 | 0.8 | 0.8 | 0.80 | Good all-rounder |
| Activity star | 1.0 | 0.5 | 0.5 | 0.67 | Limited by stability/cost |
| Stability star | 0.5 | 1.0 | 0.5 | 0.67 | Limited by activity/cost |
| Cost star | 0.5 | 0.5 | 1.0 | 0.67 | Limited by performance |
| Extreme | 0.9 | 0.9 | 0.3 | 0.70 | Cost is limiting |
Ranking Algorithm¶
ASCICat ranks catalysts by ASCI in descending order:
# Results are automatically sorted
results = calc.calculate_asci()
# Rank 1 = highest ASCI
top_catalyst = results.iloc[0]
print(f"#1: {top_catalyst['symbol']} (ASCI = {top_catalyst['ASCI']:.3f})")
Tie-Breaking¶
For identical ASCI scores (rare), ranking is arbitrary. For reproducibility, the original data order is preserved.
Best Practices¶
Document Your Weights
Always document:
- Weight values used
- Rationale for weight selection
- Any sensitivity analysis performed
Compare Apples to Apples
Don't compare ASCI scores from different weight scenarios directly. A catalyst with ASCI = 0.7 under equal weights is not comparable to ASCI = 0.8 under activity-focused weights.
Use Sensitivity Analysis
Before finalizing weights, run sensitivity analysis to understand how rankings change across the weight space.