"""
calphy: a Python library and command line interface for automated free
energy calculations.
Copyright 2021-2026 (c) Sarath Menon, Yury Lysogorskiy, Ralf Drautz
Interdisciplinary Centre for Advanced Materials Simulation (ICAMS),
Ruhr University Bochum, 44801 Bochum, Germany
calphy is published and distributed under the Academic Software Licence v1.0 (ASL).
calphy is distributed in the hope that it will be useful for non-commercial academic
research, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file for details.
The ASL permits academic non-commercial use only. Contact
sarath.menon@ruhr-uni-bochum.de to enquire about commercial use rights.
More information about the program can be found in:
Menon, Sarath, Yury Lysogorskiy, Jutta Rogal, and Ralf Drautz.
"Automated Free Energy Calculation from Atomistic Simulations." Physical Review Materials 5(10), 2021
DOI: 10.1103/PhysRevMaterials.5.103801
For more information contact:
sarath.menon@ruhr-uni-bochum.de
"""
import os
import numpy as np
import shutil
import argparse as ap
import subprocess
import yaml
import time
import datetime
from calphy.input import read_inputfile
from calphy.liquid import Liquid
from calphy.solid import Solid
from calphy.alchemy import Alchemy
from calphy.routines import (
MeltingTemp,
routine_fe,
routine_ts,
routine_pscale,
routine_tscale,
routine_alchemy,
routine_composition_scaling,
)
[docs]def setup_calculation(calc):
"""
Set up a calculation
Parameters
----------
options: dict
options object
kernel: int
index of the calculation to be run
Returns
-------
job: Phase class
job class
"""
# now we need to modify the routines
if calc.mode == "melting_temperature":
simfolder = None
job = MeltingTemp(calculation=calc, simfolder=simfolder)
elif calc.mode == "alchemy" or calc.mode == "composition_scaling":
simfolder = calc.create_folders()
job = Alchemy(calculation=calc, simfolder=simfolder)
else:
simfolder = calc.create_folders()
if calc.reference_phase == "liquid":
job = Liquid(calculation=calc, simfolder=simfolder)
else:
job = Solid(calculation=calc, simfolder=simfolder)
return job
[docs]def run_calculation(job):
"""
Run calphy calculation
Parameters
----------
job: Phase class
Returns
-------
job : Phase class
"""
if job.calc.mode == "fe":
job = routine_fe(job)
elif job.calc.mode == "ts":
job = routine_ts(job)
elif job.calc.mode == "alchemy":
job = routine_alchemy(job)
elif job.calc.mode == "melting_temperature":
job.calculate_tm()
elif job.calc.mode == "tscale":
job = routine_tscale(job)
elif job.calc.mode == "pscale":
job = routine_pscale(job)
elif job.calc.mode == "composition_scaling":
job = routine_composition_scaling(job)
else:
raise ValueError(
"Mode should be either fe/ts/alchemy/melting_temperature/tscale/pscale/composition_scaling"
)
return job
[docs]def main():
arg = ap.ArgumentParser()
# argument name of input file
arg.add_argument(
"-i", "--input", required=True, type=str, help="name of the input file"
)
arg.add_argument(
"-k",
"--kernel",
required=True,
type=int,
help="kernel number of the calculation to be run.",
)
arg.add_argument(
"-s",
"--screen",
required=False,
type=bool,
help="enable logging to screen",
default=False,
)
arg.add_argument(
"--validate",
action="store_true",
default=False,
help="perform full validation during input parsing (slower, validates all calculations)",
)
# parse input
# parse arguments
args = vars(arg.parse_args())
kernel = args["kernel"]
log_to_screen = args["screen"]
validate = args["validate"]
if validate:
# Full validation mode: validate all calculations during parse
calculations = read_inputfile(args["input"], validate=True)
calc = calculations[kernel]
else:
# Fast mode: parse without validation, then validate only the one we need
# Re-read the YAML to get clean data for just the kernel we need
with open(args["input"], "r") as fin:
data = yaml.safe_load(fin)
calc_data = data["calculations"][kernel]
calc_data["kernel"] = kernel
calc_data["inputfile"] = args["input"]
# Handle pressure conversion
if "pressure" in calc_data.keys():
from calphy.input import _to_none
calc_data["pressure"] = _to_none(calc_data["pressure"])
# Now validate this single calculation
from calphy.input import Calculation
calc = Calculation(**calc_data)
# format and parse the arguments
simfolder = calc.create_folders()
if calc.mode == "melting_temperature":
os.rmdir(simfolder)
simfolder = None
job = MeltingTemp(
calculation=calc, simfolder=simfolder, log_to_screen=log_to_screen
)
elif calc.mode == "alchemy" or calc.mode == "composition_scaling":
job = Alchemy(
calculation=calc, simfolder=simfolder, log_to_screen=log_to_screen
)
os.chdir(simfolder)
else:
if calc.reference_phase == "liquid":
job = Liquid(
calculation=calc, simfolder=simfolder, log_to_screen=log_to_screen
)
else:
job = Solid(
calculation=calc, simfolder=simfolder, log_to_screen=log_to_screen
)
os.chdir(simfolder)
if job.calc.mode == "fe":
_ = routine_fe(job)
elif job.calc.mode == "ts":
_ = routine_ts(job)
elif job.calc.mode == "alchemy":
_ = routine_alchemy(job)
elif job.calc.mode == "melting_temperature":
job.calculate_tm()
elif job.calc.mode == "tscale":
_ = routine_tscale(job)
elif job.calc.mode == "pscale":
_ = routine_pscale(job)
elif job.calc.mode == "composition_scaling":
_ = routine_composition_scaling(job)
else:
raise ValueError(
"Mode should be either fe/ts/alchemy/melting_temperature/tscale/pscale/composition_scaling"
)