From 82c62f1a50297ab2d6e2c86c7435e137d42a93a9 Mon Sep 17 00:00:00 2001 From: Michael Sevestre Date: Tue, 14 Feb 2023 11:59:26 -0500 Subject: [PATCH] Fixes #603 extension of formula reference --- src/OSPSuite.Assets/UIConstants.cs | 2 + src/OSPSuite.Core/Domain/Container.cs | 26 +-- src/OSPSuite.Core/Domain/EntityRules.cs | 2 +- src/OSPSuite.Core/Domain/FormulaUsablePath.cs | 5 +- .../Domain/Formulas/FormulaExtensions.cs | 12 +- ...NeighborhoodCollectionToContainerMapper.cs | 9 +- src/OSPSuite.Core/Domain/ObjectPath.cs | 16 +- .../Domain/ObjectPathKeywords.cs | 8 +- .../Domain/QuantityAndContainer.cs | 4 +- .../Domain/Services/FormulaTask.cs | 93 ++++++++-- .../Domain/Services/KeywordReplacerTask.cs | 34 ++-- .../Domain/Services/ModelConstructor.cs | 4 +- .../Domain/Services/ModelFinalizer.cs | 14 +- .../Domain/FormulaExtensionsSpecs.cs | 18 ++ .../Domain/FormulaTaskSpecs.cs | 164 +++++++++++++++++- .../Domain/KeywordReplacerTaskSpecs.cs | 7 +- .../Domain/ObjectPathSpecs.cs | 4 +- .../Domain/TableFormulaWithXArgumentSpecs.cs | 2 +- 18 files changed, 330 insertions(+), 94 deletions(-) diff --git a/src/OSPSuite.Assets/UIConstants.cs b/src/OSPSuite.Assets/UIConstants.cs index 97df55ed6..5e2e951c3 100644 --- a/src/OSPSuite.Assets/UIConstants.cs +++ b/src/OSPSuite.Assets/UIConstants.cs @@ -1724,6 +1724,8 @@ public static string TimeArrayValuesDoesNotMatchFirstIndividual(int id, int inde public static string UnitIsNotDefinedInDimension(string unit, string dimension) => $"Unit '{unit}' is not defined in dimension '{dimension}'."; + public static string CouldNotFindNeighborhoodBetween(string container1, string container2) => $"Could not find neighborhood between '{container1}' and '{container2}'"; + public static class SensitivityAnalysis { public static readonly string NoSimulationDefined = "No simulation defined"; diff --git a/src/OSPSuite.Core/Domain/Container.cs b/src/OSPSuite.Core/Domain/Container.cs index f8b688d08..c8cad2a96 100644 --- a/src/OSPSuite.Core/Domain/Container.cs +++ b/src/OSPSuite.Core/Domain/Container.cs @@ -64,15 +64,15 @@ public interface IContainer : IEntity, IEnumerable IEnumerable GetChildren(Func predicate) where T : class, IEntity; /// - /// returns the neighbors from the container connected by the given neigborhoods. + /// returns the neighbors from the container connected by the given neighborhoods. /// - IEnumerable GetNeighborsFrom(IEnumerable neighborhoods); + IReadOnlyList GetNeighborsFrom(IReadOnlyList neighborhoods); /// /// returns the neighborhoods connecting the container with its neighbors. /// /// The possible neighborhoods. - IEnumerable GetNeighborhoods(IEnumerable neighborhoods); + IReadOnlyList GetNeighborhoods(IReadOnlyList neighborhoods); /// /// Returns all children containers defined and the container itself, if the container is form type @@ -204,30 +204,30 @@ where predicate(castChild) select castChild; } - public virtual IEnumerable GetNeighborsFrom(IEnumerable neighborhoods) + public virtual IReadOnlyList GetNeighborsFrom(IReadOnlyList neighborhoods) { - var allNeighborhoods = neighborhoods.ToList(); - var first = from neighborhood in GetNeighborhoods(allNeighborhoods) + var first = from neighborhood in GetNeighborhoods(neighborhoods) where neighborhood.FirstNeighbor != this select neighborhood.FirstNeighbor; - var second = from neighborhood in GetNeighborhoods(allNeighborhoods) + var second = from neighborhood in GetNeighborhoods(neighborhoods) where neighborhood.SecondNeighbor != this select neighborhood.SecondNeighbor; - return first.Union(second); + + return first.Union(second).ToList(); } - public virtual IEnumerable GetNeighborhoods(IEnumerable neighborhoods) + public virtual IReadOnlyList GetNeighborhoods(IReadOnlyList neighborhoods) { - var allNeighborhoods = neighborhoods.ToList(); - var first = from neighborhood in allNeighborhoods + var first = from neighborhood in neighborhoods where neighborhood.FirstNeighbor == this select neighborhood; - var second = from neighborhood in allNeighborhoods + var second = from neighborhood in neighborhoods where neighborhood.SecondNeighbor == this select neighborhood; - return first.Union(second); + + return first.Union(second).ToList(); } public IReadOnlyList GetAllContainersAndSelf() where TContainer : class, IContainer => GetAllContainersAndSelf(x => true); diff --git a/src/OSPSuite.Core/Domain/EntityRules.cs b/src/OSPSuite.Core/Domain/EntityRules.cs index 944c5ad7d..9c206cd85 100644 --- a/src/OSPSuite.Core/Domain/EntityRules.cs +++ b/src/OSPSuite.Core/Domain/EntityRules.cs @@ -63,7 +63,7 @@ public static IBusinessRule NotEmptyName public static IEnumerable All() { - return _allEntityRules; + return _allEntityRules; } } } \ No newline at end of file diff --git a/src/OSPSuite.Core/Domain/FormulaUsablePath.cs b/src/OSPSuite.Core/Domain/FormulaUsablePath.cs index 9b2b024dd..c3a35d543 100644 --- a/src/OSPSuite.Core/Domain/FormulaUsablePath.cs +++ b/src/OSPSuite.Core/Domain/FormulaUsablePath.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using OSPSuite.Utility.Extensions; using OSPSuite.Core.Domain.UnitSystem; +using OSPSuite.Utility.Extensions; namespace OSPSuite.Core.Domain { @@ -32,8 +32,7 @@ public override T Clone() { Alias = Alias, Dimension = Dimension - } - .DowncastTo(); + }.DowncastTo(); } public bool Equals(FormulaUsablePath other) diff --git a/src/OSPSuite.Core/Domain/Formulas/FormulaExtensions.cs b/src/OSPSuite.Core/Domain/Formulas/FormulaExtensions.cs index 3a65134d9..1f14a2762 100644 --- a/src/OSPSuite.Core/Domain/Formulas/FormulaExtensions.cs +++ b/src/OSPSuite.Core/Domain/Formulas/FormulaExtensions.cs @@ -37,6 +37,15 @@ public static bool IsExplicit(this IFormula formula) return formula.IsAnImplementationOf(); } + /// + /// Returns true if the formula contains at least one entry using neighborhood reference otherwise false + /// + /// + public static bool IsReferencingNeighborhood(this IFormula formula) + { + return formula.ObjectPaths.Any(x => x.Contains(ObjectPathKeywords.NBH)); + } + /// /// Returns true the formula is a dynamic formula otherwise false /// @@ -83,7 +92,8 @@ public static bool IsCachable(this IFormula formula) } /// - /// Returns the used object path with the given in the or null if the is not used. + /// Returns the used object path with the given in the or null if + /// the is not used. /// public static IFormulaUsablePath FormulaUsablePathBy(this IFormula formula, string alias) { diff --git a/src/OSPSuite.Core/Domain/Mappers/NeighborhoodCollectionToContainerMapper.cs b/src/OSPSuite.Core/Domain/Mappers/NeighborhoodCollectionToContainerMapper.cs index ff7d6a005..2dea5da18 100644 --- a/src/OSPSuite.Core/Domain/Mappers/NeighborhoodCollectionToContainerMapper.cs +++ b/src/OSPSuite.Core/Domain/Mappers/NeighborhoodCollectionToContainerMapper.cs @@ -11,7 +11,7 @@ namespace OSPSuite.Core.Domain.Mappers /// /// Creates Top-Container named "NEIGHBORHOODS", all mapped neighborhoods /// - /// are added as childs of this top container + /// are added as children of this top container /// public interface INeighborhoodCollectionToContainerMapper : IBuilderMapper { @@ -23,7 +23,8 @@ public class NeighborhoodCollectionToContainerMapper : INeighborhoodCollectionTo private readonly INeighborhoodBuilderToNeighborhoodMapper _neighborhoodMapper; private readonly IObjectPathFactory _objectPathFactory; - public NeighborhoodCollectionToContainerMapper(IObjectBaseFactory objectBaseFactory, + public NeighborhoodCollectionToContainerMapper( + IObjectBaseFactory objectBaseFactory, INeighborhoodBuilderToNeighborhoodMapper neighborhoodMapper, IObjectPathFactory objectPathFactory) { @@ -84,7 +85,7 @@ private ICache> presentMoleculesCachedByContainerPath(IEnu } /// - /// Returns molecules which will be created in both neighbours of the neighbourhood + /// Returns molecules which will be created in both neighbors of the neighborhood /// private IEnumerable moleculeNamesFor(INeighborhoodBuilder neighborhoodBuilder, ICache> moleculesStartValuesForFloatingMolecules) @@ -92,7 +93,7 @@ private IEnumerable moleculeNamesFor(INeighborhoodBuilder neighborhoodBu var pathToFirstNeighbor = _objectPathFactory.CreateAbsoluteObjectPath(neighborhoodBuilder.FirstNeighbor).ToString(); var pathToSecondNeighbor = _objectPathFactory.CreateAbsoluteObjectPath(neighborhoodBuilder.SecondNeighbor).ToString(); - // check if both neighbours has at least 1 molecule (if not - return empty list) + // check if both neighbors has at least 1 molecule (if not - return empty list) if (!moleculesStartValuesForFloatingMolecules.Contains(pathToFirstNeighbor) || !moleculesStartValuesForFloatingMolecules.Contains(pathToSecondNeighbor)) return new List(); diff --git a/src/OSPSuite.Core/Domain/ObjectPath.cs b/src/OSPSuite.Core/Domain/ObjectPath.cs index 744d34e5f..283c10b42 100644 --- a/src/OSPSuite.Core/Domain/ObjectPath.cs +++ b/src/OSPSuite.Core/Domain/ObjectPath.cs @@ -78,9 +78,8 @@ public interface IObjectPath : IReadOnlyCollection /// void RemoveFirst(); - /// - /// Replaces the path with the path entries in + /// Replaces the path with the path entries in /// /// Path entries used to replace the path void ReplaceWith(IEnumerable pathEntries); @@ -97,11 +96,11 @@ public class ObjectPath : IObjectPath /// /// String separating elements of the in String Representation /// - public const string PATH_DELIMITER = "|"; + public const string PATH_DELIMITER = "|"; protected readonly List _pathEntries; - public static IObjectPath Empty { get; } = new ObjectPath(); + public static IObjectPath Empty { get; } = new ObjectPath(); public ObjectPath() : this(new List()) { @@ -129,6 +128,7 @@ public virtual string PathAsString returnString.Append(PATH_DELIMITER); returnString.Append(str); } + return returnString.ToString().Substring(PATH_DELIMITER.Length); } } @@ -198,7 +198,6 @@ public virtual T Resolve(IEntity refEntity) where T : class return resolvePath(dependentObject, usePath); } - public virtual T Clone() where T : IObjectPath { return new ObjectPath(_pathEntries).DowncastTo(); @@ -278,6 +277,7 @@ public bool Equals(ObjectPath other) if (!_pathEntries[i].Equals(entry)) return false; i++; } + return true; } @@ -299,13 +299,11 @@ public override int GetHashCode() hashCode = hashCode ^ _pathEntries[i].GetHashCode(); } } + return hashCode; } - public override string ToString() - { - return PathAsString; - } + public override string ToString() => PathAsString; public static implicit operator string(ObjectPath objectPath) { diff --git a/src/OSPSuite.Core/Domain/ObjectPathKeywords.cs b/src/OSPSuite.Core/Domain/ObjectPathKeywords.cs index b555acc9e..ad4f19ef2 100644 --- a/src/OSPSuite.Core/Domain/ObjectPathKeywords.cs +++ b/src/OSPSuite.Core/Domain/ObjectPathKeywords.cs @@ -26,7 +26,7 @@ public static class ObjectPathKeywords public static readonly string TARGET = addKeyword("TARGET"); /// - /// String representing a reference to the Neighborhood there the transport is created + /// String representing a reference to the Neighborhood where the transport is created /// public static readonly string NEIGHBORHOOD = addKeyword("NEIGHBORHOOD"); @@ -61,6 +61,12 @@ public static class ObjectPathKeywords /// public static readonly string ALL_FLOATING_MOLECULES = addKeyword("ALL_FLOATING_MOLECULES"); + /// + /// Represents the keywords allowing us to identify a neighborhood between two containers + /// For example CONTAINER1|/CONTAINER2/ means the neighborhood between CONTAINER1 and CONTAINER2 + /// + public static readonly string NBH = addKeyword(""); + private static string addKeyword(string keyword) { _allKeywords.Add(keyword); diff --git a/src/OSPSuite.Core/Domain/QuantityAndContainer.cs b/src/OSPSuite.Core/Domain/QuantityAndContainer.cs index d2bd63f28..1ab0ed078 100644 --- a/src/OSPSuite.Core/Domain/QuantityAndContainer.cs +++ b/src/OSPSuite.Core/Domain/QuantityAndContainer.cs @@ -58,9 +58,9 @@ public void Add(IEntity newChild) public IEnumerable GetChildren() where T : class, IEntity => _container.GetChildren(); - public IEnumerable GetNeighborsFrom(IEnumerable neighborhoods) => _container.GetNeighborsFrom(neighborhoods); + public IReadOnlyList GetNeighborsFrom(IReadOnlyList neighborhoods) => _container.GetNeighborsFrom(neighborhoods); - public IEnumerable GetNeighborhoods(IEnumerable neighborhoods) => _container.GetNeighborhoods(neighborhoods); + public IReadOnlyList GetNeighborhoods(IReadOnlyList neighborhoods) => _container.GetNeighborhoods(neighborhoods); public IReadOnlyList GetAllContainersAndSelf() where TContainer : class, IContainer => GetAllContainersAndSelf(x => true); diff --git a/src/OSPSuite.Core/Domain/Services/FormulaTask.cs b/src/OSPSuite.Core/Domain/Services/FormulaTask.cs index 22ed7ee5a..858b01821 100644 --- a/src/OSPSuite.Core/Domain/Services/FormulaTask.cs +++ b/src/OSPSuite.Core/Domain/Services/FormulaTask.cs @@ -1,11 +1,18 @@ using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using OSPSuite.Assets; using OSPSuite.Core.Domain.Formulas; using OSPSuite.Core.Domain.UnitSystem; using OSPSuite.Core.Extensions; using OSPSuite.Utility.Collections; +using OSPSuite.Utility.Exceptions; using OSPSuite.Utility.Extensions; using OSPSuite.Utility.Visitor; +using static OSPSuite.Core.Domain.Constants; +using static OSPSuite.Core.Domain.Constants.Parameters; +using static OSPSuite.Core.Domain.ObjectPath; +using static OSPSuite.Core.Domain.ObjectPathKeywords; namespace OSPSuite.Core.Domain.Services { @@ -13,8 +20,8 @@ public interface IFormulaTask { /// /// Checks that all formula having the same origin id are indeed the same formula. - /// If not, reset the origin id. Discrepancy can happen when a formula witk key words was clone. - /// After the cloning operation, the orign id was set but used object path are not the same anymore + /// If not, reset the origin id. Discrepancy can happen when a formula with key words was clone. + /// After the cloning operation, the origin id was set but used object path are not the same anymore /// void CheckFormulaOriginIn(IModel model); @@ -24,6 +31,11 @@ public interface IFormulaTask /// bool FormulasAreTheSame(IFormula firstFormula, IFormula secondFormula); + /// + /// Ensures that all object paths referencing neighborhoods between containers are expanded + /// + void ExpandNeighborhoodReferencesIn(IModel model); + /// /// Resolves all dynamic formulas defined in /// @@ -52,9 +64,9 @@ public class FormulaTask : IFormulaTask, private readonly ICache> _originIdToFormulaCache = new Cache>(); public FormulaTask( - IObjectPathFactory objectPathFactory, - IObjectBaseFactory objectBaseFactory, - IAliasCreator aliasCreator, + IObjectPathFactory objectPathFactory, + IObjectBaseFactory objectBaseFactory, + IAliasCreator aliasCreator, IDimensionFactory dimensionFactory) { _objectPathFactory = objectPathFactory; @@ -130,20 +142,69 @@ public bool FormulasAreTheSame(IFormula firstFormula, IFormula secondFormula) return false; } } + return true; } - public void ExpandDynamicFormulaIn(IModel model) + public void ExpandNeighborhoodReferencesIn(IModel model) { - ExpandDynamicFormulaIn(model.Root); + void updatePath(IUsingFormula usingFormula, IFormulaUsablePath path) => updateNeighborhoodReferencingPath(model, path, usingFormula); + + model.Root.GetAllChildren(x => x.Formula.IsReferencingNeighborhood()) + .Each(x => x.Formula.ObjectPaths.Each(path => updatePath(x, path))); } + private void updateNeighborhoodReferencingPath(IModel model, IFormulaUsablePath formulaUsablePath, IUsingFormula usingFormula) + { + var pathAsList = formulaUsablePath.ToList(); + var firstIndex = pathAsList.FindIndex(x => x == NBH); + var lastIndex = pathAsList.FindLastIndex(x => x == NBH); + + //Only one occurrence of the marker, this is not a valid path. We do not change anything as it won't be resolved later on + if (firstIndex == lastIndex) + return; + + //We retrieve the path to first container, and second container + var pathToFirstContainer = pathAsList.Take(firstIndex).ToList(); + //+1 and -1 in order to skip the NBH tags + var pathToSecondContainer = pathAsList.Skip(firstIndex + 1).Take(lastIndex - firstIndex - 1).ToList(); + //This will need to be saved and added back to the path once we have figured out the actual neighborhood path + var restOfPath = pathAsList.Skip(lastIndex + 1).ToList(); + + //we use resolve to that an exception is thrown + var container1 = getContainerOrThrow(pathToFirstContainer, usingFormula); + var container2 = getContainerOrThrow(pathToSecondContainer, usingFormula); + + var allNeighborhoods = model.Neighborhoods.GetAllChildren(); + var allNeighborhoodsConnectedToContainer1 = container1.GetNeighborhoods(allNeighborhoods); + var neighborhoodsBetweenContainer1AndContainer2 = container2.GetNeighborhoods(allNeighborhoodsConnectedToContainer1); + if (neighborhoodsBetweenContainer1AndContainer2.Count == 0) + throw new OSPSuiteException(Error.CouldNotFindNeighborhoodBetween(container1.EntityPath(), container2.EntityPath())); + + //recreate the path for this neighborhood and add the rest of the path. Validation of this path will be done later + var neighborhoodPath = _objectPathFactory.CreateAbsoluteObjectPath(neighborhoodsBetweenContainer1AndContainer2[0]); + restOfPath.Each(neighborhoodPath.Add); + formulaUsablePath.ReplaceWith(neighborhoodPath); + } + + private IContainer getContainerOrThrow(IReadOnlyList path, IUsingFormula usingFormula) + { + //we use resolve to that an exception is thrown + var container = new ObjectPath(path).Resolve(usingFormula); + if (container == null) + throw new OSPSuiteException(Error.CouldNotFindQuantityWithPath(path.ToPathString())); + + return container; + } + + public void ExpandDynamicFormulaIn(IModel model) => ExpandDynamicFormulaIn(model.Root); + public void ExpandDynamicFormulaIn(IContainer container) { var allFormulaUsable = container.GetAllChildren().ToEntityDescriptorMapList(); var allEntityUsingDynamicFormula = container.GetAllChildren(x => x.Formula.IsDynamic()); - foreach (var entityUsingFormula in allEntityUsingDynamicFormula) + allEntityUsingDynamicFormula.Each(entityUsingFormula => { var dynamicFormula = entityUsingFormula.Formula.DowncastTo(); // Check if circular reference will be created. @@ -151,15 +212,15 @@ public void ExpandDynamicFormulaIn(IContainer container) throw new CircularReferenceInSumFormulaException(dynamicFormula.Name, entityUsingFormula.Name); entityUsingFormula.Formula = dynamicFormula.ExpandUsing(allFormulaUsable, _objectPathFactory, _objectBaseFactory); - } + }); } public string AddParentVolumeReferenceToFormula(IFormula formula) { - string volumeAlias = _aliasCreator.CreateAliasFrom(Constants.VOLUME_ALIAS, formula.ObjectPaths.Select(p => p.Alias)); + string volumeAlias = _aliasCreator.CreateAliasFrom(VOLUME_ALIAS, formula.ObjectPaths.Select(p => p.Alias)); //possible reference - var volumeReferencePath = _objectPathFactory.CreateFormulaUsablePathFrom(ObjectPath.PARENT_CONTAINER, Constants.Parameters.VOLUME) + var volumeReferencePath = _objectPathFactory.CreateFormulaUsablePathFrom(PARENT_CONTAINER, VOLUME) .WithAlias(volumeAlias) .WithDimension(_dimensionFactory.Dimension(Constants.Dimension.VOLUME)); @@ -196,16 +257,16 @@ private void resetOriginIdIfFormulasAreNotTheSame(IList formula public void Visit(IUsingFormula usingFormula) { - if (usingFormula == null) return; - addFormulaToCache(usingFormula.Formula); + addFormulaToCache(usingFormula?.Formula); } private void addFormulaToCache(IFormula formula) { - var explicitFormula = formula as ExplicitFormula; - if (explicitFormula == null) return; + if (!(formula is ExplicitFormula explicitFormula)) + return; - if (string.IsNullOrEmpty(explicitFormula.OriginId)) return; + if (string.IsNullOrEmpty(explicitFormula.OriginId)) + return; listFor(explicitFormula.OriginId).Add(explicitFormula); } diff --git a/src/OSPSuite.Core/Domain/Services/KeywordReplacerTask.cs b/src/OSPSuite.Core/Domain/Services/KeywordReplacerTask.cs index b0da6d9a8..a043b5906 100644 --- a/src/OSPSuite.Core/Domain/Services/KeywordReplacerTask.cs +++ b/src/OSPSuite.Core/Domain/Services/KeywordReplacerTask.cs @@ -3,6 +3,7 @@ using OSPSuite.Core.Domain.Builder; using OSPSuite.Utility.Extensions; using OSPSuite.Utility.Visitor; +using static OSPSuite.Core.Domain.ObjectPathKeywords; namespace OSPSuite.Core.Domain.Services { @@ -139,17 +140,17 @@ public void ReplaceIn(ITransport realization, IContainer rootContainer, string m addMoleculeReplacersTo(keywordReplacer, moleculeName); addCommonNeighborhoodReplacersTo(keywordReplacer, neighborhood); - keywordReplacer.AddReplacement(new KeywordWithPathReplacer(ObjectPathKeywords.SOURCE, + keywordReplacer.AddReplacement(new KeywordWithPathReplacer(SOURCE, _objectPathFactory.CreateAbsoluteObjectPath(realization.SourceAmount.ParentContainer))); - keywordReplacer.AddReplacement(new KeywordWithPathReplacer(ObjectPathKeywords.TARGET, + keywordReplacer.AddReplacement(new KeywordWithPathReplacer(TARGET, _objectPathFactory.CreateAbsoluteObjectPath(realization.TargetAmount.ParentContainer))); - keywordReplacer.AddReplacement(new KeywordWithPathReplacer(ObjectPathKeywords.REALIZATION, new ObjectPath(transportName, realization.Name))); - keywordReplacer.AddReplacement(new SimpleKeywordReplacer(ObjectPathKeywords.TRANSPORT, transportName)); - keywordReplacer.AddReplacement(new SimpleKeywordReplacer(ObjectPathKeywords.TRANSPORTER, transporterName)); + keywordReplacer.AddReplacement(new KeywordWithPathReplacer(REALIZATION, new ObjectPath(transportName, realization.Name))); + keywordReplacer.AddReplacement(new SimpleKeywordReplacer(TRANSPORT, transportName)); + keywordReplacer.AddReplacement(new SimpleKeywordReplacer(TRANSPORTER, transporterName)); keywordReplacer.ReplaceIn(realization); replaceInContainer(realization, rootContainer); - //replaceInContainer only replaces standard keywords. Transport specific keywords need to be replaced in all children explicitely + //replaceInContainer only replaces standard keywords. Transport specific keywords need to be replaced in all children explicitly var transportContainer = realization.ParentContainer ?? realization; transportContainer.GetAllChildren().Each(keywordReplacer.ReplaceIn); } @@ -183,10 +184,7 @@ public IObjectPath CreateModelPathFor(IObjectPath objectPath, IContainer rootCon return modelPath; } - public void ReplaceIn(IContainer rootContainer) - { - replaceInContainer(rootContainer, rootContainer); - } + public void ReplaceIn(IContainer rootContainer) => replaceInContainer(rootContainer, rootContainer); private void replaceInContainer(IContainer container, IContainer rootContainer) { @@ -229,27 +227,23 @@ private void addCommonModelReplacersTo(IKeywordReplacerCollection keywordReplace { //Replace the predefined keywords keywordReplacer.AddReplacement(new TopContainerPathReplacer(rootContainer.Name, rootContainer.GetChildren().AllNames())); - keywordReplacer.AddReplacement( - new TopContainerPathReplacer(rootContainer.Name, new[] {ObjectPathKeywords.MOLECULE, Constants.NEIGHBORHOODS})); + keywordReplacer.AddReplacement(new TopContainerPathReplacer(rootContainer.Name, new[] {MOLECULE, Constants.NEIGHBORHOODS})); } private void addCommonNeighborhoodReplacersTo(IKeywordReplacerCollection keywordReplacer, INeighborhood neighborhood) { if (neighborhood == null) return; - keywordReplacer.AddReplacement(new KeywordWithPathReplacer(ObjectPathKeywords.FIRST_NEIGHBOR, - _objectPathFactory.CreateAbsoluteObjectPath(neighborhood.FirstNeighbor))); - keywordReplacer.AddReplacement(new KeywordWithPathReplacer(ObjectPathKeywords.SECOND_NEIGHBOR, - _objectPathFactory.CreateAbsoluteObjectPath(neighborhood.SecondNeighbor))); - keywordReplacer.AddReplacement(new KeywordWithPathReplacer(ObjectPathKeywords.NEIGHBORHOOD, - _objectPathFactory.CreateAbsoluteObjectPath(neighborhood))); + keywordReplacer.AddReplacement(new KeywordWithPathReplacer(FIRST_NEIGHBOR, _objectPathFactory.CreateAbsoluteObjectPath(neighborhood.FirstNeighbor))); + keywordReplacer.AddReplacement(new KeywordWithPathReplacer(SECOND_NEIGHBOR, _objectPathFactory.CreateAbsoluteObjectPath(neighborhood.SecondNeighbor))); + keywordReplacer.AddReplacement(new KeywordWithPathReplacer(NEIGHBORHOOD, _objectPathFactory.CreateAbsoluteObjectPath(neighborhood))); //should be placed after the KeywordWithPathReplacer so that NEIGHBORHOOD is only replaced if not found yet - keywordReplacer.AddReplacement(new SimpleKeywordReplacer(ObjectPathKeywords.NEIGHBORHOOD, neighborhood.Name)); + keywordReplacer.AddReplacement(new SimpleKeywordReplacer(NEIGHBORHOOD, neighborhood.Name)); } private void addMoleculeReplacersTo(IKeywordReplacerCollection keywordReplacer, string moleculeName) { if (string.IsNullOrEmpty(moleculeName)) return; - keywordReplacer.AddReplacement(new SimpleKeywordReplacer(ObjectPathKeywords.MOLECULE, moleculeName)); + keywordReplacer.AddReplacement(new SimpleKeywordReplacer(MOLECULE, moleculeName)); } private void replaceInUsingFormula(IUsingFormula usingFormula, IContainer rootContainer) diff --git a/src/OSPSuite.Core/Domain/Services/ModelConstructor.cs b/src/OSPSuite.Core/Domain/Services/ModelConstructor.cs index d8e1a0491..b94819afd 100644 --- a/src/OSPSuite.Core/Domain/Services/ModelConstructor.cs +++ b/src/OSPSuite.Core/Domain/Services/ModelConstructor.cs @@ -260,13 +260,13 @@ private void copySpatialStructure(IModel model, IBuildConfiguration buildConfigu model.Root.AddTag(new Tag(Constants.ROOT_CONTAINER_TAG)); - //Add each container defined in the spatial strucutre and direct child of the root container + //Add each container defined in the spatial structure and direct child of the root container foreach (var topContainer in buildConfiguration.SpatialStructure.TopContainers) { model.Root.Add(_containerMapper.MapFrom(topContainer, buildConfiguration)); } - // Add the neibghborhoods + // Add the neighborhoods model.Neighborhoods = _neighborhoodsMapper.MapFrom(model, buildConfiguration); } diff --git a/src/OSPSuite.Core/Domain/Services/ModelFinalizer.cs b/src/OSPSuite.Core/Domain/Services/ModelFinalizer.cs index 8599177bb..535d8ffd6 100644 --- a/src/OSPSuite.Core/Domain/Services/ModelFinalizer.cs +++ b/src/OSPSuite.Core/Domain/Services/ModelFinalizer.cs @@ -130,7 +130,7 @@ private void finalizeTransportsInNeighborhood(IModel cloneModel, INeighborhood s /// /// Finalizes the transports in molecule parent container. Transport are - /// alway created in a Molecule specific sub container, that's why we start + /// always created in a Molecule specific sub container, that's why we start /// with them. This could be a Event Group or Neighborhood /// /// The clone model. @@ -144,11 +144,11 @@ private void finalizeTransportsInMoleculeParentContainer(IModel cloneModel, ICon var sourceTransports = moleculeContainer.GetChildren(); var tmp = cloneMoleculeContainer.GetChildren(); - finalizeTranports(cloneModel, tmp, sourceTransports); + finalizeTransports(cloneModel, tmp, sourceTransports); } } - private void finalizeTranports(IModel cloneModel, IEnumerable tmp, IEnumerable sourceTransports) + private void finalizeTransports(IModel cloneModel, IEnumerable tmp, IEnumerable sourceTransports) { var cloneTransports = new Cache(x => x.Name); @@ -161,10 +161,10 @@ private void finalizeTranports(IModel cloneModel, IEnumerable tmp, I } } - private void resolveAmounts(IModel cloneModel, ITransport sourcerTransport, ITransport cloneTransport) + private void resolveAmounts(IModel cloneModel, ITransport sourceTransport, ITransport cloneTransport) { - var sourceAmountPath = _objectPathFactory.CreateAbsoluteObjectPath(sourcerTransport.SourceAmount); - var targetAmountPath = _objectPathFactory.CreateAbsoluteObjectPath(sourcerTransport.TargetAmount); + var sourceAmountPath = _objectPathFactory.CreateAbsoluteObjectPath(sourceTransport.SourceAmount); + var targetAmountPath = _objectPathFactory.CreateAbsoluteObjectPath(sourceTransport.TargetAmount); cloneTransport.SourceAmount = sourceAmountPath.Resolve(cloneModel.Root); cloneTransport.TargetAmount = targetAmountPath.Resolve(cloneModel.Root); @@ -181,7 +181,7 @@ private void resolveNeighbors(IModel cloneModel, INeighborhood sourceNeighborhoo private ICache createNeighborhoodCache(IModel model) { - var cloneNeighborhoods = new Cache(nh => nh.Name); + var cloneNeighborhoods = new Cache(x => x.Name); cloneNeighborhoods.AddRange(model.Neighborhoods.GetChildren()); return cloneNeighborhoods; } diff --git a/tests/OSPSuite.Core.Tests/Domain/FormulaExtensionsSpecs.cs b/tests/OSPSuite.Core.Tests/Domain/FormulaExtensionsSpecs.cs index 6b0b91e7d..5f42442b2 100644 --- a/tests/OSPSuite.Core.Tests/Domain/FormulaExtensionsSpecs.cs +++ b/tests/OSPSuite.Core.Tests/Domain/FormulaExtensionsSpecs.cs @@ -52,4 +52,22 @@ public void should_return_false_otherwise() new ConstantFormula().IsCachable().ShouldBeFalse(); } } + + public class When_checking_if_a_formula_is_referencing_neighborhood : concern_for_FormulaExtensions + { + [Observation] + public void should_return_true_if_the_formula_contains_at_least_one_path_referencing_the_neighborhood_keyword() + { + var formula = new ExplicitFormula(); + formula.AddObjectPath(new FormulaUsablePath("A", "B")); + formula.AddObjectPath(new FormulaUsablePath("A", ObjectPathKeywords.NBH)); + formula.IsReferencingNeighborhood().ShouldBeTrue(); + } + + [Observation] + public void should_return_false_otherwise() + { + new ExplicitFormula().IsReferencingNeighborhood().ShouldBeFalse(); + } + } } \ No newline at end of file diff --git a/tests/OSPSuite.Core.Tests/Domain/FormulaTaskSpecs.cs b/tests/OSPSuite.Core.Tests/Domain/FormulaTaskSpecs.cs index d886ebb17..187a081e4 100644 --- a/tests/OSPSuite.Core.Tests/Domain/FormulaTaskSpecs.cs +++ b/tests/OSPSuite.Core.Tests/Domain/FormulaTaskSpecs.cs @@ -8,7 +8,13 @@ using OSPSuite.Core.Domain.Services; using OSPSuite.Core.Domain.UnitSystem; using OSPSuite.Core.Extensions; +using OSPSuite.Helpers; +using OSPSuite.Utility.Exceptions; using OSPSuite.Utility.Extensions; +using static OSPSuite.Core.Domain.Constants; +using static OSPSuite.Core.Domain.Constants.Parameters; +using static OSPSuite.Core.Domain.ObjectPath; +using static OSPSuite.Core.Domain.ObjectPathKeywords; namespace OSPSuite.Core.Domain { @@ -241,7 +247,7 @@ public void should_replace_all_dynamic_formula_with_the_corresponding_explicit_f } [Observation] - public void should_have_created_an_explicit_formula_that_is_the_sum_of_the_defined_parameers() + public void should_have_created_an_explicit_formula_that_is_the_sum_of_the_defined_parameters() { var explicitFormula = _parameter.Formula.DowncastTo(); explicitFormula.FormulaString.ShouldBeEqualTo("P_1 + P_2"); @@ -249,7 +255,147 @@ public void should_have_created_an_explicit_formula_that_is_the_sum_of_the_defin } } - public class When_expanding_a_dynamic_formula__in_a_model_at_a_formula_useable_satisfying_their_own_criteria : + public class When_replacing_the_neighborhood_keyword_in_a_well_defined_path : concern_for_FormulaTask + { + private IParameter _liverCellParameter; + private IContainer _rootContainer; + private FormulaUsablePath _objectPath; + private Container _liver; + private Container _liverCell; + private Container _liverPlasma; + private Neighborhood _neighborhood_liver_cell_liver_pls; + private IParameter _neighborhoodParameter; + private Model _model; + + protected override void Context() + { + base.Context(); + _model = new Model(); + _rootContainer = new Container().WithName("ROOT"); + _liver = new Container().WithName("Liver"); + _liverCell = new Container().WithName("Intracellular").WithParentContainer(_liver); + _liverPlasma = new Container().WithName("Plasma").WithParentContainer(_liver); + + _neighborhood_liver_cell_liver_pls = new Neighborhood + { + FirstNeighbor = _liverCell, + SecondNeighbor = _liverPlasma + }; + + _model.Root = _rootContainer; + _model.Neighborhoods = new Container().WithName(NEIGHBORHOODS); + _model.Neighborhoods.Add(_neighborhood_liver_cell_liver_pls); + _neighborhoodParameter = DomainHelperForSpecs.ConstantParameterWithValue(10).WithName("K").WithParentContainer(_neighborhood_liver_cell_liver_pls); + _liverCellParameter = new Parameter().WithName("P").WithParentContainer(_liverCell); + + _rootContainer.Add(_liver); + _liverCellParameter.Formula = new ExplicitFormula("K+10"); + //..||..|..|Plasma||K + _objectPath = new FormulaUsablePath(PARENT_CONTAINER, NBH, PARENT_CONTAINER, PARENT_CONTAINER, "Plasma", NBH, _neighborhoodParameter.Name) {Alias = "K"}; + _liverCellParameter.Formula.AddObjectPath(_objectPath); + } + + protected override void Because() + { + sut.ExpandNeighborhoodReferencesIn(_model); + } + + [Observation] + public void should_have_replaced_the_nbh_with_the_actual_path_to_the_neighborhood() + { + _liverCellParameter.Value.ShouldBeEqualTo(20); + } + } + + public class When_replacing_the_neighborhood_keyword_in_a_path_missing_one_container : concern_for_FormulaTask + { + private IParameter _liverCellParameter; + private IContainer _rootContainer; + private FormulaUsablePath _objectPath; + private Container _liver; + private Container _liverCell; + private Container _liverPlasma; + private Neighborhood _neighborhood_liver_cell_liver_pls; + private IParameter _neighborhoodParameter; + private Model _model; + + protected override void Context() + { + base.Context(); + _model = new Model(); + _rootContainer = new Container().WithName("ROOT"); + _liver = new Container().WithName("Liver"); + _liverCell = new Container().WithName("Intracellular").WithParentContainer(_liver); + _liverPlasma = new Container().WithName("Plasma").WithParentContainer(_liver); + + _neighborhood_liver_cell_liver_pls = new Neighborhood + { + FirstNeighbor = _liverCell, + SecondNeighbor = _liverPlasma + }; + + _model.Root = _rootContainer; + _model.Neighborhoods = new Container().WithName(NEIGHBORHOODS); + _model.Neighborhoods.Add(_neighborhood_liver_cell_liver_pls); + _neighborhoodParameter = DomainHelperForSpecs.ConstantParameterWithValue(10).WithName("K").WithParentContainer(_neighborhood_liver_cell_liver_pls); + _liverCellParameter = new Parameter().WithName("P").WithParentContainer(_liverCell); + + _rootContainer.Add(_liver); + _liverCellParameter.Formula = new ExplicitFormula("K+10"); + //..||..|..|Interstitial||K_<==_does_not_exist_in_the_model + _objectPath = new FormulaUsablePath(PARENT_CONTAINER, NBH, PARENT_CONTAINER, PARENT_CONTAINER, "Interstitial", NBH, _neighborhoodParameter.Name) { Alias = "K" }; + _liverCellParameter.Formula.AddObjectPath(_objectPath); + } + + [Observation] + public void should_have_replaced_the_nbh_with_the_actual_path_to_the_neighborhood() + { + The.Action(() => sut.ExpandNeighborhoodReferencesIn(_model)).ShouldThrowAn(); + } + } + + public class When_replacing_the_neighborhood_keyword_in_between_two_containers_without_neighborhood : concern_for_FormulaTask + { + private IParameter _liverCellParameter; + private IContainer _rootContainer; + private FormulaUsablePath _objectPath; + private Container _liver; + private Container _liverCell; + private Neighborhood _neighborhood_liver_cell_liver_pls; + private IParameter _neighborhoodParameter; + private Model _model; + + protected override void Context() + { + base.Context(); + _model = new Model(); + _rootContainer = new Container().WithName("ROOT"); + _liver = new Container().WithName("Liver"); + _liverCell = new Container().WithName("Intracellular").WithParentContainer(_liver); + + _neighborhood_liver_cell_liver_pls = new Neighborhood(); + + _model.Root = _rootContainer; + _model.Neighborhoods = new Container().WithName(NEIGHBORHOODS); + _model.Neighborhoods.Add(_neighborhood_liver_cell_liver_pls); + _neighborhoodParameter = DomainHelperForSpecs.ConstantParameterWithValue(10).WithName("K").WithParentContainer(_neighborhood_liver_cell_liver_pls); + _liverCellParameter = new Parameter().WithName("P").WithParentContainer(_liverCell); + + _rootContainer.Add(_liver); + _liverCellParameter.Formula = new ExplicitFormula("K+10"); + //..||..|..|Plasma||K_<==_does_not_exist_in_the_model + _objectPath = new FormulaUsablePath(PARENT_CONTAINER, NBH, PARENT_CONTAINER, PARENT_CONTAINER, "Plasma", NBH, _neighborhoodParameter.Name) { Alias = "K" }; + _liverCellParameter.Formula.AddObjectPath(_objectPath); + } + + [Observation] + public void should_have_replaced_the_nbh_with_the_actual_path_to_the_neighborhood() + { + The.Action(() => sut.ExpandNeighborhoodReferencesIn(_model)).ShouldThrowAn(); + } + } + + public class When_expanding_a_dynamic_formula_in_a_model_at_a_formula_useable_satisfying_their_own_criteria : concern_for_FormulaTask { private IModel _model; @@ -320,10 +466,10 @@ protected override void Because() [Observation] public void should_simply_add_it_and_return_the_volume_alias() { - _alias.ShouldBeEqualTo(Constants.VOLUME_ALIAS); - var volumeReference = _explicitFormula.ObjectPaths.Find(x => x.Alias == Constants.VOLUME_ALIAS); + _alias.ShouldBeEqualTo(VOLUME_ALIAS); + var volumeReference = _explicitFormula.ObjectPaths.Find(x => x.Alias == VOLUME_ALIAS); volumeReference.ShouldNotBeNull(); - volumeReference.PathAsString.ShouldBeEqualTo(new[] {ObjectPath.PARENT_CONTAINER, Constants.Parameters.VOLUME}.ToPathString()); + volumeReference.PathAsString.ShouldBeEqualTo(new[] {PARENT_CONTAINER, VOLUME}.ToPathString()); } } @@ -336,7 +482,7 @@ protected override void Context() { base.Context(); _explicitFormula = new ExplicitFormula(); - _explicitFormula.AddObjectPath(new FormulaUsablePath(ObjectPath.PARENT_CONTAINER, Constants.Parameters.VOLUME).WithAlias(Constants.VOLUME_ALIAS)); + _explicitFormula.AddObjectPath(new FormulaUsablePath(PARENT_CONTAINER, VOLUME).WithAlias(VOLUME_ALIAS)); } protected override void Because() @@ -347,7 +493,7 @@ protected override void Because() [Observation] public void should_simply_use_it() { - _alias.ShouldBeEqualTo(Constants.VOLUME_ALIAS); + _alias.ShouldBeEqualTo(VOLUME_ALIAS); } } @@ -361,7 +507,7 @@ protected override void Context() base.Context(); _explicitFormula = new ExplicitFormula(); //another path - _explicitFormula.AddObjectPath(new FormulaUsablePath(ObjectPath.PARENT_CONTAINER, ObjectPath.PARENT_CONTAINER, Constants.Parameters.VOLUME, Constants.Parameters.VOLUME).WithAlias(Constants.VOLUME_ALIAS)); + _explicitFormula.AddObjectPath(new FormulaUsablePath(PARENT_CONTAINER, PARENT_CONTAINER, VOLUME, VOLUME).WithAlias(VOLUME_ALIAS)); } protected override void Because() @@ -372,7 +518,7 @@ protected override void Because() [Observation] public void should_not_use_the_default_volume_alias() { - _alias.ShouldNotBeEqualTo(Constants.VOLUME_ALIAS); + _alias.ShouldNotBeEqualTo(VOLUME_ALIAS); } } diff --git a/tests/OSPSuite.Core.Tests/Domain/KeywordReplacerTaskSpecs.cs b/tests/OSPSuite.Core.Tests/Domain/KeywordReplacerTaskSpecs.cs index a57ef3f46..211531699 100644 --- a/tests/OSPSuite.Core.Tests/Domain/KeywordReplacerTaskSpecs.cs +++ b/tests/OSPSuite.Core.Tests/Domain/KeywordReplacerTaskSpecs.cs @@ -5,6 +5,7 @@ using OSPSuite.Core.Domain.Formulas; using OSPSuite.Core.Domain.Services; using OSPSuite.Helpers; +using static OSPSuite.Core.Domain.ObjectPathKeywords; namespace OSPSuite.Core.Domain { @@ -23,7 +24,7 @@ protected override void Context() _model.Root = A.Fake().WithName(_modelName); A.CallTo(() => _model.Root.GetChildren()) .Returns(new[] {new Container().WithName(ConstantsForSpecs.Organism), new Container().WithName("B")}); - _objPathFirstNeighbor = new FormulaUsablePath(new[] {ObjectPathKeywords.FIRST_NEIGHBOR, "A"}); + _objPathFirstNeighbor = new FormulaUsablePath(new[] {FIRST_NEIGHBOR, "A"}); _objPathMolecule = new FormulaUsablePath(new[] {"B"}); _objPathOrganism = new FormulaUsablePath(new[] {ConstantsForSpecs.Organism, "C"}); sut = new KeywordReplacerTask(new ObjectPathFactory(new AliasCreator())); @@ -70,7 +71,7 @@ public class When_replacing_the_keyword_in_a_molecule_properties_container : con protected override void Context() { base.Context(); - _objPathMolecule = new FormulaUsablePath(new[] {ObjectPathKeywords.MOLECULE}); + _objPathMolecule = new FormulaUsablePath(new[] {MOLECULE}); _moleculeName = "REPLACED"; _moleculeContainer = new Container().WithName(_modelName); var moleculeParameter = new Parameter().WithFormula(A.Fake()); @@ -113,7 +114,7 @@ protected override void Context() _moleculeContainer = new Container().WithName("CYP").WithContainerType(ContainerType.Molecule); _parameter = DomainHelperForSpecs.ConstantParameterWithValue(4).WithName("P").WithParentContainer(_moleculeContainer); _parameter.Formula = new ExplicitFormula(); - _objectPath = new FormulaUsablePath("SIM", ObjectPathKeywords.MOLECULE, "test"); + _objectPath = new FormulaUsablePath("SIM", MOLECULE, "test"); _parameter.Formula.AddObjectPath(_objectPath); } diff --git a/tests/OSPSuite.Core.Tests/Domain/ObjectPathSpecs.cs b/tests/OSPSuite.Core.Tests/Domain/ObjectPathSpecs.cs index 66ed3ffc1..09a952136 100644 --- a/tests/OSPSuite.Core.Tests/Domain/ObjectPathSpecs.cs +++ b/tests/OSPSuite.Core.Tests/Domain/ObjectPathSpecs.cs @@ -106,7 +106,7 @@ public class When_replacing_the_content_of_a_path_with_another_path : concern_fo public void should_have_updated_the_path() { sut = new ObjectPath("A", "B"); - sut.ReplaceWith(new []{"C", "D"}); + sut.ReplaceWith(new[] {"C", "D"}); sut.PathAsString.ShouldBeEqualTo("C|D"); } } @@ -125,7 +125,7 @@ public class When_removing_the_element_of_an_path_by_index : concern_for_ObjectP [Observation] public void should_have_remove_the_element() { - sut = new ObjectPath("A", "B","C"); + sut = new ObjectPath("A", "B", "C"); sut.RemoveAt(1); sut.ShouldOnlyContain("A", "C"); } diff --git a/tests/OSPSuite.Core.Tests/Domain/TableFormulaWithXArgumentSpecs.cs b/tests/OSPSuite.Core.Tests/Domain/TableFormulaWithXArgumentSpecs.cs index a55291ad1..77ecd46d9 100644 --- a/tests/OSPSuite.Core.Tests/Domain/TableFormulaWithXArgumentSpecs.cs +++ b/tests/OSPSuite.Core.Tests/Domain/TableFormulaWithXArgumentSpecs.cs @@ -75,7 +75,7 @@ public void should_return_the_largest_value_for_an_argument_above_the_last_time_ } [Observation] - public void should_retun_the_interpolated_value_if_the_argument_is_not_one_of_the_defined_sample() + public void should_return_the_interpolated_value_if_the_argument_is_not_one_of_the_defined_sample() { _xArgumentObject.Value = 1.5; _parameter.Value.ShouldBeEqualTo(15);