LES extension (dipole, quadrupoles, polarizability, etc.) merge clean - #1478
Conversation
Clean tp5
Clean tp5
…zability behavior
Clean l1l2 quads clean
…MACELES/LES Merge commit resolving conflicts in 5 files using resolutions validated on branch update-upstream-compat (both fit.sh and bec_test_original.sh pass): Conflict resolutions: - mace/modules/models.py: upstream base + AtomicForcesMACE + fix num_interactions==1 guard (add `not keep_last_layer_irreps`) - mace/modules/extensions.py: keep MACELES (with keep_last_layer_irreps=True and zero external_field→None fix) + upstream PolarMACE - mace/modules/symmetric_contraction.py: upstream + our EmptyParam.__deepcopy__ - mace/calculators/mace.py: upstream + BEC/external field support - mace/data/atomic_data.py: upstream + atomic_numbers field in from_config Fork-specific additions preserved in auto-merged files: - mace/modules/blocks.py: LinearLesReadoutBlock / NonLinearLesReadoutBlock - mace/modules/__init__.py: LES readout block exports + AtomicForcesMACE - mace/cli/run_train.py: AtomicForcesMACE training branch - mace/cli/eval_configs.py: latent_charges/dipoles/kappas/alphas output - mace/tools/arg_parser.py: AtomicForcesMACE model choice - mace/tools/model_script_utils.py: AtomicForcesMACE build block
- calculators/mace.py: insert LES_alphas, LES_kappas, BEC storage and BEC force application block after results_map processing (block was absent in upstream refactor to results_map pattern) - modules/extensions.py: add import logging; replace all bare print() calls with logging.info/debug to match mace logging conventions
- modules/models.py: remove AtomicForcesMACE class (unused, no tests) - modules/__init__.py: remove AtomicForcesMACE export - tools/arg_parser.py: remove AtomicForcesMACE from model choices - tools/model_script_utils.py: remove AtomicForcesMACE build block - cli/run_train.py: remove AtomicForcesMACE training branch - modules/symmetric_contraction.py: remove EmptyParam.__deepcopy__ (absent from upstream; PyTorch default is sufficient) - tools/train.py: remove commented-out logging artifact from merge
aacostadiaz
left a comment
There was a problem hiding this comment.
Thanks for the work here! Two things before merge: there are no tests yet, and I think the new tensor outputs give wrong numbers on the cueq backend.
|
@cursor review |
Bugbot couldn't runBugbot is not enabled for your user on this team. Ask your team administrator to increase your team's hard limit for Bugbot seats or add you to the allowlist in the Cursor dashboard. |
Changes addressing review feedbackThank you for the prompt review!
+++ |
aacostadiaz
left a comment
There was a problem hiding this comment.
Thanks for addressing the review. I found some issues in the external-field force path, details inline.
…ar branch, ensure to normalize field to (3,)
aacostadiaz
left a comment
There was a problem hiding this comment.
Thanks, both fixes look right.
Brings the four commits that landed on main into develop: 54b9645 Add control of dispersion coordination cutoff in mace_mp (#1536) e333ae6 LES extension (dipole, quadrupoles, polarizability, etc.) (#1478) 22f0809 Make node_energy Mac Silicon MPS-compatible (#1505) f1c09d8 fix cueq_conv_fusion for LAMMPS (#1475) Three conflicts, all additive, resolved as unions: mace/modules/__init__.py export MACELES alongside the magnetic classes; __all__ already listed both mace/calculators/mace.py keep develop's model_kwargs name, add main's external_field injection and the compute_bec branch mace/modules/extensions.py union of both import sets; every symbol on both sides is used tests/extensions/les/test_maceles.py merged automatically: git followed the rename from the test-suite restructure on develop.
Add MACELES: long-range electrostatics implementation with added features (dipoles, quadrupoles, polarizability, etc.)
mace/modules/extensions.py— MACELES classclass MACELES(line 123)MACELES augments the model wiht per-atom electrostatic
degrees of freedom predicted from the equivariant node features and
passing them to the
leslibrary to calculate long-range part.This PR adds extended features including dipoles, quadrupoles, polarizability, etc.
Readout architecture (
__init__, lines 124–226)Each quantity uses a dedicated readout module cloned from the MACE readout
stack via two helper functions:
_copy_mace_readout(readout, change_irrep_out=...)(line 50): extendedfrom upstream's version with an optional
change_irrep_outargument.Used to clone the existing readout and retarget its output irreps —
e.g.
change_irrep_out="1x1o"for dipoles,"1x2e"for quadrupoles._copy_mace_readout_tp(readout, ...)(line 77, new): builds aLinearLesReadoutBlockorNonLinearLesReadoutBlockfor quantitiesthat require an outer product of l=1 features (see
blocks.pybelow).The per-quantity readouts are:
les_readouts)0eles_u_readouts)change_irrep_out="1x1o"1oles_quad_2e_readouts)change_irrep_out="1x2e", then converted viachange_of_basis_quads(ReducedTensorProducts('ij=ji', i='1o')) to Cartesian 3×32eles_quad_1o_readouts)_copy_mace_readout_tp→LinearLesReadoutBlock1o ⊗ 1oles_alpha_readouts)0eles_alpha_2e_readouts)change_irrep_out="1x0e + 1x2e", converted viaCartesianTensor("ij=ji").reduced_tensor_products().change_of_basis0e + 2eles_alpha_1o_readouts)_copy_mace_readout_tp→LinearLesReadoutBlock1o ⊗ 1oles_kappa_readouts)0eThe l=1 outer-product path (
les_quad_1o_readouts,les_alpha_1o_readouts)exists because many trained MACE models use only l≤1 hidden irreps and
therefore have no
2efeatures — the quadrupole/polarizability tensormust then be formed as v⊗v from the available
1ochannel.keep_last_layer_irrepsrequirement (line 126)MACELES forces this flag before calling
super().__init__(). Without it,MACE.__init__would collapse the last interaction layer to scalar-onlyoutput (see
models.pyfix below), making vector/tensor featuresunavailable at readout time.
mace/modules/blocks.py— LES readout blocks (lines 1400–1555)LinearLesReadoutBlock(line 1400)Predicts a per-atom 3×3 symmetric polarizability tensor from equivariant
features. For each node:
Linearlayer mixing features within the inputirreps.
0e) weights αᵢₖ and vector (1o/1e) channels vᵢₖ.scalar_to_weight_1o/scalar_to_weight_1elayer.T = Σₖ wₖ · (vₖ ⊗ vₖ)— this isautomatically symmetric and equivariant (SO(3)-covariant under
rotations of the input features).
(
make_w_pos=True).The outer product lives entirely in standard PyTorch (no e3nn CG
coefficients needed for the contraction), keeping compilation overhead low.
NonLinearLesReadoutBlock(line 1502)Nonlinear variant: passes the equivariant features through a gated
nonlinearity (
e3nn.nn.Gate) before the same outer-product readout.Used when the linear model undershoots the polarizability dynamic range.
mace/modules/models.py(line 169)When
num_interactions=1, upstream unconditionally reduced the hiddenirreps to scalars only, even when
keep_last_layer_irreps=True. MACELESsets this flag to preserve vector/tensor features; without the fix,
hidden_irreps_outbecomes e.g."128x0e"instead of the full irreps,and the downstream LES readout blocks raise error due to lack of L=1 or L=2 node features.
mace/data/atomic_data.py—atomic_numbersfield (line 402)Required for future use of element-specific fixed charge options in the LES
library (e.g.
use_fixed_atomic_charges).AtomicData.from_configwas not populating this field, causing aKeyErrorwhen those LES options are active.mace/calculators/mace.py— BEC forces under external electric fields and LES outputsMACECalculator.__init__(line ~112)Five new parameters:
compute_bec,external_field,eps_infty,electric_field_unit,keep_neutral. Whencompute_bec=True,"bec"is added to
implemented_properties.MACECalculator.calculate— forward pass (lines 644–683)compute_bec=Trueis forwarded viaforward_kwargs.The LES latent outputs (
latent_alphas,latent_kappas,BEC) arecollected into Python lists (
ret_tensors.setdefault(..., []).append(...)),so they are accessible as ASE calculator results during MD.
MACECalculator.calculate— post-processing (lines 760–803)After the
results_mapblock, the list-accumulated LES quantities areaveraged over ensemble models and stored:
When
external_fieldis set, atomic forces are augmented by the BECcontribution:
keep_neutral=Truesubtracts the mean BEC before the force applicationto enforce acoustic sum rule neutrality.
More details are described in our preprint.
mace/cli/eval_configs.py— latent quantity outputrun(): added collection listsus_list,kappas_list,alphas_listalongside the existing
qs_list. Per-structure latent quantities aresplit from the batch using
batch.ptrand written toatoms.arraysas{prefix}latent_dipoles,{prefix}latent_kappas,{prefix}latent_alphas.This enables inspection of the internal LES state without modifying the
model forward pass.
Backward compatibility
With this branch, training scripts and pre-trained
MACELESpotentials fromChengUCB/mace:mainload and run without modification. (The training scripts and potentials areshared in https://github.com/ChengUCB/extended_les_fit/tree/main/MLIPs/MACE-LES)
MACELES.forwardwraps attribute accesses that wereadded in later versions (
les_output_scale,les_kappa_scale,les_alpha_scale,use_anisotropic_polarizability) withhasattrguardsto handle older checkpoints.