Project: s41467-024-49305-x | Function: PGE_residual_update (cpp)

system: You are an expert C++ programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow C++ best practices and maintain consistent code style.
Return **only** the complete C++ function code.

user: Complete this C++ function:

**DESCRIPTION:** Updates mass-constraint residuals for Partitioning Gibbs Energy calculations using the Gibbs-Duhem constraint

**KEY TERMS:** {
  "mass_residual": "mass balance residual",
  "pp_n": "pure phase fraction",
  "p_em": "endmember proportion",
  "z_em": "endmember charge",
  "bulk_rock": "bulk composition",
  "Comp": "phase composition matrix"
}

**CONTEXT CODE:**
	}
	printf("\n");
	printf(" Oxide  |");
	for (int i = 0; i < z_b.nzEl_val; i++){		
		printf(" %11s",gv.ox[z_b.nzEl_array[i]]);
	}
	printf("\n"); 
	printf(" Gamma  |");
	for (int i = 0; i < z_b.nzEl_val; i++){	
		if	(gv.gam_tot[z_b.nzEl_array[i]] <= -1000.0){ 
			printf(" %.5f",gv.gam_tot[z_b.nzEl_array[i]]);
		}
		else{
			printf(" %.6f",gv.gam_tot[z_b.nzEl_array[i]]);
		}
	}
	printf("\n"); 
	printf(" dGamma |");
	for (int i = 0; i < z_b.nzEl_val; i++){	
		printf(" %+11f",gv.dGamma[z_b.nzEl_array[i]]);
	}
	printf("  -> *%.5f",gv.alpha);
	printf("\n\n");
	printf(" [GIBBS SYSTEM (Gibbs-Duhem) %.8f (with mu %.8f)]\n",gv.G_system,gv.G_system_mu);
	printf(" [MASS RESIDUAL NORM  = %+.8f ]\n",gv.BR_norm);
};

/** 
  Ipdate mass-constraint residual
*/
global_variable PGE_residual_update(			bulk_info 				z_b,
												global_variable  		gv,

												PP_ref 					*PP_ref_db,
												SS_ref 					*SS_ref_db,
												csd_phase_set  			*cp
){
	int ss;

	/**  if we are doing linear programming */
	if (gv.LP == 1 && gv.PGE == 0){
		
		for (int j = 0; j < gv.len_ox; j++){
		gv.mass_residual[j] = -z_b.bulk_rock[j];
			for (int i = 0; i < gv.len_pp; i++){
				if (gv.pp_flags[i][1] == 1){
					gv.mass_residual[j] += PP_ref_db[i].Comp[j]*PP_ref_db[i].factor*gv.pp_n[i];
				}
			}	

			/** calculate residual as function of endmember fractions */
			for (int i = 0; i < gv.len_cp; i++){
				if (cp[i].ss_flags[1] == 1 ){
					ss = cp[i].id;
					for (int k = 0; k < cp[i].n_em; k++){
						gv.mass_residual[j] += SS_ref_db[ss].Comp[k][j]*cp[i].factor*cp[i].p_em[k]*SS_ref_db[ss].z_em[k]*cp[i].ss_n;
					}
				}
			}
		}	
	}
	
	if(gv.LP == 0 && gv.PGE == 1){
		for (int j = 0; j < gv.len_ox; j++){
		gv.mass_residual[j] = -z_b.bulk_rock[j];
			for (int i = 0; i < gv.len_pp; i++){
				if (gv.pp_flags[i][1] == 1){
					gv.mass_residual[j] += PP_ref_db[i].Comp[j]*PP_ref_db[i].factor*gv.pp_n[i];
				}
			}	

			/** calculate residual as function xi fraction and not endmember fractions from x-eos */
			for (int i = 0; i < gv.len_cp; i++){
				if (cp[i].ss_flags[1] == 1 ){
					ss = cp[i].id;
					for (int k = 0; k < cp[i].n_em; k++){
						gv.mass_residual[j] += SS_ref_db[ss].Comp[k][j]*cp[i].factor*cp[i].p_em[k]*cp[i].xi_em[k]*SS_ref_db[ss].z_em[k]*cp[i].ss_n;
					}
				}
			}
		}
	}

	gv.BR_norm    = norm_vector(	gv.mass_residual,
									z_b.nzEl_val				);

	/* Calculate G-system */
	gv.G_system = 0.0;
	for (int j = 0; j < gv.len_ox; j++){ gv.G_system += z_b.bulk_rock[j]*gv.gam_tot[j]; }
	
	gv.G_system_mu = gv.G_system;
	for (int i = 0; i < gv.len_cp; i++){
		if (cp[i].ss_flags[1] == 1){
			for (int j = 0; j < cp[i].n_em; j++){
				gv.G_system_mu +=  cp[i].ss_n*cp[i].p_em[j]*cp[i].mu[j]*cp[i].factor;
			}
		}
	}
	for (int i = 0; i < gv.len_pp; i++){
		if (gv.pp_flags[i][1] == 1){
			gv.G_system_mu +=  gv.pp_n[i]*PP_ref_db[i].gb_lvl*PP_ref_db[i].factor;
		}
	}
	gv.gibbs_ev[gv.global_ite] = gv.G_system;

   return gv;
};

/** 
  Function to update chemical potential of endmembers (mui)
*/
global_variable PGE_update_mu(		bulk_info 	z_b,
									global_variable  	gv,

									PP_ref 				*PP_ref_db,
									SS_ref 				*SS_ref_db,
									csd_phase_set  		*cp
){
	int 	ss;
	double  cv;
	for (int i = 0; i < gv.len_cp; i++){
		if (cp[i].ss_flags[0] == 1){
			ss  = cp[i].id;

			/** rotate gbase with respect to the G-hyperplane (change of base) */
			for (int k = 0; k < cp[i].n_em; k++) {
				cp[i].delta_mu[k] = 0.0;
				for (int j = 0; j < gv.len_ox; j++) {
					cp[i].delta_mu[k] 	-= SS_ref_db[ss].Comp[k][j]*gv.delta_gam_tot[j];
				}
				cp[i].mu[k] += cp[i].delta_mu[k];
				cp[i].df 	+= cp[i].delta_mu[k]*cp[i].p_em[k];
			}
		}
	}

   return gv;
};

/** 
  Partitioning Gibbs Energy function 
*/
global_variable PGE_update_xi(		bulk_info 	z_b,
									global_variable  	gv,

									PP_ref 				*PP_ref_db,
									SS_ref 				*SS_ref_db,
									csd_phase_set  		*cp
){
	int ss;