Skip to content

HaConfig: added art_initiation_rate#356

Open
CarelPretorius wants to merge 1 commit into
mainfrom
LFG_Development4
Open

HaConfig: added art_initiation_rate#356
CarelPretorius wants to merge 1 commit into
mainfrom
LFG_Development4

Conversation

@CarelPretorius

Copy link
Copy Markdown
Collaborator

Adding functionality to calc ART initiation directly from a input rate

HaConfig: art_initiation_rate
adult_sim: added code to use art initiation rate to calc new art initiation
goals_sim: : added code to use art initiation rate to calc new art initiation
HvConfig: renamed an input parameter

adult_sim: added code to use art initiation rate to calc new art initiation
goals_sim: : added code to use art initiation rate to calc new art initiation
HvConfig: renamed an input parameter
if constexpr (ModelVariant::run_goals) {
if(t > pars.hv.goals_base_year_idx){
temp_art_adult_dropout = -std::log(1.0 - pars.hv.art_interupt_rate(t) *
temp_art_adult_dropout = -std::log(1.0 - pars.hv.art_interrupt_rate(t) *

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed the name tpo: art_interrupt_rate

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the conversion of the interruption rate from a percent to a per-capita rate via the log call needed here? Leapfrog might already handle that as a preprocessing step.

Comment on lines -555 to +575
i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;
if (pars.hv.art_cov_num_percent[0] == ART_INIT_RATE) {
real_type NewInf = 0.0;
real_type PLHIV = 0.0;
real_type OnART = 0.0;
for (int ha = 0; ha < hAG; ++ha) {
const int a = ha + SS::p_idx_hiv_first_adult;
NewInf += c_ha.p_infections(a, s);
PLHIV += c_ha.p_hivpop(a, s);
for (int hm = i_ha.everARTelig_idx; hm < hDS; ++hm) {
for (int hu = 0; hu < hTS; ++hu) {
OnART += c_ha.h_artpop(hu, hm, ha, s);
}
}
}

i_ha.artinit_hts = std::max( opts.dt * pars.ha.art_initiation_rate(s,t) * (NewInf + PLHIV - OnART), 0.0);
}
else{

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculating ART initiation rate directly from input. The previous calc of new ART is in the second block of the iff statement. if (pars.hv.art_cov_num_percent[0] == ART_INIT_RATE) . A SS variable can be created for int ART_INIT_RATE = 5;

Comment on lines +221 to +225
},

"art_initiation_rate": {
"num_type": "real_type",
"dims": ["SS::NS", "opts.proj_steps"]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this input var

},

"art_interupt_rate": {
"art_interrupt_rate": {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected the var name

Comment on lines +2956 to +2967

if( pars.hv.art_cov_num_percent[0] == ART_INIT_RATE ){

real_type NewInf = c_hv.new_inf_vrs(v, rg, s);
real_type PLHIV = 0.0;
real_type OnART = 0.0;
for (int hd = CD4_GT500; hd <= CD4_LT50; ++hd) {
PLHIV += c_hv.adults(v, rg, hd, s);
OnART += c_hv.adults(v, rg, hd + hOnArt, s);
}

start_art[v][rg][s] = std::max( opts.dt * pars.ha.art_initiation_rate(s,t) * (NewInf + PLHIV - OnART), 0.0);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculating new ART directly from an input rate, instead of from a coverage

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need to tally then deduct OnART here? It looks like PLHIV might already include just people not on ART.

@r-ash r-ash self-requested a review July 9, 2026 10:10
for (int s = 0; s < NS; ++s) {
i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this static constexpr, this will ensure that ART_INIT_RATE is created at compile time and always refers to the same variable (no allocation within the loop)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should have a chat about this though, is there any reason we keep this as value 5? We have 3 cases?

  1. Passed in art cov as number
  2. Passed in art cov as %
  3. Passed in art initiation rate

Can we mix between art initiation rate per year?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could also use a real enum here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in slack message: should work for now but a more general implementation can follow

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ART_INIT_RATE = 5 is an artifact from the Delphi implementation that exists because of the various ways adult ART uptake can be specified in Spectrum and the chronological order in which those were added to the software. It would be best to hide or abstract these artifacts from Leapfrog, but I defer to folks who know Leapfrog better what approach makes the most sense.

Comment on lines +559 to +570
real_type NewInf = 0.0;
real_type PLHIV = 0.0;
real_type OnART = 0.0;
for (int ha = 0; ha < hAG; ++ha) {
const int a = ha + SS::p_idx_hiv_first_adult;
NewInf += c_ha.p_infections(a, s);
PLHIV += c_ha.p_hivpop(a, s);
for (int hm = i_ha.everARTelig_idx; hm < hDS; ++hm) {
for (int hu = 0; hu < hTS; ++hu) {
OnART += c_ha.h_artpop(hu, hm, ha, s);
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we fix the indentation here please.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;
if (pars.hv.art_cov_num_percent[0] == ART_INIT_RATE) {

@r-ash r-ash Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should change this name, it is misleading. In fact this won't work to refer to some data from the goals model here in the adult model sim.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass this in with length 5 but then only ever use the 0th index of it? What is this input?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has 5 potential values all in [0] since it comes from a radio button control group. This works for now but we can also change the input for to make it less confusing

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

happy to change the name pars.hv.art_cov_num_percent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Rob here, the art_cov_num_percent name didn't anticipate all the various ways we can specify ART coverage, so it seems best to make the name more general, e.g., art_entry_option.

Comment on lines +945 to 948
for (int ha = 0; ha < hAG; ++ha) {

const int a = ha + p_idx_hiv_first_adult;
const int a = ha + SS::p_idx_hiv_first_adult;
//adults, plhiv not on art

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this an intended change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@r-ash r-ash left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is getting a bit big, I wonder if we try and refactor it to make it a bit easier to read, extract the if/else block into 2 helper functions computeArtInitFromRate(..) and computeArtInitFromCoverage(..) or similar?

i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;
if (pars.hv.art_cov_num_percent[0] == ART_INIT_RATE) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass this in with length 5 but then only ever use the 0th index of it? What is this input?

for (int s = 0; s < NS; ++s) {
i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could also use a real enum here

@r-ash r-ash requested review from jeffeaton and rlglaubius July 10, 2026 14:05

@rlglaubius rlglaubius left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a few comments and questions on some of the open discussions here.

Comment on lines +2956 to +2967

if( pars.hv.art_cov_num_percent[0] == ART_INIT_RATE ){

real_type NewInf = c_hv.new_inf_vrs(v, rg, s);
real_type PLHIV = 0.0;
real_type OnART = 0.0;
for (int hd = CD4_GT500; hd <= CD4_LT50; ++hd) {
PLHIV += c_hv.adults(v, rg, hd, s);
OnART += c_hv.adults(v, rg, hd + hOnArt, s);
}

start_art[v][rg][s] = std::max( opts.dt * pars.ha.art_initiation_rate(s,t) * (NewInf + PLHIV - OnART), 0.0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need to tally then deduct OnART here? It looks like PLHIV might already include just people not on ART.

if constexpr (ModelVariant::run_goals) {
if(t > pars.hv.goals_base_year_idx){
temp_art_adult_dropout = -std::log(1.0 - pars.hv.art_interupt_rate(t) *
temp_art_adult_dropout = -std::log(1.0 - pars.hv.art_interrupt_rate(t) *

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the conversion of the interruption rate from a percent to a per-capita rate via the log call needed here? Leapfrog might already handle that as a preprocessing step.

for (int s = 0; s < NS; ++s) {
i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ART_INIT_RATE = 5 is an artifact from the Delphi implementation that exists because of the various ways adult ART uptake can be specified in Spectrum and the chronological order in which those were added to the software. It would be best to hide or abstract these artifacts from Leapfrog, but I defer to folks who know Leapfrog better what approach makes the most sense.

i_ha.Xart_15plus = 0.0;

int ART_INIT_RATE = 5;
if (pars.hv.art_cov_num_percent[0] == ART_INIT_RATE) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Rob here, the art_cov_num_percent name didn't anticipate all the various ways we can specify ART coverage, so it seems best to make the name more general, e.g., art_entry_option.

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.

3 participants