Skip to content

Commit

Permalink
2627 no expression profiles created when exporting a simulation from …
Browse files Browse the repository at this point in the history
…pk sim to mobi (#2645)

* Fixes #2627 no expression profiles created

* Fixes #2627 no expression profiles created

* Fixes type
  • Loading branch information
msevestre authored May 30, 2023
1 parent 7d6337f commit ee9a1ee
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/PKSim.Core/Model/SimulationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public Simulation CreateFrom(ISimulationSubject simulationSubject, IReadOnlyList

_simulationBuildingBlockUpdater.UpdateUsedBuildingBlockInSimulationFromTemplate(simulation, simulationSubject, PKSimBuildingBlockType.SimulationSubject);
_simulationBuildingBlockUpdater.UpdateMultipleUsedBuildingBlockInSimulationFromTemplate(simulation, compounds, PKSimBuildingBlockType.Compound);
_simulationBuildingBlockUpdater.UpdateMultipleUsedBuildingBlockInSimulationFromTemplate(simulation, simulationSubject.AllExpressionProfiles(), PKSimBuildingBlockType.ExpressionProfile);

//set basic properties
if (originalSimulation != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
using System.Collections.Generic;
using System.Linq;
using PKSim.Assets;
using OSPSuite.Core.Commands.Core;
using OSPSuite.Core.Domain;
using OSPSuite.Core.Domain.Services;
using OSPSuite.Core.Events;
using OSPSuite.Utility.Collections;
using PKSim.Assets;
using PKSim.Core.Commands;
using PKSim.Core.Model;
using OSPSuite.Core.Domain;
using OSPSuite.Core.Domain.Services;

namespace PKSim.Core.Services
{
public interface IBuildingBlockParametersToSimulationUpdater
{
/// <summary>
/// Updates the parameter values into the building block given as parameter in the simulation
/// Updates the parameter values from the building block given as parameter into the simulation
/// </summary>
/// <param name="templateBuildingBlock">Template building block containing the original values</param>
/// <param name="simulation">Simulation whose parameter will be updated</param>
Expand All @@ -25,20 +26,26 @@ public class BuildingBlockParametersToSimulationUpdater : IBuildingBlockParamete
private readonly IExecutionContext _executionContext;
private readonly IContainerTask _containerTask;
private readonly IParameterSetUpdater _parameterSetUpdater;
private readonly IExpressionProfileUpdater _expressionProfileUpdater;

public BuildingBlockParametersToSimulationUpdater(IExecutionContext executionContext, IContainerTask containerTask, IParameterSetUpdater parameterSetUpdater)
public BuildingBlockParametersToSimulationUpdater(
IExecutionContext executionContext,
IContainerTask containerTask,
IParameterSetUpdater parameterSetUpdater,
IExpressionProfileUpdater expressionProfileUpdater)
{
_executionContext = executionContext;
_containerTask = containerTask;
_parameterSetUpdater = parameterSetUpdater;
_expressionProfileUpdater = expressionProfileUpdater;
}

public ICommand UpdateParametersFromBuildingBlockInSimulation(IPKSimBuildingBlock templateBuildingBlock, Simulation simulation)
{
//Update the building block in the simulation based on the same template
var usedBuildingBlock = simulation.UsedBuildingBlockByTemplateId(templateBuildingBlock.Id);
//Template was not used in the simulation...return
if (usedBuildingBlock == null)
if (usedBuildingBlock == null)
return null;

var buildingBlockType = _executionContext.TypeFor(templateBuildingBlock);
Expand All @@ -65,9 +72,28 @@ public ICommand UpdateParametersFromBuildingBlockInSimulation(IPKSimBuildingBloc
updateCommands.CommandType = PKSimConstants.Command.CommandTypeUpdate;
updateCommands.Description = PKSimConstants.Command.UpdateBuildingBlockCommandDescription(buildingBlockType, templateBuildingBlock.Name, simulation.Name);
_executionContext.UpdateBuildingBlockPropertiesInCommand(updateCommands, simulation);

synchronizeBuildingBlocks(templateBuildingBlock, simulation);
return updateCommands;
}

/// <summary>
/// We need to make sure that once the simulation has been updated with the building block, depending building blocks
/// are also updated
/// For instance, if we update the individual in the simulation, we will also update all expression profile (since
/// expression profile are linked to the individual)
/// </summary>
private void synchronizeBuildingBlocks(IPKSimBuildingBlock templateBuildingBlock, Simulation simulation)
{
if (templateBuildingBlock is not ISimulationSubject simulationSubject)
return;

_expressionProfileUpdater.SynchronizeExpressionProfilesUsedInSimulationSubjectWithSimulation(simulationSubject, simulation);

//we need to raise an event here to ensure that the UI reflects the fact that we have synchronized our building blocks
_executionContext.PublishEvent(new SimulationStatusChangedEvent(simulation));
}

/// <summary>
/// Update parameter values from the used building block into the simulation
/// </summary>
Expand All @@ -88,9 +114,9 @@ private IEnumerable<ICommand> updateParameterValues(IEnumerable<IParameter> used
}

return from parameter in usedBuildingBlockParameters.OrderBy(x => x.IsDistributed())
let simParams = simulationParameterCache[parameter.Id]
from simParam in simParams
select _parameterSetUpdater.UpdateValue(parameter, simParam);
let simParams = simulationParameterCache[parameter.Id]
from simParam in simParams
select _parameterSetUpdater.UpdateValue(parameter, simParam);
}

private ICommand updateParameterValues(PathCache<IParameter> sourceParameters, PathCache<IParameter> targetParameters)
Expand All @@ -99,6 +125,5 @@ private ICommand updateParameterValues(PathCache<IParameter> sourceParameters, P
}

private PathCache<IParameter> parametersToUpdateFrom(IPKSimBuildingBlock buildingBlock) => _containerTask.CacheAllChildren<IParameter>(buildingBlock);

}
}
28 changes: 28 additions & 0 deletions src/PKSim.Core/Services/ExpressionProfileUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ public interface IExpressionProfileUpdater
/// <param name="sourceExpressionProfile">Expression profile to update used as source</param>
/// <param name="targetExpressionProfile">Expression profile to update</param>
void SynchronizeExpressionProfileWithExpressionProfile(ExpressionProfile sourceExpressionProfile, ExpressionProfile targetExpressionProfile);

/// <summary>
/// Updates the values from all expression profiles used by the <paramref name="templateSimulationSubject" /> into the
/// <paramref name="simulation" />
/// This is required for instance when synchronizing and individual with a simulation=>Underlying building block may
/// need to be updated as well
/// </summary>
/// <param name="templateSimulationSubject">
/// Template simulation subject (building block) that should be used to update the
/// expression profile in the simulation
/// </param>
/// <param name="simulation">Simulation to update </param>
void SynchronizeExpressionProfilesUsedInSimulationSubjectWithSimulation(ISimulationSubject templateSimulationSubject, Simulation simulation);
}

public class ExpressionProfileUpdater : IExpressionProfileUpdater
Expand Down Expand Up @@ -170,6 +183,21 @@ public void SynchronizeExpressionProfileWithExpressionProfile(ExpressionProfile
synchronizeExpressionProfiles(sourceMolecule, sourceIndividual, targetMolecule, targetIndividual, updateParameterOriginId: false);
}

public void SynchronizeExpressionProfilesUsedInSimulationSubjectWithSimulation(ISimulationSubject templateSimulationSubject, Simulation simulation)
{
templateSimulationSubject.AllExpressionProfiles().Each(template =>
{
var usedBuildingBlock = simulation.UsedBuildingBlockByTemplateId(template.Id);
if (usedBuildingBlock?.BuildingBlock is not ExpressionProfile simulationExpressionProfile)
return;

SynchronizeExpressionProfileWithExpressionProfile(template, simulationExpressionProfile);
//They are supposed to be the same => same version
simulationExpressionProfile.Version = template.Version;
usedBuildingBlock.Version = template.Version;
});
}

private void updateMoleculeParameters(IndividualMolecule sourceMolecule, ISimulationSubject sourceSimulationSubject, IndividualMolecule targetMolecule, ISimulationSubject targetSimulationSubject, bool updateParameterOriginId)
{
var allTargetMoleculeParameters = allMoleculeParametersFor(targetSimulationSubject, targetMolecule);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public ICommand UpdateParametersFromSimulationInBuildingBlock(Simulation simulat
updateCommands.Add(updateTemplateParametersCommand);

//Last, see if we have some special cases to handle
var synchronizeCommand = synchronizeBuildingBlocks(templateBuildingBlock, updateTemplateParametersCommand);
var synchronizeCommand = synchronizeBuildingBlocks(templateBuildingBlock, updateTemplateParametersCommand, simulation);
updateCommands.Add(synchronizeCommand);

//now make sure that the used building block is updated with the template building block info
Expand All @@ -79,11 +79,10 @@ public ICommand UpdateParametersFromSimulationInBuildingBlock(Simulation simulat
return updateCommands;
}

private ICommand synchronizeBuildingBlocks(IPKSimBuildingBlock templateBuildingBlock, IPKSimMacroCommand updateTemplateParametersCommand)
private ICommand synchronizeBuildingBlocks(IPKSimBuildingBlock templateBuildingBlock, IPKSimMacroCommand updateTemplateParametersCommand, Simulation simulation)
{
var simulationSubject = templateBuildingBlock as ISimulationSubject;
//For now, deal with update from Individual or Population into Expression Profile
if (simulationSubject == null)
if (templateBuildingBlock is not ISimulationSubject simulationSubject)
return new PKSimEmptyCommand();

var allExpressionProfileParameterValueCommand = updateTemplateParametersCommand.All()
Expand Down Expand Up @@ -126,6 +125,8 @@ private ICommand synchronizeBuildingBlocks(IPKSimBuildingBlock templateBuildingB
//Now that our expression profile are updated, we need to trigger the synchronization in all building blocks
expressionProfilesToUpdate.Each(x => _expressionProfileUpdater.SynchronizeAllSimulationSubjectsWithExpressionProfile(x));

//last, synchronize the expression profile in the simulation as well
_expressionProfileUpdater.SynchronizeExpressionProfilesUsedInSimulationSubjectWithSimulation(simulationSubject, simulation);
return macroCommand;
}

Expand Down
27 changes: 17 additions & 10 deletions src/PKSim.Infrastructure/Services/LazyLoadTask.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using PKSim.Assets;
using OSPSuite.Core.Domain;
using OSPSuite.Core.Domain.ParameterIdentifications;
using OSPSuite.Core.Domain.SensitivityAnalyses;
using OSPSuite.Utility.Events;
using OSPSuite.Utility.Extensions;
using PKSim.Assets;
using PKSim.Core.Model;
using PKSim.Core.Services;
using OSPSuite.Core.Domain;
using OSPSuite.Core.Domain.ParameterIdentifications;
using OSPSuite.Core.Domain.SensitivityAnalyses;

namespace PKSim.Infrastructure.Services
{
Expand All @@ -22,12 +22,12 @@ public class LazyLoadTask : ILazyLoadTask
private readonly ISensitivityAnalysisContentLoader _sensitivityAnalysisContentLoader;

public LazyLoadTask(
IContentLoader contentLoader,
ISimulationResultsLoader simulationResultsLoader,
IContentLoader contentLoader,
ISimulationResultsLoader simulationResultsLoader,
ISimulationChartsLoader simulationChartsLoader,
ISimulationComparisonContentLoader simulationComparisonContentLoader,
ISimulationComparisonContentLoader simulationComparisonContentLoader,
ISimulationAnalysesLoader simulationAnalysesLoader,
IParameterIdentificationContentLoader parameterIdentificationContentLoader,
IParameterIdentificationContentLoader parameterIdentificationContentLoader,
ISensitivityAnalysisContentLoader sensitivityAnalysisContentLoader,
IRegistrationTask registrationTask,
IProgressManager progressManager)
Expand Down Expand Up @@ -69,7 +69,7 @@ public void LoadResults<TSimulation>(TSimulation simulation) where TSimulation :
{
if (simulation == null)
return;

Load(simulation);

if (simulation.HasResults)
Expand Down Expand Up @@ -113,14 +113,21 @@ private void loadSimulations(Simulation simulation)

//updating results may triggered update of has changed flag that is not accurate. We save the original state and update it at the end
var hasChanged = simulation.HasChanged;

//Only load results for individual simulations
if (simulation.IsAnImplementationOf<IndividualSimulation>())
_simulationResultsLoader.LoadResultsFor(simulation.DowncastTo<IndividualSimulation>());

else if (simulation.IsAnImplementationOf<PopulationSimulation>())
_simulationAnalysesLoader.LoadAnalysesFor(simulation.DowncastTo<PopulationSimulation>());

//make sure each individual gets the expression profile defined in the simulation)
var simulationSubject = simulation.BuildingBlock<ISimulationSubject>();

//this can happen for an imported simulation
if (simulationSubject != null)
simulation.AllBuildingBlocks<ExpressionProfile>().Each(simulationSubject.AddExpressionProfile);

//in all cases, load the charts
_simulationChartsLoader.LoadChartsFor(simulation);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ protected override IContextMenu CreateFor(Simulation simulation, UsedBuildingBlo
}
}


public class UsedExpressionProfileInSimulationTreeNodeContextMenuFactory : UsedBuildingBlockInSimulationTreeNodeContextMenuFactory<ExpressionProfile>
{
public UsedExpressionProfileInSimulationTreeNodeContextMenuFactory(IContainer container) : base(container)
{
}

protected override IContextMenu CreateFor(Simulation simulation, UsedBuildingBlock usedBuildingBlock, ExpressionProfile expressionProfile)
{
return new UsedBuildingBlockInSimulationContextMenu<ExpressionProfile>(simulation, usedBuildingBlock, expressionProfile, _container);
}
}

public class UsedCompoundInSimulationTreeNodeContextMenuFactory : UsedBuildingBlockInSimulationTreeNodeContextMenuFactory<Compound>
{
public UsedCompoundInSimulationTreeNodeContextMenuFactory(IContainer container) : base(container)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ private void addUsedBuildingBlockNodes(Simulation simulation, ITreeNode simulati
.Under(simulationNode);

addUsedBuildingBlock(simulation, simulationNode, PKSimBuildingBlockType.SimulationSubject);
//Used for debug purposes for now addUsedBuildingBlock(simulation, simulationNode, PKSimBuildingBlockType.ExpressionProfile);
addUsedBuildingBlock(simulation, simulationNode, PKSimBuildingBlockType.Compound);
addUsedBuildingBlock(simulation, simulationNode, PKSimBuildingBlockType.Protocol);
addUsedBuildingBlock(simulation, simulationNode, PKSimBuildingBlockType.Formulation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected SimulationWizardPresenter(TView view, ISubPresenterItemManager<ISimula
}

public abstract void ModelConfigurationDone();

protected abstract string HeavyWorkCaption { get; }

public override void WizardCurrent(int previousIndex, int newIndex)
Expand Down
35 changes: 30 additions & 5 deletions tests/PKSim.Tests/Infrastructure/LazyLoadTaskSpecs.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using FakeItEasy;
using NPOI.SS.Formula.Functions;
using OSPSuite.BDDHelper;
using OSPSuite.BDDHelper.Extensions;
using OSPSuite.Core.Domain;
using OSPSuite.Utility.Events;
using PKSim.Core;
using PKSim.Core.Model;
using PKSim.Core.Services;
using PKSim.Infrastructure.Services;
Expand All @@ -20,7 +21,7 @@ public abstract class concern_for_LazyLoadTask : ContextSpecification<ILazyLoadT
protected ISimulationComparisonContentLoader _simulationComparisonContentLoader;
protected ISimulationChartsLoader _simulationChartsLoader;
protected ISimulationAnalysesLoader _simulationAnalysesLoader;
private IParameterIdentificationContentLoader _parameterIdentificationContentendLoader;
private IParameterIdentificationContentLoader _parameterIdentificationContentLoader;
private ISensitivityAnalysisContentLoader _sensitivityAnalysisContentLoader;

protected override void Context()
Expand All @@ -33,12 +34,12 @@ protected override void Context()
_simulationResultsLoader = A.Fake<ISimulationResultsLoader>();
_simulationComparisonContentLoader = A.Fake<ISimulationComparisonContentLoader>();
_simulationAnalysesLoader = A.Fake<ISimulationAnalysesLoader>();
_parameterIdentificationContentendLoader = A.Fake<IParameterIdentificationContentLoader>();
_parameterIdentificationContentLoader = A.Fake<IParameterIdentificationContentLoader>();
_sensitivityAnalysisContentLoader = A.Fake<ISensitivityAnalysisContentLoader>();

A.CallTo(() => _progressManager.Create()).Returns(_progressUpdater);
sut = new LazyLoadTask(_contentLoader, _simulationResultsLoader, _simulationChartsLoader,
_simulationComparisonContentLoader, _simulationAnalysesLoader, _parameterIdentificationContentendLoader, _sensitivityAnalysisContentLoader,
_simulationComparisonContentLoader, _simulationAnalysesLoader, _parameterIdentificationContentLoader, _sensitivityAnalysisContentLoader,
_registrationTask, _progressManager);
_objectToLoad = A.Fake<IPKSimBuildingBlock>();
_objectToLoad.Id = "objectId";
Expand Down Expand Up @@ -141,11 +142,29 @@ public void should_register_the_object_in_the_factory()
public class When_loading_an_individual_simulation : concern_for_LazyLoadTask
{
private IndividualSimulation _individualSimulation;
private Individual _individual;
private ExpressionProfile _exp1;
private ExpressionProfile _exp2;

protected override void Context()
{
base.Context();
_individualSimulation = A.Fake<IndividualSimulation>();
_individualSimulation = new IndividualSimulation();
_individual = new Individual();
_exp1 = new ExpressionProfile();
_exp2 = new ExpressionProfile();
_individualSimulation.AddUsedBuildingBlock(new UsedBuildingBlock("ind", PKSimBuildingBlockType.Individual)
{
BuildingBlock = _individual
});
_individualSimulation.AddUsedBuildingBlock(new UsedBuildingBlock("exp1", PKSimBuildingBlockType.ExpressionProfile)
{
BuildingBlock = _exp1
});
_individualSimulation.AddUsedBuildingBlock(new UsedBuildingBlock("exp2", PKSimBuildingBlockType.ExpressionProfile)
{
BuildingBlock = _exp2
});
}

protected override void Because()
Expand All @@ -158,6 +177,12 @@ public void should_also_load_the_simulation_results()
{
A.CallTo(() => _simulationResultsLoader.LoadResultsFor(_individualSimulation)).MustHaveHappened();
}

[Observation]
public void should_add_all_expression_profiles_defined_as_used_building_block_as_building_block_of_the_individual()
{
_individual.AllExpressionProfiles().ShouldOnlyContain(_exp1, _exp2);
}
}

public class When_loading_a_population_simulation : concern_for_LazyLoadTask
Expand Down
Loading

0 comments on commit ee9a1ee

Please sign in to comment.