Analyzing APE output¶
Use soda_analyzer to inspect the states, elementary processes, and local
environments recorded by APE. The same functions apply to the
EAM and MACE examples.
Run the Python snippets after the calculation has finished, from its run
directory, with the matching SODA environment activated. If you only have a
copied database, follow the standalone analyzer setup
and replace output/output.h5 with its path.
What the database contains¶
Item |
Meaning |
|---|---|
State |
A registered structure, identified by |
Process |
A transition rooted at a state, identified by that state’s |
Environment record |
One local atomic environment in the descriptor registry, linked to its source structure and atom. |
Environment cluster |
A group of similar local environments used by the DECAF classifier to guide APE sampling. |
Process structure selectors use rsp=0 for the reactant, rsp=1 for the
saddle, and rsp=2 for the product. A process ID is local to its state, so
retain both IDs when reporting a transition.
Results at each phase¶
Function signatures show key arguments; ... marks additional options in
the linked API reference.
APE accumulates states, processes, and environment information throughout exploration; these queries let you inspect each result as saved.
Result to inspect |
Helper or property |
|---|---|
Registered minima |
|
Search attempts, when summaries were saved |
Read each state’s |
Discovered processes and their three structures |
|
Process-to-state connections |
|
Learned environment clusters |
|
db.state_list holds the registered-state catalogue when state loading is
enabled. db.read_process_product_states() returns the resolved connections,
mapping (state_id, process_id) to product_state_id; it is empty if the
connection table is absent. Several processes can lead to the same registered
state. Use the state and process objects for structures and barrier data, and
db.environment_lib for environment queries as shown below.
Read states and barriers¶
This opens the database read-only and lists the recorded processes:
import soda_analyzer as soda
db = soda.SodaLibrary(
"output/output.h5",
workflow="ape",
state_load=True,
proc_load=True,
structure_load=False,
env_load=True,
storesummariesinfile=True,
)
try:
print("States:", db.state_num)
for state in db.state_list:
print("State:", state.stateID, "Processes:", state.proc_num)
for process in state.proc_list:
print(" Process:", process.procID,
"Barriers [forward, reverse]:", process.get_barrier_list())
if db.environment_lib is not None:
print("Environment clusters:", db.environment_lib.get_cluster_ids())
finally:
db.close()
Forward and reverse barriers are measured from the reactant and product to the saddle, respectively, and are reported in eV. Counts and ordering can vary with the parallel layout; a short run may have no accepted processes.
The loading switches control how much data is prepared when opening the file.
See the soda_analyzer.soda_lib_class reference for their defaults.
State and process functions¶
Here, state is an entry in db.state_list, and process is an entry in
that state’s proc_list.
|
Load and cache process objects belonging to this state. |
Build an ASE structure from stored coordinates and fixed-atom flags. |
|
Read forward and reverse process activation barriers. |
|
|
Read energies of the reactant, saddle, and product. |
Read forward and reverse process attempt frequencies. |
|
|
Calculate forward and reverse rates from this process summary. |
Read the number of times this process was rediscovered. |
|
|
Build an ASE structure from stored coordinates and fixed-atom flags. |
Build ASE structures for the reactant, saddle, and product. |
Keep analysis light for large databases
Use proc_load=False and call state.get_proc_list() only for states you
need. For environment-only queries, use state_load=False with
env_load=True. Keep the database open while calling helpers, and close it
when finished.
Read APE search summaries¶
Search summaries require --storesummariesinfile=1 when running SODA.
Currently, state.search_stats and state.termination_list filter for random
and recycled searches, excluding APE’s descriptor-selected searches. Read the
stored table directly to count APE attempts and their termination codes:
import numpy as np
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ape",
state_load=False, structure_load=False)
try:
total = 0
for state_id in db.state_id_list:
table = db.h5.get(f"States/{state_id}/summary_details")
if table is None:
continue
rows = table[()].reshape(-1)
# Process-search job (1), with an APE-selected search center (5).
ape_searches = (rows["job"]["type"] == 1) & (rows["job"]["disp_type"] == 5)
terminations = rows["resultinfo"]["job_termination"][ape_searches]
codes, counts = np.unique(terminations, return_counts=True)
total += len(terminations)
print("State:", state_id, "APE searches:", len(terminations),
"Termination counts:", dict(zip(codes.tolist(), counts.tolist())))
if total == 0:
print("No APE search summaries are stored.")
finally:
db.close()
These counts include unsuccessful attempts and rediscoveries. They count saved searches, not unique processes. See the termination-code definitions when interpreting the codes.
Export a process trajectory¶
This selects the first state that has a process and exports its three structures. It does not assume that state 0 or process 0 is available.
from ase.io import write
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ape",
state_load=True, proc_load=True, structure_load=False)
try:
for state in db.state_list:
if not state.proc_list:
continue
process = state.proc_list[0]
images = [
soda.get_atoms(db.h5, stateID=state.stateID,
procID=process.procID, rsp=rsp, delta=True)
for rsp in (0, 1, 2)
]
write("first_process.xyz", images)
print("Wrote reactant, saddle, product to first_process.xyz")
break
else:
print("No accepted process is available yet.")
finally:
db.close()
Open first_process.xyz in an atomistic viewer to inspect the moving atoms.
The low-level soda.get_atoms(...) call uses delta=True to reconstruct a
process structure from the stored changes. process.get_rsp_atoms() is a
shorter way to obtain the same three images.
Inspect environment clusters¶
Start by listing cluster summaries, then trace one cluster member back to its structure and atom:
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ape",
state_load=False, structure_load=False, env_load=True)
try:
envlib = db.environment_lib
if envlib is None:
print("No environment data is present.")
else:
for summary in envlib.summarize_clusters():
print("Cluster:", summary["cluster_id"],
"Records:", summary["registry_row_count"],
"Unique descriptors:", summary["unique_full_descriptor_count"])
cluster_ids = envlib.get_cluster_ids()
if cluster_ids:
cluster_id = cluster_ids[0]
rows = envlib.get_cluster_members(cluster_id)
print("First member:", envlib.get_trace(rows[0]))
print("Atom:", envlib.get_atom(rows[0]))
representative = envlib.get_representative_environment(cluster_id)
print("Representative registry row:", representative.registry_row_id)
else:
print("No assigned environment clusters are present yet.")
finally:
db.close()
A registry row identifies an environment occurrence, while
full_descriptor_id points to its descriptor vector. Several records may
reference the same vector, so registry-row and unique-descriptor counts need
not match. A trace’s negative process_id denotes a state-level environment
rather than a process structure.
Environment functions¶
These methods belong to envlib = db.environment_lib:
|
Read cluster IDs present in the selected source structures. |
|
Summarize cluster membership, source structures, and provenance. |
|
Select registry indices assigned to one cluster. |
|
Select descriptor registry indices using structure and cluster filters. |
|
Read source-structure identifiers and provenance for a registry row. |
|
Trace cluster members back to their source atoms and structures. |
|
Group cluster-member traces by their source structure. |
|
Read cluster IDs appearing in a state or process structure. |
|
Select a representative environment from one cluster. |
|
Read the full source structure associated with a registry row. |
|
Read the central atom associated with a registry row. |
|
Write source structures containing members of one cluster. |
|
Read embedded descriptor vectors for selected registry indices. |
|
Read full descriptor vectors referenced by registry indices. |
|
Read the earliest known update cycle for each cluster. |
Copy the descriptor model metadata. |
Compare physical environments across runs
Cluster numbers are labels within a run. Changing the descriptor or its embedding can change the labels and groupings, so compare representative structures and atoms when comparing EAM and MACE explorations. More clusters or processes alone does not establish convergence.
The detailed APE analyzer reference provides additional examples for cluster exports, descriptor statistics, and restoring the saved DECAF model. Basic database queries above do not require loading the force potential or MACE model.
See the generated API reference for full signatures, parameters, and return values. The query summaries above come directly from the source docstrings.