Skip to content

Getting Started with fr5

This guide will walk you through your first steps with fr5, from basic usage to advanced features.

Quick Example

Let's start with a simple example - predicting ionic liquid viscosity:

import fr5
from fr5.property_prediction_il import predict_viscosity

# Predict viscosity for 1-ethyl-3-methylimidazolium tetrafluoroborate (EMIM-BF4)
result = predict_viscosity({
    'cation': 'EMIM',
    'anion': 'BF4',
    'temperature': 298.15  # 25°C
})

print(f"Predicted viscosity: {result['viscosity']:.2f} mPa·s")

Core Concepts

1. Package Structure

fr5 is organized into specialized modules:

import fr5

# Molecular descriptors
from fr5.descriptors import generate_il_descriptors

# Property prediction
from fr5.property_prediction_il import predict_viscosity, predict_density
from fr5.property_prediction_mof import predict_co2_uptake

# Bayesian optimization  
from fr5.bayes_opt import BOExperimentManager

# Reaction analysis
from fr5.reaction_analysis import predict_reaction_outcome

# Shared utilities
from fr5.shared import clean_chemical_data, plot_property_comparison

2. Data Formats

fr5 works with standard Python data structures:

# Single ionic liquid
il_single = {
    'cation': 'EMIM',
    'anion': 'BF4',
    'temperature': 298.15
}

# Multiple ionic liquids
il_batch = [
    {'cation': 'EMIM', 'anion': 'BF4', 'temperature': 298.15},
    {'cation': 'BMIM', 'anion': 'PF6', 'temperature': 298.15},
    {'cation': 'HMIM', 'anion': 'Tf2N', 'temperature': 298.15}
]

# Pandas DataFrame (preferred for larger datasets)
import pandas as pd
il_dataframe = pd.DataFrame(il_batch)

Step-by-Step Tutorial

Step 1: Basic Property Prediction

Start with simple property predictions:

from fr5.property_prediction_il import predict_viscosity, predict_density

# Single prediction
viscosity_result = predict_viscosity({
    'cation': 'BMIM',
    'anion': 'PF6', 
    'temperature': 323.15  # 50°C
})

density_result = predict_density({
    'cation': 'BMIM',
    'anion': 'PF6',
    'temperature': 323.15
})

print(f"At 50°C, BMIM-PF6 has:")
print(f"  Viscosity: {viscosity_result['viscosity']:.1f} mPa·s")
print(f"  Density: {density_result['density']:.3f} g/cm³")

Step 2: Batch Predictions

Process multiple conditions efficiently:

import pandas as pd
from fr5.property_prediction_il import predict_viscosity_batch

# Create dataset with temperature variation
il_data = []
for temp in range(298, 374, 25):  # 25°C to 100°C
    il_data.append({
        'cation': 'EMIM',
        'anion': 'BF4',
        'temperature': temp
    })

# Batch prediction
results = predict_viscosity_batch(il_data)

# Convert to DataFrame for analysis
df = pd.DataFrame(results)
print(df[['temperature', 'viscosity']])

Step 3: Working with MOF Properties

Predict Metal-Organic Framework properties:

from fr5.property_prediction_mof import predict_co2_uptake

# Predict CO₂ uptake for HKUST-1
co2_result = predict_co2_uptake(
    mof_name='HKUST-1',
    pressure=1.0,      # 1 bar
    temperature=298.15  # 25°C
)

print(f"HKUST-1 CO₂ uptake: {co2_result['uptake']:.2f} mmol/g")

# From structure file
co2_result_file = predict_co2_uptake(
    mof_structure='path/to/HKUST-1.cif',
    pressure=1.0,
    temperature=298.15
)

Step 4: Molecular Descriptors

Generate molecular descriptors for custom analysis:

from fr5.descriptors import generate_il_descriptors, generate_mof_descriptors

# IL descriptors
il_descriptors = generate_il_descriptors(
    cation='EMIM',
    anion='BF4',
    descriptor_types=['fragment', 'property', 'topological']
)

print(f"Generated {len(il_descriptors)} IL descriptors")
print(f"Sample descriptors: {list(il_descriptors.keys())[:5]}")

# MOF descriptors (from structure file)
mof_descriptors = generate_mof_descriptors(
    structure_file='MOF-5.cif',
    descriptor_types=['geometric', 'chemical', 'topological']
)

print(f"Generated {len(mof_descriptors)} MOF descriptors")

Step 5: Visualization

Create publication-quality plots:

import matplotlib.pyplot as plt
from fr5.shared.visualization import plot_property_comparison

# Generate some predictions for plotting
experimental = [45.2, 78.9, 156.3, 298.7]  # Experimental viscosities
predicted = [43.8, 82.1, 149.2, 305.4]     # Predicted viscosities

# Create comparison plot
plot_property_comparison(
    experimental=experimental,
    predicted=predicted,
    property_name='Viscosity',
    units='mPa·s',
    title='Viscosity Prediction Performance',
    save_path='viscosity_comparison.png'
)

Advanced Features

Bayesian Optimization

Optimize experimental conditions automatically:

import pandas as pd
from fr5.bayes_opt import BOExperimentManager

# Define experimental variables
variables = [
    {'name': 'temperature', 'type': 'continuous', 'bounds': (20, 100)},
    {'name': 'pressure', 'type': 'continuous', 'bounds': (1, 10)},
    {'name': 'catalyst', 'type': 'categorical', 'categories': ['Pd', 'Pt', 'Ni']}
]

# Initial experimental data
initial_data = pd.DataFrame({
    'temperature': [25, 50, 75],
    'pressure': [1, 5, 10],
    'catalyst': ['Pd', 'Pt', 'Ni'],
    'yield': [45.2, 72.8, 68.9],
    'selectivity': [85.1, 91.2, 78.4]
})

# Create Bayesian optimization manager
bo_manager = BOExperimentManager(
    input_details=variables,
    initial_experiments=initial_data,
    db_path="optimization.db",
    objective_names=['yield', 'selectivity'],
    strategy_type='mobo_pareto'
)

# Suggest next experiments
next_experiments = bo_manager.suggest_experiments(batch_size=3)
print("Next experiments to perform:")
print(next_experiments)

Reaction Analysis

Analyze and predict reaction outcomes:

from fr5.reaction_analysis import predict_reaction_outcome, analyze_kinetics

# Predict reaction conversion
reaction_result = predict_reaction_outcome(
    reaction_type='hydrogenation',
    conditions={
        'catalyst': 'Pd/C',
        'solvent': 'ethanol',
        'temperature': 60,    # °C
        'pressure': 1.0,      # atm
        'time': 4.0          # hours
    }
)

print(f"Predicted conversion: {reaction_result['conversion']:.1f}%")
print(f"Predicted selectivity: {reaction_result['selectivity']:.1f}%")

# Analyze reaction kinetics from time-course data
time_points = [0, 1, 2, 4, 8, 12]  # hours
conversions = [0, 25, 42, 65, 78, 85]  # %

kinetic_analysis = analyze_kinetics(
    time=time_points,
    conversion=conversions,
    models=['first_order', 'second_order']
)

best_model = kinetic_analysis.best_model
print(f"Best kinetic model: {best_model['name']}")
print(f"Rate constant: {best_model['rate_constant']:.2e} h⁻¹")

Working with Real Data

Loading Experimental Data

import pandas as pd
from fr5.shared.io import load_experimental_data

# Load from various formats
csv_data = pd.read_csv('experiments.csv')
excel_data = pd.read_excel('experiments.xlsx', sheet_name='results')

# Load from database
from fr5.bayes_opt import load_dataframe_from_db
db_data = load_dataframe_from_db('experiments.db', 'optimization_results')

# Validate data quality
from fr5.shared.validation import validate_chemical_data
validation_report = validate_chemical_data(csv_data)

if validation_report['is_valid']:
    print("✅ Data validation passed")
else:
    print("❌ Data validation issues found:")
    for error in validation_report['errors']:
        print(f"  - {error}")

Data Preprocessing

from fr5.shared import clean_chemical_data, handle_missing_values

# Clean experimental data
clean_data = clean_chemical_data(
    data=raw_data,
    remove_outliers=True,
    outlier_method='iqr',
    missing_value_strategy='interpolate'
)

# Handle specific missing value patterns
filled_data = handle_missing_values(
    data=clean_data,
    strategies={
        'viscosity': 'knn_impute',
        'density': 'interpolate',
        'temperature': 'forward_fill'
    }
)

Best Practices

1. Data Organization

Keep your data well-organized:

# Good: Consistent naming and units
good_data = {
    'cation': 'EMIM',           # Standard abbreviation
    'anion': 'BF4',             # Standard abbreviation  
    'temperature': 298.15,      # Kelvin
    'viscosity': 45.2           # mPa·s
}

# Avoid: Inconsistent naming and units
bad_data = {
    'Cat': '1-ethyl-3-methylimidazolium',  # Non-standard name
    'An': 'tetrafluoroborate',             # Non-standard name
    'T': 25,                               # Celsius (ambiguous)
    'visc': 0.0452                         # Pa·s (wrong units)
}

2. Error Handling

Always handle potential errors:

from fr5.property_prediction_il import predict_viscosity
from fr5.shared.exceptions import ChemistryValidationError

try:
    result = predict_viscosity({
        'cation': 'UNKNOWN_CATION',
        'anion': 'BF4',
        'temperature': 298.15
    })
except ChemistryValidationError as e:
    print(f"Chemistry validation failed: {e}")
    # Handle error gracefully
except Exception as e:
    print(f"Unexpected error: {e}")
    # Log error and continue

3. Performance Optimization

For large datasets, use batch processing:

# Efficient batch processing
from fr5.property_prediction_il import predict_viscosity_batch

# Instead of individual predictions
results = []
for il in large_il_list:
    result = predict_viscosity(il)  # Slow for large datasets
    results.append(result)

# Use batch prediction
batch_results = predict_viscosity_batch(large_il_list)  # Much faster

4. Reproducibility

Set random seeds for reproducible results:

import numpy as np
import torch
from fr5.shared import set_random_seed

# Set global random seed
set_random_seed(42)

# Or set individually
np.random.seed(42)
torch.manual_seed(42)

Common Workflows

Workflow 1: Property Screening

Screen ionic liquids for optimal properties:

import pandas as pd
from fr5.property_prediction_il import predict_viscosity_batch, predict_density_batch

# Define IL library
cations = ['EMIM', 'BMIM', 'HMIM']
anions = ['BF4', 'PF6', 'Tf2N']
temperatures = [298.15, 323.15, 348.15]

# Generate all combinations
il_library = []
for cation in cations:
    for anion in anions:
        for temp in temperatures:
            il_library.append({
                'cation': cation,
                'anion': anion,
                'temperature': temp
            })

# Predict properties
viscosities = predict_viscosity_batch(il_library)
densities = predict_density_batch(il_library)

# Combine results
results_df = pd.DataFrame(il_library)
results_df['viscosity'] = [r['viscosity'] for r in viscosities]
results_df['density'] = [r['density'] for r in densities]

# Find optimal ILs (e.g., low viscosity, high density)
optimal_ils = results_df[
    (results_df['viscosity'] < 50) & 
    (results_df['density'] > 1.2)
].sort_values('viscosity')

print("Optimal ionic liquids:")
print(optimal_ils[['cation', 'anion', 'temperature', 'viscosity', 'density']])

Workflow 2: Experimental Design

Use Bayesian optimization to design experiments:

import pandas as pd
from fr5.bayes_opt import BOExperimentManager

# Step 1: Define experimental space
experimental_space = [
    {'name': 'temperature', 'type': 'continuous', 'bounds': (20, 100)},
    {'name': 'catalyst_loading', 'type': 'continuous', 'bounds': (0.1, 5.0)},
    {'name': 'reaction_time', 'type': 'continuous', 'bounds': (0.5, 24)},
    {'name': 'solvent', 'type': 'categorical', 'categories': ['ethanol', 'water', 'dms0']}
]

# Step 2: Provide initial data
initial_experiments = pd.DataFrame({
    'temperature': [25, 50, 75, 100],
    'catalyst_loading': [0.5, 1.0, 2.0, 5.0],
    'reaction_time': [2, 4, 8, 16],
    'solvent': ['ethanol', 'water', 'dmso', 'ethanol'],
    'yield': [12.3, 45.6, 78.9, 65.4],
    'selectivity': [85.2, 91.7, 88.3, 82.1]
})

# Step 3: Set up optimization
optimizer = BOExperimentManager(
    input_details=experimental_space,
    initial_experiments=initial_experiments,
    db_path="reaction_optimization.db",
    objective_names=['yield', 'selectivity'],
    strategy_type='mobo_pareto'
)

# Step 4: Iterative optimization
for iteration in range(10):
    print(f"\n--- Iteration {iteration + 1} ---")

    # Suggest next experiments
    suggestions = optimizer.suggest_experiments(batch_size=2)
    print(f"Suggested experiments:\n{suggestions}")

    # Simulate experimental results (replace with real experiments)
    new_results = simulate_experiments(suggestions)  # Your experimental function

    # Add results to database
    optimizer.add_experiments(new_results)

    # Check optimization progress
    best_results = optimizer.get_best_results()
    print(f"Best yield so far: {best_results['yield']:.1f}%")
    print(f"Best selectivity so far: {best_results['selectivity']:.1f}%")

Workflow 3: Model Validation

Validate predictions against experimental data:

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
from fr5.property_prediction_il import predict_viscosity_batch
from fr5.shared.validation import validate_model

# Load experimental dataset
experimental_data = pd.read_csv('experimental_viscosities.csv')

# Split into train and test
train_data, test_data = train_test_split(
    experimental_data, 
    test_size=0.2, 
    random_state=42
)

# Make predictions on test set
test_inputs = test_data[['cation', 'anion', 'temperature']].to_dict('records')
predictions = predict_viscosity_batch(test_inputs)
predicted_values = [p['viscosity'] for p in predictions]

# Calculate validation metrics
experimental_values = test_data['viscosity_experimental'].values

rmse = np.sqrt(mean_squared_error(experimental_values, predicted_values))
r2 = r2_score(experimental_values, predicted_values)
mape = np.mean(np.abs((experimental_values - predicted_values) / experimental_values)) * 100

print(f"Model Validation Results:")
print(f"  RMSE: {rmse:.2f} mPa·s")
print(f"  R²: {r2:.3f}")
print(f"  MAPE: {mape:.1f}%")

# Plot predictions vs experimental
from fr5.shared.visualization import plot_property_comparison
plot_property_comparison(
    experimental=experimental_values,
    predicted=predicted_values,
    property_name='Viscosity',
    units='mPa·s',
    title='Model Validation Results'
)

Next Steps

Now that you're familiar with the basics of fr5:

  1. Explore specific modules in detail using the module documentation
  2. Try the examples in the examples/ directory
  3. Read the API documentation for complete function references
  4. Join the community discussions on GitHub
  5. Contribute improvements or report issues

Getting Help

Happy experimenting with fr5! 🧪✨