Skip to content

Fix active.sex and young.prop in the sexual-cessation configuration - #88

Merged
smjenness merged 1 commit into
mainfrom
fix/sex-cessation-active-sex
Aug 22, 2026
Merged

Fix active.sex and young.prop in the sexual-cessation configuration#88
smjenness merged 1 commit into
mainfrom
fix/sex-cessation-active-sex

Conversation

@smjenness

@smjenness smjenness commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Two fixes to the sexual-cessation configuration, where age.limits[2] sits above age.sexual.cessation so the population keeps aging past the point where it stops forming partnerships. Both surfaced while building the CEA-variant model for the CABARET cost-effectiveness paper.

active.sex was keyed on the realized maximum age group. build_netstats() zeroed it on max(attr_age.grp) rather than on the age group the breaks declare. Whenever the post-cessation group came out empty that marked the oldest sexually active group inactive and zeroed its degree on all three layers, with no error and no warning. On a 15-to-100 population with cessation at 65 and young.prop = 1, every 55-to-64 year old was retired instead:

# 15-to-100 population, sexual cessation at 65, on main as it stands
ns <- build_netstats(es, np, network.size = 5000, young.prop = 1)
sum(ns$attr$active.sex == 0)
#> [1] 919
round(range(ns$attr$age[ns$attr$active.sex == 0]), 1)
#> [1] 55 65      # the 55-64 band, which should be the most active older group
sum(ns$attr$age >= 65)
#> [1] 0

The same applied to the target_pop data frame path, so a projected population whose oldest member happens to sit below the cessation age hit it too, which is the more likely route in ARTnetPredict. The band is now read off age.breaks, which build_epistats() guarantees contains age.sexual.cessation.

young.prop = 1 is now rejected rather than silently degenerate. It empties the post-cessation group by construction, which triggered the defect above and then failed much later in netest() on a target.stats length mismatch, because an age group with no members carries no ergm term. Both endpoints now error with a message pointing at a usable value (0.999 seeds about 0.1% of network.size above the cessation age, enough for the level to exist while the group fills over the burn-in). A band that comes out empty for other reasons, a network.size too small for the seeded share or a target_pop with no post-cessation members, warns for the same reason.

Neither change touches the default configuration, where the population upper limit and the cessation age coincide and there is no post-cessation band. young.prop there is still inert and still accepted at any value. Checked directly: on the Atlanta 15-to-65 configuration at 25,000 nodes with a fixed seed, epistats, netparams, the attribute vectors, every numeric target statistic on all three layers, and demog are byte-identical between 2.9.0 and this branch.

tests/testthat/test-sex-cessation.R is new: 37 assertions covering the configuration, the mortality cut moving from 65 to the upper age limit, active.sex agreeing exactly with age >= age.sexual.cessation on both paths, the empty-band regression on the data frame path, and the two endpoint errors. Full suite is 623 passing, R CMD check is clean.

Two defects in build_netstats() when age.limits[2] sits above
age.sexual.cessation, so the population keeps aging past the point where it
stops forming partnerships.

active.sex was zeroed on max(attr_age.grp) as realized rather than on the age
group the breaks declare. Whenever the post-cessation group came out empty
that marked the oldest sexually active group inactive and zeroed its degree on
all three layers, silently. It hit the sampling path and the target_pop data
frame path alike, so a projected population whose oldest member sits below the
cessation age had the same problem.

young.prop = 1 empties the post-cessation group by construction, which
triggered the first defect and then failed much later in netest() on a
target.stats length mismatch, since an age group with no members carries no
ergm term. Both endpoints are now rejected with a message pointing at a usable
value, and a band that comes out empty for other reasons warns instead.

Neither change touches the default configuration, where the population upper
limit and the cessation age coincide and there is no post-cessation band.
@smjenness
smjenness merged commit 79441d1 into main Aug 22, 2026
1 check passed
@smjenness
smjenness deleted the fix/sex-cessation-active-sex branch August 22, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant