Analyzing EZGA output¶
Use soda_analyzer to inspect EZGA agents, generations, populations, and
evaluated structures. For a quick result from the FCC example,
run its supplied analyze.py; the examples below explain the underlying
queries.
Run the Python snippets after the calculation has finished, from its run
directory, with the matching SODA environment activated. For a copied
database, use the standalone analyzer setup
and replace output/output.h5 with its path.
What the database contains¶
Item |
Meaning |
|---|---|
Agent |
An evolutionary search with its own configuration and population. |
Generation |
An iteration within an agent’s search; generation 0 holds the initial-population evaluation in the FCC example. |
Structure |
A stored candidate with identity, provenance, and, when available, evaluated energy and atomic data. |
Population membership |
A link between a structure and a population view at a particular agent, generation, and stage. |
Generation commit |
A save record identifying an agent, generation, stage, completion status, and saved-data boundaries. |
Results at each phase¶
Function signatures show key arguments; ... marks additional options in
the linked API reference.
Optional stages depend on the EZGA configuration. Queries accept agent, generation, or stage filters as described in their API pages.
Result to inspect |
Helper |
|---|---|
Initial and newly created candidates |
|
Evaluations and objective values |
|
Population after a generation |
|
Rejected candidates |
|
Optional HISE stages |
|
Optional reactivity paths and refinement |
|
Saved-generation completion |
|
Latest completely saved generation |
|
Rows within a saved generation’s data boundary |
|
Recorded convergence checks |
|
These methods belong to ezga = db.ezga.
Inspect the latest saved generation¶
An EZGA search can stop at an arbitrary generation. Use
get_latest_complete_commit(agent_id=None, stage_index=None, …)
to identify the latest completely saved generation for each agent:
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ezga")
try:
ezga = db.ezga
for agent_id in ezga.get_agent_ids():
commit = ezga.get_latest_complete_commit(agent_id=agent_id)
if commit is None:
print("No complete generation for agent", agent_id)
continue
print("Agent:", agent_id, "Generation:", commit.generation,
"Stage:", commit.stage_index)
finally:
db.close()
The returned record contains save metadata, including row counts that delimit
saved tables. It does not contain the population structures. Without agent_id,
the method selects one record across all agents. “Latest” means the greatest
commit ID, with row position breaking ties.
To select an earlier generation or a particular stage, pass those filters:
ezga.get_latest_complete_commit(agent_id=0, generation=5, stage_index=0).
The result is None if no matching complete save exists.
Read the saved population¶
get_population_memberships(agent_id=None, generation=None, …)
selects membership rows by agent, generation, and stage, but does not exclude
rows beyond a commit’s saved-data boundaries. The following example first uses
get_committed_table_prefix(table_name, commit=None)
to exclude later incomplete writes, then selects active members and loads their
unique structures. Change agent_id to inspect another agent.
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ezga")
try:
ezga = db.ezga
agent_id = 0
commit = ezga.get_latest_complete_commit(agent_id=agent_id)
if commit is None:
print("No complete generation for agent", agent_id)
else:
table = ezga.get_committed_table_prefix("population_membership", commit)
members = [dict(zip(table.dtype.names, row)) for row in table]
members = [
row for row in members
if row.get("agent_id", 0) == agent_id
and row["generation"] == commit.generation
and row["stage_index"] == commit.stage_index
and row["active"] == 1
]
table = ezga.get_committed_table_prefix("structure", commit)
rows_by_id = {}
for row_index, row in enumerate(table):
row = dict(zip(table.dtype.names, row))
if row.get("agent_id", 0) == agent_id:
rows_by_id[int(row["structure_id"])] = row_index
# Several population views can refer to the same structure.
structure_ids = dict.fromkeys(int(row["structure_id"]) for row in members)
for structure_id in structure_ids:
if structure_id not in rows_by_id:
raise ValueError(f"Structure {structure_id} is outside this save")
structure = ezga.read_structure(row_index=rows_by_id[structure_id])
print(structure.structure_id, structure.potential_energy)
# structure.atoms contains the ASE Atoms object.
finally:
db.close()
members retains all active population views; inspect view_kind and
view_rank to distinguish them. Structures are loaded once per ID. A structure
may have been created in an earlier generation and remain in this population;
get_structures(generation_created=...) selects creation history instead.
Legacy rows without an agent_id belong to agent 0.
Check convergence separately¶
A complete save records successful persistence, not search convergence.
Inspect ezga.get_rows("convergence_history", agent_id=..., generation=..., stage_index=..., as_dict=True) for the selected generation’s recorded checks,
including objective_converged, information_converged, and final_converged
when present. Interpret them against the configured convergence criteria;
missing records do not establish convergence. To stay within a selected save,
apply the same table-prefix filtering shown above to "convergence_history".
Inspect rejected candidates¶
Population constraints can reject a
candidate before dispatch or after its calculation. Their failures share
reason code 6 (native_validation_failed) with native energy-bound
(ef_bounds) failures. The records do not identify which predicate failed
or report constraint rejections as a separate count. Observable blacklist
hits use code 3 (blacklisted).
To inspect rejection records within a complete generation save:
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ezga")
try:
ezga = db.ezga
agent_id = 0
commit = ezga.get_latest_complete_commit(agent_id=agent_id)
if commit is None:
print("No complete generation for agent", agent_id)
else:
table = ezga.get_committed_table_prefix("rejected_structure", commit)
for row in table:
if (row["agent_id"] == agent_id
and row["generation"] == commit.generation
and row["stage_index"] == commit.stage_index):
print("Reason:", row["rejection_reason"],
"Candidate hash:", row["candidate_hash"])
finally:
db.close()
For a direct query, ezga.get_rejected_structures(..., as_dict=True) also
decodes rejection_reason_name, but does not restrict rows to a commit’s
saved-data boundary. Use the table-prefix approach above when a database
may contain incomplete writes.
These are compact metadata records. Constraint rejection rows have
structure_id = -1; they do not save the rejected geometry or feature row
for read_structure(). rejection_value is an energy-like value when
available, not the violated feature or bound; before calculation it may
still reflect a parent’s energy. A generation can have rejection records
and a complete save even when every proposal was rejected before dispatch
and no worker calculation ran.
Inspect agents and generations¶
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ezga")
try:
ezga = db.ezga
print(ezga.summarize())
for agent_id in ezga.get_agent_ids():
print("Agent:", agent_id)
generations = ezga.get_generations(agent_id=agent_id)
print("Generations with complete saves:", generations)
for generation in generations:
commit = ezga.get_latest_complete_commit(
agent_id=agent_id, generation=generation,
)
print("Generation:", generation, "Latest complete stage:", commit.stage_index,
"Commit:", commit.commit_id)
finally:
db.close()
get_generations() lists generation numbers with at least one complete save;
other stages in the same generation may still be incomplete. The loop reports
the latest complete stage for each generation. Pass stage_index to both
queries to restrict the listing to a particular stage.
To read a population, use the commit boundaries in
Read the saved population. Membership rows can
include several population views and are not a count of unique candidates;
use view_kind and active to select a view. The
detailed EZGA analyzer guide describes them.
Export the lowest-energy FCC structure¶
For this fixed-composition example, compare finite evaluated energies and exclude the seed placeholder before choosing a structure:
import numpy as np
from ase.io import write
import soda_analyzer as soda
db = soda.SodaLibrary("output/output.h5", workflow="ezga")
try:
structures = db.ezga.get_structures(payload_load=True)
evaluated = [
structure for structure in structures
if structure.potential_energy is not None
and np.isfinite(structure.potential_energy)
and structure.potential_energy != 9999.0
]
if evaluated:
best = min(evaluated, key=lambda structure: structure.potential_energy)
lattice = abs(np.linalg.det(best.cell)) ** (1 / 3) / 2
print("Structure:", best.structure_id)
print("Energy per atom [eV]:", best.potential_energy / best.atom_count)
print("Effective FCC lattice constant [angstrom]:", lattice)
write("best_fcc.xyz", best.atoms, format="extxyz")
else:
print("No evaluated structures are available yet.")
finally:
db.close()
The division by two assumes the supplied 2 × 2 × 2 conventional supercell. For a distorted cell this is an effective volume-based lattice constant; also inspect the cell shape and atomic positions.
Match the comparison to the search objective
The lowest total energy is meaningful for the FCC example because every candidate contains the same 32 Ar atoms. For variable composition, different sizes, or multiple objectives, use the configured objectives and appropriate references. A low-energy structure alone does not establish that the search or relaxation has converged.
Query functions¶
These methods belong to ezga = db.ezga. Keep the database open while
loading structures and their atomic data.
Summarize EZGA agents, table counts, commits, and optional payload coverage. |
|
Return the agent IDs present in metadata, commits, or structures. |
|
|
Return generation numbers, restricted to complete commits by default. |
|
Return generation commit records matching the supplied filters. |
|
Return the complete commit with the greatest commit ID and row index. |
|
Read the table prefix covered by a generation commit. |
|
Return structure records filtered by their creation metadata. |
|
Return generation population memberships and view flags. |
|
Read a structure by row index or agent and structure ID. |
|
Select EZGA table rows using column filters. |
|
Return structure rejection records for matching generations. |
|
Return HISE stage records with optional status filtering. |
A structure’s .atoms property returns an ASE Atoms object;
.potential_energy, .cell, and .atom_count support numerical analysis.
Filter by agent and stage when combining results from larger searches.
Use the EZGA analyzer reference for
population views, optional SAGE fields, and advanced export recipes.
See the generated API reference for full signatures, parameters, and return values. The query summaries above come directly from the source docstrings.