Repository navigation
Releases: cameronabrams/htpolynet
Release list
Release v2.15.0
Added
-
Young's modulus now comes with an error bar, and
deformstages can be
replicated.plots postfits every pull -- each direction, each replica -- on its
own and reports E as the mean of those fits with the standard error between them,
writing the individual fits toE-fits.csv. A single fit's own statistics do not
say how far a second pull would land; the scatter between pulls does. It warns when
that error exceeds 20% of E. Adeformstage takesreplicas: Nto run N pulls with
their own velocities, in<subdir>-r1...<subdir>-rN, andseedto make them
reproducible. -
fit_strainsets the strain window E is fitted over. The window used to be a
range of rows,[10, 200], which covers 0.1-2% strain only at the default rate and
energy-output interval, and silently covered something else whenever either changed
or the pull was shorter.plots postsays when a pull stops short of it.
Changed
-
deformnow defaults to a 40 ps pull, three replicas, and a fit over 0.1-3%
strain (was 1000 ps -- a pull to 100% strain -- one replica, and rows 10-200). The
defaults were measured on example 6's network, 15 pulls to 10% strain: E is flat to
3-4% and softens past it, a single pull scatters by 80% fitted to 2% but by 30% fitted
to 3%, and nine pulls give E to about 10% (2.67 +/- 0.19 GPa there). Tutorials 2, 4, 5
and 6 use them.plots poston a run from before replicas existed still finds its
singledeform-<d>directory. -
Young's modulus is fitted with a free intercept. The fit used to be forced
through zero stress at zero strain, which assumes the structure starts unstressed. A
glass equilibrated for tens of ps does not: example 6's started at +150-200 bar, which
through the origin adds that offset over the strain to E -- about +1 GPa over a 2%
window -- and another network's started at -180 bar, biasing it low. Moduli computed
by earlier versions are off by an amount that depends on each structure's residual
stress. The R^2 warning added earlier in this cycle is gone: a sound fit to a real
trace has R^2 near 0.45, and the scatter between pulls is the real test.
Changed
-
Example 6 now cures bisphenol-A dicyanate by real cyclotrimerization. Its one
monomer, BDC, carries two cyanate groups, and aring_cureblock closes three of them
at a time into a 1,3,5-triazine, the reaction that actually cures a cyanate ester.
Conversion is the cyanate-group conversion, counted directly, and an unreacted group
simply stays -O-C#N, so the example needs no postcure repair. Until now the example
used a stand-in model -- bisphenol A plus a pre-formed triazine, joined by aryl
ethers, with a repair stage dismantling incomplete rings afterward -- whose triazines
existed before cure began, so the way its network formed, and its gel point, were not
those of the chemistry. The tutorial is rewritten to match, with every number from a
reference build (234 rings, cyanate conversion 0.975, Tg 529 K heating and 520 K
cooling, fractional free volume 0.213), and thepostcure_repairpage now describes
the repair stage as the tool for reproducing builds made the old way. The tutorial
no longer quotes a Young's modulus: its deform stages reach 1% strain, which the
reference build showed is too little to resolve one. -
htpolynet now requires ycleptic 2.4.4 or later (was 2.4.1). From 2.4.3 on,
ycleptic checks a base config's shape when it loads, not only its vocabulary: an
attribute indented one level too deep, adefault:that contradicts its owntype:,
or a non-attribute in anattributes:list used to be ignored silently, so whatever it
declared simply did not exist. 2.4.4 extends that check to the top-level list.
htpolynet's own schema passes it clean.
Fixed
-
A postsim stage with an unusable
subdirfailed only when its turn came, after
every stage before it had run -- a 21-stage grid would spend its allocation on the
first stages and then die with a bare assertion.postsimnow checks every stage's
directory before running any and names all the bad ones. A subdir must be one name
inside a project directory (postsim/deform-x); deeper paths such as
postsim/grid-A/deform-xare not supported. -
htpolynet plots diagfailed on every ring-cure run with a bareIndexError. It
recognized onlyCURE's two progress lines, and a ring cure logs its own, so it parsed
nothing. It now reads both, and a log with neither raises an error that says so. Its
iteration axes were also off by one for every cure: the cure's start was plotted as
iteration 1. -
htpolynet plots postcrashed before computing Young's modulus on pandas 3
("Columns with duplicate values are not supported in stack"), losinge.pngand
E.csvafter Tg had been plotted. The deform curves are now averaged without stacking
same-named columns. -
A bond stored with its atoms in descending order could silently keep stale
parameters.Topology.reset_override_from_typeput the requested pair in canonical
order but compared it against the stored row as written, so it missed a bond stored as
(aj, ai); the postcure repair'srefresh_bond_paramsthen swallowed the failure at
debug level and the cap's C-N bond kept its aromatic parameters. The lookup now
compares canonical order on both sides, and a refresh failure is a warning. Found by
the first end-to-end test of the repair driver, which also lands here. -
A ring cure's setup log no longer quotes a meaningless bond count. "100% conversion
is N bonds" came fromCURE's pairwise count, which treated a three-reactant ring
closure as three independent pairwise reactions: 360 BADCy monomers reported 1080
bonds, where full conversion is 240 rings and 720 bonds. A ring cure now says its
conversion is counted in reactive groups and points at the "Ring cure begins" line.
In a configuration with both pairwise and ring-closing reactions, the countCURE
uses for its own target now leaves the ring-closing reaction out. -
Two documentation passages still described Berendsen coupling as current after 2.14.0
moved off it: the densification gate's warning now says the packagednpt.mdpuses
C-rescale (and what that means for densities from older builds), and the container
guide's list of GPU-update-compatible features names V-rescale and C-rescale.
Release v2.14.0
Changed
-
No packaged mdp uses a Berendsen thermostat or barostat any more, and every one of
them now passesgromppwith zero warnings --- which none of them did before. -
Every temperature-coupled stage now uses the V-rescale thermostat. All seven
packaged mdps used Berendsen, which Gromacs deprecates in the same breath as the
barostat: "does not generate the correct kinetic energy distribution." That is every
stage htpolynet runs, including the ones that produce published densities and
transition temperatures. Same cause as the barostat, and the same remedy;tau_tis
unchanged at 0.1 ps. -
Constant-pressure stages no longer use the Berendsen barostat.
npt.mdp,
drag-npt.mdpandrelax-npt.mdpnow use C-rescale, which samples a correct NPT
ensemble and is robust far from equilibrium; Berendsen is neither, and Gromacs has said
so at everygromppsince 2025 --- unheard, because those warnings were being
discarded (fixed in 2.13.0). This covers densification, precure and postcure
equilibration, the CURE relax and drag ladders, and the ring cure's relax and
equilibrate.The deformation stages are the exception: they force
pcoupltype = anisotropic, which
C-rescale does not support at all ---grompperrors outright --- sodeformand
clampedshearnow set Parrinello-Rahman, which does support it and also samples
the right ensemble. Isochoric shear has no barostat and is unaffected.This changes the sampled ensemble. Densities, glass-transition temperatures and
moduli from earlier versions are not strictly comparable with ones produced now. That
cost is deliberate and was taken on Cameron's decision, in the same spirit as the 2.7.0
constraint change.tau_pis unchanged at 0.5 ps, so that if anything proves unstable
there is only one variable to look at; it is the first knob to try.A test now fails if any packaged mdp reintroduces a deprecated barostat, or pairs
C-rescale with the anisotropic coupling it cannot do.
Release v2.13.0
Changed
-
Deformation and shear stages now say what a modulus from them is worth. Every run
reports the strain it will reach and is told that one ramp gives a modulus but not
its uncertainty: eight independent ramps of one protocol on one network gave shear
moduli from 1.29 to 1.74 GPa --- 10% scatter --- while each run's own fit reported an
error an order of magnitude smaller. Take the scatter between seeds as the error bar,
not the fit's.A run fast enough that little of it sits at low strain also draws a warning, since what
gets fitted is then a secant over a large window. It does not claim a bias, because
none is established: a single slow ramp appeared to show the default understating G
threefold and a replicate of that same protocol refuted it. Rate dependence past yield
is real and does replicate --- 1.22 +/- 0.06 GPa atedot = 1e-3against
0.96 +/- 0.02 at1e-4. The default is unchanged. -
Deformation and shear stages now sample stress ten times more often. Both
inheritednstenergy = 500from the packagednpt.mdp, one frame per ps --- and
stress is the quantity these stages exist to measure. Tested on a real shear run:
going to a frame every 0.1 ps cut the standard error on the fitted modulus by
sqrt(10), which is the independent-sample scaling, so the extra frames carry real
information rather than repeating themselves. The cost is a largeredrand nothing
else --- no change to the trajectory, the ensemble, or the physics.
Fixed
-
Gromacs warnings were discarded, so nobody ever saw one. htpolynet runs every
gromppwith-maxwarn 4, so up to four warnings do not stop a run, and the command
wrapper only echoes captured output when a command fails. Between them, a warning on
a successful run reached neither the user nor the diagnostic log.That is how the Berendsen barostat deprecation --- emitted by every constant-pressure
stage htpolynet runs, on every Gromacs since 2025 --- went unnoticed for as long as it
has been emitted, and it is why the shear stage's off-diagonal conflict had to be found
by runninggromppby hand. Warnings are how Gromacs says a setup is questionable,
and they are worth seeing even when the run proceeds. They are now logged at WARNING
level, with the mdp that produced them. -
Uniaxial deformation was rejected by Gromacs 2025 before it started, so Young's
modulus could not be measured at all. From Gromacs 2025, combiningdeformwith
generated velocities is a hard grompp error unlessdeform-init-flow = yesis set,
because the initial velocities have to carry the flow profile the deformation implies.
htpolynet never set it. Everydeformstage therefore failed at grompp on a current
Gromacs --- not with a bad number, but with no run. Bothdeformand the newshear
now set it.This was invisible because nothing ran the stage:
analysis/postsim.pyhad no tests,
and the deformation traces on hand were produced by an older Gromacs. It was found by
putting a real mdp throughgmx gromppwhile trying to build a shear fixture.
Added
-
The shear stage's box coupling is now a choice, because it changes the answer by
29%.ring_cure-style shear has to be run somehow, and there are three defensible
ways to hold the box while shearing it; measured on one network with replicates they
give G of 1.17, 1.35 and 1.51 GPa. That spread is wider than the trajectory scatter
of any of them, so it does not average away.coupling: clamped(the default, and
what every measurement so far used) keeps the two unsheared directions at pressure;
coupling: isochoricuses no barostat at all, needs no uncoupling, and is the only one
of the three that passesgromppwith no warnings whatsoever.The default is unchanged, and not because it is the best: the Poisson ratio read off
the box dimensions ranks it last of the three. It is unchanged because that is one
cross-check at one strain, and switching would silently move every G anyone has
measured. The stage now logs which coupling it used, so a G is never quoted without
it. -
A
shearpostsim stage, for the shear modulus. The counterpart ofdeform:
Gromacs'deformdrives one off-diagonal element of the box matrix at a constant
rate, and G is the slope of shear stress against engineering shear strain, exactly
as E is the slope of tensile stress against tensile strain.directionnames the
plane ---xy,xzoryz--- andedotis the shear rate in ps^-1.The normal directions stay pressure-coupled at
Pwhile the off-diagonal is driven,
because the anisotropic barostat is given zero compressibility off the diagonal and so
does not oppose the deformation. The stage writes the driven box element and the
matching off-diagonal of the pressure tensor, with derived-strainand-stress
columns;analysis.utils.compute_Gfits those, ascompute_Edoes for tension.One thing that differs from the tensile case and is easy to get wrong: the engineering
shear strain is the driven box element over the length of the sheared face and starts
at zero, so it carries no-1.A known disagreement, stated because it is not resolved in this release. Shearing
bydeformrequires the barostat to be switched off for the Cartesian component the
shear moves, since Gromacs refuses the run otherwise. G from that configuration
comes out 1.51 +/- 0.05 GPa where a less constrained variant gives 1.17 +/- 0.02 on the
same network --- and only the latter is consistent with that network's own E and
Poisson ratio. So G here disagrees with E by about a quarter.The tempting explanation, that the constraint inflates G, does not survive its own
test: a constant-volume variant fixes all three box dimensions rather than one, so it
is more constrained still, and its mean sits below the clamped value. The cause is
open. Treat G from this stage as disagreeing with E by roughly a quarter, and do
not assume you know why.The mdp is accepted by Gromacs; no G has been measured. Putting it through
gmx gromppfound two defects the unit tests could not: the barostat must be
uncoupled for the Cartesian component the shear moves, not merely off the diagonal ---
grompp warns of "spurious periodicity effects" otherwise --- anddeform-init-flow
must be set, which is a hard error without. Both are fixed and grompp now accepts
every plane. The energy terms it traces (Box-YX,Box-ZX,Box-ZY,Pres-XY,
Pres-XZ,Pres-ZY) were confirmed present in Gromacs 2025.4.First real run, on a threading-filtered BADCy network of 12,600 atoms: both stages
completed, the stress-strain curve has the right shape for a glassy thermoset --- an
elastic rise to about gamma 0.08, a roll-over, then a yield plateau near 160-175 MPa
--- and G comes out 1.3 +/- 0.2 GPa over gamma 0.08-0.10.deformwith
deform-init-flowwas also verified on Gromacs 2026.3, not only the 2025.4 used here.That G is one window's slope, not a zero-strain tangent, and should not be quoted as
a converged modulus. Below gamma 0.03 the fit has no power at all --- the standard
error exceeds the estimate and the sign flips --- because per-frame virial noise is
456 bar and a 300 ps ramp atedot = 1e-3puts only 20 frames below that strain.
Above it the fitted G falls from 2.08 to 1.31 GPa as the window grows, the same way
the uniaxial fixture's E falls from 4.74 to 2.72. A slower ramp, or more frequent
energy output at the same rate, is what a modulus fixture needs.ROADMAP.mdcarries
this, and the related finding that the uniaxial trace has no resolved elastic region
either. -
Real simulation traces as test fixtures, for Tg and Young's modulus. Until now
every test of those fits used synthetic data, where a clean straight line recovers its
slope to eight digits.tests/unit/fixtures/now carries a real 61-point temperature
ladder and a real 1001-frame uniaxial deformation, the latter written by
PostSimDeformitself.They show what synthetic data cannot. On the real ladder, Tg spans 377 to 406 K
across fit windows a reasonable person would choose; on the real deformation, E spans
2420 to 4398 MPa, nearly a factor of two. The R-squared of a perfectly sensible
modulus fit is under 0.5, because single-frame pressure in MD scatters hugely --- so
anyone tempted to add an R-squared quality gate should look at that first. All of
this is now pinned by tests, andROADMAP.mdcarries what to do about it.There is deliberately no shear fixture, and a test asserts its absence so that
adding one forces the accompanying tests to be written. No shear simulation has been
run. -
Tests for the two numbers postsim exists to produce.
analysis/postsim.pywas a
zero-coverage module, andcompute_Eandcompute_tg--- the fits behind Young's
modulus and the glass transition --- had no tests at all, while theshearstage added
hours earlier did. Now covered: both fits against known slopes and a known
intersection, the mdp construction for everydeformandsheardirection, that the
driven axis is not pressure-coupled while the others are, and that an unknown direction
is refused rather than guessed.Two of those tests document behavior rather than assert it is good.
compute_Efitted
across a rolled-over stress curve understates the modulus by more than a factor of two,
which is what the fit window exists to prevent. Andcompute_tgtrusts its windows: a
hot window reaching about 20 K below the transition biases Tg roughly 7 K low, with no
check and no warning.ROADMAP.mdcarries what a fix would look like.
Release v2.12.0
Added
-
A test now fails when a configuration section is added without documenting it.
configs-for-run.rststill opens by saying a run config has "at most ten distinct
sections" while the schema has fourteen:ncpu,ring_cureandpostcure_repairhave
never been written up. The new test carries those three in a named list so the
existing debt does not block the suite, fails on any new undocumented section, and
fails again if one of the three is documented and left on the list. -
htpolynet piercingsreports any bond threaded through a ring in a finished
network. Since 2.11.4 a ring cure can refuse to create a threading, and there was
no way to ask whether one is there. That asymmetry is the reason this exists: a
preventive check whose effect cannot be measured with the same tool is hard to trust,
and every number that justified those filters came from scripts outside the package.
Nothing about it is chemistry-specific --- any network built by dragging monomers
together can thread a bond through a ring.It takes a top and gro pair like
make-viz, reports each piercing with the ring's
composition, whether the piercing bond belongs to a residue of the ring, the offset of
the crossing from the ring centre in nm and as a fraction of the ring radius, the
angle to the ring normal, and the bond length; it correlates those against bonds too
long to be bonds, which in measured data are the same set.-jsonwrites the
findings out. It exits 1 when it finds any, so it can gate a pipeline.The geometry moved to
htpolynet.geometry.piercing, shared with the ring cure's own
filters so that a measurement and the thing it measures cannot drift apart.Validated against 12 independently measured builds (8 affected, 10 piercings):
counts agree on all 12, including the four clean ones, and both interlocked cases are
found with both halves. Bond lengths agree to the last digit, offsets within
0.001 A, angles within 0.03 degrees. Four of those piercings are in the test suite as
fixtures, atom-for-atom.That comparison found two defects in the reference data rather than in this code ---
atom indices off by one, and a fraction divided by a mean rather than a maximum
centre-to-vertex distance --- both since corrected upstream. The index error was
findable only by testing whether the reported ring was a closed cycle in the topology,
which is the check worth keeping in mind when two implementations are compared: agree
with the data, not with the other detector.The angle is reported as
angle_from_normal, and 0 degrees means the bond runs
straight through, perpendicular to the ring plane. The report says so in a legend,
because the obvious reading of such a field is the opposite one.Documented in the user guide, in the subcommand table and with a section of its own,
alongside a note that a threaded ring is a topological defect rather than a strain
relaxation removes: if one is found in a finished build, rebuilding with the ring-cure
filters on is the remedy and there is no post-hoc repair.Image before you average. Every ring atom is brought into one periodic image
before the centroid is taken. Without that, a ring straddling a boundary gets a
centroid in the middle of the box and a triangle fan spanning the cell: measured here
on a 2 nm box, a 0.14 nm ring reads as 0.938 nm about a centre a full box-half away,
which reports a piercing for nearly everything nearby. A detector written without it
found 212 piercings in an uncured melt, where the answer is zero by construction, and
about 15% of rings straddle a boundary in a real system. The test suite covers that
case, its converse, over-long piercing bonds of 2.4 to 3.2 A, composition classified
on the whole ring rather than on a residue count or a truncated printout, and two
interlocked rings.
Release v2.11.6
Added
-
A ring cure now refuses a closure that threads a ring already present, and registers
the rings it makes. Two halves of one gap.reject_threaded_rings, added in 2.11.4, asks whether a new ring would close around
an existing bond. It does not ask the converse --- whether dragging three monomers
together has run one of their bonds through a ring that was already there. Over ten
builds htpolynet-study found that second kind is mostly phenyls, which outnumber
triazines about six to one here, and that two of three were made by the cure rather
than by packing: one appeared in the same iteration as a triazine piercing, which is
what identifies the two as a single event.reject_new_threadingcloses it.Only threadings the ladder creates count. The participating monomers are surveyed
before the closure ladder and again after it, so a monomer that arrived from packing
already threaded is not silently refused every reaction for the rest of the build ---
one of those exists in their data.The second half is that
Topology.ringsnever contained a cure-formed ring.
detect_rings()runs once per molecule template and the list is replicated at system
build; nothing updated it afterwards, and the product splice did not register the
triazine it creates. So every ring the ring cure ever made was invisible to every
ring-aware check in htpolynet --- including CURE's ownpierces_ring, which means a
pairwise crosslink could thread a previously formed triazine with nothing noticing.
form_ringsnow registers each ring as it makes it.No evidence yet that threading of either kind measurably affects Tg or density; the
case for both filters is mechanism, not measured harm. Set
ring_cure.reject_new_threading: falseto reproduce the old behavior.
Changed
-
The box and density figures in the 2.11.5 and 2.11.4 notes below are retracted.
Both releases quote traces --- +27.9% in volume, path 0.4104 nm, a two-regime
expansion and contraction --- produced byrelaxandequilibraterunning against
the ladder topology, which is the defect 2.11.5 fixes. The box was responding to a
restrained, unbonded system, so those numbers describe nothing real and should not be
requoted. They are left in place rather than rewritten, because the releases went out
with them.What survives unaffected: conversion, ring counts and iteration counts, which are
decided before those stages run; the threading filter's validation; the constant-volume
control, since NVT cannot move the box whatever topology it reads; and every
measurement from the pairwise route, which writes its topology correctly.
Release v2.11.5
Fixed
-
A ring cure relaxed and equilibrated against the wrong topology. Gromacs reads
the topology from disk;form_ringschanges it only in memory and writes nothing.
close_ringswrites one per ladder stage, so after the rings were formed the newest
file on disk was still the ladder topology --- restraints in place at
kb = 3e5, and the new C-N bonds not yet present.relaxandequilibrateboth
grompp'd against it. Onlysettleand the ladder itself wrote what they ran.Counted on one iteration of 89 rings:
ringclose-1-6.topcarries 267 type-6
restraints, exactly 3 per ring, and 11880 bonds; the post-formationring-1.tophas
0 restraints and 12147 bonds. The command issued was
grompp -c ringrelax-1-npt.gro -p ringclose-1-6.top--- current coordinates against a
stale topology, because each mdrun refreshes the coordinate file and nothing refreshed
the topology.So for the whole 100 ps equilibration the rings were held together by restraints
rather than bonded, with the ladder ending around 0.229 nm against its 0.150 nm
target, meaning those restraints were still loaded. Successful runs carried a
Harmonic Pot. term of 2.1e5 kJ/mol throughout. One failure reached 775 K in a 300 K
stage at -57429 bar with 1809 LINCS warnings, which is that restraint energy coming
out.Every MD stage now writes the topology it is about to run.
relaxhas had this
defect since the ring cure was introduced, andequilibratesince 2.11.4, so box
and density traces from any ring-cure build to date are not trustworthy and need
re-measuring. Conversion, ring counts and the threading filter are unaffected ---
all are decided before this stage runs.
Release v2.11.4
Added
-
A file of your own that replaces a packaged one now says so.
pfs.checkout
searches the user library, then the user cache, then what htpolynet ships, and took
the first hit silently. Overriding a packaged file is supported and often the point;
the trap is that a copy taken from an older version keeps that version's settings for
as long as it stays on the search path, and nothing said which file a run actually
used.Found the hard way. A study's
npt.mdp, copied from 2.6.2 and edited in one place,
stayed on its path across four upgrades. From 2.7.0 the packaged file sets
lincs_order = 8, because atdt = 0.002GROMACS' default of 4 is marginal --- it
was fatal on one chemistry in 7 of 7 builds, with exit codes 1 and 139. The stale
copy reverted every run to order 4, and the instability that produced was written up
for weeks as a property of the systems being built.This bears on the 2.11.3 notes below. "Eleven of twenty builds died in the
anneal" is the rate from builds carrying that stale mdp, so it overstates what the
unrelaxed final batch alone causes. The defect and the remedy are unaffected: the
bond energies entering the anneal are single-point energies of the structure and owe
nothing to integration settings, and the paired seed test showed the settle helping
even at LINCS order 4. Only the headline failure rate is in doubt. -
A ring cure now refuses a ring that would close around an existing bond. CURE
tests every candidate bond for piercing an existing ring; the ring cure has the
converse problem and tested nothing at all ---ringcontroller.pyhad no reference to
bondtest,pierces_ringorBTRC. A triazine could therefore form around a
monomer that was already there and thread it permanently.That is what the stretched monomers were. Over ten finished builds, six carried a
threaded triazine, and the threading bonds are the same bonds found stretched past
2 A: 15 stretched, 13 threading, none threading without being stretched. 10 of 11
were pre-existing monomer backbone bonds. It explains why those bonds sit on monomers
that took no part in the ring, why a 160 ps anneal left them unchanged to two
decimals, and why they hold roughly 700 kT without being thermal --- escaping a
topological trap needs a bond to break, not a barrier to be crossed.The test runs after the closure ladder and before the bonds are formed, which is
where the question is unambiguous: the loop is down to nearly its final size, so what
is inside it is what the triazine will enclose. It treats the loop as a triangle fan
from its centroid rather than as a plane, and images every coordinate against an
anchor atom, since a centroid of raw wrapped positions gives false positives across a
periodic boundary.ring_cure.prefilter_threaded_candidatesapplies the same test at candidate time as
well, to avoid spending a ladder on a ring that is already threaded. It is off by
default, because a candidate loop is three groups a search radius apart: a formed
triazine's circumscribed radius is about 1.37 A and near enough rigid across 233 of
them, while the candidate loop encloses roughly nine times that area at a 1.0 nm
search radius and over twenty at 1.6 nm, which is where late iterations run.
Threading would carry through the shrink if nothing moved, but the ladder runs six
stages of NVT at 600 K, so a bond inside the candidate loop need not still be inside
at closure. Setring_cure.reject_threaded_rings: falseto reproduce the old
behavior entirely.Measured across three arms, same image, no mdp override. Filtered builds came out
clean 6 of 6; unfiltered builds carried a pierced triazine in 2 of 4 --- 4, 2, 0 and 0
piercings, a mean of 1.5 each. There is no conversion cost: 0.971 to 0.983 in 21 to
23 iterations against 0.971 to 0.979 in 20 to 22 without it.The closure-time test declined 2, 10 and 1 candidates across three builds, a mean of
4.3 against those 1.5 piercings. Same order of magnitude with overlapping
distributions, and that is as much as the data supports: at three builds against four
the smallest two-sided Mann-Whitney p obtainable is 0.057, so the observed 0.47 is no
more evidence of agreement than of excess. The two are not the same quantity either
--- a declined candidate is one that would have threaded, and the alternative
select_disjointtakes instead may itself thread and be declined in turn, so declines
bound the piercings prevented rather than counting them.The candidate-time prefilter is a different matter: it discarded 1216 to 4385
candidates per build to catch what the closure test catches in single digits, and both
arms finished equally clean --- which is why it stays off by default.
Fixed
-
A ring cure ran at constant volume from first ring to last, so it could not
densify. Every ring-cure stage was NVT — the relaxation after each iteration, the
closure ladder, and the settle — with no constant-pressure step anywhere. Measured
over 22 iterations of a 233-triazine build, the box stayed at 5.241 nm to four
decimals and the density at 1155.9 kg/m3 from the first ring to the last, with the
entire volume change deferred to a single postcure NPT. CURE's density climbs
through its cure, roughly 1085 to 1170, because its relaxation stages run NPT above
Tg and are deliberately "the only above-Tg constant-pressure time in a cure".Cure shrinkage cannot be reproduced at fixed volume by construction — Snow measures
0.0324 ml/g for BADCy — and a network that forms in a box which cannot respond
accumulates internal stress with nowhere to put it, which is a plausible route to the
stretched bonds recorded inROADMAP.md.ring_cure.relaxnow mirrors CURE'srelax.equilibrationexactly --- minimization,
NVT at 600 K, then NPT at 600 K and 1 bar --- rather than CURE's much longer
per-iteration equilibration. The whole sequence is above Tg, not just the
constant-pressure stage: relaxing at 300 K in between two 600 K closure ladders cooled
the network and would have put a cold, less mobile configuration under the barostat,
which is a good way to measure no densification and wrongly conclude the stage does
not work. The closure ladder itself stays constant-volume on purpose: NPT while
restraints are actively pulling groups together is not a stable combination. -
A ring cure had no cold equilibration, so once it could move, its box only grew.
Adding the constant-pressure relax stage above let the box respond, butrelaxends
hot. Measured with relax alone over a full build, the box expands thermally for six
iterations to +27.9% in volume, then comes back down as the network forms: peak to
minimum is -7.49% in volume and +71.9 kg/m3. The two legs nearly cancel, so the net
is near zero and the path length is 0.4104 nm --- a frozen box captures none of it.That descending leg is not a measurement of cure shrinkage: 2 ps at 600 K does not
equilibrate, so it mixes in residual thermal relaxation, and it spans conversion 0.55
to 0.971. It costs iterations too ---search_radiusis an absolute length and does
not scale with the box, so an expanded box encloses fewer groups per sphere, and the
hot-only arm needed 25 iterations against 22 for the same conversion. A cure is not only shrinkage --- the hot stages enlarge
the box and a cold stage pulls it back, and on the pairwise route the box travels
three to nine times further in length than it ends up moving.ring_curenow has anequilibratestage identical in shape and in defaults to
CURE.equilibrate--- NPT, 300 K, 1 bar, 50000 steps of 2 fs --- run once per
iteration afterrelax, through the same code path the pairwise cure uses, so it
picks up that route's box logging and density series rather than reimplementing them.
The per-iteration shape of the two cures is now the same.It is also the expensive stage, roughly 99 s against a 280 s iteration. Whether
100 ps is the right number for either route has never been measured;ROADMAP.md
sketches the study. -
Three
ring_curesettings could not be set at all, because they were missing from
the schema, and ycleptic rejects an unknown key outright rather than ignoring it.
ring_cure.relaxandring_cure.closure.equilibrationwere never exposed, and
closure.max_accept— added in 2.11.1 — was written intoCURE'sdragblock by
mistake, where a config could set it and nothing would read it. All three now sit
where they belong, and a test asserts that every key the controller honors appears in
the schema with a matching default.
Changed
-
The installation page now says not to install
htpolynetfrom conda-forge. The
package published there is several releases behind and cannot currently be updated:
htpolynetdepends onycleptic, which is not yet available on conda-forge, so the
feedstock's automatic version bumps produce a package that cannot import and none has
been published. What is on conda-forge does install and run, so nothing warns a user
that they have an oldhtpolynet.The conda-only recipe now installs Python and the MD binaries from conda-forge and
htpolynetitself from PyPI. The warning is scoped to thehtpolynetpackage alone
--- conda-forge remains the recommended source for AmberTools and Gromacs --- and it
gives the two commands to check the current state rather than asking the reader to
trust a page that may itself be stale. The container remains the recommendation, and
the page now says outright that it requires no installation at all.
Added
- A build now says so when it hands on a bond that is not a bond any more. At the
end of a ring cure and again in the final data, any bond longer than 0.2 nm is
reported, the worst nam...
Release v2.11.3
Fixed
-
A ring cure handed its last batch of rings to postcure unrelaxed. Every batch
but the last is relaxed twice: once by the per-iteration relax as soon as its bonds
exist, and again by the next iteration's closure ladder, which is eight
minimizations and 16 ps at 600 K against the relax's one minimization and 4 ps at
300 K. The final batch only ever gets the first, and then goes straight into a
500 K anneal.htpolynet-study measured it on three 2.11.1 builds, all of which had ended on a
ring-forming iteration and had therefore been relaxed. Bond energy entering the
anneal tracked how many rings that last iteration formed -- 1, 1 and 4 rings for
18849, 26113 and 35959 kJ/mol, against 13619 +/- 226 for a route that enters already
relaxed. An inventory of one such structure found five bonds beyond 2.0 A: one
triazine's three ring bonds at 2.24 A, and two monomers whose own backbones the
ladder had stretched to 2.34 and 3.20 A while pulling their arms toward different
rings. Eleven of twenty builds died in the anneal.The cure now runs a minimization and a short 300 K NVT once on exit, mirroring what
the postcure repair stage already did for the route that never saw this. It is
unconditional -- the cure has four ways to end and which of them leaves strain behind
is not worth reasoning about per exit -- and configurable asring_cure.settle.Confirmed paired, same eight velocity seeds against the same two structures on the
same hardware: two anneals exploded without the settle and none with it. The two
came from different source structures, one of which had never failed in production,
so the effect is not particular to a pathological build, and nothing exploded during
the settle itself -- the minimization absorbs the strain rather than the failure
moving earlier. Eight seeds is a small sample and that comparison alone would be
weak; it is corroboration of the bond energies above, not the case on its own.
Release v2.11.2
Fixed
-
A ring cure near its target kept re-proposing the triples it had just declined.
Declining a ring that the closure ladder could not pull shut leaves its three groups
unreacted, which is the point -- but the next search then offered the same groups,
ran the same eight-stage ladder, and declined them again. htpolynet-study measured
the cost on a 2.11.1 build: at conversion 0.97 with 24 groups left, nine iterations
running produced 77 declines and not one ring, at about 67 s each.A declined combination is now passed over while any untried ring is within the search
radius. It is a preference and not a prohibition: that cure escaped on iteration 41
and finished at 0.971, because the relaxation between iterations moves the groups, so
a triple that failed once can still close later. When the memory is the only thing
left keeping an iteration idle, the search lifts it and offers those rings again
rather than stand still.
Release v2.11.1
Fixed
-
A cyclotrimerization's charges were computed on a ring that was never closed. The
trimer template reached antechamber with its three ring bonds recorded but still about
2.2 A long: the closure ladder cannot beat the reactants' own C#N, whose sp parameters
hold each group linear, and no force constant wins that. Charges come from the
geometry, so the semiempirical step charged three separate cyanates rather than a
triazine, leaving every ring carbon about 0.21 e low with the deficit smeared over
the rest of the molecule.Nothing caught it. The atom types were right, because antechamber perceives those from
the connectivity, which was right; the molecules were neutral, because the error sums
to zero; and the system's own ring bonds relaxed to 1.347 A after the splice, because
the bonded parameters were fine. It surfaced only when htpolynet-study ran a campaign
withbcccharges and half their builds died in a 500 K anneal -- the release smoke
test had usedgascharges, which are computed from the topology and could not have
seen it.A product whose reaction closes a ring is now relaxed against its own connectivity
before it is parameterized, and refused if the ring is still open afterward.
Measured on a methyl cyanate trimer, ring-carbon charge: +0.699 before, +0.909
after, against +0.909 for the same molecule built independently from SMILES.
htpolynet-study measured the same 0.21 e deficit on a bisphenol A dicyanate trimer.Two things fell out of the investigation that are worth knowing:
- Correcting the bond orders does not fix it. An addition bond leaves the cyanate
carbon pentavalent -- it keeps its C#N, gains the ring bond, and still carries its
ester oxygen -- and that is now repaired too, since RDKit cannot read a molecule with
an impossible valence. But repairing the orders alone produced charges identical to
the broken ones, because the semiempirical step reads coordinates, not bond orders. - antechamber's output geometry is its input geometry. It is not evidence about
what the charge calculation did or did not relax, and it should not be read as such.
- Correcting the bond orders does not fix it. An addition bond leaves the cyanate
-
A ring the closure ladder could not pull shut is now declined rather than bonded
long. Late in a cure the matrix is rigid enough that a ring sometimes stops well
short -- one batch at conversion 0.95 ended 0.327 nm apart against a target of 0.150 --
and forming the bond anyway leaves it long for good: two ring bonds survived relaxation
and a 500 K anneal still 2.6 A apart, strained but stable, a defect the network then
carries permanently. Declining costs almost nothing, since the groups stay unreacted
and are offered again next iteration once the neighbourhood has moved. The limit is
ring_cure: closure: max_accept, default 0.30 nm; 0 restores the old behavior. Only
visible now because the charge fix let a build reach conversion 0.95 for the first
time. -
Topology.rebalance_mol2_bond_ordersreduces a multiple bond when an addition would
otherwise leave an atom over-valent, preferring a bond that is over-valent at both ends
because reducing it repairs two atoms at once.