Skip to content

Commit

Permalink
Fixes #2596 implement hepatic impairement disease sate
Browse files Browse the repository at this point in the history
  • Loading branch information
msevestre committed May 4, 2023
1 parent a72afca commit 98555fa
Show file tree
Hide file tree
Showing 11 changed files with 427 additions and 372 deletions.
2 changes: 1 addition & 1 deletion src/PKSim.Assets/PKSimConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2598,7 +2598,7 @@ public static string UserTemplateDatabaseDatabaseUsedOldFormatAndCannotBeLoaded(

public static string LinkedExpressionProfileIs(string expressionProfileName) => $"Using expression profile <b>{expressionProfileName}</b>";

public static string ChildPughScoreFor (string score)=> $"Hepatic Impairment – Child-Pugh {score}";
public static string ChildPughScoreFor (string score)=> $"Child-Pugh {score}";
}

public static class Reporting
Expand Down
3 changes: 0 additions & 3 deletions src/PKSim.Core/Mappers/ParameterListOfValuesRetriever.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using OSPSuite.Core.Domain;
using OSPSuite.Utility.Collections;
using OSPSuite.Utility.Extensions;
using OSPSuite.Utility.Format;
using PKSim.Assets;
using PKSim.Core.Model;
using PKSim.Core.Services;
using static OSPSuite.Core.Domain.Constants.Parameters;
Expand Down Expand Up @@ -38,7 +36,6 @@ public ParameterListOfValuesRetriever(HashSet<string> parameterWithListOfValues)
//TODO MOVE TO CORE
_parameterWithListOfValues.Add(HIDiseaseStateImplementation.CHILD_PUGH_SCORE);
_formatter = new NumericFormatter<double>(NumericFormatterOptions.Instance);

}

public ICache<double, string> ListOfValuesFor(IParameter parameter)
Expand Down
48 changes: 25 additions & 23 deletions src/PKSim.Core/Model/RandomPopulationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,31 +249,35 @@ private void perturbate(Individual currentIndividual, RandomPopulationSettings p
numberOfTry++;

//first create a new age value if necessary
double value;
if (originData.Population.IsAgeDependent)
{
originData.Age = new OriginDataParameter(createRandomValueFor(originData, populationSettings, CoreConstants.Parameters.AGE, randomGenerator, out success));
(value, success) = createRandomValueFor(originData, populationSettings, CoreConstants.Parameters.AGE, randomGenerator);
originData.Age = new OriginDataParameter(value);
currentIndividual.Organism.Parameter(CoreConstants.Parameters.AGE).Value = originData.Age.Value;
if (!success) continue;
}

if (originData.Population.IsPreterm)
{
originData.GestationalAge = new OriginDataParameter(createDiscreteRandomValueFor(populationSettings, Constants.Parameters.GESTATIONAL_AGE, randomGenerator, out success));
(value, success) = createDiscreteRandomValueFor(populationSettings, Constants.Parameters.GESTATIONAL_AGE, randomGenerator);
originData.GestationalAge = new OriginDataParameter(value);
currentIndividual.Organism.Parameter(Constants.Parameters.GESTATIONAL_AGE).Value = originData.GestationalAge.Value;
if (!success) continue;
}

//Then define gender depending on selecting proportions
if (originData.Population.IsHeightDependent)
{
originData.Height = new OriginDataParameter(createRandomValueFor(originData, populationSettings, CoreConstants.Parameters.MEAN_HEIGHT, randomGenerator, out success));
(value, success) = createRandomValueFor(originData, populationSettings, CoreConstants.Parameters.MEAN_HEIGHT, randomGenerator);
originData.Height = new OriginDataParameter(value);
currentIndividual.Organism.Parameter(CoreConstants.Parameters.HEIGHT).Value = originData.Height.Value;
}

//now assign any random parameters due to disease state
foreach (var diseaseStateParameter in originData.DiseaseStateParameters)
{
diseaseStateParameter.Value = createDiseaseStateRandomParameterValueFor(originData, populationSettings, diseaseStateParameter, randomGenerator, out success);
(diseaseStateParameter.Value, success) = createDiseaseStateRandomParameterValueFor(originData, populationSettings, diseaseStateParameter, randomGenerator);
}

if (!success) continue;
Expand All @@ -284,51 +288,49 @@ private void perturbate(Individual currentIndividual, RandomPopulationSettings p
throw new CannotCreatePopulationWithConstraintsException(_reportGenerator.StringReportFor(populationSettings));
}

private double createRandomValueFor(OriginData originData, RandomPopulationSettings populationSettings, string parameterName, RandomGenerator randomGenerator, out bool success)
private (double value, bool success) createRandomValueFor(OriginData originData, RandomPopulationSettings populationSettings, string parameterName, RandomGenerator randomGenerator)
{
var parameterRange = populationSettings.ParameterRange(parameterName);
var parameter = _individualModelTask.MeanOrganismParameter(originData, parameterName);
return tryCreateRandomValueFor(parameterRange, parameter, randomGenerator, out success);
return tryCreateRandomValueFor(parameterRange, parameter, randomGenerator);
}

private double createDiseaseStateRandomParameterValueFor(OriginData originData, RandomPopulationSettings populationSettings, OriginDataParameter diseaseStateParameter, RandomGenerator randomGenerator, out bool success)
private (double value, bool success) createDiseaseStateRandomParameterValueFor(OriginData originData, RandomPopulationSettings populationSettings, OriginDataParameter diseaseStateParameter, RandomGenerator randomGenerator)
{
var parameterRange = populationSettings.ParameterRange(diseaseStateParameter.Name);
var parameter = originData.DiseaseState.Parameter(diseaseStateParameter.Name);
return tryCreateRandomValueFor(parameterRange, parameter, randomGenerator, out success);
var parameterRange = populationSettings.ParameterRange(diseaseStateParameter.Name);
//possible for categorial parameters that the parameter range is not defined
if (parameterRange != null)
return tryCreateRandomValueFor(parameterRange, parameter, randomGenerator);

return (parameter.Value, true);
}

private double createDiscreteRandomValueFor(RandomPopulationSettings populationSettings, string parameterName, RandomGenerator randomGenerator, out bool success)
private (double value, bool success) createDiscreteRandomValueFor(RandomPopulationSettings populationSettings, string parameterName, RandomGenerator randomGenerator)
{
var parameterRange = populationSettings.ParameterRange(parameterName) as DiscreteParameterRange;
success = true;

if (parameterRange == null)
{
success = false;
return 0;
}
return (0, false);

if (parameterRange.IsConstant)
return parameterRange.MinValue.Value;
return (parameterRange.MinValue.Value, true);

return randomGenerator.NextInteger(parameterRange.MinValue.ConvertedTo<int>(), parameterRange.MaxValue.ConvertedTo<int>());
return (randomGenerator.NextInteger(parameterRange.MinValue.ConvertedTo<int>(), parameterRange.MaxValue.ConvertedTo<int>()), true);
}

private double tryCreateRandomValueFor(ParameterRange parameterRange, IParameter baseParameter, RandomGenerator randomGenerator, out bool success)
private (double value, bool success) tryCreateRandomValueFor(ParameterRange parameterRange, IParameter baseParameter, RandomGenerator randomGenerator)
{
try
{
success = true;
if (parameterRange.IsConstant)
return parameterRange.MinValue.Value;
return (parameterRange.MinValue.Value, success: true);

return baseParameter.RandomDeviateIn(randomGenerator, parameterRange.MinValue, parameterRange.MaxValue);
return (baseParameter.RandomDeviateIn(randomGenerator, parameterRange.MinValue, parameterRange.MaxValue), success: true);
}
catch (DistributionException)
{
success = false;
return baseParameter.Value;
return (baseParameter.Value, success: false);
}
}
}
Expand Down
136 changes: 22 additions & 114 deletions src/PKSim.Core/Services/CKDDiseaseStateImplementation.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OSPSuite.Core.Domain;
using OSPSuite.Core.Domain.Formulas;
using OSPSuite.Core.Domain.Services;
using OSPSuite.Core.Domain.UnitSystem;
using OSPSuite.Utility.Exceptions;
using OSPSuite.Utility.Extensions;
using PKSim.Assets;
using PKSim.Core.Model;
using PKSim.Core.Repositories;
Expand All @@ -17,7 +13,7 @@

namespace PKSim.Core.Services
{
public class CKDDiseaseStateImplementation : IDiseaseStateImplementation
public class CKDDiseaseStateImplementation : AbstractDiseaseStateImplementation
{
private enum CKDStage
{
Expand All @@ -27,35 +23,25 @@ private enum CKDStage
}

private readonly IValueOriginRepository _valueOriginRepository;
private readonly IFormulaFactory _formulaFactory;
private readonly IIndividualFactory _individualFactory;
private readonly IContainerTask _containerTask;
private readonly IParameterSetUpdater _parameterSetUpdater;
private readonly IDimension _dimensionForGFR;
public const string TARGET_GFR = "eGFR";
private readonly IDimension _ageDimension;
private const int CKD_VALUE_ORIGIN_ID = 92;
public const string GFR_UNIT = "ml/min/1.73m²";

public CKDDiseaseStateImplementation(
IValueOriginRepository valueOriginRepository,
IDimensionRepository dimensionRepository,
IValueOriginRepository valueOriginRepository,
IFormulaFactory formulaFactory,
IIndividualFactory individualFactory,
IContainerTask containerTask,
IParameterSetUpdater parameterSetUpdater)
IParameterSetUpdater parameterSetUpdater) : base(valueOriginRepository, formulaFactory, individualFactory, containerTask, parameterSetUpdater, CoreConstants.DiseaseStates.CKD)
{
_valueOriginRepository = valueOriginRepository;
_formulaFactory = formulaFactory;
_individualFactory = individualFactory;
_containerTask = containerTask;
_parameterSetUpdater = parameterSetUpdater;
_dimensionForGFR = dimensionRepository.DimensionForUnit(GFR_UNIT);
_ageDimension = dimensionRepository.AgeInYears;
}

public bool IsSatisfiedBy(DiseaseState diseaseState) => diseaseState.IsNamed(CoreConstants.DiseaseStates.CKD);

private (
IParameter hct,
IParameter GFR_spec,
Expand Down Expand Up @@ -84,18 +70,11 @@ IParameter gastricEmptyingTime
return (hct, GFR_spec, kidneyVolume, kidneySpecificBloodFlowRate, fatVolume, plasmaProteinScaleFactorParameter, smallIntestinalTransitTime, gastricEmptyingTime);
}

private IReadOnlyList<IParameter> parametersChangedByCKDAlgorithmAsList(Individual individual)
{
var p = parametersChangedByCKDAlgorithm(individual);

return new[] {p.hct, p.GFR_spec, p.kidneyVolume, p.kidneySpecificBloodFlowRate, p.fatVolume, p.plasmaProteinScaleFactorParameter, p.smallIntestinalTransitTime, p.gastricEmptyingTime};
}
public override bool ApplyTo(Individual individual) => applyTo(individual, UpdateParameter(CKD_VALUE_ORIGIN_ID));

public bool ApplyTo(Individual individual) => applyTo(individual, updateParameter);
public override bool ApplyForPopulationTo(Individual individual) => applyTo(individual, UpdateParameterValue);

public bool ApplyForPopulationTo(Individual individual) => applyTo(individual, updateParameterValues);

private bool applyTo(Individual individual, Action<IParameter, double> updateParameterFunc)
private bool applyTo(Individual individual, Action<IParameter, double, bool> updateParameterFunc)
{
var targetGFR = individual.OriginData.DiseaseStateParameters.FindByName(TARGET_GFR);

Expand All @@ -114,108 +93,31 @@ private bool applyTo(Individual individual, Action<IParameter, double> updatePar

//Adjust kidney volume and update fat accordingly
var healthyKidneyVolume = kidneyVolume.Value;
updateParameterFunc(kidneyVolume, getKidneyVolumeFactor(targetGFRValue) / getKidneyVolumeFactor(GFR_0) * healthyKidneyVolume);
updateParameterFunc(fatVolume, fatVolume.Value + healthyKidneyVolume - kidneyVolume.Value);
updateParameterFunc(kidneyVolume, getKidneyVolumeFactor(targetGFRValue) / getKidneyVolumeFactor(GFR_0), true);
updateParameterFunc(fatVolume, fatVolume.Value + healthyKidneyVolume - kidneyVolume.Value, false);

//Adjust renal blood flow spec
updateParameterFunc(kidneySpecificBloodFlowRate, getRenalBloodFlowFactor(targetGFRValue) / getRenalBloodFlowFactor(GFR_0) * kidneySpecificBloodFlowRate.Value);
updateParameterFunc(kidneySpecificBloodFlowRate, getRenalBloodFlowFactor(targetGFRValue) / getRenalBloodFlowFactor(GFR_0), true);

//Correct specific GFR
updateParameterFunc(GFR_Spec, GFR_Spec.Value * targetGFRValue / GFR_0 * healthyKidneyVolume / kidneyVolume.Value);
updateParameterFunc(GFR_Spec, targetGFRValue / GFR_0 * healthyKidneyVolume / kidneyVolume.Value, true);

var (plasmaProteinScaleFactor, gastricEmptyingTimeFactor, smallIntestinalTransitTimeFactor) = getCategorialFactors(targetGFRValue);

//Categorial Parameters as constant: We set the value as is as the value will not be reset when creating a population
updateParameterFunc(plasmaProteinScaleFactorParameter, plasmaProteinScaleFactor);
updateParameterFunc(plasmaProteinScaleFactorParameter, plasmaProteinScaleFactor, false);

//Categorial Parameters distributed: We apply the variation to the default value
updateParameterFunc(gastricEmptyingTime, gastricEmptyingTime.Value * gastricEmptyingTimeFactor);
updateParameterFunc(smallIntestinalTransitTime, smallIntestinalTransitTime.Value * smallIntestinalTransitTimeFactor);
updateParameterFunc(gastricEmptyingTime, gastricEmptyingTimeFactor, true);
updateParameterFunc(smallIntestinalTransitTime, smallIntestinalTransitTimeFactor, true);

//Special case for Hematocrit
updateParameterFunc(hct, hct.Value * getHematocritFactor(targetGFRValue, individual.OriginData.Gender));
updateParameterFunc(hct, getHematocritFactor(targetGFRValue, individual.OriginData.Gender), true);

//no parameters to lock for now
lockParametersAfterCKDImplementation();
return true;
}

private void lockParametersAfterCKDImplementation(params IParameter[] parameters)
{
parameters.Each(x => x.Editable = false);
}

private void updateParameterValues(IParameter parameter, double value)
{
parameter.Value = value;
}

private void updateParameter(IParameter parameter, double value)
{
updateValueOriginsFor(parameter);
if (parameter is IDistributedParameter distributedParameter)
{
distributedParameter.ScaleDistributionBasedOn(value / distributedParameter.Value);
return;
}

//We are using a formula, we override with a constant
if (parameter.Formula.IsExplicit())
{
parameter.Formula = _formulaFactory.ConstantFormula(value, parameter.Dimension);
return;
}

//constant formula
updateParameterValues(parameter, value);
parameter.DefaultValue = value;
parameter.IsFixedValue = false;
}

public Individual CreateBaseIndividualForPopulation(Individual originalIndividual)
{
//we need to create a new individual WITHOUT CKD and set all percentiles as in the original individuals. Other parameters, value wil be taken as is
var originData = originalIndividual.OriginData.Clone();

//remove the disease state to create a healthy Individual
originData.DiseaseState = null;
var healthyIndividual = _individualFactory.CreateAndOptimizeFor(originData, originalIndividual.Seed);

var allCKDParameters = parametersChangedByCKDAlgorithmAsList(healthyIndividual);

//Make sure we update the flags that might not be set coming from the database
allCKDParameters.Each(x => x.IsChangedByCreateIndividual = true);

//do not update parameters changed by CKD algorithm or that are not visible
var allHealthyParameters = _containerTask.CacheAllChildrenSatisfying<IParameter>(healthyIndividual, x => !allCKDParameters.Contains(x) && x.Visible);
var allOriginalParameters = _containerTask.CacheAllChildren<IParameter>(originalIndividual);
_parameterSetUpdater.UpdateValues(allOriginalParameters, allHealthyParameters);

//we have a healthy individuals based on the CKD individual where all changes were all manual changes were accounted for
//we now need to add the disease state contributions from the original individual
originData.DiseaseState = originalIndividual.OriginData.DiseaseState;
originalIndividual.OriginData.DiseaseStateParameters.Each(x => originData.AddDiseaseStateParameter(x.Clone()));

return healthyIndividual;
}

public void ResetParametersAfterPopulationIteration(Individual individual)
{
//ensures that formula parameters are reset so that they can be reused in next iteration
var allCKDParameters = parametersChangedByCKDAlgorithmAsList(individual).Where(x => x.IsFixedValue);
allCKDParameters.Each(x => x.ResetToDefault());
}

public void Validate(OriginData originData)
{
var (valid, error) = IsValid(originData);
if (valid)
return;

throw new OSPSuiteException(error);
}

public (bool isValid, string error) IsValid(OriginData originData)
public override (bool isValid, string error) IsValid(OriginData originData)
{
var ageInYears = _ageDimension.BaseUnitValueToUnitValue(_ageDimension.Unit(CoreConstants.Units.Years), originData.Age.Value);
if (ageInYears >= 18)
Expand All @@ -224,11 +126,17 @@ public void Validate(OriginData originData)
return (false, PKSimConstants.Error.CKDOnlyAvailableForAdult);
}

public void ApplyTo(Individual individual, IndividualMolecule individualMolecule)
public override void ApplyTo(Individual individual, IndividualMolecule individualMolecule)
{
//nothing to do here
}

protected override IReadOnlyList<IParameter> ParameterChangedByDiseaseStateAsList(Individual individual)
{
var (hct, gfrSpec, kidneyVolume, kidneySpecificBloodFlowRate, fatVolume, plasmaProteinScaleFactorParameter, smallIntestinalTransitTime, gastricEmptyingTime) = parametersChangedByCKDAlgorithm(individual);
return new[] {hct, gfrSpec, kidneyVolume, kidneySpecificBloodFlowRate, fatVolume, plasmaProteinScaleFactorParameter, smallIntestinalTransitTime, gastricEmptyingTime};
}

private double getHematocritFactor(double targetGFR, Gender gender)
{
var isFemale = gender.IsNamed(CoreConstants.Gender.FEMALE);
Expand Down
Loading

0 comments on commit 98555fa

Please sign in to comment.