Changelog¶
Unreleased¶
0.8.0 - 2026-05-19¶
Added¶
Hexagonal Wigner-Seitz periodic supercell mesh:
generate_hex_periodic_mesh(geometry, pixel_size)builds a regular hexagonal mesh centered on the AA stacking site, with exact C6 rotational symmetry around the center (vertex positions placed on a triangular sub-lattice through origin with spacing vectors V1/N, V2/N, filtered to those inside the WS hexagon, with the 6 corners added explicitly).identify_hex_periodic_boundary(mesh)returns the 6 corner-vertex indices, vertex-index lists on each of the 6 edges (sorted counterclockwise), and 3 opposite-edge pair lists ready to plug intoPeriodicPairConstraint. Compared to the parallelogram supercell built bygenerate_finite_mesh(which has only C2 symmetry), this hexagonal cell respects the C3 symmetry of the moiré physics, which matters for low-twist relaxation problems where C3 symmetry of the relaxed state is required (the parallelogram cell systematically biases one moiré-vector direction in problems with rich GSFE structure).PeriodicPairConstraintis now a public class inmoire_metrology.mean_constraint, exported from the top-levelmoire_metrologynamespace. Encodes the linear equalityu_i = u_jfor a list of vertex-index pairs on one layer, as needed for kinematic periodicity across a mesh cut (one pair set per pair of opposite edges of a finite supercell). It composes withMeanDisplacementConstraintandRotationConstraintviastack_mean_constraintsand works with bothlinear_solver="direct"(KKT) andlinear_solver="iterative"(null-space projection).SolverConfig(method="trust-ncg")— true trust-region Newton-CG solver viascipy.optimize.minimize, using the unmodified sparse Hessian through matrix-free Hessian-vector products (energy_func.hessp). Unlike the existingmethod="newton"(Levenberg-Marquardt damped Newton on an eigenvalue-flipped modified Hessian), the new method follows the true unmodified Hessian and can step along negative-curvature directions when they arise on the inner-CG path. Supports homogeneousmean_constraints(B U = 0, satisfied byPeriodicPairConstraint,RotationConstraint, andMeanDisplacementConstraintwith default target) via null-space projection.mean_constraintsnow work withlinear_solver="iterative"via a null-space (projection) method. Each Newton step decomposesdU = dU_p + dU_hwheredU_pis the minimum-norm constraint correction satisfyingB dU_p = -(B U - t)anddU_h ∈ null(B)is the Newton step in the constrained subspace, found by MINRES onP (H + μI) P dU_h = -P (grad + (H + μI) dU_p)with the projectorP = I − Bᵀ (B Bᵀ)⁻¹ B. This enablesRotationConstraintandMeanDisplacementConstraintto be combined with the matrix-free Hessian-vector solver, which is much faster and lower-memory than the sparse-LU KKT path for large problems (n_sol >> 10⁴). Previouslymean_constraintsraisedNotImplementedErroroutsidedirect.SolverConfig(method="L-BFGS-B")now supportsmean_constraintsvia the same null-space projection used by'trust-ncg', with the Gram-matrix factorization viascipy.sparse.linalg.splu(so problems with O(10⁴) constraint rows from per-vertex pairings stay within memory budget). Previously the L-BFGS-B path fell back to rawscipy.optimize.minimizeand refusedmean_constraintswithNotImplementedError.examples/tdbg_low_twist_relaxation.py— TDBG-DFT-D2 relaxation at θ = 0.02° on a hexagonal Wigner-Seitz periodic cell with the bottom flake pinned (the published TDBG MATLAB-code convention). Renders the relaxed “double domain wall” (2DW) network: circular AB / BA domains bounded by curved paired walls, characteristic of the non-zeroc4, c5(AB ↔ BA asymmetric) GSFE terms of TDBG. Usesmethod='two_phase'; runs in ~7–8 min atpixel_size = 4nm.SolverConfig(method="two_phase")— a discovery + polish pipeline that runs'L-BFGS-B'first with a loosened gradient target (gtol_discover = gtol_discover_factor * gtol) and a separate iteration budget (max_iter_discover), then hands off to'trust-ncg'for tight 2nd-order convergence inside whichever basin discovery found. Recommended for low-twist relaxation where both basin choice and high precision matter. NewSolverConfigfields:max_iter_discover: int = 300,gtol_discover_factor: float = 10.0.
Fixed¶
GSFE c4 / c5 derivative sign errors. Five sign errors were corrected in
GSFESurface.dw,d2w2, andd2vw— specifically in thec4 * sin(v + w),c5 * cos(2w),c5 * sin(2w), andc5 * sin(2v + 2w)contributions. The bug affected gradient and Hessian (both sparse-assembled and matrix-free hessp) for any GSFE with non-zeroc4, c5(i.e. interfaces with broken AB ↔ BA symmetry: TDBG, hBN homobilayers, graphene/hBN, TMD heterobilayers). TBG and any centrosymmetric homobilayer withc4 = c5 = 0were unaffected. The error systematically biased the relaxation trajectory toward a wrong stacking-direction basin; for TDBG at θ = 0.02° this showed up as the relaxation landing in a higher-energy single-domain- wall network rather than the lower-energy double-domain-wall (2DW) topology. Fix verified by FD-vs-analytical comparison and reproduces the published TDBG MATLAB reference 2DW state at θ = 0.02°. Existing test coverage intests/test_gsfe.pywas extended with a newTestGSFEDerivativesAsymmetricclass that exercises the c4 / c5 paths with non-zero coefficients (which would have caught all five sign errors).
Changed¶
Default
SolverConfig.methodis now'trust-ncg'instead of'newton'. The Levenberg-Marquardt damped Newton with the eigenvalue-flipped modified Hessian (method='newton') cannot follow negative-curvature directions and converges to whichever local minimum is reached by purely convex steps from the initial guess.'trust-ncg'uses the unmodified Hessian via matrix-free Hessian-vector products and can step along negative-curvature directions when they arise on the inner-CG path, which makes it a more robust default 2nd-order solver. Existing code that explicitly setsmethod='newton'is unaffected; code that relies on the default will now use'trust-ncg'.
0.7.1 - 2026-04-13¶
Fixed¶
Moire unit cell orientation was wrong for heterointerfaces with lattice mismatch.
MoireGeometry.moire_matrixusedR(-theta)where the stacking phase formula requiresR(+theta). The moire cell had the correct wavelength but the wrong orientation, so the GSFE did not tile periodically across cell boundaries. For graphene homobilayers (delta=0) the error was ~1-2% per cell and invisible; for heterointerfaces like MoSe2/WSe2 (delta~0.18%) it was ~10-19% per cell and clearly visible as stitching discontinuities in tiled stacking-energy maps. Fix:self.R_twist->self.R_twist.Tinlattice.py(#29).Strain extraction sweep had a branch discontinuity. The
strain_extraction_and_pinning.pyexample always passedphi0_guess=0toget_strain_minimize_compression, causing the phi0 optimizer to jump between branches at certain dphi values and producing a discontinuous twist-angle curve. Fixed by seeding from the symmetric point (dphi=60) and propagating phi0 across the sweep (#29).
Added¶
Sphinx documentation site with furo theme, deployed to GitHub Pages at
dorrih.github.io/moire_metrology/. Pages: installation, quick start, theory background, examples with embedded figures, custom materials, API reference (auto-generated from docstrings), references, and changelog (#27, #28, #29).Bundled polyline data for the spatial strain extraction example.
examples/data/mose2_wse2_polylines.csvcontains 700 traced moire fringe points (62 polylines, two families) from an H-MoSe2/WSe2 sample, makingspatial_strain_relaxation.pyfully reproducible without the previously required private.matfile (#29).
Changed¶
README slimmed from ~360 to ~120 lines; verbose sections moved to the documentation site (#28).
0.7.0 - 2026-04-12¶
Changed¶
Version is now derived from git tags via
hatch-vcs. The hard-codedversionfield inpyproject.tomland__version__in__init__.pyare replaced by a build-time_version.pygenerated from the nearestv*tag. No manual version bumps needed for future releases — just tag and push.Status upgraded from Alpha to Beta in README and PyPI classifier.
Added¶
PyPI publishing.
pip install moire-metrologynow works. Arelease.ymlGitHub Actions workflow builds and publishes sdist + wheel to PyPI via OIDC trusted publishing on everyv*tag push.
0.6.0 - 2026-04-12¶
Added¶
SolverConfig.rtol— relative gradient tolerance. Convergence is declared when|grad| / |grad_initial| < rtol. This criterion matters at low twist angles where the absolute gradient norm at the energy minimum can be O(1–10) due to large total energies, making any reasonable absolutegtolunreachable. Default:1e-4.SolverConfig.etolandSolverConfig.etol_window— energy stagnation tolerance and window size. If the fractional energy improvement over the lastetol_windowaccepted steps falls belowetol, the Newton solver declares convergence. Previously these were hard-coded internal constants; they are now user-configurable. Defaults:etol=1e-6,etol_window=5.RelaxationResult.convergedproperty — boolean indicating whether the optimizer reported successful convergence.RelaxationResult.convergence_messageproperty — human-readable description of the convergence outcome (e.g. which criterion was met, or “max iterations reached”).Post-hoc relative convergence check for L-BFGS-B. SciPy’s L-BFGS-B only supports an absolute
gtol. The solver now captures the initial gradient norm and checksrtolafter scipy returns, overriding the success flag when the relative criterion is met.Solver display output now includes the relative gradient ratio
(rel X.XXe-XX)alongside the absolute gradient norm.Convergence exit messages now report which criterion triggered (absolute gtol, relative rtol, or energy stagnation) with numeric values.
Fixed¶
pseudo_dynamicssolver always reported failure even when converged. The top-of-loop early exit (if converged(gnorm): break) fired after a single converged step, but the success flag requiredcount_conv >= 3consecutive converged steps. The early break preventedcount_convfrom ever reaching 3, so the solver reportedsuccess=Falseon every run. Fixed by replacing the early break withif count_conv >= count_conv_required(#23).
0.5.0 - 2026-04-12¶
Changed (breaking)¶
Three separate bilayer example scripts replaced by a single
bilayer_relaxation.pywith a--presetsystem. The formerbilayer_graphene_relaxation.py,hbn_relaxation.py, andtmd_heterostructure.pywere identical except for default parameters. The new script accepts--preset graphene(default),--preset hbn, or--preset tmd. Any explicit CLI flag overrides the preset value.
Added¶
CLI-configurable examples. All bundled examples now accept
--interface(bundled name or TOML file path),--theta-twist,--pixel-size,--method,--max-iter,--gtol,--no-plots,--force, and--output-dir. The multilayer example adds--n-top,--n-bottom, and iterative-solver flags. The strain extraction example adds--heterostrain,--n-cells, and--skip-part-a/--skip-part-b.--list-interfacesflag on every example. Prints a detailed table of all bundled interfaces (materials, GSFE coefficients, literature references, CLI alias) and exits.Shared
examples/_cli.pyhelper — interface name resolution with fuzzy matching (e.g.graphene,mose2-wse2-h,hbn-aapall work), TOML loading, graceful error messages for unsupported configurations (heterointerface on multilayer, missing TOML file, unknown interface name).docs/examples.md— guide to all bundled examples with preset tables, CLI argument reference, and usage recipes.docs/custom-materials.md— TOML schema reference for defining custom materials and interfaces, unit conventions, GSFE literature pointers, and limitation notes.Example smoke tests in CI — new
example smoke testsjob runningbilayer_relaxation.py --preset tmd, Part A of the strain extraction example,--list-interfaces, and TOML interface loading.ruff checkextended to coverexamples/.
Fixed¶
Duplicate
--list-interfacesargparse registration instrain_extraction_and_pinning.pyandspatial_strain_relaxation.pycaused anArgumentErrorat parse time (PR #21).
0.4.1 - 2026-04-12¶
Fixed¶
Spurious moire phase on homobilayer GSFE pairs. When both layers of a homobilayer used the same Bernal stacking function, the solver injected an incorrect stacking offset that distorted the GSFE landscape. Fixed by detecting the homobilayer case and zeroing the intra-flake offset. Reproduces Fig 4 of Halbertal et al. arXiv:2206.06395 correctly (PR #18).
0.4.0 - 2026-04-10¶
Changed (breaking)¶
moire_metrology.strain.compute_strain_fieldrewritten with the validated paper-Fig-1 formula. The v0.3.0 scaffolding version took raw gradient arrays(dIdx, dIdy, dJdx, dJdy)and applied a closed- formdu/dx, du/dysolve that was never validated against the paper. The new signature takes query coordinates andRegistryFieldinstances directly, evaluates the analytic gradients internally, and recovers(θ, ε_c, ε_s)per point via the eq. 9 inversion of Halbertal et al. ACS Nano 16, 1471 (2022) followed by the per-pointget_strainsolver. Output keys are nowtheta,eps_c,eps_s(pluslambda1,lambda2,phi1_deg,phi2_deg) — matching what the paper figures plot. The oldeps_xx, eps_xy, eps_yykeys are gone. Validated end-to-end against the maintainer’s MATLAB spatial-extraction script and against paper Fig 1c-e on real H-MoSe2/WSe2 polyline data.compute_displacement_fieldrewritten as a stacking-phase IC builder. Same motivation as above — the v0.3.0 version was unvalidated scaffolding. The new signature takes aMoireGeometryand atarget_stacking(one of"AA","AB","BA") and solvesMu @ u = (v0 - v_target)per query point, wherev_targetcomes from the polynomial registry fit plus the constant phase offset that puts integer registry sites at the requested stacking. Drops the unuseddrparameter.FringeSet.fit_registry_fieldsno longer resamples polylines. Theresample_densityparameter is gone; the fit now uses raw polyline points only. Defaultorderbumped from 8 to 11 to match the paper Methods section. The previous spline-resampling code path produced spurious 180° outliers at the data hull boundary on real data — it was never validated and is removed.theta0_degstandardized tophi0_degacross the spatial strain functions, matching the paper notation and the existing pointwiseget_strain(... phi0=...)API.
Added¶
moire_metrology.strain.convex_hull_mask(data_x, data_y, qx, qy)helper for confining queries to the convex hull of a registry fit’s training data. A high-degree polynomial extrapolates with rapid growth outside its data support; this mask catches mesh vertices and grid points that lie past the data extent before they produce nonsense displacements or strain values.tests/test_strain_spatial.py— 9 unit tests covering the rewritten spatial strain API: rigid-twist registry → recovered|θ|exact and zero strain; output shape preservation; the IC realizes thev_targetphase contract viageom.stacking_phases; switchingtarget_stackingadds a constant displacement offset; unknown stacking →ValueError; convex hull classification.compute_strain_fieldnow also returns the full strain tensor. Output dict gainsS11,S12,S22(symmetric strain tensor in the global(x, y)frame) pluseps1,eps2,strain_angle(principal strains and axis). The previously returnedtheta,eps_c,eps_s,lambda*,phi*_degkeys are unchanged. The new components are the natural inputs to a gradient-integration IC builder for the relaxation framework.displacement_from_strain_field(disc, theta_deg, theta_avg_deg, S11, S12, S22, pin_vertex)— new helper that integrates a target local twist + strain field on the FEM mesh into a displacement fieldu(r)in the relaxation framework’s native language. Implementation is a sparse least-squares Poisson reconstruction:[Dx; Dy] @ u_x = (S11; S12 - δθ)and similarly foru_y, with one vertex pinned to fix the global translation gauge. The eps=0.5 layer-partition factor was confirmed againstenergy.py/discretization.py(factor of 1 on both rotation and strain). Where the previous pointwise phase-matching IC built a step discontinuity inuat the data hull boundary (cliff that the elastic energy hated), the gradient-integration IC has continuousu(r)everywhere.examples/spatial_strain_extraction.py→examples/spatial_strain_relaxation.py, re-extended with the relaxation step. The example now usesdisplacement_from_strain_fieldto build the IC from the strain extraction output (with the strain field zeroed outside the data convex hull, so the integratedusmoothly relaxes to the average configuration there), then runs Newton relaxation againstMOSE2_WSE2_H_INTERFACEwith three-sublattice pinning (AA + BA + AB sites from the polynomial registry fit). Headline figure has 4 panels: paper Fig 1c-e plus the relaxed stacking-energy density showing the equilibrium domain pattern aligned with the traced polylines.examples/hbn_relaxation.py— bundled example demonstrating the graphene/hBN heterointerface relaxation. Headline case is θ = 0° (pure lattice-mismatch moiré, λ ≈ 15.75 nm), where the 1.6% intrinsic mismatch between graphene and hBN drives a moiré pattern even without twist. The relaxed stacking-energy map shows the textbook single-minimum hexagonal domain pattern (vs the AB/BA triangular pattern of TBG). Runs end-to-end in ~3 seconds with L-BFGS-B. UsesGRAPHENE_HBN_INTERFACEand the new v0.3.0Material.moduli_n_per_mproperty to print literature-traceable N/m values for the materials at run start.examples/tmd_heterostructure.py— bundled example demonstrating the H-stacked MoSe2/WSe2 heterointerface at θ = 1.5° (λ ≈ 12.5 nm). The relaxed stacking-energy map shows the three distinct stacking minima (XX’, MX’, MM’) from the broken inversion symmetry, and the energy reduction is ~34% — much larger than TBG’s ~30%, the textbook signature of a “deep moiré potential”. Runs end-to-end in ~30 seconds with L-BFGS-B. Cites Shabani / Halbertal Nat. Phys. 17, 720 (2021).README Quick start section now points at all three bundled example scripts (graphene + hBN + TMD).
Modified-Hessian Newton solver — flips negative eigenvalues of the per-vertex 2×2 GSFE Hessian blocks before assembly. The GSFE curvature at AA sites has eigenvalues down to -3.9×10⁶; the old scalar Levenberg-Marquardt damping couldn’t compensate efficiently. The per-vertex flip produces a globally PD Hessian, enabling meaningful Newton steps from iteration 1. Converges 3× faster than
pseudo_dynamicsin the early phase.3 new round-trip tests in
tests/test_strain_spatial.pyfordisplacement_from_strain_field: pure strain, pure rotation, and general affineu. The integrator reconstructs the target field exactly (up to the global translation gauge), pinning down the partition-factor convention concretely.
0.3.0 - 2026-04-08¶
Added¶
TOML loaders for
MaterialandInterface. Both dataclasses now havefrom_dict()andfrom_toml()classmethods so users can define a custom material or heterointerface in a standalone TOML file and load it without forking the package. The schema mirrors the dataclass field names; an[interface]table inlines its two materials under[interface.bottom]and[interface.top]. Worked example atexamples/data/mose2_wse2_h.tomlreproduces the bundledMOSE2_WSE2_H_INTERFACEand is asserted bit-identical in the test suite.tomli>=2.0added as a conditional dependency for Python 3.10;tomllib(stdlib) is used on 3.11+.README Custom materials and interfaces section now has a TOML loader subsection alongside the existing direct-construction one.
16 new tests in
tests/test_toml_loader.py: round-trips, missing fields, typo’d extra fields (with a helpful “did you mean Interface?” pointer for GSFE onMaterial), wrong-length GSFE coefficients, non-numeric GSFE coefficients, and end-to-end numerical equivalence to the bundled equivalent.Material.from_2d_moduli_n_per_m()constructor — accepts K, G in literature-standard 2D N/m units (the convention used in Lee et al. Science 2008 and most experimental papers) and converts to the internal meV/uc convention. Lets future custom materials be specified in their natural units without the user having to redo the conversion arithmetic by hand.Material.moduli_n_per_mproperty — round-trip helper that returns(K, G)in N/m for sanity-checking existing materials against published indentation values.9 new tests in
tests/test_elastic_units.pycovering the converter round-trip, the lattice-constant scaling, and regression-locking the correctedGRAPHENEparameters against the paper values.
Fixed¶
pyproject.tomlversion bumped from"0.1.0"to"0.2.0". The v0.2.0 refactor PR bumped__version__insrc/moire_metrology/__init__.pybut missed the wheel metadata inpyproject.toml, so the wheel and sdist built from the v0.2.0 tag still self-identify as0.1.0. The Zenodo archive of the v0.2.0 tag is frozen with the wrong wheel metadata; this fix takes effect for the next release tag (v0.2.1 / v0.3.0).Graphene elastic moduli were wrong by a factor of ~8. v0.1.0 and v0.2.0 shipped
GRAPHENE.bulk_modulus = 8595 meV/ucandshear_modulus = 5765 meV/uc, which round-trip to ~26 N/m and ~17 N/m — far below the experimental literature value of ~211 N/m (Lee et al. Science 321, 385 (2008)). The values were not from any cited source and did not match the published Halbertal et al. Nat. Commun. 12, 242 (2021) SI Table 1, which gives K=69518, G=47352 meV/uc citing Carr et al. PRB 98, 224102 (2018). This commit restores the paper values verbatim. Any quantitative graphene relaxation result from a previous version was using ~8× too soft elastic constants and is biased toward over-relaxation (domain walls too narrow, AA areas too small).Graphene GSFE coefficients were ~3% drifted from the paper. The package derived them from a Zhou et al. starting point via
_zhou_to_carrand produced(7.036, 4.041, -0.372, -0.094, 0, 0), whereas the paper Table 1 gives(6.832, 4.064, -0.374, -0.095, 0, 0)taken directly from Carr et al. with no transformation. This commit hard-codes the literal Carr/paper values for graphene, eliminating the drift.hBN elastic moduli now from Falin et al. Nat. Commun. 8, 15815 (2017). v0.1.0 and v0.2.0 shipped
HBN_AA.bulk_modulus = 8595andshear_modulus = 5765 meV/uc— the same wrong-by-~8x values graphene had, copy-pasted from the pre-fixGRAPHENEentry, with no hBN source. They round-tripped to ~26 / ~17 N/m, which is physically implausible for hBN. This commit replaces them with the literature- derived values: Falin et al. reportE_3D = 0.865 ± 0.073 TPafor monolayer hBN by indentation, which givesE_2D ≈ 286 N/musing a 0.334 nm thickness. Withν ≈ 0.21(also Falin), the standard isotropic 2D Lamé relations giveK_2D ≈ 181 N/m, G_2D ≈ 118 N/m, which the newfrom_2d_moduli_n_per_mconstructor converts toK ≈ 61638 meV/uc, G ≈ 40252 meV/uc. BothHBN_AAandHBN_AAPnow use these values; the AA / AA’ designation is a stacking convention for the interface, not a per-layer material property.All bundled materials and interfaces now have explicit literature citations as inline source comments AND as
reference=strings on theInterfaceentries. The hBN GSFE coefficients onHBN_AA_HOMOBILAYER,HBN_AAP_HOMOBILAYER, andGRAPHENE_HBN_INTERFACEwere verified to be exact verbatim copies of Zhou et al. PRB 92, 155438 (2015), Table III, by readingdocs_internal/zhou2015.pdfdirectly. The graphene K, G and GSFE onGRAPHENEandGRAPHENE_GRAPHENEwere verified against Carr et al. PRB 98, 224102 (2018), Table I, by readingdocs_internal/carr2018.pdfdirectly. The MoSe2/WSe2 entries were already verified against the Halbertal Nat. Commun. 2021 SI Table 1 and Shabani Nat. Phys. 2021 Methods section in the v0.2.0 work. The repo no longer contains any uncited material/interface parameters.
0.2.0 - 2026-04-08¶
Changed (breaking)¶
GSFE moved off
Materialand onto a newInterfacedataclass. GSFE describes the registry-dependent stacking energy between two adjacent layers, not a property of either material individually. The v0.1.0 model conflated the two and worked around it for the bundledMOSE2/WSE2heterostructure by storing duplicate coefficients on both materials. The new model has a clean separation:Materialcarriesname,lattice_constant,bulk_modulus,shear_modulus;Interfacecarriesname,bottom,top,gsfe_coeffs,stacking_func,reference.RelaxationSolver.solve()API: replacesolver.solve(material1=GRAPHENE, material2=GRAPHENE, theta_twist=1.05)
with
from moire_metrology.interfaces import GRAPHENE_GRAPHENE solver.solve(moire_interface=GRAPHENE_GRAPHENE, theta_twist=1.05)
For multi-layer flakes also pass
top_interface=and/orbottom_interface=, and usen_top/n_bottominstead ofnlayer1/nlayer2. The legacy kwargs raise aTypeErrorwith a redirect to the new signature.LayerStack: same migration.LayerStack(top=GRAPHENE, n_top=2, bottom=GRAPHENE, n_bottom=3, theta_twist=...)becomesLayerStack(moire_interface=GRAPHENE_GRAPHENE, top_interface=GRAPHENE_GRAPHENE, bottom_interface=GRAPHENE_GRAPHENE, n_top=2, n_bottom=3, theta_twist=...). Validation is done at construction time: missing or mismatched homobilayer interfaces raiseValueErrorwith a clear pointer to the offending field.RelaxationResult: gainsmoire_interface,top_interface,bottom_interfacefields and drops the standalonematerial1,material2fields.result.material1andresult.material2remain available as convenience properties pointing atmoire_interface.topandmoire_interface.bottom. The cached.npzschema bumps to includemoire_interface_name; old caches from v0.1.0 need to be regenerated.GRAPHENE_ON_HBNmaterial removed. The graphene-on-hBN heterostructure is now expressed as theGRAPHENE_HBN_INTERFACEbundled interface that pairs the standaloneGRAPHENEandHBN_AAmaterials. Note: the hBN polytype the underlying Zhou et al. GSFE was fitted against is still pending verification — see the inline TODO ininterfaces.py.
Added¶
New
moire_metrology.interfacessubmodule with theInterfacedataclass and bundled entries:GRAPHENE_GRAPHENE,HBN_AA_HOMOBILAYER,HBN_AAP_HOMOBILAYER,GRAPHENE_HBN_INTERFACE,MOSE2_WSE2_H_INTERFACE. Each entry carries areferencestring with the literature citation for its GSFE numbers.BUNDLED_INTERFACEStuple containing every bundled interface.Interface.is_homobilayerproperty.README “Custom materials and interfaces” section showing how to construct user-defined
MaterialandInterfaceobjects without forking the package.New tests: legacy-kwargs error path, custom-Interface end-to-end agreement with bundled equivalent.
Fixed¶
v0.1.0 always sourced the moiré interface GSFE from
material1.gsfe_coeffs, ignoringmaterial2.gsfe_coeffs. For user-defined heterointerfaces this could silently produce wrong physics. The new model makes the moiré GSFE come from the explicitmoire_interface=argument, eliminating the bug at the API boundary.
0.1.0 - 2026-04-08¶
Initial public release.
Added¶
Core moire relaxation solver (Phase 1).
Strain extraction from moire patterns (Phase 2).
Multi-layer relaxation (Phase 3), including Hessian finite-difference tests for multi-layer configurations.
Constrained relaxation with pinned stacking sites.
pseudo_dynamicssolver: implicit theta-method on the gradient flow, with a matrix-free MINRES path for large systems (#1, #4).fix_top/fix_bottomlayer-clamping options onLayerStackand the solver (#1).examples/multilayer_penetration.py: bulk-uniform substrate relaxation example (#3).Finite-mesh / point-pinning relaxation support for non-periodic geometries (Plan B MVP) (#5).
Public-release scaffolding:
LICENSE(MIT),README.md, fullpyproject.tomlmetadata, GitHub Actions CI (ruff + tests on Python 3.10/3.11/3.12 + sdist/wheel build smoke test),CONTRIBUTING.md, issue and PR templates, citation file.
Fixed¶
Strain extraction: alpha double-counting in the deformation matrix (#6).
Various bug fixes and example/README polish from the hardening pass.